@@ -19,6 +19,7 @@ import (
19
19
"encoding/json"
20
20
"fmt"
21
21
"sync/atomic"
22
+ "sync"
22
23
"time"
23
24
"unsafe"
24
25
@@ -33,6 +34,7 @@ import (
33
34
"github.com/cockroachdb/cockroach/pkg/util/mon"
34
35
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
35
36
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
37
+ "github.com/cockroachdb/cockroach/pkg/util/drwmutex"
36
38
"github.com/cockroachdb/errors"
37
39
)
38
40
@@ -95,29 +97,32 @@ type Container struct {
95
97
}
96
98
97
99
mu struct {
98
- syncutil.RWMutex
99
-
100
- // acc is the memory account that tracks memory allocations related to stmts
101
- // and txns within this Container struct.
102
- // Since currently we do not destroy the Container struct when we perform
103
- // reset, we never close this account.
104
- acc mon.BoundAccount
100
+ mx drwmutex.DRWMutex
105
101
106
102
stmts map [stmtKey ]* stmtStats
107
103
txns map [appstatspb.TransactionFingerprintID ]* txnStats
108
- }
109
104
110
105
// Use a separate lock to avoid lock contention. Don't block the statement
111
106
// stats just to update the sampled plan time.
112
- muPlanCache struct {
113
- syncutil. RWMutex
107
+ // muPlanCache struct {
108
+ // mx drwmutex.DRWMutex
114
109
115
110
// sampledPlanMetadataCache records when was the last time the plan was
116
111
// sampled. This data structure uses a subset of stmtKey as the key into
117
112
// in-memory dictionary in order to allow lookup for whether a plan has been
118
113
// sampled for a statement without needing to know the statement's
119
114
// transaction fingerprintID.
120
115
sampledPlanMetadataCache map [sampledPlanKey ]time.Time
116
+ //}
117
+ }
118
+
119
+ muAcc struct {
120
+ sync.Mutex
121
+ // acc is the memory account that tracks memory allocations related to stmts
122
+ // and txns within this Container struct.
123
+ // Since currently we do not destroy the Container struct when we perform
124
+ // reset, we never close this account.
125
+ acc mon.BoundAccount
121
126
}
122
127
123
128
txnCounts transactionCounts
@@ -155,12 +160,13 @@ func New(
155
160
}
156
161
157
162
if mon != nil {
158
- s .mu .acc = mon .MakeBoundAccount ()
163
+ s .muAcc .acc = mon .MakeBoundAccount ()
159
164
}
160
165
166
+ s .mu .mx = drwmutex .New ()
161
167
s .mu .stmts = make (map [stmtKey ]* stmtStats )
162
168
s .mu .txns = make (map [appstatspb.TransactionFingerprintID ]* txnStats )
163
- s .muPlanCache .sampledPlanMetadataCache = make (map [sampledPlanKey ]time.Time )
169
+ s .mu .sampledPlanMetadataCache = make (map [sampledPlanKey ]time.Time )
164
170
165
171
s .atomic .uniqueStmtFingerprintCount = uniqueStmtFingerprintCount
166
172
s .atomic .uniqueTxnFingerprintCount = uniqueTxnFingerprintCount
@@ -276,7 +282,7 @@ func NewTempContainerFromExistingStmtStats(
276
282
transactionFingerprintID : statistics [i ].Key .KeyData .TransactionFingerprintID ,
277
283
}
278
284
stmtStats , _ , throttled :=
279
- container .getStatsForStmtWithKeyLocked (key , statistics [i ].ID , true /* createIfNonexistent */ )
285
+ container .getStatsForStmtWithKey (key , statistics [i ].ID , true /* createIfNonexistent */ )
280
286
if throttled {
281
287
return nil /* container */ , nil /* remaining */ , ErrFingerprintLimitReached
282
288
}
@@ -360,8 +366,9 @@ func (s *Container) NewApplicationStatsWithInheritedOptions() sqlstats.Applicati
360
366
uniqueStmtFingerprintCount int64
361
367
uniqueTxnFingerprintCount int64
362
368
)
363
- s .mu .Lock ()
364
- defer s .mu .Unlock ()
369
+ rlock := s .mu .mx .RLocker ()
370
+ rlock .Lock ()
371
+ defer rlock .Unlock ()
365
372
return New (
366
373
s .st ,
367
374
sqlstats .MaxSQLStatsStmtFingerprintsPerExplicitTxn ,
@@ -552,31 +559,23 @@ func (s *Container) getStatsForStmtWithKey(
552
559
) (stats * stmtStats , created , throttled bool ) {
553
560
// Use the read lock to get the key to avoid contention.
554
561
ok := func () (ok bool ) {
555
- s .mu .RLock ()
556
- defer s . mu . RUnlock ()
562
+ rlock := s .mu .mx . RLocker ()
563
+ rlock . Lock ()
557
564
stats , ok = s .mu .stmts [key ]
565
+ rlock .Unlock ()
566
+
558
567
return ok
559
568
}()
560
569
if ok || ! createIfNonexistent {
561
- return stats , false /* created */ , false /* throttled */
562
- }
563
-
564
- // Key does not exist in map. Take a full lock to add the key.
565
- s .mu .Lock ()
566
- defer s .mu .Unlock ()
567
- return s .getStatsForStmtWithKeyLocked (key , stmtFingerprintID , createIfNonexistent )
570
+ return stats , false /* created */ , false /* throttled */
571
+ }
572
+ // Key does not exist in map, slow path.
573
+ return s .getStatsForStmtWithKeySlow (key , stmtFingerprintID , createIfNonexistent )
568
574
}
569
575
570
- func (s * Container ) getStatsForStmtWithKeyLocked (
576
+ func (s * Container ) getStatsForStmtWithKeySlow (
571
577
key stmtKey , stmtFingerprintID appstatspb.StmtFingerprintID , createIfNonexistent bool ,
572
578
) (stats * stmtStats , created , throttled bool ) {
573
- // Retrieve the per-statement statistic object, and create it if it
574
- // doesn't exist yet.
575
- stats , ok := s .mu .stmts [key ]
576
- if ok || ! createIfNonexistent {
577
- return stats , false /* created */ , false /* throttled */
578
- }
579
-
580
579
// If the uniqueStmtFingerprintCount is nil, then we don't check for
581
580
// fingerprint limit.
582
581
if s .atomic .uniqueStmtFingerprintCount != nil {
@@ -594,9 +593,12 @@ func (s *Container) getStatsForStmtWithKeyLocked(
594
593
}
595
594
stats = & stmtStats {}
596
595
stats .ID = stmtFingerprintID
597
- s . mu . stmts [ key ] = stats
596
+ t := s . getTimeNow ()
598
597
599
- s .setLogicalPlanLastSampled (key .sampledPlanKey , s .getTimeNow ())
598
+ s .mu .mx .Lock ()
599
+ s .mu .stmts [key ] = stats
600
+ s .mu .sampledPlanMetadataCache [key .sampledPlanKey ] = t
601
+ s .mu .mx .Unlock ()
600
602
601
603
return stats , true /* created */ , false /* throttled */
602
604
}
@@ -608,18 +610,19 @@ func (s *Container) getStatsForTxnWithKey(
608
610
) (stats * txnStats , created , throttled bool ) {
609
611
// Use the read lock to get the key to avoid contention
610
612
ok := func () (ok bool ) {
611
- s .mu .RLock ()
612
- defer s .mu .RUnlock ()
613
+ rlock := s .mu .mx .RLocker ()
614
+ rlock .Lock ()
615
+ defer rlock .Unlock ()
613
616
stats , ok = s .mu .txns [key ]
614
617
return ok
615
618
}()
616
619
if ok || ! createIfNonexistent {
617
620
return stats , false /* created */ , false /* throttled */
618
621
}
619
-
620
622
// Key does not exist in map. Take a full lock to add the key.
621
- s .mu .Lock ()
622
- defer s .mu .Unlock ()
623
+ s .mu .mx .Lock ()
624
+ defer s .mu .mx .Unlock ()
625
+
623
626
return s .getStatsForTxnWithKeyLocked (key , stmtFingerprintIDs , createIfNonexistent )
624
627
}
625
628
@@ -657,8 +660,9 @@ func (s *Container) getStatsForTxnWithKeyLocked(
657
660
658
661
// SaveToLog saves the existing statement stats into the info log.
659
662
func (s * Container ) SaveToLog (ctx context.Context , appName string ) {
660
- s .mu .RLock ()
661
- defer s .mu .RUnlock ()
663
+ rlock := s .mu .mx .RLocker ()
664
+ rlock .Lock ()
665
+ defer rlock .Unlock ()
662
666
if len (s .mu .stmts ) == 0 {
663
667
return
664
668
}
@@ -679,36 +683,28 @@ func (s *Container) SaveToLog(ctx context.Context, appName string) {
679
683
// Clear clears the data stored in this Container and prepare the Container
680
684
// for reuse.
681
685
func (s * Container ) Clear (ctx context.Context ) {
682
- s .mu .Lock ()
683
- defer s .mu .Unlock ()
684
-
685
- s .freeLocked (ctx )
686
-
686
+ s .Free (ctx )
687
+
688
+ s .mu .mx .Lock ()
689
+ defer s .mu .mx .Unlock ()
687
690
// Clear the map, to release the memory; make the new map somewhat already
688
691
// large for the likely future workload.
689
692
s .mu .stmts = make (map [stmtKey ]* stmtStats , len (s .mu .stmts )/ 2 )
690
693
s .mu .txns = make (map [appstatspb.TransactionFingerprintID ]* txnStats , len (s .mu .txns )/ 2 )
691
694
692
- s .muPlanCache .Lock ()
693
- defer s .muPlanCache .Unlock ()
694
- s .muPlanCache .sampledPlanMetadataCache = make (map [sampledPlanKey ]time.Time , len (s .muPlanCache .sampledPlanMetadataCache )/ 2 )
695
+ s .mu .sampledPlanMetadataCache = make (map [sampledPlanKey ]time.Time , len (s .mu .sampledPlanMetadataCache )/ 2 )
695
696
}
696
697
697
698
// Free frees the accounted resources from the Container. The Container is
698
699
// presumed to be no longer in use and its actual allocated memory will
699
700
// eventually be GC'd.
700
701
func (s * Container ) Free (ctx context.Context ) {
701
- s .mu .Lock ()
702
- defer s .mu .Unlock ()
703
-
704
- s .freeLocked (ctx )
705
- }
706
-
707
- func (s * Container ) freeLocked (ctx context.Context ) {
708
702
atomic .AddInt64 (s .atomic .uniqueStmtFingerprintCount , int64 (- len (s .mu .stmts )))
709
- atomic .AddInt64 (s .atomic .uniqueTxnFingerprintCount , int64 (- len (s .mu .txns )))
703
+ atomic .AddInt64 (s .atomic .uniqueTxnFingerprintCount , int64 (- len (s .mu .txns )))
710
704
711
- s .mu .acc .Clear (ctx )
705
+ s .muAcc .Lock ()
706
+ s .muAcc .acc .Clear (ctx )
707
+ s .muAcc .Unlock ()
712
708
}
713
709
714
710
// MergeApplicationStatementStats implements the sqlstats.ApplicationStats interface.
@@ -801,8 +797,9 @@ func (s *Container) MergeApplicationTransactionStats(
801
797
// a lock on a will cause a deadlock.
802
798
func (s * Container ) Add (ctx context.Context , other * Container ) (err error ) {
803
799
statMap := func () map [stmtKey ]* stmtStats {
804
- other .mu .RLock ()
805
- defer other .mu .RUnlock ()
800
+ rlock := other .mu .mx .RLocker ()
801
+ rlock .Lock ()
802
+ defer rlock .Unlock ()
806
803
807
804
statMap := make (map [stmtKey ]* stmtStats )
808
805
for k , v := range other .mu .stmts {
@@ -845,11 +842,14 @@ func (s *Container) Add(ctx context.Context, other *Container) (err error) {
845
842
// We still want to continue this loop to merge stats that are already
846
843
// present in our map that do not require allocation.
847
844
if latestErr := func () error {
848
- s .mu .Lock ()
849
- defer s .mu .Unlock ()
850
- growErr := s .mu .acc .Grow (ctx , estimatedAllocBytes )
845
+ s .muAcc .Lock ()
846
+ growErr := s .muAcc .acc .Grow (ctx , estimatedAllocBytes )
847
+ s .muAcc .Unlock ()
848
+
851
849
if growErr != nil {
850
+ s .mu .mx .Lock ()
852
851
delete (s .mu .stmts , k )
852
+ s .mu .mx .Unlock ()
853
853
}
854
854
return growErr
855
855
}(); latestErr != nil {
@@ -871,8 +871,8 @@ func (s *Container) Add(ctx context.Context, other *Container) (err error) {
871
871
872
872
// Do what we did above for the statMap for the txn Map now.
873
873
txnMap := func () map [appstatspb.TransactionFingerprintID ]* txnStats {
874
- other .mu .Lock ()
875
- defer other .mu .Unlock ()
874
+ other .mu .mx . Lock ()
875
+ defer other .mu .mx . Unlock ()
876
876
txnMap := make (map [appstatspb.TransactionFingerprintID ]* txnStats )
877
877
for k , v := range other .mu .txns {
878
878
txnMap [k ] = v
@@ -915,10 +915,10 @@ func (s *Container) Add(ctx context.Context, other *Container) (err error) {
915
915
// We still want to continue this loop to merge stats that are already
916
916
// present in our map that do not require allocation.
917
917
if latestErr := func () error {
918
- s .mu .Lock ()
919
- defer s .mu .Unlock ()
918
+ s .muAcc .Lock ()
919
+ growErr := s .muAcc .acc .Grow (ctx , estimatedAllocBytes )
920
+ s .muAcc .Unlock ()
920
921
921
- growErr := s .mu .acc .Grow (ctx , estimatedAllocBytes )
922
922
if growErr != nil {
923
923
delete (s .mu .txns , k )
924
924
}
@@ -979,16 +979,17 @@ func (s *transactionCounts) recordTransactionCounts(
979
979
func (s * Container ) getLogicalPlanLastSampled (
980
980
key sampledPlanKey ,
981
981
) (lastSampled time.Time , found bool ) {
982
- s .muPlanCache .RLock ()
983
- defer s .muPlanCache .RUnlock ()
984
- lastSampled , found = s .muPlanCache .sampledPlanMetadataCache [key ]
982
+ rlock := s .mu .mx .RLocker ()
983
+ rlock .Lock ()
984
+ defer rlock .Unlock ()
985
+ lastSampled , found = s .mu .sampledPlanMetadataCache [key ]
985
986
return lastSampled , found
986
987
}
987
988
988
989
func (s * Container ) setLogicalPlanLastSampled (key sampledPlanKey , time time.Time ) {
989
- s .muPlanCache .Lock ()
990
- defer s .muPlanCache .Unlock ()
991
- s .muPlanCache .sampledPlanMetadataCache [key ] = time
990
+ s .mu . mx .Lock ()
991
+ defer s .mu . mx .Unlock ()
992
+ s .mu .sampledPlanMetadataCache [key ] = time
992
993
}
993
994
994
995
// shouldSaveLogicalPlanDescription returns whether we should save the sample
0 commit comments