mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-27 20:41:58 +03:00
More tweaking of OPFS concurrency measures and the related test app.
FossilOrigin-Name: a8d4da1501d411085ec2fd48c4a056c8b1d97ef3c3203c5b403a854ac2864870
This commit is contained in:
@ -220,22 +220,19 @@ class GetSyncHandleError extends Error {
|
||||
}
|
||||
};
|
||||
GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
if(1){
|
||||
/* This approach returns SQLITE_LOCKED to the C API
|
||||
when getSyncHandle() fails but makes the very
|
||||
wild assumption that such a failure _is_ a locking
|
||||
error. In practice that appears to be the most
|
||||
common error, by far, but we cannot unambiguously
|
||||
if(0){
|
||||
/* This approach makes the very wild assumption that such a
|
||||
failure _is_ a locking error. In practice that appears to be
|
||||
the most common error, by far, but we cannot unambiguously
|
||||
distinguish that from other errors.
|
||||
|
||||
This approach demonstrably reduces concurrency-related
|
||||
errors but is highly questionable.
|
||||
This approach is highly questionable.
|
||||
*/
|
||||
return (e instanceof GetSyncHandleError)
|
||||
? state.sq3Codes.SQLITE_LOCKED
|
||||
? state.sq3Codes.SQLITE_IOERR_LOCK
|
||||
: rc;
|
||||
}else{
|
||||
return ec;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -253,7 +250,7 @@ const getSyncHandle = async (fh)=>{
|
||||
if(!fh.syncHandle){
|
||||
const t = performance.now();
|
||||
log("Acquiring sync handle for",fh.filenameAbs);
|
||||
const maxTries = 4, msBase = 300;
|
||||
const maxTries = 6, msBase = 300;
|
||||
let i = 1, ms = msBase;
|
||||
for(; true; ms = msBase * ++i){
|
||||
try {
|
||||
@ -271,7 +268,7 @@ const getSyncHandle = async (fh)=>{
|
||||
}
|
||||
warn("Error getting sync handle. Waiting",ms,
|
||||
"ms and trying again.",fh.filenameAbs,e);
|
||||
//await closeAutoLocks();
|
||||
await closeAutoLocks();
|
||||
Atomics.wait(state.sabOPView, state.opIds.retry, 0, ms);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,21 @@
|
||||
<h1></h1>
|
||||
<p>
|
||||
OPFS concurrency tester using multiple independent Workers.
|
||||
This app is incomplete.
|
||||
Disclaimer: concurrency in OPFS is currently a pain point
|
||||
and timing/concurrency mitigation in this environment is
|
||||
highly unpredictable!
|
||||
</p>
|
||||
<p>
|
||||
URL flags: pass a number of workers using
|
||||
the <code>workers=N</code> URL flag and the worker work interval
|
||||
as <code>interval=N</code> (milliseconds). Enable OPFS VFS
|
||||
verbosity with <code>verbose=1-3</code> (output goes to the
|
||||
dev console).
|
||||
</p>
|
||||
<p>Achtung: if it does not start to do anything within a couple of
|
||||
seconds, check the dev console: Chrome often fails with "cannot allocate
|
||||
WasmMemory" at startup. Closing and re-opening the tab usually resolves
|
||||
it.
|
||||
</p>
|
||||
<div class='input-wrapper'>
|
||||
<input type='checkbox' id='cb-log-reverse'>
|
||||
|
@ -57,6 +57,12 @@
|
||||
options.workerCount = (
|
||||
urlArgsHtml.has('workers') ? +urlArgsHtml.get('workers') : 3
|
||||
) || 3;
|
||||
options.opfsVerbose = (
|
||||
urlArgsHtml.has('verbose') ? +urlArgsHtml.get('verbose') : 1
|
||||
) || 1;
|
||||
options.interval = (
|
||||
urlArgsHtml.has('interval') ? +urlArgsHtml.get('interval') : 750
|
||||
) || 750;
|
||||
const workers = [];
|
||||
workers.post = (type,...args)=>{
|
||||
for(const w of workers) w.postMessage({type, payload:args});
|
||||
@ -91,7 +97,8 @@
|
||||
workers.uri = (
|
||||
'worker.js?'
|
||||
+ 'sqlite3.dir='+options.sqlite3Dir
|
||||
+ '&opfs-verbose=2'
|
||||
+ '&interval='+options.interval
|
||||
+ '&opfs-verbose='+options.opfsVerbose
|
||||
);
|
||||
for(let i = 0; i < options.workerCount; ++i){
|
||||
stdout("Launching worker...");
|
||||
|
@ -8,7 +8,6 @@ self.sqlite3InitModule().then(async function(sqlite3){
|
||||
};
|
||||
const stdout = (...args)=>wPost('stdout',...args);
|
||||
const stderr = (...args)=>wPost('stderr',...args);
|
||||
const postErr = (...args)=>wPost('error',...args);
|
||||
if(!sqlite3.opfs){
|
||||
stderr("OPFS support not detected. Aborting.");
|
||||
return;
|
||||
@ -19,15 +18,33 @@ self.sqlite3InitModule().then(async function(sqlite3){
|
||||
};
|
||||
|
||||
const dbName = 'concurrency-tester.db';
|
||||
if((new URL(self.location.href).searchParams).has('unlink-db')){
|
||||
const urlArgs = new URL(self.location.href).searchParams;
|
||||
if(urlArgs.has('unlink-db')){
|
||||
await sqlite3.opfs.unlink(dbName);
|
||||
stdout("Unlinked",dbName);
|
||||
}
|
||||
wPost('loaded');
|
||||
|
||||
let db;
|
||||
const interval = Object.assign(Object.create(null),{
|
||||
delay: urlArgs.has('interval') ? (+urlArgs.get('interval') || 750) : 750,
|
||||
handle: undefined,
|
||||
count: 0
|
||||
});
|
||||
const finish = ()=>{
|
||||
if(db){
|
||||
if(!db.pointer) return;
|
||||
db.close();
|
||||
}
|
||||
if(interval.error){
|
||||
wPost('failed',"Ending work after interval #"+interval.count,
|
||||
"due to error:",interval.error);
|
||||
}else{
|
||||
wPost('finished',"Ending work after",interval.count,"intervals.");
|
||||
}
|
||||
};
|
||||
const run = async function(){
|
||||
const db = new sqlite3.opfs.OpfsDb(dbName,'c');
|
||||
//sqlite3.capi.sqlite3_busy_timeout(db.pointer, 2000);
|
||||
db = new sqlite3.opfs.OpfsDb(dbName,'c');
|
||||
sqlite3.capi.sqlite3_busy_timeout(db.pointer, 5000);
|
||||
db.transaction((db)=>{
|
||||
db.exec([
|
||||
"create table if not exists t1(w TEXT UNIQUE ON CONFLICT REPLACE,v);",
|
||||
@ -36,11 +53,6 @@ self.sqlite3InitModule().then(async function(sqlite3){
|
||||
});
|
||||
|
||||
const maxIterations = 10;
|
||||
const interval = Object.assign(Object.create(null),{
|
||||
delay: 500,
|
||||
handle: undefined,
|
||||
count: 0
|
||||
});
|
||||
stdout("Starting interval-based db updates with delay of",interval.delay,"ms.");
|
||||
const doWork = async ()=>{
|
||||
const tm = new Date().getTime();
|
||||
@ -57,15 +69,6 @@ self.sqlite3InitModule().then(async function(sqlite3){
|
||||
interval.error = e;
|
||||
}
|
||||
};
|
||||
const finish = ()=>{
|
||||
db.close();
|
||||
if(interval.error){
|
||||
wPost('failed',"Ending work after interval #"+interval.count,
|
||||
"due to error:",interval.error);
|
||||
}else{
|
||||
wPost('finished',"Ending work after",interval.count,"intervals.");
|
||||
}
|
||||
};
|
||||
if(1){/*use setInterval()*/
|
||||
interval.handle = setInterval(async ()=>{
|
||||
await doWork();
|
||||
@ -89,7 +92,10 @@ self.sqlite3InitModule().then(async function(sqlite3){
|
||||
|
||||
self.onmessage = function({data}){
|
||||
switch(data.type){
|
||||
case 'run': run().catch((e)=>postErr(e.message));
|
||||
case 'run': run().catch((e)=>{
|
||||
if(!interval.error) interval.error = e;
|
||||
finish();
|
||||
});
|
||||
break;
|
||||
default:
|
||||
stderr("Unhandled message type '"+data.type+"'.");
|
||||
|
Reference in New Issue
Block a user