MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / execute_kv_transfer

Method execute_kv_transfer

nodedb/src/data/executor/handlers/kv/transfer.rs:31–200  ·  view source on GitHub ↗

Atomic fungible transfer: source.field -= amount, dest.field += amount. Entire read-validate-write is one Data Plane pass. No TOCTOU.

(
        &mut self,
        task: &ExecutionTask,
        params: TransferParams<'_>,
    )

Source from the content-addressed store, hash-verified

29 ///
30 /// Entire read-validate-write is one Data Plane pass. No TOCTOU.
31 pub(in crate::data::executor) fn execute_kv_transfer(
32 &mut self,
33 task: &ExecutionTask,
34 params: TransferParams<'_>,
35 ) -> Response {
36 let TransferParams {
37 tid,
38 collection,
39 source_key,
40 dest_key,
41 field,
42 amount,
43 } = params;
44 debug!(core = self.core_id, %collection, %field, amount, "kv transfer");
45
46 if self.kv_engine.is_over_budget() {
47 return self.response_error(task, ErrorCode::ResourcesExhausted);
48 }
49
50 let now_ms = current_ms();
51
52 // Step 1: Read both values atomically (same core, no interleaving).
53 let source_val = self.kv_engine.get(tid, collection, source_key, now_ms);
54 let dest_val = self.kv_engine.get(tid, collection, dest_key, now_ms);
55
56 let Some(source_bytes) = source_val else {
57 return self.response_error(task, ErrorCode::NotFound);
58 };
59
60 // Step 2: Extract and validate source balance.
61 let source_balance = match extract_numeric_field(&source_bytes, field) {
62 Some(v) => v,
63 None => {
64 return self.response_error(
65 task,
66 ErrorCode::TypeMismatch {
67 collection: collection.to_string(),
68 detail: format!("field '{field}' is not numeric or missing"),
69 },
70 );
71 }
72 };
73
74 if source_balance < amount {
75 return self.response_error(
76 task,
77 ErrorCode::InsufficientBalance {
78 collection: collection.to_string(),
79 detail: format!("source has {source_balance}, need {amount}"),
80 },
81 );
82 }
83
84 let dest_bytes = dest_val.unwrap_or_default();
85 let dest_balance = extract_numeric_field(&dest_bytes, field).unwrap_or(0.0);
86
87 // Step 3: Compute new values.
88 let new_source = match update_numeric_field(&source_bytes, field, source_balance - amount) {

Callers 1

execute_kvMethod · 0.80

Calls 14

extract_numeric_fieldFunction · 0.85
update_numeric_fieldFunction · 0.85
encode_jsonFunction · 0.85
is_over_budgetMethod · 0.80
response_errorMethod · 0.80
to_stringMethod · 0.80
record_kv_putMethod · 0.80
emit_write_eventMethod · 0.80
response_with_payloadMethod · 0.80
current_msFunction · 0.50
json_to_msgpackFunction · 0.50
getMethod · 0.45

Tested by

no test coverage detected