Skip to content

Commit

Permalink
fix: Restart sync loop when resuming from sleep (#2370)
Browse files Browse the repository at this point in the history
When the computer goes to sleep, we stop the synchronization lifecycle
which results in suspending the synchronization loop.

The `resume()` method used to call the `start()` method which in turn
starts the synchronization loop.
However, the `resume()` method now has its own actions and we forgot
to start the synchronization loop as part of them.

This resulted in changes not being synchronized although they were
detected by the watchers.
  • Loading branch information
taratatach authored Jan 6, 2025
2 parents d3d87cb + 2a1fcf6 commit 69d3268
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 20 deletions.
35 changes: 19 additions & 16 deletions core/sync/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,14 +288,7 @@ class Sync {

this.lifecycle.end('start')

try {
while (!this.lifecycle.willStop()) {
await this.lifecycle.ready()
await this.sync()
}
} catch (err) {
await this.fatal(err)
}
await this.runSyncLoop()
}

async resume() {
Expand All @@ -313,6 +306,8 @@ class Sync {
this.local.resume()
this.remote.resume()
this.lifecycle.end('start')

await this.runSyncLoop()
}

suspend() {
Expand Down Expand Up @@ -391,15 +386,23 @@ class Sync {
return this.stop()
}

async sync({
manualRun = false
} /*: { manualRun?: boolean } */ = {}) /*: Promise<*> */ {
let seq = await this.pouch.getLocalSeq()
async runSyncLoop() {
try {
while (!this.lifecycle.willStop()) {
await this.lifecycle.ready()

const seq = await this.pouch.getLocalSeq()
const change = await this.waitForChangeAfter(seq)
if (change == null) continue

if (!manualRun) {
const change = await this.waitForNewChanges(seq)
if (change == null) return
await this.sync()
}
} catch (err) {
await this.fatal(err)
}
}

async sync() /*: Promise<*> */ {
this.events.emit('sync-start')
try {
await this.syncBatch()
Expand Down Expand Up @@ -615,7 +618,7 @@ class Sync {

// Wait until a change is emitted by PouchDB into its changesfeed (i.e. we've
// merged some change on a document).
async waitForNewChanges(seq /*: number */) {
async waitForChangeAfter(seq /*: number */) {
log.trace('Waiting for changes since seq', { seq })
const opts = this.baseChangeOptions(seq)
opts.live = true
Expand Down
2 changes: 1 addition & 1 deletion test/support/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class TestHelpers {

async syncAll() {
this._sync.lifecycle.transitionTo('done-start')
await this._sync.sync({ manualRun: true })
await this._sync.sync()
// Wait until all potentially blocking changes have been handled
await this._sync.lifecycle.ready()
this._sync.lifecycle.transitionTo('done-stop')
Expand Down
6 changes: 3 additions & 3 deletions test/unit/sync/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('Sync', function() {
events.emit('remote:fatal', err)
})
this.remote.stop = sinon.stub().resolves()
this.sync.sync = sinon.stub().resolves()
this.sync.runSyncLoop = sinon.stub().resolves()
sinon.spy(this.sync, 'stop')
sinon.spy(this.sync.events, 'emit')
})
Expand All @@ -108,7 +108,7 @@ describe('Sync', function() {
await this.sync.started()
should(this.local.start).have.been.calledOnce()
should(this.remote.start).have.been.calledOnce()
should(this.sync.sync).have.been.called()
should(this.sync.runSyncLoop).have.been.called()
})

context('if local watcher fails to start', () => {
Expand All @@ -118,7 +118,7 @@ describe('Sync', function() {

it('does not start replication', async function() {
await this.sync.start()
should(this.sync.sync).not.have.been.called()
should(this.sync.runSyncLoop).not.have.been.called()
})

it('does not start remote watcher', async function() {
Expand Down

0 comments on commit 69d3268

Please sign in to comment.