42
42
type persistentQueueSettings [T any ] struct {
43
43
sizer sizer [T ]
44
44
capacity int64
45
+ blocking bool
45
46
signal pipeline.Signal
46
47
storageID component.ID
47
48
marshaler Marshaler [T ]
@@ -78,10 +79,12 @@ type persistentQueue[T any] struct {
78
79
79
80
// isRequestSized indicates whether the queue is sized by the number of requests.
80
81
isRequestSized bool
82
+ blocking bool
81
83
82
84
// mu guards everything declared below.
83
85
mu sync.Mutex
84
- hasElements * sync.Cond
86
+ hasMoreElements * cond
87
+ hasMoreSpace * cond
85
88
readIndex uint64
86
89
writeIndex uint64
87
90
currentlyDispatchedItems []uint64
@@ -95,10 +98,11 @@ func newPersistentQueue[T any](set persistentQueueSettings[T]) Queue[T] {
95
98
_ , isRequestSized := set .sizer .(* requestSizer [T ])
96
99
pq := & persistentQueue [T ]{
97
100
set : set ,
98
- logger : set .set .Logger ,
99
101
isRequestSized : isRequestSized ,
102
+ blocking : set .blocking ,
100
103
}
101
- pq .hasElements = sync .NewCond (& pq .mu )
104
+ pq .hasMoreElements = newCond (& pq .mu )
105
+ pq .hasMoreSpace = newCond (& pq .mu )
102
106
return pq
103
107
}
104
108
@@ -194,7 +198,7 @@ func (pq *persistentQueue[T]) Shutdown(ctx context.Context) error {
194
198
backupErr := pq .backupQueueSize (ctx )
195
199
// Mark this queue as stopped, so consumer don't start any more work.
196
200
pq .stopped = true
197
- pq .hasElements .Broadcast ()
201
+ pq .hasMoreElements .Broadcast ()
198
202
return multierr .Combine (backupErr , pq .unrefClient (ctx ))
199
203
}
200
204
@@ -233,8 +237,13 @@ func (pq *persistentQueue[T]) Offer(ctx context.Context, req T) error {
233
237
// putInternal is the internal version that requires caller to hold the mutex lock.
234
238
func (pq * persistentQueue [T ]) putInternal (ctx context.Context , req T ) error {
235
239
reqSize := pq .set .sizer .Sizeof (req )
236
- if pq .queueSize + reqSize > pq .set .capacity {
237
- return ErrQueueIsFull
240
+ for pq .queueSize + reqSize > pq .set .capacity {
241
+ if ! pq .blocking {
242
+ return ErrQueueIsFull
243
+ }
244
+ if err := pq .hasMoreSpace .Wait (ctx ); err != nil {
245
+ return err
246
+ }
238
247
}
239
248
240
249
reqBuf , err := pq .set .marshaler (req )
@@ -253,7 +262,7 @@ func (pq *persistentQueue[T]) putInternal(ctx context.Context, req T) error {
253
262
254
263
pq .writeIndex ++
255
264
pq .queueSize += reqSize
256
- pq .hasElements .Signal ()
265
+ pq .hasMoreElements .Signal ()
257
266
258
267
// Back up the queue size to storage every 10 writes. The stored value is used to recover the queue size
259
268
// in case if the collector is killed. The recovered queue size is allowed to be inaccurate.
@@ -270,31 +279,31 @@ func (pq *persistentQueue[T]) Read(ctx context.Context) (uint64, context.Context
270
279
pq .mu .Lock ()
271
280
defer pq .mu .Unlock ()
272
281
for {
273
- if pq .stopped {
274
- var req T
275
- return 0 , context .Background (), req , false
276
- }
277
282
278
283
// If queue is empty, wait until more elements and restart.
279
- if pq .readIndex == pq .writeIndex {
280
- pq .hasElements .Wait ()
281
- continue
282
- }
283
-
284
- index , req , consumed := pq .getNextItem (ctx )
285
- if consumed {
286
- pq .queueSize -= pq .set .sizer .Sizeof (req )
287
- // The size might be not in sync with the queue in case it's restored from the disk
288
- // because we don't flush the current queue size on the disk on every read/write.
289
- // In that case we need to make sure it doesn't go below 0.
290
- if pq .queueSize < 0 {
291
- pq .queueSize = 0
284
+ for pq .readIndex != pq .writeIndex {
285
+ index , req , consumed := pq .getNextItem (ctx )
286
+ if consumed {
287
+ pq .queueSize -= pq .set .sizer .Sizeof (req )
288
+ // The size might be not in sync with the queue in case it's restored from the disk
289
+ // because we don't flush the current queue size on the disk on every read/write.
290
+ // In that case we need to make sure it doesn't go below 0.
291
+ if pq .queueSize < 0 {
292
+ pq .queueSize = 0
293
+ }
294
+
295
+ return index , context .Background (), req , true
292
296
}
297
+ }
293
298
294
- return index , context .Background (), req , true
299
+ if pq .stopped {
300
+ var req T
301
+ return 0 , context .Background (), req , false
295
302
}
296
303
297
- // If we did not consume any element retry from the beginning.
304
+ // TODO: Change the Queue interface to return an error to allow distinguish between shutdown and context canceled.
305
+ // Ok to ignore the error, since the context.Background() will never be done.
306
+ _ = pq .hasMoreElements .Wait (context .Background ())
298
307
}
299
308
}
300
309
0 commit comments