Skip to content

Commit e20ca6d

Browse files
authored
Merge pull request #2
Add TTL support and refactor store architecture
2 parents 33191e5 + dc1e4d2 commit e20ca6d

39 files changed

+3545
-2231
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "feoxdb"
3-
version = "0.1.3"
3+
version = "0.2.0"
44
edition = "2021"
55
authors = ["Mehran Toosi <dev@mehran.dk>"]
66
description = "Iron-oxide fast embedded database - nanosecond-level key-value storage"

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,38 @@ fn main() -> feoxdb::Result<()> {
135135
.max_memory(2_000_000_000) // 2GB limit
136136
.enable_caching(true) // Enable CLOCK cache
137137
.hash_bits(20) // 1M hash buckets
138+
.enable_ttl(true) // Enable TTL support
138139
.build()?;
139140

140141
Ok(())
141142
}
142143
```
143144

145+
### Time-To-Live (TTL) Support
146+
147+
```rust
148+
use feoxdb::FeoxStore;
149+
150+
// Enable TTL feature via builder
151+
let store = FeoxStore::builder()
152+
.enable_ttl(true)
153+
.build()?;
154+
155+
// Set key to expire after 60 seconds
156+
store.insert_with_ttl(b"session:123", b"session_data", 60)?;
157+
158+
// Check remaining TTL
159+
if let Some(ttl) = store.get_ttl(b"session:123")? {
160+
println!("Session expires in {} seconds", ttl);
161+
}
162+
163+
// Extend TTL to 120 seconds
164+
store.update_ttl(b"session:123", 120)?;
165+
166+
// Remove TTL (make permanent)
167+
store.persist(b"session:123")?;
168+
```
169+
144170
### Concurrent Access
145171

146172
```rust

src/core/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod cache;
22
pub mod record;
33
pub mod store;
4+
pub mod ttl_sweep;

src/core/record.rs

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,24 @@ use crate::constants::*;
88
#[repr(C)]
99
#[derive(Debug)]
1010
pub struct Record {
11-
// Hash table link for intrusive list
12-
pub hash_link: AtomicLink,
13-
14-
// Key and value data
15-
pub key: Vec<u8>,
16-
pub key_len: u16,
17-
pub value: parking_lot::RwLock<Option<Bytes>>, // None when value is on disk
18-
pub value_len: usize,
19-
20-
// Metadata
21-
pub timestamp: u64,
22-
pub sector: AtomicU64, // Disk sector location (0 means in-memory only)
23-
24-
// Reference counting for RCU-style access
25-
pub refcount: AtomicU32,
26-
27-
// Cache metadata
28-
pub cache_ref_bit: AtomicU32,
29-
pub cache_access_time: AtomicU64,
11+
// Cache line 1 (64 bytes) - GET hot path
12+
// These 3 fields are accessed together on EVERY GET operation
13+
pub key: Vec<u8>, // 24 bytes
14+
pub value: parking_lot::RwLock<Option<Bytes>>, // ~40 bytes (32 + Option overhead)
15+
16+
// Cache line 2 (64 bytes) - TTL and metadata
17+
pub ttl_expiry: AtomicU64, // 8 bytes - checked on every GET
18+
pub timestamp: u64, // 8 bytes - checked on updates
19+
pub value_len: usize, // 8 bytes - used for size calcs
20+
pub sector: AtomicU64, // 8 bytes - for persistence
21+
pub refcount: AtomicU32, // 4 bytes - memory management
22+
pub key_len: u16, // 2 bytes - used with value_len
23+
// Total so far: 40 bytes
24+
25+
// Still in cache line 2 or start of line 3 - cold fields
26+
pub hash_link: AtomicLink, // 8 bytes - only for hash ops
27+
pub cache_ref_bit: AtomicU32, // 4 bytes - rarely used
28+
pub cache_access_time: AtomicU64, // 8 bytes - rarely used
3029
}
3130

3231
// Custom atomic link for lock-free hash table
@@ -91,14 +90,20 @@ impl Record {
9190
let value_bytes = Bytes::from(value);
9291

9392
Self {
94-
hash_link: AtomicLink::new(),
95-
key_len,
93+
// Cache line 1 - GET hot path
9694
key,
9795
value: parking_lot::RwLock::new(Some(value_bytes)),
98-
value_len,
96+
97+
// Cache line 2 - TTL and metadata
98+
ttl_expiry: AtomicU64::new(0),
9999
timestamp,
100+
value_len,
100101
sector: AtomicU64::new(0),
101102
refcount: AtomicU32::new(1),
103+
key_len,
104+
105+
// Cold fields
106+
hash_link: AtomicLink::new(),
102107
cache_ref_bit: AtomicU32::new(0),
103108
cache_access_time: AtomicU64::new(0),
104109
}
@@ -108,6 +113,17 @@ impl Record {
108113
Self::new(key, value, timestamp)
109114
}
110115

116+
pub fn new_with_timestamp_ttl(
117+
key: Vec<u8>,
118+
value: Vec<u8>,
119+
timestamp: u64,
120+
ttl_expiry: u64,
121+
) -> Self {
122+
let record = Self::new(key, value, timestamp);
123+
record.ttl_expiry.store(ttl_expiry, Ordering::Release);
124+
record
125+
}
126+
111127
pub fn calculate_size(&self) -> usize {
112128
mem::size_of::<Self>() + self.key.capacity() + self.value_len
113129
}

0 commit comments

Comments
 (0)