@@ -72,15 +72,18 @@ impl KvIdentityGraph {
7272 }
7373
7474 /// Serializes an entry body and metadata for insertion.
75- fn serialize_entry ( entry : & KvEntry ) -> Result < ( String , String ) , Report < TrustedServerError > > {
75+ fn serialize_entry (
76+ entry : & KvEntry ,
77+ store_name : & str ,
78+ ) -> Result < ( String , String ) , Report < TrustedServerError > > {
7679 let body = serde_json:: to_string ( entry) . change_context ( TrustedServerError :: KvStore {
77- store_name : String :: new ( ) ,
80+ store_name : store_name . to_owned ( ) ,
7881 message : "Failed to serialize KV entry body" . to_owned ( ) ,
7982 } ) ?;
8083 let meta = KvMetadata :: from_entry ( entry) ;
8184 let meta_str =
8285 serde_json:: to_string ( & meta) . change_context ( TrustedServerError :: KvStore {
83- store_name : String :: new ( ) ,
86+ store_name : store_name . to_owned ( ) ,
8487 message : "Failed to serialize KV entry metadata" . to_owned ( ) ,
8588 } ) ?;
8689 Ok ( ( body, meta_str) )
@@ -169,7 +172,7 @@ impl KvIdentityGraph {
169172 /// key already exists (`ItemPreconditionFailed`).
170173 pub fn create ( & self , ec_hash : & str , entry : & KvEntry ) -> Result < ( ) , Report < TrustedServerError > > {
171174 let store = self . open_store ( ) ?;
172- let ( body, meta_str) = Self :: serialize_entry ( entry) ?;
175+ let ( body, meta_str) = Self :: serialize_entry ( entry, & self . store_name ) ?;
173176 let created = Self :: try_insert_add ( & store, ec_hash, & body, & meta_str, & self . store_name ) ?;
174177 if created {
175178 Ok ( ( ) )
@@ -231,9 +234,11 @@ impl KvIdentityGraph {
231234 ec_hash : & str ,
232235 entry : & KvEntry ,
233236 ) -> Result < ( ) , Report < TrustedServerError > > {
234- // Try create first — fast path for new entries .
237+ // Serialize once and reuse across the fast path and CAS loop .
235238 let store = self . open_store ( ) ?;
236- let ( body, meta_str) = Self :: serialize_entry ( entry) ?;
239+ let ( body, meta_str) = Self :: serialize_entry ( entry, & self . store_name ) ?;
240+
241+ // Try create first — fast path for new entries.
237242 if Self :: try_insert_add ( & store, ec_hash, & body, & meta_str, & self . store_name ) ? {
238243 return Ok ( ( ) ) ;
239244 }
@@ -253,8 +258,6 @@ impl KvIdentityGraph {
253258
254259 // Tombstone — CAS overwrite to revive.
255260 log:: info!( "create_or_revive: reviving tombstone for '{ec_hash}'" ) ;
256- let store = self . open_store ( ) ?;
257- let ( body, meta_str) = Self :: serialize_entry ( entry) ?;
258261
259262 let mut current_gen = generation;
260263 for attempt in 0 ..MAX_CAS_RETRIES {
@@ -325,6 +328,10 @@ impl KvIdentityGraph {
325328 uid : & str ,
326329 synced : u64 ,
327330 ) -> Result < ( ) , Report < TrustedServerError > > {
331+ // Open store once for write operations. Note: `self.get()` opens
332+ // its own handle internally — this is intentional since `KVStore::open`
333+ // is a cheap name lookup, and keeping the read/write APIs independent
334+ // simplifies the method signatures.
328335 let store = self . open_store ( ) ?;
329336
330337 for attempt in 0 ..MAX_CAS_RETRIES {
@@ -336,7 +343,7 @@ impl KvIdentityGraph {
336343 "upsert_partner_id: no entry for '{ec_hash}', creating minimal entry"
337344 ) ;
338345 let minimal = KvEntry :: minimal ( partner_id, uid, synced) ;
339- let ( min_body, min_meta) = Self :: serialize_entry ( & minimal) ?;
346+ let ( min_body, min_meta) = Self :: serialize_entry ( & minimal, & self . store_name ) ?;
340347 if Self :: try_insert_add (
341348 & store,
342349 ec_hash,
@@ -348,7 +355,9 @@ impl KvIdentityGraph {
348355 }
349356 // Key appeared between get() and create — re-read on next iteration.
350357 log:: debug!(
351- "upsert_partner_id: minimal create raced for '{ec_hash}', retrying"
358+ "upsert_partner_id: minimal create raced for '{ec_hash}', retrying (attempt {}/{})" ,
359+ attempt + 1 ,
360+ MAX_CAS_RETRIES ,
352361 ) ;
353362 continue ;
354363 }
@@ -377,7 +386,7 @@ impl KvIdentityGraph {
377386 } ,
378387 ) ;
379388
380- let ( body, meta_str) = Self :: serialize_entry ( & entry) ?;
389+ let ( body, meta_str) = Self :: serialize_entry ( & entry, & self . store_name ) ?;
381390
382391 match store
383392 . build_insert ( )
@@ -448,18 +457,27 @@ impl KvIdentityGraph {
448457 return Ok ( ( ) ) ;
449458 }
450459
460+ // Guard against stale/out-of-order timestamps.
461+ if timestamp <= entry. last_seen {
462+ log:: trace!(
463+ "update_last_seen: stale timestamp for '{ec_hash}' (stored={}, incoming={timestamp})" ,
464+ entry. last_seen,
465+ ) ;
466+ return Ok ( ( ) ) ;
467+ }
468+
451469 // Debounce: skip if the stored value is recent enough.
452- if timestamp. saturating_sub ( entry. last_seen ) < LAST_SEEN_DEBOUNCE_SECS {
470+ if timestamp - entry. last_seen < LAST_SEEN_DEBOUNCE_SECS {
453471 log:: trace!(
454472 "update_last_seen: debounced for '{ec_hash}' (delta={}s)" ,
455- timestamp. saturating_sub ( entry. last_seen) ,
473+ timestamp - entry. last_seen,
456474 ) ;
457475 return Ok ( ( ) ) ;
458476 }
459477
460478 entry. last_seen = timestamp;
461479 let store = self . open_store ( ) ?;
462- let ( body, meta_str) = Self :: serialize_entry ( & entry) ?;
480+ let ( body, meta_str) = Self :: serialize_entry ( & entry, & self . store_name ) ?;
463481
464482 store
465483 . build_insert ( )
@@ -493,7 +511,7 @@ impl KvIdentityGraph {
493511 ) -> Result < ( ) , Report < TrustedServerError > > {
494512 let store = self . open_store ( ) ?;
495513 let entry = KvEntry :: tombstone ( current_timestamp ( ) ) ;
496- let ( body, meta_str) = Self :: serialize_entry ( & entry) ?;
514+ let ( body, meta_str) = Self :: serialize_entry ( & entry, & self . store_name ) ?;
497515
498516 store
499517 . build_insert ( )
@@ -557,7 +575,7 @@ mod tests {
557575 fn serialize_entry_produces_valid_json ( ) {
558576 let entry = KvEntry :: tombstone ( 1000 ) ;
559577 let ( body, meta) =
560- KvIdentityGraph :: serialize_entry ( & entry) . expect ( "should serialize entry" ) ;
578+ KvIdentityGraph :: serialize_entry ( & entry, "test-store" ) . expect ( "should serialize entry" ) ;
561579
562580 // Verify body is valid JSON.
563581 let _: KvEntry =
0 commit comments