pubsub: bump consensus, Corona→Corona field, fix raft
- Bump github.com/luxfi/consensus v1.22.84 → v1.25.17 (go mod tidy clean). - Migrate internal/consensus to current quasar API: QuasarHybridConsensus→Quasar, HybridSignature→QuasarSig, NewQuasarHybridConsensus→NewQuasar, VerifyHybridSignature→VerifyQuasarSig. Signature field is .Corona (no Corona). Route threshold<2 (single-node/dev) to NewTestQuasar, >=2 to NewQuasar (BFT), matching upstream's two-constructor production-vs-test split. - Repair botched upstream merge in server/: restore corrupted files from the upstream merge parent with Hanzo import paths preserved. Fixes RaftNodeCheckpoint redeclaration, undefined gsl/standalone/numLimitableConsumers, dropped consumer ack-format fields, resetStartingSeq arity, filestore IntersectGSL bool callbacks and fetchMsgNoCopyLocked, jetstream_cluster creatingRaftGroups/wca.State. go build ./... green; internal tests pass.
This commit is contained in:
@@ -7,69 +7,85 @@ require (
|
||||
github.com/google/go-tpm v0.9.8
|
||||
github.com/hanzoai/pubsub-go v1.0.1-0.20260513042624-1b25bdfe16a6
|
||||
github.com/klauspost/compress v1.18.5
|
||||
github.com/luxfi/age v1.4.0
|
||||
github.com/luxfi/consensus v1.22.84
|
||||
github.com/luxfi/age v1.5.0
|
||||
github.com/luxfi/consensus v1.25.17
|
||||
github.com/luxfi/zap v0.8.1
|
||||
github.com/luxfi/zapdb v1.0.0
|
||||
github.com/luxfi/zapdb v1.10.0
|
||||
github.com/nats-io/jwt/v2 v2.8.1
|
||||
github.com/nats-io/nats-server/v2 v2.12.3
|
||||
github.com/nats-io/nats.go v1.50.0
|
||||
github.com/nats-io/nkeys v0.4.15
|
||||
github.com/nats-io/nuid v1.0.1
|
||||
github.com/zap-proto/go v0.2.0
|
||||
golang.org/x/crypto v0.52.0
|
||||
golang.org/x/sys v0.45.0
|
||||
golang.org/x/time v0.15.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
filippo.io/hpke v0.4.0 // indirect
|
||||
github.com/ALTree/bigfloat v0.2.0 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/consensys/gnark-crypto v0.20.1 // indirect
|
||||
github.com/cronokirby/saferith v0.33.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/rpc v1.2.1 // indirect
|
||||
github.com/grandcat/zeroconf v1.0.0 // indirect
|
||||
github.com/gtank/merlin v0.1.1 // indirect
|
||||
github.com/gtank/ristretto255 v0.2.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/luxfi/accel v1.1.9 // indirect
|
||||
github.com/luxfi/cache v1.2.1 // indirect
|
||||
github.com/luxfi/accel v1.2.2 // indirect
|
||||
github.com/luxfi/codec v1.1.4 // indirect
|
||||
github.com/luxfi/corona v0.7.6 // indirect
|
||||
github.com/luxfi/crypto v1.19.17 // indirect
|
||||
github.com/luxfi/crypto/ipa v1.2.4 // indirect
|
||||
github.com/luxfi/ids v1.2.9 // indirect
|
||||
github.com/luxfi/lattice/v7 v7.0.0 // indirect
|
||||
github.com/luxfi/ids v1.2.10 // indirect
|
||||
github.com/luxfi/lattice/v7 v7.1.4 // indirect
|
||||
github.com/luxfi/lens v0.1.4 // indirect
|
||||
github.com/luxfi/log v1.4.1 // indirect
|
||||
github.com/luxfi/magnetar v1.2.0 // indirect
|
||||
github.com/luxfi/math v1.4.1 // indirect
|
||||
github.com/luxfi/mdns v0.1.1 // indirect
|
||||
github.com/luxfi/metric v1.5.7 // indirect
|
||||
github.com/luxfi/mock v0.1.1 // indirect
|
||||
github.com/luxfi/corona v0.2.0 // indirect
|
||||
github.com/luxfi/utils v1.1.4 // indirect
|
||||
github.com/luxfi/pulsar v1.1.1 // indirect
|
||||
github.com/luxfi/threshold v1.9.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.100 // indirect
|
||||
github.com/montanaflynn/stats v0.8.2 // indirect
|
||||
github.com/montanaflynn/stats v0.9.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
github.com/tinylib/msgp v1.6.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
|
||||
@@ -79,6 +95,7 @@ require (
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAwLGRZ+sebTUruuM4Ra3M=
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A=
|
||||
filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY=
|
||||
github.com/ALTree/bigfloat v0.2.0 h1:AwNzawrpFuw55/YDVlcPw0F0cmmXrmngBHhVrvdXPvM=
|
||||
github.com/ALTree/bigfloat v0.2.0/go.mod h1:+NaH2gLeY6RPBPPQf4aRotPPStg+eXc8f9ZaE4vRfD4=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260215031811-a0ab0b218a81 h1:TBzelXBdnzDy+HCrBMcomEnhrmigkWOI1/mIPCi2u4M=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260215031811-a0ab0b218a81/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ChainSafe/go-schnorrkel v1.1.0 h1:rZ6EU+CZFCjB4sHUE1jIu8VDoB/wRKZxoe1tkcO71Wk=
|
||||
github.com/ChainSafe/go-schnorrkel v1.1.0/go.mod h1:ABkENxiP+cvjFiByMIZ9LYbRoNNLeBLiakC1XeTFxfE=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0 h1:uWDG8BqLD1lI2ps38WDz2vXflrTX2+vLX0SvZtztJtE=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM=
|
||||
github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
|
||||
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
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/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA=
|
||||
github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
|
||||
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
|
||||
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
|
||||
github.com/consensys/gnark-crypto v0.20.1 h1:PXDUBvk8AzhvWowHLWBEAfUQcV1/aZgWIqD6eMpXmDg=
|
||||
github.com/consensys/gnark-crypto v0.20.1/go.mod h1:RBWrSgy+IDbGR69RRV313th3M/aZU1ubk2om+qHuTSc=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||
github.com/cronokirby/saferith v0.33.0 h1:TgoQlfsD4LIwx71+ChfRcIpjkw+RPOapDEVxa+LhwLo=
|
||||
github.com/cronokirby/saferith v0.33.0/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7kthcD7UzbnoA=
|
||||
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/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU=
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
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/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -47,18 +45,26 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
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/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
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/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw=
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
||||
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/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
|
||||
github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptRfGk=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/gtank/ristretto255 v0.2.0 h1:LeOuWr6giplWkkMizx2emfG03SRPJqKt1nfIHLVHQ/0=
|
||||
github.com/gtank/ristretto255 v0.2.0/go.mod h1:OJ1ox/dWcp7sJ5grYDcZ+kkHYuj5nelW5aaL7ESVXBw=
|
||||
github.com/hanzoai/pubsub-go v1.0.1-0.20260513042624-1b25bdfe16a6 h1:3n1OhMquvkAb6jVFl3t819+dGOAK4QrX0aN+903OAbU=
|
||||
github.com/hanzoai/pubsub-go v1.0.1-0.20260513042624-1b25bdfe16a6/go.mod h1:18E+ko7z8kBNUlph5+KIAUFrqxt9PRo5BROlIO3AZxI=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
@@ -68,61 +74,64 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/luxfi/accel v1.0.7 h1:ksHieAp50umwqxqgyHk9WiOmXM54kia3IEDb5H7FsM8=
|
||||
github.com/luxfi/accel v1.0.7/go.mod h1:iZD3oxffiMEIT/KvzD8bgwC/cBn4AYlMW3QJpbRa4RE=
|
||||
github.com/luxfi/accel v1.1.9 h1:Tsk6gXj2uKE19501bD0ajRYdeCHIlTGb6jYyLc+F8hc=
|
||||
github.com/luxfi/accel v1.1.9/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
|
||||
github.com/luxfi/age v1.4.0 h1:tU5q65RQSQdaVq64Z/DVUhiPQMoMPQT6pr41LsvG0AQ=
|
||||
github.com/luxfi/age v1.4.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
|
||||
github.com/luxfi/cache v1.1.0 h1:6LUyGGZ+rrMAJBbAU6+UwkcamXj3zsboRUodIof2Ong=
|
||||
github.com/luxfi/cache v1.1.0/go.mod h1:9GvlEEE9rFPaaWxvVpSPwW8ZMo2+8VMNNcuPa4AwzPg=
|
||||
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
|
||||
github.com/luxfi/consensus v1.22.0 h1:zMUM8OUm+Pheuv9ln+/H/zK6Sm2TU9wUwqZiB4NKqlk=
|
||||
github.com/luxfi/consensus v1.22.0/go.mod h1:l8ZM8xMlljmq8YnIrJRm61Ed/bwxTsOY4B+tLsyjufY=
|
||||
github.com/luxfi/consensus v1.22.84 h1:fKw4n7fY+lCWW5HSx/ciGeRSxv4knODQGWh6lBWAveQ=
|
||||
github.com/luxfi/consensus v1.22.84/go.mod h1:y6y+bVjvpDLkkTBCCKLcch6R4PRW8EA+lTfr+6sB6F8=
|
||||
github.com/luxfi/crypto v1.17.4 h1:JcFaMC9EHjx/47XypQjl2woAysztl7a9kMsDY+pQh8I=
|
||||
github.com/luxfi/crypto v1.17.4/go.mod h1:Y+TkHDuHXe2vlwNlwkdI5TYds6C2d4OZcvmXjcMRZCQ=
|
||||
github.com/luxfi/crypto v1.19.0 h1:VtH6kvZrCEjCnkHPkhExDU+GJ0ZulYX4rnA+lXJdJHw=
|
||||
github.com/luxfi/crypto v1.19.0/go.mod h1:ee525i8Recbpb0jVTDZYZBr1MmvJ27OITJHZ/nlNMBw=
|
||||
github.com/luxfi/accel v1.2.2 h1:IzG1qUogapr4/KbbHP9avAimu3ioGJqh67WYKHxfZys=
|
||||
github.com/luxfi/accel v1.2.2/go.mod h1:5IvpNMfw9FgeBB9oobp+jbauAdhapfhc2bHYPUsHL9Y=
|
||||
github.com/luxfi/age v1.5.0 h1:G69HbSV4R3vKEH9B0CulnRaMdSdf4RalMgP8xKmxHeI=
|
||||
github.com/luxfi/age v1.5.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
|
||||
github.com/luxfi/codec v1.1.4 h1:Yl8ZalMNkqo7cD6R9AjczAajkLOmsjyZ9+DASVYHrvg=
|
||||
github.com/luxfi/codec v1.1.4/go.mod h1:oGQ3j6E8c2P0pL0irYtWkrB1hmDUFIE0puXHK4gV5KI=
|
||||
github.com/luxfi/consensus v1.25.17 h1:4EvtruOZSiInByIN9miha0SHlxGNlI5S59lhE1xa3LU=
|
||||
github.com/luxfi/consensus v1.25.17/go.mod h1:lvyDsuqWSDxwc0keI84PccZi8sRFu5zn+LAcPt79oIg=
|
||||
github.com/luxfi/corona v0.7.6 h1:CJP6smygD55dL0HHkKkWryL9H24a+wXvs+L+WchK7Nc=
|
||||
github.com/luxfi/corona v0.7.6/go.mod h1:4aD7+ZqnlZ2aVuU/DBQ5aspIagv5ux45LW2sJ4+siY8=
|
||||
github.com/luxfi/crypto v1.19.17 h1:l2LLu7UFyICtJVfraLDLRi+lFGiDXKHSL18M9/m1gsQ=
|
||||
github.com/luxfi/crypto v1.19.17/go.mod h1:INjdZtke85k8hX/QAmTMAY8bbZ4gzGZQLqURg3xf6Gk=
|
||||
github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xfU=
|
||||
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
|
||||
github.com/luxfi/ids v1.1.2 h1:+qCUzE9Ga4slSHbnYl7T3I6c8y+n4/kKk4rzSEkLv/k=
|
||||
github.com/luxfi/ids v1.1.2/go.mod h1:cMbto3Q8N3IaBxdz4Lzaq9wYl33coGXUdlr2+YwXeUw=
|
||||
github.com/luxfi/ids v1.2.9 h1:+yjdhXW99drnd2Zlp1u/p8k3G23W3/1btJQ4ogHawUI=
|
||||
github.com/luxfi/ids v1.2.9/go.mod h1:khJOEdOPxd22yn0jcVrnbX1ADa0GHn5Y74gvCzN5BYc=
|
||||
github.com/luxfi/lattice/v7 v7.0.0 h1:d1vgan6mlb2KtwYfPc1g69uxVYaoVYZmlrIm4aCO88w=
|
||||
github.com/luxfi/lattice/v7 v7.0.0/go.mod h1:PFDdOkuGTQ0cbJMbKojzEJMGWUQmZW+wK9/wJ9F9fOs=
|
||||
github.com/luxfi/mdns v0.1.0 h1:VB3mQcETc9j5SY1S6lAgFtuGr/rjWuDgPYnxS+OKWMQ=
|
||||
github.com/luxfi/mdns v0.1.0/go.mod h1:/3dheKVjUk2yiS/ocH1IDzeLXOIe+kpVsErIGDOZdiQ=
|
||||
github.com/luxfi/ids v1.2.10 h1:f1WILZE199ayMuqnEyB2WP1qfMZkmozOQXSVYtB3e5k=
|
||||
github.com/luxfi/ids v1.2.10/go.mod h1:QBIwy3OHvrtskbUqKh1+OYRa6PsyR7f7oNX33sOfK7w=
|
||||
github.com/luxfi/lattice/v7 v7.1.4 h1:hQR02M6cHTAV5+joOPi9gb9Gm+z/hKJnhJF4IlciIJs=
|
||||
github.com/luxfi/lattice/v7 v7.1.4/go.mod h1:DmIQFi3mJiehVsR235l1NKYEU0JhU649OX5p7gMEW2c=
|
||||
github.com/luxfi/lens v0.1.4 h1:goGjGDXx2BNdjzXDunL5QT8elK2ZyCcc0z8TAbtWYrg=
|
||||
github.com/luxfi/lens v0.1.4/go.mod h1:mL+G8IK+9L41d78/2FYRgfhEzAjcr5+VEXB8SGuHbus=
|
||||
github.com/luxfi/log v1.4.1 h1:rIfFRodb9jrD/w7KayaUk0Oc+37PaQQdKEEMJCjR8gw=
|
||||
github.com/luxfi/log v1.4.1/go.mod h1:64IE3xRMJcpkQwnPUfJw3pDj7wU0kRS7BZ9wM7R72jk=
|
||||
github.com/luxfi/magnetar v1.2.0 h1:bsxHmBnJiswc/A6ElQ0pWz5g6ogqewIEKKqR26VgizA=
|
||||
github.com/luxfi/magnetar v1.2.0/go.mod h1:7J9YP9jByWbwCjssMFJNUkTU8tcPlSUoVSSiYShtvFs=
|
||||
github.com/luxfi/math v1.4.1 h1:1t9bCCsEqnl9yIKrShlbs80DBKyYTWdnzkVfBqEeO7Q=
|
||||
github.com/luxfi/math v1.4.1/go.mod h1:QvbRxauQyE1w4lvbcLSe6c8yeJz2Zj1Bq1rayGgs2tA=
|
||||
github.com/luxfi/mdns v0.1.1 h1:g2eRr9AXcziPkkcd24M+Qu9ApEpoKKjfI79QSNqv0rQ=
|
||||
github.com/luxfi/mdns v0.1.1/go.mod h1:dbp5f3h3aE7CGzwbaWzBM9cwdcekhmSrWhQevgYhhNA=
|
||||
github.com/luxfi/mock v0.1.0 h1:IwElfNu+T9sXvzFX6tudPDx1vqPuACRSRdxpD5lxW+o=
|
||||
github.com/luxfi/mock v0.1.0/go.mod h1:izF+9K0gGzFC9zERn6Po37v46eLdPB+EIsDjL3GLk+U=
|
||||
github.com/luxfi/metric v1.5.7 h1:LoSPEUpak2SLcynF+LT2cXjl9ECp4nY+Lia9zudmDv4=
|
||||
github.com/luxfi/metric v1.5.7/go.mod h1:CMguEhyuLi4YUWyXimJ+UHply99BDFrL0pxedB7rBqM=
|
||||
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
|
||||
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
|
||||
github.com/luxfi/corona v0.2.0 h1:DbLMZmE//2T2wXXS/gEkKZrTbre9LD+7EE/tz5SQszU=
|
||||
github.com/luxfi/corona v0.2.0/go.mod h1:SZ+aDLUdfSAtaTRaaYzeDZ5DNQiLaOCelSaIGjL21R0=
|
||||
github.com/luxfi/utils v1.1.0 h1:ti7HvjNwJd4ILDMERJtOAWE9mF8l+zqDVkgWnF7Agic=
|
||||
github.com/luxfi/utils v1.1.0/go.mod h1:ABqhBdGNig0CaDcnNYldv1byS0BTNi5IKPbJSPF1p98=
|
||||
github.com/luxfi/utils v1.1.4/go.mod h1:c3yz1RjzrB+cs5GZm+q1T3/2cCKElO9vxm9yRRtgSEM=
|
||||
github.com/luxfi/zap v0.2.0 h1:RzvOkp3EoN5UCkpnqfObHLM1sEHy7YcxXKuermhE/VA=
|
||||
github.com/luxfi/zap v0.2.0/go.mod h1:2hydPSa/XMCMtfW6/DC9M5Bt03N5h75QwCV5Vypsqr4=
|
||||
github.com/luxfi/pq v1.0.3 h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
|
||||
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/pulsar v1.1.1 h1:jo1jEgUsGiVxpT17Eg7Gw4Ax+07pbKFzq4NhHMLmDpI=
|
||||
github.com/luxfi/pulsar v1.1.1/go.mod h1:U7tPleeAHJ9dZ61ymtstzLKKoZjxM2zFeGZ+RSjHyRw=
|
||||
github.com/luxfi/threshold v1.9.4 h1:H69e9QkvygDtWkni1FkD5ztLdiTasmpJXcuVzgebdxo=
|
||||
github.com/luxfi/threshold v1.9.4/go.mod h1:o50rsc4H9fIXHuZXSf93qDFfRTXncXaXf1Q+6JAD1vU=
|
||||
github.com/luxfi/zap v0.8.1 h1:MiVRsRA808bXn6Orc81cjlGjqbn8gf7fr9aP5MPbLEM=
|
||||
github.com/luxfi/zap v0.8.1/go.mod h1:gUoQ/V9nTLLIb4/n0bY/L2zQDYK6pDBG121rK4t8bpg=
|
||||
github.com/luxfi/zapdb v1.0.0 h1:HFBlC8ka9RdP1MEXIGNNYg/0F6/Hu6O/OE6wKDtdk7g=
|
||||
github.com/luxfi/zapdb v1.0.0/go.mod h1:Qukh3hDRD0MnxA6z+a28JTnXhN85AiLLgp6TYr4QAMc=
|
||||
github.com/luxfi/zapdb/v4 v4.9.1 h1:oTg2ibBSxGtHTD9YHkclg5R1NecXWTi8R9XzY36uwgA=
|
||||
github.com/luxfi/zapdb/v4 v4.9.1/go.mod h1:gYJjLcFCxmUATZ6Zg7iZHwAbgBMxGVP1g8UOWomK70Y=
|
||||
github.com/luxfi/zapdb v1.10.0 h1:1lLHEmkyC0BucnA/zjQYsMkUVxuEo2vQkEaQGjYfuuc=
|
||||
github.com/luxfi/zapdb v1.10.0/go.mod h1:Qukh3hDRD0MnxA6z+a28JTnXhN85AiLLgp6TYr4QAMc=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU=
|
||||
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/highwayhash v1.0.4 h1:asJizugGgchQod2ja9NJlGOWq4s7KsAWr5XUc9Clgl4=
|
||||
@@ -131,8 +140,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.100 h1:ShkWi8Tyj9RtU57OQB2HIXKz4bFgtVib0bbT1sbtLI8=
|
||||
github.com/minio/minio-go/v7 v7.0.100/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
|
||||
github.com/montanaflynn/stats v0.8.2 h1:52wnefTJnPI5FoHif1DQh2soKRw0yYs+4AVyvtcZCH0=
|
||||
github.com/montanaflynn/stats v0.8.2/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/montanaflynn/stats v0.9.0 h1:tsBJ0RXwph9BmAuFoCmqGv6e8xa0MENQ8m0ptKq29mQ=
|
||||
github.com/montanaflynn/stats v0.9.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
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/nats-io/jwt/v2 v2.8.1 h1:V0xpGuD/N8Mi+fQNDynXohVvp7ZztevW5io8CUWlPmU=
|
||||
@@ -145,72 +154,71 @@ github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
|
||||
github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
|
||||
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
|
||||
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
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.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
|
||||
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
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/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
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/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
|
||||
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/zap-proto/go v0.2.0/go.mod h1:OITYSXRd0v93MWZuGl9XnPS77a09DYzN/Uy9/VwhuCs=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -219,12 +227,15 @@ golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -24,11 +24,10 @@ import (
|
||||
"github.com/hanzoai/pubsub/internal/store"
|
||||
)
|
||||
|
||||
// Type aliases — the published v1.22.0 uses legacy "Hybrid" names.
|
||||
// We alias to the correct names here; a new release will export these directly.
|
||||
// Type aliases to the upstream consensus quasar primitives.
|
||||
type (
|
||||
QuasarConsensus = quasar.QuasarHybridConsensus
|
||||
QuasarSignature = quasar.HybridSignature
|
||||
QuasarConsensus = quasar.Quasar
|
||||
QuasarSignature = quasar.QuasarSig
|
||||
)
|
||||
|
||||
// maxPendingOps caps the pending map size to prevent OOM from proposal spam.
|
||||
@@ -170,13 +169,19 @@ func New(cfg Config) (*Quasar, error) {
|
||||
cfg.ZAPPort = 0
|
||||
}
|
||||
|
||||
signer, err := quasar.NewQuasarHybridConsensus(cfg.Threshold)
|
||||
// Production BFT consensus requires threshold >= 2 (NewQuasar). A threshold
|
||||
// of 1 is a single-node/dev topology, which upstream exposes via NewTestQuasar.
|
||||
newSigner := quasar.NewQuasar
|
||||
if cfg.Threshold < 2 {
|
||||
newSigner = quasar.NewTestQuasar
|
||||
}
|
||||
signer, err := newSigner(cfg.Threshold)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("quasar: init signer: %w", err)
|
||||
}
|
||||
|
||||
for _, id := range cfg.ValidatorIDs {
|
||||
if err := signer.AddValidator(id, 1); err != nil {
|
||||
if _, err := signer.AddValidator(id, 1); err != nil {
|
||||
return nil, fmt.Errorf("quasar: add validator %s: %w", id, err)
|
||||
}
|
||||
}
|
||||
@@ -315,7 +320,7 @@ func (q *Quasar) addVoteLocked(hash string, validatorID string, sig *QuasarSigna
|
||||
if err != nil || len(hashBytes) == 0 {
|
||||
return false
|
||||
}
|
||||
if !q.signer.VerifyHybridSignature(hashBytes, sig) {
|
||||
if !q.signer.VerifyQuasarSig(hashBytes, sig) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+36
-12
@@ -442,6 +442,9 @@ type consumer struct {
|
||||
rlimit *rate.Limiter
|
||||
reqSub *subscription
|
||||
resetSub *subscription
|
||||
ackSubOld *subscription
|
||||
ackReplyOldT string
|
||||
ackSubjOld string
|
||||
ackSub *subscription
|
||||
ackReplyT string
|
||||
ackSubj string
|
||||
@@ -1111,10 +1114,12 @@ func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname stri
|
||||
return nil, NewJSConsumerDoesNotExistError()
|
||||
}
|
||||
|
||||
standalone := !s.JetStreamIsClustered() && s.standAloneMode()
|
||||
|
||||
// If we're clustered we've already done this check, only do this if we're a standalone server.
|
||||
// But if we're standalone, only enforce if we're not recovering, since the MaxConsumers could've
|
||||
// been updated while we already had more consumers on disk.
|
||||
if !s.JetStreamIsClustered() && s.standAloneMode() && !isRecovering {
|
||||
if standalone && !isRecovering {
|
||||
// Check for any limits, if the config for the consumer sets a limit we check against that
|
||||
// but if not we use the value from account limits, if account limits is more restrictive
|
||||
// than stream config we prefer the account limits to handle cases where account limits are
|
||||
@@ -1123,7 +1128,7 @@ func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname stri
|
||||
if maxc <= 0 || (selectedLimits.MaxConsumers > 0 && selectedLimits.MaxConsumers < maxc) {
|
||||
maxc = selectedLimits.MaxConsumers
|
||||
}
|
||||
if maxc > 0 && mset.numPublicConsumers() >= maxc {
|
||||
if maxc > 0 && mset.numLimitableConsumers() >= maxc {
|
||||
mset.mu.Unlock()
|
||||
return nil, NewJSMaximumConsumersLimitError()
|
||||
}
|
||||
@@ -1714,10 +1719,6 @@ func (o *consumer) setLeader(isLeader bool) error {
|
||||
o.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
if o.resetSub, err = o.subscribeInternal(o.resetSubj, o.processResetReq); err != nil {
|
||||
o.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Check on flow control settings.
|
||||
if o.cfg.FlowControl {
|
||||
@@ -1846,8 +1847,9 @@ func (o *consumer) setLeader(isLeader bool) error {
|
||||
o.unsubscribe(o.ackSub)
|
||||
o.unsubscribe(o.reqSub)
|
||||
o.unsubscribe(o.resetSub)
|
||||
o.unsubscribe(o.fcSubOld)
|
||||
o.unsubscribe(o.fcSub)
|
||||
o.ackSub, o.reqSub, o.resetSub, o.fcSub = nil, nil, nil, nil
|
||||
o.ackSubOld, o.ackSub, o.reqSub, o.resetSub, o.fcSubOld, o.fcSub = nil, nil, nil, nil, nil, nil
|
||||
if o.infoSub != nil {
|
||||
o.srv.sysUnsubscribe(o.infoSub)
|
||||
o.infoSub = nil
|
||||
@@ -2627,6 +2629,9 @@ func (o *consumer) updateConfig(cfg *ConsumerConfig) error {
|
||||
// Allowed but considered no-op, [Description, SampleFrequency, MaxWaiting, HeadersOnly]
|
||||
o.cfg = *cfg
|
||||
|
||||
if cfg.Sourcing && (!o.srv.JetStreamIsClustered() && o.srv.standAloneMode()) {
|
||||
o.resetStartingSeqLocked(0, _EMPTY_, false)
|
||||
}
|
||||
if updatedFilters {
|
||||
// Cleanup messages that lost interest.
|
||||
if o.retention == InterestPolicy {
|
||||
@@ -2792,10 +2797,14 @@ func (o *consumer) updateSkipped(seq uint64) {
|
||||
o.propose(b[:])
|
||||
}
|
||||
|
||||
func (o *consumer) resetStartingSeq(seq uint64, reply string) (uint64, bool, error) {
|
||||
func (o *consumer) resetStartingSeq(seq uint64, reply string, internal bool) (uint64, bool, error) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
return o.resetStartingSeqLocked(seq, reply, internal)
|
||||
}
|
||||
|
||||
// Lock should be held.
|
||||
func (o *consumer) resetStartingSeqLocked(seq uint64, reply string, internal bool) (uint64, bool, error) {
|
||||
// Reset to a specific sequence, or back to the ack floor.
|
||||
if seq == 0 {
|
||||
seq = o.asflr + 1
|
||||
@@ -2824,17 +2833,27 @@ VALID:
|
||||
if seq <= 0 {
|
||||
seq = 1
|
||||
}
|
||||
o.resetLocalStartingSeq(seq)
|
||||
// Clustered mode and R>1.
|
||||
// The replicated path requires quorum first before the reset actually takes effect.
|
||||
if o.node != nil {
|
||||
if !o.isLeader() {
|
||||
return 0, false, nil
|
||||
}
|
||||
b := make([]byte, 1+8+len(reply))
|
||||
b[0] = byte(resetSeqOp)
|
||||
var le = binary.LittleEndian
|
||||
le.PutUint64(b[1:], seq)
|
||||
copy(b[1+8:], reply)
|
||||
o.propose(b[:])
|
||||
if reply != _EMPTY_ {
|
||||
if o.rsm == nil {
|
||||
o.rsm = make(map[string]bool, 1)
|
||||
}
|
||||
o.rsm[reply] = internal
|
||||
}
|
||||
return seq, false, nil
|
||||
} else if o.store != nil {
|
||||
}
|
||||
o.resetLocalStartingSeq(seq)
|
||||
if o.store != nil {
|
||||
o.store.Reset(seq - 1)
|
||||
// Cleanup messages that lost interest.
|
||||
if o.retention == InterestPolicy {
|
||||
@@ -4498,7 +4517,7 @@ func (o *consumer) processResetReq(_ *subscription, c *client, a *Account, _, re
|
||||
return
|
||||
}
|
||||
}
|
||||
resetSeq, canRespond, err := o.resetStartingSeq(req.Seq, reply)
|
||||
resetSeq, canRespond, err := o.resetStartingSeq(req.Seq, reply, false)
|
||||
if err != nil {
|
||||
resp.Error = NewJSConsumerInvalidResetError(err)
|
||||
s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
|
||||
@@ -4813,6 +4832,9 @@ func (o *consumer) getNextMsg() (*jsPubMsg, uint64, error) {
|
||||
// That will correct the pending state and delivery/ack floors, so just skip here.
|
||||
pmsg.returnToPool()
|
||||
pmsg = nil
|
||||
if p, ok := o.pending[seq]; ok {
|
||||
o.processTermLocked(seq, p.Sequence, dc-1, ackTermUnackedLimitsReason, _EMPTY_, false)
|
||||
}
|
||||
continue
|
||||
}
|
||||
return pmsg, dc, err
|
||||
@@ -6583,11 +6605,13 @@ func (o *consumer) stopWithFlags(dflag, sdflag, doSignal, advisory bool) error {
|
||||
o.unsubscribe(o.ackSub)
|
||||
o.unsubscribe(o.reqSub)
|
||||
o.unsubscribe(o.resetSub)
|
||||
o.unsubscribe(o.fcSubOld)
|
||||
o.unsubscribe(o.fcSub)
|
||||
o.ackSubOld = nil
|
||||
o.ackSub = nil
|
||||
o.reqSub = nil
|
||||
o.resetSub = nil
|
||||
o.fcSubOld = nil
|
||||
o.fcSub = nil
|
||||
if o.infoSub != nil {
|
||||
o.srv.sysUnsubscribe(o.infoSub)
|
||||
|
||||
+108
-57
@@ -1212,8 +1212,8 @@ func (fs *fileStore) recoverMsgBlock(index uint32) (*msgBlock, error) {
|
||||
}
|
||||
// We can recycle it now.
|
||||
recycleMsgBlockBuf(buf)
|
||||
} else {
|
||||
file.ReadAt(lchk[:], int64(mb.rbytes)-checksumSize)
|
||||
} else if _, err = file.ReadAt(lchk[:], int64(mb.rbytes)-checksumSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1686,8 +1686,13 @@ func (mb *msgBlock) rebuildStateFromBufLocked(buf []byte, allowTruncate bool) (*
|
||||
dlen := int(rl) - msgHdrSize
|
||||
// Do some quick sanity checks here.
|
||||
if dlen < 0 || shlen > (dlen-recordHashSize) || dlen > int(rl) || index+rl > lbuf || rl > rlBadThresh {
|
||||
truncate(index)
|
||||
return gatherLost(lbuf - index), tombstones, errBadMsg{mb.mfn, fmt.Sprintf("sanity check failed (dlen %d slen %d rl %d index %d lbuf %d)", dlen, slen, rl, index, lbuf)}
|
||||
err = errBadMsg{mb.mfn, fmt.Sprintf("sanity check failed (dlen %d slen %d rl %d index %d lbuf %d)", dlen, slen, rl, index, lbuf)}
|
||||
if allowTruncate {
|
||||
if err = truncate(index); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return gatherLost(lbuf - index), tombstones, err
|
||||
}
|
||||
|
||||
// Check for checksum failures before additional processing.
|
||||
@@ -2431,7 +2436,10 @@ func (fs *fileStore) recoverMsgs() error {
|
||||
// out from underneath of us this is possible.
|
||||
mb.mu.Lock()
|
||||
if atomic.LoadUint64(&mb.first.seq) == 0 {
|
||||
mb.dirtyCloseWithRemove(true)
|
||||
if err := mb.dirtyCloseWithRemove(true); err != nil {
|
||||
mb.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
fs.removeMsgBlockFromList(mb)
|
||||
mb.mu.Unlock()
|
||||
continue
|
||||
@@ -2769,6 +2777,7 @@ func (fs *fileStore) GetSeqFromTime(t time.Time) uint64 {
|
||||
|
||||
// Using a binary search, but need to be aware of interior deletes in the block.
|
||||
seq := lseq + 1
|
||||
mb.mu.Lock()
|
||||
loop:
|
||||
for fseq <= lseq {
|
||||
mid := fseq + (lseq-fseq)/2
|
||||
@@ -2776,7 +2785,7 @@ loop:
|
||||
// Potentially skip over gaps. We keep the original middle but keep track of a
|
||||
// potential delete range with an offset.
|
||||
for {
|
||||
sm, _, err := mb.fetchMsgNoCopy(mid+off, &smv)
|
||||
sm, _, err := mb.fetchMsgNoCopyLocked(mid+off, &smv)
|
||||
if err != nil || sm == nil {
|
||||
off++
|
||||
if mid+off <= lseq {
|
||||
@@ -2803,6 +2812,8 @@ loop:
|
||||
fseq = mid + off + 1
|
||||
}
|
||||
}
|
||||
mb.finishedWithCache()
|
||||
mb.mu.Unlock()
|
||||
return seq
|
||||
}
|
||||
|
||||
@@ -2843,10 +2854,7 @@ func (mb *msgBlock) firstMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *
|
||||
// If there are no subject matches then this is effectively no-op.
|
||||
hseq := uint64(math.MaxUint64)
|
||||
var ierr error
|
||||
stree.IntersectGSL(mb.fss, sl, func(subj []byte, ss *SimpleState) {
|
||||
if ierr != nil {
|
||||
return
|
||||
}
|
||||
stree.IntersectGSL(mb.fss, sl, func(subj []byte, ss *SimpleState) bool {
|
||||
if ss.firstNeedsUpdate || ss.lastNeedsUpdate {
|
||||
// mb is already loaded into the cache so should be fast-ish.
|
||||
if ierr = mb.recalculateForSubj(bytesToString(subj), ss); ierr != nil {
|
||||
@@ -2870,16 +2878,6 @@ func (mb *msgBlock) firstMatchingMulti(sl *gsl.SimpleSublist, start uint64, sm *
|
||||
if sm == nil {
|
||||
sm = new(StoreMsg)
|
||||
}
|
||||
// Need messages loaded from here on out.
|
||||
if mb.cacheNotLoaded() {
|
||||
if ierr = mb.loadMsgsWithLock(); ierr != nil {
|
||||
return
|
||||
}
|
||||
didLoad = true
|
||||
}
|
||||
if sm == nil {
|
||||
sm = new(StoreMsg)
|
||||
}
|
||||
if first == ss.First {
|
||||
// If the start floor is below where this subject starts then we can
|
||||
// short-circuit, avoiding needing to scan for the next message.
|
||||
@@ -3464,16 +3462,29 @@ func (fs *fileStore) checkSkipFirstBlock(filter string, wc bool, bi int) (int, e
|
||||
// This is used to see if we can selectively jump start blocks based on filter subjects and a starting block index.
|
||||
// Will return -1 and ErrStoreEOF if no matches at all or no more from where we are.
|
||||
func (fs *fileStore) checkSkipFirstBlockMulti(sl *gsl.SimpleSublist, bi int) (int, error) {
|
||||
// Don't bother if full wildcard.
|
||||
if sl.MatchesFullWildcard() || fs.psim.Size() > highCardinalityThreshold {
|
||||
return bi + 1, nil
|
||||
}
|
||||
// Move through psim to gather start and stop bounds.
|
||||
start, stop := uint32(math.MaxUint32), uint32(0)
|
||||
stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) {
|
||||
guard := fs.blks[bi].getIndex() + 1
|
||||
stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) bool {
|
||||
if psi.fblk < start {
|
||||
start = psi.fblk
|
||||
}
|
||||
if start == guard {
|
||||
// One of the subjects matches the next block, so there's no point in carrying on trying to skip.
|
||||
return false
|
||||
}
|
||||
if psi.lblk > stop {
|
||||
stop = psi.lblk
|
||||
}
|
||||
return true
|
||||
})
|
||||
if start == guard {
|
||||
return bi + 1, nil
|
||||
}
|
||||
// Nothing was found.
|
||||
if start == uint32(math.MaxUint32) {
|
||||
return -1, ErrStoreEOF
|
||||
@@ -3748,7 +3759,9 @@ func (fs *fileStore) allLastSeqsLocked() ([]uint64, error) {
|
||||
// Check if we need to recalculate. We only care about the last sequence.
|
||||
if ss.lastNeedsUpdate {
|
||||
// mb is already loaded into the cache so should be fast-ish.
|
||||
mb.recalculateForSubj(bytesToString(bsubj), ss)
|
||||
if ierr = mb.recalculateForSubj(bytesToString(bsubj), ss); ierr != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
seqs = append(seqs, ss.Last)
|
||||
subs[string(bsubj)] = struct{}{}
|
||||
@@ -5380,8 +5393,9 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) {
|
||||
|
||||
bsubj := stringToBytes(subj)
|
||||
if ss, ok := mb.fss.Find(bsubj); ok && ss != nil {
|
||||
var err error
|
||||
if ss.firstNeedsUpdate || ss.lastNeedsUpdate {
|
||||
mb.recalculateForSubj(subj, ss)
|
||||
err = mb.recalculateForSubj(subj, ss)
|
||||
}
|
||||
mb.mu.Unlock()
|
||||
// Re-acquire fs lock
|
||||
@@ -5392,6 +5406,9 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) {
|
||||
info.fblk = i
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ss.First, nil
|
||||
}
|
||||
// If we did not find it and we loaded this msgBlock try to expire as long as not the last.
|
||||
@@ -5709,7 +5726,7 @@ func (fs *fileStore) removeMsg(seq uint64, secure, viaLimits, needFSLock bool) (
|
||||
|
||||
// Remove a message from the given block, optionally rewriting the mb file.
|
||||
// fs lock should be held.
|
||||
func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLimits bool) (bool, error) {
|
||||
func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLimits bool) (removed bool, rerr error) {
|
||||
mb.mu.Lock()
|
||||
|
||||
// See if we are closed or the sequence number is still relevant or if we know its deleted.
|
||||
@@ -5753,7 +5770,6 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
|
||||
lhdr, lmsg int
|
||||
ttl int64
|
||||
)
|
||||
// We don't use a copy as long as that's possible. When unlocking mb or erasing, we'll copy the subject.
|
||||
sm, err := mb.cacheLookupNoCopy(seq, &smv)
|
||||
if err != nil {
|
||||
finishedWithCache()
|
||||
@@ -5764,7 +5780,9 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
|
||||
}
|
||||
return false, err
|
||||
} else if sm != nil {
|
||||
subj = sm.subj
|
||||
// subj aliases mb.cache.buf; copy now because the cache may be erased or
|
||||
// recycled after we drop mb.mu. The rest are scalars stashed for later use.
|
||||
subj = copyString(sm.subj)
|
||||
ts = sm.ts
|
||||
lhdr = len(sm.hdr)
|
||||
lmsg = len(sm.msg)
|
||||
@@ -5776,17 +5794,18 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
|
||||
// when the last block is empty.
|
||||
// If not via limits and not empty (empty writes tombstone below if last) write tombstone.
|
||||
if !viaLimits && !isEmpty && sm != nil {
|
||||
// Need to copy the subject since we unlock and re-acquire, and the cache could change.
|
||||
subj = copyString(subj)
|
||||
mb.mu.Unlock() // Only safe way to checkLastBlock is to unlock here...
|
||||
lmb, err := fs.checkLastBlock(emptyRecordLen)
|
||||
if err != nil {
|
||||
mb.mu.Lock()
|
||||
finishedWithCache()
|
||||
mb.mu.Unlock()
|
||||
return false, err
|
||||
}
|
||||
if err := lmb.writeTombstone(seq, ts); err != nil {
|
||||
mb.mu.Lock()
|
||||
finishedWithCache()
|
||||
mb.mu.Unlock()
|
||||
return false, err
|
||||
}
|
||||
mb.mu.Lock() // We'll need the lock back to carry on safely.
|
||||
@@ -5807,9 +5826,6 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
|
||||
mb.mu.Unlock()
|
||||
return false, err
|
||||
}
|
||||
// Need to copy the subject, as eraseMsg will overwrite the cache and we won't
|
||||
// be able to access sm.subj anymore later on.
|
||||
subj = copyString(subj)
|
||||
if err := mb.eraseMsg(seq, int(ri), int(msz), isLastBlock); err != nil {
|
||||
finishedWithCache()
|
||||
mb.mu.Unlock()
|
||||
@@ -5851,7 +5867,11 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
|
||||
}
|
||||
|
||||
// If we are tracking multiple subjects here make sure we update that accounting.
|
||||
mb.removeSeqPerSubject(subj, seq)
|
||||
if _, err = mb.removeSeqPerSubject(subj, seq); err != nil {
|
||||
finishedWithCache()
|
||||
mb.mu.Unlock()
|
||||
return false, err
|
||||
}
|
||||
fs.removePerSubject(subj)
|
||||
if fs.ttls != nil && ttl > 0 {
|
||||
expires := time.Duration(ts) + (time.Second * time.Duration(ttl))
|
||||
@@ -5933,18 +5953,17 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim
|
||||
|
||||
// Remove all messages in the range [first, last]
|
||||
// Lock should be held.
|
||||
func (fs *fileStore) removeMsgsInRange(first, last uint64, viaLimits bool) {
|
||||
func (fs *fileStore) removeMsgsInRange(first, last uint64, viaLimits bool) error {
|
||||
last = min(last, fs.state.LastSeq)
|
||||
if first > last {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
firstBlock := sort.Search(len(fs.blks), func(i int) bool {
|
||||
return atomic.LoadUint64(&fs.blks[i].last.seq) >= first
|
||||
})
|
||||
|
||||
if firstBlock >= len(fs.blks) {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := firstBlock; i < len(fs.blks); {
|
||||
@@ -5961,16 +5980,21 @@ func (fs *fileStore) removeMsgsInRange(first, last uint64, viaLimits bool) {
|
||||
// purgeMgsBlock, which also removes the block from fs.blks.
|
||||
// After purgeMsgBlock, i will be the index of the following
|
||||
// msgBlock, if any. Therefore, continue without incrementing i.
|
||||
fs.purgeMsgBlock(mb)
|
||||
if err := fs.purgeMsgBlock(mb); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
from := max(first, mbFirstSeq)
|
||||
to := min(last, mbLastSeq)
|
||||
for seq := from; seq <= to; seq++ {
|
||||
fs.removeMsgFromBlock(mb, seq, false, viaLimits)
|
||||
if _, err := fs.removeMsgFromBlock(mb, seq, false, viaLimits); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tests whether we should try to compact this block while inline removing msgs.
|
||||
@@ -8896,9 +8920,17 @@ func (fs *fileStore) SubjectForSeq(seq uint64) (string, error) {
|
||||
mb := fs.selectMsgBlock(seq)
|
||||
fs.mu.RUnlock()
|
||||
if mb != nil {
|
||||
if sm, _, _ := mb.fetchMsgNoCopy(seq, &smv); sm != nil {
|
||||
mb.mu.Lock()
|
||||
sm, _, _ := mb.fetchMsgNoCopyLocked(seq, &smv)
|
||||
var subj string
|
||||
if sm != nil {
|
||||
// Copy the subject, as it's used elsewhere, and the backing cache could be reused in the meantime.
|
||||
return copyString(sm.subj), nil
|
||||
subj = copyString(sm.subj)
|
||||
}
|
||||
mb.finishedWithCache()
|
||||
mb.mu.Unlock()
|
||||
if sm != nil {
|
||||
return subj, nil
|
||||
}
|
||||
}
|
||||
return _EMPTY_, ErrStoreMsgNotFound
|
||||
@@ -8978,7 +9010,10 @@ func (fs *fileStore) loadLastLocked(subj string, sm *StoreMsg) (lsm *StoreMsg, e
|
||||
// Check if we need to recalculate. We only care about the last sequence.
|
||||
if ss.lastNeedsUpdate {
|
||||
// mb is already loaded into the cache so should be fast-ish.
|
||||
mb.recalculateForSubj(subj, ss)
|
||||
if err = mb.recalculateForSubj(subj, ss); err != nil {
|
||||
mb.mu.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
l = ss.Last
|
||||
}
|
||||
@@ -9052,12 +9087,13 @@ func (fs *fileStore) LoadNextMsgMulti(sl *gsl.SimpleSublist, start uint64, smp *
|
||||
if start <= fs.state.FirstSeq {
|
||||
var total uint64
|
||||
blkStart := uint32(math.MaxUint32)
|
||||
stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) {
|
||||
stree.IntersectGSL(fs.psim, sl, func(subj []byte, psi *psi) bool {
|
||||
total += psi.total
|
||||
// Keep track of start index for this subject.
|
||||
if psi.fblk < blkStart {
|
||||
blkStart = psi.fblk
|
||||
}
|
||||
return true
|
||||
})
|
||||
// Nothing available.
|
||||
if total == 0 {
|
||||
@@ -9980,7 +10016,7 @@ func (fs *fileStore) PurgeEx(subject string, sequence, keep uint64) (purged uint
|
||||
// When writing multiple tombstones we will flush at the end.
|
||||
if len(tombs) > 0 {
|
||||
for _, tomb := range tombs {
|
||||
if err := fs.writeTombstoneNoFlush(tomb.seq, tomb.ts); err != nil {
|
||||
if err = fs.writeTombstoneNoFlush(tomb.seq, tomb.ts); err != nil {
|
||||
fs.mu.Unlock()
|
||||
return purged, err
|
||||
}
|
||||
@@ -10478,7 +10514,7 @@ SKIP:
|
||||
// When writing multiple tombstones we will flush at the end.
|
||||
if len(tombs) > 0 {
|
||||
for _, tomb := range tombs {
|
||||
if err := fs.writeTombstoneNoFlush(tomb.seq, tomb.ts); err != nil {
|
||||
if err = fs.writeTombstoneNoFlush(tomb.seq, tomb.ts); err != nil {
|
||||
fs.mu.Unlock()
|
||||
return purged, err
|
||||
}
|
||||
@@ -11699,7 +11735,7 @@ func (fs *fileStore) _writeFullState(force bool) error {
|
||||
if fs.state.LastSeq > fs.state.FirstSeq {
|
||||
numDeleted = int((fs.state.LastSeq - fs.state.FirstSeq + 1) - fs.state.Msgs)
|
||||
}
|
||||
if numSubjects > numThreshold || numDeleted > numThreshold {
|
||||
if numSubjects > highCardinalityThreshold || numDeleted > highCardinalityThreshold {
|
||||
fs.mu.RUnlock()
|
||||
return errStateTooBig
|
||||
}
|
||||
@@ -11860,11 +11896,9 @@ func (fs *fileStore) _writeFullState(force bool) error {
|
||||
}
|
||||
|
||||
// Update dirty if successful.
|
||||
if err == nil {
|
||||
fs.mu.Lock()
|
||||
fs.dirty -= priorDirty
|
||||
fs.mu.Unlock()
|
||||
}
|
||||
fs.mu.Lock()
|
||||
fs.dirty -= priorDirty
|
||||
fs.mu.Unlock()
|
||||
|
||||
// Attempt to write both files, an error in one should not prevent the other from being written.
|
||||
ttlErr := fs.writeTTLState()
|
||||
@@ -12386,16 +12420,33 @@ func (fs *fileStore) SyncDeleted(dbs DeleteBlocks) error {
|
||||
mdbs := fs.deleteBlocks()
|
||||
fs.readUnlockAllMsgBlocks()
|
||||
|
||||
for _, db := range needsCheck {
|
||||
if dr, ok := db.(*DeleteRange); ok {
|
||||
first, last, _ := dr.State()
|
||||
fs.removeMsgsInRange(first, last, true)
|
||||
for _, db := range dbs {
|
||||
first, last, _ := db.State()
|
||||
if first > lseq {
|
||||
break
|
||||
}
|
||||
|
||||
var prune bool
|
||||
if prune, mdbs = pruneDeleteBlock(db, mdbs); prune {
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
if _, ok := db.(*DeleteRange); ok {
|
||||
err = fs.removeMsgsInRange(first, last, true)
|
||||
} else {
|
||||
db.Range(func(dseq uint64) bool {
|
||||
fs.removeMsg(dseq, false, true, false)
|
||||
return true
|
||||
_, err = fs.removeMsg(dseq, false, true, false)
|
||||
// Can continue safely if the message doesn't exist.
|
||||
if err == ErrStoreEOF || err == ErrStoreMsgNotFound {
|
||||
err = nil
|
||||
}
|
||||
return err == nil
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -12738,7 +12789,7 @@ func (o *consumerFileStore) SetStarting(sseq uint64) error {
|
||||
o.mu.Lock()
|
||||
o.state.Delivered.Stream = sseq
|
||||
o.state.AckFloor.Stream = sseq
|
||||
buf, err := o.encodeState()
|
||||
buf := encodeConsumerState(&o.state)
|
||||
o.mu.Unlock()
|
||||
return o.writeState(buf)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/hanzoai/pubsub/server/gsl"
|
||||
"github.com/hanzoai/pubsub/server/sysmem"
|
||||
"github.com/hanzoai/pubsub/server/tpm"
|
||||
"github.com/nats-io/nkeys"
|
||||
|
||||
@@ -775,6 +775,7 @@ type JSApiConsumerResetRequest struct {
|
||||
Seq uint64 `json:"seq,omitempty"`
|
||||
}
|
||||
|
||||
// JSApiConsumerResetResponse is a superset of JSApiConsumerCreateResponse, but including an explicit ResetSeq.
|
||||
type JSApiConsumerResetResponse struct {
|
||||
ApiResponse
|
||||
*ConsumerInfo
|
||||
@@ -4313,6 +4314,8 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult,
|
||||
|
||||
var hdr []byte
|
||||
chunk := make([]byte, chunkSize)
|
||||
ackTimer := time.NewTimer(snapshotAckTimeout)
|
||||
defer stopAndClearTimer(&ackTimer)
|
||||
for index := 1; ; index++ {
|
||||
select {
|
||||
case <-slots:
|
||||
@@ -4325,7 +4328,7 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult,
|
||||
// The snapshotting goroutine has failed for some reason.
|
||||
hdr = []byte(fmt.Sprintf("NATS/1.0 500 %s\r\n\r\n", err))
|
||||
goto done
|
||||
case <-time.After(snapshotAckTimeout):
|
||||
case <-ackTimer.C:
|
||||
// It's taking a very long time for the receiver to send us acks,
|
||||
// they have probably stalled or there is high loss on the link.
|
||||
hdr = []byte("NATS/1.0 408 No Flow Response\r\n\r\n")
|
||||
@@ -4344,6 +4347,7 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult,
|
||||
hdr = []byte("NATS/1.0 204\r\n\r\n")
|
||||
}
|
||||
mset.outq.send(newJSPubMsg(reply, _EMPTY_, ackReply, nil, chunk, nil, 0))
|
||||
ackTimer.Reset(snapshotAckTimeout)
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
+150
-96
@@ -53,6 +53,10 @@ type jetStreamCluster struct {
|
||||
// a response but they need to be same group, peers etc. and sync subjects.
|
||||
inflightStreams map[string]map[string]*inflightStreamInfo
|
||||
inflightConsumers map[string]map[string]map[string]*inflightConsumerInfo
|
||||
// Tracks raft groups currently being started by createRaftGroup, so that
|
||||
// concurrent callers for the same group can wait without holding js.mu
|
||||
// across the disk I/O performed during startup.
|
||||
creatingRaftGroups map[string]chan struct{}
|
||||
// Holds a map of a peer ID to the reply subject, to only respond after gaining
|
||||
// quorum on the peer-remove action.
|
||||
peerRemoveReply map[string]peerRemoveInfo
|
||||
@@ -807,6 +811,14 @@ func (js *jetStream) isConsumerHealthy(mset *stream, consumer string, ca *consum
|
||||
js.mu.RUnlock()
|
||||
return errors.New("consumer assignment or group missing")
|
||||
}
|
||||
// Surface any persisted assignment-level error (e.g. failed create on this
|
||||
// peer) so the health check reflects the broken state instead of falling
|
||||
// through to runtime-only checks.
|
||||
if ca.err != nil {
|
||||
err := ca.err
|
||||
js.mu.RUnlock()
|
||||
return fmt.Errorf("consumer assignment error: %w", err)
|
||||
}
|
||||
created := ca.Created
|
||||
node := ca.Group.node
|
||||
js.mu.RUnlock()
|
||||
@@ -1548,7 +1560,7 @@ func (js *jetStream) getOrphans() (streams []*stream, consumers []*consumer) {
|
||||
streams = append(streams, mset)
|
||||
} else {
|
||||
// This one is good, check consumers now.
|
||||
for _, o := range mset.getConsumers() {
|
||||
for _, o := range mset.getPublicConsumers() {
|
||||
if sa.consumers[o.String()] == nil {
|
||||
consumers = append(consumers, o)
|
||||
}
|
||||
@@ -2166,7 +2178,7 @@ func (js *jetStream) decodeMetaSnapshot(buf []byte) (map[string]map[string]*stre
|
||||
if wca.Stream == _EMPTY_ {
|
||||
wca.Stream = sa.Config.Name // Rehydrate from the stream name.
|
||||
}
|
||||
ca := &consumerAssignment{Client: wca.Client, Created: wca.Created, Name: wca.Name, Stream: wca.Stream, ConfigJSON: wca.ConfigJSON, Group: wca.Group, State: wca.State}
|
||||
ca := &consumerAssignment{Client: wca.Client, Created: wca.Created, Name: wca.Name, Stream: wca.Stream, ConfigJSON: wca.ConfigJSON, Group: wca.Group}
|
||||
if err := decodeConsumerAssignmentConfig(ca); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2206,7 +2218,6 @@ func (js *jetStream) encodeMetaSnapshot(streams map[string]map[string]*streamAss
|
||||
Stream: ca.Stream,
|
||||
ConfigJSON: ca.ConfigJSON,
|
||||
Group: ca.Group,
|
||||
State: ca.State,
|
||||
}
|
||||
wsa.Consumers = append(wsa.Consumers, &wca)
|
||||
nca++
|
||||
@@ -2328,6 +2339,10 @@ func (js *jetStream) collectStreamAndConsumerChanges(c RaftNodeCheckpoint, strea
|
||||
as = make(map[string]*streamAssignment)
|
||||
streams[sa.Client.serviceAccount()] = as
|
||||
}
|
||||
// Preserve consumers from the previous assignment.
|
||||
if osa := as[sa.Config.Name]; osa != nil {
|
||||
sa.consumers = osa.consumers
|
||||
}
|
||||
as[sa.Config.Name] = sa
|
||||
}
|
||||
for _, cas := range ru.updateConsumers {
|
||||
@@ -2595,7 +2610,9 @@ func (js *jetStream) applyMetaEntries(entries []*Entry, ru *recoveryUpdates) (bo
|
||||
}
|
||||
|
||||
if e.Type == EntrySnapshot {
|
||||
js.applyMetaSnapshot(e.Data, ru, isRecovering)
|
||||
if err := js.applyMetaSnapshot(e.Data, ru, isRecovering); err != nil {
|
||||
return isRecovering, didSnap, err
|
||||
}
|
||||
didSnap = true
|
||||
} else if e.Type == EntryRemovePeer {
|
||||
if !js.isMetaRecovering() {
|
||||
@@ -3117,11 +3134,16 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
|
||||
// fully recovered from disk.
|
||||
isRecovering := true
|
||||
|
||||
var failedSnapshots int
|
||||
var (
|
||||
snapMu sync.Mutex
|
||||
snapshotting bool
|
||||
fallbackSnapshot bool
|
||||
failedSnapshots int
|
||||
)
|
||||
|
||||
doSnapshot := func(force bool) {
|
||||
// Suppress during recovery.
|
||||
// If snapshots have failed, and we're not forced to, we'll wait for the timer since it'll now be forced.
|
||||
if mset == nil || isRecovering || isRestore || (!force && failedSnapshots > 0) {
|
||||
if mset == nil || isRecovering || isRestore {
|
||||
return
|
||||
}
|
||||
snapMu.Lock()
|
||||
@@ -3155,28 +3177,70 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
|
||||
}
|
||||
|
||||
// Make sure all pending data is flushed before allowing snapshots.
|
||||
mset.flushAllPending()
|
||||
if err := mset.flushAllPending(); err != nil {
|
||||
// If the pending data couldn't be flushed, we have no safe way to continue.
|
||||
s.Errorf("Failed to flush pending data for '%s > %s' [%s]: %v", accName, mset.name(), n.Group(), err)
|
||||
assert.Unreachable("Stream snapshot flush failed", map[string]any{
|
||||
"account": accName,
|
||||
"stream": mset.name(),
|
||||
"group": n.Group(),
|
||||
"err": err,
|
||||
})
|
||||
c.Abort()
|
||||
mset.setWriteErr(err)
|
||||
n.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// If we had a significant number of failed snapshots, start relaxing Raft-layer checks
|
||||
// to force it through. We might have been catching up a peer for a long period, and this
|
||||
// protects our log size from growing indefinitely.
|
||||
forceSnapshot := failedSnapshots > 4
|
||||
if err := n.InstallSnapshot(mset.stateSnapshot(), forceSnapshot); err == nil {
|
||||
lastState = curState
|
||||
// If there was a failed snapshot before, we reduced the timer's interval.
|
||||
// Reset it back to the original interval now.
|
||||
if failedSnapshots > 0 {
|
||||
t.Reset(compactInterval + rci)
|
||||
snap := mset.stateSnapshot()
|
||||
|
||||
handleInstallResult := func(err error) {
|
||||
snapshotting = false
|
||||
if err == nil {
|
||||
// If there was a failed snapshot before, we reduced the timer's interval.
|
||||
// Reset it back to the original interval now.
|
||||
if failedSnapshots > 0 {
|
||||
t.Reset(compactInterval + rci)
|
||||
}
|
||||
failedSnapshots = 0
|
||||
fallbackSnapshot = false
|
||||
} else {
|
||||
c.Abort()
|
||||
|
||||
if err == errNoSnapAvailable || err == errNodeClosed || err == errCatchupsRunning || err == errSnapAborted {
|
||||
return
|
||||
}
|
||||
|
||||
s.RateLimitWarnf("Failed to install snapshot for '%s > %s' [%s]: %v, will fall back to blocking snapshot",
|
||||
mset.acc.Name, mset.name(), n.Group(), err)
|
||||
fallbackSnapshot = true
|
||||
// If this is the first failure, reduce the interval of the snapshot timer.
|
||||
// This ensures we're not waiting too long for snapshotting to eventually become forced.
|
||||
if failedSnapshots == 0 {
|
||||
t.Reset(compactMinInterval)
|
||||
}
|
||||
failedSnapshots++
|
||||
}
|
||||
failedSnapshots = 0
|
||||
} else if err != errNoSnapAvailable && err != errNodeClosed && err != errCatchupsRunning {
|
||||
s.RateLimitWarnf("Failed to install snapshot for '%s > %s' [%s]: %v", mset.acc.Name, mset.name(), n.Group(), err)
|
||||
// If this is the first failure, reduce the interval of the snapshot timer.
|
||||
// This ensures we're not waiting too long for snapshotting to eventually become forced.
|
||||
if failedSnapshots == 0 {
|
||||
t.Reset(compactMinInterval)
|
||||
}
|
||||
|
||||
snapshotting = true
|
||||
if fallbackSnapshot {
|
||||
_, err = c.InstallSnapshot(snap)
|
||||
handleInstallResult(err)
|
||||
} else {
|
||||
started := s.startGoRoutine(func() {
|
||||
defer s.grWG.Done()
|
||||
|
||||
_, err := c.InstallSnapshot(snap)
|
||||
|
||||
snapMu.Lock()
|
||||
defer snapMu.Unlock()
|
||||
handleInstallResult(err)
|
||||
})
|
||||
if !started {
|
||||
snapshotting = false
|
||||
c.Abort()
|
||||
}
|
||||
failedSnapshots++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3253,6 +3317,9 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
|
||||
select {
|
||||
case <-s.quitCh:
|
||||
// Server shutting down, but we might receive this before qch, so try to snapshot.
|
||||
snapMu.Lock()
|
||||
fallbackSnapshot = true
|
||||
snapMu.Unlock()
|
||||
doSnapshot(false)
|
||||
return
|
||||
case <-mqch:
|
||||
@@ -3260,6 +3327,9 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
|
||||
// Don't snapshot if not shutting down, monitor goroutine could be going away
|
||||
// on a scale down or a remove for example.
|
||||
if s.isShuttingDown() {
|
||||
snapMu.Lock()
|
||||
fallbackSnapshot = true
|
||||
snapMu.Unlock()
|
||||
doSnapshot(false)
|
||||
}
|
||||
return
|
||||
@@ -3459,7 +3529,9 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps
|
||||
|
||||
case <-t.C:
|
||||
// Start forcing snapshots if they failed previously.
|
||||
snapMu.Lock()
|
||||
forceIfFailed := failedSnapshots > 0
|
||||
snapMu.Unlock()
|
||||
doSnapshot(forceIfFailed)
|
||||
|
||||
case <-uch:
|
||||
@@ -5038,8 +5110,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
|
||||
s.Warnf("JetStream cluster detected stream remapping for '%s > %s' from %q to %q",
|
||||
acc, cfg.Name, osa.Group.Name, sa.Group.Name)
|
||||
mset.removeNode()
|
||||
mset.signalMonitorQuit()
|
||||
mset.monitorWg.Wait()
|
||||
mset.stopMonitoring()
|
||||
alreadyRunning, needsNode = false, true
|
||||
// Make sure to clear from original.
|
||||
js.mu.Lock()
|
||||
@@ -5080,8 +5151,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss
|
||||
} else if numReplicas == 1 && alreadyRunning {
|
||||
// We downgraded to R1. Make sure we cleanup the raft node and the stream monitor.
|
||||
mset.removeNode()
|
||||
mset.signalMonitorQuit()
|
||||
mset.monitorWg.Wait()
|
||||
mset.stopMonitoring()
|
||||
// In case we need to shutdown the cluster specific subs, etc.
|
||||
mset.mu.Lock()
|
||||
// Stop responding to sync requests.
|
||||
@@ -6267,6 +6337,9 @@ func (js *jetStream) consumerAssignmentsOrInflightSeq(account, stream string) it
|
||||
}
|
||||
}
|
||||
sa := js.streamAssignment(account, stream)
|
||||
if sa == nil {
|
||||
return
|
||||
}
|
||||
for _, ca := range sa.consumers {
|
||||
// Skip if we already iterated over it as inflight.
|
||||
if _, ok := inflight[ca.Name]; ok {
|
||||
@@ -6416,33 +6489,16 @@ func (js *jetStream) monitorConsumer(o *consumer, ca *consumerAssignment) {
|
||||
}
|
||||
|
||||
if snap, err := o.store.EncodedState(); err == nil {
|
||||
hash := highwayhash.Sum(snap, key)
|
||||
// If the state hasn't changed but the log has gone way over
|
||||
// the compaction size then we will want to compact anyway.
|
||||
// This can happen for example when a pull consumer fetches a
|
||||
// lot on an idle stream, log entries get distributed but the
|
||||
// state never changes, therefore the log never gets compacted.
|
||||
if !bytes.Equal(hash[:], lastSnap) || ne >= compactNumMin || nb >= compactSizeMin {
|
||||
// If we had a significant number of failed snapshots, start relaxing Raft-layer checks
|
||||
// to force it through. We might have been catching up a peer for a long period, and this
|
||||
// protects our log size from growing indefinitely.
|
||||
forceSnapshot := failedSnapshots > 4
|
||||
if err := n.InstallSnapshot(snap, forceSnapshot); err == nil {
|
||||
lastSnap, lastSnapTime = hash[:], time.Now()
|
||||
// If there was a failed snapshot before, we reduced the timer's interval.
|
||||
// Reset it back to the original interval now.
|
||||
if failedSnapshots > 0 {
|
||||
t.Reset(compactInterval + rci)
|
||||
}
|
||||
failedSnapshots = 0
|
||||
} else if err != errNoSnapAvailable && err != errNodeClosed && err != errCatchupsRunning {
|
||||
s.RateLimitWarnf("Failed to install snapshot for '%s > %s > %s' [%s]: %v", o.acc.Name, ca.Stream, ca.Name, n.Group(), err)
|
||||
// If this is the first failure, reduce the interval of the snapshot timer.
|
||||
// This ensures we're not waiting too long for snapshotting to eventually become forced.
|
||||
if failedSnapshots == 0 {
|
||||
t.Reset(compactMinInterval)
|
||||
}
|
||||
failedSnapshots++
|
||||
// If we had a significant number of failed snapshots, start relaxing Raft-layer checks
|
||||
// to force it through. We might have been catching up a peer for a long period, and this
|
||||
// protects our log size from growing indefinitely.
|
||||
forceSnapshot := failedSnapshots > 4
|
||||
if err := n.InstallSnapshot(snap, forceSnapshot); err == nil {
|
||||
lastSnapTime = time.Now()
|
||||
// If there was a failed snapshot before, we reduced the timer's interval.
|
||||
// Reset it back to the original interval now.
|
||||
if failedSnapshots > 0 {
|
||||
t.Reset(compactInterval + rci)
|
||||
}
|
||||
failedSnapshots = 0
|
||||
} else if err != errNoSnapAvailable && err != errNodeClosed && err != errCatchupsRunning {
|
||||
@@ -6785,8 +6841,19 @@ func (js *jetStream) applyConsumerEntries(o *consumer, ce *CommittedEntry, isLea
|
||||
o.streamNumPending()
|
||||
o.signalNewMessages()
|
||||
s, a := o.srv, o.acc
|
||||
o.mu.Unlock()
|
||||
if reply != _EMPTY_ {
|
||||
if reply == _EMPTY_ {
|
||||
o.mu.Unlock()
|
||||
} else if internal, ok := o.rsm[reply]; !ok {
|
||||
o.mu.Unlock()
|
||||
} else {
|
||||
delete(o.rsm, reply)
|
||||
o.mu.Unlock()
|
||||
|
||||
// Check if the reset request needs to be answered on the system account.
|
||||
// This will happen for replicated sourcing consumers that get reset as part of a create/update.
|
||||
if internal {
|
||||
a = nil
|
||||
}
|
||||
var resp = JSApiConsumerResetResponse{ApiResponse: ApiResponse{Type: JSApiConsumerResetResponseType}}
|
||||
resp.ConsumerInfo = setDynamicConsumerInfoMetadata(o.info())
|
||||
resp.ResetSeq = sseq
|
||||
@@ -7179,8 +7246,9 @@ func (js *jetStream) processStreamAssignmentResults(sub *subscription, c *client
|
||||
}
|
||||
s.Warnf("Stream assignment for '%s > %s' rejected by assigned member: %v", sa.Client.serviceAccount(), sa.Config.Name, apiErr)
|
||||
sa.err = NewJSClusterNotAssignedError()
|
||||
cc.meta.Propose(encodeDeleteStreamAssignment(sa))
|
||||
cc.trackInflightStreamProposal(result.Account, sa, true)
|
||||
if err := cc.meta.Propose(encodeDeleteStreamAssignment(sa)); err == nil {
|
||||
cc.trackInflightStreamProposal(result.Account, sa, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8278,26 +8346,6 @@ func (s *Server) jsClusteredStreamUpdateRequest(ci *ClientInfo, acc *Account, su
|
||||
if len(rg.Peers) == 1 {
|
||||
rg.Name = groupNameForStream(rg.Peers, rg.Storage)
|
||||
}
|
||||
for _, peer := range rg.Peers {
|
||||
if len(selected) == newCfg.Replicas {
|
||||
break
|
||||
}
|
||||
if peer == curLeader {
|
||||
continue
|
||||
}
|
||||
if si, ok := s.nodeToInfo.Load(peer); ok && si != nil {
|
||||
if si.(nodeInfo).offline {
|
||||
continue
|
||||
}
|
||||
selected = append(selected, peer)
|
||||
}
|
||||
}
|
||||
rg.Peers = selected
|
||||
// Single nodes are not recorded by the NRG layer so we can rename.
|
||||
// MUST do this, otherwise a scaleup afterward could potentially lead to inconsistencies.
|
||||
if len(rg.Peers) == 1 {
|
||||
rg.Name = groupNameForStream(rg.Peers, rg.Storage)
|
||||
}
|
||||
}
|
||||
|
||||
// Need to remap any consumers.
|
||||
@@ -8450,7 +8498,11 @@ func (s *Server) jsClusteredStreamUpdateRequest(ci *ClientInfo, acc *Account, su
|
||||
rg.Preferred = _EMPTY_
|
||||
}
|
||||
|
||||
sa := &streamAssignment{Group: rg, Sync: osa.Sync, Created: osa.Created, Config: newCfg, Subject: subject, Reply: reply, Client: ci}
|
||||
syncSubject := osa.Sync
|
||||
if syncSubject == _EMPTY_ {
|
||||
syncSubject = syncSubjForStream()
|
||||
}
|
||||
sa := &streamAssignment{Group: rg, Sync: syncSubject, Created: osa.Created, Config: newCfg, Subject: subject, Reply: reply, Client: ci}
|
||||
if err := meta.Propose(encodeUpdateStreamAssignment(sa)); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -9280,13 +9332,13 @@ func (s *Server) jsClusteredConsumerRequest(ci *ClientInfo, acc *Account, subjec
|
||||
// we're likely updating an existing consumer, so don't count it. Otherwise
|
||||
// we will incorrectly return NewJSMaximumConsumersLimitError for an update.
|
||||
if oname == _EMPTY_ || js.consumerAssignmentOrInflight(acc.Name, stream, oname) == nil {
|
||||
// Don't count DIRECTS.
|
||||
// Don't count direct/sourcing consumers.
|
||||
total := 0
|
||||
for ca := range js.consumerAssignmentsOrInflightSeq(acc.Name, stream) {
|
||||
if ca.unsupported != nil {
|
||||
continue
|
||||
}
|
||||
if ca.Config != nil && !ca.Config.Direct {
|
||||
if ca.Config != nil && !ca.Config.Direct && !ca.Config.Sourcing {
|
||||
total++
|
||||
}
|
||||
}
|
||||
@@ -9436,10 +9488,16 @@ func (s *Server) jsClusteredConsumerRequest(ci *ClientInfo, acc *Account, subjec
|
||||
s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
|
||||
return
|
||||
}
|
||||
subjects := gatherSubjectFilters(cfg.FilterSubject, cfg.FilterSubjects)
|
||||
for oca := range js.consumerAssignmentsOrInflightSeq(acc.Name, stream) {
|
||||
if oca.Name == oname {
|
||||
if oca.Name == oname || oca.Config.Direct || oca.Config.Sourcing {
|
||||
continue
|
||||
}
|
||||
if len(subjects) == 0 {
|
||||
resp.Error = NewJSConsumerWQMultipleUnfilteredError()
|
||||
s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp))
|
||||
return
|
||||
}
|
||||
for _, psubj := range gatherSubjectFilters(oca.Config.FilterSubject, oca.Config.FilterSubjects) {
|
||||
for _, subj := range subjects {
|
||||
if SubjectsCollide(subj, psubj) {
|
||||
@@ -9538,14 +9596,6 @@ func (s *Server) jsClusteredConsumerRequest(ci *ClientInfo, acc *Account, subjec
|
||||
if len(nca.Group.Peers) == 1 {
|
||||
nca.Group.Name = groupNameForConsumer(nca.Group.Peers, nca.Group.Storage)
|
||||
}
|
||||
// scale down by removing peers from the end
|
||||
newPeerSet = newPeerSet[len(newPeerSet)-rAfter:]
|
||||
nca.Group.Peers = newPeerSet
|
||||
// Single nodes are not recorded by the NRG layer so we can rename.
|
||||
// MUST do this, otherwise a scaleup afterward could potentially lead to inconsistencies.
|
||||
if len(nca.Group.Peers) == 1 {
|
||||
nca.Group.Name = groupNameForConsumer(nca.Group.Peers, nca.Group.Storage)
|
||||
}
|
||||
}
|
||||
|
||||
// Update config and client info on copy of existing.
|
||||
@@ -10440,9 +10490,13 @@ RETRY:
|
||||
if lseq >= snap.LastSeq {
|
||||
// We MUST ensure all data is flushed up to this point, if the store hadn't already.
|
||||
// Because the snapshot needs to represent what has been persisted.
|
||||
mset.flushAllPending()
|
||||
s.Noticef("Catchup for stream '%s > %s' complete (took %v)", mset.account(), mset.name(), time.Since(start).Round(time.Millisecond))
|
||||
return nil
|
||||
err = mset.flushAllPending()
|
||||
if err == nil {
|
||||
s.Noticef("Catchup for stream '%s > %s' complete (took %v)", mset.account(), mset.name(), time.Since(start).Round(time.Millisecond))
|
||||
} else {
|
||||
s.Noticef("Catchup for stream '%s > %s' errored: %v (took %v)", mset.account(), mset.name(), err, time.Since(start).Round(time.Millisecond))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure we do not spin and make things worse.
|
||||
|
||||
@@ -353,6 +353,9 @@ const (
|
||||
// JSMessageSchedulesRollupInvalidErr message schedules invalid rollup
|
||||
JSMessageSchedulesRollupInvalidErr ErrorIdentifier = 10192
|
||||
|
||||
// JSMessageSchedulesSchedulerInvalidErr message schedules invalid scheduler
|
||||
JSMessageSchedulesSchedulerInvalidErr ErrorIdentifier = 10212
|
||||
|
||||
// JSMessageSchedulesSourceInvalidErr message schedules source is invalid
|
||||
JSMessageSchedulesSourceInvalidErr ErrorIdentifier = 10203
|
||||
|
||||
@@ -787,6 +790,7 @@ var (
|
||||
JSMessageSchedulesDisabledErr: {Code: 400, ErrCode: 10188, Description: "message schedules is disabled"},
|
||||
JSMessageSchedulesPatternInvalidErr: {Code: 400, ErrCode: 10189, Description: "message schedules pattern is invalid"},
|
||||
JSMessageSchedulesRollupInvalidErr: {Code: 400, ErrCode: 10192, Description: "message schedules invalid rollup"},
|
||||
JSMessageSchedulesSchedulerInvalidErr: {Code: 400, ErrCode: 10212, Description: "message schedules invalid scheduler"},
|
||||
JSMessageSchedulesSourceInvalidErr: {Code: 400, ErrCode: 10203, Description: "message schedules source is invalid"},
|
||||
JSMessageSchedulesTTLInvalidErr: {Code: 400, ErrCode: 10191, Description: "message schedules invalid per-message TTL"},
|
||||
JSMessageSchedulesTargetInvalidErr: {Code: 400, ErrCode: 10190, Description: "message schedules target is invalid"},
|
||||
@@ -2173,6 +2177,16 @@ func NewJSMessageSchedulesRollupInvalidError(opts ...ErrorOption) *ApiError {
|
||||
return ApiErrors[JSMessageSchedulesRollupInvalidErr]
|
||||
}
|
||||
|
||||
// NewJSMessageSchedulesSchedulerInvalidError creates a new JSMessageSchedulesSchedulerInvalidErr error: "message schedules invalid scheduler"
|
||||
func NewJSMessageSchedulesSchedulerInvalidError(opts ...ErrorOption) *ApiError {
|
||||
eopts := parseOpts(opts)
|
||||
if ae, ok := eopts.err.(*ApiError); ok {
|
||||
return ae
|
||||
}
|
||||
|
||||
return ApiErrors[JSMessageSchedulesSchedulerInvalidErr]
|
||||
}
|
||||
|
||||
// NewJSMessageSchedulesSourceInvalidError creates a new JSMessageSchedulesSourceInvalidErr error: "message schedules source is invalid"
|
||||
func NewJSMessageSchedulesSourceInvalidError(opts ...ErrorOption) *ApiError {
|
||||
eopts := parseOpts(opts)
|
||||
|
||||
@@ -17,7 +17,7 @@ import "strconv"
|
||||
|
||||
const (
|
||||
// JSApiLevel is the maximum supported JetStream API level for this server.
|
||||
JSApiLevel int = 3
|
||||
JSApiLevel int = 4
|
||||
|
||||
JSRequiredLevelMetadataKey = "_nats.req.level"
|
||||
JSServerVersionMetadataKey = "_nats.ver"
|
||||
|
||||
+35
-15
@@ -103,17 +103,6 @@ type RaftNodeCheckpoint interface {
|
||||
InstallSnapshot(data []byte) (uint64, error)
|
||||
}
|
||||
|
||||
// RaftNodeCheckpoint is used as an alternative to a direct InstallSnapshot.
|
||||
// A checkpoint is created from CreateSnapshotCheckpoint and allows installing snapshots asynchronously,
|
||||
// as well as loading the last snapshot or entries between the last snapshot and the one we're about to create.
|
||||
// Abort can be called to cancel the snapshot installation at any time, or InstallSnapshot to install it.
|
||||
type RaftNodeCheckpoint interface {
|
||||
LoadLastSnapshot() (snap []byte, err error)
|
||||
AppendEntriesSeq() iter.Seq2[*appendEntry, error]
|
||||
Abort()
|
||||
InstallSnapshot(data []byte) (uint64, error)
|
||||
}
|
||||
|
||||
type WAL interface {
|
||||
Type() StorageType
|
||||
StoreMsg(subj string, hdr, msg []byte, ttl int64) (uint64, int64, error)
|
||||
@@ -260,6 +249,9 @@ type raft struct {
|
||||
scaleUp bool // The node is part of a scale up, puts us in observer mode until the log contains data.
|
||||
deleted bool // If the node was deleted.
|
||||
snapshotting bool // Snapshot is in progress.
|
||||
quorumPaused bool // Pause replication and quorum participation to prevent log growth during slow applies.
|
||||
|
||||
overrunCount uint64 // Counter of how many times we were overrun, either as follower or as leader.
|
||||
}
|
||||
|
||||
type proposedEntry struct {
|
||||
@@ -1557,12 +1549,20 @@ func (c *checkpoint) InstallSnapshot(data []byte) (uint64, error) {
|
||||
n.Unlock()
|
||||
err := writeFileWithSync(c.snapFile, encoded, defaultFilePerms)
|
||||
n.Lock()
|
||||
// On either failure path, drop the file we just wrote so it doesn't get
|
||||
// picked up by setupLastSnapshot on restart. Skip the remove if it's the
|
||||
// snapshot already adopted into n.snapfile for this term/applied.
|
||||
if err != nil {
|
||||
if c.snapFile != n.snapfile {
|
||||
os.Remove(c.snapFile)
|
||||
}
|
||||
// We could set write err here, but if this is a temporary situation, too many open files etc.
|
||||
// we want to retry and snapshots are not fatal.
|
||||
return 0, err
|
||||
} else if !n.snapshotting {
|
||||
// The checkpoint can be aborted at any time, don't continue if that happened.
|
||||
if c.snapFile != n.snapfile {
|
||||
os.Remove(c.snapFile)
|
||||
}
|
||||
return 0, errSnapAborted
|
||||
}
|
||||
|
||||
@@ -2979,6 +2979,16 @@ func (n *raft) handleForwardedRemovePeerProposal(sub *subscription, c *client, _
|
||||
n.RUnlock()
|
||||
return
|
||||
}
|
||||
if _, ok := n.peers[string(msg)]; !ok {
|
||||
n.debug("Ignoring forwarded peer removal proposal, peer not found")
|
||||
n.RUnlock()
|
||||
return
|
||||
}
|
||||
if len(n.peers) <= 1 {
|
||||
n.debug("Ignoring forwarded peer removal proposal, remove last node")
|
||||
n.RUnlock()
|
||||
return
|
||||
}
|
||||
prop := n.prop
|
||||
n.RUnlock()
|
||||
|
||||
@@ -3979,6 +3989,19 @@ func (n *raft) truncateWAL(term, index uint64) {
|
||||
// Set after we know we have truncated properly.
|
||||
n.pterm, n.pindex = term, index
|
||||
|
||||
// Invalidate cached entries the WAL no longer has.
|
||||
if index == 0 {
|
||||
if len(n.pae) > 0 {
|
||||
n.pae = make(map[uint64]*appendEntry)
|
||||
}
|
||||
} else {
|
||||
for k := range n.pae {
|
||||
if k > index {
|
||||
delete(n.pae, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're truncating an uncommitted membership change.
|
||||
if n.membChangeIndex > 0 && n.membChangeIndex > index {
|
||||
n.membChangeIndex = 0
|
||||
@@ -4333,9 +4356,6 @@ func (n *raft) processAppendEntry(ae *appendEntry, sub *subscription) {
|
||||
|
||||
// Inherit state from appendEntry with the leader's snapshot.
|
||||
hadPreviousSnapshot := n.snapfile != _EMPTY_
|
||||
n.pindex = ae.pindex
|
||||
n.pterm = ae.pterm
|
||||
n.commit = ae.pindex
|
||||
|
||||
snap := &snapshot{
|
||||
lastTerm: ae.pterm,
|
||||
|
||||
+4
-3
@@ -199,11 +199,12 @@ func (ms *MsgScheduling) getScheduledMessages(loadMsg func(seq uint64, smv *Stor
|
||||
ms.remove(seq)
|
||||
return true
|
||||
}
|
||||
rollup := getMessageScheduleRollup(sm.hdr)
|
||||
source := getMessageScheduleSource(sm.hdr)
|
||||
if source != _EMPTY_ {
|
||||
if sm = loadLast(source, &smv); sm == nil {
|
||||
ms.remove(seq)
|
||||
return true
|
||||
// Fall back to the scheduled message's own content if the source has no last message.
|
||||
if srcSm := loadLast(source, &srcSmv); srcSm != nil {
|
||||
sm = srcSm
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-5
@@ -806,11 +806,22 @@ func (a *Account) addStreamWithAssignment(config *StreamConfig, fsConfig *FileSt
|
||||
jsa.mu.Unlock()
|
||||
return nil, NewJSNoLimitsError()
|
||||
}
|
||||
js.mu.RLock()
|
||||
if isClustered {
|
||||
_, reserved = js.tieredStreamAndReservationCount(a.Name, tier, cfg)
|
||||
}
|
||||
if err := js.checkAllLimits(&selected, cfg, reserved, 0); err != nil {
|
||||
|
||||
// Skip if we're recovering.
|
||||
if !recovering {
|
||||
reserved := int64(0)
|
||||
if !isClustered {
|
||||
reserved = jsa.tieredReservation(tier, cfg)
|
||||
}
|
||||
jsa.mu.Unlock()
|
||||
js.mu.RLock()
|
||||
if isClustered {
|
||||
_, reserved = js.tieredStreamAndReservationCount(a.Name, tier, cfg)
|
||||
}
|
||||
if err := js.checkAllLimits(&selected, tier, cfg, reserved, 0); err != nil {
|
||||
js.mu.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
js.mu.RUnlock()
|
||||
jsa.mu.Lock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user