mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-24 15:40:26 +00:00
Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
# Conflicts: # OpenTelemetryPlan/09-data-collection-reference.md
This commit is contained in:
@@ -163,14 +163,14 @@ or, for the apply pipeline: `{resource.service.name="xrpld" && name=~"tx.preflig
|
||||
|
||||
Controlled by `trace_transactions=1` in `[telemetry]` config.
|
||||
|
||||
| Span Name | Parent | Source File | Description |
|
||||
| ------------------ | ------------- | ----------- | --------------------------------------------------- |
|
||||
| `txq.enqueue` | `tx.process` | TxQ.cpp | Enqueue decision when a tx is submitted |
|
||||
| `txq.apply_direct` | `txq.enqueue` | TxQ.cpp | Direct apply attempt that bypasses the queue |
|
||||
| `txq.batch_clear` | `txq.enqueue` | TxQ.cpp | Batch clear of an account's queued txs |
|
||||
| `txq.accept` | — | TxQ.cpp | Ledger-close accept loop (drains the queue) |
|
||||
| `txq.accept_tx` | `txq.accept` | TxQ.cpp | Per-queued-transaction apply inside the accept loop |
|
||||
| `txq.cleanup` | — | TxQ.cpp | Post-close cleanup of expired queue entries |
|
||||
| Span Name | Parent | Source File | Description |
|
||||
| ------------------ | ----------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `txq.enqueue` | `tx.process` (submission path; root on open-ledger rebuild) | TxQ.cpp | Queue admission decision (apply/queue/reject). Parents to `tx.process` via explicit context on submit; correlates via `current_ledger_seq` on all paths |
|
||||
| `txq.apply_direct` | `txq.enqueue` | TxQ.cpp | Direct apply attempt that bypasses the queue |
|
||||
| `txq.batch_clear` | `txq.enqueue` | TxQ.cpp | Batch clear of an account's queued txs |
|
||||
| `txq.accept` | — | TxQ.cpp | Ledger-close accept loop (drains the queue) |
|
||||
| `txq.accept_tx` | `txq.accept` | TxQ.cpp | Per-queued-transaction apply inside the accept loop |
|
||||
| `txq.cleanup` | — | TxQ.cpp | Post-close cleanup of expired queue entries |
|
||||
|
||||
**Where to find**: Tempo → TraceQL: `{resource.service.name="xrpld" && name=~"txq.*"}`
|
||||
|
||||
@@ -310,42 +310,47 @@ aggregation. Per the 2026-05-13 naming redesign, span-attribute keys use the
|
||||
|
||||
#### Transaction Attributes
|
||||
|
||||
| Attribute | Type | Set On | Description |
|
||||
| -------------- | ------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |
|
||||
| `tx_hash` | string | `tx.process`, `tx.receive` | Transaction hash (hex-encoded) |
|
||||
| `local` | boolean | `tx.process` | `true` if locally submitted, `false` if peer-relayed |
|
||||
| `path` | string | `tx.process` | Submission path: `"sync"` or `"async"` |
|
||||
| `tx_type` | string | `tx.process`, `tx.preflight`, `tx.preclaim`, `tx.transactor` | Transaction type name (e.g., `Payment`) |
|
||||
| `fee` | int64 | `tx.process` | Transaction fee in drops |
|
||||
| `sequence` | int64 | `tx.process` | Transaction sequence number |
|
||||
| `suppressed` | boolean | `tx.receive` | `true` if transaction was suppressed (duplicate) |
|
||||
| `tx_status` | string | `tx.receive` | Transaction status (e.g., `"known_bad"`) |
|
||||
| `peer_id` | int64 | `tx.receive` | Peer identifier (also set on peer spans) |
|
||||
| `peer_version` | string | `tx.receive` | Peer protocol version string |
|
||||
| `stage` | string | `tx.preflight`, `tx.preclaim`, `tx.transactor` | Apply-pipeline stage: `preflight`, `preclaim`, or `apply` |
|
||||
| `ter_result` | string | `tx.preflight`, `tx.preclaim`, `tx.transactor` | Engine result token for that stage (e.g., `tesSUCCESS`, `terPRE_SEQ`) |
|
||||
| `applied` | boolean | `tx.transactor` | `true` if the transaction was applied to the ledger |
|
||||
| Attribute | Type | Set On | Description |
|
||||
| --------------------- | ------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tx_hash` | string | `tx.process`, `tx.receive` | Transaction hash (hex-encoded) |
|
||||
| `local` | boolean | `tx.process` | `true` if locally submitted, `false` if peer-relayed |
|
||||
| `path` | string | `tx.process` | Submission path: `"sync"` or `"async"` |
|
||||
| `tx_type` | string | `tx.process`, `tx.preflight`, `tx.preclaim`, `tx.transactor` | Transaction type name (e.g., `Payment`) |
|
||||
| `fee` | int64 | `tx.process` | Transaction fee in drops |
|
||||
| `sequence` | int64 | `tx.process` | Transaction sequence number |
|
||||
| `suppressed` | boolean | `tx.receive` | `true` if transaction was suppressed (duplicate) |
|
||||
| `tx_status` | string | `tx.receive` | Transaction status (e.g., `"known_bad"`) |
|
||||
| `peer_id` | int64 | `tx.receive` | Peer identifier (also set on peer spans) |
|
||||
| `peer_version` | string | `tx.receive` | Peer protocol version string |
|
||||
| `stage` | string | `tx.preflight`, `tx.preclaim`, `tx.transactor` | Apply-pipeline stage: `preflight`, `preclaim`, or `apply` |
|
||||
| `ter_result` | string | `tx.preflight`, `tx.preclaim`, `tx.transactor` | Engine result token for that stage (e.g., `tesSUCCESS`, `terPRE_SEQ`) |
|
||||
| `applied` | boolean | `tx.transactor` | `true` if the transaction was applied to the ledger |
|
||||
| `current_ledger_seq` | int64 | `tx.process`, `tx.receive`, `tx.preclaim`, `tx.transactor` | Seq of the ledger being worked on (open/in-flight, not established) — joins the txID-keyed spans to the ledger trace |
|
||||
| `current_ledger_hash` | string | `tx.preclaim`, `tx.transactor` | Parent hash of that ledger (= `consensus.round` trace-id seed on the build path). View-bearing stages only; `tx.preflight` omits both |
|
||||
|
||||
**Tempo query**: `{span.tx_hash="<hash>"}` to trace a specific transaction across nodes.
|
||||
Join a transaction's work to its ledger with `{span.current_ledger_seq=<N>}`.
|
||||
|
||||
**Prometheus labels**: `local`, `suppressed`, `tx_type`, `ter_result`, `stage` (SpanMetrics dimensions).
|
||||
|
||||
#### Transaction Queue (TxQ) Attributes
|
||||
|
||||
| Attribute | Type | Set On | Description |
|
||||
| -------------------- | ------- | ------------------------------ | ----------------------------------------------------------- |
|
||||
| `tx_hash` | string | `txq.enqueue`, `txq.accept_tx` | Transaction hash |
|
||||
| `tx_type` | string | `txq.enqueue` | Transaction type name |
|
||||
| `txq_status` | string | `txq.enqueue`, `txq.accept_tx` | Queue outcome (e.g. `queued`, `applied_direct`, `rejected`) |
|
||||
| `fee_level_paid` | int64 | `txq.enqueue` | Fee level paid by the queued tx |
|
||||
| `required_fee_level` | int64 | `txq.enqueue` | Minimum fee level for inclusion |
|
||||
| `num_cleared` | int64 | `txq.batch_clear` | Entries cleared in a batch |
|
||||
| `queue_size` | int64 | `txq.accept` | Current TxQ depth |
|
||||
| `ledger_changed` | boolean | `txq.accept` | Whether the ledger changed since last attempt |
|
||||
| `ter_code` | int64 | `txq.accept_tx` | Transaction engine result code |
|
||||
| `retries_remaining` | int64 | `txq.accept_tx` | Retries left before discard |
|
||||
| `ledger_seq` | int64 | `txq.cleanup` | Ledger sequence number |
|
||||
| `expired_count` | int64 | `txq.cleanup` | Number of expired entries cleared |
|
||||
| Attribute | Type | Set On | Description |
|
||||
| --------------------- | ------- | ------------------------------ | -------------------------------------------------------------------------------- |
|
||||
| `tx_hash` | string | `txq.enqueue`, `txq.accept_tx` | Transaction hash |
|
||||
| `tx_type` | string | `txq.enqueue` | Transaction type name |
|
||||
| `current_ledger_seq` | int64 | `txq.enqueue` | Seq of the ledger being worked on — correlates the enqueue to the ledger trace |
|
||||
| `current_ledger_hash` | string | `txq.enqueue` | Parent hash of that ledger (= `consensus.round` trace-id seed on the build path) |
|
||||
| `txq_status` | string | `txq.enqueue`, `txq.accept_tx` | Queue outcome (e.g. `queued`, `applied_direct`, `rejected`) |
|
||||
| `fee_level_paid` | int64 | `txq.enqueue` | Fee level paid by the queued tx |
|
||||
| `required_fee_level` | int64 | `txq.enqueue` | Minimum fee level for inclusion |
|
||||
| `num_cleared` | int64 | `txq.batch_clear` | Entries cleared in a batch |
|
||||
| `queue_size` | int64 | `txq.accept` | Current TxQ depth |
|
||||
| `ledger_changed` | boolean | `txq.accept` | Whether the ledger changed since last attempt |
|
||||
| `ter_code` | int64 | `txq.accept_tx` | Transaction engine result code |
|
||||
| `retries_remaining` | int64 | `txq.accept_tx` | Retries left before discard |
|
||||
| `ledger_seq` | int64 | `txq.cleanup` | Ledger sequence number |
|
||||
| `expired_count` | int64 | `txq.cleanup` | Number of expired entries cleared |
|
||||
|
||||
**Prometheus label**: `txq_status` (SpanMetrics dimension).
|
||||
|
||||
|
||||
@@ -78,14 +78,14 @@ All spans instrumented in xrpld, grouped by subsystem:
|
||||
|
||||
### Transaction Spans
|
||||
|
||||
| Span Name | Source File | Attributes | Description |
|
||||
| --------------- | --------------- | --------------------------------------------------------------------------------- | ------------------------------------- |
|
||||
| `tx.process` | NetworkOPs.cpp | `tx_hash`, `local`, `path`, `tx_type`, `fee`, `sequence`, `ter_result`, `applied` | Transaction submission and processing |
|
||||
| `tx.receive` | PeerImp.cpp | `peer_id`, `tx_hash`, `tx_type`, `peer_version`, `suppressed`, `tx_status` | Transaction received from peer relay |
|
||||
| `tx.apply` | BuildLedger.cpp | `ledger_seq`, `tx_count`, `tx_failed` | Transaction set applied per ledger |
|
||||
| `tx.preflight` | applySteps.cpp | `stage`, `tx_type`, `ter_result` | Stateless checks stage |
|
||||
| `tx.preclaim` | applySteps.cpp | `stage`, `tx_type`, `ter_result` | Ledger-aware checks stage |
|
||||
| `tx.transactor` | Transactor.cpp | `stage`, `tx_type`, `ter_result`, `applied` | Apply stage (transactor runs) |
|
||||
| Span Name | Source File | Attributes | Description |
|
||||
| --------------- | --------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------- |
|
||||
| `tx.process` | NetworkOPs.cpp | `tx_hash`, `local`, `path`, `tx_type`, `fee`, `sequence`, `ter_result`, `applied`, `current_ledger_seq` | Transaction submission and processing |
|
||||
| `tx.receive` | PeerImp.cpp | `peer_id`, `tx_hash`, `tx_type`, `peer_version`, `suppressed`, `tx_status`, `current_ledger_seq` | Transaction received from peer relay |
|
||||
| `tx.apply` | BuildLedger.cpp | `ledger_seq`, `tx_count`, `tx_failed` | Transaction set applied per ledger |
|
||||
| `tx.preflight` | applySteps.cpp | `stage`, `tx_type`, `ter_result` | Stateless checks stage |
|
||||
| `tx.preclaim` | applySteps.cpp | `stage`, `tx_type`, `ter_result`, `current_ledger_seq`, `current_ledger_hash` | Ledger-aware checks stage |
|
||||
| `tx.transactor` | Transactor.cpp | `stage`, `tx_type`, `ter_result`, `applied`, `current_ledger_seq`, `current_ledger_hash` | Apply stage (transactor runs) |
|
||||
|
||||
The three apply-pipeline spans (`tx.preflight`, `tx.preclaim`, `tx.transactor`)
|
||||
share a deterministic `trace_id` from `txID[0:16]`, so they group under one
|
||||
@@ -93,16 +93,23 @@ trace per transaction. The `stage` attribute (`preflight` / `preclaim` /
|
||||
`apply`) drives the collector spanmetrics `stage` dimension, giving per-stage
|
||||
RED metrics on the _Transaction Overview_ dashboard.
|
||||
|
||||
`current_ledger_seq` is the current (open/in-flight) ledger index a span acted on
|
||||
— the ledger being worked on, not an established one — so a transaction's
|
||||
txID-keyed spans can be joined to the ledger trace it targeted
|
||||
(`span.current_ledger_seq`). The view-bearing stages (`tx.preclaim`,
|
||||
`tx.transactor`) also carry `current_ledger_hash` (the current ledger's parent
|
||||
hash); `tx.preflight` is stateless and omits both.
|
||||
|
||||
### Transaction Queue Spans
|
||||
|
||||
| Span Name | Source File | Attributes | Description |
|
||||
| ------------------ | ----------- | -------------------------------------------------------- | -------------------------------------------------- |
|
||||
| `txq.enqueue` | TxQ.cpp | `tx_hash`, `tx_type` | Transaction enqueue decision (child of tx.process) |
|
||||
| `txq.apply_direct` | TxQ.cpp | -- | Direct apply attempt (bypassing queue) |
|
||||
| `txq.batch_clear` | TxQ.cpp | -- | Batch clear of queued transactions for an account |
|
||||
| `txq.accept` | TxQ.cpp | `queue_size`, `ledger_changed` | Ledger-close accept loop over queued transactions |
|
||||
| `txq.accept_tx` | TxQ.cpp | `tx_hash`, `retries_remaining`, `ter_code`, `txq_status` | Per-transaction apply during accept |
|
||||
| `txq.cleanup` | TxQ.cpp | `ledger_seq` | Post-close cleanup of expired queue entries |
|
||||
| Span Name | Source File | Attributes | Description |
|
||||
| ------------------ | ----------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `txq.enqueue` | TxQ.cpp | `tx_hash`, `tx_type`, `current_ledger_seq`, `current_ledger_hash` | Enqueue decision; parents to `tx.process` on the submission path (explicit context), a root on the open-ledger rebuild path — `current_ledger_seq` correlates it to the ledger in both cases |
|
||||
| `txq.apply_direct` | TxQ.cpp | -- | Direct apply attempt (bypassing queue) |
|
||||
| `txq.batch_clear` | TxQ.cpp | -- | Batch clear of queued transactions for an account |
|
||||
| `txq.accept` | TxQ.cpp | `queue_size`, `ledger_changed` | Ledger-close accept loop over queued transactions |
|
||||
| `txq.accept_tx` | TxQ.cpp | `tx_hash`, `retries_remaining`, `ter_code`, `txq_status` | Per-transaction apply during accept |
|
||||
| `txq.cleanup` | TxQ.cpp | `ledger_seq` | Post-close cleanup of expired queue entries |
|
||||
|
||||
### PathFinding Spans
|
||||
|
||||
@@ -345,6 +352,28 @@ sum by (stage) (rate(span_calls_total{span_name=~"tx.preflight|tx.preclaim|tx.tr
|
||||
| "Was consensus outcome normal?" | `consensus.accept` | `consensus_state` |
|
||||
| "Did a validator bow out?" | `consensus.proposal.send` | `is_bow_out` |
|
||||
| "Which ledger was validated?" | `consensus.validation.send` | `ledger_hash` |
|
||||
| "What tx work fed a given ledger?" | `tx.*` / `txq.*` | `current_ledger_seq` |
|
||||
|
||||
### Correlating a transaction to the ledger it was worked on
|
||||
|
||||
The `tx.process`, `tx.receive`, `txq.enqueue`, `tx.preclaim`, and `tx.transactor`
|
||||
spans carry `current_ledger_seq` — the open/in-flight ledger they acted on (not
|
||||
an established ledger). Because these spans are keyed on the transaction id (their
|
||||
own trace) while the ledger/consensus spans are keyed on the ledger, use the
|
||||
attribute to bridge the two id-spaces:
|
||||
|
||||
```
|
||||
# All transaction-side work recorded against ledger N
|
||||
{span.current_ledger_seq = N}
|
||||
|
||||
# Join to the ledger build/consensus trace for the same ledger
|
||||
{name="ledger.build" && span.ledger_seq = N}
|
||||
```
|
||||
|
||||
`txq.enqueue` and the view-bearing apply stages also carry `current_ledger_hash`
|
||||
(the current ledger's parent hash), which equals the `consensus.round`
|
||||
deterministic trace-id seed on the consensus-build path. `tx.preflight` is
|
||||
stateless and omits both attributes.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -146,6 +146,19 @@ inline constexpr auto closeResolutionMs = makeStr("close_resolution_ms");
|
||||
*/
|
||||
inline constexpr auto ledgerHash = makeStr("ledger_hash");
|
||||
inline constexpr auto fullValidation = makeStr("full_validation");
|
||||
|
||||
/**
|
||||
* Shared "ledger being worked on" attrs — the open/tentative or in-flight
|
||||
* consensus-build ledger a transaction is applied into, NOT an established or
|
||||
* validated ledger (that is `ledgerSeq`, set on ledger.build / consensus.round).
|
||||
* Named after the RPC field `ledger_current_index` and the `currentLedgerSeq`
|
||||
* log usage. Reused by the tx lifecycle, apply-pipeline, and TxQ spans so a
|
||||
* transaction's work can be correlated to the ledger it targeted.
|
||||
* `currentLedgerHash` is the current view's parent-ledger hash, which equals the
|
||||
* consensus.round deterministic trace-id seed on the consensus-build path.
|
||||
*/
|
||||
inline constexpr auto currentLedgerSeq = makeStr("current_ledger_seq");
|
||||
inline constexpr auto currentLedgerHash = makeStr("current_ledger_hash");
|
||||
} // namespace attr
|
||||
|
||||
// ===== Shared attribute values =============================================
|
||||
|
||||
@@ -101,6 +101,15 @@ inline constexpr auto transactor = join(seg::tx, op::transactor);
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/**
|
||||
* Shared "ledger being worked on" attrs (defined in SpanNames.h). Set on
|
||||
* tx.preclaim and tx.transactor (both run against a view whose seq() is the
|
||||
* ledger being applied into). tx.preflight is stateless (no view) and is the
|
||||
* documented exception — it carries neither.
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::currentLedgerHash;
|
||||
using ::xrpl::telemetry::attr::currentLedgerSeq;
|
||||
|
||||
/**
|
||||
* "stage" — which apply-pipeline stage this span represents. Drives the
|
||||
* collector spanmetrics `stage` dimension for per-stage RED metrics.
|
||||
|
||||
@@ -1607,6 +1607,13 @@ Transactor::operator()()
|
||||
span.setAttribute(telemetry::tx_apply_span::attr::stage, telemetry::tx_apply_span::val::apply);
|
||||
if (auto const* fmt = TxFormats::getInstance().findByType(ctx_.tx.getTxnType()))
|
||||
span.setAttribute(telemetry::tx_apply_span::attr::txType, fmt->getName().c_str());
|
||||
// The ledger being worked on (seq + parent hash) — correlates this apply
|
||||
// stage to the ledger/consensus trace it is building into.
|
||||
span.setAttribute(
|
||||
telemetry::tx_apply_span::attr::currentLedgerSeq, static_cast<std::int64_t>(view().seq()));
|
||||
span.setAttribute(
|
||||
telemetry::tx_apply_span::attr::currentLedgerHash,
|
||||
to_string(view().header().parentHash).c_str());
|
||||
|
||||
JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID();
|
||||
|
||||
|
||||
@@ -78,9 +78,20 @@ txTypeName(TxType txnType)
|
||||
* @param name Full span name (tx_apply_span::preflight / ::preclaim).
|
||||
* @param stage Stage attribute value (tx_apply_span::val::*).
|
||||
* @param tx The transaction supplying the id and type.
|
||||
* @param curLedgerSeq Seq of the ledger being worked on, set as the
|
||||
* current_ledger_seq attribute. Passed by the
|
||||
* view-bearing stages (preclaim); nullopt for the
|
||||
* stateless preflight, which then omits it.
|
||||
* @param curLedgerParentHash Parent hash of that ledger, set as
|
||||
* current_ledger_hash. nullptr to omit.
|
||||
*/
|
||||
[[nodiscard]] telemetry::SpanGuard
|
||||
makeStageSpan(std::string_view name, std::string_view stage, STTx const& tx)
|
||||
makeStageSpan(
|
||||
std::string_view name,
|
||||
std::string_view stage,
|
||||
STTx const& tx,
|
||||
std::optional<LedgerIndex> curLedgerSeq = std::nullopt,
|
||||
uint256 const* curLedgerParentHash = nullptr)
|
||||
{
|
||||
auto const txID = tx.getTransactionID();
|
||||
auto span = telemetry::SpanGuard::hashSpan(
|
||||
@@ -92,6 +103,17 @@ makeStageSpan(std::string_view name, std::string_view stage, STTx const& tx)
|
||||
span.setAttribute(telemetry::tx_apply_span::attr::stage, stage);
|
||||
if (char const* typeName = txTypeName(tx.getTxnType()))
|
||||
span.setAttribute(telemetry::tx_apply_span::attr::txType, typeName);
|
||||
// The ledger being worked on — set only by the view-bearing stages
|
||||
// (preclaim/transactor). Preflight is stateless and passes nullopt, so
|
||||
// it carries no ledger attribute (documented exception).
|
||||
if (curLedgerSeq)
|
||||
span.setAttribute(
|
||||
telemetry::tx_apply_span::attr::currentLedgerSeq,
|
||||
static_cast<std::int64_t>(*curLedgerSeq));
|
||||
if (curLedgerParentHash)
|
||||
span.setAttribute(
|
||||
telemetry::tx_apply_span::attr::currentLedgerHash,
|
||||
to_string(*curLedgerParentHash).c_str());
|
||||
}
|
||||
return span;
|
||||
}
|
||||
@@ -224,8 +246,14 @@ static TER
|
||||
invokePreclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Trace the preclaim stage under the transaction's deterministic trace_id.
|
||||
// Preclaim has a ledger view, so tag the span with the ledger being worked
|
||||
// on (seq + parent hash) to correlate it to the ledger/consensus trace.
|
||||
auto span = makeStageSpan(
|
||||
telemetry::tx_apply_span::preclaim, telemetry::tx_apply_span::val::preclaim, ctx.tx);
|
||||
telemetry::tx_apply_span::preclaim,
|
||||
telemetry::tx_apply_span::val::preclaim,
|
||||
ctx.tx,
|
||||
ctx.view.seq(),
|
||||
&ctx.view.header().parentHash);
|
||||
try
|
||||
{
|
||||
// use name hiding to accomplish compile-time polymorphism of static
|
||||
|
||||
@@ -51,3 +51,12 @@ TEST(TxApplySpanNames, stage_values_are_the_three_pipeline_stages)
|
||||
EXPECT_EQ(std::string_view(tx_apply_span::val::preclaim), "preclaim");
|
||||
EXPECT_EQ(std::string_view(tx_apply_span::val::apply), "apply");
|
||||
}
|
||||
|
||||
TEST(TxApplySpanNames, current_ledger_keys_are_shared_constants)
|
||||
{
|
||||
// "ledger being worked on" keys, re-exported from the base SpanNames.h so
|
||||
// every emitter (tx.*, txq.*, apply pipeline) shares one string. Set on
|
||||
// preclaim/transactor (view-bearing); preflight omits them (stateless).
|
||||
EXPECT_EQ(std::string_view(tx_apply_span::attr::currentLedgerSeq), "current_ledger_seq");
|
||||
EXPECT_EQ(std::string_view(tx_apply_span::attr::currentLedgerHash), "current_ledger_hash");
|
||||
}
|
||||
|
||||
@@ -1390,6 +1390,12 @@ NetworkOPsImp::processTransaction(
|
||||
auto span = std::make_shared<SpanGuard>(txProcessSpan(transaction->getID()));
|
||||
span->setAttribute(tx_span::attr::txHash, to_string(transaction->getID()).c_str());
|
||||
span->setAttribute(tx_span::attr::local, bLocal);
|
||||
// The current (open) ledger index at submission/relay time — the ledger
|
||||
// being worked on. Correlates this tx.process to the ledger trace; the tx
|
||||
// has not yet been applied to a specific ledger here, so there is no hash.
|
||||
span->setAttribute(
|
||||
tx_span::attr::currentLedgerSeq,
|
||||
static_cast<std::int64_t>(ledgerMaster_.getCurrentLedgerIndex()));
|
||||
if (auto const& stx = transaction->getSTransaction())
|
||||
{
|
||||
if (auto const* fmt = TxFormats::getInstance().findByType(stx->getTxnType()))
|
||||
@@ -1600,8 +1606,19 @@ NetworkOPsImp::apply(std::unique_lock<std::mutex>& batchLock)
|
||||
if (e.failType == FailHard::Yes)
|
||||
flags |= TapFailHard;
|
||||
|
||||
// Parent the txq.enqueue span to this tx's tx.process span
|
||||
// via an explicit captured context (the parent is explicit,
|
||||
// not ambient-inherited). Null on the open-ledger rebuild
|
||||
// path, where no tx.process span exists.
|
||||
auto const txProcessCtx =
|
||||
(e.span && *e.span) ? e.span->spanContext() : telemetry::SpanContext{};
|
||||
auto const result = registry_.get().getTxQ().apply(
|
||||
registry_.get().getApp(), view, e.transaction->getSTransaction(), flags, j);
|
||||
registry_.get().getApp(),
|
||||
view,
|
||||
e.transaction->getSTransaction(),
|
||||
flags,
|
||||
j,
|
||||
txProcessCtx.isValid() ? &txProcessCtx : nullptr);
|
||||
e.result = result.ter;
|
||||
e.applied = result.applied;
|
||||
changed = changed || result.applied;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/Units.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/telemetry/SpanGuard.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <boost/circular_buffer.hpp>
|
||||
@@ -334,7 +335,8 @@ public:
|
||||
OpenView& view,
|
||||
std::shared_ptr<STTx const> const& tx,
|
||||
ApplyFlags flags,
|
||||
beast::Journal j);
|
||||
beast::Journal j,
|
||||
telemetry::SpanContext const* parentCtx = nullptr);
|
||||
|
||||
/**
|
||||
* Fill the new open ledger with transactions from the queue.
|
||||
|
||||
@@ -749,13 +749,35 @@ TxQ::apply(
|
||||
OpenView& view,
|
||||
std::shared_ptr<STTx const> const& tx,
|
||||
ApplyFlags flags,
|
||||
beast::Journal j)
|
||||
beast::Journal j,
|
||||
telemetry::SpanContext const* parentCtx)
|
||||
{
|
||||
using namespace telemetry;
|
||||
ScopedSpanGuard span(TraceCategory::Transactions, txq_span::prefix::txq, txq_span::op::enqueue);
|
||||
// Parent the enqueue span to the caller's tx.process span when a context is
|
||||
// supplied (submission path), using the explicit-context factory: the parent
|
||||
// comes from the captured ctx rather than being inherited from whatever span
|
||||
// is ambient, and this ScopedSpanGuard's scope is RAII-bounded to this fully
|
||||
// synchronous call (no coroutine yield), so no unrelated parent leaks in and
|
||||
// its scope cannot leak out onto a reused worker.
|
||||
// On the open-ledger rebuild path parentCtx is null and this is a root; the
|
||||
// current_ledger_seq attribute below correlates it to the ledger instead.
|
||||
// A lambda (not a ternary) picks the factory: ScopedSpanGuard's move ctor is
|
||||
// deleted, so guaranteed copy elision on each return is the only way to
|
||||
// construct it conditionally.
|
||||
auto span = [&]() -> ScopedSpanGuard {
|
||||
if (parentCtx && parentCtx->isValid())
|
||||
return ScopedSpanGuard::childSpan(txq_span::enqueue, *parentCtx);
|
||||
return ScopedSpanGuard(
|
||||
TraceCategory::Transactions, txq_span::prefix::txq, txq_span::op::enqueue);
|
||||
}();
|
||||
span.setAttribute(txq_span::attr::txHash, to_string(tx->getTransactionID()).c_str());
|
||||
if (auto const* fmt = TxFormats::getInstance().findByType(tx->getTxnType()))
|
||||
span.setAttribute(txq_span::attr::txType, fmt->getName().c_str());
|
||||
// The ledger being worked on (open/tentative apply or in-flight consensus
|
||||
// build) — correlates this enqueue to the ledger trace in every context.
|
||||
span.setAttribute(txq_span::attr::currentLedgerSeq, static_cast<std::int64_t>(view.seq()));
|
||||
span.setAttribute(
|
||||
txq_span::attr::currentLedgerHash, to_string(view.header().parentHash).c_str());
|
||||
// Default outcome; overridden below on the direct-apply and queued paths.
|
||||
// Every other early return leaves the tx rejected from the queue.
|
||||
span.setAttribute(txq_span::attr::txqStatus, txq_span::val::rejected);
|
||||
|
||||
@@ -73,12 +73,22 @@ inline constexpr auto acceptTx = makeStr("accept_tx");
|
||||
inline constexpr auto cleanup = makeStr("cleanup");
|
||||
} // namespace op
|
||||
|
||||
// ===== Full span names (prefix.op) ===========================================
|
||||
//
|
||||
// Joined "txq.<op>" names for the explicit-context factories
|
||||
// (SpanGuard::childSpan(name, ctx)) that take one full span name rather than a
|
||||
// prefix/suffix pair.
|
||||
|
||||
inline constexpr auto enqueue = join(prefix::txq, op::enqueue);
|
||||
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/**
|
||||
* Canonical shared constants (defined in SpanNames.h).
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::currentLedgerHash;
|
||||
using ::xrpl::telemetry::attr::currentLedgerSeq;
|
||||
using ::xrpl::telemetry::attr::ledgerSeq;
|
||||
using ::xrpl::telemetry::attr::txHash;
|
||||
|
||||
|
||||
@@ -1320,6 +1320,12 @@ PeerImp::handleTransaction(
|
||||
auto span = std::make_shared<SpanGuard>(txReceiveSpan(txID, *m));
|
||||
span->setAttribute(tx_span::attr::txHash, to_string(txID).c_str());
|
||||
span->setAttribute(tx_span::attr::peerId, static_cast<int64_t>(id_));
|
||||
// The current (open) ledger index when the relayed tx was received —
|
||||
// the ledger being worked on. Correlates this tx.receive to the ledger
|
||||
// trace; not yet applied to a specific ledger here, so no hash.
|
||||
span->setAttribute(
|
||||
tx_span::attr::currentLedgerSeq,
|
||||
static_cast<std::int64_t>(app_.getLedgerMaster().getCurrentLedgerIndex()));
|
||||
if (auto const* fmt = TxFormats::getInstance().findByType(stx->getTxnType()))
|
||||
span->setAttribute(tx_span::attr::txType, fmt->getName().c_str());
|
||||
if (auto const version = getVersion(); !version.empty())
|
||||
|
||||
@@ -50,6 +50,13 @@ namespace attr {
|
||||
using ::xrpl::telemetry::attr::peerId;
|
||||
using ::xrpl::telemetry::attr::txHash;
|
||||
|
||||
/**
|
||||
* "current_ledger_seq" — the open/current ledger index at the time the tx is
|
||||
* received or submitted (the ledger being worked on), NOT an established ledger.
|
||||
* No parent-ledger hash here: these entry points have no ledger view.
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::currentLedgerSeq;
|
||||
|
||||
/**
|
||||
* "local" — whether tx originated locally.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user