Skip to content

Commit

Permalink
fix: Drop Chokidar initial scan events on stop
Browse files Browse the repository at this point in the history
  When stopping the Chokidar based local watcher while the initial scan
  of the local synchronization directory is taking place, we should drop
  all buffered events rather than flush them and try to process them.

  First, we don't really have any urgency to do so as these events will
  be generated during the next initial scan (when restarting the watcher
  once again), contrary to events emitted by the filesystem while the
  watcher is already running.

  Second, flushing and processing these events can lead to painful
  consequences if the scan was not complete yet since the initial scan
  step will consider any document not scanned yet as missing and will
  generate deletion events for them.
  • Loading branch information
taratatach committed Oct 4, 2023
1 parent 64f169f commit f0e128d
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 4 deletions.
4 changes: 2 additions & 2 deletions core/local/chokidar/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class LocalWatcher {

if (!this.watcher) return

if (force) {
if (force || !this.initialScanParams.flushed) {
// Drop buffered events
this.buffer.clear()
} else {
Expand All @@ -289,7 +289,7 @@ class LocalWatcher {
// Stop underlying Chokidar watcher
await this.watcher.close()
this.watcher = null
// Stop accepting new events
// Flush buffer and stop flushes loop
this.buffer.switchMode('idle')

if (!force) {
Expand Down
75 changes: 73 additions & 2 deletions test/unit/local/chokidar/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ const { ContextDir } = require('../../../support/helpers/context_dir')
const { onPlatform } = require('../../../support/helpers/platform')
const pouchHelpers = require('../../../support/helpers/pouch')

// TODO: run on darwin platform instead?
onPlatform('linux', () => {
onPlatform('darwin', () => {
describe('ChokidarWatcher Tests', function () {
let builders

Expand Down Expand Up @@ -124,6 +123,78 @@ onPlatform('linux', () => {
})
})

describe('stop', () => {
context('when initial scan events have not been flushed yet', () => {
beforeEach(async function () {
await builders.metafile().path('no-event').upToDate().create()

this.watcher.initialScanParams = {
paths: [],
emptyDirRetryCount: 3,
resolve: Promise.resolve,
flushed: false,
done: false
}
// XXX: fake presence of Chokidar watcher
this.watcher.watcher = { close: sinon.stub().resolves() }
})

it('clears the buffer and does not flush any event', async function () {
const onFlushSpy = sinon.spy(this.watcher, 'onFlush')
try {
this.watcher.buffer.push({
type: 'addDir',
path: __dirname,
stats: builders.stats().build()
})
should(this.watcher.buffer.events).have.length(1)

await this.watcher.stop()

should(this.watcher.buffer.events).be.empty()
should(onFlushSpy).not.have.been.called()
} finally {
onFlushSpy.restore()
}
})
})

context('when intial scan events have already been flushed', () => {
beforeEach(async function () {
await builders.metafile().path('no-event').upToDate().create()

this.watcher.initialScanParams = {
paths: [],
emptyDirRetryCount: 3,
resolve: Promise.resolve,
flushed: true,
done: false
}
// XXX: fake presence of Chokidar watcher
this.watcher.watcher = { close: sinon.stub().resolves() }
})

it('tries to flush buffered events before stopping', async function () {
const onFlushSpy = sinon.spy(this.watcher, 'onFlush')
try {
this.watcher.buffer.push({
type: 'addDir',
path: __dirname,
stats: builders.stats().build()
})
should(this.watcher.buffer.events).have.length(1)

await this.watcher.stop()

should(this.watcher.buffer.events).be.empty()
should(onFlushSpy).have.been.calledOnce()
} finally {
onFlushSpy.restore()
}
})
})
})

describe('checksum', () => {
const relpath = 'foo.txt'
let abspath
Expand Down

0 comments on commit f0e128d

Please sign in to comment.