Skip to content

Commit b43e95a

Browse files
xiaoyang-hhhclaude
andauthored
feat(cache): trigger eviction by disk free space on the write path (alibaba#1548)
FileCachePool::updateSpace() previously only forced recycling when the logical watermark was hit (totalUsed_ >= riskMark_). Since totalUsed_ only accounts for this cache's own writes, a shared medium filling up from other writers — or a deployment that wants capacity governed by disk free space rather than the logical watermark — was invisible to the write path and only handled on the next periodic eviction tick. Add a throttled real statvfs probe to the write path. It reuses the existing eviction() disk logic and is throttled by both time (kDiskCheckIntervalUs, via the syscall-free photon::now) and bytes written (diskCheckStepBytes_ = max(diskAvailInBytes_/8, refillUnit_)), keeping statvfs off the hot path on fast media such as tmpfs while bounding overshoot of the diskAvailInBytes_ floor. The probe is skipped entirely when diskAvailInBytes_ == 0, and a failed statvfs never forces eviction. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a80e71b commit b43e95a

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

fs/cache/full_file_cache/cache_pool.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ FileCachePool::FileCachePool(IFileSystem* mediaFs, uint64_t capacityInGB,
5656
waterMark_ = calcWaterMark(capacityInBytes, kMaxFreeSpace);
5757
// keep this relation : waterMark < riskMark < capacity
5858
riskMark_ = std::max(capacityInBytes - kEvictionMark, (static_cast<int64_t>(waterMark_) + capacityInBytes) >> 1);
59+
// Probe disk free space on the write path at most once per this many bytes
60+
// written, bounding how far we may overshoot the diskAvailInBytes_ floor
61+
// between probes. Never smaller than one refill unit.
62+
diskCheckStepBytes_ = std::max<uint64_t>(diskAvailInBytes_ / 8, refillUnit_);
5963
}
6064

6165
FileCachePool::~FileCachePool() {
@@ -234,10 +238,27 @@ void FileCachePool::updateLru(FileNameMap::iterator iter) {
234238
int64_t FileCachePool::updateSpace(FileNameMap::iterator iter, uint64_t size) {
235239
auto lruEntry = iter->second.get();
236240
auto diff = static_cast<int64_t>(size) - static_cast<int64_t>(lruEntry->size);
237-
totalUsed_ += diff;
241+
totalUsed_ += diff;
238242
lruEntry->size = size;
239-
if (totalUsed_ >= riskMark_) {
243+
if (diff > 0) bytesSinceDiskCheck_ += diff;
244+
245+
bool full = totalUsed_ >= riskMark_;
246+
if (full) {
240247
LOG_WARN("pwrite is so heavy, totalUsed:`,riskMark:` || lruEntry->size = `",totalUsed_, riskMark_, lruEntry->size);
248+
} else if (diskAvailInBytes_ > 0 &&
249+
(bytesSinceDiskCheck_ >= diskCheckStepBytes_ ||
250+
photon::now - lastDiskCheckUs_ >= kDiskCheckIntervalUs)) {
251+
// Throttled real statvfs: react to low disk free space even when our
252+
// totalUsed_ is far below riskMark_.
253+
lastDiskCheckUs_ = photon::now;
254+
bytesSinceDiskCheck_ = 0;
255+
full = diskSpaceLow();
256+
if (full) {
257+
LOG_WARN("disk free space below floor `, force recycle", diskAvailInBytes_);
258+
}
259+
}
260+
261+
if (full) {
241262
isFull_ = true;
242263
forceRecycle();
243264
if (lruEntry->size==0) diff = 0;//in some extream condition ,
@@ -246,6 +267,16 @@ int64_t FileCachePool::updateSpace(FileNameMap::iterator iter, uint64_t size) {
246267
return diff;
247268
}
248269

270+
bool FileCachePool::diskSpaceLow() {
271+
struct statvfs stFs = {};
272+
if (mediaFs_->statvfs("/", &stFs) != 0) {
273+
LOG_ERROR("statvfs failed, error code : `", ERRNO());
274+
return false; // don't force eviction on a failed probe
275+
}
276+
uint64_t avail = static_cast<uint64_t>(stFs.f_bavail) * stFs.f_frsize;
277+
return avail < diskAvailInBytes_;
278+
}
279+
249280
uint64_t FileCachePool::timerHandler(void* data) {
250281
auto cur = static_cast<FileCachePool*>(data);
251282
if (cur->running_) {

fs/cache/full_file_cache/cache_pool.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ class FileCachePool : public photon::fs::ICachePool {
135135
virtual void eviction();
136136
uint64_t calcWaterMark(uint64_t capacity, uint64_t maxFreeSpace);
137137

138+
// Throttled real disk-free-space probe used on the write path.
139+
bool diskSpaceLow();
140+
static const uint64_t kDiskCheckIntervalUs = 200'000; // 200ms
141+
138142
photon::fs::IFileSystem *mediaFs_; // owned by current class
139143
uint64_t capacityInGB_;
140144
uint64_t periodInUs_;
@@ -144,6 +148,11 @@ class FileCachePool : public photon::fs::ICachePool {
144148
int64_t riskMark_;
145149
uint64_t waterMark_;
146150

151+
// Write-path disk-check throttle state.
152+
uint64_t diskCheckStepBytes_ = 0; // bytes written between forced probes
153+
uint64_t bytesSinceDiskCheck_ = 0;
154+
uint64_t lastDiskCheckUs_ = 0;
155+
147156
photon::Timer *timer_;
148157
bool running_;
149158
bool exit_;

0 commit comments

Comments
 (0)