aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/sqlite3-opfs-async-proxy.js
diff options
context:
space:
mode:
authorstephan <stephan@noemail.net>2022-10-04 09:12:05 +0000
committerstephan <stephan@noemail.net>2022-10-04 09:12:05 +0000
commitc7fb48d4b6144b0f89fe05e7790c0e8d309494a3 (patch)
tree053b3c64eb6b82422c194436e883bc3aa358a581 /ext/wasm/sqlite3-opfs-async-proxy.js
parentf6b6188bdd930921257a72ea3eab99160120040f (diff)
downloadsqlite-c7fb48d4b6144b0f89fe05e7790c0e8d309494a3.tar.gz
sqlite-c7fb48d4b6144b0f89fe05e7790c0e8d309494a3.zip
OPFS async proxy: add a wait-and-retry policy to the get-sync-handle step to help account for cross-tab locking.
FossilOrigin-Name: 45c48c63d311052105d102189208495b2b53fa3c4174884ecf63b757aa4016e5
Diffstat (limited to 'ext/wasm/sqlite3-opfs-async-proxy.js')
-rw-r--r--ext/wasm/sqlite3-opfs-async-proxy.js58
1 files changed, 42 insertions, 16 deletions
diff --git a/ext/wasm/sqlite3-opfs-async-proxy.js b/ext/wasm/sqlite3-opfs-async-proxy.js
index 58bbf7928..b2b77fd4e 100644
--- a/ext/wasm/sqlite3-opfs-async-proxy.js
+++ b/ext/wasm/sqlite3-opfs-async-proxy.js
@@ -144,13 +144,38 @@ const getDirForFilename = async function f(absFilename, createDirs = false){
handle object (which must be a valid handle object), lazily opening
it if needed. Timestamps the handle for use in relinquishing it
during idle time.
+
+ In order to help alleviate cross-tab contention for a dabase,
+ if an exception is thrown while acquiring the handle, this routine
+ will wait briefly and try again, up to 3 times. If acquisition
+ still fails at that point it will give up and propagate the
+ exception.
*/
const getSyncHandle = async (fh)=>{
if(!fh.syncHandle){
- //const t = performance.now();
- //warn("Creating sync handle for",fh.filenameAbs);
- fh.syncHandle = await fh.fileHandle.createSyncAccessHandle();
- //warn("Got sync handle for",fh.filenameAbs,'in',performance.now() - t,'ms');
+ const t = performance.now();
+ log("Acquiring sync handle for",fh.filenameAbs);
+ const maxTries = 3;
+ let i = 1, ms = 300;
+ for(; true; ms *= ++i){
+ try {
+ //if(1===i) toss("Just testing.");
+ //TODO? A config option which tells it to throw here
+ //randomly every now and then.
+ fh.syncHandle = await fh.fileHandle.createSyncAccessHandle();
+ break;
+ }catch(e){
+ if(i === maxTries){
+ toss("Error getting sync handle.",maxTries,
+ "attempts failed. ",fh.filenameAbs, ":", e.message);
+ throw e;
+ }
+ error("Error getting sync handle. Waiting",ms,
+ "ms and trying again.",fh.filenameAbs,e);
+ Atomics.wait(state.sabOPView, state.opIds.xSleep, 0, ms);
+ }
+ }
+ log("Got sync handle for",fh.filenameAbs,'in',performance.now() - t,'ms');
}
fh.syncHandleTime = performance.now();
return fh.syncHandle;
@@ -589,23 +614,24 @@ const waitLoop = async function f(){
}
/**
waitTime is how long (ms) to wait for each Atomics.wait().
- We need to wait up periodically to give the thread a chance
+ We need to wake up periodically to give the thread a chance
to do other things.
*/
- const waitTime = 250;
+ const waitTime = 200;
/**
- relinquishTime defines the_approximate_ number of ms
- after which a db sync access handle will be relinquished
- so that we do not hold a persistent lock on it. When the following loop
- times out while waiting, every (approximate) increment of this
- value it will relinquish any db handles which have been idle for
- at least this much time.
+ relinquishTime defines the_approximate_ number of ms after which
+ a db sync access handle will be relinquished so that we do not
+ hold a persistent lock on it. When the following loop times out
+ while waiting, every (approximate) increment of this value it
+ will relinquish any db handles which have been idle for at least
+ this much time.
Reaquisition of a sync handle seems to take an average of
- 0.6-0.9ms on this dev machine but takes anywhere from 1-3ms
- every once in a while (maybe 1 time in 5 or 10).
+ 0.6-0.9ms on this dev machine but takes anywhere from 1-3ms every
+ once in a while (maybe 1 time in 5 or 10). Outliers as long as
+ 7ms have been witnessed, but they're rare.
*/
- const relinquishTime = 1000;
+ const relinquishTime = 750;
let lastOpTime = performance.now();
let now;
while(!flagAsyncShutdown){
@@ -619,7 +645,7 @@ const waitLoop = async function f(){
if(fh.syncHandle && (
now - relinquishTime >= fh.syncHandleTime
)){
- //warn("Relinquishing for timeout:",fh.filenameAbs);
+ log("Relinquishing for timeout:",fh.filenameAbs);
await closeSyncHandle(fh)
/* Testing shows that we have to wait on this async
op to finish, else we might try to re-open it