From 91a2010a340c7ce7363b1f9b43cd94d790290745 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 14 Dec 2022 07:32:59 -0800 Subject: [PATCH] Add .cluster.idempotency_ids to status json --- .../source/mr-status-json-schemas.rst.inc | 7 +++ fdbclient/IdempotencyId.actor.cpp | 61 +++++++++++++++++++ fdbclient/Schemas.cpp | 9 ++- .../include/fdbclient/IdempotencyId.actor.h | 6 +- fdbserver/Status.actor.cpp | 4 ++ 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index 70c487be0b..30eeea3530 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -902,6 +902,13 @@ "num_tenants" : 1, // on data cluster, local count; on management cluster, total metacluster count "num_tenant_groups" : 10, "tenant_group_capacity" : 20, + }, + "idempotency_ids" : { + "size_bytes" : 0, // An estimate of the current number of bytes used in the database to store idempotency ids. + "expired_version" : 0, // The commit status of a transaction whose commit version could be <= expired_version can no longer be determined. + "expired_age" : 0, // The age in seconds of expired_version. + "oldest_id_version" : 0, // The version of the oldest idempotency id still stored in the database. + "oldest_id_age" : 0 // The age in seconds of the oldest_id_version. } }, "client":{ diff --git a/fdbclient/IdempotencyId.actor.cpp b/fdbclient/IdempotencyId.actor.cpp index e0c4839b45..8c817aa08a 100644 --- a/fdbclient/IdempotencyId.actor.cpp +++ b/fdbclient/IdempotencyId.actor.cpp @@ -202,4 +202,65 @@ void decodeIdempotencyKey(KeyRef key, Version& commitVersion, uint8_t& highOrder reader >> commitVersion; commitVersion = bigEndian64(commitVersion); reader >> highOrderBatchIndex; +} + +// Find the youngest or oldest idempotency id key in `range` (depending on `reverse`) +// Write the timestamp to `*time` and the version to `*version` when non-null. +ACTOR static Future> getBoundary(Reference tr, + KeyRange range, + Reverse reverse, + Version* version, + int64_t* time) { + RangeResult result = wait(tr->getRange(range, /*limit*/ 1, Snapshot::False, reverse)); + if (!result.size()) { + return Optional(); + } + if (version != nullptr) { + BinaryReader rd(result.front().key, Unversioned()); + rd.readBytes(idempotencyIdKeys.begin.size()); + rd >> *version; + *version = bigEndian64(*version); + } + if (time != nullptr) { + BinaryReader rd(result.front().value, IncludeVersion()); + rd >> *time; + } + return result.front().key; +} + +ACTOR Future getIdmpKeyStatus(Database db) { + state Reference tr = makeReference(db); + state int64_t size; + state IdempotencyIdsExpiredVersion expired; + state KeyBackedObjectProperty expiredKey(idempotencyIdsExpiredVersion, + Unversioned()); + state int64_t oldestIdVersion = 0; + state int64_t oldestIdTime = 0; + loop { + try { + tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS); + tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE); + + wait(store(size, tr->getEstimatedRangeSizeBytes(idempotencyIdKeys)) && + store(expired, expiredKey.getD(tr)) && + success(getBoundary(tr, idempotencyIdKeys, Reverse::False, &oldestIdVersion, &oldestIdTime))); + JsonBuilderObject result; + result["size_bytes"] = size; + if (expired.expired != 0) { + result["expired_version"] = expired.expired; + } + if (expired.expiredTime != 0) { + result["expired_age"] = int64_t(now()) - expired.expiredTime; + } + if (oldestIdVersion != 0) { + result["oldest_id_version"] = oldestIdVersion; + } + if (oldestIdTime != 0) { + result["oldest_id_age"] = int64_t(now()) - oldestIdTime; + } + return result; + } catch (Error& e) { + wait(tr->onError(e)); + } + } } \ No newline at end of file diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index efe3606f7d..4e84fd004e 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -602,8 +602,6 @@ const KeyRef JSONSchemas::statusSchema = R"statusSchema( "description":"abc" } ], -)statusSchema" - R"statusSchema( "recovery_state":{ "seconds_since_last_recovered":1, "required_resolvers":1, @@ -976,6 +974,13 @@ const KeyRef JSONSchemas::statusSchema = R"statusSchema( "num_tenants":0, "num_tenant_groups":10, "tenant_group_capacity":20 + }, + "idempotency_ids":{ + "size_bytes": 0, + "expired_version": 0, + "expired_age": 0, + "oldest_id_version": 0, + "oldest_id_age": 0 } }, "client":{ diff --git a/fdbclient/include/fdbclient/IdempotencyId.actor.h b/fdbclient/include/fdbclient/IdempotencyId.actor.h index de94b01479..732b02f9e0 100644 --- a/fdbclient/include/fdbclient/IdempotencyId.actor.h +++ b/fdbclient/include/fdbclient/IdempotencyId.actor.h @@ -29,6 +29,7 @@ #pragma once #include "fdbclient/FDBTypes.h" +#include "fdbclient/JsonBuilder.h" #include "fdbclient/PImpl.h" #include "flow/Arena.h" #include "flow/IRandom.h" @@ -44,10 +45,11 @@ struct CommitResult { struct IdempotencyIdsExpiredVersion { static constexpr auto file_identifier = 3746945; Version expired = 0; + int64_t expiredTime = 0; template void serialize(Archive& ar) { - serializer(ar, expired); + serializer(ar, expired, expiredTime); } }; @@ -185,5 +187,7 @@ KeyRangeRef makeIdempotencySingleKeyRange(Arena& arena, Version version, uint8_t void decodeIdempotencyKey(KeyRef key, Version& commitVersion, uint8_t& highOrderBatchIndex); +ACTOR Future getIdmpKeyStatus(Database db); + #include "flow/unactorcompiler.h" #endif diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index 7108a606c0..05f5ba5123 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -3124,6 +3124,8 @@ ACTOR Future clusterGetStatus( state JsonBuilderObject recoveryStateStatus = wait( recoveryStateStatusFetcher(cx, ccWorker, mWorker, workers.size(), &status_incomplete_reasons, &statusCode)); + state JsonBuilderObject idmpKeyStatus = wait(getIdmpKeyStatus(cx)); + // machine metrics state WorkerEvents mMetrics = workerEventsVec[0].present() ? workerEventsVec[0].get().first : WorkerEvents(); // process metrics @@ -3505,6 +3507,8 @@ ACTOR Future clusterGetStatus( if (!recoveryStateStatus.empty()) statusObj["recovery_state"] = recoveryStateStatus; + statusObj["idempotency_ids"] = idmpKeyStatus; + // cluster messages subsection; JsonBuilderArray clientIssuesArr = getClientIssuesAsMessages(clientStatus); if (clientIssuesArr.size() > 0) {