Coverage Report

Created: 2025-09-19 09:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/gh-runner/action-runner3/_work/feoxdb/feoxdb/src/core/store/operations.rs
Line
Count
Source
1
use bytes::Bytes;
2
use std::sync::atomic::Ordering;
3
use std::sync::Arc;
4
use std::time::{SystemTime, UNIX_EPOCH};
5
6
use crate::constants::*;
7
use crate::core::record::Record;
8
use crate::error::{FeoxError, Result};
9
10
use super::FeoxStore;
11
12
impl FeoxStore {
13
    /// Insert or update a key-value pair.
14
    ///
15
    /// If the key already exists with a TTL, the TTL is removed (key becomes permanent).
16
    /// To preserve or set TTL, use `insert_with_ttl()` instead.
17
    ///
18
    /// # Arguments
19
    ///
20
    /// * `key` - The key to insert
21
    /// * `value` - The value to store
22
    /// * `timestamp` - Optional timestamp for conflict resolution. If `None`, uses current time.
23
    ///
24
    /// # Returns
25
    ///
26
    /// Returns `Ok(true)` if a new key was inserted, `Ok(false)` if an existing key was updated.
27
    ///
28
    /// # Errors
29
    ///
30
    /// * `InvalidKey` - Key is empty or too large
31
    /// * `InvalidValue` - Value is too large
32
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
33
    /// * `OutOfMemory` - Memory limit exceeded
34
    ///
35
    /// # Example
36
    ///
37
    /// ```rust
38
    /// # use feoxdb::FeoxStore;
39
    /// # fn main() -> feoxdb::Result<()> {
40
    /// # let store = FeoxStore::new(None)?;
41
    /// store.insert(b"user:123", b"{\"name\":\"Mehran\"}")?;
42
    /// # Ok(())
43
    /// # }
44
    /// ```
45
    ///
46
    /// # Performance
47
    ///
48
    /// * Memory mode: ~600ns
49
    /// * Persistent mode: ~800ns (buffered write)
50
2.07k
    pub fn insert(&self, key: &[u8], value: &[u8]) -> Result<bool> {
51
2.07k
        self.insert_with_timestamp(key, value, None)
52
2.07k
    }
53
54
    /// Insert or update a key-value pair with explicit timestamp.
55
    ///
56
    /// This is the advanced version that allows manual timestamp control for
57
    /// conflict resolution. Most users should use `insert()` instead.
58
    ///
59
    /// # Arguments
60
    ///
61
    /// * `key` - The key to insert
62
    /// * `value` - The value to store
63
    /// * `timestamp` - Optional timestamp for conflict resolution. If `None`, uses current time.
64
    ///
65
    /// # Errors
66
    ///
67
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
68
2.07k
    pub fn insert_with_timestamp(
69
2.07k
        &self,
70
2.07k
        key: &[u8],
71
2.07k
        value: &[u8],
72
2.07k
        timestamp: Option<u64>,
73
2.07k
    ) -> Result<bool> {
74
2.07k
        self.insert_with_timestamp_and_ttl_internal(key, value, timestamp, 0)
75
2.07k
    }
76
77
    /// Insert or update a key-value pair using zero-copy Bytes.
78
    ///
79
    /// This method avoids copying the value data by directly using the Bytes type,
80
    /// which provides reference-counted zero-copy semantics. Useful when inserting
81
    /// data that was already read from network or disk as Bytes.
82
    ///
83
    /// If the key already exists with a TTL, the TTL is removed (key becomes permanent).
84
    /// To preserve or set TTL, use `insert_bytes_with_ttl()` instead.
85
    ///
86
    /// # Arguments
87
    ///
88
    /// * `key` - The key to insert
89
    /// * `value` - The value to store as Bytes
90
    ///
91
    /// # Returns
92
    ///
93
    /// Returns `Ok(true)` if a new key was inserted, `Ok(false)` if an existing key was updated.
94
    ///
95
    /// # Errors
96
    ///
97
    /// * `InvalidKey` - Key is empty or too large
98
    /// * `InvalidValue` - Value is too large
99
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
100
    /// * `OutOfMemory` - Memory limit exceeded
101
    ///
102
    /// # Example
103
    ///
104
    /// ```rust
105
    /// # use feoxdb::FeoxStore;
106
    /// # use bytes::Bytes;
107
    /// # fn main() -> feoxdb::Result<()> {
108
    /// # let store = FeoxStore::new(None)?;
109
    /// let data = Bytes::from_static(b"{\"name\":\"Mehran\"}");
110
    /// store.insert_bytes(b"user:123", data)?;
111
    /// # Ok(())
112
    /// # }
113
    /// ```
114
    ///
115
    /// # Performance
116
    ///
117
    /// * Memory mode: ~600ns (avoids value copy)
118
    /// * Persistent mode: ~800ns (buffered write, avoids value copy)
119
7
    pub fn insert_bytes(&self, key: &[u8], value: Bytes) -> Result<bool> {
120
7
        self.insert_bytes_with_timestamp(key, value, None)
121
7
    }
122
123
    /// Insert or update a key-value pair using zero-copy Bytes with explicit timestamp.
124
    ///
125
    /// This is the advanced version that allows manual timestamp control for
126
    /// conflict resolution. Most users should use `insert_bytes()` instead.
127
    ///
128
    /// # Arguments
129
    ///
130
    /// * `key` - The key to insert
131
    /// * `value` - The value to store as Bytes
132
    /// * `timestamp` - Optional timestamp for conflict resolution. If `None`, uses current time.
133
    ///
134
    /// # Errors
135
    ///
136
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
137
10
    pub fn insert_bytes_with_timestamp(
138
10
        &self,
139
10
        key: &[u8],
140
10
        value: Bytes,
141
10
        timestamp: Option<u64>,
142
10
    ) -> Result<bool> {
143
10
        self.insert_bytes_with_timestamp_and_ttl_internal(key, value, timestamp, 0)
144
10
    }
145
146
2.08k
    pub(super) fn insert_with_timestamp_and_ttl_internal(
147
2.08k
        &self,
148
2.08k
        key: &[u8],
149
2.08k
        value: &[u8],
150
2.08k
        timestamp: Option<u64>,
151
2.08k
        ttl_expiry: u64,
152
2.08k
    ) -> Result<bool> {
153
2.08k
        let start = std::time::Instant::now();
154
2.08k
        let timestamp = match timestamp {
155
2.07k
            Some(0) | None => self.get_timestamp(),
156
13
            Some(ts) => ts,
157
        };
158
2.08k
        self.validate_key_value(key, value)
?3
;
159
160
        // Check for existing record
161
2.08k
        let is_update = self.hash_table.contains(key);
162
2.08k
        let existing_record = self.hash_table.read(key, |_, v| 
v500
.
clone500
());
163
2.08k
        if let Some(
existing_record500
) = existing_record {
164
500
            let existing_ts = existing_record.timestamp;
165
500
            let existing_clone = existing_record;
166
167
500
            if timestamp < existing_ts {
168
1
                return Err(FeoxError::OlderTimestamp);
169
499
            }
170
171
            // Update existing record
172
499
            return self.update_record_with_ttl(&existing_clone, value, timestamp, ttl_expiry);
173
1.58k
        }
174
175
1.58k
        let record_size = self.calculate_record_size(key.len(), value.len());
176
1.58k
        if !self.check_memory_limit(record_size) {
177
1
            return Err(FeoxError::OutOfMemory);
178
1.58k
        }
179
180
        // Create new record with TTL if specified and TTL is enabled
181
1.58k
        let record = if ttl_expiry > 0 && 
self.enable_ttl6
{
182
6
            self.stats.keys_with_ttl.fetch_add(1, Ordering::Relaxed);
183
6
            Arc::new(Record::new_with_timestamp_ttl(
184
6
                key.to_vec(),
185
6
                value.to_vec(),
186
6
                timestamp,
187
6
                ttl_expiry,
188
            ))
189
        } else {
190
1.57k
            Arc::new(Record::new(key.to_vec(), value.to_vec(), timestamp))
191
        };
192
193
1.58k
        let key_vec = record.key.clone();
194
195
        // Insert into hash table
196
1.58k
        self.hash_table.upsert(key_vec.clone(), Arc::clone(&record));
197
198
        // Insert into lock-free skip list for ordered access
199
1.58k
        self.tree.insert(key_vec, Arc::clone(&record));
200
201
        // Update statistics
202
1.58k
        self.stats.record_count.fetch_add(1, Ordering::AcqRel);
203
1.58k
        self.stats
204
1.58k
            .memory_usage
205
1.58k
            .fetch_add(record_size, Ordering::AcqRel);
206
1.58k
        self.stats
207
1.58k
            .record_insert(start.elapsed().as_nanos() as u64, is_update);
208
209
        // Only do persistence if not in memory-only mode
210
1.58k
        if !self.memory_only {
211
            // Queue for persistence if write buffer exists
212
303
            if let Some(ref wb) = self.write_buffer {
213
303
                if let Err(
_e0
) = wb.add_write(Operation::Insert, record, 0) {
214
0
                    // Don't fail the insert - data is still in memory
215
0
                    // Return code already indicates success since data is in memory
216
303
                }
217
0
            }
218
1.27k
        }
219
220
1.58k
        Ok(!is_update)
221
2.08k
    }
222
223
    /// Internal method to insert a Bytes value with timestamp and TTL (zero-copy)
224
15
    pub(super) fn insert_bytes_with_timestamp_and_ttl_internal(
225
15
        &self,
226
15
        key: &[u8],
227
15
        value: Bytes,
228
15
        timestamp: Option<u64>,
229
15
        ttl_seconds: u64,
230
15
    ) -> Result<bool> {
231
15
        let start = std::time::Instant::now();
232
        // Get timestamp before any operations
233
15
        let timestamp = match timestamp {
234
9
            Some(0) | None => self.get_timestamp(),
235
6
            Some(ts) => ts,
236
        };
237
238
15
        self.validate_key(key)
?0
;
239
15
        let value_len = value.len();
240
15
        if value_len == 0 || 
value_len > MAX_VALUE_SIZE14
{
241
1
            return Err(FeoxError::InvalidValueSize);
242
14
        }
243
244
        // Check for existing record
245
14
        let is_update = self.hash_table.contains(key);
246
14
        let existing_record = self.hash_table.read(key, |_, v| 
v6
.
clone6
());
247
14
        if let Some(
existing_record6
) = existing_record {
248
6
            let existing_ts = existing_record.timestamp;
249
250
6
            if timestamp < existing_ts {
251
2
                return Err(FeoxError::OlderTimestamp);
252
4
            }
253
254
            // Calculate TTL expiry
255
4
            let ttl_expiry = if ttl_seconds > 0 && 
self.enable_ttl1
{
256
1
                timestamp + (ttl_seconds * 1_000_000_000)
257
            } else {
258
3
                0
259
            };
260
261
            // Update existing record using the Bytes version
262
4
            return self.update_record_with_ttl_bytes(
263
4
                &existing_record,
264
4
                value,
265
4
                timestamp,
266
4
                ttl_expiry,
267
            );
268
8
        }
269
270
        // This point is only reached for new inserts (not updates)
271
8
        let new_size = self.calculate_record_size(key.len(), value_len);
272
8
        if !self.check_memory_limit(new_size) {
273
0
            return Err(FeoxError::OutOfMemory);
274
8
        }
275
276
        // Create new record with Bytes value
277
8
        let record = if ttl_seconds > 0 && 
self.enable_ttl3
{
278
3
            let ttl_expiry = timestamp + (ttl_seconds * 1_000_000_000);
279
3
            self.stats.keys_with_ttl.fetch_add(1, Ordering::Relaxed);
280
3
            Arc::new(Record::new_from_bytes_with_ttl(
281
3
                key.to_vec(),
282
3
                value,
283
3
                timestamp,
284
3
                ttl_expiry,
285
            ))
286
        } else {
287
5
            Arc::new(Record::new_from_bytes(key.to_vec(), value, timestamp))
288
        };
289
290
8
        let key_vec = record.key.clone();
291
292
        // Insert into hash table
293
8
        self.hash_table.upsert(key_vec.clone(), Arc::clone(&record));
294
295
        // Insert into skip list for ordered access
296
8
        self.tree.insert(key_vec, Arc::clone(&record));
297
298
        // Update statistics
299
8
        self.stats.record_count.fetch_add(1, Ordering::AcqRel);
300
8
        self.stats
301
8
            .memory_usage
302
8
            .fetch_add(new_size, Ordering::AcqRel);
303
8
        self.stats
304
8
            .record_insert(start.elapsed().as_nanos() as u64, is_update);
305
306
        // Only do persistence if not in memory-only mode
307
8
        if !self.memory_only {
308
            // Queue for persistence if write buffer exists
309
0
            if let Some(ref wb) = self.write_buffer {
310
0
                if let Err(_e) = wb.add_write(Operation::Insert, record, 0) {
311
0
                    // Don't fail the insert - data is still in memory
312
0
                }
313
0
            }
314
8
        }
315
316
8
        Ok(!is_update)
317
15
    }
318
319
    /// Retrieve a value by key.
320
    ///
321
    /// # Arguments
322
    ///
323
    /// * `key` - The key to look up
324
    /// * `expected_size` - Optional expected value size for validation
325
    ///
326
    /// # Returns
327
    ///
328
    /// Returns the value as a `Vec<u8>` if found.
329
    ///
330
    /// # Errors
331
    ///
332
    /// * `KeyNotFound` - Key does not exist
333
    /// * `InvalidKey` - Key is invalid
334
    /// * `SizeMismatch` - Value size doesn't match expected size
335
    /// * `IoError` - Failed to read from disk (persistent mode)
336
    ///
337
    /// # Example
338
    ///
339
    /// ```rust
340
    /// # use feoxdb::FeoxStore;
341
    /// # fn main() -> feoxdb::Result<()> {
342
    /// # let store = FeoxStore::new(None)?;
343
    /// # store.insert(b"key", b"value")?;
344
    /// let value = store.get(b"key")?;
345
    /// assert_eq!(value, b"value");
346
    /// # Ok(())
347
    /// # }
348
    /// ```
349
    ///
350
    /// # Performance
351
    ///
352
    /// * Memory mode: ~100ns
353
    /// * Persistent mode (cached): ~150ns
354
    /// * Persistent mode (disk read): ~500ns
355
3.61k
    pub fn get(&self, key: &[u8]) -> Result<Vec<u8>> {
356
3.61k
        let start = std::time::Instant::now();
357
3.61k
        self.validate_key(key)
?0
;
358
359
3.61k
        let mut cache_hit = false;
360
3.61k
        if self.enable_caching {
361
4
            if let Some(ref cache) = self.cache {
362
4
                if let Some(
value0
) = cache.get(key) {
363
0
                    self.stats
364
0
                        .record_get(start.elapsed().as_nanos() as u64, true);
365
0
                    return Ok(value.to_vec());
366
4
                }
367
0
            }
368
3.60k
        }
369
370
3.61k
        let 
record3.58k
= self
371
3.61k
            .hash_table
372
3.61k
            .read(key, |_, v| 
v3.58k
.
clone3.58k
())
373
3.61k
            .ok_or(FeoxError::KeyNotFound)
?31
;
374
375
        // Check TTL expiry if TTL is enabled
376
3.58k
        if self.enable_ttl {
377
11
            let ttl_expiry = record.ttl_expiry.load(Ordering::Relaxed);
378
11
            if ttl_expiry > 0 {
379
8
                let now = SystemTime::now()
380
8
                    .duration_since(UNIX_EPOCH)
381
8
                    .unwrap_or_default()
382
8
                    .as_nanos() as u64;
383
8
                if now > ttl_expiry {
384
3
                    self.stats.ttl_expired_lazy.fetch_add(1, Ordering::Relaxed);
385
3
                    return Err(FeoxError::KeyNotFound);
386
5
                }
387
3
            }
388
3.56k
        }
389
390
3.57k
        let value = if let Some(
val3.57k
) = record.get_value() {
391
3.57k
            val.to_vec()
392
        } else {
393
4
            cache_hit = false; // Reading from disk
394
4
            self.load_value_from_disk(&record)
?0
395
        };
396
397
3.57k
        if self.enable_caching {
398
4
            if let Some(ref cache) = self.cache {
399
4
                cache.insert(key.to_vec(), Bytes::from(value.clone()));
400
4
            
}0
401
3.57k
        }
402
403
3.57k
        self.stats
404
3.57k
            .record_get(start.elapsed().as_nanos() as u64, cache_hit);
405
3.57k
        Ok(value)
406
3.61k
    }
407
408
    /// Get a value by key without copying (zero-copy).
409
    ///
410
    /// Returns `Bytes` which avoids the memory copy that `get()` performs
411
    /// when converting to `Vec<u8>`.
412
    ///
413
    /// # Arguments
414
    ///
415
    /// * `key` - The key to look up
416
    ///
417
    /// # Returns
418
    ///
419
    /// Returns the value as `Bytes` if found.
420
    ///
421
    /// # Example
422
    ///
423
    /// ```rust
424
    /// # use feoxdb::FeoxStore;
425
    /// # fn main() -> feoxdb::Result<()> {
426
    /// # let store = FeoxStore::new(None)?;
427
    /// # store.insert(b"key", b"value")?;
428
    /// let bytes = store.get_bytes(b"key")?;
429
    /// // Use bytes directly without copying
430
    /// assert_eq!(&bytes[..], b"value");
431
    /// # Ok(())
432
    /// # }
433
    /// ```
434
    ///
435
    /// # Performance
436
    ///
437
    /// Significantly faster than `get()` for large values:
438
    /// * 100 bytes: ~15% faster
439
    /// * 1KB: ~50% faster  
440
    /// * 10KB: ~90% faster
441
    /// * 100KB: ~95% faster
442
10
    pub fn get_bytes(&self, key: &[u8]) -> Result<Bytes> {
443
10
        let start = std::time::Instant::now();
444
10
        self.validate_key(key)
?0
;
445
446
10
        if self.enable_caching {
447
0
            if let Some(ref cache) = self.cache {
448
0
                if let Some(value) = cache.get(key) {
449
0
                    self.stats
450
0
                        .record_get(start.elapsed().as_nanos() as u64, true);
451
0
                    return Ok(value);
452
0
                }
453
0
            }
454
10
        }
455
456
10
        let 
record9
= self
457
10
            .hash_table
458
10
            .read(key, |_, v| 
v9
.
clone9
())
459
10
            .ok_or(FeoxError::KeyNotFound)
?1
;
460
461
        // Check TTL expiry if TTL is enabled
462
9
        if self.enable_ttl {
463
1
            let ttl_expiry = record.ttl_expiry.load(Ordering::Relaxed);
464
1
            if ttl_expiry > 0 {
465
1
                let now = SystemTime::now()
466
1
                    .duration_since(UNIX_EPOCH)
467
1
                    .unwrap_or_default()
468
1
                    .as_nanos() as u64;
469
1
                if now > ttl_expiry {
470
0
                    self.stats.ttl_expired_lazy.fetch_add(1, Ordering::Relaxed);
471
0
                    return Err(FeoxError::KeyNotFound);
472
1
                }
473
0
            }
474
8
        }
475
476
9
        let (value, cache_hit) = if let Some(val) = record.get_value() {
477
9
            (val, true)
478
        } else {
479
0
            (Bytes::from(self.load_value_from_disk(&record)?), false)
480
        };
481
482
9
        if self.enable_caching {
483
0
            if let Some(ref cache) = self.cache {
484
0
                cache.insert(key.to_vec(), value.clone());
485
0
            }
486
9
        }
487
488
9
        self.stats
489
9
            .record_get(start.elapsed().as_nanos() as u64, cache_hit);
490
9
        Ok(value)
491
10
    }
492
493
    /// Delete a key-value pair.
494
    ///
495
    /// # Arguments
496
    ///
497
    /// * `key` - The key to delete
498
    /// * `timestamp` - Optional timestamp for conflict resolution
499
    ///
500
    /// # Returns
501
    ///
502
    /// Returns `Ok(())` if the key was deleted.
503
    ///
504
    /// # Errors
505
    ///
506
    /// * `KeyNotFound` - Key does not exist
507
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
508
    ///
509
    /// # Example
510
    ///
511
    /// ```rust
512
    /// # use feoxdb::FeoxStore;
513
    /// # fn main() -> feoxdb::Result<()> {
514
    /// # let store = FeoxStore::new(None)?;
515
    /// # store.insert(b"temp", b"data")?;
516
    /// store.delete(b"temp")?;
517
    /// # Ok(())
518
    /// # }
519
    /// ```
520
    ///
521
    /// # Performance
522
    ///
523
    /// * Memory mode: ~300ns
524
    /// * Persistent mode: ~400ns
525
30
    pub fn delete(&self, key: &[u8]) -> Result<()> {
526
30
        self.delete_with_timestamp(key, None)
527
30
    }
528
529
    /// Delete a key-value pair with explicit timestamp.
530
    ///
531
    /// This is the advanced version that allows manual timestamp control.
532
    /// Most users should use `delete()` instead.
533
    ///
534
    /// # Arguments
535
    ///
536
    /// * `key` - The key to delete
537
    /// * `timestamp` - Optional timestamp. If `None`, uses current time.
538
    ///
539
    /// # Errors
540
    ///
541
    /// * `OlderTimestamp` - Timestamp is not newer than existing record
542
32
    pub fn delete_with_timestamp(&self, key: &[u8], timestamp: Option<u64>) -> Result<()> {
543
32
        let start = std::time::Instant::now();
544
32
        let timestamp = match timestamp {
545
30
            Some(0) | None => self.get_timestamp(),
546
2
            Some(ts) => ts,
547
        };
548
32
        self.validate_key(key)
?0
;
549
550
        // Remove from hash table and get the record
551
32
        let 
record_pair22
= self.hash_table.remove(key).ok_or(FeoxError::KeyNotFound)
?10
;
552
22
        let record = record_pair.1;
553
554
22
        if timestamp < record.timestamp {
555
            // Put it back if timestamp is older
556
1
            self.hash_table.upsert(key.to_vec(), record);
557
1
            return Err(FeoxError::OlderTimestamp);
558
21
        }
559
560
21
        let record_size = record.calculate_size();
561
21
        let old_value_len = record.value_len;
562
563
        // Mark record as deleted by setting refcount to 0
564
21
        record.refcount.store(0, Ordering::Release);
565
566
        // Remove from lock-free skip list
567
21
        self.tree.remove(key);
568
569
        // Update statistics
570
21
        self.stats.record_count.fetch_sub(1, Ordering::AcqRel);
571
21
        self.stats
572
21
            .memory_usage
573
21
            .fetch_sub(record_size, Ordering::AcqRel);
574
575
        // Clear from cache
576
21
        if self.enable_caching {
577
2
            if let Some(ref cache) = self.cache {
578
2
                cache.remove(key);
579
2
            
}0
580
19
        }
581
582
        // Queue deletion for persistence if write buffer exists and not memory-only
583
21
        if !self.memory_only {
584
2
            if let Some(ref wb) = self.write_buffer {
585
2
                if let Err(
_e0
) = wb.add_write(Operation::Delete, record, old_value_len) {
586
0
                    // Silent failure - data operation succeeded in memory
587
2
                }
588
0
            }
589
19
        }
590
591
21
        self.stats.record_delete(start.elapsed().as_nanos() as u64);
592
21
        Ok(())
593
32
    }
594
595
    /// Get the size of a value without loading it.
596
    ///
597
    /// Useful for checking value size before loading large values from disk.
598
    ///
599
    /// # Arguments
600
    ///
601
    /// * `key` - The key to check
602
    ///
603
    /// # Returns
604
    ///
605
    /// Returns the size in bytes of the value.
606
    ///
607
    /// # Errors
608
    ///
609
    /// * `KeyNotFound` - Key does not exist
610
    ///
611
    /// # Example
612
    ///
613
    /// ```rust
614
    /// # use feoxdb::FeoxStore;
615
    /// # fn main() -> feoxdb::Result<()> {
616
    /// # let store = FeoxStore::new(None)?;
617
    /// store.insert(b"large_file", &vec![0u8; 1_000_000])?;
618
    ///
619
    /// // Check size before loading
620
    /// let size = store.get_size(b"large_file")?;
621
    /// assert_eq!(size, 1_000_000);
622
    /// # Ok(())
623
    /// # }
624
    /// ```
625
2
    pub fn get_size(&self, key: &[u8]) -> Result<usize> {
626
2
        self.validate_key(key)
?0
;
627
628
2
        let 
record1
= self
629
2
            .hash_table
630
2
            .read(key, |_, v| 
v1
.
clone1
())
631
2
            .ok_or(FeoxError::KeyNotFound)
?1
;
632
633
1
        Ok(record.value_len)
634
2
    }
635
636
    // Internal helper methods
637
638
5.36k
    pub(super) fn validate_key_value(&self, key: &[u8], value: &[u8]) -> Result<()> {
639
5.36k
        if key.is_empty() || 
key.len() > MAX_KEY_SIZE5.36k
{
640
2
            return Err(FeoxError::InvalidKeySize);
641
5.36k
        }
642
643
5.36k
        if value.is_empty() || 
value.len() > MAX_VALUE_SIZE5.36k
{
644
1
            return Err(FeoxError::InvalidValueSize);
645
5.36k
        }
646
647
5.36k
        Ok(())
648
5.36k
    }
649
650
4.79k
    pub(super) fn validate_key(&self, key: &[u8]) -> Result<()> {
651
4.79k
        if key.is_empty() || key.len() > MAX_KEY_SIZE {
652
0
            return Err(FeoxError::InvalidKeySize);
653
4.79k
        }
654
655
4.79k
        Ok(())
656
4.79k
    }
657
658
1.59k
    pub(super) fn check_memory_limit(&self, size: usize) -> bool {
659
1.59k
        match self.max_memory {
660
1.59k
            Some(limit) => {
661
1.59k
                let current = self.stats.memory_usage.load(Ordering::Acquire);
662
1.59k
                current + size <= limit
663
            }
664
0
            None => true,
665
        }
666
1.59k
    }
667
668
4.60k
    pub(super) fn calculate_record_size(&self, key_len: usize, value_len: usize) -> usize {
669
4.60k
        std::mem::size_of::<Record>() + key_len + value_len
670
4.60k
    }
671
672
4.34k
    pub(super) fn get_timestamp(&self) -> u64 {
673
4.34k
        self.get_timestamp_pub()
674
4.34k
    }
675
}