mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Merge pull request #1869 from nats-io/dupe-delivery
[FIXED] Bug on single replica restore would deliver duplicate messages.
This commit is contained in:
@@ -122,6 +122,7 @@ type Stream struct {
|
||||
ddindex int
|
||||
ddtmr *time.Timer
|
||||
qch chan struct{}
|
||||
active bool
|
||||
|
||||
// Clustered mode.
|
||||
sa *streamAssignment
|
||||
@@ -266,7 +267,8 @@ func (a *Account) addStream(config *StreamConfig, fsConfig *FileStoreConfig, sa
|
||||
}
|
||||
|
||||
// Call directly to set leader if not in clustered mode.
|
||||
if !s.JetStreamIsClustered() {
|
||||
// This can be called though before we actually setup clustering, so check both.
|
||||
if !s.JetStreamIsClustered() && s.standAloneMode() {
|
||||
if err := mset.setLeader(true); err != nil {
|
||||
mset.Delete()
|
||||
return nil, err
|
||||
@@ -309,6 +311,9 @@ func (mset *Stream) setLeader(isLeader bool) error {
|
||||
mset.mu.Lock()
|
||||
// If we are here we have a change in leader status.
|
||||
if isLeader {
|
||||
// Make sure we are listening for sync requests.
|
||||
// TODO(dlc) - Original design was that all in sync members of the group would do DQ.
|
||||
mset.startClusterSubs()
|
||||
// Setup subscriptions
|
||||
if err := mset.subscribeToStream(); err != nil {
|
||||
mset.mu.Unlock()
|
||||
@@ -316,9 +321,6 @@ func (mset *Stream) setLeader(isLeader bool) error {
|
||||
mset.Delete()
|
||||
return err
|
||||
}
|
||||
// Make sure we are listening for sync requests.
|
||||
// TODO(dlc) - Original design was that all in sync members of the group would do DQ.
|
||||
mset.startClusterSubs()
|
||||
} else {
|
||||
// Stop responding to sync requests.
|
||||
mset.stopClusterSubs()
|
||||
@@ -840,11 +842,15 @@ func (mset *Stream) removeMsg(seq uint64, secure bool) (bool, error) {
|
||||
// Will create internal subscriptions for the stream.
|
||||
// Lock should be held.
|
||||
func (mset *Stream) subscribeToStream() error {
|
||||
if mset.active {
|
||||
return nil
|
||||
}
|
||||
for _, subject := range mset.config.Subjects {
|
||||
if _, err := mset.subscribeInternal(subject, mset.processInboundJetStreamMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mset.active = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -854,6 +860,7 @@ func (mset *Stream) unsubscribeToStream() error {
|
||||
for _, subject := range mset.config.Subjects {
|
||||
mset.unsubscribeInternal(subject)
|
||||
}
|
||||
mset.active = false
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2612,6 +2612,56 @@ func TestJetStreamRestartAdvisories(t *testing.T) {
|
||||
checkSubsPending(t, usub, 0)
|
||||
}
|
||||
|
||||
func TestJetStreamClusterNoDuplicateOnNodeRestart(t *testing.T) {
|
||||
c := createJetStreamClusterExplicit(t, "ND", 2)
|
||||
defer c.shutdown()
|
||||
// Client based API
|
||||
s := c.randomServer()
|
||||
nc, js := jsClientConnect(t, s)
|
||||
defer nc.Close()
|
||||
_, err := js.AddStream(&nats.StreamConfig{
|
||||
Name: "TEST",
|
||||
Subjects: []string{"foo"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
js.Publish("foo", []byte("msg1"))
|
||||
if m, err := sub.NextMsg(time.Second); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
} else {
|
||||
m.Ack()
|
||||
}
|
||||
|
||||
sl := c.streamLeader("$G", "TEST")
|
||||
sl.Shutdown()
|
||||
c.restartServer(sl)
|
||||
c.waitOnStreamLeader("$G", "TEST")
|
||||
|
||||
// Send second msg
|
||||
js.Publish("foo", []byte("msg2"))
|
||||
msg, err := sub.NextMsg(time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting message: %v", err)
|
||||
}
|
||||
if string(msg.Data) != "msg2" {
|
||||
t.Fatalf("Unexpected message: %s", msg.Data)
|
||||
}
|
||||
msg.Ack()
|
||||
|
||||
// Make sure we don't get a duplicate.
|
||||
msg, err = sub.NextMsg(250 * time.Millisecond)
|
||||
if err == nil {
|
||||
t.Fatalf("Should have gotten an error, got %s", msg.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJetStreamClusterStreamPerf(t *testing.T) {
|
||||
// Comment out to run, holding place for now.
|
||||
skip(t)
|
||||
|
||||
Reference in New Issue
Block a user