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(mapping): fix insert race conditions #255

Merged
merged 16 commits into from
Nov 25, 2021

Conversation

samuelmasse
Copy link
Contributor

@samuelmasse samuelmasse commented Nov 23, 2021

It's sometime possible that sometimes the server checks if a mapping exists, and then creates it if it doesn't. If this is done fast enough, it's possible that the check to see if the mapping exists returns false more than once, and the process of creating the mapping starts multiple times. The constraints in the db will prevent more than once request from succeeding, meaning that exceptions will be thrown.

This PR fixes this problem

Closes DEV-1618
Closes DEV-2104

@linear
Copy link

linear bot commented Nov 23, 2021

DEV-1618 Race condition of inserts with tunnelId

It seems that the cache should maybe be set before the insert. Sometimes mulitple tunnels with the same value try to get inserted at the same time

@samuelmasse samuelmasse marked this pull request as ready for review November 23, 2021 23:02
Copy link
Contributor

@laurentlp laurentlp left a comment

Choose a reason for hiding this comment

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

Just make sure to comment on the reason behind this semi-hack of having a cache of promise so we don't forget in the future.

Also, we talked about adding a retry in the mapping service. Is this out of the equation?

packages/server/test/mapping.test.ts Show resolved Hide resolved
Comment on lines +107 to +119
async map(tunnelId: uuid, threadId: uuid, userId: uuid): Promise<Convmap> {
const convmap = await this.getByThreadId(tunnelId, threadId)
if (convmap) {
return convmap
}

return this.barrier.once(tunnelId, threadId, async () => {
const tunnel = await this.tunnels.get(tunnelId)
const conversation = await this.conversations.create(tunnel!.clientId, userId)
return this.create(tunnelId, conversation.id, threadId)
})
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@laurentlp here's a barrier in use. The code in the callback will be excuted only once, and return the same value for all Promises waiting on it. The way a single "task" is identified is with arguments before the callback. This particular barrier is a 2d barrier (similarly to our 2d caches), so the key is composed of two keys.

Copy link
Contributor

@laurentlp laurentlp left a comment

Choose a reason for hiding this comment

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

LGTM! Nice work 👌

@samuelmasse samuelmasse merged commit 7e070eb into master Nov 25, 2021
@samuelmasse samuelmasse deleted the sm-fix-insert-race-condition branch November 25, 2021 16:59
@linear
Copy link

linear bot commented Nov 25, 2021

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.

2 participants