Repository navigation
feat: vizualize the message flow graph, and prove tracing, OpenTelemetry, flow tracking and the graph against one catalogue of flows - #725
Merged
Conversation
… or JSON MessageFlowGraph lists every command, event, query, handler, aggregate, saga, projection, business interface, router, scheduled endpoint and channel the application declares, with the edges between them: routing, input and output channels, asynchronous delivery, applied events, and gateway request channels. What a handler emits is inferred from its source: recordThat, publish, send, sendWithRouting and publishWithRouting calls, event-sourced return values, gateway calls through typed collaborators, and the channel names a router returns. Inferred edges are marked as such. The graph is reachable as ConfiguredMessagingSystem::getMessageFlowGraph(), FlowTestSupport::getMessageFlowGraph() and ecotone:flow:dump --format=mermaid|json.
A #[Distributed] handler's message is delivered by the distributed bus node, a handler calling sendCommand, convertAndSendCommand, publishEvent, convertAndPublishEvent or sendMessage on a DistributedBus collaborator is linked to the remote message and the bus with the target service, and a DistributedServiceMap wires the bus to its command and event channels.
Inferred nodes and edges are collected in keyed arrays and the graph is created once, instead of re-creating and re-validating the whole graph per edge. The source scanner tokenizes each file once for all handlers it holds and slices method bodies by line index. The declared graph reaches the container as one JSON string. Measured on a generated application of 300 files and 1500 handlers (3602 nodes, 4200 edges), best of three runs: graph build 3462 ms -> 126 ms, of which source scanning is 114 ms; the module adds about 30 ms to bootstrap.
The scanner keeps one filtering loop that also records the first token of each line, and compares token ids against constant sets instead of calling is() with a fresh array per token. Scanning 1500 handlers in 300 files: 114 ms -> 74 ms, against a 10 ms raw tokenizer floor. The immutable-copy test now observes eventSubscriptions, which the flow graph reads from DistributedServiceMap as a public readonly property.
…s, scheduled polls, logs as span events, message identity and trace carriers
…the shared catalogue now runs
… namespace scan earlier in the run cannot enable other packages
… its channel, and a route that delivered nothing leaves no work pending
… step leading to the next, and the event channels this service subscribes to
…, combined and dynamic channels, async gateways and orchestrator steps, distributed events, transformers, interceptors, the message collector, sagas and event-sourced projections
…tions, wildcard and interface subscriptions, and asynchronous aggregate actions
…pans of the core tracer, so every tracing feature records them
…dary, replacing the OpenTelemetry-only tests
…ecome verifiers any suite can run
…nothing queued or failed, so a replayed failure can complete it
…, dead letter replay and deduplication with every tracing feature in one run
…d and delete start their own traced and tracked flow, and a polling projection's consumer span is named after its poller
…poll of its endpoint, not as a receive from the null channel
…every message it carries, and the cross-module catalogue covers outbox forwarding to plain and high-throughput channels
…atabase event store: rebuilt, rebuilt through a database channel per partition, and deleted, initialized and backfilled
…nts consumed from their own databases, and the stored flow check reads each flow from its tenant
…ded from the outbox is stored as a step of its flow, and a relayed batch settles the delivery the message already had
… from the distributed bus in the flow graph
…channels this service is allowed to publish to
…acked marks of its own message, not of the batch's first flow
…, while one sent to the channel's error channel stays failed
…is marked collected, not undelivered
…theses such as emits (publish) renders
… draws an asynchronous handler behind it, in the order the message travels
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why is this change proposed?
Why
Important
Some span names, statuses and attributes change for existing tracing and OpenTelemetry users. Dashboards or alerts keyed on the old values need updating — see Before / after.
An application can now dump its whole message flow as a graph (Enterprise), and every feature that shows a flow — core tracing, OpenTelemetry, flow tracking and the graph — is proved against one shared catalogue of flows. Before, each feature had its own tests over its own model, so they drifted: a flow could be traced correctly yet stored wrongly by flow tracking, or be missing from the graph, with no test noticing.
Before / after
rebuild orderswithecotone.projection.operationreceive projecting_manager_handler:<name>/receive nullChannelpoll <endpointId>ecotone.route.undeliveredecotone.route.collectedExample
bin/console ecotone:flow:dump --format=mermaid # or --format=jsonFor an application whose
OrderService::place()command handler publishesOrderWasPlaced, handled asynchronously byNotificationsand by anorder_listprojection, next to anorder.statusquery handler, the dump renders as:flowchart LR subgraph n1 ["Projection: order_list"] n8["Event Handler<br/>OrderListProjection::whenOrderPlaced"] end n0[("Channel<br/>notifications")] n2["Command Handler<br/>OrderService::place"] n3>"PlaceOrder"] n4["Query Handler<br/>OrderService::status"] n5{{"order.status"}} n6["Event Handler<br/>Notifications::whenOrderPlaced"] n7(["OrderWasPlaced"]) n3 --> n2 n5 --> n4 n7 --> n0 n0 ==>|"async"| n6 n7 --> n8 n2 -.->|"emits (publish)"| n7An asynchronous handler sits behind its channel, in the order the message travels. The emitted event is inferred from the handler's source (
$eventBus->publish(...)), shown as a dotted edge.Description of Changes
Message flow graph (Enterprise)
ecotone:flow:dumpandgetMessageFlowGraph()draw buses, handlers, channels, aggregates, sagas, projections, interceptors, splitters, transformers, orchestrators and distributed routes. Without a licence the console command is not registered, andgetMessageFlowGraph()throws aLicensingException.Tracing and flow tracking
Decision Models:,Conditional Append:) moved from OpenTelemetry into core tracing, so flow tracking records them too.ProjectionOperations), so tracing and flow tracking wrap them like any other entry point. The console commands andFlowTestSupport::triggerProjection(),resetProjection(),initializeProjection()anddeleteProjection()go through them.FlowTrackingScopeseam:TraceRecordingInterceptorwhen flow tracking is on, a pass-through otherwise.Start reading at
packages/Ecotone/src/Test/Flow/FlowScenarioCatalogue.php, then one suite:packages/Ecotone/tests/Tracing/TracingFlowConformanceTest.php.Known gaps, left out deliberately
#[ProjectionInitialization],#[ProjectionDelete]and#[ProjectionReset]hooks run inside the operation span, without spans of their own.Pull Request Contribution Terms