@@ -2486,7 +2486,7 @@ func (mb *msgBlock) firstMatching(filter string, wc bool, start uint64, sm *Stor
2486
2486
mb .mu .Unlock ()
2487
2487
}()
2488
2488
2489
- fseq , isAll , subs := start , filter == _EMPTY_ || filter == fwcs , [] string { filter }
2489
+ fseq , isAll := start , filter == _EMPTY_ || filter == fwcs
2490
2490
2491
2491
var didLoad bool
2492
2492
if mb .fssNotLoaded () {
@@ -2514,18 +2514,15 @@ func (mb *msgBlock) firstMatching(filter string, wc bool, start uint64, sm *Stor
2514
2514
}
2515
2515
}
2516
2516
// Make sure to start at mb.first.seq if fseq < mb.first.seq
2517
- if seq := atomic .LoadUint64 (& mb .first .seq ); seq > fseq {
2518
- fseq = seq
2519
- }
2517
+ fseq = max (fseq , atomic .LoadUint64 (& mb .first .seq ))
2520
2518
lseq := atomic .LoadUint64 (& mb .last .seq )
2521
2519
2522
2520
// Optionally build the isMatch for wildcard filters.
2523
- _tsa , _fsa := [32 ]string {}, [32 ]string {}
2524
- tsa , fsa := _tsa [:0 ], _fsa [:0 ]
2525
2521
var isMatch func (subj string ) bool
2526
2522
// Decide to build.
2527
2523
if wc {
2528
- fsa = tokenizeSubjectIntoSlice (fsa [:0 ], filter )
2524
+ _tsa , _fsa := [32 ]string {}, [32 ]string {}
2525
+ tsa , fsa := _tsa [:0 ], tokenizeSubjectIntoSlice (_fsa [:0 ], filter )
2529
2526
isMatch = func (subj string ) bool {
2530
2527
tsa = tokenizeSubjectIntoSlice (tsa [:0 ], subj )
2531
2528
return isSubsetMatchTokenized (tsa , fsa )
@@ -2545,29 +2542,22 @@ func (mb *msgBlock) firstMatching(filter string, wc bool, start uint64, sm *Stor
2545
2542
2546
2543
if ! doLinearScan {
2547
2544
// If we have a wildcard match against all tracked subjects we know about.
2548
- if wc {
2549
- subs = subs [:0 ]
2550
- mb .fss .Match (stringToBytes (filter ), func (bsubj []byte , _ * SimpleState ) {
2551
- subs = append (subs , string (bsubj ))
2552
- })
2553
- // Check if we matched anything
2554
- if len (subs ) == 0 {
2555
- return nil , didLoad , ErrStoreMsgNotFound
2556
- }
2557
- }
2558
2545
fseq = lseq + 1
2559
- for _ , subj := range subs {
2560
- ss , _ := mb .fss .Find (stringToBytes (subj ))
2561
- if ss != nil && (ss .firstNeedsUpdate || ss .lastNeedsUpdate ) {
2562
- mb .recalculateForSubj (subj , ss )
2563
- }
2564
- if ss == nil || start > ss .Last || ss .First >= fseq {
2565
- continue
2546
+ if bfilter := stringToBytes (filter ); wc {
2547
+ mb .fss .Match (bfilter , func (bsubj []byte , ss * SimpleState ) {
2548
+ if ss .firstNeedsUpdate || ss .lastNeedsUpdate {
2549
+ mb .recalculateForSubj (bytesToString (bsubj ), ss )
2550
+ }
2551
+ if start <= ss .Last {
2552
+ fseq = min (fseq , max (start , ss .First ))
2553
+ }
2554
+ })
2555
+ } else if ss , _ := mb .fss .Find (bfilter ); ss != nil {
2556
+ if ss .firstNeedsUpdate || ss .lastNeedsUpdate {
2557
+ mb .recalculateForSubj (filter , ss )
2566
2558
}
2567
- if ss .First < start {
2568
- fseq = start
2569
- } else {
2570
- fseq = ss .First
2559
+ if start <= ss .Last {
2560
+ fseq = min (fseq , max (start , ss .First ))
2571
2561
}
2572
2562
}
2573
2563
}
@@ -2576,13 +2566,6 @@ func (mb *msgBlock) firstMatching(filter string, wc bool, start uint64, sm *Stor
2576
2566
return nil , didLoad , ErrStoreMsgNotFound
2577
2567
}
2578
2568
2579
- // If we guess to not do a linear scan, but the above resulted in alot of subs that will
2580
- // need to be checked for every scanned message, revert.
2581
- // TODO(dlc) - we could memoize the subs across calls.
2582
- if ! doLinearScan && len (subs ) > int (lseq - fseq ) {
2583
- doLinearScan = true
2584
- }
2585
-
2586
2569
// Need messages loaded from here on out.
2587
2570
if mb .cacheNotLoaded () {
2588
2571
if err := mb .loadMsgsWithLock (); err != nil {
@@ -2615,18 +2598,10 @@ func (mb *msgBlock) firstMatching(filter string, wc bool, start uint64, sm *Stor
2615
2598
if isAll {
2616
2599
return fsm , expireOk , nil
2617
2600
}
2618
- if doLinearScan {
2619
- if wc && isMatch (sm .subj ) {
2620
- return fsm , expireOk , nil
2621
- } else if ! wc && fsm .subj == filter {
2622
- return fsm , expireOk , nil
2623
- }
2624
- } else {
2625
- for _ , subj := range subs {
2626
- if fsm .subj == subj {
2627
- return fsm , expireOk , nil
2628
- }
2629
- }
2601
+ if wc && isMatch (sm .subj ) {
2602
+ return fsm , expireOk , nil
2603
+ } else if ! wc && fsm .subj == filter {
2604
+ return fsm , expireOk , nil
2630
2605
}
2631
2606
// If we are here we did not match, so put the llseq back.
2632
2607
mb .llseq = llseq
@@ -3042,7 +3017,13 @@ func (fs *fileStore) SubjectsState(subject string) map[string]SimpleState {
3042
3017
func (fs * fileStore ) AllLastSeqs () ([]uint64 , error ) {
3043
3018
fs .mu .RLock ()
3044
3019
defer fs .mu .RUnlock ()
3020
+ return fs .allLastSeqsLocked ()
3021
+ }
3045
3022
3023
+ // allLastSeqsLocked will return a sorted list of last sequences for all
3024
+ // subjects, but won't take the lock to do it, to avoid the issue of compounding
3025
+ // read locks causing a deadlock with a write lock.
3026
+ func (fs * fileStore ) allLastSeqsLocked () ([]uint64 , error ) {
3046
3027
if fs .state .Msgs == 0 || fs .noTrackSubjects () {
3047
3028
return nil , nil
3048
3029
}
@@ -3113,7 +3094,7 @@ func (fs *fileStore) MultiLastSeqs(filters []string, maxSeq uint64, maxAllowed i
3113
3094
3114
3095
// See if we can short circuit if we think they are asking for all last sequences and have no maxSeq or maxAllowed set.
3115
3096
if maxSeq == 0 && maxAllowed <= 0 && fs .filterIsAll (filters ) {
3116
- return fs .AllLastSeqs ()
3097
+ return fs .allLastSeqsLocked ()
3117
3098
}
3118
3099
3119
3100
lastBlkIndex := len (fs .blks ) - 1
0 commit comments