Coverage Report

Created: 2026-07-31 00:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/runner/work/feoxdb/feoxdb/src/core/store/atomic.rs
Line
Count
Source
1
use bytes::{BufMut, BytesMut};
2
use std::sync::atomic::Ordering;
3
use std::sync::Arc;
4
5
use crate::constants::Operation;
6
use crate::core::record::Record;
7
use crate::error::{FeoxError, Result};
8
9
use super::FeoxStore;
10
11
impl FeoxStore {
12
    /// Atomically increment a numeric counter.
13
    ///
14
    /// The value must be stored as an 8-byte little-endian i64. If the key doesn't exist,
15
    /// it will be created with the given delta value. If it exists, the value will be
16
    /// incremented atomically.
17
    ///
18
    /// # Value Format
19
    ///
20
    /// The value MUST be exactly 8 bytes representing a little-endian i64.
21
    /// Use `i64::to_le_bytes()` to create the initial value:
22
    /// ```rust,ignore
23
    /// let zero: i64 = 0;
24
    /// store.insert(b"counter", &zero.to_le_bytes())?;
25
    /// ```
26
    ///
27
    /// # Arguments
28
    ///
29
    /// * `key` - The key of the counter
30
    /// * `delta` - The amount to increment by (can be negative for decrement)
31
    /// * `timestamp` - Optional timestamp for conflict resolution
32
    ///
33
    /// # Returns
34
    ///
35
    /// Returns the new value after incrementing.
36
    ///
37
    /// # Errors
38
    ///
39
    /// * `InvalidOperation` - Existing value is not exactly 8 bytes (not a valid i64)
40
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
41
    ///
42
    /// # Example
43
    ///
44
    /// ```rust
45
    /// # use feoxdb::FeoxStore;
46
    /// # fn main() -> feoxdb::Result<()> {
47
    /// # let store = FeoxStore::new(None)?;
48
    /// // Initialize counter with proper binary format
49
    /// let initial: i64 = 0;
50
    /// store.insert(b"visits", &initial.to_le_bytes())?;
51
    ///
52
    /// // Increment atomically
53
    /// let val = store.atomic_increment(b"visits", 1)?;
54
    /// assert_eq!(val, 1);
55
    ///
56
    /// // Increment by 5
57
    /// let val = store.atomic_increment(b"visits", 5)?;
58
    /// assert_eq!(val, 6);
59
    ///
60
    /// // Decrement by 2
61
    /// let val = store.atomic_increment(b"visits", -2)?;
62
    /// assert_eq!(val, 4);
63
    ///
64
    /// // Or create new counter directly (starts at delta value)
65
    /// let downloads = store.atomic_increment(b"downloads", 100)?;
66
    /// assert_eq!(downloads, 100);
67
    /// # Ok(())
68
    /// # }
69
    /// ```
70
1.13k
    pub fn atomic_increment(&self, key: &[u8], delta: i64) -> Result<i64> {
71
1.13k
        self.atomic_increment_with_timestamp_and_ttl(key, delta, None, 0)
72
1.13k
    }
73
74
    /// Atomically increment/decrement with explicit timestamp.
75
    ///
76
    /// This is the advanced version that allows manual timestamp control.
77
    /// Most users should use `atomic_increment()` instead.
78
    ///
79
    /// # Arguments
80
    ///
81
    /// * `key` - The key to increment/decrement
82
    /// * `delta` - Amount to add (negative to decrement)
83
    /// * `timestamp` - Optional timestamp. If `None`, uses current time.
84
    ///
85
    /// # Errors
86
    ///
87
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
88
6
    pub fn atomic_increment_with_timestamp(
89
6
        &self,
90
6
        key: &[u8],
91
6
        delta: i64,
92
6
        timestamp: Option<u64>,
93
6
    ) -> Result<i64> {
94
6
        self.atomic_increment_with_timestamp_and_ttl(key, delta, timestamp, 0)
95
6
    }
96
97
    /// Atomically increment/decrement with TTL support.
98
    ///
99
    /// # Arguments
100
    ///
101
    /// * `key` - The key to increment/decrement
102
    /// * `delta` - Amount to add (negative to decrement)
103
    /// * `ttl_seconds` - Time-to-live in seconds (0 for no expiry)
104
    ///
105
    /// # Errors
106
    ///
107
    /// * `InvalidOperation` - Value is not a valid i64
108
2
    pub fn atomic_increment_with_ttl(
109
2
        &self,
110
2
        key: &[u8],
111
2
        delta: i64,
112
2
        ttl_seconds: u64,
113
2
    ) -> Result<i64> {
114
2
        self.atomic_increment_with_timestamp_and_ttl(key, delta, None, ttl_seconds)
115
2
    }
116
117
    /// Atomically increment/decrement with explicit timestamp and TTL.
118
    ///
119
    /// # Arguments
120
    ///
121
    /// * `key` - The key to increment/decrement
122
    /// * `delta` - Amount to add (negative to decrement)
123
    /// * `timestamp` - Optional timestamp. If `None`, uses current time.
124
    /// * `ttl_seconds` - Time-to-live in seconds (0 for no expiry)
125
    ///
126
    /// # Errors
127
    ///
128
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
129
1.14k
    pub fn atomic_increment_with_timestamp_and_ttl(
130
1.14k
        &self,
131
1.14k
        key: &[u8],
132
1.14k
        delta: i64,
133
1.14k
        timestamp: Option<u64>,
134
1.14k
        ttl_seconds: u64,
135
1.14k
    ) -> Result<i64> {
136
1.14k
        if ttl_seconds > 0 {
137
4
            self.ensure_ttl_write_supported()
?0
;
138
1.13k
        }
139
1.14k
        self.validate_new_key(key)
?0
;
140
141
1.14k
        let key_vec = key.to_vec();
142
1.14k
        let explicit_timestamp = timestamp.filter(|timestamp| 
*timestamp8
!= 0);
143
1.14k
        let mut observed = None;
144
145
        loop {
146
1.68k
            let Some(
current1.66k
) = self.hash_table.read(key, |_, record|
Arc::clone1.66k
(
record1.66k
)) else {
147
26
                let retired_at = observed
148
26
                    .as_ref()
149
26
                    .map_or(0, |record: &Arc<Record>| 
record2
.
retirement_timestamp2
());
150
26
                let timestamp = match explicit_timestamp {
151
4
                    Some(timestamp) => timestamp,
152
                    None => {
153
22
                        let minimum = retired_at.checked_add(1).ok_or(FeoxError::OlderTimestamp)
?0
;
154
22
                        self.get_timestamp(key).max(minimum)
155
                    }
156
                };
157
26
                if timestamp <= retired_at {
158
0
                    return Err(FeoxError::OlderTimestamp);
159
26
                }
160
161
26
                match self.hash_table.entry(key_vec.clone()) {
162
0
                    scc::hash_map::Entry::Occupied(_) => continue,
163
26
                    scc::hash_map::Entry::Vacant(entry) => {
164
26
                        let record_size = self.calculate_record_size(key.len(), 8);
165
26
                        let 
reservation10
= self.reserve_memory(record_size)
?16
;
166
10
                        let new_record =
167
10
                            counter_record(key_vec.clone(), delta, timestamp, ttl_seconds);
168
10
                        let ttl_expiry = new_record.ttl_expiry.load(Ordering::Acquire);
169
10
                        let buffered_record = self
170
10
                            .write_buffer
171
10
                            .as_ref()
172
10
                            .filter(|_| !
self.memory_only1
)
173
10
                            .map(|_| 
Arc::clone1
(
&new_record1
));
174
10
                        let entry_guard = entry.insert_entry(Arc::clone(&new_record));
175
10
                        self.insert_into_tree(key_vec.clone(), new_record);
176
10
                        self.observe_published_timestamp(
177
10
                            key,
178
10
                            timestamp,
179
10
                            explicit_timestamp.is_some(),
180
                        );
181
10
                        reservation.commit();
182
10
                        self.note_ttl_transition(0, ttl_expiry);
183
10
                        self.stats.record_count.fetch_add(1, Ordering::Relaxed);
184
10
                        drop(entry_guard);
185
186
1
                        if let (Some(write_buffer), Some(record)) =
187
10
                            (&self.write_buffer, buffered_record)
188
                        {
189
1
                            write_buffer.add_write(Operation::Insert, record, 0)
?0
;
190
9
                        }
191
10
                        return Ok(delta);
192
                    }
193
                }
194
            };
195
196
1.66k
            let root = observed.get_or_insert_with(|| 
Arc::clone1.11k
(
&current1.11k
));
197
1.66k
            if explicit_timestamp.is_some_and(|timestamp| 
timestamp4
<=
current.timestamp4
) {
198
1
                return Err(FeoxError::OlderTimestamp);
199
1.66k
            }
200
201
1.66k
            let expiry = current.ttl_expiry.load(Ordering::Acquire);
202
1.66k
            if self.enable_ttl && 
expiry > 04
{
203
4
                let now = self.get_timestamp_pub();
204
4
                if now > expiry {
205
2
                    self.retire_expired_if_current(key, &current, now)
?0
;
206
2
                    continue;
207
2
                }
208
1.65k
            }
209
210
1.66k
            let (value, _, source) = match self.resolve_value(key, Arc::clone(&current)) {
211
1.66k
                Ok(resolved) => resolved,
212
                Err(FeoxError::KeyNotFound) => {
213
0
                    let now = self.get_timestamp_pub();
214
0
                    self.retire_expired_if_current(key, &current, now)?;
215
0
                    continue;
216
                }
217
0
                Err(error) => return Err(error),
218
            };
219
1.66k
            if value.len() != 8 {
220
2
                return Err(FeoxError::InvalidOperation);
221
1.65k
            }
222
1.65k
            let current_value = i64::from_le_bytes(
223
1.65k
                value
224
1.65k
                    .as_ref()
225
1.65k
                    .try_into()
226
1.65k
                    .map_err(|_| FeoxError::InvalidNumericValue)
?0
,
227
            );
228
1.65k
            let new_value = current_value.saturating_add(delta);
229
1.65k
            let timestamp = explicit_timestamp.unwrap_or_else(|| 
self1.65k
.
get_timestamp1.65k
(
key1.65k
));
230
231
1.65k
            match self.hash_table.entry(key_vec.clone()) {
232
1.65k
                scc::hash_map::Entry::Occupied(mut entry) => {
233
1.65k
                    let old_record = entry.get();
234
1.65k
                    if !Arc::ptr_eq(old_record, &source) {
235
545
                        if explicit_timestamp
236
545
                            .is_some_and(|timestamp| 
timestamp0
<=
root.retirement_timestamp()0
)
237
                        {
238
0
                            return Err(FeoxError::OlderTimestamp);
239
545
                        }
240
545
                        continue;
241
1.11k
                    }
242
1.11k
                    if timestamp <= old_record.timestamp {
243
0
                        return Err(FeoxError::OlderTimestamp);
244
1.11k
                    }
245
246
1.11k
                    let old_size = old_record.calculate_size();
247
1.11k
                    let new_size = self.calculate_record_size(old_record.key.len(), 8);
248
1.11k
                    let reservation = self.reserve_memory(new_size.saturating_sub(old_size))
?0
;
249
1.11k
                    let old_record = Arc::clone(old_record);
250
1.11k
                    let record = counter_record(key_vec.clone(), new_value, timestamp, ttl_seconds);
251
1.11k
                    let old_expiry = old_record.ttl_expiry.load(Ordering::Acquire);
252
1.11k
                    let new_expiry = record.ttl_expiry.load(Ordering::Acquire);
253
254
1.11k
                    old_record.link_successor(&record);
255
1.11k
                    old_record.refcount.store(0, Ordering::Release);
256
1.11k
                    entry.insert(Arc::clone(&record));
257
1.11k
                    self.publish_to_tree(&key_vec, Arc::clone(&record));
258
1.11k
                    self.observe_published_timestamp(key, timestamp, explicit_timestamp.is_some());
259
1.11k
                    reservation.commit();
260
1.11k
                    self.note_ttl_transition(old_expiry, new_expiry);
261
1.11k
                    if old_size > new_size {
262
0
                        self.release_memory(old_size - new_size);
263
1.11k
                    }
264
1.11k
                    drop(entry);
265
266
1.11k
                    if !self.memory_only {
267
101
                        if self.enable_caching {
268
101
                            if let Some(cache) = &self.cache {
269
101
                                cache.remove_for_record(&key_vec, &old_record);
270
101
                            
}0
271
0
                        }
272
273
101
                        if let Some(write_buffer) = &self.write_buffer {
274
101
                            write_buffer.add_replacement(record, old_record)
?0
;
275
0
                        }
276
1.01k
                    }
277
1.11k
                    return Ok(new_value);
278
                }
279
                scc::hash_map::Entry::Vacant(_) => {
280
0
                    if explicit_timestamp
281
0
                        .is_some_and(|timestamp| timestamp <= root.retirement_timestamp())
282
                    {
283
0
                        return Err(FeoxError::OlderTimestamp);
284
0
                    }
285
                }
286
            }
287
        }
288
1.14k
    }
289
290
    /// Insert a key only when it does not already exist.
291
    ///
292
    /// The existence check and insertion happen under the hash-table entry guard, so
293
    /// concurrent callers cannot both create the same key.
294
    ///
295
    /// # Returns
296
    ///
297
    /// Returns `Ok(true)` when this call inserted the key and `Ok(false)` when another
298
    /// value already exists.
299
    ///
300
    /// # Example
301
    ///
302
    /// ```rust
303
    /// # use feoxdb::FeoxStore;
304
    /// # fn main() -> feoxdb::Result<()> {
305
    /// let store = FeoxStore::new(None)?;
306
    /// assert!(store.insert_if_absent(b"job:1", b"first")?);
307
    /// assert!(!store.insert_if_absent(b"job:1", b"second")?);
308
    /// assert_eq!(store.get(b"job:1")?, b"first");
309
    /// # Ok(())
310
    /// # }
311
    /// ```
312
16.0k
    pub fn insert_if_absent(&self, key: &[u8], value: &[u8]) -> Result<bool> {
313
16.0k
        let start = std::time::Instant::now();
314
16.0k
        self.validate_key_value(key, value)
?0
;
315
16.0k
        let key_vec = key.to_vec();
316
317
16.0k
        match self.hash_table.entry(key_vec.clone()) {
318
6.61k
            scc::hash_map::Entry::Occupied(_) => Ok(false),
319
9.42k
            scc::hash_map::Entry::Vacant(entry) => {
320
9.42k
                let record_size = self.calculate_record_size(key.len(), value.len());
321
9.42k
                let 
reservation9.40k
= self.reserve_memory(record_size)
?15
;
322
323
9.40k
                let timestamp = self.get_timestamp(key);
324
9.40k
                let record = Arc::new(Record::new(key_vec.clone(), value.to_vec(), timestamp));
325
9.40k
                let buffered_record = self
326
9.40k
                    .write_buffer
327
9.40k
                    .as_ref()
328
9.40k
                    .filter(|_| !
self.memory_only1
)
329
9.40k
                    .map(|_| 
Arc::clone1
(
&record1
));
330
9.40k
                let entry_guard = entry.insert_entry(Arc::clone(&record));
331
332
9.40k
                self.insert_into_tree(key_vec, record);
333
9.40k
                reservation.commit();
334
9.40k
                self.stats.record_count.fetch_add(1, Ordering::Relaxed);
335
9.40k
                drop(entry_guard);
336
9.40k
                self.stats
337
9.40k
                    .record_insert(start.elapsed().as_nanos() as u64, false);
338
339
9.40k
                if let (Some(
write_buffer1
), Some(
record1
)) = (&self.write_buffer, buffered_record) {
340
1
                    write_buffer.add_write(Operation::Insert, record, 0)
?0
;
341
9.40k
                }
342
343
9.40k
                Ok(true)
344
            }
345
        }
346
16.0k
    }
347
348
    /// Atomically compare and swap a value.
349
    ///
350
    /// Compares the current value of a key with an expected value, and if they match,
351
    /// atomically replaces it with a new value. This operation is atomic within the
352
    /// HashMap shard, preventing race conditions.
353
    ///
354
    /// # Arguments
355
    ///
356
    /// * `key` - The key to check and potentially update
357
    /// * `expected` - The expected current value
358
    /// * `new_value` - The new value to set if comparison succeeds
359
    ///
360
    /// # Returns
361
    ///
362
    /// Returns `Ok(true)` if the swap succeeded (current value matched expected).
363
    /// Returns `Ok(false)` if the current value didn't match or key doesn't exist.
364
    ///
365
    /// # Errors
366
    ///
367
    /// * `InvalidKeySize` - Key is invalid
368
    /// * `InvalidValueSize` - New value is too large
369
    /// * `OutOfMemory` - Memory limit exceeded
370
    /// * `IoError` - Failed to read value from disk
371
    ///
372
    /// # Example
373
    ///
374
    /// ```rust
375
    /// # use feoxdb::FeoxStore;
376
    /// # fn main() -> feoxdb::Result<()> {
377
    /// # let store = FeoxStore::new(None)?;
378
    /// store.insert(b"config", b"v1")?;
379
    ///
380
    /// // Successful CAS - value matches
381
    /// let swapped = store.compare_and_swap(b"config", b"v1", b"v2")?;
382
    /// assert_eq!(swapped, true);
383
    ///
384
    /// // Failed CAS - value doesn't match
385
    /// let swapped = store.compare_and_swap(b"config", b"v1", b"v3")?;
386
    /// assert_eq!(swapped, false); // Value is now "v2", not "v1"
387
    ///
388
    /// // CAS on non-existent key
389
    /// let swapped = store.compare_and_swap(b"missing", b"any", b"new")?;
390
    /// assert_eq!(swapped, false);
391
    /// # Ok(())
392
    /// # }
393
    /// ```
394
2.31k
    pub fn compare_and_swap(&self, key: &[u8], expected: &[u8], new_value: &[u8]) -> Result<bool> {
395
2.31k
        self.compare_and_swap_with_timestamp_and_ttl(key, expected, new_value, None, 0)
396
2.31k
    }
397
398
    /// Compare and swap with explicit timestamp.
399
    ///
400
    /// This is the advanced version that allows manual timestamp control for
401
    /// conflict resolution. Most users should use `compare_and_swap()` instead.
402
    ///
403
    /// # Arguments
404
    ///
405
    /// * `key` - The key to check and potentially update
406
    /// * `expected` - The expected current value
407
    /// * `new_value` - The new value to set if comparison succeeds
408
    /// * `timestamp` - Optional timestamp. If `None`, uses current time.
409
    ///
410
    /// # Errors
411
    ///
412
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
413
3
    pub fn compare_and_swap_with_timestamp(
414
3
        &self,
415
3
        key: &[u8],
416
3
        expected: &[u8],
417
3
        new_value: &[u8],
418
3
        timestamp: Option<u64>,
419
3
    ) -> Result<bool> {
420
3
        self.compare_and_swap_with_timestamp_and_ttl(key, expected, new_value, timestamp, 0)
421
3
    }
422
423
    /// Compare and swap with TTL support.
424
    ///
425
    /// # Arguments
426
    ///
427
    /// * `key` - The key to check and potentially update
428
    /// * `expected` - The expected current value
429
    /// * `new_value` - The new value to set if comparison succeeds
430
    /// * `ttl_seconds` - Time-to-live in seconds (0 for no expiry)
431
    ///
432
    /// # Errors
433
    ///
434
    /// * `InvalidKeySize` - Key is invalid
435
    /// * `InvalidValueSize` - New value is too large
436
1
    pub fn compare_and_swap_with_ttl(
437
1
        &self,
438
1
        key: &[u8],
439
1
        expected: &[u8],
440
1
        new_value: &[u8],
441
1
        ttl_seconds: u64,
442
1
    ) -> Result<bool> {
443
1
        self.compare_and_swap_with_timestamp_and_ttl(key, expected, new_value, None, ttl_seconds)
444
1
    }
445
446
    /// Compare and swap with explicit timestamp and TTL.
447
    ///
448
    /// # Arguments
449
    ///
450
    /// * `key` - The key to check and potentially update
451
    /// * `expected` - The expected current value
452
    /// * `new_value` - The new value to set if comparison succeeds
453
    /// * `timestamp` - Optional timestamp. If `None`, uses current time.
454
    /// * `ttl_seconds` - Time-to-live in seconds (0 for no expiry)
455
    ///
456
    /// # Errors
457
    ///
458
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
459
2.32k
    pub fn compare_and_swap_with_timestamp_and_ttl(
460
2.32k
        &self,
461
2.32k
        key: &[u8],
462
2.32k
        expected: &[u8],
463
2.32k
        new_value: &[u8],
464
2.32k
        timestamp: Option<u64>,
465
2.32k
        ttl_seconds: u64,
466
2.32k
    ) -> Result<bool> {
467
2.32k
        if ttl_seconds > 0 {
468
1
            self.ensure_ttl_write_supported()
?0
;
469
2.32k
        }
470
2.32k
        let start = std::time::Instant::now();
471
2.32k
        self.validate_key_value(key, new_value)
?0
;
472
2.32k
        let key_vec = key.to_vec();
473
474
1.79k
        let initial_record = {
475
2.32k
            let 
record2.32k
= match self
476
2.32k
                .hash_table
477
2.32k
                .read(&key_vec, |_, record| 
Arc::clone2.32k
(
record2.32k
))
478
            {
479
2.32k
                Some(record) => record,
480
1
                None => return Ok(false),
481
            };
482
2.32k
            let (value, cache_hit, source) = match self.resolve_value(key, record) {
483
2.32k
                Ok(resolved) => resolved,
484
0
                Err(FeoxError::KeyNotFound | FeoxError::StaleExtent) => return Ok(false),
485
0
                Err(error) => return Err(error),
486
            };
487
488
2.32k
            if !cache_hit {
489
1
                if let Some(cache) = &self.cache {
490
1
                    cache.insert_for_record(key_vec.clone(), value.clone(), &source);
491
1
                
}0
492
2.31k
            }
493
2.32k
            if value.as_ref() != expected {
494
525
                return Ok(false);
495
1.79k
            }
496
1.79k
            source
497
        };
498
499
1.79k
        let timestamp = self.resolve_timestamp(key, timestamp);
500
1.79k
        self.replace_record_if_current(
501
1.79k
            &key_vec,
502
1.79k
            &initial_record,
503
1.79k
            new_value,
504
1.79k
            timestamp,
505
1.79k
            ttl_seconds,
506
1.79k
            start,
507
        )
508
2.32k
    }
509
510
1.80k
    pub(super) fn replace_record_if_current(
511
1.80k
        &self,
512
1.80k
        key: &[u8],
513
1.80k
        expected: &Arc<Record>,
514
1.80k
        new_value: &[u8],
515
1.80k
        timestamp: (u64, bool),
516
1.80k
        ttl_seconds: u64,
517
1.80k
        start: std::time::Instant,
518
1.80k
    ) -> Result<bool> {
519
1.80k
        let (timestamp, explicit_timestamp) = timestamp;
520
1.80k
        match self.hash_table.entry(key.to_vec()) {
521
1.80k
            scc::hash_map::Entry::Occupied(mut entry) => {
522
1.80k
                let old_record = entry.get();
523
524
1.80k
                if !Arc::ptr_eq(old_record, expected) {
525
665
                    return Ok(false);
526
1.13k
                }
527
528
1.13k
                if timestamp <= old_record.timestamp {
529
1
                    return Err(FeoxError::OlderTimestamp);
530
1.13k
                }
531
532
1.13k
                let old_size = old_record.calculate_size();
533
1.13k
                let new_size = self.calculate_record_size(key.len(), new_value.len());
534
1.13k
                let 
reservation1.11k
= self.reserve_memory(new_size.saturating_sub(old_size))
?16
;
535
1.11k
                let old_record_arc = Arc::clone(old_record);
536
1.11k
                let old_expiry = old_record_arc.ttl_expiry.load(Ordering::Acquire);
537
538
                // Create new record with TTL if specified
539
1.11k
                let new_record = if ttl_seconds > 0 {
540
1
                    let ttl_expiry =
541
1
                        timestamp.saturating_add(ttl_seconds.saturating_mul(1_000_000_000));
542
1
                    Arc::new(Record::new_with_timestamp_ttl(
543
1
                        key.to_vec(),
544
1
                        new_value.to_vec(),
545
1
                        timestamp,
546
1
                        ttl_expiry,
547
                    ))
548
                } else {
549
1.11k
                    Arc::new(Record::new(key.to_vec(), new_value.to_vec(), timestamp))
550
                };
551
552
1.11k
                old_record_arc.link_successor(&new_record);
553
1.11k
                old_record_arc.refcount.store(0, Ordering::Release);
554
1.11k
                entry.insert(Arc::clone(&new_record));
555
1.11k
                self.publish_to_tree(key, Arc::clone(&new_record));
556
1.11k
                self.observe_published_timestamp(key, timestamp, explicit_timestamp);
557
1.11k
                reservation.commit();
558
1.11k
                self.note_ttl_transition(old_expiry, new_record.ttl_expiry.load(Ordering::Acquire));
559
1.11k
                if old_size > new_size {
560
3
                    self.release_memory(old_size - new_size);
561
1.11k
                }
562
1.11k
                drop(entry);
563
564
1.11k
                self.stats
565
1.11k
                    .record_insert(start.elapsed().as_nanos() as u64, true);
566
567
1.11k
                if !self.memory_only {
568
3
                    if self.enable_caching {
569
3
                        if let Some(ref cache) = self.cache {
570
3
                            cache.remove_for_record(key, &old_record_arc);
571
3
                        
}0
572
0
                    }
573
574
3
                    if let Some(ref wb) = self.write_buffer {
575
3
                        wb.add_replacement(new_record, old_record_arc)
?0
;
576
0
                    }
577
1.11k
                }
578
579
1.11k
                Ok(true)
580
            }
581
0
            scc::hash_map::Entry::Vacant(_) => Ok(false),
582
        }
583
1.80k
    }
584
}
585
586
1.12k
fn counter_record(key: Vec<u8>, value: i64, timestamp: u64, ttl_seconds: u64) -> Arc<Record> {
587
1.12k
    let mut bytes = BytesMut::with_capacity(std::mem::size_of::<i64>());
588
1.12k
    bytes.put_i64_le(value);
589
1.12k
    let value = bytes.freeze();
590
591
1.12k
    if ttl_seconds > 0 {
592
4
        Arc::new(Record::new_from_bytes_with_ttl(
593
4
            key,
594
4
            value,
595
4
            timestamp,
596
4
            timestamp.saturating_add(ttl_seconds.saturating_mul(1_000_000_000)),
597
        ))
598
    } else {
599
1.11k
        Arc::new(Record::new_from_bytes(key, value, timestamp))
600
    }
601
1.12k
}