Skip to content

Commit

Permalink
thoughts on coordinating access to the db to preserve perf and concur…
Browse files Browse the repository at this point in the history
…rency
  • Loading branch information
tantaman committed Jun 20, 2023
1 parent 9218cbb commit f811dac
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 20 deletions.
2 changes: 1 addition & 1 deletion js/deps/wa-sqlite
Submodule wa-sqlite updated 0 files
2 changes: 1 addition & 1 deletion js/packages/sandbox/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</head>

<body>
<div id="root"></div>
<div id="kind"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

Expand Down
109 changes: 91 additions & 18 deletions js/packages/sandbox/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,96 @@
import initWasm from "@vlcn.io/crsqlite-wasm";
import wasmUrl from "@vlcn.io/crsqlite-wasm/crsqlite.wasm?url";
import workerUrl from "./service-worker.js?url";

const sqlite = await initWasm(() => wasmUrl);
const db = await sqlite.open(":memory:");
navigator.serviceWorker
.register(workerUrl, {
scope: "./src/",
})
.then((registration) => {
console.log("registration..");
let serviceWorker;
if (registration.installing) {
serviceWorker = registration.installing;
document.querySelector("#kind")!.textContent = "installing";
} else if (registration.waiting) {
serviceWorker = registration.waiting;
document.querySelector("#kind")!.textContent = "waiting";
} else if (registration.active) {
serviceWorker = registration.active;
document.querySelector("#kind")!.textContent = "active";
}
if (serviceWorker) {
console.log(serviceWorker.state);
serviceWorker.addEventListener("statechange", (e) => {
console.log(e);
// console.log(e.target?.state);
});
console.log("posting msg to service worker from main thread");
serviceWorker.postMessage("Hello");
}
})
.catch((error) => {
// Something went wrong during registration. The service-worker.js file
// might be unavailable or contain a syntax error.
});

await db.exec("CREATE TABLE foo (a primary key, b);");
const container = navigator.serviceWorker;

db.onUpdate(() => {
console.log("received update callback!");
});
container.onmessage = (msg) => {
console.log("Main thread got this from service worker:", msg);
};

try {
await db.tx(async (tx) => {
console.log("insert 1");
await tx.exec("INSERT INTO foo (1, 2);");
console.log("insert 2");
await tx.exec("INSERT INTO foo (2, 3);");
navigator.serviceWorker.ready.then((registration) => {
console.log("another attempt to post a message");
navigator.serviceWorker.controller!.postMessage({
type: "MESSAGE_IDENTIFIER",
});
} catch (e) {
console.log("wtf");
console.log(e);
}
});

/**
* Algorithm:
* 1. Each tab spawns a dedicated worker
* 2. Each dedicated worker waits for db open events
* 3. On open event, try to acquire a weblock for the given db name.
*
* We need some sort of central "opener" to coordinate message port passing.
* The DB could already be open in a different tab is the issue and we need to find it if so.
*
* We attempt to open the DB by:
* 1. Acquiring the weblock for it
* 2. Setting up a migration listener
* 3. Broadcasting a request for msg ports
*
* If we get the lock we notify ourselves thru the same listener.
*
* Main tab:
* 1. Create message channel
* 2. Start worker
* 3. Send worker other end of message channel
* 4. Worker:
* 1. Registers for DB Open event message against a broadcast channel
* 2. On receipt, tries to claim a weblock for that db name.
* 2. On DB Open:
* 1. Register a listener for DB open event with our worker
* 2. Broadcast event
*
* Workers all need to ping the service worker. Workers would pass around msg ports depending on lock acquisition.
* Well if worker dies msg ports die with it.
*
* ServiceWorker is the pub-sub channel.
*
* Tab tells ServiceWorker, SW tells workers when a new DB is requested. Worker that "gets that db" or "has that db" responds to SW.
* This response to SW includes a message port for each interested tab.
* SW sends these ports back out to all the registered tabs...
*
* Maybe need a bit more.
*
* Tab -ask for db-> SW -ask for db-> Workers
*
* Worker -has db-> SW -db avail, worker num-> Tabs
*
* Tab -msg port for db, worker num-> SW -msg port-> Worker
*
* SW isn't intended to be stateful.. but if it has message channels set up, does it ever die?
* It would seem it should not.
*
* If not, we can do something like: https://github.com/rhashimoto/wa-sqlite/blob/master/demo/SharedService/SharedService_SharedWorker.js
*/
49 changes: 49 additions & 0 deletions js/packages/sandbox/src/service-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// TODO: supposedly ServiceWorkers can be killed arbitrarily?
// If that is the case we would need to make this stateless by passing all needed information
// in messages.
/*
If we passed everything..
Tab asks SW for DB.
SW would need some state for a short duration to map back who requested the DB when getting a response from who
has the DB.
*/
const mapNameToProviderPort = new Map<string, MessagePort>();

onmessage = (event) => {
if (event.data.type === "worker_connect") {
onWorkerConnected(event.ports[0]);
}
};

function onWorkerConnected(workerPort: MessagePort) {
workerPort.onmessage = async (event) => {
if (event.ports.length) {
// Register new port provider.
const name = event.data;
const providerPort = event.ports[0];
providerPort.start();
mapNameToProviderPort.get(name)?.close();
mapNameToProviderPort.set(name, providerPort);

new BroadcastChannel("SharedService").postMessage(name);
} else {
// Handle port provider request.
const { name, lockId } = event.data;
const providerPort = mapNameToProviderPort.get(name);
if (providerPort) {
providerPort.addEventListener(
"message",
(event) => {
event.stopImmediatePropagation();
// @ts-ignore
workerPort.postMessage(null, event.ports);
},
{ once: true }
);
providerPort.postMessage(lockId);
}
}
};
}
3 changes: 3 additions & 0 deletions js/packages/sandbox/src/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
onmessage = (msg) => {
console.log("Worker got:", msg);
};

0 comments on commit f811dac

Please sign in to comment.