summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorHeyang Zhou <zhy20000919@hotmail.com>2023-08-18 17:34:16 +0800
committerGitHub <noreply@github.com>2023-08-18 17:34:16 +0800
commitc77c836a238ce683b21c12f88cd1101b930ce042 (patch)
treeb69b2318f618d1b384dc168ab6652496d63e2f2c /cli
parentb5839eefcf02e62e9e77e8095f372ac06a523cba (diff)
feat(ext/kv): key expiration (#20091)
Co-authored-by: Luca Casonato <hello@lcas.dev>
Diffstat (limited to 'cli')
-rw-r--r--cli/tests/unit/kv_test.ts126
-rw-r--r--cli/tsc/dts/lib.deno.unstable.d.ts32
2 files changed, 154 insertions, 4 deletions
diff --git a/cli/tests/unit/kv_test.ts b/cli/tests/unit/kv_test.ts
index 74a8ed6b3..438ebd7ee 100644
--- a/cli/tests/unit/kv_test.ts
+++ b/cli/tests/unit/kv_test.ts
@@ -1806,3 +1806,129 @@ Deno.test({
}
},
});
+
+Deno.test({
+ name: "kv expiration",
+ async fn() {
+ const filename = await Deno.makeTempFile({ prefix: "kv_expiration_db" });
+ try {
+ await Deno.remove(filename);
+ } catch {
+ // pass
+ }
+ let db: Deno.Kv | null = null;
+
+ try {
+ db = await Deno.openKv(filename);
+
+ await db.set(["a"], 1, { expireIn: 1000 });
+ await db.set(["b"], 2, { expireIn: 1000 });
+ assertEquals((await db.get(["a"])).value, 1);
+ assertEquals((await db.get(["b"])).value, 2);
+
+ // Value overwrite should also reset expiration
+ await db.set(["b"], 2, { expireIn: 3600 * 1000 });
+
+ // Wait for expiration
+ await sleep(1000);
+
+ // Re-open to trigger immediate cleanup
+ db.close();
+ db = null;
+ db = await Deno.openKv(filename);
+
+ let ok = false;
+ for (let i = 0; i < 50; i++) {
+ await sleep(100);
+ if (
+ JSON.stringify(
+ (await db.getMany([["a"], ["b"]])).map((x) => x.value),
+ ) === "[null,2]"
+ ) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ throw new Error("Values did not expire");
+ }
+ } finally {
+ if (db) {
+ try {
+ db.close();
+ } catch {
+ // pass
+ }
+ }
+ try {
+ await Deno.remove(filename);
+ } catch {
+ // pass
+ }
+ }
+ },
+});
+
+Deno.test({
+ name: "kv expiration with atomic",
+ async fn() {
+ const filename = await Deno.makeTempFile({ prefix: "kv_expiration_db" });
+ try {
+ await Deno.remove(filename);
+ } catch {
+ // pass
+ }
+ let db: Deno.Kv | null = null;
+
+ try {
+ db = await Deno.openKv(filename);
+
+ await db.atomic().set(["a"], 1, { expireIn: 1000 }).set(["b"], 2, {
+ expireIn: 1000,
+ }).commit();
+ assertEquals((await db.getMany([["a"], ["b"]])).map((x) => x.value), [
+ 1,
+ 2,
+ ]);
+
+ // Wait for expiration
+ await sleep(1000);
+
+ // Re-open to trigger immediate cleanup
+ db.close();
+ db = null;
+ db = await Deno.openKv(filename);
+
+ let ok = false;
+ for (let i = 0; i < 50; i++) {
+ await sleep(100);
+ if (
+ JSON.stringify(
+ (await db.getMany([["a"], ["b"]])).map((x) => x.value),
+ ) === "[null,null]"
+ ) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ throw new Error("Values did not expire");
+ }
+ } finally {
+ if (db) {
+ try {
+ db.close();
+ } catch {
+ // pass
+ }
+ }
+ try {
+ await Deno.remove(filename);
+ } catch {
+ // pass
+ }
+ }
+ },
+});
diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts
index eb612a655..70b346a2a 100644
--- a/cli/tsc/dts/lib.deno.unstable.d.ts
+++ b/cli/tsc/dts/lib.deno.unstable.d.ts
@@ -1357,7 +1357,13 @@ declare namespace Deno {
* mutation is applied to the key.
*
* - `set` - Sets the value of the key to the given value, overwriting any
- * existing value.
+ * existing value. Optionally an `expireIn` option can be specified to
+ * set a time-to-live (TTL) for the key. The TTL is specified in
+ * milliseconds, and the key will be deleted from the database at earliest
+ * after the specified number of milliseconds have elapsed. Once the
+ * specified duration has passed, the key may still be visible for some
+ * additional time. If the `expireIn` option is not specified, the key will
+ * not expire.
* - `delete` - Deletes the key from the database. The mutation is a no-op if
* the key does not exist.
* - `sum` - Adds the given value to the existing value of the key. Both the
@@ -1379,7 +1385,7 @@ declare namespace Deno {
export type KvMutation =
& { key: KvKey }
& (
- | { type: "set"; value: unknown }
+ | { type: "set"; value: unknown; expireIn?: number }
| { type: "delete" }
| { type: "sum"; value: KvU64 }
| { type: "max"; value: KvU64 }
@@ -1591,8 +1597,15 @@ declare namespace Deno {
/**
* Add to the operation a mutation that sets the value of the specified key
* to the specified value if all checks pass during the commit.
+ *
+ * Optionally an `expireIn` option can be specified to set a time-to-live
+ * (TTL) for the key. The TTL is specified in milliseconds, and the key will
+ * be deleted from the database at earliest after the specified number of
+ * milliseconds have elapsed. Once the specified duration has passed, the
+ * key may still be visible for some additional time. If the `expireIn`
+ * option is not specified, the key will not expire.
*/
- set(key: KvKey, value: unknown): this;
+ set(key: KvKey, value: unknown, options?: { expireIn?: number }): this;
/**
* Add to the operation a mutation that deletes the specified key if all
* checks pass during the commit.
@@ -1721,8 +1734,19 @@ declare namespace Deno {
* const db = await Deno.openKv();
* await db.set(["foo"], "bar");
* ```
+ *
+ * Optionally an `expireIn` option can be specified to set a time-to-live
+ * (TTL) for the key. The TTL is specified in milliseconds, and the key will
+ * be deleted from the database at earliest after the specified number of
+ * milliseconds have elapsed. Once the specified duration has passed, the
+ * key may still be visible for some additional time. If the `expireIn`
+ * option is not specified, the key will not expire.
*/
- set(key: KvKey, value: unknown): Promise<KvCommitResult>;
+ set(
+ key: KvKey,
+ value: unknown,
+ options?: { expireIn?: number },
+ ): Promise<KvCommitResult>;
/**
* Delete the value for the given key from the database. If no value exists