importScripts(
(new URL(self.location.href).searchParams).get('sqlite3.dir') + '/sqlite3.js'
);
self.sqlite3InitModule().then(async function(sqlite3){
const urlArgs = new URL(self.location.href).searchParams;
const options = {
workerName: urlArgs.get('workerId') || Math.round(Math.random()*10000),
unlockAsap: urlArgs.get('opfs-unlock-asap') || 0
};
const wPost = (type,...payload)=>{
postMessage({type, worker: options.workerName, payload});
};
const stdout = (...args)=>wPost('stdout',...args);
const stderr = (...args)=>wPost('stderr',...args);
if(!sqlite3.opfs){
stderr("OPFS support not detected. Aborting.");
return;
}
const wait = async (ms)=>{
return new Promise((resolve)=>setTimeout(resolve,ms));
};
const dbName = 'concurrency-tester.db';
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(){
db = new sqlite3.oo1.OpfsDb({
filename: 'file:'+dbName+'?opfs-unlock-asap='+options.unlockAsap,
flags: '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);",
"create table if not exists t2(w TEXT UNIQUE ON CONFLICT REPLACE,v);"
]);
});
const maxIterations =
urlArgs.has('iterations') ? (+urlArgs.get('iterations') || 10) : 10;
stdout("Starting interval-based db updates with delay of",interval.delay,"ms.");
const doWork = async ()=>{
const tm = new Date().getTime();
++interval.count;
const prefix = "v(#"+interval.count+")";
stdout("Setting",prefix,"=",tm);
try{
db.exec({
sql:"INSERT OR REPLACE INTO t1(w,v) VALUES(?,?)",
bind: [options.workerName, new Date().getTime()]
});
}catch(e){
interval.error = e;
}
};
if(1){
setTimeout(async function timer(){
await doWork();
if(interval.error || maxIterations === interval.count){
finish();
}else{
setTimeout(timer, interval.delay);
}
}, interval.delay);
}else{
let i;
for(i = 0; i < maxIterations; ++i){
await doWork();
if(interval.error) break;
await wait(interval.ms);
}
finish();
}
};
self.onmessage = function({data}){
switch(data.type){
case 'run': run().catch((e)=>{
if(!interval.error) interval.error = e;
finish();
});
break;
default:
stderr("Unhandled message type '"+data.type+"'.");
break;
}
};
});