diff options
author | Heyang Zhou <zhy20000919@hotmail.com> | 2023-08-18 17:34:16 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-18 17:34:16 +0800 |
commit | c77c836a238ce683b21c12f88cd1101b930ce042 (patch) | |
tree | b69b2318f618d1b384dc168ab6652496d63e2f2c /cli | |
parent | b5839eefcf02e62e9e77e8095f372ac06a523cba (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.ts | 126 | ||||
-rw-r--r-- | cli/tsc/dts/lib.deno.unstable.d.ts | 32 |
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 |