Repository navigation
Description
Problem
ShenYu has request-level tracing and terminal chain timing, but the common plugin boundary does not expose a backend-neutral, optional observation of each plugin publisher's subscribed lifetime. Interpreting the existing before/after DEBUG cost as asynchronous plugin execution time is incorrect.
Reviewed 750673e3f619e335333150ff1b2d14070d5d5c31. ShenyuPlugin.before/after explicitly bracket publisher construction; the default chain preserves that timing. ShenyuWebHandler.handle already measures the entire request subscription, and TracingPlugin already ends a gateway server span on completion/error/cancellation. Reuse these rather than adding another gateway server span.
First delivery
- Define a small optional observer/adapter contract around the real legacy plugin invocation and returned publisher. Allocate state per invocation/subscription, use a monotonic clock, and define construction failure, subscribed completion, error and cancellation explicitly. A synchronously thrown
executefailure is distinct from a publisher that was returned and then failed. - Preserve existing
before/aftertiming and overrides, including WASM, and keepexecute, continuation andfork()behavior unchanged. Do not add detached subscriptions, implicitcache(), buffering, publisher replay or an execution-mode switch. - Define the observation scope before adding metrics: a plugin publisher commonly encloses its downstream continuation, so its duration is inclusive. It is not exclusive CPU time, transport latency, or an AI upstream attempt. Construction duration may be reported separately.
- Distinguish a skipped plugin, an entered plugin and an actual selector/rule match. The core must not rerun matching or infer successful matching merely because
skip()returned false. Richer match events can be an optional adapter to existing matching code, not a new matcher. - Supply a no-op default and one integration with the existing tracing/metrics facilities. Avoid duplicate server spans; sampling and bounded plugin labels must be explicit. Keep observer errors from replacing the request outcome. Do not label metrics by request IDs, raw paths, arbitrary conditions or unbounded config revisions.
- Document multiple root subscriptions, downstream resubscriptions/retry, short-circuits and fork dispatch. Define observation identity/scope for each instead of assuming a plugin object equals one request or that all terminal callbacks mean independent upstream attempts.
Acceptance criteria
- Real default-chain tests cover asynchronous completion/error, synchronous
executefailure, cancellation, skip, rule miss, short-circuit and a wrapper around an asynchronous successor. - Existing lifecycle/WASM hook timing remains unchanged, and the observer's records follow the documented invocation/subscription identity across repeated subscriptions and supported fork dispatch.
- A controlled delayed successor shows inclusive duration; output/documentation never labels it exclusive plugin time.
- Terminal observation and observer failure neither swallow cancellation nor duplicate writes, continuation side effects or gateway server spans.
- Disabled instrumentation preserves behavior and has a reproducible allocation/latency comparison; enabled tracing respects sampling and bounded output. Do not claim an unmeasured performance gain.
- Tests use the reusable plugin test support where available and verify actual runtime execution, not only a direct observer mock.
Relationship and non-goals
#7566 owns end-to-end gateway tracing and outbound propagation; #8000 owns future graph observability; #8015 owns graph continuation semantics; #8125 owns AI attempt telemetry. This issue provides an additive legacy request-plugin boundary usable by those integrations. It does not implement exclusive time accounting, another tracing backend, transport attempts, graph execution or a change to the historical hook ABI.
Tracking
Plugin-system tracker: #8199.
Activity
Aias00 commentedon Oct 28, 2026
Implementation refinement from the chain-extension review at
750673e3f619e335333150ff1b2d14070d5d5c31:WebSocketPluginexplicitly documents that its returned Mono completes after the upgrade handshake; session accounting is attached toWebSocketHandler.handle()instead. An escaped chain error may still be rendered by the outerGlobalErrorHandlerafter the plugin chain terminates.The focused HTTP attempt adapter #8209 consumes transport/body facts; it must not report inclusive plugin-publisher duration as exclusive transport duration or create another gateway server span.