Copilot commented on code in PR #13904:
URL: https://github.com/apache/trafficserver/pull/13904#discussion_r4393329649


##########
include/mgmt/config/ConfigContext.h:
##########
@@ -90,20 +90,23 @@ class RecordTriggeredReloadContinuation;
 class ConfigContext
 {
 public:
-  ConfigContext();
+  ConfigContext() = default;
 
   explicit ConfigContext(std::shared_ptr<ConfigReloadTask> t, std::string_view 
description = "", std::string_view filename = "");
 
-  ~ConfigContext();
+  ~ConfigContext() = default;
 
   // Copy only - move is intentionally suppressed.
-  // ConfigContext holds a weak_ptr (cheap to copy) and a YAML::Node 
(ref-counted).
+  // ConfigContext holds a weak_ptr to the task and a shared_ptr to the 
RPC-supplied
+  // YAML and reload directives, so a copy is at most two reference-count 
increments.
+  // Keep it that small: it is passed by value everywhere (see the size check 
in
+  // ConfigContext.cc).
   // Suppressing move ensures that std::move(ctx) silently copies, keeping the
   // original valid. This is critical for execute_reload()'s post-handler 
check:
   // if a handler defers work (e.g. LogConfig), the original ctx must remain
   // valid so is_terminal() can detect the non-terminal state and emit a 
warning.
-  ConfigContext(ConfigContext const &);
-  ConfigContext &operator=(ConfigContext const &);
+  ConfigContext(ConfigContext const &)            = default;
+  ConfigContext &operator=(ConfigContext const &) = default;

Review Comment:
   Copy-assignment now rebinds the context's payload rather than writing 
through the shared `YAML::Node`, which changes behavior from before. The 
comment above these defaulted operations does not mention this. Add a note that 
assignment replaces this context's content without modifying other copies, so 
callers that relied on the old write-through behavior can find the change.



##########
src/records/unit_tests/test_ConfigReloadProgress.cc:
##########
@@ -86,3 +99,52 @@ TEST_CASE("Progress checker releases a task that timed out", 
"[config][reload][l
   REQUIRE(wait_for_sole_owner(task, std::chrono::seconds{10}));
   REQUIRE(task->get_state() == ConfigReloadTask::State::TIMEOUT);
 }
+
+// execute_reload() gives a handler a context with a task only while a reload 
is in
+// progress, and starting one needs ET_TASK for its progress checker, so this 
case
+// lives in this binary.
+TEST_CASE("A dependent context inherits its parent's RPC content", 
"[config][reload][context]")
+{
+  static std::optional<ConfigContext> parent;
+  static std::optional<ConfigContext> child;
+
+  std::string token;
+  REQUIRE(ReloadCoordinator::Get_Instance().prepare_reload(token, 
"test-dep-").is_ok());
+  auto const main_task = ReloadCoordinator::Get_Instance().get_current_task();
+  REQUIRE(main_task);
+  // prepare_reload() started a progress checker, which holds one of these.
+  auto const owners_with_checker = main_task.use_count();
+
+  auto &reg = config::ConfigRegistry::Get_Instance();
+  reg.register_config("test_dependent_payload", "", "",
+                      [](ConfigContext ctx) {
+                        parent.emplace(ctx);
+                        child.emplace(ctx.add_dependent_ctx("dependent"));
+                        child->complete();
+                        ctx.complete();
+                      },
+                      config::ConfigSource::FileAndRpc, {});
+  reg.set_passed_config("test_dependent_payload", YAML::Load("{_reload: {id: 
foo}, data: x}"));
+  reg.execute_reload("test_dependent_payload");
+
+  REQUIRE(parent.has_value());
+  REQUIRE(child.has_value());
+  REQUIRE(static_cast<bool>(*parent));
+  REQUIRE(static_cast<bool>(*child));
+
+  YAML::Node const content = child->supplied_yaml();
+  REQUIRE(content);
+  REQUIRE(content["data"].as<std::string>() == "x");
+  YAML::Node const directives = child->reload_directives();
+  REQUIRE(directives);
+  REQUIRE(directives["id"].as<std::string>() == "foo");
+  REQUIRE(content.is(parent->supplied_yaml()));
+  REQUIRE(directives.is(parent->reload_directives()));
+
+  child.reset();
+  parent.reset();
+
+  // Like the cases above, don't leave the checker running past the test. Same 
bound as the
+  // terminal case: the checker confirms a terminal state about 7 s after it 
starts.
+  REQUIRE(wait_for_owners_below(main_task, owners_with_checker, 
std::chrono::seconds{15}));

Review Comment:
   The cleanup at lines 144-149 is skipped if any REQUIRE above it fails, 
because Catch2 throws. The progress checker then keeps running past the test, 
and the static optionals `parent` and `child` keep state for later cases. Move 
the reset and the wait into a scope guard (an RAII object whose destructor 
resets both optionals and waits on `main_task`) so cleanup runs on every exit 
path. This keeps the intent stated in the comment at line 147.



##########
src/records/unit_tests/test_ConfigReloadProgress.cc:
##########
@@ -86,3 +99,52 @@ TEST_CASE("Progress checker releases a task that timed out", 
"[config][reload][l
   REQUIRE(wait_for_sole_owner(task, std::chrono::seconds{10}));
   REQUIRE(task->get_state() == ConfigReloadTask::State::TIMEOUT);
 }
+
+// execute_reload() gives a handler a context with a task only while a reload 
is in
+// progress, and starting one needs ET_TASK for its progress checker, so this 
case
+// lives in this binary.
+TEST_CASE("A dependent context inherits its parent's RPC content", 
"[config][reload][context]")
+{
+  static std::optional<ConfigContext> parent;
+  static std::optional<ConfigContext> child;
+
+  std::string token;
+  REQUIRE(ReloadCoordinator::Get_Instance().prepare_reload(token, 
"test-dep-").is_ok());
+  auto const main_task = ReloadCoordinator::Get_Instance().get_current_task();
+  REQUIRE(main_task);
+  // prepare_reload() started a progress checker, which holds one of these.
+  auto const owners_with_checker = main_task.use_count();
+
+  auto &reg = config::ConfigRegistry::Get_Instance();
+  reg.register_config("test_dependent_payload", "", "",
+                      [](ConfigContext ctx) {
+                        parent.emplace(ctx);
+                        child.emplace(ctx.add_dependent_ctx("dependent"));
+                        child->complete();
+                        ctx.complete();
+                      },
+                      config::ConfigSource::FileAndRpc, {});
+  reg.set_passed_config("test_dependent_payload", YAML::Load("{_reload: {id: 
foo}, data: x}"));
+  reg.execute_reload("test_dependent_payload");
+
+  REQUIRE(parent.has_value());
+  REQUIRE(child.has_value());
+  REQUIRE(static_cast<bool>(*parent));
+  REQUIRE(static_cast<bool>(*child));

Review Comment:
   The cleanup at lines 144-149 is skipped if any REQUIRE above it fails, 
because Catch2 throws. The progress checker then keeps running past the test, 
and the static optionals `parent` and `child` keep state for later cases. Move 
the reset and the wait into a scope guard (an RAII object whose destructor 
resets both optionals and waits on `main_task`) so cleanup runs on every exit 
path. This keeps the intent stated in the comment at line 147.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to