Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: manage concurent initializations #3132

Merged
merged 15 commits into from
Feb 17, 2025

Conversation

elevatebart
Copy link
Contributor

@elevatebart elevatebart commented Feb 14, 2025

πŸ”— Linked issue

closes #3129

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

Improve the concurent initialization of collections by waiting until one is finished to start another init.

Prevent double init

  • At startup add a record in the info table to state we are about to init
  • When launching a new loadAdapter(), check if info table and the init record are already here. If not, proceed with init.
  • If already in the process of initialization, poll the table every 100ms to see if the record remains
  • When the record has disappeared, return the query
  • If a previous init of a previous version does not go all the way, the version will be different. Then we ignore the flag and init anyway.

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Copy link

pkg-pr-new bot commented Feb 14, 2025

npm i https://pkg.pr.new/@nuxt/content@3132

commit: dd65f2b

Co-authored-by: Farnabaz <[email protected]>
@elevatebart
Copy link
Contributor Author

This is tested and works fine on https://kestra.io/docs

Copy link
Member

@farnabaz farnabaz left a comment

Choose a reason for hiding this comment

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

Thanks @elevatebart for the PR.
Using ready column as lock is smart πŸ‘
I have two concerns regarding endless loop and invalid checksum (comments added)

And a suggestion to remove confusion from code

src/module.ts Outdated

collectionChecksum[collection.name] = hash(collectionDump[collection.name])
collectionChecksum[collection.name] = hash(insertList)
Copy link
Member

Choose a reason for hiding this comment

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

With this change, the table definition is not used for hash generation. For example, if I change the default value of a number column in the table, we will still use the old table because the hash will not change.

Comment on lines 66 to 80
// if another request has already started the initialization of
// this version of this collection
// wait for it to finish
if (before.ready === false && before.version === integrityVersion) {
await new Promise((resolve) => {
const interval = setInterval(async () => {
const { ready } = await db.first<{ ready: boolean }>(`select ready from ${tables.info} where id = ?`, [`checksum_${collection}`]).catch(() => ({ ready: true }))
if (ready) {
clearInterval(interval)
resolve(0)
}
}, 200)
})
return true
}
Copy link
Member

Choose a reason for hiding this comment

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

I think it is worth adding a timeout mechanism to this interval to make sure it always resolves.
Think of an edge case in which importing data fails when updating ready to true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

totally agree. I added a 5 min timeout if it does not respond. Then I throw.

@@ -61,12 +61,30 @@ export async function checkAndImportDatabaseIntegrity(event: H3Event, collection
async function _checkAndImportDatabaseIntegrity(event: H3Event, collection: string, integrityVersion: string, config: RuntimeConfig['content']) {
const db = loadDatabaseAdapter(config)

const before = await db.first<{ version: string }>(`select * from ${tables.info} where id = ?`, [`checksum_${collection}`]).catch(() => ({ version: '' }))
const before = await db.first<{ version: string, ready: boolean }>(`select * from ${tables.info} where id = ?`, [`checksum_${collection}`]).catch(() => ({ version: '', ready: true }))
Copy link
Member

Choose a reason for hiding this comment

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

At first glance it is hard to understand why read is true in catch.
it would be better if we return null or undefined in catch and move the interval logic inside if (before?.version) {.

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

if (before?.version) {
  if (before.ready === true && before.version === integrityVersion) {
    return true
  }
  
  if (before.ready === false && before.version === integrityVersion) {
    // Loop 
  }
  
  // Delete old version -- checksum exists but does not match with bundled checksum
  await db.exec(`DELETE FROM ${tables.info} WHERE id = ?`, [`checksum_${collection}`])
}

Copy link
Member

@farnabaz farnabaz left a comment

Choose a reason for hiding this comment

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

Thanks

@farnabaz farnabaz merged commit c351947 into nuxt:main Feb 17, 2025
3 checks passed
@elevatebart
Copy link
Contributor Author

I was in the process of testing it out, I hope I did not make any mistake...

@elevatebart
Copy link
Contributor Author

And... famous last words, he made a few mistake and broke nuxt content

elevatebart added a commit to elevatebart/content that referenced this pull request Feb 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cloudflare Server Side Rendering fails on big pages.
2 participants