Freeze perfdata arrays and remove locks in code using them

Since perfdata is set once when a check result is created and
never changed again, locking this is unnecessary. This avoids
components unnecessarily waiting on each other when processing
perfdata.

This fixes the locking cascade observed sometimes when the perfdata
writer work queue blocks, where it extends to a lock on the entire
check result eventually, affecting even more components.
This commit is contained in:
Johannes Schmidt 2026-05-19 12:11:04 +02:00
parent 6c73bb4af4
commit 80362fa2c8
14 changed files with 20 additions and 17 deletions

View file

@ -33,3 +33,9 @@ double CheckResult::CalculateLatency() const
return latency;
}
void CheckResult::SetPerformanceData(const Array::Ptr& value)
{
value->Freeze();
ObjectImpl<CheckResult>::SetPerformanceData(value);
}

View file

@ -22,6 +22,7 @@ public:
double CalculateExecutionTime() const;
double CalculateLatency() const;
void SetPerformanceData(const Array::Ptr& value);
};
}

View file

@ -185,8 +185,6 @@ String PluginUtility::FormatPerfdata(const Array::Ptr& perfdata, bool normalize)
std::ostringstream result;
ObjectLock olock(perfdata);
bool first = true;
for (const Value& pdv : perfdata) {
if (!first)

View file

@ -174,8 +174,6 @@ static void DoIfwNetIo(
}
if (perfdata) {
ObjectLock oLock (perfdata);
for (auto& pv : perfdata) {
if (!pv.IsString()) {
cr->SetOutput(

View file

@ -202,7 +202,6 @@ void ElasticsearchWriter::AddCheckResult(const Dictionary::Ptr& fields, const Ch
CheckCommand::Ptr checkCommand = checkable->GetCheckCommand();
if (perfdata) {
ObjectLock olock(perfdata);
for (const Value& val : perfdata) {
PerfdataValue::Ptr pdv;

View file

@ -196,7 +196,6 @@ void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const Check
Array::Ptr perfdata = cr->GetPerformanceData();
if (perfdata) {
ObjectLock olock(perfdata);
for (const Value& val : perfdata) {
PerfdataValue::Ptr pdv;

View file

@ -224,7 +224,6 @@ void GraphiteWriter::SendPerfdata(const Checkable::Ptr& checkable, const String&
CheckCommand::Ptr checkCommand = checkable->GetCheckCommand();
ObjectLock olock(perfdata);
for (const Value& val : perfdata) {
PerfdataValue::Ptr pdv;

View file

@ -213,7 +213,6 @@ void InfluxdbCommonWriter::CheckResultHandler(const Checkable::Ptr& checkable, c
double ts = cr->GetExecutionEnd();
if (Array::Ptr perfdata = cr->GetPerformanceData()) {
ObjectLock olock(perfdata);
for (const Value& val : perfdata) {
PerfdataValue::Ptr pdv;

View file

@ -308,7 +308,6 @@ void OpenTsdbWriter::AddPerfdata(const Checkable::Ptr& checkable, const String&
CheckCommand::Ptr checkCommand = checkable->GetCheckCommand();
ObjectLock olock(perfdata);
for (const Value& val : perfdata) {
PerfdataValue::Ptr pdv;

View file

@ -181,7 +181,6 @@ void OTLPMetricsWriter::CheckResultHandler(const Checkable::Ptr& checkable, cons
auto endTime = cr->GetExecutionEnd();
Array::Ptr perfdata = cr->GetPerformanceData();
ObjectLock olock(perfdata);
for (const Value& val : perfdata) {
PerfdataValue::Ptr pdv;
if (val.IsObjectType<PerfdataValue>()) {

View file

@ -38,6 +38,7 @@ BOOST_AUTO_TEST_CASE(quotes)
BOOST_AUTO_TEST_CASE(multiple)
{
Array::Ptr pd = PluginUtility::SplitPerfdata("testA=123456 testB=123456");
pd->Freeze();
BOOST_CHECK_EQUAL(pd->GetLength(), 2);
String str = PluginUtility::FormatPerfdata(pd);
@ -47,12 +48,14 @@ BOOST_AUTO_TEST_CASE(multiple)
BOOST_AUTO_TEST_CASE(multiline)
{
Array::Ptr pd = PluginUtility::SplitPerfdata(" 'testA'=123456 'testB'=123456");
pd->Freeze();
BOOST_CHECK_EQUAL(pd->GetLength(), 2);
String str = PluginUtility::FormatPerfdata(pd);
BOOST_CHECK_EQUAL(str, "testA=123456 testB=123456");
pd = PluginUtility::SplitPerfdata(" 'testA'=123456 \n'testB'=123456");
pd->Freeze();
BOOST_CHECK_EQUAL(pd->GetLength(), 2);
str = PluginUtility::FormatPerfdata(pd);
@ -62,6 +65,7 @@ BOOST_AUTO_TEST_CASE(multiline)
BOOST_AUTO_TEST_CASE(normalize)
{
Array::Ptr pd = PluginUtility::SplitPerfdata("testA=2m;3;4;1;5 testB=2foobar");
pd->Freeze();
BOOST_CHECK_EQUAL(pd->GetLength(), 2);
String str = PluginUtility::FormatPerfdata(pd, true);
@ -333,6 +337,7 @@ BOOST_AUTO_TEST_CASE(ignore_warn_crit_ranges)
BOOST_AUTO_TEST_CASE(empty_warn_crit_min_max)
{
Array::Ptr pd = PluginUtility::SplitPerfdata("testA=5;;7;1;9 testB=5;7;;1;9 testC=5;;;1;9 testD=2m;;;1 testE=5;;7;;");
pd->Freeze();
BOOST_CHECK_EQUAL(pd->GetLength(), 5);
String str = PluginUtility::FormatPerfdata(pd, true);

View file

@ -63,7 +63,7 @@ object Host "h1" {
void ReceiveCheckResults(
std::size_t num,
ServiceState state,
const std::function<void(const CheckResult::Ptr&)>& fn = {}
const std::function<void(const CheckResult::Ptr&, const Array::Ptr&)>& fn = {}
)
{
::ReceiveCheckResults(m_Host, num, state, fn);
@ -95,8 +95,8 @@ object Host "h1" {
auto start = std::chrono::steady_clock::now();
std::size_t unchangedCount = 0;
while(true){
ReceiveCheckResults(10, ServiceCritical, [&](const CheckResult::Ptr& cr) {
cr->GetPerformanceData()->Add(new PerfdataValue{GetRandomString("", 4096), 1});
ReceiveCheckResults(10, ServiceCritical, [&](const CheckResult::Ptr&, const Array::Ptr& perfdata) {
perfdata->Add(new PerfdataValue{GetRandomString("", 4096), 1});
});
if (std::chrono::steady_clock::now() - start >= timeout) {

View file

@ -93,7 +93,7 @@ void ReceiveCheckResults(
const icinga::Checkable::Ptr& host,
std::size_t num,
icinga::ServiceState state,
const std::function<void(const icinga::CheckResult::Ptr&)>& fn
const std::function<void(const icinga::CheckResult::Ptr&, const icinga::Array::Ptr&)>& fn
)
{
using namespace icinga;
@ -114,12 +114,13 @@ void ReceiveCheckResults(
Array::Ptr perfData = new Array;
perfData->Add(new PerfdataValue{"dummy", 42});
cr->SetPerformanceData(perfData);
if (fn) {
fn(cr);
fn(cr, perfData);
}
cr->SetPerformanceData(perfData);
BOOST_REQUIRE(host->ProcessCheckResult(cr, wg) == Checkable::ProcessingResult::Ok);
}
}

View file

@ -33,5 +33,5 @@ void ReceiveCheckResults(
const icinga::Checkable::Ptr& host,
std::size_t num,
icinga::ServiceState state,
const std::function<void(const icinga::CheckResult::Ptr&)>& fn = {}
const std::function<void(const icinga::CheckResult::Ptr&, const icinga::Array::Ptr&)>& fn = {}
);