Skip to content

Commit e77abce

Browse files
Fix potential deadlock in MultiLastSeqs (#6899)
`MultiLastSeqs` already held the read lock before calling `AllLastSeqs` which would try to take the read lock again, but this could result in a deadlock if another goroutine is meanwhile trying to take the lock for writing. Signed-off-by: Neil Twigg <[email protected]>
2 parents a436fa1 + 693abf8 commit e77abce

File tree

2 files changed

+14
-2
lines changed

2 files changed

+14
-2
lines changed

server/filestore.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3042,7 +3042,13 @@ func (fs *fileStore) SubjectsState(subject string) map[string]SimpleState {
30423042
func (fs *fileStore) AllLastSeqs() ([]uint64, error) {
30433043
fs.mu.RLock()
30443044
defer fs.mu.RUnlock()
3045+
return fs.allLastSeqsLocked()
3046+
}
30453047

3048+
// allLastSeqsLocked will return a sorted list of last sequences for all
3049+
// subjects, but won't take the lock to do it, to avoid the issue of compounding
3050+
// read locks causing a deadlock with a write lock.
3051+
func (fs *fileStore) allLastSeqsLocked() ([]uint64, error) {
30463052
if fs.state.Msgs == 0 || fs.noTrackSubjects() {
30473053
return nil, nil
30483054
}
@@ -3113,7 +3119,7 @@ func (fs *fileStore) MultiLastSeqs(filters []string, maxSeq uint64, maxAllowed i
31133119

31143120
// See if we can short circuit if we think they are asking for all last sequences and have no maxSeq or maxAllowed set.
31153121
if maxSeq == 0 && maxAllowed <= 0 && fs.filterIsAll(filters) {
3116-
return fs.AllLastSeqs()
3122+
return fs.allLastSeqsLocked()
31173123
}
31183124

31193125
lastBlkIndex := len(fs.blks) - 1

server/memstore.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,13 @@ func (ms *memStore) SubjectsState(subject string) map[string]SimpleState {
641641
func (ms *memStore) AllLastSeqs() ([]uint64, error) {
642642
ms.mu.RLock()
643643
defer ms.mu.RUnlock()
644+
return ms.allLastSeqsLocked()
645+
}
644646

647+
// allLastSeqsLocked will return a sorted list of last sequences for all
648+
// subjects, but won't take the lock to do it, to avoid the issue of compounding
649+
// read locks causing a deadlock with a write lock.
650+
func (ms *memStore) allLastSeqsLocked() ([]uint64, error) {
645651
if len(ms.msgs) == 0 {
646652
return nil, nil
647653
}
@@ -685,7 +691,7 @@ func (ms *memStore) MultiLastSeqs(filters []string, maxSeq uint64, maxAllowed in
685691

686692
// See if we can short circuit if we think they are asking for all last sequences and have no maxSeq or maxAllowed set.
687693
if maxSeq == 0 && maxAllowed <= 0 && ms.filterIsAll(filters) {
688-
return ms.AllLastSeqs()
694+
return ms.allLastSeqsLocked()
689695
}
690696

691697
// Implied last sequence.

0 commit comments

Comments
 (0)