fix(cmd/explorer): wait for evm_blocks before mounting API (race fix)

The standalone API server's detectTables() runs once on construction and
caches the table name (evm_blocks vs blocks). If main.go mounts the API
before runChain has called idx.Init() on the EVM indexer, detectTables
sees no evm_blocks and falls back to the bare 'blocks' default — which
never exists, so every query returns empty.

Verified locally: with the table-wait fix, multi-chain mode indexes all
five mainnet chains correctly. After ~50s of run time:
  cchain  blocks=845 max=844 txs=1
  zoo     blocks=833 max=832 txs=52   (fully synced)
  hanzo   blocks=47  max=46  txs=58   (fully synced)
  spc     blocks=11  max=10  txs=2    (fully synced)
  pars    blocks=29  max=28  txs=42   (fully synced)

/v1/indexer/{slug}/blocks returns chain-specific data; /v1/explorer/*
legacy alias works; /v1/indexer/cchain/* default-chain alias works.
This commit is contained in:
2026-05-02 17:08:02 -07:00
parent 0ca18e0436
commit 828aeddc14
+21
View File
@@ -23,6 +23,7 @@ package main
import (
"context"
"database/sql"
"encoding/json"
"flag"
"fmt"
@@ -299,6 +300,26 @@ func main() {
}
continue
}
// Wait for evm_blocks table to exist before mounting — the
// standalone server caches the table name on construction
// and won't see later schema changes. EVM-typed chains use
// evm_blocks; non-EVM chains skip this check.
if chain.Type == "evm" {
db, openErr := sql.Open("sqlite3", fmt.Sprintf("file:%s?mode=ro", path))
if openErr == nil {
var c int
err := db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='evm_blocks'").Scan(&c)
db.Close()
if err != nil || c == 0 {
lastErr = fmt.Errorf("evm_blocks table not yet created (count=%d, err=%v)", c, err)
if time.Now().After(deadline) {
log.Printf("[%s] API mount waiting on evm_blocks table (attempt %d): %v", chain.Slug, attempt, lastErr)
deadline = time.Now().Add(5 * time.Minute)
}
continue
}
}
}
apiSrv, err := explorer.NewStandaloneServer(explorer.Config{
IndexerDBPath: path,
ChainID: chain.ChainID,