This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 2d9d666bc6 Add traffic_ctl cache clear command (#13418)
2d9d666bc6 is described below
commit 2d9d666bc698934ea1fda903ec7e727a9213cacc
Author: Brian Neradt <[email protected]>
AuthorDate: Tue Aug 4 14:59:35 2026 -0500
Add traffic_ctl cache clear command (#13418)
Cache resets currently require restarting Traffic Server or manually
changing the cache generation. This complicates repeatable workload
comparisons and their automation.
This adds a traffic_ctl cache clear command backed by a restricted
JSON-RPC method that advances the global HTTP cache generation. This
also documents the logical purge semantics and exercises the command in
the cache-generation AuTest.
Fixes: #9399
---
doc/appendices/command-line/traffic_ctl.en.rst | 18 ++++++
doc/developer-guide/jsonrpc/jsonrpc-api.en.rst | 57 ++++++++++++++++++
include/mgmt/rpc/handlers/cache/Cache.h | 31 ++++++++++
src/mgmt/rpc/CMakeLists.txt | 1 +
src/mgmt/rpc/handlers/cache/Cache.cc | 69 ++++++++++++++++++++++
src/traffic_ctl/CtrlCommands.cc | 20 +++++++
src/traffic_ctl/CtrlCommands.h | 11 ++++
src/traffic_ctl/jsonrpc/CtrlRPCRequests.h | 8 +++
src/traffic_ctl/traffic_ctl.cc | 6 ++
src/traffic_server/RpcAdminPubHandlers.cc | 5 ++
.../cache/cache-generation-clear.test.py | 12 ++--
.../gold/{hit_default77.gold => hit_default0.gold} | 2 +-
.../{miss_default77.gold => miss_default0.gold} | 2 +-
13 files changed, 234 insertions(+), 8 deletions(-)
diff --git a/doc/appendices/command-line/traffic_ctl.en.rst
b/doc/appendices/command-line/traffic_ctl.en.rst
index 5fff291cf1..6d50dce4ca 100644
--- a/doc/appendices/command-line/traffic_ctl.en.rst
+++ b/doc/appendices/command-line/traffic_ctl.en.rst
@@ -43,6 +43,8 @@ of subcommands that control different aspects of Traffic
Server:
:program:`traffic_ctl config`
Manipulate and display configuration records
+:program:`traffic_ctl cache`
+ Invalidate cached objects
:program:`traffic_ctl metric`
Manipulate performance and status metrics
:program:`traffic_ctl server`
@@ -1137,6 +1139,22 @@ traffic_ctl server
$ traffic_ctl server debug disable
■ TS Runtime debug set to »OFF(0)«
+.. _traffic-control-command-cache:
+
+traffic_ctl cache
+-----------------
+
+.. program:: traffic_ctl cache
+.. option:: clear
+
+ :ref:`admin_cache_clear`
+
+ Invalidate cached HTTP objects in the global cache-generation namespace
without restarting |TS|. This advances
+ :ts:cv:`proxy.config.http.cache.generation`, causing subsequent cache
lookups to use a new key namespace. Remap rules that
+ override this configuration have independent namespaces and are not
affected. The invalidated data remains on cache storage until
+ it is overwritten through normal cache operation. The configuration change
applies to the running process and is not persisted to
+ :file:`records.yaml`.
+
.. _traffic-control-command-storage:
traffic_ctl storage
diff --git a/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
b/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
index d0084b7eaa..8a06ecf6b0 100644
--- a/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
+++ b/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
@@ -444,6 +444,8 @@ Example with stats meta:
JSONRPC API
===========
+* `admin_cache_clear`_
+
* `admin_lookup_records`_
@@ -471,6 +473,61 @@ JSONRPC API
* `get_connection_tracker_info`_
+
+Cache
+=====
+
+.. _admin_cache_clear:
+
+admin_cache_clear
+-----------------
+
+|method|
+
+Description
+~~~~~~~~~~~
+
+Invalidate cached HTTP objects in the global cache-generation namespace
without restarting |TS|. The method advances
+:ts:cv:`proxy.config.http.cache.generation`, causing subsequent cache lookups
to use a new key namespace. Remap rules that override
+this configuration have independent namespaces and are not affected. The
invalidated data remains on cache storage until it is
+overwritten through normal cache operation. The configuration change applies
to the running process and is not persisted to
+:file:`records.yaml`.
+
+Parameters
+~~~~~~~~~~
+
+* ``params``: Omitted
+
+Result
+~~~~~~
+
+The response will contain the default `success_response` or an error.
:ref:`jsonrpc-node-errors`.
+
+Examples
+~~~~~~~~
+
+Request:
+
+.. code-block:: json
+ :linenos:
+
+ {
+ "id": "9db0608c-b696-46f4-b865-1b5dc8a80d38",
+ "jsonrpc": "2.0",
+ "method": "admin_cache_clear"
+ }
+
+Response:
+
+.. code-block:: json
+ :linenos:
+
+ {
+ "jsonrpc": "2.0",
+ "result": "success",
+ "id": "9db0608c-b696-46f4-b865-1b5dc8a80d38"
+ }
+
.. _jsonapi-management-records:
diff --git a/include/mgmt/rpc/handlers/cache/Cache.h
b/include/mgmt/rpc/handlers/cache/Cache.h
new file mode 100644
index 0000000000..2a7dcefae7
--- /dev/null
+++ b/include/mgmt/rpc/handlers/cache/Cache.h
@@ -0,0 +1,31 @@
+/** @file
+
+ Cache JSON-RPC handlers.
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#pragma once
+
+#include "mgmt/rpc/jsonrpc/JsonRPCManager.h"
+
+namespace rpc::handlers::cache
+{
+swoc::Rv<YAML::Node> clear_cache(std::string_view const &id, YAML::Node const
¶ms);
+} // namespace rpc::handlers::cache
diff --git a/src/mgmt/rpc/CMakeLists.txt b/src/mgmt/rpc/CMakeLists.txt
index 8bd738c48c..cab1ed7f61 100644
--- a/src/mgmt/rpc/CMakeLists.txt
+++ b/src/mgmt/rpc/CMakeLists.txt
@@ -39,6 +39,7 @@ target_link_libraries(jsonrpc_server PUBLIC
ts::jsonrpc_protocol)
add_library(
rpcpublichandlers STATIC
+ handlers/cache/Cache.cc
handlers/common/ErrorUtils.cc
handlers/common/RecordsUtils.cc
handlers/config/Configuration.cc
diff --git a/src/mgmt/rpc/handlers/cache/Cache.cc
b/src/mgmt/rpc/handlers/cache/Cache.cc
new file mode 100644
index 0000000000..01f6ba7fdf
--- /dev/null
+++ b/src/mgmt/rpc/handlers/cache/Cache.cc
@@ -0,0 +1,69 @@
+/** @file
+
+ Cache JSON-RPC handlers.
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "mgmt/rpc/handlers/cache/Cache.h"
+#include "mgmt/rpc/handlers/common/ErrorUtils.h"
+
+#include "records/RecCore.h"
+
+#include <limits>
+#include <mutex>
+
+namespace
+{
+constexpr char CACHE_GENERATION_RECORD[] =
"proxy.config.http.cache.generation";
+std::mutex generation_mutex;
+} // namespace
+
+namespace rpc::handlers::cache
+{
+swoc::Rv<YAML::Node>
+clear_cache(std::string_view const & /* id ATS_UNUSED */, YAML::Node const &
/* params ATS_UNUSED */)
+{
+ namespace err = rpc::handlers::errors;
+
+ swoc::Rv<YAML::Node> resp;
+ std::lock_guard lock{generation_mutex};
+ auto generation = RecGetRecordInt(CACHE_GENERATION_RECORD);
+
+ if (!generation) {
+ resp.errata()
+ .assign(std::error_code{err::Codes::CONFIGURATION})
+ .note("Could not read cache generation record '{}'.",
CACHE_GENERATION_RECORD);
+ return resp;
+ }
+
+ RecInt next_generation = 0;
+ if (*generation >= 0 && *generation < std::numeric_limits<RecInt>::max()) {
+ next_generation = *generation + 1;
+ }
+
+ if (RecSetRecordInt(CACHE_GENERATION_RECORD, next_generation,
REC_SOURCE_DEFAULT) != REC_ERR_OKAY) {
+ resp.errata()
+ .assign(std::error_code{err::Codes::CONFIGURATION})
+ .note("Could not advance cache generation record '{}'.",
CACHE_GENERATION_RECORD);
+ }
+
+ return resp;
+}
+} // namespace rpc::handlers::cache
diff --git a/src/traffic_ctl/CtrlCommands.cc b/src/traffic_ctl/CtrlCommands.cc
index 8c5d51b4a4..514c5d1709 100644
--- a/src/traffic_ctl/CtrlCommands.cc
+++ b/src/traffic_ctl/CtrlCommands.cc
@@ -737,6 +737,26 @@ ConfigCommand::config_show_file_registry()
_printer->write_output(invoke_rpc(ConfigShowFileRegistryRequest{}));
}
//------------------------------------------------------------------------------------------------------------------------------------
+CacheCommand::CacheCommand(ts::Arguments *args) : CtrlCommand(args)
+{
+ auto printOpts = parse_print_opts(args);
+
+ if (get_parsed_arguments()->get(CLEAR_STR)) {
+ _printer = std::make_unique<GenericPrinter>(printOpts);
+ _invoked_func = [&]() { clear(); };
+ }
+}
+
+void
+CacheCommand::clear()
+{
+ CacheClearRequest request;
+
+ auto response = invoke_rpc(request);
+
+ _printer->write_output(response);
+}
+//------------------------------------------------------------------------------------------------------------------------------------
MetricCommand::MetricCommand(ts::Arguments *args) : RecordCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
diff --git a/src/traffic_ctl/CtrlCommands.h b/src/traffic_ctl/CtrlCommands.h
index eb6913109c..0958fb7069 100644
--- a/src/traffic_ctl/CtrlCommands.h
+++ b/src/traffic_ctl/CtrlCommands.h
@@ -160,6 +160,17 @@ public:
ConfigCommand(ts::Arguments *args);
};
//
-----------------------------------------------------------------------------------------------------------------------------------
+class CacheCommand : public CtrlCommand
+{
+public:
+ CacheCommand(ts::Arguments *args);
+
+private:
+ static inline const std::string CLEAR_STR{"clear"};
+
+ void clear();
+};
+//
-----------------------------------------------------------------------------------------------------------------------------------
class MetricCommand : public RecordCommand
{
static inline const std::string MONITOR_STR{"monitor"};
diff --git a/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
b/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
index f3e51b77ce..6583340529 100644
--- a/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
+++ b/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
@@ -142,6 +142,14 @@ struct ConfigSetRecordResponse {
std::vector<UpdatedRec> data;
};
//------------------------------------------------------------------------------------------------------------------------------------
+struct CacheClearRequest : shared::rpc::ClientRequest {
+ std::string
+ get_method() const override
+ {
+ return "admin_cache_clear";
+ }
+};
+//------------------------------------------------------------------------------------------------------------------------------------
struct HostStatusLookUpResponse {
struct HostStatusInfo {
std::string hostName;
diff --git a/src/traffic_ctl/traffic_ctl.cc b/src/traffic_ctl/traffic_ctl.cc
index 9697aaa05d..f39759e2f2 100644
--- a/src/traffic_ctl/traffic_ctl.cc
+++ b/src/traffic_ctl/traffic_ctl.cc
@@ -94,6 +94,7 @@ main([[maybe_unused]] int argc, const char **argv)
.add_option("--watch", "-w", "Execute a program periodically. Watch
interval(in seconds) can be passed.", "", 1, "-1", "watch");
auto &config_command = parser.add_command("config", "Manipulate
configuration records").require_commands();
+ auto &cache_command = parser.add_command("cache", "Manage the document
cache").require_commands();
auto &metric_command = parser.add_command("metric", "Manipulate
performance metrics").require_commands();
auto &server_command = parser.add_command("server", "Stop, restart and
examine the server").require_commands();
auto &storage_command = parser.add_command("storage", "Manipulate cache
storage").require_commands();
@@ -201,6 +202,10 @@ main([[maybe_unused]] int argc, const char **argv)
config_command.add_command("registry", "Show configuration file registry",
Command_Execute)
.add_example_usage("traffic_ctl config registry");
+ // cache commands
+ cache_command.add_command("clear", "Advance the global cache generation",
"", 0, Command_Execute)
+ .add_example_usage("traffic_ctl cache clear");
+
// ssl-multicert subcommand
auto &ssl_multicert_command =
config_command.add_command("ssl-multicert", "Manage ssl_multicert
configuration").require_commands();
@@ -330,6 +335,7 @@ main([[maybe_unused]] int argc, const char **argv)
}
static const std::map<std::string,
std::function<std::unique_ptr<CtrlCommand>(ts::Arguments *)>> factories = {
+ {"cache", [](ts::Arguments *a) { return
std::make_unique<CacheCommand>(a); } },
{"metric", [](ts::Arguments *a) { return
std::make_unique<MetricCommand>(a); } },
{"server", [](ts::Arguments *a) { return
std::make_unique<ServerCommand>(a); } },
{"storage", [](ts::Arguments *a) { return
std::make_unique<StorageCommand>(a); } },
diff --git a/src/traffic_server/RpcAdminPubHandlers.cc
b/src/traffic_server/RpcAdminPubHandlers.cc
index 1e053db966..58a0794121 100644
--- a/src/traffic_server/RpcAdminPubHandlers.cc
+++ b/src/traffic_server/RpcAdminPubHandlers.cc
@@ -21,6 +21,7 @@
#include "mgmt/rpc/jsonrpc/JsonRPC.h"
// Admin API Implementation headers.
+#include "mgmt/rpc/handlers/cache/Cache.h"
#include "mgmt/rpc/handlers/config/Configuration.h"
#include "mgmt/rpc/handlers/hostdb/HostDB.h"
#include "mgmt/rpc/handlers/records/Records.h"
@@ -34,6 +35,10 @@ namespace rpc::admin
void
register_admin_jsonrpc_handlers()
{
+ // Cache
+ using namespace rpc::handlers::cache;
+ rpc::add_method_handler("admin_cache_clear", &clear_cache,
&core_ats_rpc_service_provider_handle, {{rpc::RESTRICTED_API}});
+
// Config
using namespace rpc::handlers::config;
rpc::add_method_handler("admin_config_set_records", &set_config_records,
&core_ats_rpc_service_provider_handle,
diff --git a/tests/gold_tests/cache/cache-generation-clear.test.py
b/tests/gold_tests/cache/cache-generation-clear.test.py
index 94e7d4d5f5..8095f2c519 100644
--- a/tests/gold_tests/cache/cache-generation-clear.test.py
+++ b/tests/gold_tests/cache/cache-generation-clear.test.py
@@ -64,9 +64,9 @@ tr.MakeCurlCommand(
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.Streams.All = "gold/hit_default-1.gold"
-# Call traffic_ctrl to set new generation
+# Clear the cache through traffic_ctl. The command advances the initial
generation from -1 to 0.
tr = Test.AddTestRun()
-tr.Processes.Default.Command = f'traffic_ctl --debug config set
proxy.config.http.cache.generation 77'
+tr.Processes.Default.Command = 'traffic_ctl cache clear'
tr.Processes.Default.ForceUseShell = False
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.Env = ts.Env # set the environment for traffic_control
to run in
@@ -80,16 +80,16 @@ tr.MakeCurlCommand(
ts.Variables.port, objectid),
ts=ts)
tr.Processes.Default.ReturnCode = 0
-tr.Processes.Default.Streams.All = "gold/miss_default77.gold"
+tr.Processes.Default.Streams.All = "gold/miss_default0.gold"
-# new generation should should now hit.
+# The new generation should now hit.
tr = Test.AddTestRun()
tr.MakeCurlCommand(
'"http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug:
x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(
ts.Variables.port, objectid),
ts=ts)
tr.Processes.Default.ReturnCode = 0
-tr.Processes.Default.Streams.All = "gold/hit_default77.gold"
+tr.Processes.Default.Streams.All = "gold/hit_default0.gold"
# should still hit.
tr = Test.AddTestRun()
@@ -98,4 +98,4 @@ tr.MakeCurlCommand(
ts.Variables.port, objectid),
ts=ts)
tr.Processes.Default.ReturnCode = 0
-tr.Processes.Default.Streams.All = "gold/hit_default77.gold"
+tr.Processes.Default.Streams.All = "gold/hit_default0.gold"
diff --git a/tests/gold_tests/cache/gold/hit_default77.gold
b/tests/gold_tests/cache/gold/hit_default0.gold
similarity index 83%
rename from tests/gold_tests/cache/gold/hit_default77.gold
rename to tests/gold_tests/cache/gold/hit_default0.gold
index 79f4dc85c5..73d272ad25 100644
--- a/tests/gold_tests/cache/gold/hit_default77.gold
+++ b/tests/gold_tests/cache/gold/hit_default0.gold
@@ -4,5 +4,5 @@
< Server: ATS/{}
< X-Cache-Key: http://127.0.0.1/cache/10/{}
< X-Cache: hit-fresh
-< X-Cache-Generation: 77
+< X-Cache-Generation: 0
{}
diff --git a/tests/gold_tests/cache/gold/miss_default77.gold
b/tests/gold_tests/cache/gold/miss_default0.gold
similarity index 82%
rename from tests/gold_tests/cache/gold/miss_default77.gold
rename to tests/gold_tests/cache/gold/miss_default0.gold
index 083ff08d09..0972ecee4d 100644
--- a/tests/gold_tests/cache/gold/miss_default77.gold
+++ b/tests/gold_tests/cache/gold/miss_default0.gold
@@ -4,5 +4,5 @@
< Server: ATS/{}
< X-Cache-Key: http://127.0.0.1/cache/10/{}
< X-Cache: miss
-< X-Cache-Generation: 77
+< X-Cache-Generation: 0
{}