deno icon indicating copy to clipboard operation
deno copied to clipboard

[Deno KV] In-memory backend

Open rracariu opened this issue 1 year ago • 5 comments

ATM the local KV instances are always backed by an SQLite database.

There would be at least two use-cases where having a memory only backend would be interesting:

  • Memcached/Redis alternative, this has the advantage that you get no dependencies and possible extra performance from the in-process memory. Plus, you get a uniform API for working with ephemeral or persistent storages.
  • An interesting and possible more performant way to implement Web worker concurrency, this would be implemented with the semantics of the KV atomic operations.

rracariu avatar Nov 29 '23 17:11 rracariu

Deno.openKv(":memory:"); :)

lucacasonato avatar Nov 29 '23 19:11 lucacasonato

Deno.openKv(":memory:"); :)

Interesting, never though you would pass that straight to sqlite 👍🏻

rracariu avatar Nov 29 '23 20:11 rracariu

Looks that you can do this shared in memory db and pass data from/to workers

if (!import.meta.url.endsWith("#worker")) {
  // Shared cache
  const kv = await Deno.openKv("file::memory:?cache=shared");
  await kv.set(["foo"], "bar");

  // Start worker
  const worker = new Worker(import.meta.url + "#worker", {
    type: "module",
  });

  // Listen to messages enqueued by worker
  kv.listenQueue((msg: unknown) => {
    console.log(msg);
  });
} else {
  // Shared cache
  const kv = await Deno.openKv("file::memory:?cache=shared");

  // Read from shared cache
  console.log(await kv.get(["foo"]));

  let count = 0;

  setInterval(async () => {
    await kv.enqueue({ msg: "Hello from worker", count: ++count });
  }, 1000);
}

rracariu avatar Nov 29 '23 20:11 rracariu

Stressing this a bit doesn't go that well. Just changing to:

setInterval(async () => { await kv.enqueue({ msg: "Hello from worker", count: ++count }); }, 5);

will crash Deno

Platform: macos aarch64
Version: 1.38.3
Args: ["deno", "run", "-A", "--unstable", "worker.ts"]

thread '<unnamed>' panicked at /Users/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/denokv_sqlite-0.2.1/lib.rs:215:15:
KV queue dequeue failed: database table is locked
stack backtrace:
  0:        0x1010d3f98 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h4bff038afc05d0ab
  1:        0x10053caec - core::fmt::write::he9fa43146d420946
  2:        0x1010a8824 - std::io::Write::write_fmt::hf4fd30c8192b15d9
  3:        0x1010d86c4 - std::sys_common::backtrace::print::h5d71f7b09493fc9b
  4:        0x1010d831c - std::panicking::default_hook::{{closure}}::hb75620628e95e648
  5:        0x1010d7f88 - std::panicking::default_hook::h4ac4acc64d21c4ad
  6:        0x10040ea54 - deno::setup_panic_hook::{{closure}}::h6bc9ac56a78a26f4
  7:        0x1010d8fb0 - std::panicking::rust_panic_with_hook::h81dc715fa5de32cf
  8:        0x1010d8d88 - std::panicking::begin_panic_handler::{{closure}}::h26c73eb75670321f
  9:        0x1010d8cf4 - std::sys_common::backtrace::__rust_end_short_backtrace::hef4773cf0f80485b
 10:        0x1010d8ce8 - _rust_begin_unwind
 11:        0x10053b004 - core::panicking::panic_fmt::h10bd3bfe5bb30d67
 12:        0x100ea67b8 - denokv_sqlite::sqlite_thread::{{closure}}::hb00c634db1831b2c
 13:        0x100ea2438 - std::sys_common::backtrace::__rust_begin_short_backtrace::hf24b61066522010f
 14:        0x100eac85c - core::ops::function::FnOnce::call_once{{vtable.shim}}::h69fc3aa8957ff612
 15:        0x1010dc100 - std::sys::unix::thread::Thread::new::thread_start::h616a123cfe4994dc
 16:        0x18827d034 - __pthread_joiner_wake

rracariu avatar Nov 29 '23 21:11 rracariu

SQLite only supports one writer at a time per database file. If many threads and/or processes write the database at the same instant, will cause database table is locked!

drag0n-app avatar Feb 03 '24 01:02 drag0n-app