@@ -14,6 +14,7 @@ import (
1414 "expvar"
1515 "fmt"
1616 "slices"
17+ "sync"
1718
1819 sgbucket "github.com/couchbase/sg-bucket"
1920)
@@ -23,17 +24,19 @@ var _ Bucket = &LeakyBucket{}
2324// A wrapper around a Bucket to support forced errors. For testing use only.
2425type LeakyBucket struct {
2526 bucket Bucket
26- config * LeakyBucketConfig
27+ _config * LeakyBucketConfig
28+ configLock sync.RWMutex
2729 collections map [string ]* LeakyDataStore
2830}
2931
3032var _ sgbucket.BucketStore = & LeakyBucket {}
3133var _ sgbucket.DynamicDataStoreBucket = & LeakyBucket {}
3234
35+ // NewLeakyBucket creates a wrapper around a Bucket to support forced errors. The configuration will be shared by all DataStores that belong to this bucket.
3336func NewLeakyBucket (bucket Bucket , config LeakyBucketConfig ) * LeakyBucket {
3437 return & LeakyBucket {
3538 bucket : bucket ,
36- config : & config ,
39+ _config : & config ,
3740 collections : make (map [string ]* LeakyDataStore ),
3841 }
3942}
@@ -47,16 +50,11 @@ func (b *LeakyBucket) UUID(ctx context.Context) (string, error) {
4750}
4851
4952func (b * LeakyBucket ) Close (ctx context.Context ) {
50- if ! b .config . IgnoreClose {
53+ if ! b .getIgnoreClose () {
5154 b .bucket .Close (ctx )
5255 }
5356}
5457
55- // For walrus handling, ignore close needs to be set after the bucket is initialized
56- func (b * LeakyBucket ) SetIgnoreClose (value bool ) {
57- b .config .IgnoreClose = value
58- }
59-
6058func (b * LeakyBucket ) CloseAndDelete (ctx context.Context ) error {
6159 if bucket , ok := b .bucket .(sgbucket.DeleteableStore ); ok {
6260 return bucket .CloseAndDelete (ctx )
@@ -73,7 +71,7 @@ func (b *LeakyBucket) GetMaxVbno(ctx context.Context) (uint16, error) {
7371}
7472
7573func (b * LeakyBucket ) DefaultDataStore (ctx context.Context ) sgbucket.DataStore {
76- return NewLeakyDataStore (b , b .bucket .DefaultDataStore (ctx ), b . config )
74+ return NewLeakyDataStore (b , b .bucket .DefaultDataStore (ctx ))
7775}
7876
7977func (b * LeakyBucket ) ListDataStores (ctx context.Context ) ([]sgbucket.DataStoreName , error ) {
@@ -85,7 +83,7 @@ func (b *LeakyBucket) NamedDataStore(ctx context.Context, name sgbucket.DataStor
8583 if err != nil {
8684 return nil , err
8785 }
88- return NewLeakyDataStore (b , dataStore , b . config ), nil
86+ return NewLeakyDataStore (b , dataStore ), nil
8987}
9088
9189func (b * LeakyBucket ) GetUnderlyingBucket () Bucket {
@@ -172,9 +170,10 @@ type LeakyBucketConfig struct {
172170}
173171
174172func (b * LeakyBucket ) StartDCPFeed (ctx context.Context , args sgbucket.FeedArguments , callback sgbucket.FeedEventCallbackFunc , dbStats * expvar.Map ) error {
175- if len (b .config .DCPFeedMissingDocs ) > 0 {
173+ missingDocs := b .getDCPFeedMissingDocs ()
174+ if len (missingDocs ) > 0 {
176175 wrappedCallback := func (event sgbucket.FeedEvent ) bool {
177- if slices .Contains (b . config . DCPFeedMissingDocs , string (event .Key )) {
176+ if slices .Contains (missingDocs , string (event .Key )) {
178177 return false
179178 }
180179 return callback (event )
@@ -183,3 +182,224 @@ func (b *LeakyBucket) StartDCPFeed(ctx context.Context, args sgbucket.FeedArgume
183182 }
184183 return b .bucket .StartDCPFeed (ctx , args , callback , dbStats )
185184}
185+
186+ func (b * LeakyBucket ) getIgnoreClose () bool {
187+ b .configLock .RLock ()
188+ defer b .configLock .RUnlock ()
189+ return b ._config .IgnoreClose
190+ }
191+
192+ func (b * LeakyBucket ) getDCPFeedMissingDocs () []string {
193+ b .configLock .RLock ()
194+ defer b .configLock .RUnlock ()
195+ return b ._config .DCPFeedMissingDocs
196+ }
197+
198+ func (b * LeakyBucket ) getRawCallback () func (string ) error {
199+ b .configLock .RLock ()
200+ defer b .configLock .RUnlock ()
201+ return b ._config .GetRawCallback
202+ }
203+
204+ func (b * LeakyBucket ) setRawCallback (fn func (string ) error ) {
205+ b .configLock .Lock ()
206+ defer b .configLock .Unlock ()
207+ b ._config .GetRawCallback = fn
208+ }
209+
210+ func (b * LeakyBucket ) getWithXattrCallback () func (string ) error {
211+ b .configLock .RLock ()
212+ defer b .configLock .RUnlock ()
213+ return b ._config .GetWithXattrCallback
214+ }
215+
216+ func (b * LeakyBucket ) setWithXattrCallback (fn func (string ) error ) {
217+ b .configLock .Lock ()
218+ defer b .configLock .Unlock ()
219+ b ._config .GetWithXattrCallback = fn
220+ }
221+
222+ func (b * LeakyBucket ) getTouchCallback () func (string ) error {
223+ b .configLock .RLock ()
224+ defer b .configLock .RUnlock ()
225+ return b ._config .TouchCallback
226+ }
227+
228+ func (b * LeakyBucket ) getAddCallback () func (string ) (bool , error ) {
229+ b .configLock .RLock ()
230+ defer b .configLock .RUnlock ()
231+ return b ._config .AddCallback
232+ }
233+
234+ func (b * LeakyBucket ) getForceErrorSetRawKeys () []string {
235+ b .configLock .RLock ()
236+ defer b .configLock .RUnlock ()
237+ return b ._config .ForceErrorSetRawKeys
238+ }
239+
240+ func (b * LeakyBucket ) getWriteCasCallback () func (string ) (uint64 , error ) {
241+ b .configLock .RLock ()
242+ defer b .configLock .RUnlock ()
243+ return b ._config .WriteCasCallback
244+ }
245+
246+ func (b * LeakyBucket ) setWriteCasCallback (fn func (string ) (uint64 , error )) {
247+ b .configLock .Lock ()
248+ defer b .configLock .Unlock ()
249+ b ._config .WriteCasCallback = fn
250+ }
251+
252+ func (b * LeakyBucket ) getUpdateCallback () func (string ) {
253+ b .configLock .RLock ()
254+ defer b .configLock .RUnlock ()
255+ return b ._config .UpdateCallback
256+ }
257+
258+ func (b * LeakyBucket ) setUpdateCallback (fn func (string )) {
259+ b .configLock .Lock ()
260+ defer b .configLock .Unlock ()
261+ b ._config .UpdateCallback = fn
262+ }
263+
264+ func (b * LeakyBucket ) getPostUpdateCallback () func (string ) {
265+ b .configLock .RLock ()
266+ defer b .configLock .RUnlock ()
267+ return b ._config .PostUpdateCallback
268+ }
269+
270+ func (b * LeakyBucket ) setPostUpdateCallback (fn func (string )) {
271+ b .configLock .Lock ()
272+ defer b .configLock .Unlock ()
273+ b ._config .PostUpdateCallback = fn
274+ }
275+
276+ func (b * LeakyBucket ) getForceTimeoutErrorOnUpdateKeys () []string {
277+ b .configLock .RLock ()
278+ defer b .configLock .RUnlock ()
279+ return b ._config .ForceTimeoutErrorOnUpdateKeys
280+ }
281+
282+ func (b * LeakyBucket ) getIncrTemporaryFailCount () uint16 {
283+ b .configLock .RLock ()
284+ defer b .configLock .RUnlock ()
285+ return b ._config .IncrTemporaryFailCount
286+ }
287+
288+ func (b * LeakyBucket ) getIncrCallback () func () {
289+ b .configLock .RLock ()
290+ defer b .configLock .RUnlock ()
291+ return b ._config .IncrCallback
292+ }
293+
294+ // decrementDDocGetErrorCount atomically decrements DDocGetErrorCount if positive.
295+ // Returns the post-decrement remaining count and whether the error should be returned.
296+ func (b * LeakyBucket ) decrementDDocGetErrorCount () (remaining int , shouldFail bool ) {
297+ b .configLock .Lock ()
298+ defer b .configLock .Unlock ()
299+ if b ._config .DDocGetErrorCount > 0 {
300+ b ._config .DDocGetErrorCount --
301+ return b ._config .DDocGetErrorCount , true
302+ }
303+ return 0 , false
304+ }
305+
306+ func (b * LeakyBucket ) setDDocGetErrorCount (i int ) {
307+ b .configLock .Lock ()
308+ defer b .configLock .Unlock ()
309+ b ._config .DDocGetErrorCount = i
310+ }
311+
312+ // decrementDDocDeleteErrorCount atomically decrements DDocDeleteErrorCount if positive.
313+ // Returns the post-decrement remaining count and whether the error should be returned.
314+ func (b * LeakyBucket ) decrementDDocDeleteErrorCount () (remaining int , shouldFail bool ) {
315+ b .configLock .Lock ()
316+ defer b .configLock .Unlock ()
317+ if b ._config .DDocDeleteErrorCount > 0 {
318+ b ._config .DDocDeleteErrorCount --
319+ return b ._config .DDocDeleteErrorCount , true
320+ }
321+ return 0 , false
322+ }
323+
324+ func (b * LeakyBucket ) setDDocDeleteErrorCount (i int ) {
325+ b .configLock .Lock ()
326+ defer b .configLock .Unlock ()
327+ b ._config .DDocDeleteErrorCount = i
328+ }
329+
330+ func (b * LeakyBucket ) getQueryCallback () func (string , string , map [string ]any ) error {
331+ b .configLock .RLock ()
332+ defer b .configLock .RUnlock ()
333+ return b ._config .QueryCallback
334+ }
335+
336+ func (b * LeakyBucket ) setQueryCallback (fn func (string , string , map [string ]any ) error ) {
337+ b .configLock .Lock ()
338+ defer b .configLock .Unlock ()
339+ b ._config .QueryCallback = fn
340+ }
341+
342+ func (b * LeakyBucket ) getPostQueryCallback () func (string , string , map [string ]any ) {
343+ b .configLock .RLock ()
344+ defer b .configLock .RUnlock ()
345+ return b ._config .PostQueryCallback
346+ }
347+
348+ func (b * LeakyBucket ) setPostQueryCallback (fn func (string , string , map [string ]any )) {
349+ b .configLock .Lock ()
350+ defer b .configLock .Unlock ()
351+ b ._config .PostQueryCallback = fn
352+ }
353+
354+ // getAndClearFirstTimeViewCustomPartialError atomically reads and clears the flag.
355+ func (b * LeakyBucket ) getAndClearFirstTimeViewCustomPartialError () bool {
356+ b .configLock .Lock ()
357+ defer b .configLock .Unlock ()
358+ v := b ._config .FirstTimeViewCustomPartialError
359+ if v {
360+ b ._config .FirstTimeViewCustomPartialError = false
361+ }
362+ return v
363+ }
364+
365+ func (b * LeakyBucket ) setFirstTimeViewCustomPartialError (v bool ) {
366+ b .configLock .Lock ()
367+ defer b .configLock .Unlock ()
368+ b ._config .FirstTimeViewCustomPartialError = v
369+ }
370+
371+ func (b * LeakyBucket ) getWriteWithXattrCallback () func (string ) {
372+ b .configLock .RLock ()
373+ defer b .configLock .RUnlock ()
374+ return b ._config .WriteWithXattrCallback
375+ }
376+
377+ func (b * LeakyBucket ) getSetXattrCallback () func (string ) error {
378+ b .configLock .RLock ()
379+ defer b .configLock .RUnlock ()
380+ return b ._config .SetXattrCallback
381+ }
382+
383+ func (b * LeakyBucket ) getN1QLQueryCallback () func (context.Context , string , map [string ]any , ConsistencyMode , bool ) error {
384+ b .configLock .RLock ()
385+ defer b .configLock .RUnlock ()
386+ return b ._config .N1QLQueryCallback
387+ }
388+
389+ func (b * LeakyBucket ) getPostN1QLQueryCallback () func () {
390+ b .configLock .RLock ()
391+ defer b .configLock .RUnlock ()
392+ return b ._config .PostN1QLQueryCallback
393+ }
394+
395+ func (b * LeakyBucket ) setPostN1QLQueryCallback (fn func ()) {
396+ b .configLock .Lock ()
397+ defer b .configLock .Unlock ()
398+ b ._config .PostN1QLQueryCallback = fn
399+ }
400+
401+ func (b * LeakyBucket ) getCreateIndexIfNotExistsCallback () func (string ) {
402+ b .configLock .RLock ()
403+ defer b .configLock .RUnlock ()
404+ return b ._config .CreateIndexIfNotExistsCallback
405+ }
0 commit comments