Skip to content

Commit

Permalink
services/horizon: Return 503 error in POST /transactions if Stellar-C…
Browse files Browse the repository at this point in the history
…ore not synced
  • Loading branch information
bartekn committed Jun 2, 2021
1 parent fc45699 commit eedcd1a
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 49 deletions.
4 changes: 4 additions & 0 deletions services/horizon/internal/actions/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ func getAsset(r *http.Request, prefix string) (xdr.Asset, error) {
func getURLParam(r *http.Request, key string) (string, bool) {
rctx := chi.RouteContext(r.Context())

if rctx == nil {
return "", false
}

// Return immediately if keys does not match Values
// This can happen when a named param is not specified.
// This is a bug in chi: https://github.com/go-chi/chi/issues/426
Expand Down
22 changes: 8 additions & 14 deletions services/horizon/internal/actions/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,18 @@ import (
"net/url"

"github.com/stellar/go/protocols/horizon"
"github.com/stellar/go/services/horizon/internal/corestate"
"github.com/stellar/go/services/horizon/internal/ledger"
"github.com/stellar/go/services/horizon/internal/resourceadapter"
)

type CoreSettings struct {
Synced bool
CurrentProtocolVersion int32
CoreSupportedProtocolVersion int32
CoreVersion string
}

type CoreSettingsGetter interface {
GetCoreSettings() CoreSettings
type CoreStateGetter interface {
GetCoreState() corestate.State
}

type GetRootHandler struct {
LedgerState *ledger.State
CoreSettingsGetter
CoreStateGetter
NetworkPassphrase string
FriendbotURL *url.URL
HorizonVersion string
Expand All @@ -37,16 +31,16 @@ func (handler GetRootHandler) GetResource(w HeaderWriter, r *http.Request) (inte
"strictReceivePaths": StrictReceivePathsQuery{}.URITemplate(),
"strictSendPaths": FindFixedPathsQuery{}.URITemplate(),
}
coreSettings := handler.GetCoreSettings()
coreState := handler.GetCoreState()
resourceadapter.PopulateRoot(
r.Context(),
&res,
handler.LedgerState.CurrentStatus(),
handler.HorizonVersion,
coreSettings.CoreVersion,
coreState.CoreVersion,
handler.NetworkPassphrase,
coreSettings.CurrentProtocolVersion,
coreSettings.CoreSupportedProtocolVersion,
coreState.CurrentProtocolVersion,
coreState.CoreSupportedProtocolVersion,
handler.FriendbotURL,
templates,
)
Expand Down
6 changes: 6 additions & 0 deletions services/horizon/internal/actions/submit_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type SubmitTransactionHandler struct {
Submitter *txsub.System
NetworkPassphrase string
CoreStateGetter
}

type envelopeInfo struct {
Expand Down Expand Up @@ -136,6 +137,11 @@ func (handler SubmitTransactionHandler) GetResource(w HeaderWriter, r *http.Requ
}
}

coreState := handler.GetCoreState()
if !coreState.Synced {
return nil, hProblem.StaleHistory
}

submission := handler.Submitter.Submit(
r.Context(),
info.raw,
Expand Down
31 changes: 5 additions & 26 deletions services/horizon/internal/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import (
"github.com/prometheus/client_golang/prometheus"

"github.com/stellar/go/clients/stellarcore"
proto "github.com/stellar/go/protocols/stellarcore"
"github.com/stellar/go/services/horizon/internal/actions"
"github.com/stellar/go/services/horizon/internal/corestate"
"github.com/stellar/go/services/horizon/internal/db2/history"
"github.com/stellar/go/services/horizon/internal/httpx"
"github.com/stellar/go/services/horizon/internal/ingest"
Expand All @@ -30,26 +29,6 @@ import (
"github.com/stellar/go/support/log"
)

type coreSettingsStore struct {
sync.RWMutex
actions.CoreSettings
}

func (c *coreSettingsStore) set(resp *proto.InfoResponse) {
c.Lock()
defer c.Unlock()
c.Synced = resp.IsSynced()
c.CoreVersion = resp.Info.Build
c.CurrentProtocolVersion = int32(resp.Info.Ledger.Version)
c.CoreSupportedProtocolVersion = int32(resp.Info.ProtocolVersion)
}

func (c *coreSettingsStore) get() actions.CoreSettings {
c.RLock()
defer c.RUnlock()
return c.CoreSettings
}

// App represents the root of the state of a horizon instance.
type App struct {
done chan struct{}
Expand All @@ -60,7 +39,7 @@ type App struct {
ctx context.Context
cancel func()
horizonVersion string
coreSettings coreSettingsStore
coreState corestate.Store
orderBookStream *ingest.OrderBookStream
submitter *txsub.System
paths paths.Finder
Expand All @@ -80,8 +59,8 @@ type App struct {
coreSynced prometheus.GaugeFunc
}

func (a *App) GetCoreSettings() actions.CoreSettings {
return a.coreSettings.get()
func (a *App) GetCoreState() corestate.State {
return a.coreState.Get()
}

const tickerMaxFrequency = 1 * time.Second
Expand Down Expand Up @@ -390,7 +369,7 @@ func (a *App) UpdateStellarCoreInfo(ctx context.Context) {
os.Exit(1)
}

a.coreSettings.set(resp)
a.coreState.Set(resp)
}

// DeleteUnretainedHistory forwards to the app's reaper. See
Expand Down
13 changes: 7 additions & 6 deletions services/horizon/internal/httpx/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type RouterConfig struct {
MaxPathLength uint
PathFinder paths.Finder
PrometheusRegistry *prometheus.Registry
CoreGetter actions.CoreSettingsGetter
CoreGetter actions.CoreStateGetter
HorizonVersion string
FriendbotURL *url.URL
HealthCheck http.Handler
Expand Down Expand Up @@ -124,11 +124,11 @@ func (r *Router) addRoutes(config *RouterConfig, rateLimiter *throttled.HTTPRate
r.Method(http.MethodGet, "/health", config.HealthCheck)

r.Method(http.MethodGet, "/", ObjectActionHandler{Action: actions.GetRootHandler{
LedgerState: ledgerState,
CoreSettingsGetter: config.CoreGetter,
NetworkPassphrase: config.NetworkPassphrase,
FriendbotURL: config.FriendbotURL,
HorizonVersion: config.HorizonVersion,
LedgerState: ledgerState,
CoreStateGetter: config.CoreGetter,
NetworkPassphrase: config.NetworkPassphrase,
FriendbotURL: config.FriendbotURL,
HorizonVersion: config.HorizonVersion,
}})

streamHandler := sse.StreamHandler{
Expand Down Expand Up @@ -292,6 +292,7 @@ func (r *Router) addRoutes(config *RouterConfig, rateLimiter *throttled.HTTPRate
r.Method(http.MethodPost, "/transactions", ObjectActionHandler{actions.SubmitTransactionHandler{
Submitter: config.TxSubmitter,
NetworkPassphrase: config.NetworkPassphrase,
CoreStateGetter: config.CoreGetter,
}})

// Network state related endpoints
Expand Down
2 changes: 1 addition & 1 deletion services/horizon/internal/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func initDbMetrics(app *App) {
Help: "determines if Stellar-Core defined by --stellar-core-url is synced with the network",
},
func() float64 {
if app.coreSettings.Synced {
if app.coreState.Synced {
return 1
} else {
return 0
Expand Down
4 changes: 2 additions & 2 deletions services/horizon/internal/render/problem/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ var (
Status: http.StatusServiceUnavailable,
Detail: "This horizon instance is configured to reject client requests " +
"when it can determine that the history database is lagging too far " +
"behind the connected instance of stellar-core or read replica. Please " +
"try again later.",
"behind the connected instance of Stellar-Core or read replica. It's " +
"also possible that Stellar-Core is out of sync. Please try again later.",
}

// StillIngesting is a well-known problem type. Use it as a shortcut
Expand Down

0 comments on commit eedcd1a

Please sign in to comment.