prevent double apply of transactions (#5990)

Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
Vyacheslav Tumanov 2024-07-10 16:14:30 +05:00 committed by GitHub
parent 2d8f0ebfec
commit 55af032065
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 14 deletions

View File

@ -96,6 +96,7 @@ class ClientImpl implements AccountClient, BackupClient {
notify?: (...tx: Tx[]) => void notify?: (...tx: Tx[]) => void
hierarchy!: Hierarchy hierarchy!: Hierarchy
model!: ModelDb model!: ModelDb
private readonly appliedModelTransactions = new Set<Ref<Tx>>()
constructor (private readonly conn: ClientConnection) {} constructor (private readonly conn: ClientConnection) {}
setModel (hierarchy: Hierarchy, model: ModelDb): void { setModel (hierarchy: Hierarchy, model: ModelDb): void {
@ -147,6 +148,7 @@ class ClientImpl implements AccountClient, BackupClient {
if (tx.objectSpace === core.space.Model) { if (tx.objectSpace === core.space.Model) {
this.hierarchy.tx(tx) this.hierarchy.tx(tx)
await this.model.tx(tx) await this.model.tx(tx)
this.appliedModelTransactions.add(tx._id)
} }
// We need to handle it on server, before performing local live query updates. // We need to handle it on server, before performing local live query updates.
return await this.conn.tx(tx) return await this.conn.tx(tx)
@ -156,8 +158,13 @@ class ClientImpl implements AccountClient, BackupClient {
for (const t of tx) { for (const t of tx) {
try { try {
if (t.objectSpace === core.space.Model) { if (t.objectSpace === core.space.Model) {
const hasTx = this.appliedModelTransactions.has(t._id)
if (!hasTx) {
this.hierarchy.tx(t) this.hierarchy.tx(t)
await this.model.tx(t) await this.model.tx(t)
} else {
this.appliedModelTransactions.delete(t._id)
}
} }
} catch (err) { } catch (err) {
console.error('failed to apply model transaction, skipping', t) console.error('failed to apply model transaction, skipping', t)

View File

@ -281,13 +281,17 @@ describe('query', () => {
const expectedLength = 2 const expectedLength = 2
let attempt = 0 let attempt = 0
let x: undefined | ((s: any) => void)
const y = new Promise((resolve) => (x = resolve))
const pp = new Promise((resolve) => { const pp = new Promise((resolve) => {
liveQuery.query<Space>(core.class.Space, { private: false }, (result) => { liveQuery.query<Space>(core.class.Space, { private: false }, (result) => {
expect(result).toHaveLength(expectedLength - attempt) expect(result).toHaveLength(expectedLength - attempt)
if (attempt === 0) x?.(null)
if (attempt++ === expectedLength) resolve(null) if (attempt++ === expectedLength) resolve(null)
}) })
}) })
await y
const spaces = await liveQuery.findAll(core.class.Space, {}) const spaces = await liveQuery.findAll(core.class.Space, {})
for (const space of spaces) { for (const space of spaces) {
await factory.removeDoc(space._class, space.space, space._id) await factory.removeDoc(space._class, space.space, space._id)
@ -298,13 +302,16 @@ describe('query', () => {
it('remove with limit', async () => { it('remove with limit', async () => {
const { liveQuery, factory } = await getClient() const { liveQuery, factory } = await getClient()
const expectedLength = 1 const expectedLength = 2
let attempt = 0 let attempt = 0
let x: undefined | ((s: any) => void)
const y = new Promise((resolve) => (x = resolve))
const pp = new Promise((resolve) => { const pp = new Promise((resolve) => {
liveQuery.query<Space>( liveQuery.query<Space>(
core.class.Space, core.class.Space,
{ private: false }, { private: false },
(result) => { (result) => {
if (attempt === 0) x?.(null)
expect(result).toHaveLength(attempt++ === expectedLength ? 0 : 1) expect(result).toHaveLength(attempt++ === expectedLength ? 0 : 1)
if (attempt === expectedLength) resolve(null) if (attempt === expectedLength) resolve(null)
}, },
@ -312,6 +319,7 @@ describe('query', () => {
) )
}) })
await y
const spaces = await liveQuery.findAll(core.class.Space, {}) const spaces = await liveQuery.findAll(core.class.Space, {})
for (const space of spaces) { for (const space of spaces) {
await factory.removeDoc(space._class, space.space, space._id) await factory.removeDoc(space._class, space.space, space._id)
@ -415,14 +423,7 @@ describe('query', () => {
} else { } else {
expect(comment.$lookup?.space).toBeUndefined() expect(comment.$lookup?.space).toBeUndefined()
attempt++ attempt++
} void factory.createDoc(
}
},
{ lookup: { space: core.class.Space } }
)
})
await factory.createDoc(
core.class.Space, core.class.Space,
futureSpace.space, futureSpace.space,
{ {
@ -430,6 +431,13 @@ describe('query', () => {
}, },
futureSpace._id futureSpace._id
) )
}
}
},
{ lookup: { space: core.class.Space } }
)
})
await pp await pp
}) })
@ -580,6 +588,7 @@ describe('query', () => {
} else { } else {
expect((comment.$lookup?.space as Doc)?._id).toEqual(futureSpace) expect((comment.$lookup?.space as Doc)?._id).toEqual(futureSpace)
attempt++ attempt++
void factory.removeDoc(core.class.Space, core.space.Model, futureSpace)
} }
} }
}, },
@ -587,8 +596,6 @@ describe('query', () => {
) )
}) })
await factory.removeDoc(core.class.Space, core.space.Model, futureSpace)
await pp await pp
}) })