dyedgreen / deno-sqlite

Deno SQLite module
https://deno.land/x/sqlite
MIT License
409 stars 36 forks source link

VFS raises error when requested to delete a non-existing file #253

Open rherrmann opened 9 months ago

rherrmann commented 9 months ago

In an attempt to test the behaviour of an SQL statement when run in parallel, I tried to execute the statement from multiple workers.

Here is a striped-down version of the code:

// main.ts
import { DB } from "https://deno.land/x/sqlite@v3.8/mod.ts";

const filename = "test.sqlite";
const db = new DB(filename);
db.execute("drop table if exists t; create table t (c text);");
const workers: Worker[] = [];
const workerPromises: Promise<void>[] = [];
for (let index = 0; index < 10; index++) {
  const worker = new Worker(
    new URL("./worker.ts", import.meta.url).href,
    { name: "worker " + index, type: "module" },
  );
  const deferred = Promise.withResolvers<void>();
  worker.onmessage = () => deferred.resolve();
  worker.onmessageerror = () => deferred.reject();
  workers.push(worker);
  workerPromises.push(deferred.promise);
}
workers.forEach((worker) => worker.postMessage({ filename }));
await Promise.all(workerPromises);
db.queryEntries("select * from t").forEach((row) => console.log(row));
db.close();
// worker.ts
/// <reference lib="webworker" />
import { DB } from "https://deno.land/x/sqlite@v3.8/mod.ts";

self.onmessage = (event) => {
  const { filename } = event.data;
  const db = new DB(filename, { mode: "write" });
  db.query("insert into t (c) values (:c)", { c: "inserted by " + self.name });
  db.close();
  self.postMessage({ done: true });
  self.close();
};

When the VFS is asked to delete a journal file, it raises this error:

error: Uncaught (in worker "worker 1") NotFound: No such file or directory (os error 2): remove 'test.sqlite-journal'
      Deno.removeSync(path);
           ^
    at Object.removeSync (ext:deno_fs/30_fs.js:209:7)
    at js_delete (https://deno.land/x/sqlite@v3.8/build/vfs.js:39:12)
    at <anonymous> (wasm://wasm/0028679a:1:5597)
    ...
error: Uncaught (in promise) Error: Unhandled error in child worker.
    at Worker.#pollControl (ext:runtime/11_workers.js:164:19)
    at eventLoopTick (ext:core/01_core.js:182:7)

After reading #240 and #249 I'm uncertain if the code should get thus far. However, the SQLite demo VFS seems to silently ignore request to delete a non-existing file and returns SQLITE_OK (search for demoDelete).

dyedgreen commented 9 months ago

Yes, I think this looks like an issue with file locking (I think the denk api is still unstable as well) but essentially it’s currently not really possible to detect locks held by the same deno process.

I feel like failing loudly when there is an unexpected problem like this seems better than to silently ignore the error though? (Eg in this instance you noticed there is an issue with concurrent access from the same process, which might not have been obvious if the error was swallowed.)