Skip to content

Commit 766cbdd

Browse files
committed
First cut at new volume configs for RAM cache
1 parent b38147f commit 766cbdd

File tree

7 files changed

+230
-33
lines changed

7 files changed

+230
-33
lines changed

configs/volume.config.default

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,37 @@
2929
# disk (assuming each disk has enough free space available).
3030
#
3131
# To create one volume of size 10% of the total cache space and
32-
# another 1 Gig volume,
32+
# another 1 Gig volume,
3333
# volume=1 scheme=http size=10%
3434
# volume=2 scheme=http size=1024
35+
#
36+
# Additional optional parameters:
37+
#
38+
# ramcache=true/false
39+
# Enable or disable RAM cache for this volume (default: true)
40+
#
41+
# ram_cache_size=<size>
42+
# Allocate a dedicated RAM cache pool for this volume (e.g., 512M, 2G)
43+
# This amount is automatically subtracted from the global ram_cache.size
44+
# setting, with the remainder shared among other volumes.
45+
#
46+
# ram_cache_cutoff=<size>
47+
# Override the global ram_cache_cutoff for this volume (e.g., 64K, 1M)
48+
# Objects larger than this will not be stored in RAM cache.
49+
#
50+
# avg_obj_size=<size>
51+
# Override the global min_average_object_size for this volume
52+
#
53+
# fragment_size=<size>
54+
# Override the global target_fragment_size for this volume (max: 4MB)
55+
#
56+
# Advanced RAM cache configuration examples:
57+
#
58+
# Example 1: Volume with dedicated 2GB RAM cache
59+
# volume=1 scheme=http size=40% ram_cache_size=2G
60+
#
61+
# Example 2: Small objects with custom cutoff and dedicated RAM
62+
# volume=2 scheme=http size=20% ram_cache_size=512M ram_cache_cutoff=64K
63+
#
64+
# Example 3: Large media with higher cutoff (shares remaining RAM pool)
65+
# volume=3 scheme=http size=40% ram_cache_cutoff=1M

doc/admin-guide/files/volume.config.en.rst

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,66 @@ Optional directory entry sizing
7373

7474
You can also add an option ``avg_obj_size=<size>`` to the volume configuration
7575
line. This overrides the global :ts:cv:`proxy.config.cache.min_average_object_size`
76-
configuration for this volume. This is useful if you have a volume that is dedicated
77-
for say very small objects, and you need a lot of directory entries to store them.
76+
configuration for this volume. The size supports multipliers (K, M, G, T) for
77+
convenience (e.g., ``avg_obj_size=64K`` or ``avg_obj_size=1M``). This is useful
78+
if you have a volume that is dedicated for say very small objects, and you need
79+
a lot of directory entries to store them.
7880

7981
Optional fragment size setting
8082
------------------------------
8183

8284
You can also add an option ``fragment_size=<size>`` to the volume configuration
8385
line. This overrides the global :ts:cv:`proxy.config.cache.target_fragment_size`
84-
configuration for this volume. This allows for a smaller, or larger, fragment size
85-
for a particular volume. This may be useful together with ``avg_obj_size`` as well,
86-
since a larger fragment size could reduce the number of directory entries needed
87-
for a large object.
86+
configuration for this volume. The size supports multipliers (K, M, G, T) for
87+
convenience (e.g., ``fragment_size=512K`` or ``fragment_size=2M``). This allows
88+
for a smaller, or larger, fragment size for a particular volume. This may be
89+
useful together with ``avg_obj_size`` as well, since a larger fragment size could
90+
reduce the number of directory entries needed for a large object.
8891

8992
Note that this setting has a maximmum value of 4MB.
9093

94+
Optional RAM cache size allocation
95+
-----------------------------------
96+
97+
You can add an option ``ram_cache_size=<size>`` to the volume configuration line
98+
to allocate a dedicated RAM cache pool for this volume. The size supports
99+
multipliers (K, M, G, T) for convenience (e.g., ``ram_cache_size=512M`` or
100+
``ram_cache_size=2G``). Setting ``ram_cache_size=0`` disables the RAM cache
101+
for this volume, which is equivalent to ``ramcache=false``.
102+
103+
When ``ram_cache_size`` is specified for a volume, that amount is **automatically
104+
subtracted** from the global :ts:cv:`proxy.config.cache.ram_cache.size` setting,
105+
and the remainder is shared among volumes without private allocations. This ensures
106+
total RAM cache usage never exceeds the configured global limit.
107+
108+
For example, if the global RAM cache size is 4GB and you allocate 1GB to volume 1
109+
and 512MB to volume 2, the remaining 2.5GB will be distributed among other volumes
110+
using the normal proportional allocation based on disk space.
111+
112+
**Important notes:**
113+
114+
* If the sum of all ``ram_cache_size`` allocations exceeds the global RAM cache size,
115+
a warning is logged and the private allocations are disabled, falling back to
116+
the standard shared allocation.
117+
* This setting only takes effect when :ts:cv:`proxy.config.cache.ram_cache.size`
118+
is set to a positive value (not ``-1`` for automatic sizing).
119+
120+
Optional RAM cache cutoff override
121+
-----------------------------------
122+
123+
You can add an option ``ram_cache_cutoff=<size>`` to the volume configuration line
124+
to override the global :ts:cv:`proxy.config.cache.ram_cache_cutoff` setting for
125+
this specific volume. The size supports multipliers (K, M, G, T) for convenience
126+
(e.g., ``ram_cache_cutoff=64K`` or ``ram_cache_cutoff=1M``).
127+
128+
This cutoff determines the maximum object size that will be stored in the RAM cache.
129+
Objects larger than this size will only be stored on disk. Setting different cutoffs
130+
per volume allows you to:
131+
132+
* Use larger cutoffs for volumes serving frequently accessed large objects
133+
* Use smaller cutoffs for volumes with many small objects to maximize RAM cache hits
134+
* Disable RAM caching entirely for certain objects by setting a very low cutoff
135+
91136
Exclusive spans and volume sizes
92137
================================
93138

@@ -126,5 +171,24 @@ ramcache has been disabled.::
126171
volume=1 scheme=http size=20%
127172
volume=2 scheme=http size=20%
128173
volume=3 scheme=http size=20%
129-
volume=4 scheme=http size=20% avg_obj_size=4096
130-
volume=5 scheme=http size=20% ramcache=false fragment_size=524288
174+
volume=4 scheme=http size=20% avg_obj_size=4K
175+
volume=5 scheme=http size=20% ramcache=false fragment_size=512K
176+
177+
The following example shows advanced RAM cache configuration with dedicated
178+
allocations and custom cutoffs::
179+
180+
# Volume 1: General content with 2GB dedicated RAM cache
181+
volume=1 scheme=http size=40% ram_cache_size=2G
182+
183+
# Volume 2: Small API responses with custom cutoff and 512MB RAM cache
184+
volume=2 scheme=http size=20% ram_cache_size=512M ram_cache_cutoff=64K
185+
186+
# Volume 3: Large media with higher cutoff for thumbnails
187+
volume=3 scheme=http size=40% ram_cache_cutoff=1M
188+
189+
In this example, assuming a global ``proxy.config.cache.ram_cache.size`` of 4GB:
190+
191+
* Volume 1 gets a dedicated 2GB RAM cache allocation
192+
* Volume 2 gets a dedicated 512MB RAM cache allocation and only caches objects up to 64KB
193+
* Volume 3 shares from the remaining 1.5GB pool (4GB - 2GB - 512MB) and caches objects up to 1MB
194+
* The automatic subtraction ensures total RAM usage stays within the 4GB limit

src/iocore/cache/CacheHosting.cc

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ ConfigVolumes::BuildListFromString(char *config_file_path, char *file_buf)
652652
bool ramcache_enabled = true;
653653
int avg_obj_size = -1; // Defaults
654654
int fragment_size = -1;
655+
int64_t ram_cache_size = -1; // -1 means use shared allocation
656+
int64_t ram_cache_cutoff = -1; // -1 means use global cutoff
655657

656658
while (true) {
657659
// skip all blank spaces at beginning of line
@@ -741,16 +743,24 @@ ConfigVolumes::BuildListFromString(char *config_file_path, char *file_buf)
741743
}
742744
} else if (strcasecmp(tmp, "avg_obj_size") == 0) { // match avg_obj_size
743745
tmp += 13;
744-
avg_obj_size = atoi(tmp);
746+
avg_obj_size = static_cast<int>(ink_atoi64(tmp));
745747

746-
while (ParseRules::is_digit(*tmp)) {
748+
if (avg_obj_size < 0) {
749+
err = "Invalid avg_obj_size value (must be >= 0)";
750+
break;
751+
}
752+
while (*tmp && (ParseRules::is_digit(*tmp) || strchr("kmgtKMGT", *tmp))) {
747753
tmp++;
748754
}
749755
} else if (strcasecmp(tmp, "fragment_size") == 0) { // match fragment_size
750756
tmp += 14;
751-
fragment_size = atoi(tmp);
757+
fragment_size = static_cast<int>(ink_atoi64(tmp));
752758

753-
while (ParseRules::is_digit(*tmp)) {
759+
if (fragment_size < 0) {
760+
err = "Invalid fragment_size value (must be >= 0)";
761+
break;
762+
}
763+
while (*tmp && (ParseRules::is_digit(*tmp) || strchr("kmgtKMGT", *tmp))) {
754764
tmp++;
755765
}
756766
} else if (strcasecmp(tmp, "ramcache") == 0) { // match ramcache
@@ -765,6 +775,29 @@ ConfigVolumes::BuildListFromString(char *config_file_path, char *file_buf)
765775
err = "Unexpected end of line";
766776
break;
767777
}
778+
} else if (strcasecmp(tmp, "ram_cache_size") == 0) { // match ram_cache_size
779+
tmp += 15;
780+
ram_cache_size = ink_atoi64(tmp);
781+
782+
if (ram_cache_size < 0) {
783+
err = "Invalid ram_cache_size value (must be >= 0)";
784+
break;
785+
}
786+
// Note: ram_cache_size=0 disables RAM cache for this volume, same as ramcache=false
787+
while (*tmp && (ParseRules::is_digit(*tmp) || strchr("kmgtKMGT", *tmp))) {
788+
tmp++;
789+
}
790+
} else if (strcasecmp(tmp, "ram_cache_cutoff") == 0) { // match ram_cache_cutoff
791+
tmp += 17;
792+
ram_cache_cutoff = ink_atoi64(tmp);
793+
794+
if (ram_cache_cutoff < 0) {
795+
err = "Invalid ram_cache_cutoff value (must be >= 0)";
796+
break;
797+
}
798+
while (*tmp && (ParseRules::is_digit(*tmp) || strchr("kmgtKMGT", *tmp))) {
799+
tmp++;
800+
}
768801
}
769802

770803
// ends here
@@ -791,6 +824,8 @@ ConfigVolumes::BuildListFromString(char *config_file_path, char *file_buf)
791824
configp->size = size;
792825
configp->avg_obj_size = avg_obj_size;
793826
configp->fragment_size = fragment_size;
827+
configp->ram_cache_size = ram_cache_size;
828+
configp->ram_cache_cutoff = ram_cache_cutoff;
794829
configp->cachep = nullptr;
795830
configp->ramcache_enabled = ramcache_enabled;
796831
cp_queue.enqueue(configp);

src/iocore/cache/CacheProcessor.cc

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,8 @@ cplist_update()
11701170
cp->ramcache_enabled = config_vol->ramcache_enabled;
11711171
cp->avg_obj_size = config_vol->avg_obj_size;
11721172
cp->fragment_size = config_vol->fragment_size;
1173+
cp->ram_cache_size = config_vol->ram_cache_size;
1174+
cp->ram_cache_cutoff = config_vol->ram_cache_cutoff;
11731175
config_vol->cachep = cp;
11741176
} else {
11751177
/* delete this volume from all the disks */
@@ -1442,22 +1444,54 @@ CacheProcessor::cacheInitialized()
14421444
}
14431445
}
14441446

1447+
// Calculate total private RAM allocations from per-volume configurations
14451448
int64_t http_ram_cache_size = 0;
1449+
int64_t total_private_ram = 0;
1450+
1451+
if (cache_config_ram_cache_size != AUTO_SIZE_RAM_CACHE) {
1452+
CacheVol *cp = cp_list.head;
1453+
1454+
for (; cp; cp = cp->link.next) {
1455+
if (cp->ram_cache_size > 0) {
1456+
total_private_ram += cp->ram_cache_size;
1457+
Dbg(dbg_ctl_cache_init, "Volume %d has private RAM allocation: %" PRId64 " bytes (%" PRId64 " MB)", cp->vol_number,
1458+
cp->ram_cache_size, cp->ram_cache_size / (1024 * 1024));
1459+
}
1460+
}
1461+
1462+
if (total_private_ram > 0) {
1463+
Dbg(dbg_ctl_cache_init, "Total private RAM allocations: %" PRId64 " bytes (%" PRId64 " MB)", total_private_ram,
1464+
total_private_ram / (1024 * 1024));
1465+
}
1466+
}
14461467

1447-
// let us calculate the Size
14481468
if (cache_config_ram_cache_size == AUTO_SIZE_RAM_CACHE) {
14491469
Dbg(dbg_ctl_cache_init, "cache_config_ram_cache_size == AUTO_SIZE_RAM_CACHE");
14501470
} else {
14511471
// we got configured memory size
14521472
// TODO, should we check the available system memories, or you will
14531473
// OOM or swapout, that is not a good situation for the server
14541474
Dbg(dbg_ctl_cache_init, "%" PRId64 " != AUTO_SIZE_RAM_CACHE", cache_config_ram_cache_size);
1455-
http_ram_cache_size =
1456-
static_cast<int64_t>((static_cast<double>(theCache->cache_size) / total_size) * cache_config_ram_cache_size);
1475+
1476+
// Calculate shared pool: global RAM cache size minus private allocations
1477+
int64_t shared_pool = cache_config_ram_cache_size - total_private_ram;
1478+
1479+
if (shared_pool < 0) {
1480+
Warning("Total private RAM cache allocations (%" PRId64 " bytes) exceed global ram_cache.size (%" PRId64 " bytes). "
1481+
"Using global limit. Consider increasing proxy.config.cache.ram_cache.size.",
1482+
total_private_ram, cache_config_ram_cache_size);
1483+
shared_pool = cache_config_ram_cache_size; // Fall back to using the global pool for all
1484+
total_private_ram = 0; // Disable private allocations
1485+
} else if (total_private_ram > 0) {
1486+
Dbg(dbg_ctl_cache_init, "Shared RAM cache pool (after private allocations): %" PRId64 " bytes (%" PRId64 " MB)",
1487+
shared_pool, shared_pool / (1024 * 1024));
1488+
}
1489+
1490+
http_ram_cache_size = static_cast<int64_t>((static_cast<double>(theCache->cache_size) / total_size) * shared_pool);
14571491

14581492
Dbg(dbg_ctl_cache_init, "http_ram_cache_size = %" PRId64 " = %" PRId64 "Mb", http_ram_cache_size,
14591493
http_ram_cache_size / (1024 * 1024));
1460-
int64_t stream_ram_cache_size = cache_config_ram_cache_size - http_ram_cache_size;
1494+
int64_t stream_ram_cache_size = shared_pool - http_ram_cache_size;
14611495

14621496
Dbg(dbg_ctl_cache_init, "stream_ram_cache_size = %" PRId64 " = %" PRId64 "Mb", stream_ram_cache_size,
14631497
stream_ram_cache_size / (1024 * 1024));
@@ -1470,22 +1504,49 @@ CacheProcessor::cacheInitialized()
14701504
uint64_t total_cache_bytes = 0; // bytes that can used in total_size
14711505
uint64_t total_direntries = 0; // all the direntries in the cache
14721506
uint64_t used_direntries = 0; // and used
1473-
uint64_t total_ram_cache_bytes = 0;
1507+
uint64_t total_ram_cache_bytes = 0; // Total RAM cache size across all volumes
1508+
uint64_t shared_cache_size = 0; // Total cache size of volumes without explicit RAM allocations
1509+
1510+
// Calculate total cache size of volumes without explicit RAM allocations
1511+
if (http_ram_cache_size > 0) {
1512+
for (int i = 0; i < gnstripes; i++) {
1513+
if (gstripes[i]->cache_vol->ram_cache_size <= 0) {
1514+
shared_cache_size += (gstripes[i]->len >> STORE_BLOCK_SHIFT);
1515+
}
1516+
}
1517+
Dbg(dbg_ctl_cache_init, "Shared cache size (for RAM pool distribution): %" PRId64 " blocks", shared_cache_size);
1518+
}
14741519

14751520
for (int i = 0; i < gnstripes; i++) {
14761521
StripeSM *stripe = gstripes[i];
14771522
int64_t ram_cache_bytes = 0;
14781523

1479-
if (stripe->cache_vol->ramcache_enabled) {
1480-
if (http_ram_cache_size == 0) {
1524+
// If RAM cache enabled, check if this volume has a private RAM cache allocation
1525+
if (stripe->cache_vol->ramcache_enabled && stripe->cache_vol->ram_cache_size != 0) {
1526+
if (stripe->cache_vol->ram_cache_size > 0) {
1527+
int64_t volume_stripe_count = 0;
1528+
1529+
for (int j = 0; j < gnstripes; j++) {
1530+
if (gstripes[j]->cache_vol == stripe->cache_vol) {
1531+
volume_stripe_count++;
1532+
}
1533+
}
1534+
1535+
if (volume_stripe_count > 0) {
1536+
ram_cache_bytes = stripe->cache_vol->ram_cache_size / volume_stripe_count;
1537+
Dbg(dbg_ctl_cache_init, "Volume %d stripe %d using private RAM allocation: %" PRId64 " bytes (%" PRId64 " MB)",
1538+
stripe->cache_vol->vol_number, i, ram_cache_bytes, ram_cache_bytes / (1024 * 1024));
1539+
}
1540+
} else if (http_ram_cache_size == 0) {
14811541
// AUTO_SIZE_RAM_CACHE
14821542
ram_cache_bytes = stripe->dirlen() * DEFAULT_RAM_CACHE_MULTIPLIER;
14831543
} else {
1544+
// Use shared pool allocation - distribute only among volumes without explicit allocations
14841545
ink_assert(stripe->cache != nullptr);
1546+
int64_t divisor = (shared_cache_size > 0) ? shared_cache_size : theCache->cache_size;
1547+
double factor = static_cast<double>(static_cast<int64_t>(stripe->len >> STORE_BLOCK_SHIFT)) / divisor;
14851548

1486-
double factor = static_cast<double>(static_cast<int64_t>(stripe->len >> STORE_BLOCK_SHIFT)) / theCache->cache_size;
1487-
Dbg(dbg_ctl_cache_init, "factor = %f", factor);
1488-
1549+
Dbg(dbg_ctl_cache_init, "factor = %f (divisor = %" PRId64 ")", factor, divisor);
14891550
ram_cache_bytes = static_cast<int64_t>(http_ram_cache_size * factor);
14901551
}
14911552

@@ -1497,19 +1558,19 @@ CacheProcessor::cacheInitialized()
14971558
ram_cache_bytes, ram_cache_bytes / (1024 * 1024));
14981559
}
14991560

1500-
uint64_t vol_total_cache_bytes = stripe->len - stripe->dirlen();
1501-
total_cache_bytes += vol_total_cache_bytes;
1561+
uint64_t vol_total_cache_bytes = stripe->len - stripe->dirlen();
1562+
uint64_t vol_total_direntries = stripe->directory.entries();
1563+
uint64_t vol_used_direntries = stripe->directory.entries_used();
1564+
1565+
total_cache_bytes += vol_total_cache_bytes;
15021566
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.bytes_total, vol_total_cache_bytes);
15031567
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.stripes);
15041568

15051569
Dbg(dbg_ctl_cache_init, "total_cache_bytes = %" PRId64 " = %" PRId64 "Mb", total_cache_bytes,
15061570
total_cache_bytes / (1024 * 1024));
15071571

1508-
uint64_t vol_total_direntries = stripe->directory.entries();
1509-
total_direntries += vol_total_direntries;
1572+
total_direntries += vol_total_direntries;
15101573
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.direntries_total, vol_total_direntries);
1511-
1512-
uint64_t vol_used_direntries = stripe->directory.entries_used();
15131574
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.direntries_used, vol_used_direntries);
15141575
used_direntries += vol_used_direntries;
15151576
}

src/iocore/cache/CacheVC.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,15 +412,17 @@ CacheVC::handleReadDone(int event, Event * /* e ATS_UNUSED */)
412412
// Put the request in the ram cache only if its a open_read or lookup
413413
if (vio.op == VIO::READ && okay) {
414414
bool cutoff_check;
415+
// Determine effective cutoff: use per-volume override if set, otherwise use global
416+
int64_t effective_cutoff =
417+
(stripe->cache_vol->ram_cache_cutoff > 0) ? stripe->cache_vol->ram_cache_cutoff : cache_config_ram_cache_cutoff;
415418
// cutoff_check :
416419
// doc_len == 0 for the first fragment (it is set from the vector)
417420
// The decision on the first fragment is based on
418421
// doc->total_len
419422
// After that, the decision is based of doc_len (doc_len != 0)
420-
// (cache_config_ram_cache_cutoff == 0) : no cutoffs
421-
cutoff_check =
422-
((!doc_len && static_cast<int64_t>(doc->total_len) < cache_config_ram_cache_cutoff) ||
423-
(doc_len && static_cast<int64_t>(doc_len) < cache_config_ram_cache_cutoff) || !cache_config_ram_cache_cutoff);
423+
// (effective_cutoff == 0) : no cutoffs
424+
cutoff_check = ((!doc_len && static_cast<int64_t>(doc->total_len) < effective_cutoff) ||
425+
(doc_len && static_cast<int64_t>(doc_len) < effective_cutoff) || !effective_cutoff);
424426
if (cutoff_check && !f.doc_from_ram_cache) {
425427
uint64_t o = dir_offset(&dir);
426428
stripe->ram_cache->put(read_key, buf.get(), doc->len, http_copy_hdr, o);

src/iocore/cache/P_CacheHosting.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ struct ConfigVol {
298298
int percent;
299299
int avg_obj_size;
300300
int fragment_size;
301+
int64_t ram_cache_size; // Per-volume RAM cache size (-1 = use shared allocation)
302+
int64_t ram_cache_cutoff; // Per-volume RAM cache cutoff (-1 = use global cutoff)
301303
CacheVol *cachep;
302304
LINK(ConfigVol, link);
303305
};

0 commit comments

Comments
 (0)