Request Correlation and Business Flow Identification¶
Observability topic table of contents · Spec table of contents · Previous: 03. Message Flow Tracing
Defines the creation, format, propagation, ownership, and lifetime of
correlation_id,flow_id, andflow_origin. The ownership split among this topic's four documents follows the topic table of contents "2. Documents In This Topic".
1. What Is Identified¶
This document defines the contract by which the framework exactly links a request and terminal reply, and identifies several messages continuing from the same cause as one business flow. The application doesn't generate this identifier or use it to link a reply.
The identifying information created when sending a request and kept the
same through the terminal reply is called
reply correlation, and its
public field name is correlation_id. The value indicating that several
hops and fan-out branches started from the same cause is flow_id.
flow_origin indicates where that flow first started.
This document only defines the creation, format, propagation, ownership, and lifetime of these three fields. The ownership and size of metadata the application sends with a message are defined by Message Model. The condition for including a field in a trace and sampling are defined by 03. Message Flow Tracing. The three fields are framework-managed context — not an application metadata key.
2. The Role of the Two Identifiers¶
| Identifier | Scope it links | Who creates it | Valid period |
|---|---|---|---|
correlation_id |
One request and its one response or error | The framework runtime that started the request | Until the request terminally completes |
flow_id |
Several messages and fan-out branches derived from the same cause | The framework runtime processing that flow's first work | Until work propagating to related branches finishes |
The framework only uses correlation_id to link a reply with the
currently pending request. flow_id is a value for observation and
isn't used for message dedup, idempotency, or current-owner
verification.
%%{init: {'sequence': {'actorFontSize': '18px', 'messageFontSize': '18px', 'noteFontSize': '18px', 'boxMargin': 8, 'width': 140}, 'themeVariables': {'fontSize': '18px'}}}%%
sequenceDiagram
participant Origin
participant Handler
participant Downstream
Origin->>Handler: sends the original request (correlation A, flow F)
Handler->>Downstream: sends a new request (correlation B, flow F)
Downstream-->>Handler: sends the new request's reply (correlation B, flow F)
Handler-->>Origin: sends the original request's reply (correlation A, flow F)
Work where a handler, while processing the original request, sends a new
request to a different target is called a
downstream request. A new
correlation_id is built for each downstream request, but flow_id is
kept if it continues from the same cause. A correlation_id isn't built
for a one-way message with no reply.
3. Format and Ownership¶
| Field | Format and value range |
|---|---|
correlation_id |
A framework-built opaque ASCII value, 1..64 bytes. It can't be duplicated among requests concurrently pending within the same lifecycle of the runtime that built the value. |
flow_id |
A UUIDv7 written in lowercase with hyphens, exactly 36 ASCII bytes. |
flow_origin |
One of inbound, timer, application, lifecycle. The value fixed when the flow was first built is kept across subsequent hops. |
The application doesn't interpret or assemble these three values.
flow_id and flow_origin must exist together or be absent together.
A malformed flow_id, a zero-byte-length correlation_id, or flow
information with only one of the two fields present is a protocol
error.
| Where the invalid value arrives | How the framework completes it |
|---|---|
| Framework message envelope | Completes that operation with ProtocolError. |
| STREAM frame | Terminates the connection with ProtocolError. |
4. When A Flow Is Created¶
If an inbound message has a well-formed flow_id and flow_origin,
they're used as-is. However, the two fields are only read and put into
flow context when the current runtime's message-flow tracing is on. If
the two fields are absent, the framework treats the following work as
the start of a new flow and builds the values. In this list, a
logical target that keeps the same ID for messages even when the node
running it changes is a
Spot.
- STREAM ingress and Node/Channel/Spot/Instance Spot/Actor inbound processing
- Timer callback and lifecycle callback
- The first outbound operation started by application code outside a framework callback
If the diagnostics level is Off, every observation-only flow-handling
step is skipped. A new flow_id isn't built, and an inbound message's
flow field isn't turned into flow context or copied to the next message.
The two fields also aren't added to an outbound envelope. An outbound
request the client connector starts follows the same rule.
correlation_id is protocol information linking a request and terminal
reply. Even if the diagnostics level is Off, it's built per request and
preserved through the reply. This value can't be removed by turning off
tracing.
The framework sets the current flow context when starting callback
execution. It restores the pre-execution context at the callback's
terminal completion. If tracing is Off, this context isn't built or
put into async-local storage.
The rule for changing diagnostics level at runtime follows
03. Message Flow Tracing "5. Changing The Record Level At Runtime And The Cost Rule".
Once each processing point confirms Off, it doesn't build a flow ID,
validate, capture context, add an envelope field, or build an internal
propagation message. A change isn't retroactively applied to an
already-built outbound frame.
5. Propagation Rule¶
While message-flow tracing is on, the framework delivers flow_id and
flow_origin together for as long as cause and effect continue in one
piece of work. When Off, the §4 omission rule applies.
The standard list of processing boundaries preserving the two flow fields is the same as the boundaries defined by 03. Message Flow Tracing "2.2 The Public Behavior Recorded" — Node direct and Channel, Spot direct (sent by Global Spot ID), Instance Spot direct, Actor direct and STREAM Actor dispatch, Actor relocation, a push connected to the current session, and Logical Multicast and Classic fanout, which delivers an event over a separate socket. At each boundary, the two flow fields are preserved through that boundary's last recorded processing point — for example, Instance Spot direct preserves them from source lookup through the first application turn, and Actor relocation preserves them through relocation control and the target Actor's related lifecycle work.
Logical Multicast sends a message to several Spots by ChannelName and
topic, and Classic fanout sends an event over a separate PUB/SUB path.
The two methods have a different target identity or local sequence per
branch, but the same flow_id.
When an intermediate runtime forwards the original request, it
preserves the original correlation_id through the terminal reply. A
downstream request uses a new correlation_id. If tracing is on and a
current flow context exists, the two flow fields are also delivered.
If the target first selected for an Instance Spot doesn't obtain
creation authority, the message can be delivered once to a
Ready owner that can currently accept the
request. In this case the original correlation_id is kept. If tracing
is on, flow_id and flow_origin are also kept. Once the target queue
accepts the message, the framework doesn't automatically resend it.
6. Async Work and Execution Context¶
The framework preserves the current flow context in an async continuation it's waiting on. Context isn't implicitly delivered to a task run separately from the framework, a separate executor, or an external callback. If there's no explicitly delivered context, it's treated as a new application flow. However, a new flow is only built and context preserved when tracing is on.
A language that can't safely support async-local context provides a public interface to explicitly capture context. The current flow isn't guessed from a process-global variable, thread ID, or a mutable connector field.
7. Reply and Failure¶
A Response and error preserve the request's correlation_id. If
tracing is on at the moment the reply is built and a request flow
context exists, flow_id and flow_origin are also preserved. Once a
request terminally completes via reply, error, timeout, cancellation, or
shutdown, the framework no longer uses that correlation_id for reply
linking.
A reply arriving after timeout or cancellation isn't linked to a different pending request. After a connection is replaced, a previous STREAM session's reply and push also aren't linked to the new session's flow. Even when the binding token identifying the connection between an Actor and the current STREAM session is no longer valid, a reply and push aren't linked to the new session's flow. If a dispatch failure can be recorded, the correlation and flow information read from the failed message is kept. If an identifier can't be read from an invalid frame, a new identifier isn't built and marked as if it were the original request's record.
Downstream terminal completion is delivered exactly once to the original activation that started it. If that operation's confirmed generation changes, or the owner terminates, it ends as a stale result. Timeout, cancellation, and a late reply don't cause handler dispatch to re-run or a route to be re-selected.
Sharing the same flow_id doesn't authorize a retry. Whether to retry
and whether to issue a new correlation_id follows that messaging
surface's contract.
8. Observability and Privacy¶
Tracing records correlation_id, flow_id, and flow_origin. The
precise inclusion condition and structured-log key are defined by
03. Message Flow Tracing "3.2 Attribute Inclusion Conditions".
None of the three values is used in a metric label.
The three fields don't encode user ID, Actor ID, the global address identifying a Spot (Spot ID), endpoint, payload, or application metadata. An external trace adapter also doesn't change the format and ownership the framework set.
9. Verification Requirements¶
The following is confirmed using only the public surface: the
request/reply exchange of correlation_id/flow_id/flow_origin and
the public interface for capturing execution context. Each item leads to
one contract test.
Correlation and reply
- A request and terminal reply use the same
correlation_id, and the request completes exactly once. - A target that didn't obtain creation authority doesn't build a new identifier even when delivering a message to the Ready owner.
- A previous STREAM session's reply and a late reply aren't linked to a new correlation.
- A downstream request uses a new
correlation_id, and terminal completion is delivered exactly once to the original Spot/Actor activation.
Flow propagation
- Node, Channel, Spot, Actor, and STREAM hops continuing from the same
cause use the same
flow_idandflow_origin. - An Instance Spot's source lookup, Spot creation message, target creation-authority acquisition, the wait before opening application processing, and the first handler keep the same correlation and flow information.
- With tracing on, every branch of Logical Multicast and Classic fanout
preserves the original
flow_id. - No previous flow context remains on an unrelated callback after a callback ends.
With tracing off
- A runtime with tracing off doesn't build a trace-only
flow_id,flow_origin, flow context, or internal propagation message. - A runtime with tracing off still builds
correlation_idfor request/reply and preserves it through the terminal reply. - After turning tracing off at runtime, a new processing point doesn't add existing flow information to context or an outbound envelope, and turning it back on doesn't retroactively apply it to a processing stage that already passed.
Privacy
- Correlation and flow information aren't used as a metric label or an application metadata value.
Observability topic table of contents · Spec table of contents · Previous: 03. Message Flow Tracing