-
-
Notifications
You must be signed in to change notification settings - Fork 43
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
Gotcha with async prepared statement execution #199
Comments
Interesting, you should be using transactions anyways to insert data anyways as it is a disk operation that can fail. Does using prepared statements on inserts have a speed benefit? It should be mostly an append operation and maybe an index update? |
Maybe there is something we can do here @rflopezm. We should put a mutex when a prepared statement is executing to prevent this race condition. This can be done on TypeScript on index.ts no need to modify the C++ code. |
This is inside a transaction (manually calling |
manually calling BEGIN TRANSACTION is not the same as using db.transaction. db.transaction has a built in mutex to prevent race conditions between statements. I guess the speed up you are seeing is not because of the prepared statement, is the avoidance of this mutex but you loose the guarantees of consistency. Maybe you are better off using executeBatch which wraps multiple statements in the native code. |
I finally understood the issue. Unfortunately, the changes I was making won't solve this directly by limiting the threads to one. The issue is queueing work to a thread takes time before the next task must be consumed. There is one easy solution though, which is making |
@ospfranco I tried a few different methods including the ones above and this was the fastest to insert - we're loading data from an API and inserting 100s of thousands of records across around 100 tables in batches of 200-2000. Using We were originally reusing the prepared statement across simultaneous HTTP response handlers when doing the inserts. Removing that (i.e. going to 1 PS per handler) hasn't changed performance too much and has resolved the issue for us as we were already binding/executing records in a loop within each batch, so no concurrency issues there. Re making I think I'm leaning towards this belonging in the category of holding it wrong, but if there's a way to make it more foolproof then awesome :) Thanks for looking into it 👍🏻 |
You are not holding it wrong. It is a built-in race condition |
New version is out, let preparedStatement = ...
await preparedStatement.bind(...)
await preparedStatement.execute(...) |
Awesome, thanks again @ospfranco ❤️ |
I don't think this is a bug report as such, more of a gotcha that might be worth mentioning in docs.
The scenario is fetching some data in parallel and inserting each batch as it's received over the network.
Previously with the synchronous api, when calling
bind
/execute
on a prepared statement shared between all batches, there was never a case where execute would execute with bound data other than the data bound immediately before the execute call.Now with the asynchronous API, it seems it's possible for race conditions to occur in this scenario, where I'm guessing the prepared statement is reset or rebound by another network request handler between binding and executing. This causes occasional errors
Can be resolved by queuing up the data to be inserted, or probably by not reusing the prepared statement between batches - haven't tried that yet though.
The text was updated successfully, but these errors were encountered: