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

Add Local Provider optimistic concurrency support #1414

Merged
merged 1 commit into from
Jun 29, 2023

Conversation

gonzalojaubert
Copy link
Contributor

Add Local Provider optimistic concurrency support

@ghost
Copy link

ghost commented Jun 6, 2023

👇 Click on the image for a new way to code review

Review these changes using an interactive CodeSee Map

Legend

CodeSee Map legend

Copy link
Contributor

@alvaroloes alvaroloes left a comment

Choose a reason for hiding this comment

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

Just tons of questions

describe('With two projections for the same ReadModel', () => {
if (process.env.TESTED_PROVIDER === 'AWS') {
console.log('AWS Provider is not working properly when inserting a ReadModel with two projections') // TODO: Fix AWS Provider
return
Copy link
Contributor

Choose a reason for hiding this comment

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

We should create an issue to track this and add details

Comment on lines 41 to 44
docs.map((doc: ReadModelEnvelope) => {
doc.value.boosterMetadata!.optimisticConcurrencyValue = this.generateOptimisticConcurrencyValue(doc.value)
})
resolve(docs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to do this when reading the read model? Would it not be already stored in the DB?

Copy link
Contributor Author

@gonzalojaubert gonzalojaubert Jun 23, 2023

Choose a reason for hiding this comment

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

There could be old readmodels where the value is undefined I removed the field, see my next comment

Comment on lines 52 to 57
const readModelOptimistic: ReadModelEnvelope & { uniqueKey?: string } = readModel
readModelOptimistic.uniqueKey = `${readModel.typeName}_${readModel.value.id}_${readModel.value.boosterMetadata?.version}`
if (readModelOptimistic.value.boosterMetadata?.version === 1) {
return this.insert(readModel)
}
return this.update(readModelOptimistic, expectedCurrentVersion)
Copy link
Contributor

Choose a reason for hiding this comment

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

So we have three values (I think) related to optimistic concurrency, but I don't fully get their purpose. We have:

  • readModel.boosterMetadata.version (contains the read model version)
  • readModel.uniqueKey (a unique key build from the version so that you can achieve the optimistic concurrency)
  • readModel.boosterMetadata.optimisticConcurrencyValue (apparently, it also contains the read model version?)

Could you please clarify their purpose?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, the optimisticConcurrencyValue is not needed as it will be the version always. I will remove it and simplify it. The uniqueKey is, as you said, used to avoid inserting same ReadModel twice. The value of this field will be <id>_<boosterMetadata.version>. If the ReadModel doesn't exists in the database and there are two instances trying to insert the same ReadModel, then both of them will try to do it with version = 0. To be sure that only one of the insert finish successfully we will need to look up into the database before inserting to check if another instance inserted the same read model. As we don't are not able to lock rows or tables we need a unique key to ensure we will never insert a read model with the same and same version (in this case 0).

'value.id': readModel.value.id,
$or: [
{ 'value.boosterMetadata.optimisticConcurrencyValue': { $exists: false } },
{ 'value.boosterMetadata.optimisticConcurrencyValue': expectedCurrentVersion },
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh! I thought NeDB didn't support optimistic concurrency... Why do we need this condition if we are already using the unique key approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll change it to use the version as the field is not needed. About your question, the uniqueKey is only to prevent inserting the same read model twice and the version to update only when the version number is equals to the expected one. If the version is not equals to the expected one it means that the row was updated by another instance.

(err) => {
{ upsert: false, returnUpdatedDocs: true },
(err: unknown, numAffected: number) => {
if (numAffected === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I though when we have an error updating something, numAffected is always 0. How do you know that if this condition is true, then it must be a optimistic concurrency error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's not a perfect solution but in this case we are updating read models only son we can expect that the updated documents will be greater than 0. In this case, using the query (version = expected version) while updating we will have 0 documents updated if the row was updated by another instance.

Copy link
Contributor

@alvaroloes alvaroloes left a comment

Choose a reason for hiding this comment

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

What a great work, specially with the integration tests!!

Add rush change
Add local provider concurrency
@gonzalogarciajaubert
Copy link
Collaborator

/integration sha=a0aca17

@github-actions
Copy link
Contributor

⌛ Integration tests are running...

Check their status here 👈

@github-actions
Copy link
Contributor

✅ Integration tests have finished successfully!

@gonzalogarciajaubert gonzalogarciajaubert merged commit b06f34f into boostercloud:main Jun 29, 2023
6 checks passed
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.

4 participants