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 1372b5efba Fix records_yaml startup lookup race (#13538)
1372b5efba is described below

commit 1372b5efba2508e82252799e1242bd1b7ab121f5
Author: Brian Neradt <[email protected]>
AuthorDate: Wed Aug 19 12:45:36 2026 -0500

    Fix records_yaml startup lookup race (#13538)
    
    The records_yaml AuTest intermittently reported that
    proxy.config.diags.output.note was missing when a configuration
    callback ran while startup was still registering metrics.
    Metrics::find() returned the then-current end iterator for a miss, but
    RecLookupRecord() compared it with a new end iterator. An intervening
    registration made them differ and misread the missing string record as
    a numeric metric.
    
    This patch addresses the race by using the direct metrics lookup API,
    which reports a miss without comparing two moving end positions. It also
    adds a concurrent registration test that exercises string record lookups
    during metric creation.
---
 src/records/RecCore.cc                     | 18 ++++++++--------
 src/records/unit_tests/test_RecRegister.cc | 33 ++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/src/records/RecCore.cc b/src/records/RecCore.cc
index 9639430d24..91dd163717 100644
--- a/src/records/RecCore.cc
+++ b/src/records/RecCore.cc
@@ -516,18 +516,20 @@ RecGetRecordCounter(const char *name, bool lock)
 RecErrT
 RecLookupRecord(const char *name, void (*callback)(const RecRecord *, void *), 
void *data, bool lock)
 {
-  RecErrT      err     = REC_ERR_FAIL;
-  ts::Metrics &metrics = ts::Metrics::instance();
-  auto         it      = metrics.find(name);
+  RecErrT             err     = REC_ERR_FAIL;
+  ts::Metrics        &metrics = ts::Metrics::instance();
+  ts::Metrics::IdType metric_id;
 
-  if (it != metrics.end()) {
+  // A metric's storage is stable after creation. Avoid find()/end() here 
because end() is the current insertion position and
+  // can advance between those two calls while another thread registers a 
metric.
+  if (auto *metric = metrics.lookup(name, &metric_id); metric != nullptr) {
     RecRecord r{};
-    auto &&[name, type, val] = *it;
 
     r.rec_type     = RECT_PLUGIN;
-    r.data_type    = type == ts::Metrics::MetricType::COUNTER ? RECD_COUNTER : 
RECD_INT;
-    r.name         = name.data();
-    r.data.rec_int = val;
+    r.data_type    = metrics.type(metric_id) == 
ts::Metrics::MetricType::COUNTER ? RECD_COUNTER : RECD_INT;
+    r.name         = name;
+    r.data.rec_int = metric->load();
+    r.registered   = true;
 
     callback(&r, data);
     err = REC_ERR_OKAY;
diff --git a/src/records/unit_tests/test_RecRegister.cc 
b/src/records/unit_tests/test_RecRegister.cc
index 77d4c28762..38fb1adbf0 100644
--- a/src/records/unit_tests/test_RecRegister.cc
+++ b/src/records/unit_tests/test_RecRegister.cc
@@ -22,8 +22,12 @@
 #include "iocore/eventsystem/EventSystem.h"
 #include "iocore/eventsystem/RecProcess.h"
 #include "tscore/Layout.h"
+#include "tsutil/Metrics.h"
 #include "test_Diags.h"
 
+#include <atomic>
+#include <thread>
+
 TEST_CASE("RecRegisterConfig - Type Dispatch", "[librecords][RecConfig]")
 {
   SECTION("RecRegisterConfigInt")
@@ -87,3 +91,32 @@ TEST_CASE("RecRegisterStat - Type Dispatch", 
"[librecords][RecStat]")
     REQUIRE(value == 500);
   }
 }
+
+TEST_CASE("RecLookupRecord - Concurrent metric registration", 
"[librecords][RecLookup]")
+{
+  constexpr char record_name[]  = "proxy.test.concurrent.string_value";
+  constexpr char record_value[] = "stable";
+
+  REQUIRE(RecRegisterConfigString(RECT_CONFIG, record_name, record_value, 
RECU_DYNAMIC, RECC_NULL, nullptr, REC_SOURCE_NULL) ==
+          REC_ERR_OKAY);
+
+  std::atomic<bool> finished{false};
+  std::thread       register_metrics([&]() {
+    for (int i = 0; i < 100000; ++i) {
+      ts::Metrics::Counter::createSpan(1);
+    }
+    finished.store(true, std::memory_order_release);
+  });
+
+  bool all_lookups_succeeded = true;
+
+  do {
+    if (RecGetRecordStringAlloc(record_name) != record_value) {
+      all_lookups_succeeded = false;
+      break;
+    }
+  } while (!finished.load(std::memory_order_acquire));
+  register_metrics.join();
+
+  CHECK(all_lookups_succeeded);
+}

Reply via email to