Skip to content

Commit

Permalink
feat: add API to change sync auto-stop timeout (#747)
Browse files Browse the repository at this point in the history
`project.$sync.setAutostopDataSyncTimeout(1234)` sets the auto-stop
timeout to 1234 milliseconds without changing the enabled state of sync.

Closes [#746].

[#746]: #746
  • Loading branch information
EvanHahn authored Aug 14, 2024
1 parent 3537d5b commit 81bf517
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 8 deletions.
23 changes: 17 additions & 6 deletions src/sync/sync-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,13 @@ export class SyncApi extends TypedEmitter {
* happens after this duration in milliseconds, sync will be automatically
* stopped as if {@link stop} was called.
*/
start({ autostopDataSyncAfter = null } = {}) {
assertAutostopDataSyncAfterIsValid(autostopDataSyncAfter)
start({ autostopDataSyncAfter } = {}) {
this.#wantsToSyncData = true
this.#autostopDataSyncAfter = autostopDataSyncAfter
// Ensure the timeout is started anew.
this.#clearAutostopDataSyncTimeoutIfExists()
this.#updateState()
if (autostopDataSyncAfter === undefined) {
this.#updateState()
} else {
this.setAutostopDataSyncTimeout(autostopDataSyncAfter)
}
}

/**
Expand Down Expand Up @@ -279,6 +279,17 @@ export class SyncApi extends TypedEmitter {
this.#updateState()
}

/**
* @param {null | number} autostopDataSyncAfter
* @returns {void}
*/
setAutostopDataSyncTimeout(autostopDataSyncAfter) {
assertAutostopDataSyncAfterIsValid(autostopDataSyncAfter)
this.#clearAutostopDataSyncTimeoutIfExists()
this.#autostopDataSyncAfter = autostopDataSyncAfter
this.#updateState()
}

/**
* @param {SyncType} type
* @returns {Promise<void>}
Expand Down
110 changes: 108 additions & 2 deletions test-e2e/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ test('auto-stop', async (t) => {
)
const [invitorProject, inviteeProject] = projects

const generatedObservations = generate('observation', { count: 2 })
const generatedObservations = generate('observation', { count: 3 })

invitorProject.$sync.start({ autostopDataSyncAfter: 10_000 })
inviteeProject.$sync.start({ autostopDataSyncAfter: 10_000 })
Expand Down Expand Up @@ -302,7 +302,7 @@ test('auto-stop', async (t) => {
"invitee hasn't auto-stopped yet because the timer has been restarted"
)

const invitorProjectOnSyncDisabled = pEvent(
let invitorProjectOnSyncDisabled = pEvent(
invitorProject.$sync,
'sync-state',
({ data: { isSyncEnabled } }) => !isSyncEnabled
Expand All @@ -327,6 +327,109 @@ test('auto-stop', async (t) => {
!inviteeProject.$sync.getState().data.isSyncEnabled,
'invitee has auto-stopped'
)

invitorProject.$sync.setAutostopDataSyncTimeout(20_000)
assert(
!invitorProject.$sync.getState().data.isSyncEnabled,
'invitor is still stopped'
)

invitorProject.$sync.start()

const observation3 = await invitorProject.observation.create(
valueOf(generatedObservations[2])
)
await waitForSync(projects, 'full')
assert(
await inviteeProject.observation.getByDocId(observation3.docId),
'invitee receives doc'
)

await clock.tickAsync(19_000)

assert(
invitorProject.$sync.getState().data.isSyncEnabled,
"invitor hasn't auto-stopped"
)

invitorProjectOnSyncDisabled = pEvent(
invitorProject.$sync,
'sync-state',
({ data: { isSyncEnabled } }) => !isSyncEnabled
)

clock.tick(2_000)

await invitorProjectOnSyncDisabled
assert(
!invitorProject.$sync.getState().data.isSyncEnabled,
'invitor has auto-stopped'
)

invitorProject.$sync.start({ autostopDataSyncAfter: 999_999 })
invitorProject.$sync.setAutostopDataSyncTimeout(20_000)

assert(
invitorProject.$sync.getState().data.isSyncEnabled,
'invitor has not yet auto-stopped'
)

invitorProjectOnSyncDisabled = pEvent(
invitorProject.$sync,
'sync-state',
({ data: { isSyncEnabled } }) => !isSyncEnabled
)
clock.tick(21_000)
await invitorProjectOnSyncDisabled

assert(
!invitorProject.$sync.getState().data.isSyncEnabled,
'invitor has auto-stopped'
)
})

test('disabling auto-stop timeout', async (t) => {
const clock = FakeTimers.install({ shouldAdvanceTime: true })
t.after(() => clock.uninstall())

const managers = await createManagers(2, t)
const [invitor, ...invitees] = managers

const disconnect = connectPeers(managers, { discovery: false })
t.after(disconnect)

const projectId = await invitor.createProject({ name: 'mapeo' })
await invite({ invitor, invitees, projectId })

const projects = await Promise.all(
managers.map((m) => m.getProject(projectId))
)
const [invitorProject, inviteeProject] = projects

invitorProject.$sync.start({ autostopDataSyncAfter: 10_000 })
invitorProject.$sync.setAutostopDataSyncTimeout(null)

inviteeProject.$sync.start()

assert(
invitorProject.$sync.getState().data.isSyncEnabled,
"invitor hasn't auto-stopped yet"
)

const observation1 = await invitorProject.observation.create(
valueOf(generate('observation')[0])
)
await waitForSync(projects, 'full')
assert(
await inviteeProject.observation.getByDocId(observation1.docId),
'invitee receives doc'
)

await clock.tickAsync(999_999_999)
assert(
invitorProject.$sync.getState().data.isSyncEnabled,
"invitor still hasn't auto-stopped"
)
})

test('validates auto-stop timeouts', async (t) => {
Expand All @@ -339,6 +442,9 @@ test('validates auto-stop timeouts', async (t) => {
assert.throws(() => {
project.$sync.start({ autostopDataSyncAfter })
})
assert.throws(() => {
project.$sync.setAutostopDataSyncTimeout(autostopDataSyncAfter)
})
}

assert(!project.$sync.getState().data.isSyncEnabled, 'sync is not enabled')
Expand Down

0 comments on commit 81bf517

Please sign in to comment.