Skip to content

Commit 7fe01d6

Browse files
committed
Finalize extension prefixed namespaces
1 parent e46d1b4 commit 7fe01d6

File tree

5 files changed

+191
-3
lines changed

5 files changed

+191
-3
lines changed
+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// Special marker for explicitly storing `None` values in binary storage.
22
pub(crate) const OPTION_TOMBSTONE_MARKER: [u8; 2] = [0xFF, 0xFE];
33

4+
/// Namespaced extension prefixes
45
pub(crate) const OPTION_PREFIX: &[u8] = b"--extension-option--";
56
pub(crate) const TTL_PREFIX: &[u8] = b"--extension-ttl--";

simd-r-drive-extensions/src/storage_cache_ext.rs

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ use simd_r_drive::DataStore;
66
use std::io::{self, ErrorKind};
77
use std::time::{SystemTime, UNIX_EPOCH};
88

9+
#[cfg(any(test, debug_assertions))]
10+
pub const TEST_TTL_PREFIX: &[u8] = TTL_PREFIX;
11+
912
/// # Storage Utilities for Handling Auto-Evicting TTL Entries
1013
///
1114
/// Stores a timestamp (in seconds) before the actual value.

simd-r-drive-extensions/src/storage_option_ext.rs

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use std::io::{self, ErrorKind};
99
#[cfg(any(test, debug_assertions))]
1010
pub const TEST_OPTION_TOMBSTONE_MARKER: [u8; 2] = OPTION_TOMBSTONE_MARKER;
1111

12+
#[cfg(any(test, debug_assertions))]
13+
pub const TEST_OPTION_PREFIX: &[u8] = OPTION_PREFIX;
14+
1215
/// # Storage Utilities for Handling `Option<T>`
1316
///
1417
/// This trait provides methods to store and retrieve `Option<T>` values

simd-r-drive-extensions/tests/storage_cache_tests.rs

+71-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde::{Deserialize, Serialize};
22
use simd_r_drive::DataStore;
3-
use simd_r_drive_extensions::StorageCacheExt;
3+
use simd_r_drive_extensions::{utils::prefix_key, StorageCacheExt, TEST_TTL_PREFIX};
44
use std::io::ErrorKind;
55
use std::thread::sleep;
66
use std::time::Duration;
@@ -253,3 +253,73 @@ fn test_write_and_read_option_with_ttl() {
253253
"Expected refreshed entry before TTL expires"
254254
);
255255
}
256+
257+
#[test]
258+
fn test_ttl_prefix_is_applied() {
259+
let (_dir, storage) = create_temp_storage();
260+
261+
let key = b"test_key";
262+
let prefixed_key = prefix_key(TEST_TTL_PREFIX, key);
263+
let test_value = TestData {
264+
id: 123,
265+
name: "Test Value".to_string(),
266+
};
267+
268+
// Write data with TTL
269+
storage
270+
.write_with_ttl(key, &test_value, 60)
271+
.expect("Failed to write with TTL");
272+
273+
// Ensure the prefixed key exists in storage
274+
let raw_data = storage.read(&prefixed_key);
275+
assert!(
276+
raw_data.is_some(),
277+
"Expected data to be stored under the prefixed key"
278+
);
279+
280+
// Ensure the unprefixed key does not exist
281+
let raw_data_unprefixed = storage.read(key);
282+
assert!(
283+
raw_data_unprefixed.is_none(),
284+
"Unprefixed key should not exist in storage"
285+
);
286+
287+
// Ensure we can read the value correctly
288+
let retrieved = storage
289+
.read_with_ttl::<TestData>(key)
290+
.expect("Failed to read with TTL");
291+
292+
assert_eq!(
293+
retrieved,
294+
Some(test_value),
295+
"Stored and retrieved values do not match"
296+
);
297+
}
298+
299+
#[test]
300+
fn test_ttl_prefixing_does_not_affect_regular_storage() {
301+
let (_dir, storage) = create_temp_storage();
302+
303+
let key = b"test_key";
304+
let test_value = TestData {
305+
id: 999,
306+
name: "Non-TTL Value".to_string(),
307+
};
308+
309+
// Directly write without TTL
310+
storage
311+
.write(key, &bincode::serialize(&test_value).unwrap())
312+
.expect("Failed to write without TTL");
313+
314+
// Ensure reading from TTL-prefixed key fails (since it was not stored with TTL)
315+
let prefixed_key = prefix_key(TEST_TTL_PREFIX, key);
316+
let raw_data_prefixed = storage.read(&prefixed_key);
317+
assert!(
318+
raw_data_prefixed.is_none(),
319+
"No TTL-prefixed entry should exist for a non-TTL write"
320+
);
321+
322+
// Ensure we can still retrieve the non-TTL stored value
323+
let retrieved: TestData = bincode::deserialize(&storage.read(key).unwrap()).unwrap();
324+
assert_eq!(retrieved, test_value, "Non-TTL value should be retrievable");
325+
}

simd-r-drive-extensions/tests/storage_option_tests.rs

+113-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod tests {
33
use serde::{Deserialize, Serialize};
44
use simd_r_drive::DataStore;
55
use simd_r_drive_extensions::{
6-
utils::prefix_key, StorageOptionExt, TEST_OPTION_TOMBSTONE_MARKER,
6+
utils::prefix_key, StorageOptionExt, TEST_OPTION_PREFIX, TEST_OPTION_TOMBSTONE_MARKER,
77
};
88
use std::io::ErrorKind;
99
use tempfile::tempdir;
@@ -107,7 +107,7 @@ mod tests {
107107
);
108108

109109
// Step 4: Ensure the entry still exists in storage (not fully deleted)
110-
let raw_entry = storage.read(&prefix_key(b"--extension-option--", key));
110+
let raw_entry = storage.read(&prefix_key(&TEST_OPTION_PREFIX, key));
111111
assert!(
112112
raw_entry.is_some(),
113113
"Entry should still exist in storage even after writing None"
@@ -214,4 +214,115 @@ mod tests {
214214
);
215215
}
216216
}
217+
218+
#[test]
219+
fn test_option_prefix_is_applied_for_some() {
220+
let (_dir, storage) = create_temp_storage();
221+
222+
let key = b"test_key_option";
223+
let prefixed_key = prefix_key(TEST_OPTION_PREFIX, key);
224+
let test_value = Some(TestData {
225+
id: 456,
226+
name: "Test Option Value".to_string(),
227+
});
228+
229+
// Write `Some(value)` with option handling
230+
storage
231+
.write_option(key, test_value.as_ref())
232+
.expect("Failed to write option");
233+
234+
// Ensure the prefixed key exists in storage
235+
let raw_data = storage.read(&prefixed_key);
236+
assert!(
237+
raw_data.is_some(),
238+
"Expected data to be stored under the prefixed key"
239+
);
240+
241+
// Ensure the unprefixed key does not exist
242+
let raw_data_unprefixed = storage.read(key);
243+
assert!(
244+
raw_data_unprefixed.is_none(),
245+
"Unprefixed key should not exist in storage"
246+
);
247+
248+
// Ensure we can read the value correctly
249+
let retrieved = storage
250+
.read_option::<TestData>(key)
251+
.expect("Failed to read option");
252+
253+
assert_eq!(
254+
retrieved, test_value,
255+
"Stored and retrieved option values do not match"
256+
);
257+
}
258+
259+
#[test]
260+
fn test_option_prefix_is_applied_for_none() {
261+
let (_dir, storage) = create_temp_storage();
262+
263+
let key = b"test_key_none";
264+
let prefixed_key = prefix_key(TEST_OPTION_PREFIX, key);
265+
266+
// Write `None`
267+
storage
268+
.write_option::<TestData>(key, None)
269+
.expect("Failed to write None with option handling");
270+
271+
// Ensure the prefixed key exists in storage (tombstone marker stored)
272+
let raw_data = storage.read(&prefixed_key);
273+
assert!(
274+
raw_data.is_some(),
275+
"Expected tombstone marker to be stored under the prefixed key"
276+
);
277+
278+
// Ensure the unprefixed key does not exist
279+
let raw_data_unprefixed = storage.read(key);
280+
assert!(
281+
raw_data_unprefixed.is_none(),
282+
"Unprefixed key should not exist in storage"
283+
);
284+
285+
// Ensure we can read the value correctly
286+
let retrieved = storage
287+
.read_option::<TestData>(key)
288+
.expect("Failed to read None with option handling");
289+
290+
assert_eq!(
291+
retrieved, None,
292+
"Expected None when retrieving a stored tombstone marker"
293+
);
294+
}
295+
296+
#[test]
297+
fn test_option_prefixing_does_not_affect_regular_storage() {
298+
let (_dir, storage) = create_temp_storage();
299+
300+
let key = b"test_key_option_non_prefixed";
301+
let test_value = TestData {
302+
id: 789,
303+
name: "Non-Prefixed Option Value".to_string(),
304+
};
305+
306+
// Directly write a non-option value to the base storage
307+
storage
308+
.write(key, &bincode::serialize(&test_value).unwrap())
309+
.expect("Failed to write non-option value");
310+
311+
// Ensure reading from the option-prefixed key fails (since it was not stored as an option)
312+
let prefixed_key = prefix_key(TEST_OPTION_PREFIX, key);
313+
let raw_data_prefixed = storage.read(&prefixed_key);
314+
assert!(
315+
raw_data_prefixed.is_none(),
316+
"No option-prefixed entry should exist for a non-prefixed write"
317+
);
318+
319+
// Ensure we can still retrieve the non-prefixed stored value
320+
let raw_bytes = storage.read(key).expect("Failed to read stored data");
321+
let retrieved: TestData =
322+
bincode::deserialize(&raw_bytes).expect("Failed to deserialize TestData");
323+
assert_eq!(
324+
retrieved, test_value,
325+
"Non-prefixed value should be retrievable as a plain `T`"
326+
);
327+
}
217328
}

0 commit comments

Comments
 (0)