Skip to content

Commit e3fb656

Browse files
committed
Merge branch 'devel' of github.com:ORNL/DataFed into devel
2 parents d6c7636 + 0a1a509 commit e3fb656

File tree

4 files changed

+299
-69
lines changed

4 files changed

+299
-69
lines changed

core/server/DatabaseAPI.cpp

Lines changed: 82 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3656,91 +3656,105 @@ void DatabaseAPI::taskPurge(uint32_t a_age_sec, LogContext log_context) {
36563656
}
36573657
*/
36583658

3659+
/**
3660+
* @brief Converts client metrics into a structured JSON string.
3661+
*
3662+
* This method takes a timestamp, a total count, and a nested map of client
3663+
* metrics, and produces a JSON string with the following structure:
3664+
*
3665+
* - `timestamp`: the timestamp provided.
3666+
* - `total`: the total count provided.
3667+
* - `uids` (or clients): a JSON object where each key is a client ID and the
3668+
* value is an object containing:
3669+
* - `tot`: subtotal for the client (from message type 0, if present)
3670+
* - `msg`: an object mapping message types (as strings) to counts.
3671+
*
3672+
* @param a_timestamp The timestamp to include in the JSON payload.
3673+
* @param a_total The total count across all clients (sum of all subtotals).
3674+
* @param a_metrics A map where:
3675+
* - The first key (`std::string`) is the client ID.
3676+
* - The nested map (`std::map<uint16_t, uint32_t>`) maps:
3677+
* - Key: message type (uint16_t), where `0` is reserved for subtotal.
3678+
* - Value: count of messages for that type, at key `0` it is the
3679+
* subtotal.
3680+
* - If a client has no metrics, the nested map will be empty.
3681+
* - Message type `0` is used as a subtotal and is never a valid message type.
3682+
*
3683+
* @return A formatted JSON string representing the clients and their message
3684+
* counts.
3685+
*
3686+
* @note The JSON structure will always include the `"uids"` object, even if
3687+
* empty.
3688+
* @note Message type keys in `"msg"` are converted to strings to ensure valid
3689+
* JSON objects.
3690+
*
3691+
* In the below example total is equivalent to subtotal because there is only
3692+
* one client.
3693+
*
3694+
* @example
3695+
* Input:
3696+
* @code
3697+
* timestamp = 111
3698+
* total = 15
3699+
* metrics = {
3700+
* {"client1", {{0, 15}, {1, 10}, {2, 5}}}
3701+
* }
3702+
* @endcode
3703+
*
3704+
* Output JSON:
3705+
* @code
3706+
* {
3707+
* "timestamp": 111,
3708+
* "total": 15,
3709+
* "uids": {
3710+
* "client1": {
3711+
* "tot": 15,
3712+
* "msg": {
3713+
* "1": 10,
3714+
* "2": 5
3715+
* }
3716+
* }
3717+
* }
3718+
* }
3719+
* @endcode
3720+
*/
36593721
std::string DatabaseAPI::newJsonMetricParse(
36603722
uint32_t a_timestamp, uint32_t a_total,
36613723
const std::map<std::string, std::map<uint16_t, uint32_t>> &a_metrics) {
3662-
map<string, std::map<uint16_t, uint32_t>>::const_iterator u;
3663-
map<uint16_t, uint32_t>::const_iterator m;
3724+
36643725
nlohmann::json payload;
3665-
payload["timestamp"] = to_string(a_timestamp);
3666-
payload["total"] = to_string(a_total);
3667-
3668-
nlohmann::json uids;
3669-
for (u = a_metrics.begin(); u != a_metrics.end(); ++u) {
3670-
nlohmann::json uid_body;
3671-
uid_body["tot"] = to_string(u->second.at(0));
3672-
nlohmann::json uid_msg;
3673-
for (m = u->second.begin(); m != u->second.end(); ++m) {
3674-
if (m->first != 0) {
3675-
uid_msg[to_string(m->first)] = to_string(m->second);
3676-
}
3677-
}
3678-
uid_body["msg"] = uid_msg;
3726+
payload["timestamp"] = a_timestamp;
3727+
payload["total"] = a_total;
36793728

3680-
uids[u->first] = uid_body;
3681-
}
3729+
nlohmann::json clients_json = nlohmann::json::object();
36823730

3683-
payload["uids"] = uids;
3684-
string body = payload.dump(-1, ' ', true);
3685-
return body;
3686-
}
3731+
for (const auto &[client_id, client_metrics] : a_metrics) {
3732+
nlohmann::json client_json;
36873733

3688-
// TODO: verify and remove
3689-
std::string DatabaseAPI::oldJsonMetricParse(
3690-
uint32_t a_timestamp, uint32_t a_total,
3691-
const std::map<std::string, std::map<uint16_t, uint32_t>> &a_metrics) {
3692-
map<string, std::map<uint16_t, uint32_t>>::const_iterator u;
3693-
map<uint16_t, uint32_t>::const_iterator m;
3694-
string body = "{\"timestamp\":" + to_string(a_timestamp) +
3695-
",\"total\":" + to_string(a_total) + ",\"uids\":{";
3696-
bool c = false, cc;
3697-
3698-
for (u = a_metrics.begin(); u != a_metrics.end(); ++u) {
3699-
if (c)
3700-
body += ",";
3701-
else
3702-
c = true;
3703-
3704-
body += "\"" + u->first + "\":{\"tot\":" + to_string(u->second.at(0)) +
3705-
",\"msg\":{";
3706-
3707-
for (cc = false, m = u->second.begin(); m != u->second.end(); ++m) {
3708-
if (m->first != 0) {
3709-
if (cc)
3710-
body += ",";
3711-
else
3712-
cc = true;
3713-
3714-
body += "\"" + to_string(m->first) + "\":" + to_string(m->second);
3715-
}
3734+
// Use safe access for total
3735+
auto it = client_metrics.find(0);
3736+
client_json["tot"] = (it != client_metrics.end()) ? it->second : 0;
3737+
3738+
nlohmann::json msg_json;
3739+
for (const auto &[msg_id, msg_count] : client_metrics) {
3740+
if (msg_id != 0)
3741+
msg_json[std::to_string(msg_id)] = msg_count;
37163742
}
3717-
body += "}}";
3743+
3744+
client_json["msg"] = msg_json;
3745+
clients_json[client_id] = client_json;
37183746
}
37193747

3720-
body += "}}";
3721-
return body;
3748+
payload["uids"] = clients_json;
3749+
return payload.dump(-1, ' ', true);
37223750
}
37233751

37243752
void DatabaseAPI::metricsUpdateMsgCounts(
37253753
uint32_t a_timestamp, uint32_t a_total,
37263754
const std::map<std::string, std::map<uint16_t, uint32_t>> &a_metrics,
37273755
LogContext log_context) {
37283756

3729-
string body;
3730-
string new_body = newJsonMetricParse(a_timestamp, a_total, a_metrics);
3731-
string old_body = oldJsonMetricParse(a_timestamp, a_total, a_metrics);
3732-
3733-
if (new_body == old_body) {
3734-
// on match use safer serialization
3735-
body = new_body;
3736-
} else {
3737-
body = old_body;
3738-
DL_WARNING(
3739-
log_context,
3740-
"Serialized metric bodies did not match, new serialization yielded:\n"
3741-
<< new_body << "\n old serialization yielded:\n"
3742-
<< old_body);
3743-
}
3757+
std::string body = newJsonMetricParse(a_timestamp, a_total, a_metrics);
37443758

37453759
libjson::Value result;
37463760

core/server/DatabaseAPI.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ class DatabaseAPI {
333333
LogContext);
334334
void metricsPurge(uint32_t a_timestamp, LogContext);
335335

336-
private:
336+
protected:
337337
long dbGet(const char *a_url_path,
338338
const std::vector<std::pair<std::string, std::string>> &a_params,
339339
libjson::Value &a_result, LogContext, bool a_log = true);

core/server/tests/unit/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
foreach(PROG
33
test_AuthMap
44
test_AuthenticationManager
5+
test_DatabaseAPI
56
)
67

78
file(GLOB ${PROG}_SOURCES ${PROG}*.cpp)

0 commit comments

Comments
 (0)