Persist found blocks in SQLite (Phase 2b.5)

New internal/store package wraps modernc.org/sqlite (pure Go,
no CGO) with a BlockStore that exposes Open/Close/InsertBlock/
Recent. The aggregator now accepts an optional *store.BlockStore;
on Run() it loads up to maxBlockHistory rows from the store before
the first refresh, and each ingested block gets written to the
DB before being appended to the in-memory ring.

main.go opens the store at cfg.DBPath and logs a warning + falls
back to in-memory-only if the file can't be created — a broken
data volume shouldn't stop the pool from running.

InsertBlock uses INSERT OR IGNORE on the height primary key so
replayed log events after a restart are harmless.
This commit is contained in:
satoshi
2026-04-14 11:06:38 +03:00
parent c92a991e89
commit 01829746ac
6 changed files with 219 additions and 1 deletions
+17
View File
@@ -21,6 +21,7 @@ import (
"github.com/kamadopool/kamado-api/internal/httpapi"
"github.com/kamadopool/kamado-api/internal/logmon"
"github.com/kamadopool/kamado-api/internal/state"
"github.com/kamadopool/kamado-api/internal/store"
)
func main() {
@@ -41,7 +42,23 @@ func main() {
ck := ckpool.New(cfg.CKPoolSockDir)
rpc := bitcoind.NewRPC(cfg.BitcoinRPCURL, cfg.BitcoinRPCUser, cfg.BitcoinRPCPassword, cfg.BitcoinRPCTimeout)
// Persistent block history. Non-fatal if it can't be opened — the
// aggregator falls back to an in-memory ring so the pool keeps
// running even with a broken data volume.
var blockStore *store.BlockStore
if cfg.DBPath != "" {
if s, err := store.Open(cfg.DBPath); err != nil {
log.Warn("block store open failed, running without persistence", "path", cfg.DBPath, "err", err)
} else {
blockStore = s
defer blockStore.Close()
log.Info("block store opened", "path", cfg.DBPath)
}
}
agg := state.New(ck, rpc, cfg.PollInterval, log)
agg.Store = blockStore
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()