@@ -154,6 +154,24 @@ struct AccessConfig {
154154 lock_power : u32 ,
155155}
156156
157+ /// NvmDevice configuration options.
158+ #[ allow( dead_code) ]
159+ enum NvmDevice {
160+ SimpleFile {
161+ file_name : String ,
162+ file_size : u64 ,
163+ truncate : bool ,
164+ } ,
165+ DefaultDevice ,
166+ }
167+
168+ /// NvmCache configuration options.
169+ struct NvmCacheConfig {
170+ block_size : u64 ,
171+ region_size : u32 ,
172+ device : NvmDevice ,
173+ }
174+
157175/// LRU cache configuration options.
158176pub struct LruCacheConfig {
159177 size : usize ,
@@ -164,6 +182,7 @@ pub struct LruCacheConfig {
164182 access_config : Option < AccessConfig > ,
165183 cache_directory : Option < PathBuf > ,
166184 base_address : Option < * mut std:: ffi:: c_void > ,
185+ nvm_cache_config : Option < NvmCacheConfig > ,
167186}
168187
169188impl LruCacheConfig {
@@ -178,9 +197,17 @@ impl LruCacheConfig {
178197 access_config : None ,
179198 cache_directory : None ,
180199 base_address : None ,
200+ nvm_cache_config : None ,
181201 }
182202 }
183203
204+ /// Enable NvmCache with the given configuration.
205+ #[ allow( private_interfaces) ]
206+ pub fn set_nvm_cache_config ( mut self , config : NvmCacheConfig ) -> Self {
207+ self . nvm_cache_config = Some ( config) ;
208+ self
209+ }
210+
184211 /// Enables the container shrinker if running in a supported container runtime.
185212 /// So far, this only works for Facebook internal containers
186213 pub fn set_container_shrinker ( mut self ) -> Self {
@@ -354,6 +381,22 @@ impl LruCache {
354381 ffi:: set_base_address ( cache_config. pin_mut ( ) , addr as usize ) ?;
355382 }
356383
384+ if let Some ( nvm_config) = config. nvm_cache_config {
385+ let mut nvm_cache_config = ffi:: make_nvm_cache_config ( ) ?;
386+ ffi:: set_block_size ( nvm_cache_config. pin_mut ( ) , nvm_config. block_size ) ?;
387+ if let NvmDevice :: SimpleFile {
388+ file_name,
389+ file_size,
390+ truncate,
391+ } = nvm_config. device
392+ {
393+ let_cxx_string ! ( name = file_name) ;
394+ ffi:: set_simple_file ( nvm_cache_config. pin_mut ( ) , & name, file_size, truncate) ?;
395+ }
396+ ffi:: set_region_size ( nvm_cache_config. pin_mut ( ) , nvm_config. region_size ) ?;
397+ ffi:: enable_nvm_cache ( cache_config. pin_mut ( ) , nvm_cache_config. pin_mut ( ) ) ?;
398+ }
399+
357400 if let Some ( cache_directory) = config. cache_directory {
358401 // If cache directory is enabled, create a persistent shared-memory cache.
359402 let_cxx_string ! ( cache_directory = cache_directory. as_os_str( ) . as_bytes( ) ) ;
@@ -1163,12 +1206,13 @@ impl VolatileLruCachePool {
11631206
11641207#[ cfg( test) ]
11651208mod test {
1209+ use tempfile:: NamedTempFile ;
11661210 use tempfile:: TempDir ;
11671211
11681212 use super :: * ;
11691213
1170- fn create_cache ( fb : FacebookInit ) {
1171- let config = LruCacheConfig :: new ( 128 * 1024 * 1024 )
1214+ fn create_basic_cache_config ( ) -> LruCacheConfig {
1215+ LruCacheConfig :: new ( 128 * 1024 * 1024 )
11721216 . set_shrinker ( ShrinkMonitor {
11731217 shrinker_type : ShrinkMonitorType :: ResidentSize {
11741218 max_process_size_gib : 16 ,
@@ -1196,7 +1240,11 @@ mod test {
11961240 age_difference_ratio : 0.1 ,
11971241 min_retained_slabs : 1 ,
11981242 } ,
1199- } ) ;
1243+ } )
1244+ }
1245+
1246+ fn create_cache ( fb : FacebookInit ) {
1247+ let config = create_basic_cache_config ( ) ;
12001248
12011249 if let Err ( e) = init_cache ( fb, config) {
12021250 panic ! ( "{}" , e) ;
@@ -1207,38 +1255,34 @@ mod test {
12071255 TempDir :: with_prefix ( dir_prefix) . expect ( "failed to create temp dir" )
12081256 }
12091257
1258+ fn create_temp_file ( ) -> String {
1259+ NamedTempFile :: new ( )
1260+ . expect ( "Failed to create temporary file in test" )
1261+ . path ( )
1262+ . to_string_lossy ( )
1263+ . to_string ( )
1264+ }
1265+
12101266 fn create_shared_cache ( fb : FacebookInit , cache_directory : PathBuf ) {
1211- let config = LruCacheConfig :: new ( 128 * 1024 * 1024 )
1212- . set_shrinker ( ShrinkMonitor {
1213- shrinker_type : ShrinkMonitorType :: ResidentSize {
1214- max_process_size_gib : 16 ,
1215- min_process_size_gib : 1 ,
1216- } ,
1217- interval : Duration :: new ( 1 , 0 ) ,
1218- max_resize_per_iteration_percent : 10 ,
1219- max_removed_percent : 90 ,
1220- strategy : RebalanceStrategy :: LruTailAge {
1221- age_difference_ratio : 0.1 ,
1222- min_retained_slabs : 1 ,
1223- } ,
1224- } )
1225- . set_pool_resizer ( PoolResizeConfig {
1226- interval : Duration :: new ( 1 , 0 ) ,
1227- slabs_per_iteration : 100 ,
1228- strategy : RebalanceStrategy :: LruTailAge {
1229- age_difference_ratio : 0.1 ,
1230- min_retained_slabs : 1 ,
1231- } ,
1232- } )
1233- . set_cache_dir ( cache_directory)
1234- . set_pool_rebalance ( PoolRebalanceConfig {
1235- interval : Duration :: new ( 1 , 0 ) ,
1236- strategy : RebalanceStrategy :: LruTailAge {
1237- age_difference_ratio : 0.1 ,
1238- min_retained_slabs : 1 ,
1239- } ,
1240- } ) ;
1267+ let mut config = create_basic_cache_config ( ) ;
1268+ config = config. set_cache_dir ( cache_directory) ;
1269+ if let Err ( e) = init_cache ( fb, config) {
1270+ panic ! ( "{}" , e) ;
1271+ }
1272+ }
12411273
1274+ fn create_hybrid_cache ( fb : FacebookInit ) {
1275+ let mut config = create_basic_cache_config ( ) ;
1276+ let nvm_cache_config = NvmCacheConfig {
1277+ block_size : 4096 ,
1278+ region_size : 16 * 1024 * 1024 ,
1279+ device : NvmDevice :: SimpleFile {
1280+ file_name : create_temp_file ( ) ,
1281+ file_size : 18 * 1024 * 1024 ,
1282+ truncate : false ,
1283+ } ,
1284+ } ;
1285+ config = config. set_nvm_cache_config ( nvm_cache_config) ;
12421286 if let Err ( e) = init_cache ( fb, config) {
12431287 panic ! ( "{}" , e) ;
12441288 }
@@ -1257,6 +1301,11 @@ mod test {
12571301 ) ;
12581302 }
12591303
1304+ #[ fbinit:: test]
1305+ fn only_create_hybrid_cache ( fb : FacebookInit ) {
1306+ create_hybrid_cache ( fb) ;
1307+ }
1308+
12601309 #[ fbinit:: test]
12611310 fn set_item ( fb : FacebookInit ) {
12621311 // Insert only, and confirm insert success
@@ -1479,6 +1528,42 @@ mod test {
14791528 Ok ( ( ) )
14801529 }
14811530
1531+ #[ fbinit:: test]
1532+ fn test_hybrid_cache ( fb : FacebookInit ) -> Result < ( ) > {
1533+ create_hybrid_cache ( fb) ;
1534+
1535+ // Test set or replace (since Insert API is not support for HybridCache right now)
1536+ let pool = get_or_create_pool ( "find_pool_by_name" , 24 * 1024 * 1024 ) ?;
1537+ assert ! (
1538+ pool. set_or_replace( b"rimmer" , Bytes :: from( b"I am a fish" . as_ref( ) ) )
1539+ . unwrap( ) ,
1540+ "Set failed"
1541+ ) ;
1542+
1543+ // Fetch an item that doesn't exist
1544+ assert_eq ! (
1545+ pool. get( b"doesnotexist" ) . unwrap( ) ,
1546+ None ,
1547+ "Successfully fetched a bad value"
1548+ ) ;
1549+
1550+ // Test get
1551+ assert_eq ! (
1552+ pool. get( b"rimmer" ) . unwrap( ) ,
1553+ Some ( Bytes :: from( b"I am a fish" . as_ref( ) ) ) ,
1554+ "Fetch failed"
1555+ ) ;
1556+
1557+ // Test update and fetch
1558+ pool. set_or_replace ( b"rimmer" , Bytes :: from ( b"I am a bird" . as_ref ( ) ) ) ?;
1559+ assert_eq ! (
1560+ pool. get( b"rimmer" ) . unwrap( ) ,
1561+ Some ( Bytes :: from( b"I am a bird" . as_ref( ) ) ) ,
1562+ "Fetch failed"
1563+ ) ;
1564+ Ok ( ( ) )
1565+ }
1566+
14821567 #[ fbinit:: test]
14831568 fn ttl ( fb : FacebookInit ) {
14841569 // test ttl in seconds of cache items are set correctly based on the input
0 commit comments