Skip to content

Commit 98210ec

Browse files
RajivTSfacebook-github-bot
authored andcommitted
Add rust bindings for HybridMode cachelib
Summary: Cachelib has [`HybridMode`](https://staticdocs.internalfb.com/cachelib/docs/Cache_Library_User_Guides/HybridCache) where you can make use of DRAM as the primary cache and NVM flash storage as the secondary layer of cache. In Source Control, we rely heavily on caching yet do not take advantage of the disk space available on all our hosts. This diff adds the necessary Cxx bindings to make the HybridMode cache availble for rust clients. Differential Revision: D71834889 fbshipit-source-id: 76fb33acbabb1ad35ab28e4953928c3e5f4e76a4
1 parent 856e97b commit 98210ec

4 files changed

Lines changed: 175 additions & 33 deletions

File tree

cachelib/rust/src/cachelib.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ std::unique_ptr<LruAllocator> make_shm_lru_allocator(
4747
std::unique_ptr<LruAllocatorConfig> make_lru_allocator_config() {
4848
return std::make_unique<LruAllocatorConfig>();
4949
}
50+
51+
std::unique_ptr<NvmCacheConfig> make_nvm_cache_config() {
52+
return std::make_unique<NvmCacheConfig>();
53+
}
54+
5055
std::shared_ptr<facebook::cachelib::RebalanceStrategy>
5156
make_hits_per_slab_rebalancer(double diff_ratio,
5257
unsigned int min_retained_slabs,
@@ -130,6 +135,25 @@ void set_base_address(LruAllocatorConfig& config, size_t addr) {
130135
config.slabMemoryBaseAddr = (void*)addr;
131136
}
132137

138+
void enable_nvm_cache(LruAllocatorConfig& config, NvmCacheConfig& nvmConfig) {
139+
config.enableNvmCache(nvmConfig);
140+
}
141+
142+
void set_block_size(NvmCacheConfig& config, uint64_t blockSize) {
143+
config.navyConfig.setBlockSize(blockSize);
144+
}
145+
146+
void set_simple_file(NvmCacheConfig& config,
147+
const std::string& fileName,
148+
uint64_t fileSize,
149+
bool truncateFile) {
150+
config.navyConfig.setSimpleFile(fileName, fileSize, truncateFile);
151+
}
152+
153+
void set_region_size(NvmCacheConfig& config, uint32_t regionSize) {
154+
config.navyConfig.blockCache().setRegionSize(regionSize);
155+
}
156+
133157
int8_t add_pool(const LruAllocator& cache,
134158
folly::StringPiece name,
135159
size_t size) {

cachelib/rust/src/cachelib.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace cachelib {
2929
using LruAllocator = facebook::cachelib::LruAllocator;
3030
using LruAllocatorConfig = LruAllocator::Config;
3131
using LruItemHandle = LruAllocator::WriteHandle;
32+
using NvmCacheConfig = LruAllocator::NvmCacheConfig;
3233

3334
std::unique_ptr<facebook::cachelib::CacheAdmin> make_cacheadmin(
3435
LruAllocator& cache, const std::string& oncall);
@@ -38,8 +39,12 @@ std::unique_ptr<LruAllocator> make_shm_lru_allocator(
3839
std::unique_ptr<LruAllocatorConfig> config);
3940
std::unique_ptr<LruAllocatorConfig> make_lru_allocator_config();
4041

42+
std::unique_ptr<NvmCacheConfig> make_nvm_cache_config();
43+
4144
bool enable_container_memory_monitor(LruAllocatorConfig& config);
4245

46+
void enable_nvm_cache(LruAllocatorConfig& config, NvmCacheConfig& nvmConfig);
47+
4348
std::shared_ptr<facebook::cachelib::RebalanceStrategy>
4449
make_hits_per_slab_rebalancer(double diff_ratio,
4550
unsigned int min_retained_slabs,
@@ -84,6 +89,15 @@ void enable_cache_persistence(LruAllocatorConfig& config,
8489

8590
void set_base_address(LruAllocatorConfig& config, size_t addr);
8691

92+
void set_block_size(NvmCacheConfig& config, uint64_t blockSize);
93+
94+
void set_simple_file(NvmCacheConfig& config,
95+
const std::string& fileName,
96+
uint64_t fileSize,
97+
bool truncateFile);
98+
99+
void set_region_size(NvmCacheConfig& config, uint32_t regionSize);
100+
87101
int8_t add_pool(const LruAllocator& cache,
88102
folly::StringPiece name,
89103
size_t size);

cachelib/rust/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ mod ffi {
5555
config: UniquePtr<LruAllocatorConfig>,
5656
) -> Result<UniquePtr<LruAllocator>>;
5757

58+
type NvmCacheConfig;
59+
fn make_nvm_cache_config() -> Result<UniquePtr<NvmCacheConfig>>;
60+
5861
type LruAllocatorConfig;
5962
fn make_lru_allocator_config() -> Result<UniquePtr<LruAllocatorConfig>>;
6063
fn setCacheSize(
@@ -75,6 +78,11 @@ mod ffi {
7578
min_retained_slabs: u32,
7679
) -> Result<SharedPtr<RebalanceStrategy>>;
7780

81+
fn enable_nvm_cache(
82+
config: Pin<&mut LruAllocatorConfig>,
83+
nvm_config: Pin<&mut NvmCacheConfig>,
84+
) -> Result<()>;
85+
7886
fn enable_free_memory_monitor(
7987
config: Pin<&mut LruAllocatorConfig>,
8088
interval: milliseconds,
@@ -123,6 +131,17 @@ mod ffi {
123131
directory: Pin<&mut CxxString>,
124132
);
125133

134+
fn set_block_size(config: Pin<&mut NvmCacheConfig>, size: u64) -> Result<()>;
135+
136+
fn set_simple_file(
137+
config: Pin<&mut NvmCacheConfig>,
138+
file_name: &CxxString,
139+
file_size: u64,
140+
truncate_file: bool,
141+
) -> Result<()>;
142+
143+
fn set_region_size(config: Pin<&mut NvmCacheConfig>, region_size: u32) -> Result<()>;
144+
126145
fn add_pool(cache: &LruAllocator, name: StringPiece<'_>, size: usize) -> Result<i8>;
127146
fn get_unreserved_size(cache: &LruAllocator) -> usize;
128147

cachelib/rust/src/lrucache.rs

Lines changed: 118 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
158176
pub 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

169188
impl 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)]
11651208
mod 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

Comments
 (0)