feat(kv): set_if compare-and-swap for KV (per-app + shared + SDK) (Track A M5)
No atomic CAS existed, so concurrent writers to the same KV key raced. Add set_if(key, expected, new): writes only when the current value equals expected, or (expected absent) only when the key is missing — the primitive for lock-free counters / optimistic concurrency. - KvService + GroupKvService traits gain set_if with a defaulted "unsupported" impl (Noop + other impls untouched); the real services override it. - kv_repo/group_kv_repo: atomic set_if — a single UPDATE ... WHERE value = $expected (JSONB semantic equality) or INSERT ... ON CONFLICT DO NOTHING. Atomic without a transaction; returns whether the write happened. - Services enforce the same value-size cap + (group) writes-authed + row/byte quotas as set; the event fires only on a successful swap. - Executor Rhai SDK: kv::collection(c).set_if(key, expected, new) and the shared_collection variant; Rhai () for expected means "only if absent". Pinned by kv_service/group_kv_service set_if unit tests (CAS + fail-closed) + sdk_kv::kv_set_if_compare_and_swap (through a real script). No migration. docs/sdk-shape.md documents the primitive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,8 +57,18 @@ returned by an `::collection(name)` constructor:
|
||||
let widgets = kv::collection("widgets");
|
||||
widgets.set("k", "v");
|
||||
let v = widgets.get("k");
|
||||
// Compare-and-swap (v1.2, Track A M5): atomic, returns a bool.
|
||||
widgets.set_if("k", (), "v0"); // insert only if ABSENT
|
||||
widgets.set_if("k", "v0", "v1"); // swap only if current == "v0"
|
||||
```
|
||||
|
||||
`set_if(key, expected, new)` is an atomic compare-and-swap: an `expected` of
|
||||
`()` means "only if the key is absent" (insert-if-absent); any other `expected`
|
||||
swaps only when the stored value equals it. Returns `true` if the write
|
||||
happened, `false` if the precondition failed — the primitive for lock-free
|
||||
counters / optimistic concurrency. Available on both `kv::collection` and
|
||||
`kv::shared_collection`.
|
||||
|
||||
Not `kv::set("widgets", "k", "v")`. The handle is a Rhai custom type
|
||||
the service registers; method calls bind to that type. This:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user