- Automatic collection tracing requests traces for eligible changes in selected collections. Configure the collection list in the Ditto instance configuration or set it with the
ALTER SYSTEMDQL statement. You can also change it for a particular peer at runtime through Remote Query. - Programmatic tracing uses the
system_trace_sync_propagationDQL function inside a write transaction. It lets the application choose document IDs to trace or limit the number of traces requested in that transaction.
Requirements
- To observe propagation between peers, use at least two peers that sync the change. Use Ditto SDK 5.2 or later on every peer along the path that you want represented in the trace. Older peers can still receive and forward document changes, but the trace cannot continue through them.
- An OpenTelemetry Protocol (OTLP)-compatible collector and trace backend.
- The OTLP endpoint configured on each peer whose spans you want to export.
Configure OpenTelemetry export
Propagation tracing uses Ditto’s OpenTelemetry exporter. If the peer is already configured to export Ditto spans through OpenTelemetry, you may skip this section. Propagation spans use the same OTLP configuration. Otherwise, set the following environment variables in the peer’s process environment before Ditto initializes its logging system. Set them before the first Ditto SDK or logger call. How you configure the process environment depends on the platform. These are not Ditto system parameters and cannot be changed withALTER SYSTEM.
For a local collector example, see Try Jaeger locally.
Automatic collection tracing
Automatic tracing is disabled by default. It requests traces for eligible changes made by local, app-initiated write transactions in the selected collections. The app does not need to call a tracing function for each transaction. The peer’s rate and retention limits still apply; see Set trace retention and rate limits. UseALTER SYSTEM to set propagation_trace_auto_trace_collections to the list of collection names to trace automatically:
DQL
ALTER SYSTEM statements. The peer also needs its OTLP exporter configured to send spans to your collector.
If the app sets the parameter locally with ALTER SYSTEM, the setting applies only to the current Ditto instance. It must be applied again after each initialization. To set it at startup, configure propagation_trace_auto_trace_collections in the peer’s Ditto instance configuration. This may require changing the app or its deployment configuration.
The change applies to application-initiated write transactions that start after it is set. Transactions already in progress retain their existing configuration.
The collection list controls which changes start traces on that peer. A receiving peer continues a trace it receives even if the collection is not in its own list. Add a collection to each peer where you also want changes made there to start traces. Configure OTLP export on every peer whose spans you want to collect.
Programmatic tracing in a transaction
Callsystem_trace_sync_propagation from a DQL write transaction when application code needs to choose which changes to trace. This function requests traces for matching changes made later in that transaction. You can select document IDs and cap the number of requests. The peer’s rate and retention limits still apply.
This is a provisional Small Peer DQL API. Its name, arguments, and behavior may change before general availability. The function requires
dql_enable_preview_mode, which is off by default. This parameter enables preview DQL features generally; it is not specific to propagation tracing. Enable it on each peer whose app calls the function. A peer that only receives and forwards a trace does not need preview mode.DQL
ALTER SYSTEM, the setting applies only to the current Ditto instance. Apply it after each initialization and before the transaction that calls the function. You can also set it through Remote Query or in the Ditto instance configuration. See Customizing System Settings for configuration details.
Create a write transaction through the SDK transaction API. Execute the function before the document changes you want to trace. Send the function and each document change as separate DQL statements against the same transaction, then commit it. The example requests a trace for task-1 and inserts that document later in the transaction:
DQL
DQL
NULL when the request succeeds.
Function arguments
The first argument names the collection. The optional second argument is an object that narrows which changes in that collection are eligible:
If you omit an option, the request has no constraint for it. An empty
ids array or max_traces value of 0 makes the request a no-op. Peer-wide limits still apply. Unknown option fields, invalid collection names or document IDs, and invalid option types return errors.
Transaction behavior
- The function only applies to changes made later in the same transaction. Changes made before the call are not traced.
- A successful commit makes the requested traces available for propagation. A rollback discards them.
- If you execute the function outside an explicit transaction, Ditto runs it in an implicit write transaction. The call can succeed, but starts no trace by itself because no document change follows in that transaction.
- The function requires a write transaction; it cannot run in a read-only transaction.
- Multiple calls add separate requests and limits. A document gets at most one trace in a transaction, even if it matches multiple requests.
- The peer’s trace rate and retention limits apply to these requests. See Set trace retention and rate limits.
Set trace retention and rate limits
Without limits, propagation tracing could consume significant memory on peers and increase network and backend load. These peer-level parameters bound those costs for both automatic and programmatic trace requests. You can change them at runtime with theALTER SYSTEM statement.
Rate limiting
Parameter:propagation_trace_max_established_per_minute
Default value: 12
Description:
Sets the maximum steady-state rate at which the peer starts new traces. When the limit is reached, eligible changes are not traced until capacity becomes available. Requests are not queued; available capacity applies to later trace requests.
For example, if an app requests traces for 120 eligible changes per minute and the peer uses the default limit of 12, the peer will generally start traces for about 10% of those changes over time.
Each peer imposes this limit on the new traces it starts. It is not a shared budget. As the number of peers in the mesh increases, so can the overall rate of traces started by the mesh.
Local TTL
Parameter:propagation_trace_ttl_secs
Default value: 60
Description:
Sets the maximum time the peer retains its local knowledge of a trace after first observing the change.
Because a peer can’t predict when other peers will reconnect to it, it can never know whether it has retransmitted a given change for the last time or not. To be absolutely certain it always propagates traces appropriately, it would theoretically have to retain knowledge of each trace forever. This system parameter gives the peer a time-based escape hatch that allows it
to assume a trace is no longer relevant after it has held it for a certain period of time.
For traces received from another peer, this TTL is relative to the moment that the local peer became aware of the trace, not from the moment the trace started. This avoids clock skew issues that could otherwise cause the trace to be prematurely terminated.
A longer TTL reduces the likelihood that a trace will stop before reaching a late-joining peer or crossing a slow connection. It also keeps local spans open longer, delaying their visibility in the OpenTelemetry backend.
Maximum retained traces
Parameter:propagation_trace_max_count
Default value: 100
Description:
Sets the maximum number of active propagation traces that the peer retains at once. If the peer reaches this limit, it may expire its oldest trace records before their TTL. The peer logs a trace lifecycle event when this happens.
The main purpose of this limit is to control memory use. If a peer frequently reaches this limit, and traces are incomplete as a result, you can:
- Increase
propagation_trace_max_countto retain more concurrent traces locally. - Decrease
propagation_trace_max_established_per_minuteon all the peers in the mesh to limit the total number of concurrent traces that need to be handled at a time.
Read and interpret a trace
In your OpenTelemetry backend, filter for serviceditto and span name doc_trace. Backend interfaces may use different labels for these filters.
Each trace follows one document change. The root span captures the activity on the peer where the change was made. Receiving peers add spans, and parent-child relationships show the route the change took through the mesh.
The span attributes identify the peers and document involved:
local.peer_ididentifies the peer represented by the span.received_from.remote.peer_ididentifies the peer that supplied the change.collectionanddocument_ididentify the document. Within that document,dotidentifies the specific change being traced.
Limitations
Trace timing and visibility
No single peer manages the lifetime of an OpenTelemetry trace. Each peer keeps a local record containing its span and the context needed to continue the trace.propagation_trace_ttl_secs limits how long the peer keeps that record after first observing the change. When the record expires, the peer closes its span and exports it if OTLP is configured. Other peers may still have their own records and open spans. Span duration therefore reflects how long that peer retained the trace record, not how long it took the change to reach or pass through the peer. Use span start times to estimate propagation delay.
TTL expiry is not a scheduled export deadline. A peer closes its span when trace maintenance notices that its local record has expired. Maintenance runs during Small Peer write transactions and when Ditto checks traces while preparing outgoing sync updates. When multihop presence is enabled (the default), Small Peers also write presence updates when connectivity changes and send keepalives every 30 seconds. These writes run trace maintenance too.
In an active mesh, sync, store, and presence activity tend to close local spans near their TTL. An idle or offline peer, or a peer with multihop presence disabled, may keep an expired record longer. The span may then appear in the backend later than its TTL.
Trace coverage
Complete propagation tracing is only possible along paths where every peer can continue the trace and export its spans. When a peer sends a traced change to a peer that cannot continue tracing, it logs that the trace was not sent. The document change may still continue through the mesh, but the trace does not cross that boundary. Peers that cannot continue propagation traces include:- Small Peers running Ditto SDK versions earlier than 5.2.
- Ditto Server.
- A peer may restart after receiving a traced change but before forwarding it. The restart discards its local trace record.
- A peer may wait longer than the configured TTL between receiving a traced change and relaying it. For example, if it is disconnected long enough for its local trace record to expire, the trace will not accompany the change when it later relays it.
- Ditto’s optional reliable UDP multicast data-sync transport is a private beta and does not currently propagate trace context.
Troubleshooting
Appendix: Try Jaeger locally
Jaeger accepts OTLP and provides a web interface for viewing traces. To run it locally with Docker:localhost, set OTEL_EXPORTER_OTLP_ENDPOINT to http://localhost:4317 for gRPC or http://localhost:4318 for http/protobuf. If Ditto runs in a container or on another device, use an endpoint reachable from that process. Open http://localhost:16686 on the machine running Jaeger to view traces. For production, use your organization’s collector and backend configuration.