// Serve starts the horizon system, binding it to a socket, setting up // the shutdown signals and starting the appropriate db-streaming pumps. func (a *App) Serve() { a.web.router.Compile() http.Handle("/", a.web.router) listenStr := fmt.Sprintf(":%d", a.config.Port) listener := bind.Socket(listenStr) log.Infof(a.ctx, "Starting horizon on %s", listener.Addr()) graceful.HandleSignals() bind.Ready() graceful.PreHook(func() { log.Info(a.ctx, "received signal, gracefully stopping") a.Cancel() }) graceful.PostHook(func() { log.Info(a.ctx, "stopped") }) if a.config.Autopump { sse.SetPump(a.ctx, sse.AutoPump) } else { sse.SetPump(a.ctx, db.NewLedgerClosePump(a.ctx, a.historyDb)) } err := graceful.Serve(listener, http.DefaultServeMux) if err != nil { log.Panic(a.ctx, err) } graceful.Wait() }
// NewLedgerClosePump starts a background proc that continually watches the // history database provided. The watch is stopped after the provided context // is cancelled. // // Every second, the proc spawned by calling this func will check to see // if a new ledger has been imported (by ruby-horizon as of 2015-04-30, but // should eventually end up being in this project). If a new ledger is seen // the the channel returned by this function emits func NewLedgerClosePump(ctx context.Context, db *sql.DB) <-chan time.Time { result := make(chan time.Time) go func() { var lastSeenLedger int32 for { select { case <-time.After(1 * time.Second): var latestLedger int32 row := db.QueryRow("SELECT MAX(sequence) FROM history_ledgers") err := row.Scan(&latestLedger) if err != nil { log.Warn(ctx, "Failed to check latest ledger", err) break } if latestLedger > lastSeenLedger { log.Debugf(ctx, "saw new ledger: %d, prev: %d", latestLedger, lastSeenLedger) lastSeenLedger = latestLedger result <- time.Now() } case <-ctx.Done(): log.Info(ctx, "canceling ledger pump") return } } }() return result }