Skip to content

Fix gRPC terminal and response boundaries - #467

Merged
binaryfire merged 2 commits into
0.4from
audit/grpc-correctness-and-lifecycles
Aug 3, 2026
Merged

Fix gRPC terminal and response boundaries#467
binaryfire merged 2 commits into
0.4from
audit/grpc-correctness-and-lifecycles

Conversation

@binaryfire

@binaryfire binaryfire commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes three correctness boundaries in the gRPC package and restores navigation to two ported guides.

Client-streaming calls now accept a terminal response that arrives before the request is half-closed. A successful response makes writesDone() an idempotent operation, while a failed response is translated through the existing metadata-rich RpcException boundary. This also covers the race where the receiver completes and releases the stream while the half-close is waiting for the connection send boundary.

Response decoding now stops immediately after the configured message or byte buffer is exhausted. The previous implementation recorded ResourceExhausted but continued advancing the lazy frame decoder, allowing later malformed input to replace the intended terminal result and performing unnecessary work after the call had already failed.

Streamed response metadata is now measured against the headers the Swoole response bridge actually emits. Streamed responses do not send content-length, so reserving bytes for it rejected valid metadata at the configured boundary. The unused reservation state and accessor are removed.

The Boost documentation index now links the existing Object Pools and gRPC guides. The audit dependency index and ledger record the targeted corrections and completed cross-package revalidation.

Behavior

The documented client-streaming sequence now works even when the server responds early:

$call->writesDone();
$reply = $call->wait();

Non-OK terminal responses still surface as RpcException with the call method, target, status, and metadata. Attempts to write a new message after completion retain the existing LogicException behavior.

There are no public signature, generated-client, routing, configuration, pooling, or transport changes.

Implementation

  • Split terminal completion from write-misuse handling inside the existing call owner.
  • Prefer the observed terminal result when the response receiver wins the half-close race.
  • Break lazy frame decoding at the first buffer-limit failure.
  • Count content-length only for unary responses, matching actual response emission.
  • Remove dead streamed content-length reservation state.
  • Replace an unbounded test poll with deterministic state and semaphore barriers.
  • Add focused regressions for early success, early failure, blocked half-close races, decoder exhaustion, and exact streamed metadata limits.

The normal request path gains no lock, retry, yield, serialization layer, network round trip, registry, or worker-retained state. Buffer exhaustion now performs less work, and metadata accounting removes unused state.

Testing

  • Focused client-streaming, stream-state, response-factory, streamed-response, and middleware tests.
  • Complete gRPC unit suite.
  • Real gRPC integration servers.
  • Full repository formatting, static analysis, parallel test, Testbench package, and dogfood gates through composer fix.
  • Diff, stale-symbol, documentation navigation, and audit-record integrity checks.

Summary by CodeRabbit

  • Bug Fixes

    • Improved gRPC streaming reliability when calls complete before request writing finishes.
    • Prevented additional messages from being processed after stream abandonment or buffer exhaustion.
    • Corrected streamed response metadata handling at size limits.
    • Improved propagation of terminal RPC outcomes and metadata.
  • Documentation

    • Added navigation for Object Pools and gRPC documentation.
  • Tests

    • Expanded coverage for early completion, stream limits, metadata boundaries, and cleanup behavior.

Treat an already-completed successful response as an idempotent client-streaming half-close, and translate an early failed response through the existing metadata-rich RPC exception boundary. This preserves the documented writesDone-then-wait sequence even when the receiver wins the connection-send race.

Stop lazy frame decoding as soon as buffered response limits are exhausted. Account streamed metadata using only the headers the Swoole response bridge emits, and remove the dead reserved content-length state.

Add deterministic regressions for early terminal responses, blocked half-closes, decoder exhaustion, and exact streamed metadata limits. Link the ported gRPC and Object Pools guides, and record the completed targeted corrections and cross-package revalidation in the audit ledger.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2bab6828-4583-4a51-aa0a-eda9c6c6a598

📥 Commits

Reviewing files that changed from the base of the PR and between 40903ca and 0cb9cab.

📒 Files selected for processing (12)
  • docs/plans/2026-07-12-0900-framework-coroutine-state-lifecycle-audit.md
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • src/boost/docs/documentation.md
  • src/grpc/src/Client/Call.php
  • src/grpc/src/Client/StreamState.php
  • src/grpc/src/Server/GrpcStreamedResponse.php
  • src/grpc/src/Server/ResponseFactory.php
  • tests/Grpc/ClientStreamingCallTest.php
  • tests/Grpc/GrpcStreamedResponseTest.php
  • tests/Grpc/HandleCallTest.php
  • tests/Grpc/ResponseFactoryTest.php
  • tests/Grpc/StreamStateTest.php
💤 Files with no reviewable changes (3)
  • tests/Grpc/GrpcStreamedResponseTest.php
  • tests/Grpc/HandleCallTest.php
  • src/grpc/src/Server/GrpcStreamedResponse.php

📝 Walkthrough

Walkthrough

The PR corrects gRPC client terminal handling, stops stream decoding after buffer exhaustion, removes reserved streamed content-length accounting, adds regression coverage, and updates audit and Boost documentation navigation.

Changes

gRPC lifecycle corrections

Layer / File(s) Summary
Client terminal-state handling
src/grpc/src/Client/Call.php, tests/Grpc/ClientStreamingCallTest.php
Client half-close logic preserves early terminal responses and propagates failed terminal statuses. Tests cover response races, metadata, coroutine cleanup, and terminal state access.
Stream exhaustion termination
src/grpc/src/Client/StreamState.php, tests/Grpc/StreamStateTest.php
Frame decoding stops after the buffer limit is exceeded. Tests verify stream abandonment, status, callback count, and buffer cleanup.
Streamed metadata accounting
src/grpc/src/Server/GrpcStreamedResponse.php, src/grpc/src/Server/ResponseFactory.php, tests/Grpc/GrpcStreamedResponseTest.php, tests/Grpc/HandleCallTest.php, tests/Grpc/ResponseFactoryTest.php
Streamed responses no longer reserve a first-frame content length. Tests cover constructor updates and exact metadata size limits.
Audit and documentation records
docs/plans/*.md, src/boost/docs/documentation.md
Audit records describe the completed gRPC corrections. Boost documentation navigation links Object Pools and gRPC documentation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main gRPC changes to terminal handling and response boundaries.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/grpc-correctness-and-lifecycles

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Greptile Summary

The PR corrects terminal-state handling for client-streaming calls, stops response decoding at the configured buffer boundary, and aligns streamed metadata accounting with emitted HTTP/2 headers.

  • Makes half-close idempotent after an early successful response while preserving metadata-rich RPC failures.
  • Stops lazy frame decoding immediately after response-buffer exhaustion.
  • Removes streamed content-length reservation and counts that header only for unary responses.
  • Adds deterministic concurrency, decoding-boundary, and metadata-limit regressions.
  • Restores Object Pools and gRPC links in the Boost documentation index.

Confidence Score: 5/5

The PR appears safe to merge, with the changed terminal, decoding, and metadata boundaries consistent with their callers and transport behavior.

The half-close logic preserves completed statuses across the receiver race, decoding terminates at the first buffer failure, and streamed metadata sizing now matches the response bridge that removes content-length before emission.

Important Files Changed

Filename Overview
src/grpc/src/Client/Call.php Separates successful terminal completion from write misuse and preserves terminal RPC failures across half-close races.
src/grpc/src/Client/StreamState.php Stops lazy frame iteration immediately after recording response-buffer exhaustion.
src/grpc/src/Server/ResponseFactory.php Restricts content-length metadata accounting to unary responses, matching the transport bridge’s emitted headers.
src/grpc/src/Server/GrpcStreamedResponse.php Removes reservation state that is no longer part of streamed metadata accounting.
tests/Grpc/ClientStreamingCallTest.php Adds deterministic coverage for early success, early RPC failure, and receiver-versus-half-close races.
tests/Grpc/StreamStateTest.php Verifies that buffer exhaustion prevents later malformed frames from being decoded.
tests/Grpc/ResponseFactoryTest.php Verifies exact streamed metadata limits against headers actually emitted by the transport.
src/boost/docs/documentation.md Adds navigation links for the existing Object Pools and gRPC guides.

Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin/0.4..." | Re-trigger Greptile

@binaryfire
binaryfire merged commit 54db79c into 0.4 Aug 3, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant