cloudflare / workerd

The JavaScript / Wasm runtime that powers Cloudflare Workers

Home Page:https://blog.cloudflare.com/workerd-open-source-workers-runtime/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

πŸ› Bug Report β€” SQLITE_MISUSE

clibequilibrium opened this issue Β· comments

Hello,

I am using KV to simulate local environment. My use case is simply writing keys with text and expiration of 1 hour. The bug happens after my key is expired and I am trying to read or write to KV again.

workerd/jsg/util.c++:281: error: e = workerd/util/sqlite.c++:845: failed: expected _ec != SQLITE_MISUSE [21 != 21]; SQLite misused: sqlite3_bind_text(statement, i+1, text.begin(), text.size(), SQLITE_STATIC) stack: 7ff7da4a253f 7ff7da88e78e 7ff7da88d996 7ff7da88d7ec 7ff7daaf5c79 7ff7daaf8fe1 7ff7daaf82e1 7ff7daaf8051 7ff7da56dfdd 7ff7da56dd12 7ff7da56dcb1 7ff7d95683fe 7ff7d95681b9 7ff7d9567fb5 7ff7db7c7139 7ff7db73092f 7ff7db73092f 7ff7db73092f 7ff7db73092f 7ff7db73092f 7ff7db72e69b 7ff7db72e29a 7ff7d9647054 7ff7d9646473 7ff7d951e087 7ff7da591bc4 7ff7da591859 7ff7da5917cc 7ff7daabbe28 7ff7daab9057 7ff7da8d9e6f 7ff7da8e896f; sentryErrorContext = jsgInternalError

The only way to fix that for me is to clean .wrangler\state folder

My package versions:
"wrangler": "^3.25.0",
"miniflare": "^3.20231218.4",

This is weird, that error message comes from code that is used to implement Durable Objects, but you say you see this while using Workers KV?

@mrbbot Does miniflare implement a Workers KV shim on top of DO?

This is weird, that error message comes from code that is used to implement Durable Objects, but you say you see this while using Workers KV?

@mrbbot Does miniflare implement a Workers KV shim on top of DO?

Odd ! If that helps I use miniflare with SvelteKit. I initialize Miniflare with those options and only use KV and D1.

	const mf = new Miniflare({
		kvNamespaces: ['KV'],
		kvPersist: '.wrangler/state/v3/kv',
		d1Databases: ['D1'],
		d1Persist: '.wrangler/state/v3/d1',
		modules: true,
		script: ''
	});
	env = await mf.getBindings();

I am certain that the issue is coming from miniflare-KVNamespaceObject , maybe I can investigate further if I understand how it works.

Does miniflare implement a Workers KV shim on top of DO?

Yep, https://github.com/cloudflare/workers-sdk/blob/321c7ed7355f64a22b0d26b2f097ba2e06e4b5e8/packages/miniflare/src/workers/kv/namespace.worker.ts

I'll try take a look at this and see if I can come up with a minimal reproduction. πŸ‘

Does miniflare implement a Workers KV shim on top of DO?

Yep, https://github.com/cloudflare/workers-sdk/blob/321c7ed7355f64a22b0d26b2f097ba2e06e4b5e8/packages/miniflare/src/workers/kv/namespace.worker.ts

I'll try take a look at this and see if I can come up with a minimal reproduction. πŸ‘

Something that is also odd, restarting PC also fixed my issue. If I have more info I will update this thread

Hey again @clibequilibrium! I was unable to reproduce this issue with the following script:

import assert from "node:assert";
import { setTimeout } from "node:timers/promises";
import { Miniflare } from "miniflare";

const mf = new Miniflare({
  kvNamespaces: ["NAMESPACE"],
  modules: true,
  script: `export default {
    async fetch(request, env, ctx) {
      if (request.method === "GET") {
        const value = await env.NAMESPACE.get("key");
        return new Response(value, { status: value === null ? 404 : 200 });
      } else if (request.method === "PUT") {
        await env.NAMESPACE.put("key", await request.text(), { expirationTtl: 60 });
        return new Response(null, { status: 204 });
      } else {
        return new Response(null, { status: 405 });
      }
    }
  }`,
});

async function wait(s) {
  for (let i = 0; i < s; i++) {
    await setTimeout(1000);
    process.stdout.write(`${i}/${s}\r`);
  }
  console.log();
}

try {
  // Write key
  let res = await mf.dispatchFetch("http://localhost", {
    method: "PUT",
    body: "one",
  });
  assert.strictEqual(res.status, 204, await res.text());

  // Check we can read it back
  res = await mf.dispatchFetch("http://localhost");
  assert.strictEqual(res.status, 200);
  assert.strictEqual(await res.text(), "one");

  // Wait for key to expire and internal Durable Object to be evicted
  await wait(90);

  // Check key has expired
  res = await mf.dispatchFetch("http://localhost");
  assert.strictEqual(res.status, 404);

  // Write another value
  res = await mf.dispatchFetch("http://localhost", {
    method: "PUT",
    body: "two",
  });
  assert.strictEqual(res.status, 204, await res.text());

  // Check we can read back the new value
  res = await mf.dispatchFetch("http://localhost");
  assert.strictEqual(res.status, 200);
  assert.strictEqual(await res.text(), "two");
} finally {
  await mf.dispose();
}

Would you be able to try running this in your project? If that doesn't reproduce the error, would you be able to put together a minimal reproduction?

Hey again @clibequilibrium! I was unable to reproduce this issue with the following script:

import assert from "node:assert";
import { setTimeout } from "node:timers/promises";
import { Miniflare } from "miniflare";

const mf = new Miniflare({
  kvNamespaces: ["NAMESPACE"],
  modules: true,
  script: `export default {
    async fetch(request, env, ctx) {
      if (request.method === "GET") {
        const value = await env.NAMESPACE.get("key");
        return new Response(value, { status: value === null ? 404 : 200 });
      } else if (request.method === "PUT") {
        await env.NAMESPACE.put("key", await request.text(), { expirationTtl: 60 });
        return new Response(null, { status: 204 });
      } else {
        return new Response(null, { status: 405 });
      }
    }
  }`,
});

async function wait(s) {
  for (let i = 0; i < s; i++) {
    await setTimeout(1000);
    process.stdout.write(`${i}/${s}\r`);
  }
  console.log();
}

try {
  // Write key
  let res = await mf.dispatchFetch("http://localhost", {
    method: "PUT",
    body: "one",
  });
  assert.strictEqual(res.status, 204, await res.text());

  // Check we can read it back
  res = await mf.dispatchFetch("http://localhost");
  assert.strictEqual(res.status, 200);
  assert.strictEqual(await res.text(), "one");

  // Wait for key to expire and internal Durable Object to be evicted
  await wait(90);

  // Check key has expired
  res = await mf.dispatchFetch("http://localhost");
  assert.strictEqual(res.status, 404);

  // Write another value
  res = await mf.dispatchFetch("http://localhost", {
    method: "PUT",
    body: "two",
  });
  assert.strictEqual(res.status, 204, await res.text());

  // Check we can read back the new value
  res = await mf.dispatchFetch("http://localhost");
  assert.strictEqual(res.status, 200);
  assert.strictEqual(await res.text(), "two");
} finally {
  await mf.dispose();
}

Would you be able to try running this in your project? If that doesn't reproduce the error, would you be able to put together a minimal reproduction?

Hi. I tried my best but it's very inconsistent (does not always get reproduced on PC restart). Please see this repro and readme. https://github.com/clibequilibrium/cf-kv-repro
What also happens sometimes is when I frequently hit Ctrl + C to quit pnpm run dev, Is it possible that there is I/O lock or some sort of race condition that corrupts KV .sqlite file? For me it mostly happens next time when I restart my PC . It seems that shutting my PC off while the server is running might be causing corruption.

Hi, more info with log: new Log(LogLevel.VERBOSE)

[mf:err] Error: attempt to write a readonly database
    at [object Object]
    at Object.deleteByKey (file:///F:/Documents/redacted/redacted/sites/redacted/node_modules/.pnpm/miniflare@3.20231218.4/node_modules/miniflare/src/workers/shared/sql.worker.ts:84:11)
    at KeyValueStorage.get (file:///F:/Documents/redacted/redacted/sites/redacted/node_modules/.pnpm/miniflare@3.20231218.4/node_modules/miniflare/src/workers/shared/keyvalue.worker.ts:163:22)  
    at KVNamespaceObject.get (file:///F:/Documents/redacted/redacted/sites/redacted/node_modules/.pnpm/miniflare@3.20231218.4/node_modules/miniflare/src/workers/kv/namespace.worker.ts:93:36)    
    at KVNamespaceObject.fetch (file:///F:/Documents/redacted/redacted/sites/redacted/node_modules/.pnpm/miniflare@3.20231218.4/node_modules/miniflare/src/workers/shared/router.worker.ts:45:50) 
    at KVNamespaceObject.fetch (file:///F:/Documents/redacted/redacted/sites/redacted/node_modules/.pnpm/miniflare@3.20231218.4/node_modules/miniflare/src/workers/shared/object.worker.ts:141:23)

EDIT: same happens to D1

D1_ERROR: attempt to write a readonly database

Okay I cannot reproduce the issue any longer on my side after upgrading to latest miniflare and wrangler. I have also added my project folder to my antivirus extensions.

EDIT: closing