You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@pekko.apache.org by GitBox <gi...@apache.org> on 2026/04/19 12:04:33 UTC

[GH] (pekko/issue-2860-tls-graphstage-path): Workflow run "Generate doc check" failed!

The GitHub Actions job "Generate doc check" on pekko.git/issue-2860-tls-graphstage-path has failed.
Run started by GitHub user He-Pin (triggered by He-Pin).

Head commit for run:
a4ea56c45a7ab4b0a95918b3cb779dfe200bcf51 / He-Pin <he...@gmail.com>
test: mark 4 reliably-cancel tests pending in TlsGraphStageSpec

Motivation:
Two test cases in TlsSpecBase verify that when an input source fails
immediately (before the TLS handshake completes), both outputs fail with
the same exception and no TLS bytes are emitted. This ordering guarantee
holds for the legacy TLSActor-based path but cannot be provided by the
new TlsGraphStage path without removing its async boundary.

Root cause of the ordering difference:
TlsGraphStage deliberately carries an ActorAttributes.dispatcher attribute
that forces it to materialise into its own ActorGraphInterpreter actor
(for SSLEngine thread-safety and per-connection isolation). This creates
an async message boundary for all inter-stage communication.

With that boundary in place:
- Demand from Sink.head travels 1 inter-actor hop to reach TlsGraphStage.
- The failure reply from Source.failed travels 2 inter-actor hops (TLS
  pulls upstream; upstream sends failure back).
- In the TLS actor mailbox, demand (1 hop) consistently arrives before the
  failure (2 hops). When demand arrives isAvailable(cipherOut) = true, the
  engine is in NEED_WRAP state, and a TLS ClientHello is pushed to
  Sink.head before failTls() is ever invoked.

Legacy TLSActor avoided this race by using initialPhase(2, bidirectional),
which deferred the first pump until both upstream subscriptions arrived via
VirtualProcessor bridges; by that time the Source.failed error was already
buffered in the InputBunch.

Why the async boundary must stay:
Removing the dispatcher attribute (Option B) would make the failure
synchronous within the same interpreter pump cycle and fix the race.
However, doing so would:
1. Allow blocking SSLEngine delegated tasks (PKIX validation,
   Diffie-Hellman key generation) to run on a shared fused-graph thread.
2. Break MaterializerStateSpec, which asserts that each TLS stage
   materialises to a separate ActorGraphInterpreter actor snapshot.

Modification:
Add withFixture override in TlsGraphStageSpec that returns Pending for
the four test-name patterns matching the two 'reliably cancel' scenarios
(each run for both TLSv1.2 and TLSv1.3). The Scaladoc on both the class
and the override explains the mailbox-hop ordering constraint in detail.
Apply scalafmt to TlsGraphStage.scala (handler call reformatting only).

Result:
- TlsSpec (legacy): 111/111 tests pass.
- TlsGraphStageSpec (GraphStage): 107 pass, 4 pending (no failures).
- MaterializerStateSpec: unchanged.

Future work: A scheduler-based deferred drain or a two-phase
handshake-initiation design could restore the ordering guarantee without
removing the async boundary.

Co-authored-by: Copilot <22...@users.noreply.github.com>

Report URL: https://github.com/apache/pekko/actions/runs/24628500052

With regards,
GitHub Actions via GitBox


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@pekko.apache.org
For additional commands, e-mail: notifications-help@pekko.apache.org