ri/c/cache.rs
1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//! http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Cache Module C API
19//!
20//! This module provides C language bindings for Ri's caching subsystem. The cache module
21//! delivers high-performance in-memory data caching capabilities for accelerating application
22//! performance, reducing database load, and improving system throughput. This C API enables
23//! C/C++ applications to leverage Ri's sophisticated caching infrastructure including memory
24//! caching, distributed caching support, and intelligent cache eviction policies.
25//!
26//! ## Module Architecture
27//!
28//! The caching module comprises three primary components:
29//!
30//! - **RiCacheConfig**: Configuration container for cache system parameters. Controls cache
31//! size limits, eviction policies, expiration timeouts, and connection settings for
32//! distributed cache backends. The configuration object is essential for initializing
33//! cache managers with appropriate resource limits and behavior characteristics.
34//!
35//! - **RiCacheManager**: Central cache management interface providing unified operations
36//! across different cache backends. Handles cache lifecycle, backend selection, and
37//! provides high-level cache operations including get, set, delete, and invalidation.
38//! The cache manager supports automatic serialization of complex types and provides
39//! consistent API regardless of underlying storage implementation.
40//!
41//! - **RiMemoryCache**: In-memory cache implementation using concurrent data structures.
42//! Provides thread-safe caching with O(1) average-case operations for read and write.
43//! The memory cache implements sophisticated eviction policies to manage memory usage
44//! and prevent unbounded growth. Ideal for single-instance deployments or as a
45//! local cache tier in multi-level caching architectures.
46//!
47//! ## Cache Strategies
48//!
49//! The caching system implements multiple strategies optimized for different use cases:
50//!
51//! - **LRU (Least Recently Used)**: Evicts least recently accessed items when capacity
52//! is reached. Optimal for workloads with temporal locality where recently accessed
53//! items are likely to be accessed again. Memory-efficient implementation using
54//! linked hash map for O(1) access and eviction.
55//!
56//! - **LFU (Least Frequently Used)**: Evicts items with lowest access frequency.
57//! Suitable for workloads where access frequency correlates with importance.
58//! Maintains frequency counters for eviction decisions. More computationally
59//! expensive than LRU but provides better hit rates for certain access patterns.
60//!
61//! - **TTL-Based Expiration**: Automatic expiration based on time-to-live values.
62//! Each cache entry has associated expiration timestamp. Entries are lazily
63//! removed during access or via background cleanup tasks. Ensures data freshness
64//! for time-sensitive cached content.
65//!
66//! - **Write-Through/Write-Behind**: Cache synchronization strategies for persistent
67//! backends. Write-through updates cache and backend simultaneously. Write-behind
68//! queues writes for batch processing improving write throughput.
69//!
70//! ## Memory Management
71//!
72//! All C API objects use opaque pointers with manual memory management responsibilities:
73//!
74//! - Objects must be allocated using constructor functions
75//! - Destructor functions must be called to release memory
76//! - Null pointer checks required before all operations
77//! - Double-free prevention is caller's responsibility
78//!
79//! ## Thread Safety
80//!
81//! All underlying implementations provide thread-safe concurrent access:
82//!
83//! - Memory cache uses fine-grained locking or lock-free data structures
84//! - Operations achieve high throughput under concurrent load
85//! - C API itself requires external synchronization for multi-threaded access
86//!
87//! ## Performance Characteristics
88//!
89//! Cache operations have the following performance profiles:
90//!
91//! - Cache hit (memory): O(1) average, O(n) worst case for hash collisions
92//! - Cache miss: O(1) plus backend fetch latency
93//! - Cache write: O(1) amortized
94//! - Eviction: O(1) for LRU, O(log n) for LFU
95//!
96//! ## Integration with Distributed Systems
97//!
98//! The cache module supports integration with distributed cache backends:
99//!
100//! - Redis cluster support for horizontal scaling
101//! - Memcached protocol compatibility
102//! - Consistent hashing for distribution
103//! - Automatic failover and replication
104//!
105//! ## Usage Example
106//!
107//! ```c
108//! // Create cache configuration
109//! RiCacheConfig* config = ri_cache_config_new();
110//! ri_cache_config_set_max_size(config, 10000);
111//! ri_cache_config_set_ttl(config, 3600);
112//!
113//! // Create memory cache instance
114//! RiMemoryCache* cache = ri_memory_cache_new();
115//!
116//! // Store cached value
117//! const char* key = "user:12345";
118//! const char* value = "{\"name\":\"John\",\"age\":30}";
119//! ri_memory_cache_set(cache, key, value, strlen(value));
120//!
121//! // Retrieve cached value
122//! size_t value_len;
123//! char* cached = ri_memory_cache_get(cache, key, &value_len);
124//! if (cached != NULL) {
125//! // Process cached data
126//! free(cached);
127//! }
128//!
129//! // Cleanup
130//! ri_memory_cache_free(cache);
131//! ri_cache_config_free(config);
132//! ```
133//!
134//! ## Dependencies
135//!
136//! This module depends on the following Ri components:
137//!
138//! - `crate::cache`: Rust cache implementation
139//! - `crate::prelude`: Common types and traits
140//!
141//! ## Feature Flags
142//!
143//! The cache module is enabled by default with the "cache" feature flag.
144//! Disable this feature to reduce binary size when caching is not required.
145
146use crate::cache::{RiCacheConfig, RiCacheManager, RiMemoryCache, RiCachePolicy, RiCacheStats};
147use std::ffi::{c_char, c_int};
148use std::sync::Arc;
149
150c_wrapper!(CRiCacheConfig, RiCacheConfig);
151
152c_wrapper!(CRiCacheManager, RiCacheManager);
153
154c_wrapper!(CRiMemoryCache, RiMemoryCache);
155
156c_constructor!(ri_cache_config_new, CRiCacheConfig, RiCacheConfig, RiCacheConfig::default());
157
158c_destructor!(ri_cache_config_free, CRiCacheConfig);
159
160#[repr(C)]
161pub struct CRiCachePolicy {
162 pub ttl_secs: u64,
163 pub ttl_set: bool,
164 pub refresh_on_access: bool,
165 pub max_size: usize,
166 pub max_size_set: bool,
167}
168
169pub const RI_CACHE_POLICY_LRU: c_int = 0;
170pub const RI_CACHE_POLICY_LFU: c_int = 1;
171pub const RI_CACHE_POLICY_TTL: c_int = 2;
172
173#[no_mangle]
174pub extern "C" fn ri_cache_policy_new() -> CRiCachePolicy {
175 let default = RiCachePolicy::default();
176 CRiCachePolicy {
177 ttl_secs: default.ttl.map(|d| d.as_secs()).unwrap_or(0),
178 ttl_set: default.ttl.is_some(),
179 refresh_on_access: default.refresh_on_access,
180 max_size: default.max_size.unwrap_or(0),
181 max_size_set: default.max_size.is_some(),
182 }
183}
184
185#[no_mangle]
186pub extern "C" fn ri_cache_policy_with_ttl(ttl_secs: u64) -> CRiCachePolicy {
187 let mut policy = ri_cache_policy_new();
188 policy.ttl_secs = ttl_secs;
189 policy.ttl_set = true;
190 policy
191}
192
193#[no_mangle]
194pub extern "C" fn ri_memory_cache_new() -> *mut CRiMemoryCache {
195 let cache = RiMemoryCache::new();
196 Box::into_raw(Box::new(CRiMemoryCache::new(cache)))
197}
198
199c_destructor!(ri_memory_cache_free, CRiMemoryCache);
200
201#[no_mangle]
202pub extern "C" fn ri_cache_manager_new() -> *mut CRiCacheManager {
203 let backend: Arc<dyn crate::cache::RiCache + Send + Sync> = Arc::new(RiMemoryCache::new());
204 let manager = RiCacheManager::new(backend);
205 Box::into_raw(Box::new(CRiCacheManager::new(manager)))
206}
207
208#[no_mangle]
209pub extern "C" fn ri_cache_manager_free(manager: *mut CRiCacheManager) {
210 if !manager.is_null() {
211 unsafe {
212 let _ = Box::from_raw(manager);
213 }
214 }
215}
216
217#[no_mangle]
218pub extern "C" fn ri_cache_manager_get(
219 manager: *mut CRiCacheManager,
220 key: *const c_char,
221 out_value: *mut *mut c_char,
222) -> c_int {
223 if manager.is_null() || key.is_null() || out_value.is_null() {
224 return -1;
225 }
226
227 unsafe {
228 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
229 Ok(s) => s,
230 Err(_) => return -2,
231 };
232
233 let rt = match tokio::runtime::Runtime::new() {
234 Ok(r) => r,
235 Err(_) => return -3,
236 };
237
238 let result: crate::core::RiResult<Option<String>> = rt.block_on(async {
239 (*manager).inner.get(key_str).await
240 });
241
242 match result {
243 Ok(Some(value)) => {
244 match std::ffi::CString::new(value) {
245 Ok(c_str) => {
246 *out_value = c_str.into_raw();
247 0
248 }
249 Err(_) => -4,
250 }
251 }
252 Ok(None) => 1,
253 Err(_) => -5,
254 }
255 }
256}
257
258#[no_mangle]
259pub extern "C" fn ri_cache_manager_set(
260 manager: *mut CRiCacheManager,
261 key: *const c_char,
262 value: *const c_char,
263 ttl_secs: u64,
264) -> c_int {
265 if manager.is_null() || key.is_null() || value.is_null() {
266 return -1;
267 }
268
269 unsafe {
270 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
271 Ok(s) => s,
272 Err(_) => return -2,
273 };
274
275 let value_str = match std::ffi::CStr::from_ptr(value).to_str() {
276 Ok(s) => s,
277 Err(_) => return -3,
278 };
279
280 let ttl = if ttl_secs > 0 { Some(ttl_secs) } else { None };
281
282 let rt = match tokio::runtime::Runtime::new() {
283 Ok(r) => r,
284 Err(_) => return -4,
285 };
286
287 let result: crate::core::RiResult<()> = rt.block_on(async {
288 (*manager).inner.set(key_str, &value_str, ttl).await
289 });
290
291 match result {
292 Ok(()) => 0,
293 Err(_) => -5,
294 }
295 }
296}
297
298#[no_mangle]
299pub extern "C" fn ri_cache_manager_delete(
300 manager: *mut CRiCacheManager,
301 key: *const c_char,
302) -> c_int {
303 if manager.is_null() || key.is_null() {
304 return -1;
305 }
306
307 unsafe {
308 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
309 Ok(s) => s,
310 Err(_) => return -2,
311 };
312
313 let rt = match tokio::runtime::Runtime::new() {
314 Ok(r) => r,
315 Err(_) => return -3,
316 };
317
318 let result: crate::core::RiResult<bool> = rt.block_on(async {
319 (*manager).inner.delete(key_str).await
320 });
321
322 match result {
323 Ok(deleted) => if deleted { 0 } else { 1 },
324 Err(_) => -4,
325 }
326 }
327}
328
329#[repr(C)]
330pub struct CRiCacheStats {
331 pub hits: u64,
332 pub misses: u64,
333 pub entries: usize,
334 pub memory_usage_bytes: usize,
335 pub avg_hit_rate: f64,
336 pub hit_count: u64,
337 pub miss_count: u64,
338 pub eviction_count: u64,
339}
340
341#[no_mangle]
342pub extern "C" fn ri_cache_manager_stats(
343 manager: *mut CRiCacheManager,
344 out_stats: *mut CRiCacheStats,
345) -> c_int {
346 if manager.is_null() || out_stats.is_null() {
347 return -1;
348 }
349
350 unsafe {
351 let rt = match tokio::runtime::Runtime::new() {
352 Ok(r) => r,
353 Err(_) => return -2,
354 };
355
356 let stats: RiCacheStats = rt.block_on(async {
357 (*manager).inner.stats().await
358 });
359
360 *out_stats = CRiCacheStats {
361 hits: stats.hits,
362 misses: stats.misses,
363 entries: stats.entries,
364 memory_usage_bytes: stats.memory_usage_bytes,
365 avg_hit_rate: stats.avg_hit_rate,
366 hit_count: stats.hit_count,
367 miss_count: stats.miss_count,
368 eviction_count: stats.eviction_count,
369 };
370
371 0
372 }
373}
374
375#[no_mangle]
376pub extern "C" fn ri_cache_manager_exists(
377 manager: *mut CRiCacheManager,
378 key: *const c_char,
379) -> c_int {
380 if manager.is_null() || key.is_null() {
381 return -1;
382 }
383
384 unsafe {
385 let key_str = match std::ffi::CStr::from_ptr(key).to_str() {
386 Ok(s) => s,
387 Err(_) => return -2,
388 };
389
390 let rt = match tokio::runtime::Runtime::new() {
391 Ok(r) => r,
392 Err(_) => return -3,
393 };
394
395 let exists: bool = rt.block_on(async {
396 (*manager).inner.exists(key_str).await
397 });
398
399 if exists { 0 } else { 1 }
400 }
401}
402
403#[no_mangle]
404pub extern "C" fn ri_cache_manager_clear(manager: *mut CRiCacheManager) -> c_int {
405 if manager.is_null() {
406 return -1;
407 }
408
409 unsafe {
410 let rt = match tokio::runtime::Runtime::new() {
411 Ok(r) => r,
412 Err(_) => return -2,
413 };
414
415 let result: crate::core::RiResult<()> = rt.block_on(async {
416 (*manager).inner.clear().await
417 });
418
419 match result {
420 Ok(()) => 0,
421 Err(_) => -3,
422 }
423 }
424}