Files

3513 lines
93 KiB
Go

// Copyright 2018-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !race && !skip_no_race_tests && !skip_no_race_2_tests
package server
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"math/rand"
"net"
"os"
"path/filepath"
"reflect"
"runtime"
"slices"
"strconv"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
crand "crypto/rand"
"github.com/nats-io/nats-server/v2/server/gsl"
"github.com/nats-io/nats.go"
"github.com/nats-io/nuid"
)
// IMPORTANT: Tests in this file are not executed when running with the -race flag.
// The test name should be prefixed with TestNoRace so we can run only
// those tests: go test -run=TestNoRace ...
func TestNoRaceJetStreamClusterLeafnodeConnectPerf(t *testing.T) {
// Uncomment to run. Needs to be on a big machine. Do not want as part of Travis tests atm.
skip(t)
tmpl := strings.Replace(jsClusterAccountsTempl, "store_dir:", "domain: cloud, store_dir:", 1)
c := createJetStreamCluster(t, tmpl, "CLOUD", _EMPTY_, 3, 18033, true)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "STATE",
Subjects: []string{"STATE.GLOBAL.CELL1.*.>"},
Replicas: 3,
})
require_NoError(t, err)
tmpl = strings.Replace(jsClusterTemplWithSingleFleetLeafNode, "store_dir:", "domain: vehicle, store_dir:", 1)
var vinSerial int
genVIN := func() string {
vinSerial++
return fmt.Sprintf("7PDSGAALXNN%06d", vinSerial)
}
numVehicles := 500
for i := 0; i < numVehicles; i++ {
start := time.Now()
vin := genVIN()
ln := c.createLeafNodeWithTemplateNoSystemWithProto(vin, tmpl, "ws")
nc, js := jsClientConnect(t, ln)
_, err := js.AddStream(&nats.StreamConfig{
Name: "VEHICLE",
Subjects: []string{"STATE.GLOBAL.LOCAL.>"},
Sources: []*nats.StreamSource{{
Name: "STATE",
FilterSubject: fmt.Sprintf("STATE.GLOBAL.CELL1.%s.>", vin),
External: &nats.ExternalStream{
APIPrefix: "$JS.cloud.API",
DeliverPrefix: fmt.Sprintf("DELIVER.STATE.GLOBAL.CELL1.%s", vin),
},
}},
})
require_NoError(t, err)
// Create the sourced stream.
checkLeafNodeConnectedCount(t, ln, 1)
if elapsed := time.Since(start); elapsed > 2*time.Second {
t.Fatalf("Took too long to create leafnode %d connection: %v", i+1, elapsed)
}
nc.Close()
}
}
func TestNoRaceJetStreamClusterDifferentRTTInterestBasedStreamPreAck(t *testing.T) {
tmpl := `
listen: 127.0.0.1:-1
server_name: %s
jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
cluster {
name: "F3"
listen: 127.0.0.1:%d
routes = [%s]
}
accounts {
$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
}
`
// Route Ports
// "S1": 14622,
// "S2": 15622,
// "S3": 16622,
// S2 (stream leader) will have a slow path to S1 (via proxy) and S3 (consumer leader) will have a fast path.
// Do these in order, S1, S2 (proxy) then S3.
c := &cluster{t: t, servers: make([]*Server, 3), opts: make([]*Options, 3), name: "F3"}
// S1
// The route connection to S2 must be through a slow proxy
np12 := createNetProxy(10*time.Millisecond, 1024*1024*1024, 1024*1024*1024, "route://127.0.0.1:15622", true)
routes := fmt.Sprintf("%s, route://127.0.0.1:16622", np12.routeURL())
conf := fmt.Sprintf(tmpl, "S1", t.TempDir(), 14622, routes)
c.servers[0], c.opts[0] = RunServerWithConfig(createConfFile(t, []byte(conf)))
// S2
// The route connection to S1 must be through a slow proxy
np21 := createNetProxy(10*time.Millisecond, 1024*1024*1024, 1024*1024*1024, "route://127.0.0.1:14622", true)
routes = fmt.Sprintf("%s, route://127.0.0.1:16622", np21.routeURL())
conf = fmt.Sprintf(tmpl, "S2", t.TempDir(), 15622, routes)
c.servers[1], c.opts[1] = RunServerWithConfig(createConfFile(t, []byte(conf)))
// S3
conf = fmt.Sprintf(tmpl, "S3", t.TempDir(), 16622, "route://127.0.0.1:14622, route://127.0.0.1:15622")
c.servers[2], c.opts[2] = RunServerWithConfig(createConfFile(t, []byte(conf)))
c.checkClusterFormed()
c.waitOnClusterReady()
defer c.shutdown()
defer np12.stop()
defer np21.stop()
slow := c.servers[0] // Expecting pre-acks here.
sl := c.servers[1] // Stream leader, will publish here.
cl := c.servers[2] // Consumer leader, will consume & ack here.
snc, sjs := jsClientConnect(t, sl)
defer snc.Close()
cnc, cjs := jsClientConnect(t, cl)
defer cnc.Close()
// Now create the stream.
_, err := sjs.AddStream(&nats.StreamConfig{
Name: "EVENTS",
Subjects: []string{"EV.>"},
Replicas: 3,
Retention: nats.InterestPolicy,
})
require_NoError(t, err)
// Make sure it's leader is on S2.
checkFor(t, 20*time.Second, 200*time.Millisecond, func() error {
c.waitOnStreamLeader(globalAccountName, "EVENTS")
if s := c.streamLeader(globalAccountName, "EVENTS"); s != sl {
s.JetStreamStepdownStream(globalAccountName, "EVENTS")
return fmt.Errorf("Server %s is not stream leader yet", sl)
}
return nil
})
// Now create the consumer.
_, err = sjs.AddConsumer("EVENTS", &nats.ConsumerConfig{
Durable: "C",
AckPolicy: nats.AckExplicitPolicy,
DeliverSubject: "dx",
})
require_NoError(t, err)
// Make sure the consumer leader is on S3.
checkFor(t, 20*time.Second, 200*time.Millisecond, func() error {
c.waitOnConsumerLeader(globalAccountName, "EVENTS", "C")
if s := c.consumerLeader(globalAccountName, "EVENTS", "C"); s != cl {
s.JetStreamStepdownConsumer(globalAccountName, "EVENTS", "C")
return fmt.Errorf("Server %s is not consumer leader yet", sl)
}
return nil
})
_, err = cjs.Subscribe(_EMPTY_, func(msg *nats.Msg) {
msg.Ack()
}, nats.BindStream("EVENTS"), nats.Durable("C"), nats.ManualAck())
require_NoError(t, err)
// Publish directly on the stream leader to make it efficient.
for i := 0; i < 1_000; i++ {
_, err := sjs.PublishAsync("EV.PAID", []byte("ok"))
require_NoError(t, err)
}
select {
case <-sjs.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
mset, err := slow.GlobalAccount().lookupStream("EVENTS")
require_NoError(t, err)
checkFor(t, 10*time.Second, 200*time.Millisecond, func() error {
state := mset.state()
if state.LastSeq != 1000 {
return fmt.Errorf("Haven't received all messages yet (last seq %d)", state.LastSeq)
}
mset.mu.RLock()
preAcks := mset.preAcks
mset.mu.RUnlock()
if preAcks == nil {
return fmt.Errorf("Expected to have preAcks by now")
}
if state.Msgs == 0 {
mset.mu.RLock()
lp := len(mset.preAcks)
mset.mu.RUnlock()
if lp == 0 {
return nil
} else {
t.Fatalf("Expected no preAcks with no msgs, but got %d", lp)
}
}
return fmt.Errorf("Still have %d msgs left", state.Msgs)
})
}
func TestNoRaceCheckAckFloorWithVeryLargeFirstSeqAndNewConsumers(t *testing.T) {
s := RunBasicJetStreamServer(t)
defer s.Shutdown()
nc, _ := jsClientConnect(t, s)
defer nc.Close()
// Make sure to time bound here for the acksync call below.
js, err := nc.JetStream(nats.MaxWait(200 * time.Millisecond))
require_NoError(t, err)
_, err = js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"wq-req"},
Retention: nats.WorkQueuePolicy,
})
require_NoError(t, err)
largeFirstSeq := uint64(1_200_000_000)
err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: largeFirstSeq})
require_NoError(t, err)
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
require_True(t, si.State.FirstSeq == largeFirstSeq)
// Add a simple request to the stream.
sendStreamMsg(t, nc, "wq-req", "HELP")
sub, err := js.PullSubscribe("wq-req", "dlc")
require_NoError(t, err)
msgs, err := sub.Fetch(1)
require_NoError(t, err)
require_True(t, len(msgs) == 1)
// The bug is around the checkAckFloor walking the sequences from current ackfloor
// to the first sequence of the stream. We time bound the max wait with the js context
// to 200ms. Since checkAckFloor is spinning and holding up processing of acks this will fail.
// We will short circuit new consumers to fix this one.
require_NoError(t, msgs[0].AckSync())
// Now do again so we move past the new consumer with no ack floor situation.
err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 2 * largeFirstSeq})
require_NoError(t, err)
si, err = js.StreamInfo("TEST")
require_NoError(t, err)
require_True(t, si.State.FirstSeq == 2*largeFirstSeq)
sendStreamMsg(t, nc, "wq-req", "MORE HELP")
// We check this one directly for this use case.
mset, err := s.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
o := mset.lookupConsumer("dlc")
require_True(t, o != nil)
// Purge will move the stream floor by default, so force into the situation where it is back to largeFirstSeq.
// This will not trigger the new consumer logic, but will trigger a walk of the sequence space.
// Fix will be to walk the lesser of the two linear spaces.
o.mu.Lock()
o.asflr = largeFirstSeq
o.mu.Unlock()
done := make(chan bool)
go func() {
o.checkAckFloor()
done <- true
}()
select {
case <-done:
return
case <-time.After(time.Second):
t.Fatalf("Check ack floor taking too long!")
}
}
func TestNoRaceReplicatedMirrorWithLargeStartingSequenceOverLeafnode(t *testing.T) {
// Cluster B
tmpl := strings.Replace(jsClusterTempl, "store_dir:", "domain: B, store_dir:", 1)
c := createJetStreamCluster(t, tmpl, "B", _EMPTY_, 3, 22020, true)
defer c.shutdown()
// Cluster A
// Domain is "A'
lc := c.createLeafNodesWithStartPortAndDomain("A", 3, 22110, "A")
defer lc.shutdown()
lc.waitOnClusterReady()
// Create a stream on B (HUB/CLOUD) and set its starting sequence very high.
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo"},
Replicas: 3,
})
require_NoError(t, err)
err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 1_000_000_000})
require_NoError(t, err)
// Send in a small amount of messages.
for i := 0; i < 1000; i++ {
sendStreamMsg(t, nc, "foo", "Hello")
}
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
require_True(t, si.State.FirstSeq == 1_000_000_000)
// Now try to create a replicated mirror on the leaf cluster.
lnc, ljs := jsClientConnect(t, lc.randomServer())
defer lnc.Close()
_, err = ljs.AddStream(&nats.StreamConfig{
Name: "TEST",
Mirror: &nats.StreamSource{
Name: "TEST",
Domain: "B",
},
})
require_NoError(t, err)
// Make sure we sync quickly.
checkFor(t, time.Second, 200*time.Millisecond, func() error {
si, err = ljs.StreamInfo("TEST")
require_NoError(t, err)
if si.State.Msgs == 1000 && si.State.FirstSeq == 1_000_000_000 {
return nil
}
return fmt.Errorf("Mirror state not correct: %+v", si.State)
})
}
func TestNoRaceBinaryStreamSnapshotEncodingBasic(t *testing.T) {
s := RunBasicJetStreamServer(t)
defer s.Shutdown()
nc, js := jsClientConnect(t, s)
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"*"},
MaxMsgsPerSubject: 1,
})
require_NoError(t, err)
// Set first key
sendStreamMsg(t, nc, "key:1", "hello")
// Set Second key but keep updating it, causing a laggard pattern.
value := bytes.Repeat([]byte("Z"), 8*1024)
for i := 0; i <= 1000; i++ {
_, err := js.PublishAsync("key:2", value)
require_NoError(t, err)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// Now do more of swiss cheese style.
for i := 3; i <= 1000; i++ {
key := fmt.Sprintf("key:%d", i)
_, err := js.PublishAsync(key, value)
require_NoError(t, err)
// Send it twice to create hole right behind it, like swiss cheese.
_, err = js.PublishAsync(key, value)
require_NoError(t, err)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// Make for round numbers for stream state.
sendStreamMsg(t, nc, "key:2", "hello")
sendStreamMsg(t, nc, "key:2", "world")
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
require_True(t, si.State.FirstSeq == 1)
require_True(t, si.State.LastSeq == 3000)
require_True(t, si.State.Msgs == 1000)
require_True(t, si.State.NumDeleted == 2000)
mset, err := s.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
snap, err := mset.store.EncodedStreamState(0)
require_NoError(t, err)
// Now decode the snapshot.
ss, err := DecodeStreamState(snap)
require_NoError(t, err)
require_Equal(t, ss.FirstSeq, 1)
require_Equal(t, ss.LastSeq, 3000)
require_Equal(t, ss.Msgs, 1000)
require_Equal(t, ss.Deleted.NumDeleted(), 2000)
}
func TestNoRaceFilestoreBinaryStreamSnapshotEncodingLargeGaps(t *testing.T) {
storeDir := t.TempDir()
fcfg := FileStoreConfig{
StoreDir: storeDir,
BlockSize: 512, // Small on purpose to create a lot of blks.
}
fs, err := newFileStore(fcfg, StreamConfig{Name: "zzz", Subjects: []string{"zzz"}, Storage: FileStorage})
require_NoError(t, err)
defer fs.Stop()
subj, msg := "zzz", bytes.Repeat([]byte("X"), 128)
numMsgs := 20_000
fs.StoreMsg(subj, nil, msg, 0)
for i := 2; i < numMsgs; i++ {
seq, _, err := fs.StoreMsg(subj, nil, nil, 0)
require_NoError(t, err)
fs.RemoveMsg(seq)
}
fs.StoreMsg(subj, nil, msg, 0)
// The tombstones from above will only be cleaned up when syncing blocks.
fs.syncBlocks()
snap, err := fs.EncodedStreamState(0)
require_NoError(t, err)
require_LessThan(t, len(snap), 512)
// Now decode the snapshot.
ss, err := DecodeStreamState(snap)
require_NoError(t, err)
require_Equal(t, ss.FirstSeq, 1)
require_Equal(t, ss.LastSeq, 20_000)
require_Equal(t, ss.Msgs, 2)
require_Equal(t, len(ss.Deleted), 1)
require_Equal(t, ss.Deleted.NumDeleted(), 19_998)
}
func TestNoRaceJetStreamClusterStreamSnapshotCatchup(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
// Client based API
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"*"},
MaxMsgsPerSubject: 1,
Replicas: 3,
})
require_NoError(t, err)
msg := []byte("Hello World")
_, err = js.Publish("foo", msg)
require_NoError(t, err)
for i := 1; i < 1000; i++ {
_, err := js.PublishAsync("bar", msg)
require_NoError(t, err)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
sr := c.randomNonStreamLeader(globalAccountName, "TEST")
sr.Shutdown()
// In case we were connected to sr.
nc, js = jsClientConnect(t, c.randomServer())
defer nc.Close()
// Now create a large gap.
for i := 0; i < 50_000; i++ {
_, err := js.PublishAsync("bar", msg)
require_NoError(t, err)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(10 * time.Second):
t.Fatalf("Did not receive completion signal")
}
sl := c.streamLeader(globalAccountName, "TEST")
sl.JetStreamSnapshotStream(globalAccountName, "TEST")
sr = c.restartServer(sr)
c.checkClusterFormed()
c.waitOnServerCurrent(sr)
c.waitOnStreamCurrent(sr, globalAccountName, "TEST")
mset, err := sr.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
// Make sure it's caught up
var state StreamState
mset.store.FastState(&state)
require_Equal(t, state.Msgs, 2)
require_Equal(t, state.FirstSeq, 1)
require_Equal(t, state.LastSeq, 51_000)
require_Equal(t, state.NumDeleted, 51_000-2)
sr.Shutdown()
_, err = js.Publish("baz", msg)
require_NoError(t, err)
sl.JetStreamSnapshotStream(globalAccountName, "TEST")
sr = c.restartServer(sr)
c.checkClusterFormed()
c.waitOnServerCurrent(sr)
c.waitOnStreamCurrent(sr, globalAccountName, "TEST")
mset, err = sr.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
mset.store.FastState(&state)
require_Equal(t, state.Msgs, 3)
require_Equal(t, state.FirstSeq, 1)
require_Equal(t, state.LastSeq, 51_001)
require_Equal(t, state.NumDeleted, 51_001-3)
}
func TestNoRaceStoreStreamEncoderDecoder(t *testing.T) {
cfg := &StreamConfig{
Name: "zzz",
Subjects: []string{"*"},
MaxMsgsPer: 1,
Storage: MemoryStorage,
}
ms, err := newMemStore(cfg)
require_NoError(t, err)
defer ms.Stop()
fs, err := newFileStore(
FileStoreConfig{StoreDir: t.TempDir()},
StreamConfig{Name: "zzz", Subjects: []string{"*"}, MaxMsgsPer: 1, Storage: FileStorage},
)
require_NoError(t, err)
defer fs.Stop()
const seed = 2222222
msg := bytes.Repeat([]byte("ABC"), 33) // ~100bytes
maxEncodeTime := 2 * time.Second
maxEncodeSize := 700 * 1024
test := func(t *testing.T, gs StreamStore) {
t.Parallel()
prand := rand.New(rand.NewSource(seed))
tick := time.NewTicker(time.Second)
defer tick.Stop()
done := time.NewTimer(10 * time.Second)
for running := true; running; {
select {
case <-tick.C:
var state StreamState
gs.FastState(&state)
if state.NumDeleted == 0 {
continue
}
start := time.Now()
snap, err := gs.EncodedStreamState(0)
require_NoError(t, err)
elapsed := time.Since(start)
// Should take <1ms without race but if CI/CD is slow we will give it a bit of room.
if elapsed > maxEncodeTime {
t.Logf("Encode took longer then expected: %v", elapsed)
}
if len(snap) > maxEncodeSize {
t.Fatalf("Expected snapshot size < %v got %v", friendlyBytes(maxEncodeSize), friendlyBytes(len(snap)))
}
ss, err := DecodeStreamState(snap)
require_True(t, len(ss.Deleted) > 0)
require_NoError(t, err)
case <-done.C:
running = false
default:
key := strconv.Itoa(prand.Intn(256_000))
gs.StoreMsg(key, nil, msg, 0)
}
}
}
for _, gs := range []StreamStore{ms, fs} {
switch gs.(type) {
case *memStore:
t.Run("MemStore", func(t *testing.T) {
test(t, gs)
})
case *fileStore:
t.Run("FileStore", func(t *testing.T) {
test(t, gs)
})
}
}
}
func TestNoRaceJetStreamClusterKVWithServerKill(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
// Setup the KV bucket and use for making assertions.
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.CreateKeyValue(&nats.KeyValueConfig{
Bucket: "TEST",
Replicas: 3,
History: 10,
})
require_NoError(t, err)
// Total number of keys to range over.
numKeys := 50
// ID is the server id to explicitly connect to.
work := func(ctx context.Context, wg *sync.WaitGroup, id int) {
defer wg.Done()
nc, js := jsClientConnectEx(t, c.servers[id], []nats.JSOpt{nats.Context(ctx)})
defer nc.Close()
kv, err := js.KeyValue("TEST")
require_NoError(t, err)
// 100 messages a second for each single client.
tk := time.NewTicker(10 * time.Millisecond)
defer tk.Stop()
for {
select {
case <-ctx.Done():
return
case <-tk.C:
// Pick a random key within the range.
k := fmt.Sprintf("key.%d", rand.Intn(numKeys))
// Attempt to get a key.
e, err := kv.Get(k)
// If found, attempt to update or delete.
if err == nil {
if rand.Intn(10) < 3 {
kv.Delete(k, nats.LastRevision(e.Revision()))
} else {
kv.Update(k, nil, e.Revision())
}
} else if errors.Is(err, nats.ErrKeyNotFound) {
kv.Create(k, nil)
}
}
}
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var wg sync.WaitGroup
wg.Add(3)
go work(ctx, &wg, 0)
go work(ctx, &wg, 1)
go work(ctx, &wg, 2)
time.Sleep(time.Second)
// Simulate server stop and restart.
for i := 0; i < 7; i++ {
s := c.randomServer()
s.Shutdown()
c.waitOnLeader()
c.waitOnStreamLeader(globalAccountName, "KV_TEST")
// Wait for a bit and then start the server again.
time.Sleep(time.Duration(rand.Intn(1250)) * time.Millisecond)
s = c.restartServer(s)
c.waitOnServerCurrent(s)
c.waitOnLeader()
c.waitOnStreamLeader(globalAccountName, "KV_TEST")
c.waitOnPeerCount(3)
}
// Stop the workload.
cancel()
wg.Wait()
type fullState struct {
state StreamState
lseq uint64
clfs uint64
}
grabState := func(mset *stream) *fullState {
mset.mu.RLock()
defer mset.mu.RUnlock()
var state StreamState
mset.store.FastState(&state)
return &fullState{state, mset.lseq, mset.clfs}
}
grabStore := func(mset *stream) map[string][]uint64 {
mset.mu.RLock()
store := mset.store
mset.mu.RUnlock()
var state StreamState
store.FastState(&state)
storeMap := make(map[string][]uint64)
for seq := state.FirstSeq; seq <= state.LastSeq; seq++ {
if sm, err := store.LoadMsg(seq, nil); err == nil {
storeMap[sm.subj] = append(storeMap[sm.subj], sm.seq)
}
}
return storeMap
}
checkFor(t, 10*time.Second, 500*time.Millisecond, func() error {
// Current stream leader.
sl := c.streamLeader(globalAccountName, "KV_TEST")
mset, err := sl.GlobalAccount().lookupStream("KV_TEST")
require_NoError(t, err)
lstate := grabState(mset)
golden := grabStore(mset)
// Report messages per server.
for _, s := range c.servers {
if s == sl {
continue
}
mset, err := s.GlobalAccount().lookupStream("KV_TEST")
require_NoError(t, err)
state := grabState(mset)
if !reflect.DeepEqual(state, lstate) {
return fmt.Errorf("Expected follower state\n%+v\nto match leader's\n %+v", state, lstate)
}
sm := grabStore(mset)
if !reflect.DeepEqual(sm, golden) {
t.Fatalf("Expected follower store for %v\n%+v\nto match leader's %v\n %+v", s, sm, sl, golden)
}
}
return nil
})
}
func TestNoRaceFileStoreLargeMsgsAndFirstMatching(t *testing.T) {
sd := t.TempDir()
fs, err := newFileStore(
FileStoreConfig{StoreDir: sd, BlockSize: 8 * 1024 * 1024},
StreamConfig{Name: "zzz", Subjects: []string{">"}, Storage: FileStorage})
require_NoError(t, err)
defer fs.Stop()
for i := 0; i < 150_000; i++ {
fs.StoreMsg(fmt.Sprintf("foo.bar.%d", i), nil, nil, 0)
}
for i := 0; i < 150_000; i++ {
fs.StoreMsg(fmt.Sprintf("foo.baz.%d", i), nil, nil, 0)
}
require_Equal(t, fs.numMsgBlocks(), 2)
fs.mu.RLock()
mb := fs.blks[1]
fs.mu.RUnlock()
fseq := atomic.LoadUint64(&mb.first.seq)
// The -40 leaves enough mb.fss entries to kick in linear scan.
for seq := fseq; seq < 300_000-40; seq++ {
fs.RemoveMsg(uint64(seq))
}
start := time.Now()
fs.LoadNextMsg("*.baz.*", true, fseq, nil)
require_True(t, time.Since(start) < 200*time.Microsecond)
// Now remove more to kick into non-linear logic.
for seq := 300_000 - 40; seq < 300_000; seq++ {
fs.RemoveMsg(uint64(seq))
}
start = time.Now()
fs.LoadNextMsg("*.baz.*", true, fseq, nil)
require_True(t, time.Since(start) < 200*time.Microsecond)
}
func TestNoRaceWSNoCorruptionWithFrameSizeLimit(t *testing.T) {
testWSNoCorruptionWithFrameSizeLimit(t, 50000)
}
func TestNoRaceJetStreamAPIDispatchQueuePending(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
// Setup the KV bucket and use for making assertions.
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo.*.*"},
})
require_NoError(t, err)
// Queue up 500k messages all with different subjects.
// We want to make num pending for a consumer expensive, so a large subject
// space and wildcards for now does the trick.
toks := []string{"foo", "bar", "baz"} // for second token.
for i := 1; i <= 500_000; i++ {
subj := fmt.Sprintf("foo.%s.%d", toks[rand.Intn(len(toks))], i)
_, err := js.PublishAsync(subj, nil, nats.StallWait(time.Second))
require_NoError(t, err)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(20 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// To back up our pending queue we will create lots of filtered, with wildcards, R1 consumers
// from a different server then the one hosting the stream.
// ok to share this connection here.
sldr := c.streamLeader(globalAccountName, "TEST")
for _, s := range c.servers {
if s != sldr {
nc, js = jsClientConnect(t, s)
defer nc.Close()
break
}
}
ngr, ncons := 100, 10
startCh, errCh := make(chan bool), make(chan error, ngr)
var wg, swg sync.WaitGroup
wg.Add(ngr)
swg.Add(ngr)
// The wildcard in the filter subject is the key.
cfg := &nats.ConsumerConfig{FilterSubject: "foo.*.22"}
var tt atomic.Int64
for i := 0; i < ngr; i++ {
go func() {
defer wg.Done()
swg.Done()
// Make them all fire at once.
<-startCh
for i := 0; i < ncons; i++ {
start := time.Now()
if _, err := js.AddConsumer("TEST", cfg); err != nil {
errCh <- err
t.Logf("Got err creating consumer: %v", err)
}
elapsed := time.Since(start)
tt.Add(int64(elapsed))
}
}()
}
swg.Wait()
close(startCh)
time.Sleep(time.Millisecond)
jsz, _ := sldr.Jsz(nil)
// This could be 0 legit, so just log, don't fail.
if jsz.JetStreamStats.API.Inflight == 0 {
t.Log("Expected a non-zero inflight")
}
wg.Wait()
if len(errCh) > 0 {
t.Fatalf("Expected no errors, got %d: %v", len(errCh), <-errCh)
}
}
func TestNoRaceJetStreamMirrorAndSourceConsumerFailBackoff(t *testing.T) {
owt := srcConsumerWaitTime
srcConsumerWaitTime = 2 * time.Second
defer func() { srcConsumerWaitTime = owt }()
// Check calculations first.
for i := 1; i <= 20; i++ {
backoff := calculateRetryBackoff(i)
if i < 12 {
require_Equal(t, backoff, time.Duration(i)*10*time.Second)
} else {
require_Equal(t, backoff, retryMaximum)
}
}
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo.*.*"},
})
require_NoError(t, err)
sl := c.streamLeader(globalAccountName, "TEST")
// Create a mirror.
ml := sl
// Make sure not on the same server. Should not happened in general but possible.
for ml == sl {
js.DeleteStream("MIRROR")
_, err = js.AddStream(&nats.StreamConfig{
Name: "MIRROR",
Mirror: &nats.StreamSource{Name: "TEST"},
})
require_NoError(t, err)
ml = c.streamLeader(globalAccountName, "MIRROR")
}
// Create a source.
srcl := sl
for srcl == sl {
js.DeleteStream("SOURCE")
_, err = js.AddStream(&nats.StreamConfig{
Name: "SOURCE",
Sources: []*nats.StreamSource{{Name: "TEST"}},
})
require_NoError(t, err)
srcl = c.streamLeader(globalAccountName, "MIRROR")
}
// Create sub to watch for the consumer create requests.
nc, _ = jsClientConnect(t, ml)
defer nc.Close()
sub := natsSubSync(t, nc, "$JS.API.CONSUMER.CREATE.>")
// Kill the server where the source is..
sldr := c.streamLeader(globalAccountName, "TEST")
sldr.Shutdown()
// Wait for just greater than 5s. We should only see 1 request during this time.
time.Sleep(6 * time.Second)
// There should have been 2 requests, one for mirror, one for source
n, _, _ := sub.Pending()
require_Equal(t, n, 2)
var mreq, sreq int
for i := 0; i < 2; i++ {
msg := natsNexMsg(t, sub, time.Second)
if bytes.Contains(msg.Data, []byte("$JS.M.")) {
mreq++
} else if bytes.Contains(msg.Data, []byte("$JS.S.")) {
sreq++
}
}
if mreq != 1 || sreq != 1 {
t.Fatalf("Consumer create captures invalid: mreq=%v sreq=%v", mreq, sreq)
}
// Now make sure that the fails is set properly.
mset, err := c.streamLeader(globalAccountName, "MIRROR").GlobalAccount().lookupStream("MIRROR")
require_NoError(t, err)
mset.mu.RLock()
fails := mset.mirror.fails
mset.mu.RUnlock()
require_Equal(t, fails, 1)
mset, err = c.streamLeader(globalAccountName, "SOURCE").GlobalAccount().lookupStream("SOURCE")
require_NoError(t, err)
mset.mu.RLock()
si := mset.sources["TEST > >"]
mset.mu.RUnlock()
require_True(t, si != nil)
require_Equal(t, si.fails, 1)
}
func TestNoRaceJetStreamClusterStreamCatchupLargeInteriorDeletes(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
cfg := &nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo.*"},
MaxMsgsPerSubject: 100,
Replicas: 1,
}
_, err := js.AddStream(cfg)
require_NoError(t, err)
msg := bytes.Repeat([]byte("Z"), 2*1024)
// We will create lots of interior deletes on our R1 then scale up.
_, err = js.Publish("foo.0", msg)
require_NoError(t, err)
// Create 50k messages randomly from 1-100
for i := 0; i < 50_000; i++ {
subj := fmt.Sprintf("foo.%d", rand.Intn(100)+1)
js.PublishAsync(subj, msg)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// Now create a large gap.
for i := 0; i < 100_000; i++ {
js.PublishAsync("foo.2", msg)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// Do 50k random again at end.
for i := 0; i < 50_000; i++ {
subj := fmt.Sprintf("foo.%d", rand.Intn(100)+1)
js.PublishAsync(subj, msg)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
cfg.Replicas = 2
_, err = js.UpdateStream(cfg)
require_NoError(t, err)
// Let catchup start.
c.waitOnStreamLeader(globalAccountName, "TEST")
nl := c.randomNonStreamLeader(globalAccountName, "TEST")
require_True(t, nl != nil)
mset, err := nl.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
checkFor(t, 10*time.Second, 500*time.Millisecond, func() error {
state := mset.state()
if state.Msgs == si.State.Msgs {
return nil
}
return fmt.Errorf("Msgs not equal %d vs %d", state.Msgs, si.State.Msgs)
})
}
func TestNoRaceJetStreamClusterBadRestartsWithHealthzPolling(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
cfg := &nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo.>"},
Replicas: 3,
}
_, err := js.AddStream(cfg)
require_NoError(t, err)
// We will poll healthz at a decent clip and make sure any restart logic works
// correctly with assets coming and going.
ch := make(chan struct{})
defer close(ch)
go func() {
for {
select {
case <-ch:
return
case <-time.After(50 * time.Millisecond):
for _, s := range c.servers {
s.healthz(nil)
}
}
}
}()
numConsumers := 500
consumers := make([]string, 0, numConsumers)
var wg sync.WaitGroup
for i := 0; i < numConsumers; i++ {
cname := fmt.Sprintf("CONS-%d", i+1)
consumers = append(consumers, cname)
wg.Add(1)
go func() {
defer wg.Done()
_, err := js.PullSubscribe("foo.>", cname, nats.BindStream("TEST"))
require_NoError(t, err)
}()
}
wg.Wait()
// Make sure all are reported.
c.waitOnAllCurrent()
checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
for _, s := range c.servers {
jsz, _ := s.Jsz(nil)
if jsz.Consumers != numConsumers {
return fmt.Errorf("%v wrong number of consumers: %d vs %d", s, jsz.Consumers, numConsumers)
}
}
return nil
})
// Now do same for streams.
numStreams := 200
streams := make([]string, 0, numStreams)
for i := 0; i < numStreams; i++ {
sname := fmt.Sprintf("TEST-%d", i+1)
streams = append(streams, sname)
wg.Add(1)
go func() {
defer wg.Done()
_, err := js.AddStream(&nats.StreamConfig{Name: sname, Replicas: 3})
require_NoError(t, err)
}()
}
wg.Wait()
// Make sure all are reported.
c.waitOnAllCurrent()
checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
for _, s := range c.servers {
jsz, _ := s.Jsz(nil)
if jsz.Streams != numStreams+1 {
return fmt.Errorf("%v wrong number of streams: %d vs %d", s, jsz.Streams, numStreams+1)
}
}
return nil
})
// Delete consumers.
for _, cname := range consumers {
err := js.DeleteConsumer("TEST", cname)
require_NoError(t, err)
}
// Make sure reporting goes to zero.
c.waitOnAllCurrent()
checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
for _, s := range c.servers {
jsz, _ := s.Jsz(nil)
if jsz.Consumers != 0 {
return fmt.Errorf("%v still has %d consumers", s, jsz.Consumers)
}
}
return nil
})
// Delete streams
for _, sname := range streams {
err := js.DeleteStream(sname)
require_NoError(t, err)
}
err = js.DeleteStream("TEST")
require_NoError(t, err)
// Make sure reporting goes to zero.
c.waitOnAllCurrent()
checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
for _, s := range c.servers {
jsz, _ := s.Jsz(nil)
if jsz.Streams != 0 {
return fmt.Errorf("%v still has %d streams", s, jsz.Streams)
}
}
return nil
})
}
func TestNoRaceJetStreamKVReplaceWithServerRestart(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, _ := jsClientConnect(t, c.randomServer())
defer nc.Close()
// Shorten wait time for disconnects.
js, err := nc.JetStream(nats.MaxWait(time.Second))
require_NoError(t, err)
kv, err := js.CreateKeyValue(&nats.KeyValueConfig{
Bucket: "TEST",
Replicas: 3,
})
require_NoError(t, err)
// Manually disable direct get on underlying stream, since this test
// relies on immediate consistency which we can't guarantee with direct gets
// until we provide a solution for this.
si, err := js.StreamInfo("KV_TEST")
require_NoError(t, err)
si.Config.AllowDirect = false
_, err = js.UpdateStream(&si.Config)
require_NoError(t, err)
createData := func(n int) []byte {
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return b
}
_, err = kv.Create("foo", createData(160))
require_NoError(t, err)
// Ensure all replicas have applied the key.
checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
return checkState(t, c, globalAccountName, "KV_TEST")
})
ch := make(chan struct{})
wg := sync.WaitGroup{}
// For counting errors that should not happen.
errCh := make(chan error, 1024)
wg.Add(1)
go func() {
defer wg.Done()
var lastData []byte
var revision uint64
for {
select {
case <-ch:
close(errCh)
return
default:
k, err := kv.Get("foo")
if err == nats.ErrKeyNotFound {
errCh <- err
} else if k != nil {
if lastData != nil && k.Revision() == revision && !bytes.Equal(lastData, k.Value()) {
errCh <- fmt.Errorf("data loss [%s][rev:%d] expected:[%q] is:[%q]\n", "foo", revision, lastData, k.Value())
}
newData := createData(160)
if revision, err = kv.Update("foo", newData, k.Revision()); err == nil {
lastData = newData
}
}
}
}
}()
// Wait a short bit.
time.Sleep(2 * time.Second)
for _, s := range c.servers {
s.Shutdown()
// Need to leave servers down for a while to trigger bug properly.
time.Sleep(5 * time.Second)
s = c.restartServer(s)
c.waitOnServerHealthz(s)
}
// Shutdown the go routine above.
close(ch)
// Wait for it to finish.
wg.Wait()
if len(errCh) != 0 {
for err := range errCh {
t.Logf("Received err %v during test", err)
}
t.Fatalf("Encountered errors")
}
}
func TestNoRaceMemStoreCompactPerformance(t *testing.T) {
//Load MemStore so that it is full
subj, msg := "foo", make([]byte, 1000)
storedMsgSize := memStoreMsgSize(subj, nil, msg)
toStore := uint64(10_000)
toStoreOnTop := uint64(1_000)
setSeqNo := uint64(10_000_000_000)
expectedPurge := toStore - 1
maxBytes := storedMsgSize * toStore
ms, err := newMemStore(&StreamConfig{Storage: MemoryStorage, MaxBytes: int64(maxBytes)})
require_NoError(t, err)
defer ms.Stop()
for i := uint64(0); i < toStore; i++ {
ms.StoreMsg(subj, nil, msg, 0)
}
state := ms.State()
require_Equal(t, toStore, state.Msgs)
require_Equal(t, state.Bytes, storedMsgSize*toStore)
//1st run: Load additional messages then compact
for i := uint64(0); i < toStoreOnTop; i++ {
ms.StoreMsg(subj, nil, msg, 0)
}
startFirstRun := time.Now()
purgedFirstRun, _ := ms.Compact(toStore + toStoreOnTop)
elapsedFirstRun := time.Since(startFirstRun)
require_Equal(t, expectedPurge, purgedFirstRun)
//set the seq number to a very high value by compacting with a too high seq number
purgedFull, _ := ms.Compact(setSeqNo)
require_Equal(t, 1, purgedFull)
//2nd run: Compact again
for i := uint64(0); i < toStore; i++ {
ms.StoreMsg(subj, nil, msg, 0)
}
startSecondRun := time.Now()
purgedSecondRun, _ := ms.Compact(setSeqNo + toStore - 1)
elapsedSecondRun := time.Since(startSecondRun)
require_Equal(t, expectedPurge, purgedSecondRun)
//Calculate delta between runs and fail if it is too high
require_LessThan(t, elapsedSecondRun-elapsedFirstRun, time.Duration(1)*time.Second)
}
func TestNoRaceJetStreamSnapshotsWithSlowAckDontSlowConsumer(t *testing.T) {
// Test takes less time this way.
snapshotAckTimeout = 500 * time.Millisecond
t.Cleanup(func() {
snapshotAckTimeout = defaultSnapshotAckTimeout
})
s := RunBasicJetStreamServer(t)
defer s.Shutdown()
ech := make(chan error)
ecb := func(_ *nats.Conn, _ *nats.Subscription, err error) {
if err != nil {
ech <- err
}
}
nc, js := jsClientConnect(t, s, nats.ErrorHandler(ecb))
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo"},
})
require_NoError(t, err)
// Put in over 64MB.
msg, toSend := make([]byte, 1024*1024), 80
crand.Read(msg)
for i := 0; i < toSend; i++ {
_, err := js.Publish("foo", msg)
require_NoError(t, err)
}
sreq := &JSApiStreamSnapshotRequest{
DeliverSubject: nats.NewInbox(),
ChunkSize: 1024 * 1024,
}
req, _ := json.Marshal(sreq)
rmsg, err := nc.Request(fmt.Sprintf(JSApiStreamSnapshotT, "TEST"), req, time.Second)
require_NoError(t, err)
var resp JSApiStreamSnapshotResponse
json.Unmarshal(rmsg.Data, &resp)
require_True(t, resp.Error == nil)
done := make(chan *nats.Msg)
sub, _ := nc.Subscribe(sreq.DeliverSubject, func(m *nats.Msg) {
// EOF
if len(m.Data) == 0 {
done <- m
return
}
})
defer sub.Unsubscribe()
// Check that we do not get disconnected due to slow consumer.
select {
case msg := <-done:
require_Equal(t, msg.Header.Get("Status"), "408")
require_Equal(t, msg.Header.Get("Description"), "No Flow Response")
case <-ech:
t.Fatalf("Got disconnected: %v", err)
case <-time.After(snapshotAckTimeout * 2):
t.Fatalf("Should have received EOF with error status")
}
}
func TestNoRaceJetStreamWQSkippedMsgsOnScaleUp(t *testing.T) {
checkInterestStateT = 4 * time.Second
checkInterestStateJ = 1
defer func() {
checkInterestStateT = defaultCheckInterestStateT
checkInterestStateJ = defaultCheckInterestStateJ
}()
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
const pre = "CORE_ENT_DR_OTP_22."
wcSubj := pre + ">"
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{wcSubj},
Retention: nats.WorkQueuePolicy,
AllowDirect: true,
Replicas: 3,
})
require_NoError(t, err)
cfg := &nats.ConsumerConfig{
Durable: "dlc",
FilterSubject: wcSubj,
DeliverPolicy: nats.DeliverAllPolicy,
AckPolicy: nats.AckExplicitPolicy,
MaxAckPending: 10_000,
AckWait: 500 * time.Millisecond,
MaxWaiting: 100,
MaxRequestExpires: 1050 * time.Millisecond,
}
_, err = js.AddConsumer("TEST", cfg)
require_NoError(t, err)
pdone := make(chan bool)
cdone := make(chan bool)
// We will have 51 consumer apps and a producer app. Make sure to wait for
// all go routines to end at the end of the test.
wg := sync.WaitGroup{}
wg.Add(52)
// Publish routine
go func() {
defer wg.Done()
publishSubjects := []string{
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.918886682066",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.918886682067",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543211",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543212",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543213",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543214",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543215",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543216",
"CORE_ENT_DR_OTP_22.P.H.TC.10011.1010.916596543217",
}
// ~1.7kb
msg := bytes.Repeat([]byte("Z"), 1750)
// 200 msgs/s
st := time.NewTicker(5 * time.Millisecond)
defer st.Stop()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
for {
select {
case <-st.C:
subj := publishSubjects[rand.Intn(len(publishSubjects))]
_, err = js.Publish(subj, msg)
require_NoError(t, err)
case <-pdone:
return
}
}
}()
consumerApp := func() {
defer wg.Done()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.ConsumerInfo("TEST", "dlc")
require_NoError(t, err)
_, err = js.UpdateConsumer("TEST", cfg)
require_NoError(t, err)
sub, err := js.PullSubscribe(wcSubj, "dlc")
require_NoError(t, err)
st := time.NewTicker(100 * time.Millisecond)
defer st.Stop()
for {
select {
case <-st.C:
msgs, err := sub.Fetch(1, nats.MaxWait(100*time.Millisecond))
if err != nil {
continue
}
require_Equal(t, len(msgs), 1)
m := msgs[0]
if rand.Intn(10) == 1 {
m.Nak()
} else {
// Wait up to 20ms to ack.
time.Sleep(time.Duration(rand.Intn(20)) * time.Millisecond)
// This could fail and that is ok, system should recover due to low ack wait.
m.Ack()
}
case <-cdone:
return
}
}
}
// Now consumer side single.
go consumerApp()
// Wait for 2s
time.Sleep(2 * time.Second)
// Now spin up 50 more.
for i := 1; i <= 50; i++ {
if i%5 == 0 {
time.Sleep(200 * time.Millisecond)
}
go consumerApp()
}
timeout := time.Now().Add(8 * time.Second)
for time.Now().Before(timeout) {
time.Sleep(750 * time.Millisecond)
if s := c.consumerLeader(globalAccountName, "TEST", "dlc"); s != nil {
s.JetStreamStepdownConsumer(globalAccountName, "TEST", "dlc")
}
}
// Close publishers and defer closing consumers.
close(pdone)
defer func() {
close(cdone)
wg.Wait()
}()
checkFor(t, 30*time.Second, 50*time.Millisecond, func() error {
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
if si.State.NumDeleted > 0 || si.State.Msgs > 0 {
return fmt.Errorf("State not correct: %+v", si.State)
}
return nil
})
}
func TestNoRaceConnectionObjectReleased(t *testing.T) {
ob1Conf := createConfFile(t, []byte(`
listen: "127.0.0.1:-1"
server_name: "B1"
accounts {
A { users: [{user: a, password: pwd}] }
SYS { users: [{user: sys, password: pwd}] }
}
cluster {
name: "B"
listen: "127.0.0.1:-1"
}
gateway {
name: "B"
listen: "127.0.0.1:-1"
}
leaf {
listen: "127.0.0.1:-1"
}
system_account: "SYS"
`))
sb1, ob1 := RunServerWithConfig(ob1Conf)
defer sb1.Shutdown()
oaConf := createConfFile(t, []byte(fmt.Sprintf(`
listen: "127.0.0.1:-1"
server_name: "A"
accounts {
A { users: [{user: a, password: pwd}] }
SYS { users: [{user: sys, password: pwd}] }
}
gateway {
name: "A"
listen: "127.0.0.1:-1"
gateways [
{
name: "B"
url: "nats://a:[email protected]:%d"
}
]
}
websocket {
listen: "127.0.0.1:-1"
no_tls: true
}
system_account: "SYS"
`, ob1.Gateway.Port)))
sa, oa := RunServerWithConfig(oaConf)
defer sa.Shutdown()
waitForOutboundGateways(t, sa, 1, 2*time.Second)
waitForOutboundGateways(t, sb1, 1, 2*time.Second)
ob2Conf := createConfFile(t, []byte(fmt.Sprintf(`
listen: "127.0.0.1:-1"
server_name: "B2"
accounts {
A { users: [{user: a, password: pwd}] }
SYS { users: [{user: sys, password: pwd}] }
}
cluster {
name: "B"
listen: "127.0.0.1:-1"
routes: ["nats://127.0.0.1:%d"]
}
gateway {
name: "B"
listen: "127.0.0.1:-1"
}
system_account: "SYS"
`, ob1.Cluster.Port)))
sb2, _ := RunServerWithConfig(ob2Conf)
defer sb2.Shutdown()
checkClusterFormed(t, sb1, sb2)
waitForOutboundGateways(t, sb2, 1, 2*time.Second)
waitForInboundGateways(t, sa, 2, 2*time.Second)
leafConf := createConfFile(t, []byte(fmt.Sprintf(`
listen: "127.0.0.1:-1"
server_name: "C"
accounts {
A { users: [{user: a, password: pwd}] }
SYS { users: [{user: sys, password: pwd}] }
}
leafnodes {
remotes [
{ url: "nats://a:[email protected]:%d" }
]
}
system_account: "SYS"
`, ob1.LeafNode.Port)))
leaf, _ := RunServerWithConfig(leafConf)
defer leaf.Shutdown()
checkLeafNodeConnected(t, leaf)
// Start an independent MQTT server to check MQTT client connection.
mo := testMQTTDefaultOptions()
mo.ServerName = "MQTTServer"
sm := testMQTTRunServer(t, mo)
defer testMQTTShutdownServer(sm)
mc, mr := testMQTTConnect(t, &mqttConnInfo{cleanSess: true}, mo.MQTT.Host, mo.MQTT.Port)
defer mc.Close()
testMQTTCheckConnAck(t, mr, mqttConnAckRCConnectionAccepted, false)
nc := natsConnect(t, sb1.ClientURL(), nats.UserInfo("a", "pwd"))
defer nc.Close()
cid, err := nc.GetClientID()
require_NoError(t, err)
natsSubSync(t, nc, "foo")
natsFlush(t, nc)
ncWS := natsConnect(t, fmt.Sprintf("ws://a:[email protected]:%d", oa.Websocket.Port))
defer ncWS.Close()
cidWS, err := ncWS.GetClientID()
require_NoError(t, err)
var conns []net.Conn
var total int
var ch chan string
track := func(c *client) {
total++
c.mu.Lock()
conns = append(conns, c.nc)
c.mu.Unlock()
runtime.SetFinalizer(c, func(c *client) {
ch <- fmt.Sprintf("Server=%s - Kind=%s - Conn=%v", c.srv, c.kindString(), c)
})
}
// Track the connection for the MQTT client
sm.mu.RLock()
for _, c := range sm.clients {
track(c)
}
sm.mu.RUnlock()
// Track the connection from the NATS client
track(sb1.getClient(cid))
// The outbound connection to GW "A"
track(sb1.getOutboundGatewayConnection("A"))
// The inbound connection from GW "A"
var inGW []*client
sb1.getInboundGatewayConnections(&inGW)
track(inGW[0])
// The routes from sb2
sb1.forEachRoute(func(r *client) {
track(r)
})
// The leaf form "LEAF"
sb1.mu.RLock()
for _, l := range sb1.leafs {
track(l)
}
sb1.mu.RUnlock()
// Now from sb2, the routes to sb1
sb2.forEachRoute(func(r *client) {
track(r)
})
// The outbound connection to GW "A"
track(sb2.getOutboundGatewayConnection("A"))
// From server "A", track the outbound GW
track(sa.getOutboundGatewayConnection("B"))
inGW = inGW[:0]
// Track the inbound GW connections
sa.getInboundGatewayConnections(&inGW)
for _, ig := range inGW {
track(ig)
}
// Track the websocket client
track(sa.getClient(cidWS))
// From the LEAF server, the connection to sb1
leaf.mu.RLock()
for _, l := range leaf.leafs {
track(l)
}
leaf.mu.RUnlock()
// Now close all connections and wait to see if all connections
// with the finalizer set is invoked.
ch = make(chan string, total)
// Close the clients and then all other connections to create a disconnect.
nc.Close()
mc.Close()
ncWS.Close()
for _, conn := range conns {
conn.Close()
}
// Wait and see if we get them all.
tm := time.NewTimer(10 * time.Second)
defer tm.Stop()
tk := time.NewTicker(10 * time.Millisecond)
for clients := make([]string, 0, total); len(clients) < total; {
select {
case <-tk.C:
runtime.GC()
case cs := <-ch:
clients = append(clients, cs)
case <-tm.C:
// Don't fail the test since there is no guarantee that
// finalizers are invoked.
t.Logf("Got %v out of %v finalizers", len(clients), total)
slices.Sort(clients)
for _, cs := range clients {
t.Logf(" => %s", cs)
}
return
}
}
}
func TestNoRaceFileStoreMsgLoadNextMsgMultiPerf(t *testing.T) {
fs, err := newFileStore(
FileStoreConfig{StoreDir: t.TempDir()},
StreamConfig{Name: "zzz", Subjects: []string{"foo.*"}, Storage: FileStorage})
require_NoError(t, err)
defer fs.Stop()
// Put 1k msgs in
for i := 0; i < 1000; i++ {
subj := fmt.Sprintf("foo.%d", i)
_, _, err = fs.StoreMsg(subj, nil, []byte("ZZZ"), 0)
require_NoError(t, err)
}
var smv StoreMsg
// Now do normal load next with no filter.
// This is baseline.
start := time.Now()
for i, seq := 0, uint64(1); i < 1000; i++ {
sm, nseq, err := fs.LoadNextMsg(_EMPTY_, false, seq, &smv)
require_NoError(t, err)
require_Equal(t, sm.subj, fmt.Sprintf("foo.%d", i))
require_Equal(t, nseq, seq)
seq++
}
baseline := time.Since(start)
t.Logf("Single - No filter %v", baseline)
// Allow some additional skew.
baseline += time.Millisecond
// Now do normal load next with wc filter.
start = time.Now()
for i, seq := 0, uint64(1); i < 1000; i++ {
sm, nseq, err := fs.LoadNextMsg("foo.>", true, seq, &smv)
require_NoError(t, err)
require_Equal(t, sm.subj, fmt.Sprintf("foo.%d", i))
require_Equal(t, nseq, seq)
seq++
}
elapsed := time.Since(start)
t.Logf("Single - WC filter %v", elapsed)
require_LessThan(t, elapsed, 2*baseline)
// Now do multi load next with 1 wc entry.
sl := gsl.NewSublist[struct{}]()
require_NoError(t, sl.Insert("foo.>", struct{}{}))
start = time.Now()
for i, seq := 0, uint64(1); i < 1000; i++ {
sm, nseq, err := fs.LoadNextMsgMulti(sl, seq, &smv)
require_NoError(t, err)
require_Equal(t, sm.subj, fmt.Sprintf("foo.%d", i))
require_Equal(t, nseq, seq)
seq++
}
elapsed = time.Since(start)
t.Logf("Multi - Single WC filter %v", elapsed)
require_LessThan(t, elapsed, 2*baseline)
// Now do multi load next with 1000 literal subjects.
sl = gsl.NewSublist[struct{}]()
for i := 0; i < 1000; i++ {
subj := fmt.Sprintf("foo.%d", i)
require_NoError(t, sl.Insert(subj, struct{}{}))
}
start = time.Now()
for i, seq := 0, uint64(1); i < 1000; i++ {
sm, nseq, err := fs.LoadNextMsgMulti(sl, seq, &smv)
require_NoError(t, err)
require_Equal(t, sm.subj, fmt.Sprintf("foo.%d", i))
require_Equal(t, nseq, seq)
seq++
}
elapsed = time.Since(start)
t.Logf("Multi - 1000 filters %v", elapsed)
require_LessThan(t, elapsed, 3*baseline)
}
func TestNoRaceWQAndMultiSubjectFilters(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"Z.>"},
Retention: nats.WorkQueuePolicy,
})
require_NoError(t, err)
stopPubs := make(chan bool)
publish := func(subject string) {
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
for {
select {
case <-stopPubs:
return
default:
_, _ = js.Publish(subject, []byte("hello"))
}
}
}
go publish("Z.foo")
go publish("Z.bar")
go publish("Z.baz")
// Cancel pubs after 10s.
time.AfterFunc(10*time.Second, func() { close(stopPubs) })
// Create a consumer
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "zzz",
AckPolicy: nats.AckExplicitPolicy,
AckWait: 5 * time.Second,
FilterSubjects: []string{"Z.foo", "Z.bar", "Z.baz"},
})
require_NoError(t, err)
sub, err := js.PullSubscribe(_EMPTY_, "zzz", nats.Bind("TEST", "zzz"))
require_NoError(t, err)
received := make([]uint64, 0, 256_000)
batchSize := 10
for running := true; running; {
msgs, err := sub.Fetch(batchSize, nats.MaxWait(2*time.Second))
if err == nats.ErrTimeout {
running = false
}
for _, m := range msgs {
meta, err := m.Metadata()
require_NoError(t, err)
received = append(received, meta.Sequence.Stream)
m.Ack()
}
}
slices.Sort(received)
var pseq, gaps uint64
for _, seq := range received {
if pseq != 0 && pseq != seq-1 {
gaps += seq - pseq + 1
}
pseq = seq
}
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
if si.State.Msgs != 0 || gaps > 0 {
t.Fatalf("Orphaned msgs %d with %d gaps detected", si.State.Msgs, gaps)
}
}
// https://github.com/nats-io/nats-server/issues/4957
func TestNoRaceWQAndMultiSubjectFiltersRace(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"Z.>"},
Retention: nats.WorkQueuePolicy,
Replicas: 1,
})
require_NoError(t, err)
// The bug would happen when the stream was on same server as meta-leader.
// So make that so.
// Make sure stream leader is on S-1
sl := c.streamLeader(globalAccountName, "TEST")
checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
if sl == c.leader() {
return nil
}
// Move meta-leader since stream can be R1.
snc, _ := jsClientConnect(t, c.randomServer(), nats.UserInfo("admin", "s3cr3t!"))
defer snc.Close()
if _, err := snc.Request(JSApiLeaderStepDown, nil, time.Second); err != nil {
return err
}
return fmt.Errorf("stream leader on meta-leader")
})
start := make(chan struct{})
var done, ready sync.WaitGroup
// Create num go routines who will all race to create a consumer with the same filter subject but a different name.
num := 10
ready.Add(num)
done.Add(num)
for i := 0; i < num; i++ {
go func(n int) {
// Connect directly to the meta leader but with our own connection.
s := c.leader()
nc, js := jsClientConnect(t, s)
defer nc.Close()
ready.Done()
defer done.Done()
<-start
js.AddConsumer("TEST", &nats.ConsumerConfig{
Name: fmt.Sprintf("C-%d", n),
FilterSubject: "Z.foo",
AckPolicy: nats.AckExplicitPolicy,
})
}(i)
}
// Wait for requestors to be ready
ready.Wait()
close(start)
done.Wait()
checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
si, err := js.StreamInfo("TEST")
require_NoError(t, err)
if si.State.Consumers != 1 {
return fmt.Errorf("Consumer count not correct: %d vs 1", si.State.Consumers)
}
return nil
})
}
func TestNoRaceFileStoreWriteFullStateUniqueSubjects(t *testing.T) {
fcfg := FileStoreConfig{StoreDir: t.TempDir()}
fs, err := newFileStore(fcfg,
StreamConfig{Name: "zzz", Subjects: []string{"records.>"}, Storage: FileStorage, MaxMsgsPer: 1, MaxBytes: 15 * 1024 * 1024 * 1024})
require_NoError(t, err)
defer fs.Stop()
qch := make(chan struct{})
defer close(qch)
go func() {
const numThreshold = 1_000_000
tick := time.NewTicker(1 * time.Second)
for {
select {
case <-qch:
return
case <-tick.C:
err := fs.writeFullState()
var state StreamState
fs.FastState(&state)
if state.Msgs > numThreshold && err != nil {
require_Error(t, err, errStateTooBig)
}
}
}
}()
labels := []string{"AAAAA", "BBBB", "CCCC", "DD", "EEEEE"}
msg := []byte(strings.Repeat("Z", 128))
for i := 0; i < 100; i++ {
partA := nuid.Next()
for j := 0; j < 100; j++ {
partB := nuid.Next()
for k := 0; k < 500; k++ {
partC := nuid.Next()
partD := labels[rand.Intn(len(labels)-1)]
subject := fmt.Sprintf("records.%s.%s.%s.%s.%s", partA, partB, partC, partD, nuid.Next())
start := time.Now()
fs.StoreMsg(subject, nil, msg, 0)
elapsed := time.Since(start)
if elapsed > 500*time.Millisecond {
t.Fatalf("Slow store for %q: %v\n", subject, elapsed)
}
}
}
}
// Make sure we do write the full state on stop.
fs.Stop()
fi, err := os.Stat(filepath.Join(fcfg.StoreDir, msgDir, streamStreamStateFile))
require_NoError(t, err)
// ~500MB, could change if we tweak encodings..
require_True(t, fi.Size() > 500*1024*1024)
}
// When a catchup takes a long time and the ingest rate is high enough to cause us
// to drop append entries and move our first past out catchup window due to max bytes or max msgs, etc.
func TestNoRaceLargeStreamCatchups(t *testing.T) {
// This usually takes too long on Travis.
t.Skip()
var jsMaxOutTempl = `
listen: 127.0.0.1:-1
server_name: %s
jetstream: {max_file_store: 22GB, store_dir: '%s', max_outstanding_catchup: 128KB}
cluster {
name: %s
listen: 127.0.0.1:%d
routes = [%s]
}
# For access to system account.
accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
`
c := createJetStreamClusterWithTemplate(t, jsMaxOutTempl, "R3S", 3)
defer c.shutdown()
nc, _ := jsClientConnect(t, c.randomServer())
defer nc.Close()
js, err := nc.JetStream(nats.PublishAsyncMaxPending(64 * 1024))
require_NoError(t, err)
cfg := &nats.StreamConfig{
Name: "TEST",
Subjects: []string{"Z.>"},
MaxBytes: 4 * 1024 * 1024 * 1024,
Replicas: 1, // Start at R1
}
_, err = js.AddStream(cfg)
require_NoError(t, err)
// Load up to a decent size first.
num, msg := 25_000, bytes.Repeat([]byte("Z"), 256*1024)
for i := 0; i < num; i++ {
_, err := js.PublishAsync("Z.Z.Z", msg)
require_NoError(t, err)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(20 * time.Second):
t.Fatalf("Did not receive completion signal")
}
var sl *Server
time.AfterFunc(time.Second, func() {
cfg.Replicas = 3
_, err = js.UpdateStream(cfg)
require_NoError(t, err)
c.waitOnStreamLeader(globalAccountName, "TEST")
sl = c.streamLeader(globalAccountName, "TEST")
})
// Run for 60 seconds sending new messages at a high rate.
timeout := time.Now().Add(60 * time.Second)
for time.Now().Before(timeout) {
for i := 0; i < 5_000; i++ {
// Not worried about each message, so not checking err here.
// Just generating high load of new traffic while trying to catch up.
js.PublishAsync("Z.Z.Z", msg)
}
// This will gate us waiting on a response.
js.Publish("Z.Z.Z", msg)
}
// Make sure the leader has not changed.
require_Equal(t, sl, c.streamLeader(globalAccountName, "TEST"))
// Grab the leader and its state.
mset, err := sl.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
expected := mset.state()
checkFor(t, 45*time.Second, time.Second, func() error {
for _, s := range c.servers {
if s == sl {
continue
}
mset, err := s.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
state := mset.state()
if !reflect.DeepEqual(expected, state) {
return fmt.Errorf("Follower %v state does not match: %+v vs %+v", s, state, expected)
}
}
return nil
})
}
func TestNoRaceLargeNumDeletesStreamCatchups(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, _ := jsClientConnect(t, c.randomServer())
defer nc.Close()
js, err := nc.JetStream(nats.PublishAsyncMaxPending(16 * 1024))
require_NoError(t, err)
cfg := &nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo"},
Replicas: 1, // Start at R1
}
_, err = js.AddStream(cfg)
require_NoError(t, err)
// We will manipulate the stream at the lower level to achieve large number of interior deletes.
// We will store only 2 msgs, but have 100M deletes in between.
sl := c.streamLeader(globalAccountName, "TEST")
mset, err := sl.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
mset.mu.Lock()
mset.store.StoreMsg("foo", nil, []byte("ok"), 0)
mset.store.SkipMsgs(2, 1_000_000_000)
mset.store.StoreMsg("foo", nil, []byte("ok"), 0)
mset.store.SkipMsgs(1_000_000_003, 1_000_000_000)
var state StreamState
mset.store.FastState(&state)
mset.lseq = state.LastSeq
mset.mu.Unlock()
cfg.Replicas = 3
_, err = js.UpdateStream(cfg)
require_NoError(t, err)
c.waitOnStreamLeader(globalAccountName, "TEST")
mset, err = sl.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
expected := mset.state()
// This should happen fast and not spin on all interior deletes.
checkFor(t, 250*time.Millisecond, 50*time.Millisecond, func() error {
for _, s := range c.servers {
if s == sl {
continue
}
mset, err := s.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
state := mset.state()
// Ignore LastTime for this test since we send delete range at end.
state.LastTime = expected.LastTime
if !reflect.DeepEqual(expected, state) {
return fmt.Errorf("Follower %v state does not match: %+v vs %+v", s, state, expected)
}
}
return nil
})
}
func TestNoRaceJetStreamClusterMemoryStreamLastSequenceResetAfterRestart(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
numStreams := 250
var wg sync.WaitGroup
wg.Add(numStreams)
for i := 1; i <= numStreams; i++ {
go func(n int) {
defer wg.Done()
_, err := js.AddStream(&nats.StreamConfig{
Name: fmt.Sprintf("TEST:%d", n),
Storage: nats.MemoryStorage,
Subjects: []string{fmt.Sprintf("foo.%d.*", n)},
Replicas: 3,
}, nats.MaxWait(30*time.Second))
require_NoError(t, err)
subj := fmt.Sprintf("foo.%d.bar", n)
for i := 0; i < 222; i++ {
js.Publish(subj, nil)
}
}(i)
}
wg.Wait()
// Make sure all streams have a snapshot in place to stress the snapshot logic for memory based streams.
for _, s := range c.servers {
for i := 1; i <= numStreams; i++ {
stream := fmt.Sprintf("TEST:%d", i)
mset, err := s.GlobalAccount().lookupStream(stream)
require_NoError(t, err)
node := mset.raftNode()
require_NotNil(t, node)
node.InstallSnapshot(mset.stateSnapshot(), false)
}
}
// Do 5 rolling restarts waiting on healthz in between.
for i := 0; i < 5; i++ {
// Walk the servers and shut each down, and wipe the storage directory.
for _, s := range c.servers {
s.Shutdown()
s.WaitForShutdown()
s = c.restartServer(s)
c.waitOnServerHealthz(s)
c.waitOnAllCurrent()
// Make sure all streams are current after healthz returns ok.
for i := 1; i <= numStreams; i++ {
stream := fmt.Sprintf("TEST:%d", i)
mset, err := s.GlobalAccount().lookupStream(stream)
require_NoError(t, err)
var state StreamState
checkFor(t, 30*time.Second, time.Second, func() error {
mset.store.FastState(&state)
if state.LastSeq != 222 {
return fmt.Errorf("%v Wrong last sequence %d for %q - State %+v", s, state.LastSeq, stream, state)
}
return nil
})
}
}
}
}
func TestNoRaceJetStreamClusterMemoryWorkQueueLastSequenceResetAfterRestart(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
numStreams := 50
var wg sync.WaitGroup
wg.Add(numStreams)
for i := 1; i <= numStreams; i++ {
go func(n int) {
defer wg.Done()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
checkFor(t, 5*time.Second, time.Second, func() error {
_, err := js.AddStream(&nats.StreamConfig{
Name: fmt.Sprintf("TEST:%d", n),
Storage: nats.MemoryStorage,
Retention: nats.WorkQueuePolicy,
Subjects: []string{fmt.Sprintf("foo.%d.*", n)},
Replicas: 3,
}, nats.MaxWait(time.Second))
return err
})
subj := fmt.Sprintf("foo.%d.bar", n)
for i := 0; i < 22; i++ {
checkFor(t, 5*time.Second, time.Second, func() error {
_, err := js.Publish(subj, nil)
return err
})
}
// Now consume them all as well.
var err error
var sub *nats.Subscription
checkFor(t, 5*time.Second, time.Second, func() error {
sub, err = js.PullSubscribe(subj, "wq")
return err
})
var msgs []*nats.Msg
checkFor(t, 5*time.Second, time.Second, func() error {
msgs, err = sub.Fetch(22, nats.MaxWait(time.Second))
return err
})
require_Equal(t, len(msgs), 22)
for _, m := range msgs {
checkFor(t, 5*time.Second, time.Second, func() error {
return m.AckSync()
})
}
}(i)
}
wg.Wait()
// Do 2 rolling restarts waiting on healthz in between.
for i := 0; i < 2; i++ {
// Walk the servers and shut each down, and wipe the storage directory.
for _, s := range c.servers {
s.Shutdown()
s.WaitForShutdown()
s = c.restartServer(s)
c.waitOnServerHealthz(s)
c.waitOnAllCurrent()
// Make sure all streams are current after healthz returns ok.
for i := 1; i <= numStreams; i++ {
stream := fmt.Sprintf("TEST:%d", i)
mset, err := s.GlobalAccount().lookupStream(stream)
require_NoError(t, err)
var state StreamState
checkFor(t, 20*time.Second, time.Second, func() error {
mset.store.FastState(&state)
if state.LastSeq != 22 {
return fmt.Errorf("%v Wrong last sequence %d for %q - State %+v", s, state.LastSeq, stream, state)
}
if state.FirstSeq != 23 {
return fmt.Errorf("%v Wrong first sequence %d for %q - State %+v", s, state.FirstSeq, stream, state)
}
return nil
})
}
}
}
}
func TestNoRaceJetStreamClusterMirrorSkipSequencingBug(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R5S", 5)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "ORIGIN",
Subjects: []string{"foo.*"},
MaxMsgsPerSubject: 1,
Replicas: 1,
Storage: nats.MemoryStorage,
})
require_NoError(t, err)
// Create a mirror with R5 such that it will be much slower than the ORIGIN.
_, err = js.AddStream(&nats.StreamConfig{
Name: "M",
Replicas: 5,
Mirror: &nats.StreamSource{Name: "ORIGIN"},
})
require_NoError(t, err)
// Connect new directly to ORIGIN
s := c.streamLeader(globalAccountName, "ORIGIN")
nc, js = jsClientConnect(t, s)
defer nc.Close()
// We are going to send at a high rate and also delete some along the way
// via the max msgs per limit.
for i := 0; i < 500_000; i++ {
subj := fmt.Sprintf("foo.%d", i)
js.PublishAsync(subj, nil)
// Create sequence holes every 100k.
if i%100_000 == 0 {
js.PublishAsync(subj, nil)
}
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
checkFor(t, 20*time.Second, time.Second, func() error {
si, err := js.StreamInfo("M")
require_NoError(t, err)
if si.State.Msgs != 500_000 {
return fmt.Errorf("Expected 1M msgs, got state: %+v", si.State)
}
return nil
})
}
func TestNoRaceJetStreamStandaloneDontReplyToAckBeforeProcessingIt(t *testing.T) {
s := RunBasicJetStreamServer(t)
defer s.Shutdown()
// Client for API requests.
nc, js := jsClientConnect(t, s)
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "WQ",
Discard: nats.DiscardNew,
MaxMsgsPerSubject: 1,
DiscardNewPerSubject: true,
Retention: nats.WorkQueuePolicy,
Subjects: []string{"queue.>"},
})
require_NoError(t, err)
// Keep this low since we are going to run as many go routines
// to consume, ack and republish the message.
total := 10000
// Populate the queue, one message per subject.
for i := 0; i < total; i++ {
js.Publish(fmt.Sprintf("queue.%d", i), []byte("hello"))
}
_, err = js.AddConsumer("WQ", &nats.ConsumerConfig{
Durable: "cons",
AckPolicy: nats.AckExplicitPolicy,
MaxWaiting: 20000,
MaxAckPending: -1,
})
require_NoError(t, err)
sub, err := js.PullSubscribe("queue.>", "cons", nats.BindStream("WQ"))
require_NoError(t, err)
errCh := make(chan error, total)
var wg sync.WaitGroup
for iter := 0; iter < 3; iter++ {
wg.Add(total)
for i := 0; i < total; i++ {
go func() {
defer wg.Done()
for {
msgs, err := sub.Fetch(1)
if err != nil {
time.Sleep(5 * time.Millisecond)
continue
}
msg := msgs[0]
err = msg.AckSync()
if err != nil {
time.Sleep(5 * time.Millisecond)
continue
}
_, err = js.Publish(msg.Subject, []byte("hello"))
if err != nil {
errCh <- err
return
}
break
}
}()
}
wg.Wait()
select {
case err := <-errCh:
t.Fatalf("Test failed, first error was: %v", err)
default:
// OK!
}
}
}
// Under certain scenarios an old index.db with a stream that has msg limits set will not restore properly
// due to an old index.db and compaction after the index.db took place which could lose per subject information.
func TestNoRaceFileStoreMsgLimitsAndOldRecoverState(t *testing.T) {
for _, test := range []struct {
name string
expectedFirstSeq uint64
expectedLastSeq uint64
expectedMsgs uint64
transform func(StreamConfig) StreamConfig
}{
{
name: "MaxMsgsPer",
expectedFirstSeq: 10_001,
expectedLastSeq: 1_010_001,
expectedMsgs: 1_000_001,
transform: func(config StreamConfig) StreamConfig {
config.MaxMsgsPer = 1
return config
},
},
{
name: "MaxMsgs",
expectedFirstSeq: 10_001,
expectedLastSeq: 1_010_001,
expectedMsgs: 1_000_001,
transform: func(config StreamConfig) StreamConfig {
config.MaxMsgs = 1_000_001
return config
},
},
{
name: "MaxBytes",
expectedFirstSeq: 8_624,
expectedLastSeq: 1_010_001,
expectedMsgs: 1_001_378,
transform: func(config StreamConfig) StreamConfig {
config.MaxBytes = 1_065_353_216
return config
},
},
} {
t.Run(test.name, func(t *testing.T) {
sd := t.TempDir()
fs, err := newFileStore(
FileStoreConfig{StoreDir: sd},
test.transform(StreamConfig{Name: "zzz", Subjects: []string{"foo.*"}, Storage: FileStorage}),
)
require_NoError(t, err)
defer fs.Stop()
msg := make([]byte, 1024)
for i := 0; i < 10_000; i++ {
subj := fmt.Sprintf("foo.%d", i)
fs.StoreMsg(subj, nil, msg, 0)
}
// This will write the index.db file. We will capture this and use it to replace a new one.
sfile := filepath.Join(fs.fcfg.StoreDir, msgDir, streamStreamStateFile)
fs.Stop()
_, err = os.Stat(sfile)
require_NoError(t, err)
// Read it in and make sure len > 0.
buf, err := os.ReadFile(sfile)
require_NoError(t, err)
require_True(t, len(buf) > 0)
// Restart
fs, err = newFileStore(
FileStoreConfig{StoreDir: sd},
test.transform(StreamConfig{Name: "zzz", Subjects: []string{"foo.*"}, Storage: FileStorage}),
)
require_NoError(t, err)
defer fs.Stop()
// Put in more messages with wider range. This will compact a bunch of the previous blocks.
for i := 0; i < 1_000_001; i++ {
subj := fmt.Sprintf("foo.%d", i)
fs.StoreMsg(subj, nil, msg, 0)
}
var ss StreamState
fs.FastState(&ss)
require_Equal(t, ss.FirstSeq, test.expectedFirstSeq)
require_Equal(t, ss.LastSeq, test.expectedLastSeq)
require_Equal(t, ss.Msgs, test.expectedMsgs)
// Now stop again, but replace index.db with old one.
fs.Stop()
// Put back old stream state.
require_NoError(t, os.WriteFile(sfile, buf, defaultFilePerms))
// Restart
fs, err = newFileStore(
FileStoreConfig{StoreDir: sd},
test.transform(StreamConfig{Name: "zzz", Subjects: []string{"foo.*"}, Storage: FileStorage}),
)
require_NoError(t, err)
defer fs.Stop()
fs.FastState(&ss)
require_Equal(t, ss.FirstSeq, test.expectedFirstSeq)
require_Equal(t, ss.LastSeq, test.expectedLastSeq)
require_Equal(t, ss.Msgs, test.expectedMsgs)
})
}
}
func TestNoRaceJetStreamClusterCheckInterestStatePerformanceWQ(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3F", 3)
defer c.shutdown()
s := c.randomServer()
nc, js := jsClientConnect(t, s)
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo.*"},
Retention: nats.WorkQueuePolicy,
})
require_NoError(t, err)
// Load up a bunch of messages for three different subjects.
msg := bytes.Repeat([]byte("Z"), 4096)
for i := 0; i < 100_000; i++ {
js.PublishAsync("foo.foo", msg)
}
for i := 0; i < 5_000; i++ {
js.PublishAsync("foo.bar", msg)
js.PublishAsync("foo.baz", msg)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// We will not process this one and leave it as "offline".
_, err = js.PullSubscribe("foo.foo", "A")
require_NoError(t, err)
subB, err := js.PullSubscribe("foo.bar", "B")
require_NoError(t, err)
subC, err := js.PullSubscribe("foo.baz", "C")
require_NoError(t, err)
// Now catch up both B and C but let A simulate being offline of very behind.
for i := 0; i < 5; i++ {
for _, sub := range []*nats.Subscription{subB, subC} {
msgs, err := sub.Fetch(1000)
require_NoError(t, err)
require_Equal(t, len(msgs), 1000)
for _, m := range msgs {
m.Ack()
}
}
}
// Let acks process.
nc.Flush()
time.Sleep(200 * time.Millisecond)
// Now test the check checkInterestState() on the stream.
sl := c.streamLeader(globalAccountName, "TEST")
mset, err := sl.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
expireAllBlks := func() {
mset.mu.RLock()
fs := mset.store.(*fileStore)
mset.mu.RUnlock()
fs.mu.RLock()
for _, mb := range fs.blks {
mb.tryForceExpireCache()
}
fs.mu.RUnlock()
}
// First expire all the blocks.
expireAllBlks()
start := time.Now()
mset.checkInterestState()
elapsed := time.Since(start)
// This is actually ~300 microseconds but due to travis and race flags etc.
// Was > 30 ms before fix for comparison, M2 macbook air.
require_LessThan(t, elapsed, 5*time.Millisecond)
// Make sure we set the chkflr correctly.
// The chkflr should be equal to asflr+1 (unless the next message matching the filter is further ahead).
// Otherwise, if chkflr would be set higher a subsequent call to checkInterestState will be ineffective.
requireFloorsEqual := func(o *consumer) {
t.Helper()
require_True(t, o != nil)
o.mu.RLock()
defer o.mu.RUnlock()
_, seq, err := mset.store.LoadNextMsg(o.cfg.FilterSubject, false, o.asflr+1, nil)
if err == ErrStoreEOF {
seq++
}
require_True(t, o.asflr+1 <= seq)
require_Equal(t, o.chkflr, seq)
}
requireFloorsEqual(mset.lookupConsumer("A"))
requireFloorsEqual(mset.lookupConsumer("B"))
requireFloorsEqual(mset.lookupConsumer("C"))
// Expire all the blocks again.
expireAllBlks()
// This checks the chkflr state.
start = time.Now()
mset.checkInterestState()
elapsed = time.Since(start)
require_LessThan(t, elapsed, 5*time.Millisecond)
}
func TestNoRaceJetStreamClusterCheckInterestStatePerformanceInterest(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3F", 3)
defer c.shutdown()
s := c.randomServer()
nc, js := jsClientConnect(t, s)
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo.*"},
Retention: nats.InterestPolicy,
})
require_NoError(t, err)
// We will not process this one and leave it as "offline".
_, err = js.PullSubscribe("foo.foo", "A")
require_NoError(t, err)
_, err = js.PullSubscribe("foo.*", "B")
require_NoError(t, err)
// Make subC multi-subject.
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "C",
FilterSubjects: []string{"foo.foo", "foo.bar", "foo.baz"},
AckPolicy: nats.AckExplicitPolicy,
})
require_NoError(t, err)
// Load up a bunch of messages for three different subjects.
msg := bytes.Repeat([]byte("Z"), 4096)
for i := 0; i < 90_000; i++ {
js.PublishAsync("foo.foo", msg)
}
for i := 0; i < 5_000; i++ {
js.PublishAsync("foo.bar", msg)
js.PublishAsync("foo.baz", msg)
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
// This is so we do not asynchronously update our consumer state after we set the state due to notifications
// from new messages for the stream.
time.Sleep(250 * time.Millisecond)
// Now catch up both B and C but let A simulate being offline of very behind.
// Will do this manually here to speed up tests.
sl := c.streamLeader(globalAccountName, "TEST")
mset, err := sl.GlobalAccount().lookupStream("TEST")
require_NoError(t, err)
for _, cname := range []string{"B", "C"} {
o := mset.lookupConsumer(cname)
o.mu.Lock()
o.setStoreState(&ConsumerState{
Delivered: SequencePair{100_000, 100_000},
AckFloor: SequencePair{100_000, 100_000},
})
o.mu.Unlock()
}
// Now test the check checkInterestState() on the stream.
start := time.Now()
mset.checkInterestState()
elapsed := time.Since(start)
// Make sure we set the chkflr correctly.
checkFloor := func(o *consumer) uint64 {
require_True(t, o != nil)
o.mu.RLock()
defer o.mu.RUnlock()
return o.chkflr
}
require_Equal(t, checkFloor(mset.lookupConsumer("A")), 1)
require_Equal(t, checkFloor(mset.lookupConsumer("B")), 90_001)
require_Equal(t, checkFloor(mset.lookupConsumer("C")), 100_001)
// This checks the chkflr state. For this test this should be much faster,
// two orders of magnitude then the first time.
start = time.Now()
mset.checkInterestState()
require_True(t, time.Since(start) < elapsed/100)
}
func TestNoRaceJetStreamClusterLargeMetaSnapshotTiming(t *testing.T) {
// This test was to show improvements in speed for marshaling the meta layer with lots of assets.
// Move to S2.Encode vs EncodeBetter which is 2x faster and actually better compression.
// Also moved to goccy json which is faster then the default and in my tests now always matches
// the default encoder byte for byte which last time I checked it did not.
t.Skip()
c := createJetStreamClusterExplicit(t, "R3F", 3)
defer c.shutdown()
// Create 200 streams, each with 500 consumers.
numStreams := 200
numConsumers := 500
wg := sync.WaitGroup{}
wg.Add(numStreams)
for i := 0; i < numStreams; i++ {
go func() {
defer wg.Done()
s := c.randomServer()
nc, js := jsClientConnect(t, s)
defer nc.Close()
sname := fmt.Sprintf("TEST-SNAPSHOT-%d", i)
subj := fmt.Sprintf("foo.%d", i)
_, err := js.AddStream(&nats.StreamConfig{
Name: sname,
Subjects: []string{subj},
Replicas: 3,
})
require_NoError(t, err)
// Now consumers.
for c := 0; c < numConsumers; c++ {
_, err = js.AddConsumer(sname, &nats.ConsumerConfig{
Durable: fmt.Sprintf("C-%d", c),
FilterSubject: subj,
AckPolicy: nats.AckExplicitPolicy,
Replicas: 1,
})
require_NoError(t, err)
}
}()
}
wg.Wait()
s := c.leader()
js := s.getJetStream()
n := js.getMetaGroup()
// Now let's see how long it takes to create a meta snapshot and how big it is.
start := time.Now()
snap, _, _, err := js.metaSnapshot()
require_NoError(t, err)
require_NoError(t, n.InstallSnapshot(snap, false))
t.Logf("Took %v to snap meta with size of %v\n", time.Since(start), friendlyBytes(len(snap)))
}
func TestNoRaceStoreReverseWalkWithDeletesPerf(t *testing.T) {
cfg := StreamConfig{Name: "zzz", Subjects: []string{"foo.*"}, Storage: FileStorage}
fs, err := newFileStore(FileStoreConfig{StoreDir: t.TempDir()}, cfg)
require_NoError(t, err)
defer fs.Stop()
cfg.Storage = MemoryStorage
ms, err := newMemStore(&cfg)
require_NoError(t, err)
defer ms.Stop()
msg := []byte("Hello")
for _, store := range []StreamStore{fs, ms} {
_, _, err = store.StoreMsg("foo.A", nil, msg, 0)
require_NoError(t, err)
for i := 0; i < 1_000_000; i++ {
_, _, err = store.StoreMsg("foo.B", nil, msg, 0)
require_NoError(t, err)
}
_, _, err = store.StoreMsg("foo.C", nil, msg, 0)
require_NoError(t, err)
var ss StreamState
store.FastState(&ss)
require_Equal(t, ss.Msgs, 1_000_002)
// Create a bunch of interior deletes.
p, err := store.PurgeEx("foo.B", 1, 0)
require_NoError(t, err)
require_Equal(t, p, 1_000_000)
// Preload the caches so this test only measures the reverse walk.
preloadCaches := func() {
if fs, ok := store.(*fileStore); ok {
fs.mu.Lock()
defer fs.mu.Unlock()
for _, mb := range fs.blks {
require_NoError(t, mb.loadMsgs())
}
}
}
// Now simulate a walk backwards as we currently do when searching for starting sequence numbers in sourced streams.
preloadCaches()
start := time.Now()
var smv StoreMsg
for seq := ss.LastSeq; seq > 0; seq-- {
_, err := store.LoadMsg(seq, &smv)
if err == errDeletedMsg || err == ErrStoreMsgNotFound {
continue
}
require_NoError(t, err)
}
elapsed := time.Since(start)
// Now use the optimized load prev.
preloadCaches()
seq, seen := ss.LastSeq, 0
start = time.Now()
for {
sm, err := store.LoadPrevMsg(seq, &smv)
if err == ErrStoreEOF {
break
}
require_NoError(t, err)
seq = sm.seq - 1
seen++
}
elapsedNew := time.Since(start)
require_Equal(t, seen, 2)
switch store.(type) {
case *memStore:
require_LessThan(t, elapsedNew, elapsed)
case *fileStore:
// Bigger gains for filestore.
require_LessThan(t, elapsedNew*10, elapsed)
}
}
}
type fastProdLogger struct {
DummyLogger
gotIt chan struct{}
}
func (l *fastProdLogger) Debugf(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
if strings.Contains(msg, "fast producer") {
select {
case l.gotIt <- struct{}{}:
default:
}
}
}
func TestNoRaceNoFastProducerStall(t *testing.T) {
tmpl := `
listen: "127.0.0.1:-1"
no_fast_producer_stall: %s
`
conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, "true")))
s, _ := RunServerWithConfig(conf)
defer s.Shutdown()
l := &fastProdLogger{gotIt: make(chan struct{}, 1)}
s.SetLogger(l, true, false)
ncSlow := natsConnect(t, s.ClientURL())
defer ncSlow.Close()
natsSub(t, ncSlow, "foo", func(_ *nats.Msg) {})
natsFlush(t, ncSlow)
nc := natsConnect(t, s.ClientURL())
defer nc.Close()
traceSub := natsSubSync(t, nc, "my.trace.subj")
natsFlush(t, nc)
ncProd := natsConnect(t, s.ClientURL())
defer ncProd.Close()
payload := make([]byte, 256)
cid, err := ncSlow.GetClientID()
require_NoError(t, err)
c := s.GetClient(cid)
require_True(t, c != nil)
wg := sync.WaitGroup{}
pub := func() {
wg.Add(1)
go func() {
defer wg.Done()
msg := nats.NewMsg("foo")
msg.Header.Set(MsgTraceDest, traceSub.Subject)
msg.Data = payload
ncProd.PublishMsg(msg)
}()
}
checkTraceMsg := func(err string) {
t.Helper()
var e MsgTraceEvent
traceMsg := natsNexMsg(t, traceSub, time.Second)
json.Unmarshal(traceMsg.Data, &e)
egresses := e.Egresses()
require_Equal(t, len(egresses), 1)
eg := egresses[0]
require_Equal(t, eg.CID, cid)
if err != _EMPTY_ {
require_Contains(t, eg.Error, err)
} else {
require_Equal(t, eg.Error, _EMPTY_)
}
}
// Artificially set a stall channel.
c.mu.Lock()
c.out.stc = make(chan struct{})
c.mu.Unlock()
// Publish a message, it should not stall the producer.
pub()
// Now make sure we did not get any fast producer debug statements.
select {
case <-l.gotIt:
t.Fatal("Got debug logs about fast producer")
case <-time.After(250 * time.Millisecond):
// OK!
}
wg.Wait()
checkTraceMsg(errMsgTraceFastProdNoStall)
// Now we will conf reload to enable fast producer stalling.
reloadUpdateConfig(t, s, conf, fmt.Sprintf(tmpl, "false"))
// Publish, this time the prod should be stalled.
pub()
select {
case <-l.gotIt:
// OK!
case <-time.After(500 * time.Millisecond):
t.Fatal("Timed-out waiting for a warning")
}
wg.Wait()
// Should have been delivered to the trace subscription.
checkTraceMsg(_EMPTY_)
}
func TestNoRaceProducerStallLimits(t *testing.T) {
tmpl := `
listen: "127.0.0.1:-1"
`
conf := createConfFile(t, []byte(tmpl))
s, _ := RunServerWithConfig(conf)
defer s.Shutdown()
ncSlow := natsConnect(t, s.ClientURL(), nats.Name("slow"))
defer ncSlow.Close()
natsSub(t, ncSlow, "foo", func(m *nats.Msg) { m.Respond([]byte("42")) })
natsFlush(t, ncSlow)
ncProd := natsConnect(t, s.ClientURL(), nats.Name("fast"))
defer ncProd.Close()
cid, err := ncSlow.GetClientID()
require_NoError(t, err)
c := s.GetClient(cid)
require_True(t, c != nil)
// Artificially set a stall channel on the subscriber.
c.mu.Lock()
c.out.stc = make(chan struct{})
c.mu.Unlock()
start := time.Now()
_, err = ncProd.Request("foo", []byte("HELLO"), time.Second)
elapsed := time.Since(start)
require_NoError(t, err)
// This should have not cleared on its own but should have between min and max pause.
require_True(t, elapsed >= stallClientMinDuration)
require_LessThan(t, elapsed, stallClientMaxDuration+5*time.Millisecond)
// Now test total maximum by loading up a bunch of requests and measuring the last one.
// Artificially set a stall channel again on the subscriber.
c.mu.Lock()
c.out.stc = make(chan struct{})
// This will prevent us from clearing the stc.
c.out.pb = c.out.mp/4*3 + 100
c.mu.Unlock()
for i := 0; i < 10; i++ {
err = ncProd.PublishRequest("foo", "bar", []byte("HELLO"))
require_NoError(t, err)
}
start = time.Now()
_, err = ncProd.Request("foo", []byte("HELLO"), time.Second)
elapsed = time.Since(start)
require_NoError(t, err)
require_True(t, elapsed >= stallTotalAllowed)
// Should always be close to totalAllowed (e.g. 10ms), but if you run a lot of them in one go can bump up
// just past it, hence the Max setting below to avoid a flapper.
require_LessThan(t, elapsed, stallTotalAllowed+20*time.Millisecond)
var tc *client
s.mu.Lock()
for _, client := range s.clients {
client.mu.Lock()
if client.opts.Name == "slow" {
tc = client
client.mu.Unlock()
break
}
client.mu.Unlock()
}
s.mu.Unlock()
// Verify counter was incremented.
totalStalls := atomic.LoadInt64(&tc.stalls)
if totalStalls == 0 {
t.Fatalf("Expected stalls count to be incremented, got: %d", totalStalls)
}
// Verify this shows up in Connz monitoring.
pconns := pollConnz(t, s, 1, "", nil)
var connzStalls int64
for _, conn := range pconns.Conns {
if conn.Cid == tc.cid {
connzStalls = conn.Stalls
break
}
}
if connzStalls < 1 {
t.Fatalf("Expected connz stalls count to be incremented, got: %d", connzStalls)
}
if s.NumStalledClients() < 1 {
t.Fatalf("Expected total stalled clients to be incremented, got: %d", s.NumStalledClients())
}
}
func TestNoRaceJetStreamClusterConsumerDeleteInterestPolicyPerf(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Retention: nats.InterestPolicy,
Subjects: []string{"foo.*"},
Replicas: 3,
})
require_NoError(t, err)
// Make the first sequence high. We already protect against it but for extra sanity.
err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 100_000_000})
require_NoError(t, err)
// Create 3 consumers. 1 Ack explicit, 1 AckAll and 1 AckNone
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "C1",
AckPolicy: nats.AckExplicitPolicy,
})
require_NoError(t, err)
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "C2",
AckPolicy: nats.AckAllPolicy,
})
require_NoError(t, err)
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "C3",
AckPolicy: nats.AckNonePolicy,
})
require_NoError(t, err)
for i := 0; i < 500_000; i++ {
js.PublishAsync("foo.bar", []byte("ok"))
// Confirm batch.
if i%1000 == 0 {
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
}
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
expectedStreamMsgs := func(msgs uint64) {
t.Helper()
checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
si, err := js.StreamInfo("TEST")
if err != nil {
return err
}
if si.State.Msgs != msgs {
return fmt.Errorf("require uint64 equal, but got: %d != %d", si.State.Msgs, msgs)
}
return nil
})
}
expectedStreamMsgs(500_000)
// For C1 grab 100 and ack evens.
sub, err := js.PullSubscribe("foo.bar", "C1")
require_NoError(t, err)
msgs, err := sub.Fetch(100)
require_NoError(t, err)
require_Equal(t, len(msgs), 100)
for _, m := range msgs {
meta, _ := m.Metadata()
if meta.Sequence.Stream%2 == 0 {
require_NoError(t, m.AckSync())
}
}
// For C2 grab 500 and ack 100.
sub, err = js.PullSubscribe("foo.bar", "C2")
require_NoError(t, err)
msgs, err = sub.Fetch(500)
require_NoError(t, err)
require_Equal(t, len(msgs), 500)
require_NoError(t, msgs[99].AckSync())
// Simulate stream viewer, get first 10 from C3
sub, err = js.PullSubscribe("foo.bar", "C3")
require_NoError(t, err)
msgs, err = sub.Fetch(10)
require_NoError(t, err)
require_Equal(t, len(msgs), 10)
expectedStreamMsgs(499_995)
// This test would flake depending on if mset.checkInterestState already ran or not.
// Manually call it here, because consumers don't retry removing messages below their ack floor.
for _, s := range c.servers {
acc, err := s.lookupAccount(globalAccountName)
require_NoError(t, err)
mset, err := acc.lookupStream("TEST")
require_NoError(t, err)
mset.checkInterestState()
}
// Before fix this was in the seconds. All the while the stream is locked.
// This should be short now, but messages might not be cleaned up.
start := time.Now()
err = js.DeleteConsumer("TEST", "C3")
require_NoError(t, err)
if elapsed := time.Since(start); elapsed > 100*time.Millisecond {
t.Fatalf("Deleting AckNone consumer took too long: %v", elapsed)
}
expectedStreamMsgs(499_950)
// Now do AckAll
start = time.Now()
err = js.DeleteConsumer("TEST", "C2")
require_NoError(t, err)
if elapsed := time.Since(start); elapsed > 100*time.Millisecond {
t.Fatalf("Deleting AckAll consumer took too long: %v", elapsed)
}
expectedStreamMsgs(499_950)
// Now do AckExplicit
start = time.Now()
err = js.DeleteConsumer("TEST", "C1")
require_NoError(t, err)
if elapsed := time.Since(start); elapsed > 100*time.Millisecond {
t.Fatalf("Deleting AckExplicit consumer took too long: %v", elapsed)
}
expectedStreamMsgs(0)
}
func TestNoRaceJetStreamClusterConsumerDeleteInterestPolicyUniqueFiltersPerf(t *testing.T) {
c := createJetStreamClusterExplicit(t, "R3S", 3)
defer c.shutdown()
nc, js := jsClientConnect(t, c.randomServer())
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Retention: nats.InterestPolicy,
Subjects: []string{"foo.*"},
Replicas: 3,
})
require_NoError(t, err)
// Create 2 consumers. 1 Ack explicit, 1 AckNone
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "C0",
AckPolicy: nats.AckExplicitPolicy,
FilterSubjects: []string{"foo.0"},
})
require_NoError(t, err)
_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
Durable: "C1",
AckPolicy: nats.AckNonePolicy,
FilterSubjects: []string{"foo.1"},
})
require_NoError(t, err)
for i := 0; i < 500_000; i++ {
subject := fmt.Sprintf("foo.%d", i%2)
js.PublishAsync(subject, []byte("ok"))
// Confirm batch.
if i%1000 == 0 {
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
}
}
select {
case <-js.PublishAsyncComplete():
case <-time.After(5 * time.Second):
t.Fatalf("Did not receive completion signal")
}
expectedStreamMsgs := func(msgs uint64) {
t.Helper()
checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
si, err := js.StreamInfo("TEST")
if err != nil {
return err
}
if si.State.Msgs != msgs {
return fmt.Errorf("require uint64 equal, but got: %d != %d", si.State.Msgs, msgs)
}
return nil
})
}
expectedStreamMsgs(500_000)
// For C0 grab 100 and ack them.
sub, err := js.PullSubscribe("foo.0", "C0")
require_NoError(t, err)
msgs, err := sub.Fetch(100)
require_NoError(t, err)
require_Equal(t, len(msgs), 100)
for _, msg := range msgs {
require_NoError(t, msg.AckSync())
}
expectedStreamMsgs(499_900)
start := time.Now()
err = js.DeleteConsumer("TEST", "C1")
require_NoError(t, err)
if elapsed := time.Since(start); elapsed > 100*time.Millisecond {
t.Fatalf("Deleting AckNone consumer took too long: %v", elapsed)
}
expectedStreamMsgs(499_800)
start = time.Now()
err = js.DeleteConsumer("TEST", "C0")
require_NoError(t, err)
if elapsed := time.Since(start); elapsed > 100*time.Millisecond {
t.Fatalf("Deleting AckExplicit consumer took too long: %v", elapsed)
}
expectedStreamMsgs(0)
}
func TestNoRaceFileStorePurgeExAsyncTombstones(t *testing.T) {
fs, err := newFileStore(
FileStoreConfig{StoreDir: t.TempDir()},
StreamConfig{Name: "zzz", Subjects: []string{"*.*"}, Storage: FileStorage})
require_NoError(t, err)
defer fs.Stop()
msg := []byte("zzz")
fs.StoreMsg("foo.A", nil, msg, 0)
fs.StoreMsg("foo.B", nil, msg, 0)
for i := 0; i < 500; i++ {
fs.StoreMsg("foo.C", nil, msg, 0)
}
fs.StoreMsg("foo.D", nil, msg, 0)
// Load all blocks to avoid that being a factor in timing.
fs.mu.RLock()
for _, mb := range fs.blks {
mb.loadMsgs()
}
fs.mu.RUnlock()
// Now purge 1 that is not the first message and take note of time.
// Since we are loaded this should mostly be the time to write / flush tombstones.
start := time.Now()
n, err := fs.PurgeEx("foo.B", 0, 0)
elapsed := time.Since(start)
require_NoError(t, err)
require_Equal(t, n, 1)
start = time.Now()
n, err = fs.PurgeEx("foo.C", 0, 0)
elapsed2 := time.Since(start)
require_NoError(t, err)
require_Equal(t, n, 500)
// If we are flushing for each tombstone the second elapsed time will be a larger multiple of the single message purge.
// In testing this is like >200x
// With async and flush for all tombstones will be ~30x
require_True(t, elapsed*50 > elapsed2)
}
// Chck that we do not leak any go routines from access time optimization.
func TestNoRaceAccessTimeLeakCheck(t *testing.T) {
time.Sleep(time.Second)
ngrp := runtime.NumGoroutine()
s := RunBasicJetStreamServer(t)
defer s.Shutdown()
nc, js := jsClientConnect(t, s)
defer nc.Close()
_, err := js.AddStream(&nats.StreamConfig{
Name: "TEST",
Subjects: []string{"foo"},
})
require_NoError(t, err)
_, err = js.Publish("foo", []byte("Hello World"))
require_NoError(t, err)
// Close down client and server.
nc.Close()
s.Shutdown()
s.WaitForShutdown()
checkFor(t, 5*time.Second, time.Second, func() error {
if ngra := runtime.NumGoroutine(); ngrp != ngra {
return fmt.Errorf("expected %d, got %d", ngrp, ngra)
}
return nil
})
}