Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/mapeo-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ export class MapeoManager extends TypedEmitter {
// in the config store - defining the name of the project.
// TODO: Enforce adding a project name in the invite method
const isConfigSynced = configState.want === 0 && configState.have > 0
if (ownRole.sync.config === 'blocked' && isAuthSynced) return true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to check isRoleSynced here too. However note that currently it looks like Roles.NO_ROLE allows sync of the config cores, which to me seems like a bug, and likely the cause of the race conditions you have been seeing - before any role is synced, the permissions of NO_ROLE is allowing sync of the config core to happen.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Should we maybe set NO_ROLE to disallow sync of the config core?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can't think of a reason why it would be necessary for NO_ROLE to sync the config core.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deviceInfo is under the config, so maybe that's why?

if (
isRoleSynced &&
isProjectSettingsSynced &&
Expand All @@ -803,6 +804,7 @@ export class MapeoManager extends TypedEmitter {
this.#l.log(
'Pending initial sync: role %s, projectSettings %o, auth %o, config %o',
isRoleSynced,
isProjectSettingsSynced,
isAuthSynced,
isConfigSynced
)
Expand All @@ -811,12 +813,15 @@ export class MapeoManager extends TypedEmitter {
/** @param {import('./sync/sync-state.js').State} syncState */
const onSyncState = (syncState) => {
clearTimeout(timeoutId)
if (syncState.auth.dataToSync || syncState.config.dataToSync) {
if (
syncState.auth.dataToSync ||
(syncState.config.dataToSync && ownRole.sync.config === 'allowed')
) {
timeoutId = setTimeout(onTimeout, timeoutMs)
return
}
project.$sync[kSyncState].off('state', onSyncState)
resolve(this.#waitForInitialSync(project, { timeoutMs }))
this.#waitForInitialSync(project, { timeoutMs }).then(resolve, reject)
}
const onTimeout = () => {
project.$sync[kSyncState].off('state', onSyncState)
Expand Down
15 changes: 8 additions & 7 deletions test-e2e/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,8 @@ test('no sync capabilities === no namespaces sync apart from auth', async (t) =>
const [invitor, invitee, blocked] = managers
const disconnect1 = connectPeers(managers)

t.after(() => disconnect1())

const projectId = await invitor.createProject({ name: 'Mapeo' })

await invite({
Expand All @@ -919,6 +921,8 @@ test('no sync capabilities === no namespaces sync apart from auth', async (t) =>
managers.map((m) => m.getProject(projectId))
)

t.after(() => Promise.all(projects.map((p) => p.close())))

const [invitorProject, inviteeProject] = projects

assert.equal(
Expand Down Expand Up @@ -955,18 +959,15 @@ test('no sync capabilities === no namespaces sync apart from auth', async (t) =>
assert.equal(blockedState.data.localState.have, 0) // no data docs synced

for (const ns of NAMESPACES) {
assert.equal(invitorState[ns].coreCount, 3, ns)
assert.equal(inviteeState[ns].coreCount, 3, ns)
assert.equal(blockedState[ns].coreCount, 3, ns)
assert.equal(invitorState[ns].coreCount, 3, `invitor got cores for ${ns}`)
assert.equal(inviteeState[ns].coreCount, 3, `invitee got cores for ${ns}`)
assert.equal(blockedState[ns].coreCount, 3, `blocked got cores for ${ns}`)
assert.deepEqual(
invitorState[ns].localState,
inviteeState[ns].localState,
ns
`invitor/invitee have same local state for ${ns}`
)
}

await disconnect1()
await Promise.all(projects.map((p) => p.close()))
})

test('Sync state emitted when starting and stopping sync', async function (t) {
Expand Down
Loading