@@ -201,11 +201,15 @@ BlockFileCache::BlockFileCache(const std::string& cache_base_path,
201
201
" file_cache_hit_ratio_1h" , 0.0 );
202
202
_disk_limit_mode_metrics = std::make_shared<bvar::Status<size_t >>(
203
203
_cache_base_path.c_str (), " file_cache_disk_limit_mode" , 0 );
204
+ _need_evict_cache_in_advance_metrics = std::make_shared<bvar::Status<size_t >>(
205
+ _cache_base_path.c_str (), " file_cache_need_evict_cache_in_advance" , 0 );
204
206
205
207
_storage_sync_remove_latency = std::make_shared<bvar::LatencyRecorder>(
206
208
_cache_base_path.c_str (), " file_cache_storage_sync_remove_latency_ns" );
207
209
_storage_async_remove_latency = std::make_shared<bvar::LatencyRecorder>(
208
210
_cache_base_path.c_str (), " file_cache_storage_async_remove_latency_ns" );
211
+ _evict_in_advance_latency = std::make_shared<bvar::LatencyRecorder>(
212
+ _cache_base_path.c_str (), " file_cache_evict_in_advance_latency_ns" );
209
213
210
214
_disposable_queue = LRUQueue (cache_settings.disposable_queue_size ,
211
215
cache_settings.disposable_queue_elements , 60 * 60 );
@@ -333,6 +337,8 @@ Status BlockFileCache::initialize_unlocked(std::lock_guard<std::mutex>& cache_lo
333
337
_cache_background_monitor_thread = std::thread (&BlockFileCache::run_background_monitor, this );
334
338
_cache_background_ttl_gc_thread = std::thread (&BlockFileCache::run_background_ttl_gc, this );
335
339
_cache_background_gc_thread = std::thread (&BlockFileCache::run_background_gc, this );
340
+ _cache_background_evict_in_advance_thread =
341
+ std::thread (&BlockFileCache::run_background_evict_in_advance, this );
336
342
337
343
return Status::OK ();
338
344
}
@@ -1011,6 +1017,16 @@ bool BlockFileCache::try_reserve(const UInt128Wrapper& hash, const CacheContext&
1011
1017
return true ;
1012
1018
}
1013
1019
1020
+ void BlockFileCache::try_evict_in_advance (size_t size, std::lock_guard<std::mutex>& cache_lock) {
1021
+ UInt128Wrapper hash;
1022
+ size_t offset = 0 ;
1023
+ CacheContext context;
1024
+ context.cache_type = FileCacheType::NORMAL;
1025
+ try_reserve_for_lru (hash, nullptr , context, offset, size, cache_lock, false );
1026
+ context.cache_type = FileCacheType::TTL;
1027
+ try_reserve_for_lru (hash, nullptr , context, offset, size, cache_lock, false );
1028
+ }
1029
+
1014
1030
bool BlockFileCache::remove_if_ttl_file_blocks (const UInt128Wrapper& file_key, bool remove_directly,
1015
1031
std::lock_guard<std::mutex>& cache_lock, bool sync) {
1016
1032
auto & ttl_queue = get_queue (FileCacheType::TTL);
@@ -1168,7 +1184,7 @@ void BlockFileCache::reset_range(const UInt128Wrapper& hash, size_t offset, size
1168
1184
1169
1185
bool BlockFileCache::try_reserve_from_other_queue_by_time_interval (
1170
1186
FileCacheType cur_type, std::vector<FileCacheType> other_cache_types, size_t size,
1171
- int64_t cur_time, std::lock_guard<std::mutex>& cache_lock) {
1187
+ int64_t cur_time, std::lock_guard<std::mutex>& cache_lock, bool sync_removal ) {
1172
1188
size_t removed_size = 0 ;
1173
1189
size_t cur_cache_size = _cur_cache_size;
1174
1190
std::vector<FileBlockCell*> to_evict;
@@ -1201,7 +1217,7 @@ bool BlockFileCache::try_reserve_from_other_queue_by_time_interval(
1201
1217
}
1202
1218
*(_evict_by_time_metrics_matrix[cache_type][cur_type]) << remove_size_per_type;
1203
1219
}
1204
- remove_file_blocks (to_evict, cache_lock, true );
1220
+ remove_file_blocks (to_evict, cache_lock, sync_removal );
1205
1221
1206
1222
return !is_overflow (removed_size, size, cur_cache_size);
1207
1223
}
@@ -1219,7 +1235,7 @@ bool BlockFileCache::is_overflow(size_t removed_size, size_t need_size,
1219
1235
1220
1236
bool BlockFileCache::try_reserve_from_other_queue_by_size (
1221
1237
FileCacheType cur_type, std::vector<FileCacheType> other_cache_types, size_t size,
1222
- std::lock_guard<std::mutex>& cache_lock) {
1238
+ std::lock_guard<std::mutex>& cache_lock, bool sync_removal ) {
1223
1239
size_t removed_size = 0 ;
1224
1240
size_t cur_cache_size = _cur_cache_size;
1225
1241
std::vector<FileBlockCell*> to_evict;
@@ -1239,17 +1255,18 @@ bool BlockFileCache::try_reserve_from_other_queue_by_size(
1239
1255
cur_removed_size);
1240
1256
*(_evict_by_size_metrics_matrix[cache_type][cur_type]) << cur_removed_size;
1241
1257
}
1242
- remove_file_blocks (to_evict, cache_lock, true );
1258
+ remove_file_blocks (to_evict, cache_lock, sync_removal );
1243
1259
return !is_overflow (removed_size, size, cur_cache_size);
1244
1260
}
1245
1261
1246
1262
bool BlockFileCache::try_reserve_from_other_queue (FileCacheType cur_cache_type, size_t size,
1247
1263
int64_t cur_time,
1248
- std::lock_guard<std::mutex>& cache_lock) {
1264
+ std::lock_guard<std::mutex>& cache_lock,
1265
+ bool sync_removal) {
1249
1266
// currently, TTL cache is not considered as a candidate
1250
1267
auto other_cache_types = get_other_cache_type_without_ttl (cur_cache_type);
1251
1268
bool reserve_success = try_reserve_from_other_queue_by_time_interval (
1252
- cur_cache_type, other_cache_types, size, cur_time, cache_lock);
1269
+ cur_cache_type, other_cache_types, size, cur_time, cache_lock, sync_removal );
1253
1270
if (reserve_success || !config::file_cache_enable_evict_from_other_queue_by_size) {
1254
1271
return reserve_success;
1255
1272
}
@@ -1262,14 +1279,15 @@ bool BlockFileCache::try_reserve_from_other_queue(FileCacheType cur_cache_type,
1262
1279
if (_cur_cache_size + size > _capacity && cur_queue_size + size > cur_queue_max_size) {
1263
1280
return false ;
1264
1281
}
1265
- return try_reserve_from_other_queue_by_size (cur_cache_type, other_cache_types, size,
1266
- cache_lock );
1282
+ return try_reserve_from_other_queue_by_size (cur_cache_type, other_cache_types, size, cache_lock,
1283
+ sync_removal );
1267
1284
}
1268
1285
1269
1286
bool BlockFileCache::try_reserve_for_lru (const UInt128Wrapper& hash,
1270
1287
QueryFileCacheContextPtr query_context,
1271
1288
const CacheContext& context, size_t offset, size_t size,
1272
- std::lock_guard<std::mutex>& cache_lock) {
1289
+ std::lock_guard<std::mutex>& cache_lock,
1290
+ bool sync_removal) {
1273
1291
int64_t cur_time = std::chrono::duration_cast<std::chrono::seconds>(
1274
1292
std::chrono::steady_clock::now ().time_since_epoch ())
1275
1293
.count ();
@@ -1282,7 +1300,7 @@ bool BlockFileCache::try_reserve_for_lru(const UInt128Wrapper& hash,
1282
1300
size_t cur_removed_size = 0 ;
1283
1301
find_evict_candidates (queue, size, cur_cache_size, removed_size, to_evict, cache_lock,
1284
1302
cur_removed_size);
1285
- remove_file_blocks (to_evict, cache_lock, true );
1303
+ remove_file_blocks (to_evict, cache_lock, sync_removal );
1286
1304
*(_evict_by_self_lru_metrics_matrix[context.cache_type ]) << cur_removed_size;
1287
1305
1288
1306
if (is_overflow (removed_size, size, cur_cache_size)) {
@@ -1627,7 +1645,7 @@ void BlockFileCache::check_disk_resource_limit() {
1627
1645
LOG_WARNING (" config error, set to default value" )
1628
1646
.tag (" enter" , config::file_cache_enter_disk_resource_limit_mode_percent)
1629
1647
.tag (" exit" , config::file_cache_exit_disk_resource_limit_mode_percent);
1630
- config::file_cache_enter_disk_resource_limit_mode_percent = 90 ;
1648
+ config::file_cache_enter_disk_resource_limit_mode_percent = 88 ;
1631
1649
config::file_cache_exit_disk_resource_limit_mode_percent = 80 ;
1632
1650
}
1633
1651
if (is_insufficient (space_percentage) || is_insufficient (inode_percentage)) {
@@ -1648,11 +1666,69 @@ void BlockFileCache::check_disk_resource_limit() {
1648
1666
}
1649
1667
}
1650
1668
1669
+ void BlockFileCache::check_need_evict_cache_in_advance () {
1670
+ if (_storage->get_type () != FileCacheStorageType::DISK) {
1671
+ return ;
1672
+ }
1673
+
1674
+ std::pair<int , int > percent;
1675
+ int ret = disk_used_percentage (_cache_base_path, &percent);
1676
+ if (ret != 0 ) {
1677
+ LOG_ERROR (" " ).tag (" file cache path" , _cache_base_path).tag (" error" , strerror (errno));
1678
+ return ;
1679
+ }
1680
+ auto [space_percentage, inode_percentage] = percent;
1681
+ size_t size_percentage = static_cast <size_t >(
1682
+ (static_cast <double >(_cur_cache_size) / static_cast <double >(_capacity)) * 100 );
1683
+ auto is_insufficient = [](const int & percentage) {
1684
+ return percentage >= config::file_cache_enter_need_evict_cache_in_advance_percent;
1685
+ };
1686
+ DCHECK_GE (space_percentage, 0 );
1687
+ DCHECK_LE (space_percentage, 100 );
1688
+ DCHECK_GE (inode_percentage, 0 );
1689
+ DCHECK_LE (inode_percentage, 100 );
1690
+ // ATTN: due to that can be changed dynamically, set it to default value if it's invalid
1691
+ // FIXME: reject with config validator
1692
+ if (config::file_cache_enter_need_evict_cache_in_advance_percent <=
1693
+ config::file_cache_exit_need_evict_cache_in_advance_percent) {
1694
+ LOG_WARNING (" config error, set to default value" )
1695
+ .tag (" enter" , config::file_cache_enter_need_evict_cache_in_advance_percent)
1696
+ .tag (" exit" , config::file_cache_exit_need_evict_cache_in_advance_percent);
1697
+ config::file_cache_enter_need_evict_cache_in_advance_percent = 78 ;
1698
+ config::file_cache_exit_need_evict_cache_in_advance_percent = 75 ;
1699
+ }
1700
+ if (is_insufficient (space_percentage) || is_insufficient (inode_percentage) ||
1701
+ is_insufficient (size_percentage)) {
1702
+ _need_evict_cache_in_advance = true ;
1703
+ _need_evict_cache_in_advance_metrics->set_value (1 );
1704
+ } else if (_need_evict_cache_in_advance &&
1705
+ (space_percentage < config::file_cache_exit_need_evict_cache_in_advance_percent) &&
1706
+ (inode_percentage < config::file_cache_exit_need_evict_cache_in_advance_percent) &&
1707
+ (size_percentage < config::file_cache_exit_need_evict_cache_in_advance_percent)) {
1708
+ _need_evict_cache_in_advance = false ;
1709
+ _need_evict_cache_in_advance_metrics->set_value (0 );
1710
+ }
1711
+ if (_need_evict_cache_in_advance) {
1712
+ LOG (WARNING) << " file_cache=" << get_base_path () << " space_percent=" << space_percentage
1713
+ << " inode_percent=" << inode_percentage << " size_percent=" << size_percentage
1714
+ << " is_space_insufficient=" << is_insufficient (space_percentage)
1715
+ << " is_inode_insufficient=" << is_insufficient (inode_percentage)
1716
+ << " is_size_insufficient=" << is_insufficient (size_percentage)
1717
+ << " need evict cache in advance" ;
1718
+ }
1719
+ }
1720
+
1651
1721
void BlockFileCache::run_background_monitor () {
1652
1722
int64_t interval_time_seconds = 20 ;
1653
1723
while (!_close) {
1654
1724
TEST_SYNC_POINT_CALLBACK (" BlockFileCache::set_sleep_time" , &interval_time_seconds);
1655
1725
check_disk_resource_limit ();
1726
+ if (config::enable_evict_file_cache_in_advance) {
1727
+ check_need_evict_cache_in_advance ();
1728
+ } else {
1729
+ _need_evict_cache_in_advance = false ;
1730
+ }
1731
+
1656
1732
{
1657
1733
std::unique_lock close_lock (_close_mtx);
1658
1734
_close_cv.wait_for (close_lock, std::chrono::seconds (interval_time_seconds));
@@ -1756,6 +1832,32 @@ void BlockFileCache::run_background_gc() {
1756
1832
}
1757
1833
}
1758
1834
1835
+ void BlockFileCache::run_background_evict_in_advance () {
1836
+ int64_t batch = 0 ;
1837
+ while (!_close) {
1838
+ {
1839
+ std::unique_lock close_lock (_close_mtx);
1840
+ _close_cv.wait_for (
1841
+ close_lock,
1842
+ std::chrono::milliseconds (config::file_cache_evict_in_advance_interval_ms));
1843
+ if (_close) {
1844
+ break ;
1845
+ }
1846
+ }
1847
+ batch = config::file_cache_evict_in_advance_batch_bytes;
1848
+ if (!_need_evict_cache_in_advance || _recycle_keys.size_approx () >= (batch * 10 )) {
1849
+ continue ;
1850
+ }
1851
+ int64_t duration_ns = 0 ;
1852
+ {
1853
+ SCOPED_CACHE_LOCK (_mutex);
1854
+ SCOPED_RAW_TIMER (&duration_ns);
1855
+ try_evict_in_advance (batch, cache_lock);
1856
+ }
1857
+ *_evict_in_advance_latency << duration_ns;
1858
+ }
1859
+ }
1860
+
1759
1861
void BlockFileCache::modify_expiration_time (const UInt128Wrapper& hash,
1760
1862
uint64_t new_expiration_time) {
1761
1863
SCOPED_CACHE_LOCK (_mutex);
0 commit comments