From a3faba336dfcf4622b1d25ece610ee41e5ceb173 Mon Sep 17 00:00:00 2001 From: vietddude Date: Thu, 3 Sep 2026 11:37:22 +0700 Subject: [PATCH 1/2] refactor(kvstore): replace Consul backend with Redis Consul was used purely as a plain KV store (no locks/sessions/watches), so swap it for Redis, which already runs for the bloom filter. This drops one external service and the hashicorp/consul/api dependency. - add RedisStore implementing infra.KVStore (SET/GET, SCAN-based List, pipelined BatchSet, JSON codec); keep Badger as the embedded backend - remove the Consul-specific GetWithOptions from the KVStore interface, which also unpins consul/api from pkg/infra and the test mocks - thread the shared redis client into kvstore.NewFromConfig - repoint kv-migrate (Badger<->Redis) and relax wallet-kv-load's backend - drop the consul service from docker-compose and the config template Catchup ranges still go through the generic KV store; the HASH+Lua catchupstore is a separate follow-up. --- README.md | 7 +- cmd/devtools/blockreliability/main.go | 2 +- cmd/indexer/main.go | 4 +- cmd/kv-migrate/main.go | 38 ++- cmd/wallet-kv-load/main.go | 21 +- configs/config.example.yaml | 15 +- docker-compose.yml | 14 - go.mod | 16 -- go.sum | 200 -------------- internal/indexer/stellar_test.go | 5 - internal/worker/factory_test.go | 19 +- pkg/common/config/services.go | 29 +-- pkg/common/enum/enum.go | 2 +- pkg/infra/kvstore.go | 5 +- pkg/kvstore/badger.go | 6 - pkg/kvstore/consul.go | 360 -------------------------- pkg/kvstore/factory.go | 23 +- pkg/kvstore/kvstore.go | 19 ++ pkg/kvstore/redis.go | 194 ++++++++++++++ pkg/store/blockstore/store_test.go | 9 +- 20 files changed, 288 insertions(+), 700 deletions(-) delete mode 100644 pkg/kvstore/consul.go create mode 100644 pkg/kvstore/kvstore.go create mode 100644 pkg/kvstore/redis.go diff --git a/README.md b/README.md index 4a9ac8f..82f64a0 100644 --- a/README.md +++ b/README.md @@ -247,9 +247,8 @@ go build -o indexer cmd/indexer/main.go Start required services before running the indexer (docker-compose provided): - NATS server (events) -- Consul (KV) or Badger (embedded) +- Redis (KV store, Bloom filter, ManualWorker) — or Badger for the embedded KV backend - PostgreSQL (wallet address repo) -- Redis (for Bloom filter or ManualWorker) ```bash docker-compose up -d @@ -260,7 +259,7 @@ docker-compose up -d ## 🔧 Configuration - **Chains**: configurable (`start_block`, `batch_size`, `poll_interval`) -- **KVStore**: BadgerDB / in-memory / Consul +- **KVStore**: Redis / BadgerDB (embedded) - **Bloom Filter**: Redis or in-memory - **Event Emitter**: NATS streaming - **RPC Providers**: failover + rate-limiting @@ -305,7 +304,7 @@ nats consumer sub transfer transaction-consumer # Initialize bloom filter and kvstore ./wallet-kv-load run --config configs/config.yaml --batch 10000 --debug -# Migrate from Badger to Consul (edit migrate.yaml first) +# Migrate KV between Badger and Redis (edit migrate.yaml first) ./kv-migrate run --config configs/config.yaml --dry-run ``` diff --git a/cmd/devtools/blockreliability/main.go b/cmd/devtools/blockreliability/main.go index 21c2265..c462f39 100644 --- a/cmd/devtools/blockreliability/main.go +++ b/cmd/devtools/blockreliability/main.go @@ -76,7 +76,7 @@ func main() { } // KVStore - kv, err := kvstore.NewFromConfig(services.KVS) + kv, err := kvstore.NewFromConfig(services.KVS, redisClient.GetClient()) if err != nil { logger.Fatal("KVStore connection failed", "err", err) } diff --git a/cmd/indexer/main.go b/cmd/indexer/main.go index 8d5c159..c108e4d 100644 --- a/cmd/indexer/main.go +++ b/cmd/indexer/main.go @@ -124,8 +124,8 @@ func runIndexer(chains []string, configPath string, debug, manual, catchup, from } // start kvstore - logger.Info("Connecting to kvstore", "url", services.KVS.Consul.Address) - kvstore, err := kvstore.NewFromConfig(services.KVS) + logger.Info("Connecting to kvstore", "type", services.KVS.Type) + kvstore, err := kvstore.NewFromConfig(services.KVS, redisClient.GetClient()) if err != nil { logger.Fatal("Create kvstore failed", "err", err) } diff --git a/cmd/kv-migrate/main.go b/cmd/kv-migrate/main.go index 6ace48e..a42b7c7 100644 --- a/cmd/kv-migrate/main.go +++ b/cmd/kv-migrate/main.go @@ -12,7 +12,6 @@ import ( "github.com/fystack/multichain-indexer/pkg/infra" "github.com/fystack/multichain-indexer/pkg/kvstore" "github.com/goccy/go-yaml" - "github.com/hashicorp/consul/api" ) // ANSI color codes @@ -44,7 +43,7 @@ type EndpointType string const ( EndpointTypeBadger EndpointType = "badger" - EndpointTypeConsul EndpointType = "consul" + EndpointTypeRedis EndpointType = "redis" ) type CLI struct { @@ -62,14 +61,21 @@ type MigrationConfig struct { type EndpointConfig struct { Type enum.KVStoreType `yaml:"type"` Badger config.BadgerConfig `yaml:"badger,omitempty"` - Consul config.ConsulConfig `yaml:"consul,omitempty"` + Redis RedisEndpointConfig `yaml:"redis,omitempty"` +} + +type RedisEndpointConfig struct { + URL string `yaml:"url"` + Password string `yaml:"password"` + MTLS bool `yaml:"mtls"` + Prefix string `yaml:"prefix"` } func main() { var cli CLI ctx := kong.Parse(&cli, kong.Name("kv-migrate"), - kong.Description("Migrate keys between Badger and Consul KV stores")) + kong.Description("Migrate keys between Badger and Redis KV stores")) printBanner() @@ -194,25 +200,15 @@ func buildStore(config EndpointConfig) (infra.KVStore, error) { config.Badger.Prefix, infra.JSON, ) - case enum.KVStoreTypeConsul: - if config.Consul.Address == "" { - return nil, fmt.Errorf("consul address is required") + case enum.KVStoreTypeRedis: + if config.Redis.URL == "" { + return nil, fmt.Errorf("redis url is required") } - var httpAuth *api.HttpBasicAuth - if config.Consul.HttpAuth.Username != "" || config.Consul.HttpAuth.Password != "" { - httpAuth = &api.HttpBasicAuth{ - Username: config.Consul.HttpAuth.Username, - Password: config.Consul.HttpAuth.Password, - } + rc, err := infra.NewRedisClient(config.Redis.URL, config.Redis.Password, "", config.Redis.MTLS) + if err != nil { + return nil, err } - return kvstore.NewConsulClient(kvstore.Options{ - Scheme: config.Consul.Scheme, - Address: config.Consul.Address, - Folder: config.Consul.Folder, - Codec: infra.JSON, - Token: config.Consul.Token, - HttpAuth: httpAuth, - }) + return kvstore.NewRedisStore(rc.GetClient(), config.Redis.Prefix, infra.JSON) default: return nil, fmt.Errorf("unsupported store type: %s", config.Type) } diff --git a/cmd/wallet-kv-load/main.go b/cmd/wallet-kv-load/main.go index 9a81a3a..e9bc279 100644 --- a/cmd/wallet-kv-load/main.go +++ b/cmd/wallet-kv-load/main.go @@ -15,11 +15,12 @@ import ( "github.com/fystack/multichain-indexer/pkg/kvstore" "github.com/fystack/multichain-indexer/pkg/model" "github.com/fystack/multichain-indexer/pkg/repository" + "github.com/redis/go-redis/v9" "gorm.io/gorm" ) type CLI struct { - Run RunCmd `cmd:"" help:"Load wallet addresses from DB into Consul KV."` + Run RunCmd `cmd:"" help:"Load wallet addresses from DB into the KV store."` } type RunCmd struct { @@ -48,11 +49,21 @@ func (c *RunCmd) Run() error { logger.Fatal("Create db connection failed", "err", err) } - // Build KV store from config; must be consul - if cfg.Services.KVS.Type != enum.KVStoreTypeConsul { - logger.Fatal("KVStore type must be consul for this command", "type", cfg.Services.KVS.Type) + // Build KV store from config. Redis needs a client; badger does not. + var redisClient *redis.Client + if cfg.Services.KVS.Type == enum.KVStoreTypeRedis { + rc, err := infra.NewRedisClient( + cfg.Services.Redis.URL, + cfg.Services.Redis.Password, + string(cfg.Environment), + cfg.Services.Redis.MTLS, + ) + if err != nil { + logger.Fatal("Create redis client failed", "err", err) + } + redisClient = rc.GetClient() } - store, err := kvstore.NewFromConfig(cfg.Services.KVS) + store, err := kvstore.NewFromConfig(cfg.Services.KVS, redisClient) if err != nil { logger.Fatal("Create KV store failed", "err", err) } diff --git a/configs/config.example.yaml b/configs/config.example.yaml index 9f23e51..e6f4f10 100644 --- a/configs/config.example.yaml +++ b/configs/config.example.yaml @@ -515,15 +515,12 @@ services: mtls: false # enable mutual TLS with client certificates (production only) kvstore: - type: "consul" - consul: - scheme: "http" - address: "127.0.0.1:8500" - folder: "indexer" - token: "" - # http_auth: # optional: enable if consul is secured - # username: "" - # password: "" + type: "redis" # redis or badger + redis: + prefix: "indexer" # key namespace; reuses the shared redis connection above + # badger: # embedded local backend (no external service) + # directory: "data/badger" + # prefix: "indexer" bloomfilter: type: "redis" # redis or in_memory diff --git a/docker-compose.yml b/docker-compose.yml index 1c0f934..6c81de2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,20 +31,6 @@ services: tty: true restart: always - consul: - image: hashicorp/consul:1.20 - container_name: consul - ports: - - "8500:8500" - - "8601:8600/udp" - command: "agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0" - healthcheck: - test: ["CMD", "consul", "operator", "raft", "list-peers"] - interval: 5s - timeout: 3s - retries: 10 - restart: always - volumes: db_data: redis_data: diff --git a/go.mod b/go.mod index c3063cf..959b2b6 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 github.com/goccy/go-yaml v1.19.2 github.com/golang/protobuf v1.5.4 - github.com/hashicorp/consul/api v1.32.1 github.com/jackc/pgx/v5 v5.7.5 github.com/lmittmann/tint v1.1.2 github.com/mr-tron/base58 v1.2.0 @@ -38,14 +37,12 @@ replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/armon/go-metrics v0.4.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -53,14 +50,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/serf v0.10.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect @@ -68,10 +57,6 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect @@ -87,7 +72,6 @@ require ( go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect diff --git a/go.sum b/go.sum index 662078a..e6a412a 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= @@ -10,20 +9,6 @@ github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls= @@ -44,14 +29,10 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs= @@ -64,11 +45,6 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -76,10 +52,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -93,73 +65,19 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= -github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= -github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= -github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= -github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= -github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -177,57 +95,19 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w= github.com/lmittmann/tint v1.1.2/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/nats.go v1.44.0 h1:ECKVrDLdh/kDPV1g0gAQ+2+m2KprqZK5O/eJAyAnH2M= github.com/nats-io/nats.go v1.44.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0= @@ -237,46 +117,21 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI= github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -288,19 +143,12 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= @@ -322,69 +170,26 @@ go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42s go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0= @@ -393,17 +198,12 @@ google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/indexer/stellar_test.go b/internal/indexer/stellar_test.go index 52abd5c..63b80e1 100644 --- a/internal/indexer/stellar_test.go +++ b/internal/indexer/stellar_test.go @@ -15,7 +15,6 @@ import ( "github.com/fystack/multichain-indexer/pkg/common/enum" "github.com/fystack/multichain-indexer/pkg/common/types" "github.com/fystack/multichain-indexer/pkg/infra" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -175,10 +174,6 @@ func (m *mockKVStore) Get(k string) (string, error) { return string(val), nil } -func (m *mockKVStore) GetWithOptions(k string, _ *api.QueryOptions) (string, error) { - return m.Get(k) -} - func (m *mockKVStore) SetAny(k string, v any) error { data, err := json.Marshal(v) if err != nil { diff --git a/internal/worker/factory_test.go b/internal/worker/factory_test.go index be6261b..607de51 100644 --- a/internal/worker/factory_test.go +++ b/internal/worker/factory_test.go @@ -15,7 +15,6 @@ import ( "github.com/fystack/multichain-indexer/pkg/events" "github.com/fystack/multichain-indexer/pkg/infra" "github.com/fystack/multichain-indexer/pkg/store/blockstore" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" ) @@ -211,12 +210,9 @@ func initTestLogger() { }) } -func (noopKVStore) GetName() string { return "noop" } -func (noopKVStore) Set(string, string) error { return nil } -func (noopKVStore) Get(string) (string, error) { return "", errors.New("not found") } -func (noopKVStore) GetWithOptions(string, *api.QueryOptions) (string, error) { - return "", errors.New("not found") -} +func (noopKVStore) GetName() string { return "noop" } +func (noopKVStore) Set(string, string) error { return nil } +func (noopKVStore) Get(string) (string, error) { return "", errors.New("not found") } func (noopKVStore) SetAny(string, any) error { return nil } func (noopKVStore) GetAny(string, any) (bool, error) { return false, nil } func (noopKVStore) List(string) ([]*infra.KVPair, error) { return nil, nil } @@ -224,12 +220,9 @@ func (noopKVStore) Delete(string) error { return nil } func (noopKVStore) BatchSet([]infra.KVPair) error { return nil } func (noopKVStore) Close() error { return nil } -func (s *listKVStore) GetName() string { return "list" } -func (s *listKVStore) Set(string, string) error { return nil } -func (s *listKVStore) Get(string) (string, error) { return "", errors.New("not found") } -func (s *listKVStore) GetWithOptions(string, *api.QueryOptions) (string, error) { - return "", errors.New("not found") -} +func (s *listKVStore) GetName() string { return "list" } +func (s *listKVStore) Set(string, string) error { return nil } +func (s *listKVStore) Get(string) (string, error) { return "", errors.New("not found") } func (s *listKVStore) SetAny(string, any) error { return nil } func (s *listKVStore) GetAny(string, any) (bool, error) { return false, nil } func (s *listKVStore) Delete(string) error { return nil } diff --git a/pkg/common/config/services.go b/pkg/common/config/services.go index 143bb43..dc841b3 100644 --- a/pkg/common/config/services.go +++ b/pkg/common/config/services.go @@ -51,21 +51,14 @@ type RedisConfig struct { type KVSConfig struct { Type enum.KVStoreType `yaml:"type"` - Consul ConsulConfig `yaml:"consul"` + Redis KVSRedisConfig `yaml:"redis"` Badger BadgerConfig `yaml:"badger"` } -type ConsulConfig struct { - Scheme string `yaml:"scheme"` - Address string `yaml:"address"` - Folder string `yaml:"folder"` - Token string `yaml:"token"` - HttpAuth HttpAuthConfig `yaml:"http_auth"` -} - -type HttpAuthConfig struct { - Username string `yaml:"username"` - Password string `yaml:"password"` +// KVSRedisConfig configures the Redis-backed KVStore. It reuses the shared +// Redis connection (services.redis); only the key namespace is set here. +type KVSRedisConfig struct { + Prefix string `yaml:"prefix"` } type BadgerConfig struct { @@ -74,12 +67,12 @@ type BadgerConfig struct { } type BloomfilterConfig struct { - Type enum.BFType `yaml:"type"` - WalletAddressRepo string `yaml:"wallet_address_repo"` - BatchSize int `yaml:"batch_size"` - Redis RedisBFConfig `yaml:"redis"` - InMemory InMemoryConfig `yaml:"in_memory"` - Sync BloomSyncConfig `yaml:"sync"` + Type enum.BFType `yaml:"type"` + WalletAddressRepo string `yaml:"wallet_address_repo"` + BatchSize int `yaml:"batch_size"` + Redis RedisBFConfig `yaml:"redis"` + InMemory InMemoryConfig `yaml:"in_memory"` + Sync BloomSyncConfig `yaml:"sync"` } type BloomSyncConfig struct { diff --git a/pkg/common/enum/enum.go b/pkg/common/enum/enum.go index e58e799..f4e68f8 100644 --- a/pkg/common/enum/enum.go +++ b/pkg/common/enum/enum.go @@ -51,5 +51,5 @@ const ( const ( KVStoreTypeBadger KVStoreType = "badger" - KVStoreTypeConsul KVStoreType = "consul" + KVStoreTypeRedis KVStoreType = "redis" ) diff --git a/pkg/infra/kvstore.go b/pkg/infra/kvstore.go index cc22af6..304d2af 100644 --- a/pkg/infra/kvstore.go +++ b/pkg/infra/kvstore.go @@ -4,12 +4,10 @@ import ( "bytes" "encoding/gob" "encoding/json" - - "github.com/hashicorp/consul/api" ) // KVStore is an interface for key-value stores. -// There are multiple implementations available like Consul, Postgres, Redis, BoltDB, BadgerDB, etcd, etc. +// There are multiple implementations available like Redis, BadgerDB, Postgres, etcd, etc. type KVPair struct { Key string @@ -20,7 +18,6 @@ type KVStore interface { GetName() string Set(k string, v string) error Get(k string) (v string, err error) - GetWithOptions(k string, queryOptions *api.QueryOptions) (v string, err error) // This method if you want to set v as struct or map SetAny(k string, v any) error GetAny(k string, v any) (found bool, err error) diff --git a/pkg/kvstore/badger.go b/pkg/kvstore/badger.go index fd13d4a..431e7df 100644 --- a/pkg/kvstore/badger.go +++ b/pkg/kvstore/badger.go @@ -6,7 +6,6 @@ import ( "github.com/dgraph-io/badger/v4" "github.com/fystack/multichain-indexer/pkg/common/enum" "github.com/fystack/multichain-indexer/pkg/infra" - "github.com/hashicorp/consul/api" ) type BadgerStore struct { @@ -78,11 +77,6 @@ func (b *BadgerStore) Set(key string, value string) error { }) } -// GetWithOptions is provided for interface parity; options are ignored for Badger. -func (b *BadgerStore) GetWithOptions(key string, _ *api.QueryOptions) (string, error) { - return b.Get(key) -} - func (b *BadgerStore) SetAny(key string, value any) error { if err := checkKeyAndValue(key, value); err != nil { return err diff --git a/pkg/kvstore/consul.go b/pkg/kvstore/consul.go deleted file mode 100644 index 7cf260c..0000000 --- a/pkg/kvstore/consul.go +++ /dev/null @@ -1,360 +0,0 @@ -package kvstore - -// This is a modified version of :https://github.com/philippgille/gokv/consul -// With extended functionalities: -// Get, Set k,v as string -// GetAny, SetAny, k: string, v: any -// List all keys with prefix - -import ( - "errors" - "fmt" - "time" - - "github.com/fystack/multichain-indexer/pkg/common/enum" - "github.com/fystack/multichain-indexer/pkg/infra" - "github.com/hashicorp/consul/api" -) - -var ( - ErrKeyNotFound = errors.New("key not found") - ErrKeyEmpty = errors.New("key is empty") -) - -var DefaultCacheOptions = api.QueryOptions{ - UseCache: true, - MaxAge: 30 * time.Minute, - StaleIfError: 5 * time.Minute, -} - -// CheckKeyAndValue returns an error if k == "" or if v == nil -func checkKeyAndValue(k string, v any) error { - if k == "" { - return ErrKeyEmpty - } - - if v == nil { - return errors.New("the passed value is nil, which is not allowed") - } - - return nil -} - -// ConsulClient implement infra.KVStore -type ConsulClient struct { - client *api.Client - kv *api.KV - folder string - codec infra.Codec -} - -func (c ConsulClient) GetName() string { - return string(enum.KVStoreTypeConsul) -} - -func (c ConsulClient) Set(k string, v string) error { - if err := checkKeyAndValue(k, v); err != nil { - return err - } - - if c.folder != "" { - k = c.folder + "/" + k - } - kvPair := api.KVPair{ - Key: k, - Value: []byte(v), - } - _, err := c.kv.Put(&kvPair, nil) - if err != nil { - return err - } - - return nil -} - -// Get retrieves the stored value for the given key. -func (c ConsulClient) Get(k string) (v string, err error) { - if k == "" { - return "", ErrKeyEmpty - } - - if c.folder != "" { - k = c.folder + "/" + k - } - kvPair, _, err := c.kv.Get(k, nil) - if err != nil { - return "", err - } - // If no value was found return false - if kvPair == nil { - return "", ErrKeyNotFound - } - data := kvPair.Value - return string(data), err -} - -// Get retrieves the stored value for the given key with caching options. -func (c ConsulClient) GetWithOptions( - k string, - queryOptions *api.QueryOptions, -) (v string, err error) { - if k == "" { - return "", ErrKeyEmpty - } - - if c.folder != "" { - k = c.folder + "/" + k - } - - // Use the provided QueryOptions to control caching - kvPair, _, err := c.kv.Get(k, queryOptions) - if err != nil { - return "", err - } - - // If no value was found, return an error - if kvPair == nil { - return "", ErrKeyNotFound - } - - data := kvPair.Value - return string(data), nil -} - -// Set stores the given value for the given key. -// Values are automatically marshalled to JSON or gob (depending on the configuration). -// The key must not be "" and the value must not be nil. -func (c ConsulClient) SetAny(k string, v any) error { - if err := checkKeyAndValue(k, v); err != nil { - return err - } - - // First turn the passed object into something that Consul can handle - data, err := c.codec.Marshal(v) - if err != nil { - return err - } - - if c.folder != "" { - k = c.folder + "/" + k - } - kvPair := api.KVPair{ - Key: k, - Value: data, - } - _, err = c.kv.Put(&kvPair, nil) - if err != nil { - return err - } - - return nil -} - -// Get retrieves the stored value for the given key. -// You need to pass a pointer to the value, so in case of a struct -// the automatic unmarshalling can populate the fields of the object -// that v points to with the values of the retrieved object's values. -// If no value is found it returns (false, nil). -// The key must not be "" and the pointer must not be nil. -func (c ConsulClient) GetAny(k string, v any) (found bool, err error) { - if err := checkKeyAndValue(k, v); err != nil { - return false, err - } - - if c.folder != "" { - k = c.folder + "/" + k - } - kvPair, _, err := c.kv.Get(k, nil) - if err != nil { - return false, err - } - // If no value was found return false - if kvPair == nil { - return false, nil - } - data := kvPair.Value - return true, c.codec.Unmarshal(data, v) -} - -func (c ConsulClient) List(prefix string) ([]*infra.KVPair, error) { - if prefix == "" { - return nil, errors.New("prefix is empty") - } - - if c.folder != "" { - prefix = c.folder + "/" + prefix - } - - kvPairs, _, err := c.kv.List(prefix, nil) - if err != nil { - return nil, err - } - - result := make([]*infra.KVPair, len(kvPairs)) - for i, kvPair := range kvPairs { - result[i] = &infra.KVPair{ - Key: kvPair.Key, - Value: kvPair.Value, - } - } - - return result, nil -} - -// BatchSet writes multiple key-value pairs atomically using Consul's Transaction API. -// Consul limits transactions to 64 operations, so larger batches are chunked. -func (c ConsulClient) BatchSet(pairs []infra.KVPair) error { - if len(pairs) == 0 { - return nil - } - - const maxOpsPerTxn = 64 - - for i := 0; i < len(pairs); i += maxOpsPerTxn { - end := i + maxOpsPerTxn - if end > len(pairs) { - end = len(pairs) - } - chunk := pairs[i:end] - - ops := make(api.TxnOps, 0, len(chunk)) - for _, p := range chunk { - key := p.Key - if c.folder != "" { - key = c.folder + "/" + key - } - ops = append(ops, &api.TxnOp{ - KV: &api.KVTxnOp{ - Verb: api.KVSet, - Key: key, - Value: p.Value, - }, - }) - } - - ok, resp, _, err := c.client.Txn().Txn(ops, nil) - if err != nil { - return fmt.Errorf("consul batch set failed: %w", err) - } - if !ok && resp != nil && len(resp.Errors) > 0 { - return fmt.Errorf("consul txn rejected: %s", resp.Errors[0].What) - } - } - - return nil -} - -// Delete deletes the stored value for the given key. -// Deleting a non-existing key-value pair does NOT lead to an error. -// The key must not be "". -func (c ConsulClient) Delete(k string) error { - if k == "" { - return ErrKeyEmpty - } - - if c.folder != "" { - k = c.folder + "/" + k - } - _, err := c.kv.Delete(k, nil) - return err -} - -// Close closes the client. -// In the Consul implementation this doesn't have any effect. -func (c ConsulClient) Close() error { - return nil -} - -// Options are the options for the Consul client. -type Options struct { - // URI scheme for the Consul server. - // Optional ("http" by default). - Scheme string - // Address of the Consul server, including port number. - // Optional ("127.0.0.1:8500" by default). - Address string - // Directory under which to store the key-value pairs. - // The Consul UI calls this "folder". - // Optional (none by default). - Folder string - // Encoding format. - // Optional (encoding.JSON by default). - Codec infra.Codec - - // Client token - Token string - HttpAuth *api.HttpBasicAuth -} - -// DefaultConsulOptions is an Options object with default values. -// Scheme: "http", Address: "127.0.0.1:8500", Folder: none, Codec: encoding.JSON -var DefaultConsulOptions = Options{ - Scheme: "http", - Address: "127.0.0.1:8500", - Codec: infra.JSON, - // No need to define Folder because its zero value is fine -} - -// func GetConsulOptions(environment string) Options { -// if environment != constant.EnvProduction { -// options := DefaultConsulOptions -// options.Address = viper.GetString("consul.address") -// return options -// } - -// return Options{ -// Scheme: "https", -// Address: viper.GetString("consul.address"), -// Token: viper.GetString("consul.token"), -// HttpAuth: &api.HttpBasicAuth{ -// Username: viper.GetString("consul.username"), -// Password: viper.GetString("consul.password"), -// }, -// } -// } - -// NewClient creates a new Consul client. -func NewConsulClient(options Options) (infra.KVStore, error) { - result := ConsulClient{} - - // Set default values - if options.Scheme == "" { - options.Scheme = DefaultConsulOptions.Scheme - } - if options.Address == "" { - options.Address = DefaultConsulOptions.Address - } - if options.Codec == nil { - options.Codec = DefaultConsulOptions.Codec - } - - config := api.DefaultConfig() - config.Scheme = options.Scheme - config.Address = options.Address - // Add connection timeout - config.WaitTime = 10 * time.Second - if options.Token != "" { - config.Token = options.Token - } - if options.HttpAuth != nil { - config.HttpAuth = options.HttpAuth - } - - client, err := api.NewClient(config) - if err != nil { - return result, err - } - - // Ping the Consul server to verify connectivity - _, err = client.Status().Leader() - if err != nil { - return result, fmt.Errorf("failed to connect to Consul: %w", err) - } - - result.client = client - result.kv = client.KV() - result.folder = options.Folder - result.codec = options.Codec - - return result, nil -} diff --git a/pkg/kvstore/factory.go b/pkg/kvstore/factory.go index efcea1e..37d11d4 100644 --- a/pkg/kvstore/factory.go +++ b/pkg/kvstore/factory.go @@ -6,26 +6,21 @@ import ( "github.com/fystack/multichain-indexer/pkg/common/config" "github.com/fystack/multichain-indexer/pkg/common/enum" "github.com/fystack/multichain-indexer/pkg/infra" - "github.com/hashicorp/consul/api" + "github.com/redis/go-redis/v9" ) // NewFromConfig constructs an infra.KVStore based on kvstore configuration. -func NewFromConfig(cfg config.KVSConfig) (infra.KVStore, error) { +// redisClient is required for the redis backend and ignored otherwise (it may +// be nil when only the badger backend is used). +func NewFromConfig(cfg config.KVSConfig, redisClient *redis.Client) (infra.KVStore, error) { switch cfg.Type { case enum.KVStoreTypeBadger: return NewBadgerStore(cfg.Badger.Directory, cfg.Badger.Prefix, infra.JSON) - case enum.KVStoreTypeConsul: - return NewConsulClient(Options{ - Scheme: cfg.Consul.Scheme, - Address: cfg.Consul.Address, - Folder: cfg.Consul.Folder, - Codec: infra.JSON, - Token: cfg.Consul.Token, - HttpAuth: &api.HttpBasicAuth{ - Username: cfg.Consul.HttpAuth.Username, - Password: cfg.Consul.HttpAuth.Password, - }, - }) + case enum.KVStoreTypeRedis: + if redisClient == nil { + return nil, fmt.Errorf("redis kvstore requires a redis client") + } + return NewRedisStore(redisClient, cfg.Redis.Prefix, infra.JSON) default: return nil, fmt.Errorf("unsupported kvstore type: %s", cfg.Type) } diff --git a/pkg/kvstore/kvstore.go b/pkg/kvstore/kvstore.go new file mode 100644 index 0000000..0cbaed8 --- /dev/null +++ b/pkg/kvstore/kvstore.go @@ -0,0 +1,19 @@ +package kvstore + +import "errors" + +var ( + ErrKeyNotFound = errors.New("key not found") + ErrKeyEmpty = errors.New("key is empty") +) + +// checkKeyAndValue returns an error if k == "" or if v == nil. +func checkKeyAndValue(k string, v any) error { + if k == "" { + return ErrKeyEmpty + } + if v == nil { + return errors.New("the passed value is nil, which is not allowed") + } + return nil +} diff --git a/pkg/kvstore/redis.go b/pkg/kvstore/redis.go new file mode 100644 index 0000000..111dbf8 --- /dev/null +++ b/pkg/kvstore/redis.go @@ -0,0 +1,194 @@ +package kvstore + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/fystack/multichain-indexer/pkg/common/enum" + "github.com/fystack/multichain-indexer/pkg/infra" + "github.com/redis/go-redis/v9" +) + +const redisOpTimeout = 5 * time.Second + +// RedisStore implements infra.KVStore on top of a Redis client. +type RedisStore struct { + client *redis.Client + prefix string + codec infra.Codec +} + +func NewRedisStore(client *redis.Client, prefix string, codec infra.Codec) (*RedisStore, error) { + if client == nil { + return nil, errors.New("redis client is nil") + } + if codec == nil { + codec = infra.JSON + } + ctx, cancel := context.WithTimeout(context.Background(), redisOpTimeout) + defer cancel() + if err := client.Ping(ctx).Err(); err != nil { + return nil, fmt.Errorf("failed to connect to Redis: %w", err) + } + return &RedisStore{client: client, prefix: prefix, codec: codec}, nil +} + +func (r *RedisStore) fullKey(k string) string { + if r.prefix != "" { + return r.prefix + "/" + k + } + return k +} + +func ctxTimeout() (context.Context, context.CancelFunc) { + return context.WithTimeout(context.Background(), redisOpTimeout) +} + +func (r *RedisStore) GetName() string { + return string(enum.KVStoreTypeRedis) +} + +func (r *RedisStore) Set(k string, v string) error { + if err := checkKeyAndValue(k, v); err != nil { + return err + } + ctx, cancel := ctxTimeout() + defer cancel() + return r.client.Set(ctx, r.fullKey(k), v, 0).Err() +} + +func (r *RedisStore) Get(k string) (string, error) { + if k == "" { + return "", ErrKeyEmpty + } + ctx, cancel := ctxTimeout() + defer cancel() + v, err := r.client.Get(ctx, r.fullKey(k)).Result() + if errors.Is(err, redis.Nil) { + return "", ErrKeyNotFound + } + if err != nil { + return "", err + } + return v, nil +} + +func (r *RedisStore) SetAny(k string, v any) error { + if err := checkKeyAndValue(k, v); err != nil { + return err + } + data, err := r.codec.Marshal(v) + if err != nil { + return err + } + ctx, cancel := ctxTimeout() + defer cancel() + return r.client.Set(ctx, r.fullKey(k), data, 0).Err() +} + +func (r *RedisStore) GetAny(k string, v any) (bool, error) { + if err := checkKeyAndValue(k, v); err != nil { + return false, err + } + ctx, cancel := ctxTimeout() + defer cancel() + data, err := r.client.Get(ctx, r.fullKey(k)).Bytes() + if errors.Is(err, redis.Nil) { + return false, nil + } + if err != nil { + return false, err + } + return true, r.codec.Unmarshal(data, v) +} + +// List returns all key-value pairs whose key starts with prefix. It uses SCAN +// (non-blocking, cursor-based) rather than KEYS to avoid stalling Redis. +func (r *RedisStore) List(prefix string) ([]*infra.KVPair, error) { + if prefix == "" { + return nil, errors.New("prefix is empty") + } + ctx, cancel := ctxTimeout() + defer cancel() + + match := r.fullKey(prefix) + "*" + seen := make(map[string]struct{}) + keys := make([]string, 0) + var cursor uint64 + for { + batch, next, err := r.client.Scan(ctx, cursor, match, 100).Result() + if err != nil { + return nil, err + } + for _, k := range batch { + if _, ok := seen[k]; ok { + // SCAN may return duplicate keys across cursor iterations. + continue + } + seen[k] = struct{}{} + keys = append(keys, k) + } + cursor = next + if cursor == 0 { + break + } + } + if len(keys) == 0 { + return nil, nil + } + + values, err := r.client.MGet(ctx, keys...).Result() + if err != nil { + return nil, err + } + result := make([]*infra.KVPair, 0, len(keys)) + for i, k := range keys { + raw := values[i] + if raw == nil { + continue + } + s, ok := raw.(string) + if !ok { + continue + } + result = append(result, &infra.KVPair{Key: k, Value: []byte(s)}) + } + return result, nil +} + +// BatchSet writes multiple key-value pairs in a single pipeline. Unlike Consul's +// transaction API this is not atomic, which is acceptable for the idempotent +// state (catchup ranges) written through it. +func (r *RedisStore) BatchSet(pairs []infra.KVPair) error { + if len(pairs) == 0 { + return nil + } + ctx, cancel := ctxTimeout() + defer cancel() + + pipe := r.client.Pipeline() + for _, p := range pairs { + pipe.Set(ctx, r.fullKey(p.Key), p.Value, 0) + } + _, err := pipe.Exec(ctx) + if err != nil { + return fmt.Errorf("redis batch set failed: %w", err) + } + return nil +} + +func (r *RedisStore) Delete(k string) error { + if k == "" { + return ErrKeyEmpty + } + ctx, cancel := ctxTimeout() + defer cancel() + return r.client.Del(ctx, r.fullKey(k)).Err() +} + +// Close is a no-op: the underlying Redis client is shared and closed by its owner. +func (r *RedisStore) Close() error { + return nil +} diff --git a/pkg/store/blockstore/store_test.go b/pkg/store/blockstore/store_test.go index 142ecfa..618ad61 100644 --- a/pkg/store/blockstore/store_test.go +++ b/pkg/store/blockstore/store_test.go @@ -6,7 +6,6 @@ import ( "github.com/fystack/multichain-indexer/pkg/infra" "github.com/fystack/multichain-indexer/pkg/kvstore" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -25,13 +24,9 @@ func (f *fakeKVStore) GetAny(string, any) (bool, error) { return false, nil } func (f *fakeKVStore) List(string) ([]*infra.KVPair, error) { return nil, nil } -func (f *fakeKVStore) Delete(string) error { return nil } +func (f *fakeKVStore) Delete(string) error { return nil } func (f *fakeKVStore) BatchSet([]infra.KVPair) error { return nil } -func (f *fakeKVStore) Close() error { return nil } - -func (f *fakeKVStore) GetWithOptions(string, *api.QueryOptions) (string, error) { - return f.getVal, f.getErr -} +func (f *fakeKVStore) Close() error { return nil } func TestGetLatestBlock_MissingKeyReturnsZeroNoError(t *testing.T) { bs := NewBlockStore(&fakeKVStore{getErr: kvstore.ErrKeyNotFound}) From 95ca241b840071067363d6cbd9b18ea2a1deaa3d Mon Sep 17 00:00:00 2001 From: vietddude Date: Thu, 3 Sep 2026 11:40:59 +0700 Subject: [PATCH 2/2] fix(catchup): don't backfill from genesis on a fresh start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the KV store has no persisted latest block (fresh start), GetLatestBlock returns 0 and the catchup worker created a range spanning block 1 to the chain head — millions of sub-ranges from genesis. A zero latest means no indexed position: the regular worker starts at chain head and queues real gaps into the store, so only create a catchup range when latest > 0. --- internal/worker/catchup.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/worker/catchup.go b/internal/worker/catchup.go index 910ddd0..155b2f9 100644 --- a/internal/worker/catchup.go +++ b/internal/worker/catchup.go @@ -164,9 +164,11 @@ func (cw *CatchupWorker) loadCatchupProgress() []blockstore.CatchupRange { ) } - // Only create a new range if no existing ranges found + // Only create a new range if no existing ranges found. A zero latest means a + // fresh start with no indexed position — do NOT backfill from genesis; the + // regular worker starts at chain head and queues real gaps into the store. if len(ranges) == 0 { - if latest, err1 := cw.blockStore.GetLatestBlock(cw.chain.GetNetworkInternalCode()); err1 == nil { + if latest, err1 := cw.blockStore.GetLatestBlock(cw.chain.GetNetworkInternalCode()); err1 == nil && latest > 0 { if head, err2 := cw.chain.GetLatestBlockNumber(cw.ctx); err2 == nil && head > latest { if head <= latest { // no gap between head and latest