1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

feat(ext/kv): return versionstamp from set/commit (#18512)

This commit updates the `Deno.Kv` API to return the new commited
versionstamp for the mutated data from `db.set` and `ao.commit`. This is
returned in the form of a `Deno.KvCommitResult` object that has a
`versionstamp` property.
This commit is contained in:
Luca Casonato 2023-03-30 20:57:21 +02:00 committed by GitHub
parent 206c593519
commit e888c3f534
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 137 additions and 112 deletions

View file

@ -64,7 +64,8 @@ dbTest("basic read-write-delete and versionstamps", async (db) => {
assertEquals(result1.value, null); assertEquals(result1.value, null);
assertEquals(result1.versionstamp, null); assertEquals(result1.versionstamp, null);
await db.set(["a"], "b"); const setRes = await db.set(["a"], "b");
assertEquals(setRes.versionstamp, "00000000000000010000");
const result2 = await db.get(["a"]); const result2 = await db.get(["a"]);
assertEquals(result2.key, ["a"]); assertEquals(result2.key, ["a"]);
assertEquals(result2.value, "b"); assertEquals(result2.value, "b");
@ -177,21 +178,22 @@ dbTest("compare and mutate", async (db) => {
const currentValue = await db.get(["t"]); const currentValue = await db.get(["t"]);
assertEquals(currentValue.versionstamp, "00000000000000010000"); assertEquals(currentValue.versionstamp, "00000000000000010000");
let ok = await db.atomic() let res = await db.atomic()
.check({ key: ["t"], versionstamp: currentValue.versionstamp }) .check({ key: ["t"], versionstamp: currentValue.versionstamp })
.set(currentValue.key, "2") .set(currentValue.key, "2")
.commit(); .commit();
assertEquals(ok, true); assert(res);
assertEquals(res.versionstamp, "00000000000000020000");
const newValue = await db.get(["t"]); const newValue = await db.get(["t"]);
assertEquals(newValue.versionstamp, "00000000000000020000"); assertEquals(newValue.versionstamp, "00000000000000020000");
assertEquals(newValue.value, "2"); assertEquals(newValue.value, "2");
ok = await db.atomic() res = await db.atomic()
.check({ key: ["t"], versionstamp: currentValue.versionstamp }) .check({ key: ["t"], versionstamp: currentValue.versionstamp })
.set(currentValue.key, "3") .set(currentValue.key, "3")
.commit(); .commit();
assertEquals(ok, false); assertEquals(res, null);
const newValue2 = await db.get(["t"]); const newValue2 = await db.get(["t"]);
assertEquals(newValue2.versionstamp, "00000000000000020000"); assertEquals(newValue2.versionstamp, "00000000000000020000");
@ -199,21 +201,21 @@ dbTest("compare and mutate", async (db) => {
}); });
dbTest("compare and mutate not exists", async (db) => { dbTest("compare and mutate not exists", async (db) => {
let ok = await db.atomic() let res = await db.atomic()
.check({ key: ["t"], versionstamp: null }) .check({ key: ["t"], versionstamp: null })
.set(["t"], "1") .set(["t"], "1")
.commit(); .commit();
assertEquals(ok, true); assert(res);
const newValue = await db.get(["t"]); const newValue = await db.get(["t"]);
assertEquals(newValue.versionstamp, "00000000000000010000"); assertEquals(newValue.versionstamp, "00000000000000010000");
assertEquals(newValue.value, "1"); assertEquals(newValue.value, "1");
ok = await db.atomic() res = await db.atomic()
.check({ key: ["t"], versionstamp: null }) .check({ key: ["t"], versionstamp: null })
.set(["t"], "2") .set(["t"], "2")
.commit(); .commit();
assertEquals(ok, false); assertEquals(res, null);
}); });
dbTest("compare multiple and mutate", async (db) => { dbTest("compare multiple and mutate", async (db) => {
@ -225,13 +227,13 @@ dbTest("compare multiple and mutate", async (db) => {
const currentValue2 = await db.get(["t2"]); const currentValue2 = await db.get(["t2"]);
assertEquals(currentValue2.versionstamp, "00000000000000020000"); assertEquals(currentValue2.versionstamp, "00000000000000020000");
const ok = await db.atomic() const res = await db.atomic()
.check({ key: ["t1"], versionstamp: currentValue1.versionstamp }) .check({ key: ["t1"], versionstamp: currentValue1.versionstamp })
.check({ key: ["t2"], versionstamp: currentValue2.versionstamp }) .check({ key: ["t2"], versionstamp: currentValue2.versionstamp })
.set(currentValue1.key, "3") .set(currentValue1.key, "3")
.set(currentValue2.key, "4") .set(currentValue2.key, "4")
.commit(); .commit();
assertEquals(ok, true); assert(res);
const newValue1 = await db.get(["t1"]); const newValue1 = await db.get(["t1"]);
assertEquals(newValue1.versionstamp, "00000000000000030000"); assertEquals(newValue1.versionstamp, "00000000000000030000");
@ -241,13 +243,13 @@ dbTest("compare multiple and mutate", async (db) => {
assertEquals(newValue2.value, "4"); assertEquals(newValue2.value, "4");
// just one of the two checks failed // just one of the two checks failed
const ok2 = await db.atomic() const res2 = await db.atomic()
.check({ key: ["t1"], versionstamp: newValue1.versionstamp }) .check({ key: ["t1"], versionstamp: newValue1.versionstamp })
.check({ key: ["t2"], versionstamp: null }) .check({ key: ["t2"], versionstamp: null })
.set(newValue1.key, "5") .set(newValue1.key, "5")
.set(newValue2.key, "6") .set(newValue2.key, "6")
.commit(); .commit();
assertEquals(ok2, false); assertEquals(res2, null);
const newValue3 = await db.get(["t1"]); const newValue3 = await db.get(["t1"]);
assertEquals(newValue3.versionstamp, "00000000000000030000"); assertEquals(newValue3.versionstamp, "00000000000000030000");
@ -259,79 +261,79 @@ dbTest("compare multiple and mutate", async (db) => {
dbTest("atomic mutation ordering (set before delete)", async (db) => { dbTest("atomic mutation ordering (set before delete)", async (db) => {
await db.set(["a"], "1"); await db.set(["a"], "1");
const ok1 = await db.atomic() const res = await db.atomic()
.set(["a"], "2") .set(["a"], "2")
.delete(["a"]) .delete(["a"])
.commit(); .commit();
assert(ok1); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, null); assertEquals(result.value, null);
}); });
dbTest("atomic mutation ordering (delete before set)", async (db) => { dbTest("atomic mutation ordering (delete before set)", async (db) => {
await db.set(["a"], "1"); await db.set(["a"], "1");
const ok1 = await db.atomic() const res = await db.atomic()
.delete(["a"]) .delete(["a"])
.set(["a"], "2") .set(["a"], "2")
.commit(); .commit();
assert(ok1); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, "2"); assertEquals(result.value, "2");
}); });
dbTest("atomic mutation type=set", async (db) => { dbTest("atomic mutation type=set", async (db) => {
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: "1", type: "set" }) .mutate({ key: ["a"], value: "1", type: "set" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, "1"); assertEquals(result.value, "1");
}); });
dbTest("atomic mutation type=set overwrite", async (db) => { dbTest("atomic mutation type=set overwrite", async (db) => {
await db.set(["a"], "1"); await db.set(["a"], "1");
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: "2", type: "set" }) .mutate({ key: ["a"], value: "2", type: "set" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, "2"); assertEquals(result.value, "2");
}); });
dbTest("atomic mutation type=delete", async (db) => { dbTest("atomic mutation type=delete", async (db) => {
await db.set(["a"], "1"); await db.set(["a"], "1");
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], type: "delete" }) .mutate({ key: ["a"], type: "delete" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, null); assertEquals(result.value, null);
}); });
dbTest("atomic mutation type=delete no exists", async (db) => { dbTest("atomic mutation type=delete no exists", async (db) => {
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], type: "delete" }) .mutate({ key: ["a"], type: "delete" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, null); assertEquals(result.value, null);
}); });
dbTest("atomic mutation type=sum", async (db) => { dbTest("atomic mutation type=sum", async (db) => {
await db.set(["a"], new Deno.KvU64(10n)); await db.set(["a"], new Deno.KvU64(10n));
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "sum" }) .mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "sum" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(11n)); assertEquals(result.value, new Deno.KvU64(11n));
}); });
dbTest("atomic mutation type=sum no exists", async (db) => { dbTest("atomic mutation type=sum no exists", async (db) => {
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "sum" }) .mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "sum" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assert(result.value); assert(result.value);
assertEquals(result.value, new Deno.KvU64(1n)); assertEquals(result.value, new Deno.KvU64(1n));
@ -339,21 +341,21 @@ dbTest("atomic mutation type=sum no exists", async (db) => {
dbTest("atomic mutation type=sum wrap around", async (db) => { dbTest("atomic mutation type=sum wrap around", async (db) => {
await db.set(["a"], new Deno.KvU64(0xffffffffffffffffn)); await db.set(["a"], new Deno.KvU64(0xffffffffffffffffn));
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(10n), type: "sum" }) .mutate({ key: ["a"], value: new Deno.KvU64(10n), type: "sum" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(9n)); assertEquals(result.value, new Deno.KvU64(9n));
const ok2 = await db.atomic() const res2 = await db.atomic()
.mutate({ .mutate({
key: ["a"], key: ["a"],
value: new Deno.KvU64(0xffffffffffffffffn), value: new Deno.KvU64(0xffffffffffffffffn),
type: "sum", type: "sum",
}) })
.commit(); .commit();
assert(ok2); assert(res2);
const result2 = await db.get(["a"]); const result2 = await db.get(["a"]);
assertEquals(result2.value, new Deno.KvU64(8n)); assertEquals(result2.value, new Deno.KvU64(8n));
}); });
@ -387,26 +389,26 @@ dbTest("atomic mutation type=sum wrong type in mutation", async (db) => {
dbTest("atomic mutation type=min", async (db) => { dbTest("atomic mutation type=min", async (db) => {
await db.set(["a"], new Deno.KvU64(10n)); await db.set(["a"], new Deno.KvU64(10n));
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(5n), type: "min" }) .mutate({ key: ["a"], value: new Deno.KvU64(5n), type: "min" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(5n)); assertEquals(result.value, new Deno.KvU64(5n));
const ok2 = await db.atomic() const res2 = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(15n), type: "min" }) .mutate({ key: ["a"], value: new Deno.KvU64(15n), type: "min" })
.commit(); .commit();
assert(ok2); assert(res2);
const result2 = await db.get(["a"]); const result2 = await db.get(["a"]);
assertEquals(result2.value, new Deno.KvU64(5n)); assertEquals(result2.value, new Deno.KvU64(5n));
}); });
dbTest("atomic mutation type=min no exists", async (db) => { dbTest("atomic mutation type=min no exists", async (db) => {
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "min" }) .mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "min" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assert(result.value); assert(result.value);
assertEquals(result.value, new Deno.KvU64(1n)); assertEquals(result.value, new Deno.KvU64(1n));
@ -441,26 +443,26 @@ dbTest("atomic mutation type=min wrong type in mutation", async (db) => {
dbTest("atomic mutation type=max", async (db) => { dbTest("atomic mutation type=max", async (db) => {
await db.set(["a"], new Deno.KvU64(10n)); await db.set(["a"], new Deno.KvU64(10n));
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(5n), type: "max" }) .mutate({ key: ["a"], value: new Deno.KvU64(5n), type: "max" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(10n)); assertEquals(result.value, new Deno.KvU64(10n));
const ok2 = await db.atomic() const res2 = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(15n), type: "max" }) .mutate({ key: ["a"], value: new Deno.KvU64(15n), type: "max" })
.commit(); .commit();
assert(ok2); assert(res2);
const result2 = await db.get(["a"]); const result2 = await db.get(["a"]);
assertEquals(result2.value, new Deno.KvU64(15n)); assertEquals(result2.value, new Deno.KvU64(15n));
}); });
dbTest("atomic mutation type=max no exists", async (db) => { dbTest("atomic mutation type=max no exists", async (db) => {
const ok = await db.atomic() const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "max" }) .mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "max" })
.commit(); .commit();
assert(ok); assert(res);
const result = await db.get(["a"]); const result = await db.get(["a"]);
assert(result.value); assert(result.value);
assertEquals(result.value, new Deno.KvU64(1n)); assertEquals(result.value, new Deno.KvU64(1n));
@ -1059,7 +1061,8 @@ dbTest("operation size limit", async (db) => {
i, i,
) => ["a", i]); ) => ["a", i]);
assertEquals((await db.getMany(lastValidKeys)).length, 10); const res = await db.getMany(lastValidKeys);
assertEquals(res.length, 10);
await assertRejects( await assertRejects(
async () => await db.getMany(firstInvalidKeys), async () => await db.getMany(firstInvalidKeys),
@ -1067,72 +1070,66 @@ dbTest("operation size limit", async (db) => {
"too many ranges (max 10)", "too many ranges (max 10)",
); );
assertEquals( const res2 = await collect(db.list({ prefix: ["a"] }, { batchSize: 1000 }));
(await collect(db.list({ assertEquals(res2.length, 0);
prefix: ["a"],
}, {
batchSize: 1000,
}))).length,
0,
);
assertRejects( assertRejects(
async () => async () => await collect(db.list({ prefix: ["a"] }, { batchSize: 1001 })),
await collect(db.list({
prefix: ["a"],
}, {
batchSize: 1001,
})),
TypeError, TypeError,
"too many entries (max 1000)", "too many entries (max 1000)",
); );
// when batchSize is not specified, limit is used but is clamped to 500 // when batchSize is not specified, limit is used but is clamped to 500
assertEquals( assertEquals(
(await collect(db.list({ (await collect(db.list({ prefix: ["a"] }, { limit: 1001 }))).length,
prefix: ["a"],
}, {
limit: 1001,
}))).length,
0, 0,
); );
assertEquals( const res3 = await db.atomic()
await db.atomic().check(...lastValidKeys.map((key) => ({ .check(...lastValidKeys.map((key) => ({
key, key,
versionstamp: null, versionstamp: null,
}))).mutate(...lastValidKeys.map((key) => ({ })))
.mutate(...lastValidKeys.map((key) => ({
key, key,
type: "set", type: "set",
value: 1, value: 1,
} satisfies Deno.KvMutation))).commit(), } satisfies Deno.KvMutation)))
true, .commit();
); assert(res3);
await assertRejects( await assertRejects(
async () => async () => {
await db.atomic().check(...firstInvalidKeys.map((key) => ({ await db.atomic()
.check(...firstInvalidKeys.map((key) => ({
key, key,
versionstamp: null, versionstamp: null,
}))).mutate(...lastValidKeys.map((key) => ({ })))
.mutate(...lastValidKeys.map((key) => ({
key, key,
type: "set", type: "set",
value: 1, value: 1,
} satisfies Deno.KvMutation))).commit(), } satisfies Deno.KvMutation)))
.commit();
},
TypeError, TypeError,
"too many checks (max 10)", "too many checks (max 10)",
); );
await assertRejects( await assertRejects(
async () => async () => {
await db.atomic().check(...lastValidKeys.map((key) => ({ await db.atomic()
.check(...lastValidKeys.map((key) => ({
key, key,
versionstamp: null, versionstamp: null,
}))).mutate(...firstInvalidKeys.map((key) => ({ })))
.mutate(...firstInvalidKeys.map((key) => ({
key, key,
type: "set", type: "set",
value: 1, value: 1,
} satisfies Deno.KvMutation))).commit(), } satisfies Deno.KvMutation)))
.commit();
},
TypeError, TypeError,
"too many mutations (max 10)", "too many mutations (max 10)",
); );

View file

@ -1748,6 +1748,11 @@ declare namespace Deno {
batchSize?: number; batchSize?: number;
} }
export interface KvCommitResult {
/** The versionstamp of the value committed to KV. */
versionstamp: string;
}
/** **UNSTABLE**: New API, yet to be vetted. /** **UNSTABLE**: New API, yet to be vetted.
* *
* A check to perform as part of a {@linkcode Deno.AtomicOperation}. The check * A check to perform as part of a {@linkcode Deno.AtomicOperation}. The check
@ -1787,11 +1792,13 @@ declare namespace Deno {
* performed and the operation will fail. One can then retry the read-modify- * performed and the operation will fail. One can then retry the read-modify-
* write operation in a loop until it succeeds. * write operation in a loop until it succeeds.
* *
* The `commit` method of an atomic operation returns a boolean indicating * The `commit` method of an atomic operation returns a value indicating
* whether checks passed and mutations were performed. If the operation failed * whether checks passed and mutations were performed. If the operation failed
* because of a failed check, the return value will be `false`. If the * because of a failed check, the return value will be `null`. If the
* operation failed for any other reason (storage error, invalid value, etc.), * operation failed for any other reason (storage error, invalid value, etc.),
* an exception will be thrown. * an exception will be thrown. If the operation succeeded, the return value
* will be a {@linkcode Deno.KvCommitResult} object containing the
* versionstamp of the value committed to KV.
* *
* @category KV * @category KV
*/ */
@ -1821,17 +1828,19 @@ declare namespace Deno {
*/ */
delete(key: KvKey): this; delete(key: KvKey): this;
/** /**
* Commit the operation to the KV store. Returns a boolean indicating * Commit the operation to the KV store. Returns a value indicating whether
* whether checks passed and mutations were performed. If the operation * checks passed and mutations were performed. If the operation failed
* failed because of a failed check, the return value will be `false`. If * because of a failed check, the return value will be `null`. If the
* the operation failed for any other reason (storage error, invalid value, * operation failed for any other reason (storage error, invalid value,
* etc.), an exception will be thrown. * etc.), an exception will be thrown. If the operation succeeded, the
* return value will be a {@linkcode Deno.KvCommitResult} object containing
* the versionstamp of the value committed to KV.
* *
* If the commit returns `false`, one may create a new atomic operation with * If the commit returns `null`, one may create a new atomic operation with
* updated checks and mutations and attempt to commit it again. See the note * updated checks and mutations and attempt to commit it again. See the note
* on optimistic locking in the documentation for {@linkcode Deno.AtomicOperation}. * on optimistic locking in the documentation for {@linkcode Deno.AtomicOperation}.
*/ */
commit(): Promise<boolean>; commit(): Promise<KvCommitResult | null>;
} }
/** **UNSTABLE**: New API, yet to be vetted. /** **UNSTABLE**: New API, yet to be vetted.
@ -1932,7 +1941,7 @@ declare namespace Deno {
* await db.set(["foo"], "bar"); * await db.set(["foo"], "bar");
* ``` * ```
*/ */
set(key: KvKey, value: unknown): Promise<void>; set(key: KvKey, value: unknown): Promise<KvCommitResult>;
/** /**
* Delete the value for the given key from the database. If no value exists * Delete the value for the given key from the database. If no value exists

View file

@ -111,14 +111,15 @@ class Kv {
[key, "set", value], [key, "set", value],
]; ];
const result = await core.opAsync( const versionstamp = await core.opAsync(
"op_kv_atomic_write", "op_kv_atomic_write",
this.#rid, this.#rid,
checks, checks,
mutations, mutations,
[], [],
); );
if (!result) throw new TypeError("Failed to set value"); if (versionstamp === null) throw new TypeError("Failed to set value");
return { versionstamp };
} }
async delete(key: Deno.KvKey) { async delete(key: Deno.KvKey) {
@ -255,15 +256,16 @@ class AtomicOperation {
return this; return this;
} }
async commit(): Promise<boolean> { async commit(): Promise<Deno.KvCommitResult | null> {
const result = await core.opAsync( const versionstamp = await core.opAsync(
"op_kv_atomic_write", "op_kv_atomic_write",
this.#rid, this.#rid,
this.#checks, this.#checks,
this.#mutations, this.#mutations,
[], // TODO(@losfair): enqueue [], // TODO(@losfair): enqueue
); );
return result; if (versionstamp === null) return null;
return { versionstamp };
} }
then() { then() {

View file

@ -31,7 +31,10 @@ pub trait Database {
options: SnapshotReadOptions, options: SnapshotReadOptions,
) -> Result<Vec<ReadRangeOutput>, AnyError>; ) -> Result<Vec<ReadRangeOutput>, AnyError>;
async fn atomic_write(&self, write: AtomicWrite) -> Result<bool, AnyError>; async fn atomic_write(
&self,
write: AtomicWrite,
) -> Result<Option<CommitResult>, AnyError>;
} }
/// Options for a snapshot read. /// Options for a snapshot read.
@ -304,3 +307,9 @@ impl MutationKind {
} }
} }
} }
/// The result of a successful commit of an atomic write operation.
pub struct CommitResult {
/// The new versionstamp of the data that was committed.
pub versionstamp: Versionstamp,
}

View file

@ -519,7 +519,7 @@ async fn op_kv_atomic_write<DBH>(
checks: Vec<V8KvCheck>, checks: Vec<V8KvCheck>,
mutations: Vec<V8KvMutation>, mutations: Vec<V8KvMutation>,
enqueues: Vec<V8Enqueue>, enqueues: Vec<V8Enqueue>,
) -> Result<bool, AnyError> ) -> Result<Option<String>, AnyError>
where where
DBH: DatabaseHandler + 'static, DBH: DatabaseHandler + 'static,
{ {
@ -585,7 +585,7 @@ where
let result = db.atomic_write(atomic_write).await?; let result = db.atomic_write(atomic_write).await?;
Ok(result) Ok(result.map(|res| hex::encode(res.versionstamp)))
} }
// (prefix, start, end) // (prefix, start, end)

View file

@ -17,6 +17,7 @@ use rusqlite::OptionalExtension;
use rusqlite::Transaction; use rusqlite::Transaction;
use crate::AtomicWrite; use crate::AtomicWrite;
use crate::CommitResult;
use crate::Database; use crate::Database;
use crate::DatabaseHandler; use crate::DatabaseHandler;
use crate::KvEntry; use crate::KvEntry;
@ -216,7 +217,10 @@ impl Database for SqliteDb {
Ok(responses) Ok(responses)
} }
async fn atomic_write(&self, write: AtomicWrite) -> Result<bool, AnyError> { async fn atomic_write(
&self,
write: AtomicWrite,
) -> Result<Option<CommitResult>, AnyError> {
let mut db = self.0.borrow_mut(); let mut db = self.0.borrow_mut();
let tx = db.transaction()?; let tx = db.transaction()?;
@ -228,7 +232,7 @@ impl Database for SqliteDb {
.optional()? .optional()?
.map(version_to_versionstamp); .map(version_to_versionstamp);
if real_versionstamp != check.versionstamp { if real_versionstamp != check.versionstamp {
return Ok(false); return Ok(None);
} }
} }
@ -273,7 +277,11 @@ impl Database for SqliteDb {
tx.commit()?; tx.commit()?;
Ok(true) let new_vesionstamp = version_to_versionstamp(version);
Ok(Some(CommitResult {
versionstamp: new_vesionstamp,
}))
} }
} }

View file

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "1.68.0" channel = "1.68.2"
components = ["rustfmt", "clippy"] components = ["rustfmt", "clippy"]