diff --git a/conf/config.go b/conf/config.go index ee204562..fba96707 100644 --- a/conf/config.go +++ b/conf/config.go @@ -22,7 +22,8 @@ type Config struct { // blocks that the node doesn't have data for, such as by skipping them in checkpoint sync. // For sensible reasons, indexing may actually start at an even later block, such as if // this block is already indexed or the node indicates that it doesn't have this block. - IndexingStart uint64 `koanf:"indexing_start"` + IndexingStart uint64 `koanf:"indexing_start"` + IndexingDisable bool `koanf:"indexing_disable"` Log *LogConfig `koanf:"log"` Cache *CacheConfig `koanf:"cache"` diff --git a/indexer/indexer.go b/indexer/indexer.go index 1884ec49..cabc109d 100644 --- a/indexer/indexer.go +++ b/indexer/indexer.go @@ -43,10 +43,11 @@ var ErrNotHealthy = errors.New("not healthy") type Service struct { service.BaseBackgroundService - runtimeID common.Namespace - enablePruning bool - pruningStep uint64 - indexingStart uint64 + runtimeID common.Namespace + enablePruning bool + pruningStep uint64 + indexingStart uint64 + indexingDisable bool backend Backend client client.RuntimeClient @@ -303,6 +304,14 @@ func (s *Service) indexingWorker() { // Start starts service. func (s *Service) Start() { + // TODO/NotYawning: Non-archive nodes that have the indexer disabled + // likey want to use a different notion of healthy, and probably also + // want to start a worker that monitors the database for changes. + if s.indexingDisable { + s.updateHealth(true) + return + } + go s.indexingWorker() go s.healthWorker() @@ -339,8 +348,20 @@ func New( enablePruning: cfg.EnablePruning, pruningStep: cfg.PruningStep, indexingStart: cfg.IndexingStart, + indexingDisable: cfg.IndexingDisable, } s.Logger = s.Logger.With("runtime_id", s.runtimeID.String()) + // TODO/NotYawning: Non-archive nodes probably want to do something + // different here. + if s.indexingDisable { + if _, err := s.backend.QueryLastIndexedRound(ctx); err != nil { + s.Logger.Error("indexer disabled and no rounds indexed, this will never work", + "err", err, + ) + return nil, nil, err + } + } + return s, cachingBackend, nil } diff --git a/main.go b/main.go index 4a957fe0..08c2d512 100644 --- a/main.go +++ b/main.go @@ -114,7 +114,7 @@ func truncateExec(cmd *cobra.Command, args []string) error { } // Initialize db. - db, err := psql.InitDB(ctx, cfg.Database, true) + db, err := psql.InitDB(ctx, cfg.Database, true, false) if err != nil { logger.Error("failed to initialize db", "err", err) return err @@ -145,7 +145,7 @@ func migrateExec(cmd *cobra.Command, args []string) error { logger := logging.GetLogger("migrate-db") // Initialize db. - db, err := psql.InitDB(ctx, cfg.Database, true) + db, err := psql.InitDB(ctx, cfg.Database, true, false) if err != nil { logger.Error("failed to initialize db", "err", err) return err @@ -192,8 +192,13 @@ func runRoot() error { // Create the runtime client with account module query helpers. rc := client.New(conn, runtimeID) + // For now, "disable" write access to the DB in a kind of kludgy way + // if the indexer is disabled. Yes this means that no migrations + // can be done. Deal with it. + dbReadOnly := cfg.IndexingDisable + // Initialize db for migrations (higher timeouts). - db, err := psql.InitDB(ctx, cfg.Database, true) + db, err := psql.InitDB(ctx, cfg.Database, true, dbReadOnly) if err != nil { logger.Error("failed to initialize db", "err", err) return err @@ -208,7 +213,7 @@ func runRoot() error { // Initialize db again, now with configured timeouts. var storage storage.Storage - storage, err = psql.InitDB(ctx, cfg.Database, false) + storage, err = psql.InitDB(ctx, cfg.Database, false, dbReadOnly) if err != nil { logger.Error("failed to initialize db", "err", err) return err diff --git a/storage/psql/psql.go b/storage/psql/psql.go index 8160a08a..ab8ebf80 100644 --- a/storage/psql/psql.go +++ b/storage/psql/psql.go @@ -30,7 +30,12 @@ type PostDB struct { } // InitDB creates postgresql db instance. -func InitDB(ctx context.Context, cfg *conf.DatabaseConfig, longTimeouts bool) (*PostDB, error) { +func InitDB( + ctx context.Context, + cfg *conf.DatabaseConfig, + longTimeouts bool, + readOnly bool, +) (*PostDB, error) { if cfg == nil { return nil, errors.New("nil configuration") } @@ -63,6 +68,19 @@ func InitDB(ctx context.Context, cfg *conf.DatabaseConfig, longTimeouts bool) (* } } + // Set "read-only" mode by setting the default status of new + // transactions. + // + // Note: This still allows txes to alter temporary tables, and is + // advisory rather than something that is securely enforced. + if readOnly { + opts = append(opts, pgdriver.WithConnParams( + map[string]interface{}{ + "default_transaction_read_only": "on", + }, + )) + } + pgConn := pgdriver.NewConnector(opts...) sqlDB := sql.OpenDB(pgConn) maxOpenConns := cfg.MaxOpenConns diff --git a/storage/psql/psql_test.go b/storage/psql/psql_test.go index 044cfb5a..27b74be3 100644 --- a/storage/psql/psql_test.go +++ b/storage/psql/psql_test.go @@ -21,7 +21,7 @@ func TestMain(m *testing.M) { var err error ctx := context.Background() tests.MustInitConfig() - db, err = InitDB(ctx, tests.TestsConfig.Database, false) + db, err = InitDB(ctx, tests.TestsConfig.Database, false, false) if err != nil { log.Println(`It seems database failed to initialize. Do you have PostgreSQL running? If not, you can run docker run -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres -p 5432:5432 -d postgres`) diff --git a/tests/rpc/utils.go b/tests/rpc/utils.go index 460536a7..d94950b2 100644 --- a/tests/rpc/utils.go +++ b/tests/rpc/utils.go @@ -130,7 +130,7 @@ func Setup() error { // Initialize db. ctx := context.Background() - db, err = psql.InitDB(ctx, tests.TestsConfig.Database, true) + db, err = psql.InitDB(ctx, tests.TestsConfig.Database, true, false) if err != nil { return fmt.Errorf("failed to initialize DB: %w", err) } @@ -143,7 +143,7 @@ func Setup() error { // Initialize db again, now with configured timeouts. var storage storage.Storage - storage, err = psql.InitDB(ctx, tests.TestsConfig.Database, false) + storage, err = psql.InitDB(ctx, tests.TestsConfig.Database, false, false) if err != nil { return err }