Skip to content

Commit c351947

Browse files
fix: manage concurent initializations (#3132)
--------- Co-authored-by: Farnabaz <farnabaz@gmail.com>
1 parent e8d6f73 commit c351947

File tree

3 files changed

+56
-11
lines changed

3 files changed

+56
-11
lines changed

src/module.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,6 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
244244
}
245245
const collectionHash = hash(collection)
246246
collectionDump[collection.name] = []
247-
// Collection table definition
248-
collectionDump[collection.name]!.push(
249-
...generateCollectionTableDefinition(collection, { drop: true }).split('\n'),
250-
)
251247

252248
if (!collection.source) {
253249
continue
@@ -302,14 +298,28 @@ async function processCollectionItems(nuxt: Nuxt, collections: ResolvedCollectio
302298
}
303299
// Sort by file name to ensure consistent order
304300
list.sort((a, b) => String(a[0]).localeCompare(String(b[0])))
305-
collectionDump[collection.name]!.push(...list.flatMap(([, sql]) => sql!))
301+
const insertQueriesList = list.flatMap(([, sql]) => sql!)
306302

307-
collectionChecksum[collection.name] = hash(collectionDump[collection.name])
303+
// Collection table definition
304+
const insertQueriesListWithDefinition = [...generateCollectionTableDefinition(collection, { drop: true }).split('\n'), ...insertQueriesList]
308305

306+
collectionChecksum[collection.name] = hash(insertQueriesListWithDefinition)
307+
308+
// we have to start the series of queries
309+
// by telling everyone that we are setting up the collection so no
310+
// other request start doing the same work and fail
311+
// so we create a new entry in the info table saying that it is not ready yet
309312
collectionDump[collection.name]!.push(
310313
generateCollectionTableDefinition(infoCollection, { drop: false }),
311314
`DELETE FROM ${infoCollection.tableName} WHERE id = 'checksum_${collection.name}';`,
312-
...generateCollectionInsert(infoCollection, { id: `checksum_${collection.name}`, version: collectionChecksum[collection.name] }),
315+
...generateCollectionInsert(infoCollection, { id: `checksum_${collection.name}`, version: collectionChecksum[collection.name], ready: false }),
316+
)
317+
318+
collectionDump[collection.name]!.push(...insertQueriesListWithDefinition)
319+
320+
// and finally when we are finished, we update the info table to say that the init is done
321+
collectionDump[collection.name]!.push(
322+
`UPDATE ${infoCollection.tableName} SET ready = true WHERE id = 'checksum_${collection.name}'`,
313323
)
314324
}
315325
}

src/runtime/internal/database.server.ts

+37-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ export async function checkAndImportDatabaseIntegrity(event: H3Event, collection
4545
if (checkDatabaseIntegrity[String(collection)] !== false) {
4646
checkDatabaseIntegrity[String(collection)] = false
4747
integrityCheckPromise[String(collection)] = integrityCheckPromise[String(collection)] || _checkAndImportDatabaseIntegrity(event, collection, checksums[String(collection)], config)
48-
.then((isValid) => { checkDatabaseIntegrity[String(collection)] = !isValid })
48+
.then((isValid) => {
49+
checkDatabaseIntegrity[String(collection)] = !isValid
50+
})
4951
.catch((error) => {
5052
console.error('Database integrity check failed', error)
5153
checkDatabaseIntegrity[String(collection)] = true
@@ -61,13 +63,44 @@ export async function checkAndImportDatabaseIntegrity(event: H3Event, collection
6163
async function _checkAndImportDatabaseIntegrity(event: H3Event, collection: string, integrityVersion: string, config: RuntimeConfig['content']) {
6264
const db = loadDatabaseAdapter(config)
6365

64-
const before = await db.first<{ version: string }>(`select * from ${tables.info} where id = ?`, [`checksum_${collection}`]).catch(() => ({ version: '' }))
66+
const before: { version: string, ready: boolean } | null = await db.first<{ version: string, ready: boolean }>(`select * from ${tables.info} where id = ?`, [`checksum_${collection}`]).catch((): null => null)
6567

6668
if (before?.version) {
67-
if (before?.version === integrityVersion) {
69+
if (before.ready === true && before.version === integrityVersion) {
70+
// table is already initialized and ready, use it
71+
return true
72+
}
73+
74+
if (before.ready === false && before.version === integrityVersion) {
75+
// if another request has already started the initialization of
76+
// this version of this collection, wait for it to finish
77+
// then respond that the database is ready
78+
// NOTE: only wait if the version is the same so if the previous init
79+
// was interrupted or has failed, it will not block the new init
80+
let iterationCount = 0
81+
await new Promise((resolve, reject) => {
82+
const interval = setInterval(async () => {
83+
const { ready } = await db.first<{ ready: boolean }>(`select ready from ${tables.info} where id = ?`, [`checksum_${collection}`]).catch(() => ({ ready: true }))
84+
85+
if (ready) {
86+
clearInterval(interval)
87+
resolve(0)
88+
}
89+
90+
// after timeout is reached, give up and stop the query
91+
// it has to be that initialization has failed
92+
if (iterationCount++ > 300) {
93+
clearInterval(interval)
94+
reject(new Error('Waiting for another database initialization timed out'))
95+
}
96+
}, 1000)
97+
}).catch((e) => {
98+
throw e
99+
})
68100
return true
69101
}
70-
// Delete old version
102+
103+
// Delete old version -- checksum exists but does not match with bundled checksum
71104
await db.exec(`DELETE FROM ${tables.info} WHERE id = ?`, [`checksum_${collection}`])
72105
}
73106

src/utils/collection.ts

+2
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,12 @@ export function resolveCollections(collections: Record<string, DefinedCollection
8484
schema: z.object({
8585
id: z.string(),
8686
version: z.string(),
87+
ready: z.boolean(),
8788
}),
8889
extendedSchema: z.object({
8990
id: z.string(),
9091
version: z.string(),
92+
ready: z.boolean(),
9193
}),
9294
fields: {},
9395
}

0 commit comments

Comments
 (0)