From dee51be6c41f6a53d2c2fa33789acd80a8051c0b Mon Sep 17 00:00:00 2001 From: Masaori Koshiba Date: Wed, 10 Jun 2026 13:26:29 +0900 Subject: [PATCH] cache: apply per-volume settings on first start after clear Per-volume tuning (ram_cache, ram_cache_size, ram_cache_cutoff, avg_obj_size, fragment_size) was only copied onto the CacheVol when matching an existing on-disk volume, so volumes created fresh (e.g. the first start after a cache clear) ignored the config until the next restart. Apply the settings in one pass after all CacheVols exist. Extend cache_volume_features to verify the settings take effect via per-volume metrics. --- src/iocore/cache/CacheProcessor.cc | 27 +++++++--- .../cache/cache_volume_features.replay.yaml | 50 +++++++++++++++---- .../cache/cache_volume_features.test.py | 3 +- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/iocore/cache/CacheProcessor.cc b/src/iocore/cache/CacheProcessor.cc index ffff468ef78..b2e874b8dc8 100644 --- a/src/iocore/cache/CacheProcessor.cc +++ b/src/iocore/cache/CacheProcessor.cc @@ -70,6 +70,7 @@ void register_cache_stats(CacheStatsBlock *rsb, const std::string &pre static void cplist_update(); static int create_volume(int volume_number, off_t size_in_blocks, CacheType scheme, CacheVol *cp); static int fillExclusiveDisks(CacheVol *cp); +static void cplist_apply_config_settings(CacheVol *cp, const ConfigVol *config_vol); static size_t DEFAULT_RAM_CACHE_MULTIPLIER = 10; // I.e. 10x 1MB per 1GB of disk. @@ -1093,6 +1094,14 @@ cplist_reconfigure() } } + // Apply per-volume settings after all CacheVols exist; otherwise volumes created on the first + // start after a cache clear keep defaults and ignore the config until the next restart. + for (ConfigVol *config_vol = config_volumes.cp_queue.head; config_vol; config_vol = config_vol->link.next) { + if (config_vol->cachep) { + cplist_apply_config_settings(config_vol->cachep, config_vol); + } + } + ts::Metrics::Gauge::store(cache_rsb.stripes, gnstripes); return 0; @@ -1209,6 +1218,17 @@ register_cache_stats(CacheStatsBlock *rsb, const std::string &prefix) rsb->writer_lock_contention = ts::Metrics::Counter::createPtr(prefix + ".writer.lock_contention"); } +// Copy the per-volume tuning fields from the volume config onto the CacheVol. +void +cplist_apply_config_settings(CacheVol *cp, const ConfigVol *config_vol) +{ + cp->ramcache_enabled = config_vol->ramcache_enabled; + cp->avg_obj_size = config_vol->avg_obj_size; + cp->fragment_size = config_vol->fragment_size; + cp->ram_cache_size = config_vol->ram_cache_size; + cp->ram_cache_cutoff = config_vol->ram_cache_cutoff; +} + void cplist_update() { @@ -1220,12 +1240,7 @@ cplist_update() for (config_vol = config_volumes.cp_queue.head; config_vol; config_vol = config_vol->link.next) { if (config_vol->number == cp->vol_number) { if (cp->scheme == config_vol->scheme) { - cp->ramcache_enabled = config_vol->ramcache_enabled; - cp->avg_obj_size = config_vol->avg_obj_size; - cp->fragment_size = config_vol->fragment_size; - cp->ram_cache_size = config_vol->ram_cache_size; - cp->ram_cache_cutoff = config_vol->ram_cache_cutoff; - config_vol->cachep = cp; + config_vol->cachep = cp; } else { /* delete this volume from all the disks */ int d_no; diff --git a/tests/gold_tests/cache/cache_volume_features.replay.yaml b/tests/gold_tests/cache/cache_volume_features.replay.yaml index 3ea0f5a3425..cb69a36ff59 100644 --- a/tests/gold_tests/cache/cache_volume_features.replay.yaml +++ b/tests/gold_tests/cache/cache_volume_features.replay.yaml @@ -18,20 +18,32 @@ meta: version: "1.0" # This test proves that multi-parameter storage.yaml volume entries are parsed -# correctly. The generated storage.yaml contains: +# correctly AND that the per-volume settings actually take effect on the first +# start after a cache clear (AuTest always starts ATS against a fresh cache). +# The generated storage.yaml contains: # # cache: # volumes: # - id: 1 # scheme: http -# size: 50% +# size: 128M # ram_cache_size: 32M # ram_cache_cutoff: 8K +# avg_obj_size: 2000 +# fragment_size: 1M # - id: 2 # scheme: http -# size: 50% +# size: 128M +# ram_cache: false # -# All volume parameters must be parsed correctly from the YAML format. +# All volume parameters must be parsed correctly from the YAML format, and the +# per-volume settings are verified via the metric_checks below: +# - Volume 1 (ram_cache_size: 32M, single 128MB stripe) -> ram_cache total_bytes == 32MB +# - Volume 1 (avg_obj_size: 2000) -> direntries.total == 66424 +# - Volume 2 (ram_cache: false) -> ram_cache total_bytes == 0 +# These exercise two distinct downstream paths (the RAM cache sizing in +# cacheInitialized() and the directory sizing in the StripeSM constructor). On a +# build that ignores per-volume settings on first start, none of them match. autest: description: "Test cache volume features: per-volume RAM cache and @volume= directive" @@ -60,18 +72,38 @@ autest: spans: - name: "disk-1" path: "storage" - size: "256M" + size: "512M" volumes: - # Volume 1 with all new parameters + # Volume 1 with all new parameters (one 128MB stripe for an exact RAM cache size). - id: 1 scheme: "http" - size: "50%" + size: "128M" ram_cache_size: "32M" ram_cache_cutoff: "8K" - # Volume 2 — simple volume, no extra params + avg_obj_size: "2000" + fragment_size: "1M" + # Volume 2 — RAM cache explicitly disabled. - id: 2 scheme: "http" - size: "50%" + size: "128M" + ram_cache: false + + metric_checks: + # avg_obj_size=2000 on volume 1 sizes the directory with more entries than the default + # min_average_object_size (8000) would. This exercises the avg_obj_size -> StripeSM + # directory-sizing path, which is distinct from the RAM cache path below. The value is a + # deterministic function of stripe length (one 128MB stripe) and avg_obj_size; on a build + # that ignores the per-volume setting on first start the directory uses the default size. + - metric: "proxy.process.cache.volume_1.direntries.total" + value: 66424 + # ram_cache_size=32M on volume 1 (single 128MB stripe) -> exactly 32 * 1024 * 1024 bytes. + # On first start after a cache clear this only holds if the per-volume setting is applied + # to the freshly created CacheVol rather than ignored until the next restart. + - metric: "proxy.process.cache.volume_1.ram_cache.total_bytes" + value: 33554432 + # ramcache=false on volume 2 means no RAM cache is allocated for it. + - metric: "proxy.process.cache.volume_2.ram_cache.total_bytes" + value: 0 log_validation: traffic_out: diff --git a/tests/gold_tests/cache/cache_volume_features.test.py b/tests/gold_tests/cache/cache_volume_features.test.py index b6aa8ec13d6..8b6c198114e 100644 --- a/tests/gold_tests/cache/cache_volume_features.test.py +++ b/tests/gold_tests/cache/cache_volume_features.test.py @@ -16,7 +16,8 @@ Test.Summary = ''' Comprehensive test suite for cache volume features: -- Per-volume RAM cache configuration (ram_cache_size, ram_cache_cutoff) +- Per-volume RAM cache configuration (ram_cache, ram_cache_size, ram_cache_cutoff) +- Per-volume settings take effect on the first start after a cache clear - @volume= directive in remap.config for volume selection - Integration between both features '''