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: prevent race condition when updating job results #1867
Conversation
} | ||
}; | ||
return db.knex.transaction(async (trx) => { | ||
const { result: existingResult } = await schema().from<SyncJob>(SYNC_JOB_TABLE).select('result').where({ id }).first().forUpdate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using transaction and select for update
to lock the row.
finalResult[model].deleted = deletedValue + incomingDeletedValue; | ||
} | ||
if (deletedValue !== 0 || incomingDeletedValue !== 0) { | ||
finalResult[model].deleted = deletedValue + incomingDeletedValue; |
Check warning
Code scanning / CodeQL
Prototype-polluting assignment Medium
user controlled input
This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
979d6b4
to
06c4161
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good 👌🏻
You have a failing test, I'm not sure it's related but let me know
e5f4cde
to
5fac4c9
Compare
5fac4c9
to
50d9b52
Compare
Not sure what's going on with the test. The |
My bad. I hadn't followed the 'DO NOT mock the database' rule and I got bitten. Removed the db mocking in 2018920 |
8e222b9
to
2018920
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good ✅
Took the opportunity to express some concerns but not related to this PR
} from '@nangohq/shared'; | ||
|
||
describe('Persist API', () => { | ||
const port = 3096; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be cool to use https://www.npmjs.com/package/get-port
); | ||
expect(response.status).toEqual(400); | ||
const respBody = (await response.json()) as any[]; | ||
expect(respBody[0]['errors']['issues'][0]['path'][0]).toEqual('records'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not related to this pr: Could be interesting to test the full body with toMatchSnapshot or toMatchObject since we don't have a lot of tests regarding this
} | ||
} | ||
); | ||
expect(response.status).toEqual(201); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not related to this PR: this test do not actually check if the records were deleted
} | ||
} | ||
); | ||
expect(response.status).toEqual(201); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment
const connection = (await connectionService.getConnectionById(connectionId)) as Connection; | ||
if (!connection) throw new Error('Connection not found'); | ||
|
||
const sync = await createSync(connectionId, 'sync-test'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not related to this pr: the fact that we try/catch in models or don't throw makes those boilerplate so unnecessary huge 😩
}; | ||
|
||
const clearDb = async () => { | ||
await db.knex.raw(`DROP SCHEMA nango CASCADE`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should invest in tests helpers to avoid repeating those stuff
} | ||
}; | ||
return db.knex.transaction(async (trx) => { | ||
const { result: existingResult } = await trx.from<SyncJob>(SYNC_JOB_TABLE).select('result').forUpdate().where({ id }).first(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
love that the fix is so small <3
2018920
to
f9fee65
Compare
locking the job row to prevent concurrent call to UpdateSyncJobResult to overwrite each other
f9fee65
to
6a78eb0
Compare
thank you @bodinsamuel. All your points are valid (I have updated the error matching test) and I will improve the tests as part of the track_delete and records migration |
locking the job row to prevent concurrent call to UpdateSyncJobResult to overwrite each other
Issue ticket number and link
https://linear.app/nango/issue/NAN-577/persist-handle-concurrent-access
Checklist before requesting a review (skip if just adding/editing APIs & templates)