@@ -42,7 +42,7 @@ const DEFAULT_WRITE_TIMEOUT = 250
42
42
const DEFAULT_VALIDATE = ( ) => true
43
43
44
44
module . exports = function AsyncAppendOnlyLog ( filename , opts ) {
45
- const cache = new Cache ( 1024 ) // this is potentially 65mb !
45
+ const cache = new Cache ( 1024 ) // This is potentially 64 MiB !
46
46
const raf = RAF ( filename )
47
47
const blockSize = ( opts && opts . blockSize ) || DEFAULT_BLOCK_SIZE
48
48
const codec = ( opts && opts . codec ) || DEFAULT_CODEC
@@ -52,7 +52,9 @@ module.exports = function AsyncAppendOnlyLog(filename, opts) {
52
52
53
53
const waitingLoad = [ ]
54
54
const waitingDrain = new Map ( ) // blockIndex -> []
55
+ const waitingFlushDelete = [ ]
55
56
const blocksToBeWritten = new Map ( ) // blockIndex -> { blockBuf, offset }
57
+ const blocksWithDeletables = new Map ( ) // blockIndex -> blockBuf
56
58
let writingBlockIndex = - 1
57
59
58
60
let latestBlockBuf = null
@@ -244,25 +246,64 @@ module.exports = function AsyncAppendOnlyLog(filename, opts) {
244
246
cb ( delDuringCompactErr ( ) )
245
247
return
246
248
}
247
- if ( blocksToBeWritten . has ( getBlockIndex ( offset ) ) ) {
249
+ const blockIndex = getBlockIndex ( offset )
250
+ if ( blocksToBeWritten . has ( blockIndex ) ) {
248
251
onDrain ( function delAfterDrained ( ) {
249
252
del ( offset , cb )
250
253
} )
251
254
return
252
255
}
253
- getBlock ( offset , function gotBlockForDelete ( err , blockBuf ) {
256
+
257
+ if ( blocksWithDeletables . has ( blockIndex ) ) {
258
+ const blockBuf = blocksWithDeletables . get ( blockIndex )
259
+ gotBlockForDelete ( null , blockBuf )
260
+ } else {
261
+ getBlock ( offset , gotBlockForDelete )
262
+ }
263
+ function gotBlockForDelete ( err , blockBuf ) {
254
264
if ( err ) return cb ( err )
255
- Record . overwriteWithZeroes ( blockBuf , getOffsetInBlock ( offset ) )
256
- // we write directly here to make normal write simpler
257
- const blockStart = getBlockStart ( offset )
258
- writeWithFSync ( blockStart , blockBuf , null , cb )
259
- } )
265
+ const actualBlockBuf = blocksWithDeletables . get ( blockIndex ) || blockBuf
266
+ Record . overwriteWithZeroes ( actualBlockBuf , getOffsetInBlock ( offset ) )
267
+ blocksWithDeletables . set ( blockIndex , actualBlockBuf )
268
+ scheduleFlushDelete ( )
269
+ cb ( )
270
+ }
260
271
}
261
272
262
273
function hasNoSpaceFor ( dataBuf , offsetInBlock ) {
263
274
return offsetInBlock + Record . size ( dataBuf ) + EOB . SIZE > blockSize
264
275
}
265
276
277
+ const scheduleFlushDelete = debounce ( flushDelete , writeTimeout )
278
+
279
+ function flushDelete ( ) {
280
+ if ( blocksWithDeletables . size === 0 ) {
281
+ for ( const cb of waitingFlushDelete ) cb ( )
282
+ waitingFlushDelete . length = 0
283
+ return
284
+ }
285
+ const blockIndex = blocksWithDeletables . keys ( ) . next ( ) . value
286
+ const blockStart = blockIndex * blockSize
287
+ const blockBuf = blocksWithDeletables . get ( blockIndex )
288
+ blocksWithDeletables . delete ( blockIndex )
289
+ blocksWithDeletables . set ( - 1 , null ) // indicate that flush is active
290
+
291
+ writeWithFSync ( blockStart , blockBuf , null , function flushedDelete ( err ) {
292
+ blocksWithDeletables . delete ( - 1 ) // indicate that flush is not active
293
+ if ( err ) {
294
+ for ( const cb of waitingFlushDelete ) cb ( err )
295
+ waitingFlushDelete . length = 0
296
+ return
297
+ }
298
+ flushDelete ( ) // next
299
+ } )
300
+ }
301
+
302
+ function onDeletesFlushed ( cb ) {
303
+ if ( blocksWithDeletables . size === 0 ) cb ( )
304
+ else waitingFlushDelete . push ( cb )
305
+ }
306
+
266
307
function appendSingle ( data ) {
267
308
let encodedData = codec . encode ( data )
268
309
if ( typeof encodedData === 'string' ) encodedData = Buffer . from ( encodedData )
@@ -439,26 +480,29 @@ module.exports = function AsyncAppendOnlyLog(filename, opts) {
439
480
return
440
481
}
441
482
onDrain ( function startCompactAfterDrain ( ) {
442
- compaction = new Compaction ( self , ( err , sizeDiff ) => {
443
- compaction = null
444
- if ( err ) return cb ( err )
445
- compactionProgress . set ( { sizeDiff , percent : 1 , done : true } )
446
- for ( let i = 0 , n = waitingCompaction . length ; i < n ; ++ i ) {
447
- waitingCompaction [ i ] ( )
448
- }
449
- waitingCompaction . length = 0
450
- cb ( )
451
- } )
452
- compaction . progress ( ( stats ) => {
453
- compactionProgress . set ( { ... stats , done : false } )
483
+ onDeletesFlushed ( function startCompactAfterDeletes ( ) {
484
+ compaction = new Compaction ( self , ( err , sizeDiff ) => {
485
+ compaction = null
486
+ if ( err ) return cb ( err )
487
+ compactionProgress . set ( { sizeDiff , percent : 1 , done : true } )
488
+ for ( const callback of waitingCompaction ) callback ( )
489
+ waitingCompaction . length = 0
490
+ cb ( )
491
+ } )
492
+ compaction . progress ( ( stats ) => {
493
+ compactionProgress . set ( { ... stats , done : false } )
494
+ } )
454
495
} )
455
496
} )
456
497
}
457
498
458
499
function close ( cb ) {
459
500
onDrain ( function closeAfterHavingDrained ( ) {
460
- while ( self . streams . length ) self . streams . shift ( ) . abort ( streamClosedErr ( ) )
461
- raf . close ( cb )
501
+ onDeletesFlushed ( function closeAfterDeletesFlushed ( ) {
502
+ for ( const stream of self . streams ) stream . abort ( streamClosedErr ( ) )
503
+ self . streams = [ ]
504
+ raf . close ( cb )
505
+ } )
462
506
} )
463
507
}
464
508
@@ -500,6 +544,7 @@ module.exports = function AsyncAppendOnlyLog(filename, opts) {
500
544
appendTransaction : onLoad ( appendTransaction ) ,
501
545
close : onLoad ( close ) ,
502
546
onDrain : onLoad ( onDrain ) ,
547
+ onDeletesFlushed : onLoad ( onDeletesFlushed ) ,
503
548
compact : onLoad ( compact ) ,
504
549
since,
505
550
compactionProgress,
0 commit comments