Skip to content

Commit 4d7b2ae

Browse files
committed
Merge remote-tracking branch 'origin/candidate-10.0.x'
Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
2 parents c4baf6a + 10dbcc1 commit 4d7b2ae

39 files changed

+541
-196
lines changed

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,14 @@ if(PLATFORM OR CLIENTTOOLS OR REMBED)
552552
install(FILES ${HPCC_SOURCE_DIR}/${LICENSE_FILE} DESTINATION "." COMPONENT Runtime)
553553
endif()
554554

555+
# Install jemalloc library from vcpkg
556+
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND PLATFORM)
557+
file(GLOB JEMALLOC_LIBS "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/libjemalloc.so*")
558+
if (JEMALLOC_LIBS)
559+
install(FILES ${JEMALLOC_LIBS} DESTINATION "lib")
560+
endif()
561+
endif()
562+
555563
add_subdirectory(devel)
556564

557565
#uninstall target

common/eventconsumption/eventindexhotspot.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@ class CHotspotEventVisitor : public CInterfaceOf<IEventVisitor>
3535
// The lowest and highest bucket number with observed activity.
3636
using Range = std::pair<bucket_type, bucket_type>;
3737
// The observed activity for a single bucket kind.
38-
struct Activity
38+
class Activity
3939
{
40+
public:
4041
Buckets buckets;
4142
Range range{std::numeric_limits<bucket_type>::max(), 0};
43+
44+
Activity() = default;
45+
bool hasActivity() const
46+
{
47+
return !buckets.empty();
48+
}
4249
};
4350
public:
4451
CActivity(CHotspotEventVisitor& _container, __uint64 _id)
@@ -62,11 +69,13 @@ class CHotspotEventVisitor : public CInterfaceOf<IEventVisitor>
6269
void forEachBucket(IBucketVisitor& visitor)
6370
{
6471
const char* path = container.operation.queryMetaInfoState().queryFilePath(id);
72+
if (!hasActivity()) // suppress inactive files
73+
return;
6574
visitor.arrive(id, path);
6675
for (unsigned kind = 0; kind < NumNodeKinds; kind++)
6776
{
6877
Activity& act = activity[kind];
69-
if (act.range.first != std::numeric_limits<bucket_type>::max() && !act.buckets.empty())
78+
if (act.hasActivity())
7079
{
7180
for (bucket_type bucket = act.range.first; bucket <= act.range.second; bucket++)
7281
{
@@ -102,6 +111,16 @@ class CHotspotEventVisitor : public CInterfaceOf<IEventVisitor>
102111
return 0;
103112
}
104113

114+
bool hasActivity() const
115+
{
116+
for (const Activity& act : activity)
117+
{
118+
if (act.hasActivity())
119+
return true;
120+
}
121+
return false;
122+
}
123+
105124
private:
106125
CHotspotEventVisitor& container; // back reference to obtain shared configuration
107126
__uint64 id{0};

common/eventconsumption/eventindexsummarize.cpp

Lines changed: 126 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,11 @@ class CTraceCollector : public CSummaryCollector
665665
public: // IEventVisitor
666666
virtual bool visitEvent(CEvent& event) override
667667
{
668+
// Allow EventQueryStart events to pass through; trace ID to service name
669+
// mapping is built earlier by the metadata parser (CMetaInfoState::visitEvent)
670+
if (event.queryType() == EventQueryStart)
671+
return true;
672+
668673
// Implicit event filter applied unconditionally
669674
if (queryEventContext(event.queryType()) != EventCtxIndex)
670675
return true;
@@ -688,7 +693,7 @@ class CTraceCollector : public CSummaryCollector
688693
virtual void summarize() override
689694
{
690695
// Determine which node kinds have data
691-
bool haveNodeKindEntries[NumNodeKinds] = {false,};
696+
std::fill_n(haveNodeKindEntries, NumNodeKinds, false);
692697
for (Cache::value_type& entry : stats)
693698
{
694699
TraceStats& traceStats = entry.second;
@@ -701,23 +706,18 @@ class CTraceCollector : public CSummaryCollector
701706
}
702707
}
703708
}
709+
if (summarization == IndexSummarization::byService)
710+
summarizeByService();
711+
else
712+
summarizeByTrace();
713+
}
704714

715+
protected:
716+
void summarizeByTrace()
717+
{
705718
StringBuffer line;
706719
appendCSVColumns(line, "Trace ID", "Service Name", "First Timestamp", "Last Timestamp", "Unique Files");
707-
708-
// Add aggregate statistics headers
709-
appendNodeKindHeaders(line, "Total ");
710-
711-
// Add node kind specific headers only for kinds that have data
712-
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
713-
{
714-
if (!haveNodeKindEntries[nodeKind])
715-
continue;
716-
const char* nodeKindName = mapNodeKind((NodeKind)nodeKind);
717-
VStringBuffer prefix("%c%s ", toupper(*nodeKindName), nodeKindName + 1);
718-
appendNodeKindHeaders(line, prefix);
719-
}
720-
720+
appendNodeKindHeaders(line);
721721
outputLine(line);
722722

723723
for (Cache::value_type& entry : stats)
@@ -749,7 +749,6 @@ class CTraceCollector : public CSummaryCollector
749749

750750
// Create aggregate statistics across all node kinds
751751
NodeKindStats aggregateStats = {};
752-
std::unordered_map<IndexHashKey, uint32_t, IndexHashKeyHash> allNodeMemory;
753752

754753
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
755754
aggregateStats.addStats(traceStats.kinds[nodeKind]);
@@ -770,7 +769,115 @@ class CTraceCollector : public CSummaryCollector
770769
}
771770
}
772771

772+
void summarizeByService()
773+
{
774+
// Extended TraceStats with trace counter for service summarization
775+
class ServiceStats : public TraceStats
776+
{
777+
public:
778+
uint32_t traceCount{0}; // Count of traces represented in this service
779+
NodeKindStats totals;
780+
781+
ServiceStats() = default;
782+
void addStats(const TraceStats& other)
783+
{
784+
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
785+
{
786+
const NodeKindStats& nodeKindStats = other.kinds[nodeKind];
787+
kinds[nodeKind].addStats(nodeKindStats);
788+
totals.addStats(nodeKindStats);
789+
}
790+
}
791+
};
792+
793+
// Aggregate trace stats by service name
794+
std::unordered_map<std::string, ServiceStats> serviceStats;
795+
796+
for (Cache::value_type& entry : stats)
797+
{
798+
const TraceHashKey& traceKey = entry.first;
799+
TraceStats& traceStats = entry.second;
800+
801+
// Find service name for this trace ID, or use empty string if not found
802+
const char* serviceName = operation.queryMetaInfoState().queryServiceName(traceKey.traceId.str());
803+
if (!serviceName)
804+
serviceName = "";
805+
ServiceStats& serviceAggregateStats = serviceStats[serviceName];
806+
807+
// Aggregate the stats
808+
serviceAggregateStats.addStats(traceStats);
809+
810+
// Increment trace count
811+
serviceAggregateStats.traceCount++;
812+
813+
// Merge unique files
814+
serviceAggregateStats.uniqueFileIds.insert(traceStats.uniqueFileIds.begin(), traceStats.uniqueFileIds.end());
815+
816+
// Update timestamp ranges
817+
if (traceStats.firstTimestamp != UINT64_MAX)
818+
serviceAggregateStats.firstTimestamp = std::min(serviceAggregateStats.firstTimestamp, traceStats.firstTimestamp);
819+
if (traceStats.lastTimestamp > 0)
820+
serviceAggregateStats.lastTimestamp = std::max(serviceAggregateStats.lastTimestamp, traceStats.lastTimestamp);
821+
}
822+
823+
// Output aggregated service stats
824+
StringBuffer line;
825+
appendCSVColumns(line, "Service Name", "Trace Count", "First Timestamp", "Last Timestamp", "Unique Files");
826+
appendNodeKindHeaders(line);
827+
outputLine(line);
828+
829+
for (auto& serviceEntry : serviceStats)
830+
{
831+
const std::string& serviceName = serviceEntry.first;
832+
ServiceStats& serviceStatsEntry = serviceEntry.second;
833+
834+
// Format timestamps using CDateTime (empty if no valid timestamps were found)
835+
StringBuffer firstTsStr, lastTsStr;
836+
if (serviceStatsEntry.firstTimestamp != UINT64_MAX)
837+
{
838+
CDateTime firstDt;
839+
firstDt.setTimeStampNs(serviceStatsEntry.firstTimestamp);
840+
firstDt.getString(firstTsStr);
841+
}
842+
if (serviceStatsEntry.lastTimestamp > 0)
843+
{
844+
CDateTime lastDt;
845+
lastDt.setTimeStampNs(serviceStatsEntry.lastTimestamp);
846+
lastDt.getString(lastTsStr);
847+
}
848+
849+
const char* displayServiceName = serviceName.empty() ? "<no service>" : serviceName.c_str();
850+
appendCSVColumns(line, displayServiceName, serviceStatsEntry.traceCount, firstTsStr.str(), lastTsStr.str(), serviceStatsEntry.uniqueFileIds.size());
851+
appendNodeKindData(line, serviceStatsEntry.totals);
852+
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
853+
{
854+
if (!haveNodeKindEntries[nodeKind])
855+
continue;
856+
const NodeKindStats& nodeStats = serviceStatsEntry.kinds[nodeKind];
857+
appendNodeKindData(line, nodeStats);
858+
}
859+
outputLine(line);
860+
}
861+
}
862+
773863
protected:
864+
865+
void appendNodeKindHeaders(StringBuffer& line)
866+
{
867+
// Add aggregate statistics headers
868+
appendNodeKindHeaders(line, "Total ");
869+
870+
// Add node kind specific headers only for kinds that have data
871+
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
872+
{
873+
if (!haveNodeKindEntries[nodeKind])
874+
continue;
875+
const char* nodeKindName = mapNodeKind((NodeKind)nodeKind);
876+
VStringBuffer prefix("%c%s ", toupper(*nodeKindName), nodeKindName + 1);
877+
appendNodeKindHeaders(line, prefix);
878+
}
879+
}
880+
774881
void appendNodeKindHeaders(StringBuffer& line, const char* prefix)
775882
{
776883
appendCSVEventsHeaders(line, prefix);
@@ -807,6 +914,8 @@ class CTraceCollector : public CSummaryCollector
807914
protected:
808915
using Cache = std::unordered_map<TraceHashKey, TraceStats, TraceHashKeyHash>;
809916
Cache stats;
917+
bool haveNodeKindEntries[NumNodeKinds] = {false,};
918+
810919
};
811920

812921
bool CIndexFileSummary::doOp()
@@ -820,6 +929,7 @@ bool CIndexFileSummary::doOp()
820929
collector.setown(new CNodeCollector(*this, summarization, out));
821930
break;
822931
case IndexSummarization::byTrace:
932+
case IndexSummarization::byService:
823933
collector.setown(new CTraceCollector(*this, summarization, out));
824934
break;
825935
default:

common/eventconsumption/eventindexsummarize.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ enum class IndexSummarization
3030
byNodeKind,
3131
byNode,
3232
byTrace,
33+
byService,
3334
};
3435

3536
class event_decl CIndexFileSummary : public CEventConsumingOp

common/workunit/workunit.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14571,7 +14571,8 @@ void executeThorGraph(const char * graphName, IConstWorkUnit &workunit, const IP
1457114571
params.push_back({ "_HPCC_JOB_VERSION_", optPlatformVersion.str() });
1457214572

1457314573
VStringBuffer job("%s-%s", wuid.str(), graphName);
14574-
k8s::runJob("thormanager", wuid, job, params);
14574+
bool wasScheduled = false;
14575+
k8s::runJob("thormanager", wuid, job, params, wasScheduled);
1457514576
}
1457614577

1457714578
/* In k8s, Thor feeds back the terminating exception via the workunit.

docs/EN_US/ECLLanguageReference/ECLR_mods/BltInFunc-TOJSON.xml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
<sect1 id="TOJSON">
55
<title>TOJSON</title>
66

7-
<para><emphasis role="bold">TOJSON<indexterm>
7+
<para><emphasis role="bold">TOJSON(</emphasis><emphasis> record
8+
</emphasis><emphasis role="bold">[,ALL])</emphasis><indexterm>
89
<primary>TOJSON</primary>
910
</indexterm><indexterm>
1011
<primary>TOJSON function</primary>
11-
</indexterm>(</emphasis><emphasis> record </emphasis><emphasis
12-
role="bold">)</emphasis></para>
12+
</indexterm></para>
1313

1414
<para><informaltable colsep="1" frame="all" rowsep="1">
1515
<tgroup cols="2">
16-
<colspec colwidth="77.95pt" />
16+
<colspec colwidth="77.95pt"/>
1717

1818
<tbody>
1919
<row>
@@ -22,6 +22,15 @@
2222
<entry>The row (record) of data to convert to JSON format.</entry>
2323
</row>
2424

25+
<row>
26+
<entry><emphasis role="bold">ALL</emphasis></entry>
27+
28+
<entry>Specifies fields with empty strings are included in the
29+
output. Without the ALL flag, <emphasis
30+
role="bold">TOJSON</emphasis> omits fields that contain only
31+
whitespace or are empty after trimming.</entry>
32+
</row>
33+
2534
<row>
2635
<entry>Return:</entry>
2736

docs/EN_US/ECLLanguageReference/ECLR_mods/BltInFunc-TOXML.xml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
<sect1 id="TOXML">
55
<title>TOXML</title>
66

7-
<para><emphasis role="bold">TOXML<indexterm>
7+
<para><emphasis role="bold">TOXML(</emphasis><emphasis> record
8+
</emphasis><emphasis role="bold">[,ALL])</emphasis><indexterm>
89
<primary>TOXML</primary>
910
</indexterm><indexterm>
1011
<primary>TOXML function</primary>
11-
</indexterm>(</emphasis><emphasis> record </emphasis><emphasis
12-
role="bold">)</emphasis></para>
12+
</indexterm></para>
1313

1414
<para><informaltable colsep="1" frame="all" rowsep="1">
1515
<tgroup cols="2">
16-
<colspec colwidth="77.95pt" />
16+
<colspec colwidth="77.95pt"/>
1717

1818
<tbody>
1919
<row>
@@ -23,6 +23,15 @@
2323
format.</entry>
2424
</row>
2525

26+
<row>
27+
<entry><emphasis role="bold">ALL</emphasis></entry>
28+
29+
<entry>Specifies fields with empty strings are included in the
30+
output. Without the ALL flag, <emphasis
31+
role="bold">TOXML</emphasis> omits fields that contain only
32+
whitespace or are empty after trimming.</entry>
33+
</row>
34+
2635
<row>
2736
<entry>Return:</entry>
2837

@@ -32,8 +41,6 @@
3241
</tgroup>
3342
</informaltable></para>
3443

35-
<para></para>
36-
3744
<para>The <emphasis role="bold">TOXML </emphasis>function returns a single
3845
UTF-8 string with the data in the <emphasis>record</emphasis> re-formatted
3946
as XML. If the RECORD structure of the <emphasis>record</emphasis> has

docs/common/Version.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
<chapterinfo>
66
<date id="DateVer">DEVELOPER NON-GENERATED VERSION</date>
77

8-
<releaseinfo id="FooterInfo"2025 HPCC
8+
<releaseinfo id="FooterInfo"2026 HPCC
99
Systems<superscript>®</superscript>. All rights reserved</releaseinfo>
1010

1111
<copyright id="Copyright">
12-
<year>2025 HPCC Systems<superscript>®</superscript>. All rights
12+
<year>2026 HPCC Systems<superscript>®</superscript>. All rights
1313
reserved</year>
1414
</copyright>
1515
</chapterinfo>
@@ -23,9 +23,9 @@
2323
serve one purpose and that is to store the chapterinfo and other information
2424
used by several document components.</para>
2525

26-
<para id="CHMVer">2025 Version ${DOC_VERSION}</para>
26+
<para id="CHMVer">2026 Version ${DOC_VERSION}</para>
2727

28-
<remark id="YrVer">2025</remark>
28+
<remark id="YrVer">2026</remark>
2929

3030
<para>The following line is the code to be put into the document you wish
3131
to include the above version info in:</para>

0 commit comments

Comments
 (0)