@@ -18,10 +18,13 @@ import (
18
18
var log = logging .Logger ("provider.queue" )
19
19
20
20
const (
21
+ batchSize = 1024
22
+ batchCommitInterval = 5 * time .Second
23
+
21
24
// Number of input CIDs to buffer without blocking.
22
- inputBufferSize = 65536
25
+ inputBufferSize = 1024 * 256
23
26
// Time for Close to wait to finish writing CIDs to datastore.
24
- shutdownTimeout = 5 * time .Second
27
+ shutdownTimeout = 30 * time .Second
25
28
)
26
29
27
30
// Queue provides a FIFO interface to the datastore for storing cids.
@@ -36,46 +39,49 @@ const (
36
39
type Queue struct {
37
40
// used to differentiate queues in datastore
38
41
// e.g. provider vs reprovider
39
- ds datastore.Datastore // Must be threadsafe
42
+ ds datastore.Batching
40
43
dequeue chan cid.Cid
41
44
enqueue chan cid.Cid
42
45
inBuf * chanqueue.ChanQueue [cid.Cid ] // in-memory queue to buffer input
43
46
close context.CancelFunc
44
47
closed chan struct {}
45
48
closeOnce sync.Once
49
+
50
+ syncDone chan struct {}
51
+ syncMutex sync.Mutex
46
52
}
47
53
48
54
// NewQueue creates a queue for cids
49
- func NewQueue (ds datastore.Datastore ) * Queue {
55
+ func NewQueue (ds datastore.Batching ) * Queue {
50
56
dequeue := make (chan cid.Cid )
51
57
enqueue := make (chan cid.Cid )
52
58
59
+ ctx , cancel := context .WithCancel (context .Background ())
53
60
q := & Queue {
54
- dequeue : dequeue ,
55
- enqueue : enqueue ,
61
+ close : cancel ,
62
+ closed : make (chan struct {}),
63
+ ds : namespace .Wrap (ds , datastore .NewKey ("/queue" )),
64
+ dequeue : dequeue ,
65
+ enqueue : enqueue ,
66
+ syncDone : make (chan struct {}, 1 ),
56
67
}
57
68
58
- if ds == nil {
59
- q .inBuf = chanqueue .New (
60
- chanqueue .WithInput (enqueue ),
61
- chanqueue .WithOutput (dequeue ),
62
- chanqueue.WithCapacity [cid.Cid ](inputBufferSize ),
63
- )
64
- } else {
65
- ctx , cancel := context .WithCancel (context .Background ())
66
- q .close = cancel
67
- q .inBuf = chanqueue .New (
68
- chanqueue .WithInput (enqueue ),
69
- chanqueue.WithCapacity [cid.Cid ](inputBufferSize ),
70
- )
71
- q .ds = namespace .Wrap (ds , datastore .NewKey ("/queue" ))
72
- q .closed = make (chan struct {})
73
- go q .worker (ctx )
74
- }
69
+ q .inBuf = chanqueue .New (
70
+ chanqueue .WithInput (enqueue ),
71
+ chanqueue.WithCapacity [cid.Cid ](inputBufferSize ),
72
+ )
73
+ go q .worker (ctx )
75
74
76
75
return q
77
76
}
78
77
78
+ func (q * Queue ) Sync () {
79
+ q .syncMutex .Lock ()
80
+ q .inBuf .In () <- cid .Undef
81
+ <- q .syncDone
82
+ q .syncMutex .Unlock ()
83
+ }
84
+
79
85
// Close stops the queue
80
86
func (q * Queue ) Close () error {
81
87
var err error
@@ -117,6 +123,7 @@ func (q *Queue) Dequeue() <-chan cid.Cid {
117
123
// worker run dequeues and enqueues when available.
118
124
func (q * Queue ) worker (ctx context.Context ) {
119
125
defer close (q .closed )
126
+ defer q .inBuf .Shutdown ()
120
127
121
128
var (
122
129
c cid.Cid = cid .Undef
@@ -126,7 +133,27 @@ func (q *Queue) worker(ctx context.Context) {
126
133
)
127
134
readInBuf := q .inBuf .Out ()
128
135
136
+ var batchCount int
137
+ b , err := q .ds .Batch (ctx )
138
+ if err != nil {
139
+ log .Errorf ("Failed to create batch, stopping provider: %s" , err )
140
+ return
141
+ }
142
+
143
+ defer func () {
144
+ if batchCount != 0 {
145
+ if err := b .Commit (ctx ); err != nil {
146
+ log .Errorf ("Failed to write cid batch: %s" , err )
147
+ }
148
+ }
149
+ close (q .syncDone )
150
+ }()
151
+
152
+ batchTicker := time .NewTicker (batchCommitInterval )
153
+ defer batchTicker .Stop ()
154
+
129
155
for {
156
+ //fmt.Println("---> inbuf len:", q.inBuf.Len(), "batch count:", batchCount)
130
157
if c == cid .Undef {
131
158
head , err := q .getQueueHead (ctx )
132
159
if err != nil {
@@ -153,12 +180,20 @@ func (q *Queue) worker(ctx context.Context) {
153
180
if c != cid .Undef {
154
181
dequeue = q .dequeue
155
182
}
183
+ var commit , needSync bool
156
184
157
185
select {
158
186
case toQueue , ok := <- readInBuf :
159
187
if ! ok {
160
188
return
161
189
}
190
+ if toQueue == cid .Undef {
191
+ if batchCount != 0 {
192
+ commit = true
193
+ }
194
+ needSync = true
195
+ break
196
+ }
162
197
// Add suffix to key path to allow multiple entries with same sequence.
163
198
nextKey := datastore .NewKey (fmt .Sprintf ("%020d/%s" , counter , cstr ))
164
199
counter ++
@@ -170,24 +205,53 @@ func (q *Queue) worker(ctx context.Context) {
170
205
cstr = c .String ()
171
206
}
172
207
173
- if err := q .ds .Put (ctx , nextKey , toQueue .Bytes ()); err != nil {
174
- log .Errorf ("Failed to enqueue cid: %s" , err )
208
+ //if err := q.ds.Put(ctx, nextKey, toQueue.Bytes()); err != nil {
209
+ if err := b .Put (ctx , nextKey , toQueue .Bytes ()); err != nil {
210
+ log .Errorf ("Failed to batch cid: %s" , err )
175
211
continue
176
212
}
213
+ batchCount ++
214
+ if batchCount == batchSize {
215
+ commit = true
216
+ }
217
+ case <- batchTicker .C :
218
+ if batchCount != 0 && q .inBuf .Len () == 0 {
219
+ commit = true
220
+ }
177
221
case dequeue <- c :
222
+ // Do not batch delete. Delete must be committed immediately, otherwise the same head cid will be read from the datastore.
178
223
err := q .ds .Delete (ctx , k )
179
224
if err != nil {
180
225
log .Errorf ("Failed to delete queued cid %s with key %s: %s" , c , k , err )
181
226
continue
182
227
}
183
228
c = cid .Undef
184
229
case <- ctx .Done ():
185
- if q .inBuf != nil {
186
- for range readInBuf {
187
- }
188
- }
189
230
return
190
231
}
232
+
233
+ if commit {
234
+ if err = b .Commit (ctx ); err != nil {
235
+ log .Errorf ("Failed to write cid batch, stopping provider: %s" , err )
236
+ return
237
+ }
238
+ b , err = q .ds .Batch (ctx )
239
+ if err != nil {
240
+ log .Errorf ("Failed to create batch, stopping provider: %s" , err )
241
+ return
242
+ }
243
+ batchCount = 0
244
+ commit = false
245
+ }
246
+
247
+ if needSync {
248
+ needSync = false
249
+ select {
250
+ case q .syncDone <- struct {}{}:
251
+ case <- ctx .Done ():
252
+ return
253
+ }
254
+ }
191
255
}
192
256
}
193
257
0 commit comments