Skip to content

Commit 02244cd

Browse files
committed
Added support for @Volume= in remap.config
1 parent 766cbdd commit 02244cd

File tree

14 files changed

+469
-49
lines changed

14 files changed

+469
-49
lines changed

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,13 +2737,23 @@ RAM Cache
27372737
Alternatively, it can be set to a fixed value such as
27382738
**20GB** (21474836480)
27392739

2740+
This global setting can be overridden on a per-volume basis using the
2741+
``ram_cache_size`` parameter in :file:`volume.config`. Per-volume
2742+
allocations are subtracted from the total RAM cache size before
2743+
distributing the remainder among volumes without explicit settings.
2744+
27402745
.. ts:cv:: CONFIG proxy.config.cache.ram_cache_cutoff INT 4194304
27412746
27422747
Objects greater than this size will not be kept in the RAM cache.
27432748
This should be set high enough to keep objects accessed frequently
27442749
in memory in order to improve performance.
27452750
**4MB** (4194304)
27462751

2752+
This global setting can be overridden on a per-volume basis using the
2753+
``ram_cache_cutoff`` parameter in :file:`volume.config`. When set,
2754+
the per-volume cutoff takes precedence over this global setting for
2755+
that specific volume.
2756+
27472757
.. ts:cv:: CONFIG proxy.config.cache.ram_cache.algorithm INT 1
27482758
27492759
Two distinct RAM caches are supported, the default (1) being the simpler

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,51 @@ will pass "1" and "2" to plugin1.so and "3" to plugin2.so.
455455

456456
This will pass "1" and "2" to plugin1.so and "3" to plugin2.so
457457

458+
.. _remap-config-cache-volume-selection:
459+
460+
Cache Volume Selection
461+
======================
462+
463+
The ``@volume`` directive allows you to override the default cache volume selection
464+
for specific remap rules, bypassing the hostname-based volume selection configured in
465+
:file:`hosting.config`. This provides fine-grained control over which cache volumes
466+
are used for different URL patterns.
467+
468+
Format
469+
------
470+
471+
::
472+
473+
@volume=<volume_number>
474+
475+
Where ``<volume_number>`` is the volume number as configured in :file:`volume.config`.
476+
Volume numbers must be between 1 and 255 (volume 0 is reserved and not usable).
477+
478+
Examples
479+
--------
480+
481+
::
482+
483+
# Route API requests to dedicated high-performance volume (volume 4)
484+
map https://api.example.com/ https://api-origin.example.com/ @volume=4
485+
486+
# Everything gets the default volume allocations (hashing)
487+
map https://www.example.com/ https://origin.example.com/
488+
489+
Behavior
490+
--------
491+
492+
- If the specified volume number is invalid (greater than the number of configured volumes),
493+
the system falls back to hostname-based volume selection with a warning logged.
494+
- The ``@volume`` directive takes precedence over :file:`hosting.config` rules.
495+
- Volume override works with all cache operations including reads, writes, and deletes.
496+
- This feature integrates with per-volume RAM cache settings configured in :file:`volume.config`.
497+
498+
.. note::
499+
500+
When using ``@volume``, ensure that the target volume has appropriate disk space and
501+
performance characteristics for the expected traffic patterns.
502+
458503
.. _remap-config-named-filters:
459504

460505
NextHop Selection Strategies

include/iocore/cache/Cache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ struct CacheProcessor : public Processor {
8383
Action *scan(Continuation *cont, std::string_view hostname = std::string_view{}, int KB_per_second = SCAN_KB_PER_SECOND);
8484
Action *lookup(Continuation *cont, const HttpCacheKey *key, CacheFragType frag_type = CACHE_FRAG_TYPE_HTTP);
8585
Action *open_read(Continuation *cont, const HttpCacheKey *key, CacheHTTPHdr *request, const HttpConfigAccessor *params,
86-
CacheFragType frag_type = CACHE_FRAG_TYPE_HTTP);
86+
CacheFragType frag_type = CACHE_FRAG_TYPE_HTTP, int volume_override = -1);
8787
Action *open_write(Continuation *cont, const HttpCacheKey *key, CacheHTTPInfo *old_info, time_t pin_in_cache = 0,
88-
CacheFragType frag_type = CACHE_FRAG_TYPE_HTTP);
88+
CacheFragType frag_type = CACHE_FRAG_TYPE_HTTP, int volume_override = -1);
8989
Action *remove(Continuation *cont, const HttpCacheKey *key, CacheFragType frag_type = CACHE_FRAG_TYPE_HTTP);
9090

9191
/** Mark physical disk/device/file as offline.

include/proxy/http/HttpTransact.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,10 @@ class HttpTransact
704704

705705
MgmtByte cache_open_write_fail_action = 0;
706706

707-
HttpConfigParams *http_config_param = nullptr;
708-
CacheLookupInfo cache_info;
707+
HttpConfigParams *http_config_param = nullptr;
708+
CacheLookupInfo cache_info;
709+
710+
int cache_volume_override = -1;
709711
ResolveInfo dns_info;
710712
RedirectInfo redirect_info;
711713
ConnectionTracker::TxnState outbound_conn_track_state;

include/proxy/http/remap/RemapConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class UrlRewrite;
4040
#define REMAP_OPTFLG_INTERNAL 0x0080u /* only allow internal requests to hit this remap */
4141
#define REMAP_OPTFLG_IN_IP 0x0100u /* "in_ip=" option (used for ACL filtering)*/
4242
#define REMAP_OPTFLG_STRATEGY 0x0200u /* "strategy=" the name of the nexthop selection strategy */
43+
#define REMAP_OPTFLG_VOLUME 0x0400u /* "volume=" cache volume override */
4344
#define REMAP_OPTFLG_MAP_ID 0x0800u /* associate a map ID with this rule */
4445
#define REMAP_OPTFLG_INVERT 0x80000000u /* "invert" the rule (for src_ip and src_ip_category at least) */
4546
#define REMAP_OPTFLG_ALL_FILTERS \

include/proxy/http/remap/UrlMapping.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class url_mapping
112112
bool ip_allow_check_enabled_p = false;
113113
acl_filter_rule *filter = nullptr; // acl filtering (linked list of rules)
114114
LINK(url_mapping, link); // For use with the main Queue linked list holding all the mapping
115-
NextHopSelectionStrategy *strategy = nullptr;
115+
NextHopSelectionStrategy *strategy = nullptr;
116+
int cache_volume = -1;
116117
std::string remapKey;
117118
std::atomic<uint64_t> _hitCount = 0; // counter can overflow
118119

src/iocore/cache/Cache.cc

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -529,15 +529,15 @@ Cache::scan(Continuation *cont, std::string_view hostname, int KB_per_second) co
529529

530530
Action *
531531
Cache::open_read(Continuation *cont, const CacheKey *key, CacheHTTPHdr *request, const HttpConfigAccessor *params,
532-
CacheFragType type, std::string_view hostname) const
532+
CacheFragType type, std::string_view hostname, int volume_override) const
533533
{
534534
if (!CacheProcessor::IsCacheReady(type)) {
535535
cont->handleEvent(CACHE_EVENT_OPEN_READ_FAILED, reinterpret_cast<void *>(-ECACHE_NOT_READY));
536536
return ACTION_RESULT_DONE;
537537
}
538538
ink_assert(caches[type] == this);
539539

540-
StripeSM *stripe = key_to_stripe(key, hostname);
540+
StripeSM *stripe = key_to_stripe(key, hostname, volume_override);
541541
Dir result, *last_collision = nullptr;
542542
ProxyMutex *mutex = cont->mutex.get();
543543
OpenDirEntry *od = nullptr;
@@ -603,8 +603,8 @@ Cache::open_read(Continuation *cont, const CacheKey *key, CacheHTTPHdr *request,
603603

604604
// main entry point for writing of http documents
605605
Action *
606-
Cache::open_write(Continuation *cont, const CacheKey *key, CacheHTTPInfo *info, time_t apin_in_cache, CacheFragType type,
607-
std::string_view hostname) const
606+
Cache::open_write(Continuation *cont, const CacheKey *key, CacheHTTPInfo *old_info, time_t pin_in_cache, CacheFragType type,
607+
std::string_view hostname, int volume_override) const
608608
{
609609
if (!CacheProcessor::IsCacheReady(type)) {
610610
cont->handleEvent(CACHE_EVENT_OPEN_WRITE_FAILED, reinterpret_cast<void *>(-ECACHE_NOT_READY));
@@ -613,7 +613,7 @@ Cache::open_write(Continuation *cont, const CacheKey *key, CacheHTTPInfo *info,
613613

614614
ink_assert(caches[type] == this);
615615
intptr_t err = 0;
616-
int if_writers = reinterpret_cast<uintptr_t>(info) == CACHE_ALLOW_MULTIPLE_WRITES;
616+
int if_writers = reinterpret_cast<uintptr_t>(old_info) == CACHE_ALLOW_MULTIPLE_WRITES;
617617
CacheVC *c = new_CacheVC(cont);
618618
c->vio.op = VIO::WRITE;
619619
c->first_key = *key;
@@ -629,10 +629,10 @@ Cache::open_write(Continuation *cont, const CacheKey *key, CacheHTTPInfo *info,
629629
} while (DIR_MASK_TAG(c->key.slice32(2)) == DIR_MASK_TAG(c->first_key.slice32(2)));
630630
c->earliest_key = c->key;
631631
c->frag_type = CACHE_FRAG_TYPE_HTTP;
632-
c->stripe = key_to_stripe(key, hostname);
632+
c->stripe = key_to_stripe(key, hostname, volume_override);
633633
StripeSM *stripe = c->stripe;
634-
c->info = info;
635-
if (c->info && reinterpret_cast<uintptr_t>(info) != CACHE_ALLOW_MULTIPLE_WRITES) {
634+
c->info = old_info;
635+
if (c->info && reinterpret_cast<uintptr_t>(old_info) != CACHE_ALLOW_MULTIPLE_WRITES) {
636636
/*
637637
Update has the following code paths :
638638
a) Update alternate header only :
@@ -664,17 +664,17 @@ Cache::open_write(Continuation *cont, const CacheKey *key, CacheHTTPInfo *info,
664664
c->f.update = 1;
665665
c->op_type = static_cast<int>(CacheOpType::Update);
666666
DDbg(dbg_ctl_cache_update, "Update called");
667-
info->object_key_get(&c->update_key);
667+
old_info->object_key_get(&c->update_key);
668668
ink_assert(!(c->update_key.is_zero()));
669-
c->update_len = info->object_size_get();
669+
c->update_len = old_info->object_size_get();
670670
} else {
671671
c->op_type = static_cast<int>(CacheOpType::Write);
672672
}
673673

674674
ts::Metrics::Gauge::increment(cache_rsb.status[c->op_type].active);
675675
ts::Metrics::Gauge::increment(stripe->cache_vol->vol_rsb.status[c->op_type].active);
676676
// coverity[Y2K38_SAFETY:FALSE]
677-
c->pin_in_cache = static_cast<uint32_t>(apin_in_cache);
677+
c->pin_in_cache = static_cast<uint32_t>(pin_in_cache);
678678

679679
{
680680
CACHE_TRY_LOCK(lock, c->stripe->mutex, cont->mutex->thread_holding);
@@ -745,41 +745,70 @@ CacheVConnection::CacheVConnection() : VConnection(nullptr) {}
745745

746746
// if generic_host_rec.stripes == nullptr, what do we do???
747747
StripeSM *
748-
Cache::key_to_stripe(const CacheKey *key, std::string_view hostname) const
748+
Cache::key_to_stripe(const CacheKey *key, std::string_view hostname, int volume_override) const
749749
{
750750
ReplaceablePtr<CacheHostTable>::ScopedReader hosttable(&this->hosttable);
751751

752-
uint32_t h = (key->slice32(2) >> DIR_TAG_WIDTH) % STRIPE_HASH_TABLE_SIZE;
753-
unsigned short *hash_table = hosttable->gen_host_rec.vol_hash_table;
754-
const CacheHostRecord *host_rec = &hosttable->gen_host_rec;
752+
uint32_t h = (key->slice32(2) >> DIR_TAG_WIDTH) % STRIPE_HASH_TABLE_SIZE;
753+
unsigned short *hash_table = hosttable->gen_host_rec.vol_hash_table;
754+
const CacheHostRecord *host_rec = &hosttable->gen_host_rec;
755+
StripeSM *selected_stripe = nullptr;
756+
bool remap_selection = false;
757+
758+
if (volume_override > 0) {
759+
for (int i = 0; i < host_rec->num_cachevols; i++) {
760+
if (host_rec->cp[i] && host_rec->cp[i]->vol_number == volume_override) {
761+
for (int j = 0; j < host_rec->num_vols; j++) {
762+
if (host_rec->stripes[j] && host_rec->stripes[j]->cache_vol &&
763+
host_rec->stripes[j]->cache_vol->vol_number == volume_override) {
764+
selected_stripe = host_rec->stripes[j];
765+
remap_selection = true;
766+
break;
767+
}
768+
}
769+
break;
770+
}
771+
}
772+
773+
if (!selected_stripe) {
774+
Warning("Invalid volume override %d, volume configured but no stripes available. Falling back to hostname-based selection.",
775+
volume_override);
776+
}
777+
}
755778

756-
if (hosttable->m_numEntries > 0 && !hostname.empty()) {
779+
// Normal hostname-based volume selection (if no valid override)
780+
if (!selected_stripe && hosttable->m_numEntries > 0 && !hostname.empty()) {
757781
CacheHostResult res;
782+
758783
hosttable->Match(hostname, &res);
759784
if (res.record) {
760785
unsigned short *host_hash_table = res.record->vol_hash_table;
786+
761787
if (host_hash_table) {
762-
if (dbg_ctl_cache_hosting.on()) {
763-
char format_str[50];
764-
snprintf(format_str, sizeof(format_str), "Volume: %%xd for host: %%.%ds", static_cast<int>(hostname.length()));
765-
Dbg(dbg_ctl_cache_hosting, format_str, res.record, hostname.data());
766-
}
767-
return res.record->stripes[host_hash_table[h]];
788+
Dbg(dbg_ctl_cache_hosting, "Volume: %p for host: %.*s", res.record, static_cast<int>(hostname.length()), hostname.data());
789+
selected_stripe = res.record->stripes[host_hash_table[h]];
768790
}
769791
}
770792
}
771-
if (hash_table) {
772-
if (dbg_ctl_cache_hosting.on()) {
773-
char format_str[50];
774-
snprintf(format_str, sizeof(format_str), "Generic volume: %%xd for host: %%.%ds", static_cast<int>(hostname.length()));
775-
Dbg(dbg_ctl_cache_hosting, format_str, host_rec, hostname.data());
793+
794+
// Generic/default volume selection (if no hostname match in hosting)
795+
if (!selected_stripe) {
796+
if (hash_table) {
797+
selected_stripe = host_rec->stripes[hash_table[h]];
798+
} else {
799+
selected_stripe = host_rec->stripes[0];
776800
}
777-
return host_rec->stripes[hash_table[h]];
778-
} else {
779-
return host_rec->stripes[0];
780801
}
781-
}
782802

803+
if (dbg_ctl_cache_hosting.on() && selected_stripe && selected_stripe->cache_vol) {
804+
Dbg(dbg_ctl_cache_hosting, "Cache volume selected: %d (%s) for key=%08x%08x hostname='%.*s' %s",
805+
selected_stripe->cache_vol->vol_number,
806+
selected_stripe->cache_vol->ramcache_enabled ? "ramcache_enabled" : "ramcache_disabled", key->slice32(0), key->slice32(1),
807+
static_cast<int>(hostname.length()), hostname.data(), remap_selection ? "(remap)" : "(calculated)");
808+
}
809+
810+
return selected_stripe;
811+
}
783812
int
784813
FragmentSizeUpdateCb(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data,
785814
void * /* cookie ATS_UNUSED */)

src/iocore/cache/CacheProcessor.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -412,18 +412,18 @@ CacheProcessor::lookup(Continuation *cont, const HttpCacheKey *key, CacheFragTyp
412412

413413
Action *
414414
CacheProcessor::open_read(Continuation *cont, const HttpCacheKey *key, CacheHTTPHdr *request, const HttpConfigAccessor *params,
415-
CacheFragType type)
415+
CacheFragType frag_type, int volume_override)
416416
{
417-
return caches[type]->open_read(cont, &key->hash, request, params, type,
418-
std::string_view{key->hostname, static_cast<std::string_view::size_type>(key->hostlen)});
417+
return caches[frag_type]->open_read(cont, &key->hash, request, params, frag_type,
418+
std::string_view{key->hostname, static_cast<size_t>(key->hostlen)}, volume_override);
419419
}
420420

421421
Action *
422422
CacheProcessor::open_write(Continuation *cont, const HttpCacheKey *key, CacheHTTPInfo *old_info, time_t pin_in_cache,
423-
CacheFragType type)
423+
CacheFragType frag_type, int volume_override)
424424
{
425-
return caches[type]->open_write(cont, &key->hash, old_info, pin_in_cache, type,
426-
std::string_view{key->hostname, static_cast<std::string_view::size_type>(key->hostlen)});
425+
return caches[frag_type]->open_write(cont, &key->hash, old_info, pin_in_cache, frag_type,
426+
std::string_view{key->hostname, static_cast<size_t>(key->hostlen)}, volume_override);
427427
}
428428

429429
//----------------------------------------------------------------------------

src/iocore/cache/P_CacheInternal.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,10 @@ struct Cache {
465465
Action *scan(Continuation *cont, std::string_view hostname = std::string_view{}, int KB_per_second = 2500) const;
466466

467467
Action *open_read(Continuation *cont, const CacheKey *key, CacheHTTPHdr *request, const HttpConfigAccessor *params,
468-
CacheFragType type, std::string_view hostname = std::string_view{}) const;
468+
CacheFragType type, std::string_view hostname = std::string_view{}, int volume_override = -1) const;
469469
Action *open_write(Continuation *cont, const CacheKey *key, CacheHTTPInfo *old_info, time_t pin_in_cache = 0,
470-
CacheFragType type = CACHE_FRAG_TYPE_HTTP, std::string_view hostname = std::string_view{}) const;
470+
CacheFragType type = CACHE_FRAG_TYPE_HTTP, std::string_view hostname = std::string_view{},
471+
int volume_override = -1) const;
471472
static void generate_key(CryptoHash *hash, CacheURL *url);
472473
static void generate_key(HttpCacheKey *hash, CacheURL *url, bool ignore_query = false, cache_generation_t generation = -1);
473474

@@ -480,7 +481,7 @@ struct Cache {
480481

481482
int open_done();
482483

483-
StripeSM *key_to_stripe(const CacheKey *key, std::string_view hostname) const;
484+
StripeSM *key_to_stripe(const CacheKey *key, std::string_view hostname, int volume_override = -1) const;
484485

485486
Cache() {}
486487
};

src/proxy/http/HttpCacheSM.cc

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,20 @@ HttpCacheSM::_schedule_read_retry()
317317
Action *
318318
HttpCacheSM::do_cache_open_read(const HttpCacheKey &key)
319319
{
320+
Action *action_handle = nullptr;
321+
320322
open_read_tries++;
321323
ink_assert(pending_action == nullptr);
322324

323325
// Initialising read-while-write-inprogress flag
324326
this->readwhilewrite_inprogress = false;
325-
Action *action_handle = cacheProcessor.open_read(this, &key, this->read_request_hdr, &http_params);
327+
328+
if (master_sm && master_sm->t_state.cache_volume_override > 0) {
329+
action_handle = cacheProcessor.open_read(this, &key, this->read_request_hdr, &http_params, CACHE_FRAG_TYPE_HTTP,
330+
master_sm->t_state.cache_volume_override);
331+
} else {
332+
action_handle = cacheProcessor.open_read(this, &key, this->read_request_hdr, &http_params);
333+
}
326334

327335
if (action_handle != ACTION_RESULT_DONE) {
328336
pending_action = action_handle;
@@ -416,9 +424,15 @@ HttpCacheSM::open_write(const HttpCacheKey *key, URL *url, HTTPHdr *request, Cac
416424
return ACTION_RESULT_DONE;
417425
}
418426

419-
// INKqa11166
420427
CacheHTTPInfo *info = allow_multiple ? reinterpret_cast<CacheHTTPInfo *>(CACHE_ALLOW_MULTIPLE_WRITES) : old_info;
421-
Action *action_handle = cacheProcessor.open_write(this, key, info, pin_in_cache);
428+
Action *action_handle = nullptr;
429+
430+
if (master_sm && master_sm->t_state.cache_volume_override > 0) {
431+
action_handle =
432+
cacheProcessor.open_write(this, key, info, pin_in_cache, CACHE_FRAG_TYPE_HTTP, master_sm->t_state.cache_volume_override);
433+
} else {
434+
action_handle = cacheProcessor.open_write(this, key, info, pin_in_cache);
435+
}
422436

423437
if (action_handle != ACTION_RESULT_DONE) {
424438
pending_action = action_handle;

0 commit comments

Comments
 (0)