Skip to content

Release idle database sessions and improve coroutine HTTP streaming - #57

Merged
binaryfire merged 24 commits into
0.4from
fix/framework-io-lifecycle
Oct 7, 2026
Merged

binaryfire merged 24 commits into
0.4from
fix/framework-io-lifecycle

Conversation

@binaryfire

@binaryfire binaryfire commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

This PR lets requests return idle database sessions to the pool while waiting on an external service, and makes streamed HTTP responses deliver data incrementally. It also fixes several resource-lifetime and isolation problems exposed by those paths.

The motivating case is a request that reads from the database, waits several seconds for an API, then writes a result. That could be an AI response, a webhook, a payment service, or a file service. Holding a database connection during the wait limits concurrency even when the database itself has little work to do.

Release idle database sessions before HTTP requests

Previously, a coroutine kept its borrowed database connection until it finished. The HTTP client now returns idle database sessions before sending an outgoing request. The next query borrows a session automatically.

The PHP connection object remains owned by the request. Existing builders, query logs, callbacks and sticky read routing stay valid while the underlying PDO can return to the pool. This reuses existing database connections; it does not establish a new database connection for each request.

Release happens after request callbacks and before destination resolution or network I/O, including redirects and retries. Faked requests do not release connections, and the release check does not resolve database services or open a connection.

Active queries, transactions, open cursors and scoped foreign-key suppression keep their sessions automatically. Ordinary queries require no application changes. For other external work, or before starting child coroutines, release explicitly:

use Hypervel\Support\Facades\DB;
use Hypervel\Support\Facades\Http;

use function Hypervel\Coroutine\parallel;

$order = DB::table('orders')->find($orderId);

DB::releaseIdleConnections();

$responses = parallel([
    fn () => Http::get($inventoryUrl),
    fn () => Http::get($shippingUrl),
]);

Children own their own database connections and cannot release a connection held by their parent. Callers supplying a complete Guzzle client through setClient() also own the release boundary.

Code that deliberately depends on one physical session across an external call can pin it:

$connection = DB::connection('pgsql');

$response = $connection->withPinnedSession(function () use ($connection, $accountId, $url) {
    $connection->selectFromWriteConnection('select pg_advisory_lock(?)', [$accountId]);

    try {
        return Http::post($url);
    } finally {
        $connection->selectFromWriteConnection('select pg_advisory_unlock(?)', [$accountId]);
    }
});

DB::withPinnedSession() provides the same scope for the default connection. This is needed for temporary tables, session locks, retained raw PDOs/statements, or manual session changes spanning a release. Transactions already pin automatically. Session configurators still apply the current execution's settings before a borrowed PDO is used.

Custom drivers registered with DB::extend() retain their complete driver object until execution ends. The same applies to selectable endpoints with different drivers, database names or table prefixes. PDO drivers registered through Connection::resolverFor() can participate in early release when their logical database identity agrees.

Database measurements

These are local, pool-constrained workloads, not predictions of general SQL throughput. Measurements used an idle machine, PHP 8.4.25, Swoole 6.2.2, libcurl 8.5.0, file-backed SQLite, identical dependencies, CLI OPcache enabled, JIT disabled and GC enabled. Tables report medians from ten alternating runs; p95 columns are medians of each run's p95.

The integrated workload runs 128 requests at concurrency 32 with four database slots. Each request executes two queries separated by a real, verified-TLS HTTP call whose server waits 50 ms before returning headers. There is no explicit release call in the application path.

Variant Requests/s Database checkout p95 Session hold p95 Request p95 Client CPU/request
Before 74.6 374.7 ms 54.9 ms 427.4 ms 1,014.7 µs
Automatic release 324.8 30.9 ms 7.2 ms 98.6 ms 1,999.3 µs
Transaction pinned 74.3 375.6 ms 55.2 ms 429.3 ms 1,138.4 µs

Automatic release produced 4.35× throughput here. Throughput ranges were 74.4–75.1 requests/s before and 314.5–333.9 after. All borrowed slots were returned. Checkout time includes acquisition bookkeeping as well as waiting for a slot.

A separate experiment replaces the HTTP call with a 50 ms simulated external wait to isolate database retention. It improves from 78.1 to 598.1 requests/s, approximately 7.7×. That is a different workload from the integrated result above.

The integrated CPU increase includes repeated HTTP/TLS setup. A connection-count diagnostic observed 11–12 HTTP connections before versus 119 after, for 128 requests: releasing database slots allows larger HTTP bursts, which exceed Guzzle's three-idle-handle default for buffered requests. Configurable buffered retention is a separate dependency enhancement, tracked with a reproducible test. The streaming retention change below does not fix that buffered path.

The database ownership cost was measured separately, without HTTP or early release:

Database workload Before, CPU/request After, CPU/request Added cost
Resolve a connection 15.4 µs 24.5 µs 9.1 µs
One query 123.9 µs 136.9 µs 13.1 µs
Ten queries 886.4 µs 891.6 µs 5.1 µs
Concurrent queries with a short wait 495.9 µs 506.7 µs 10.8 µs
Transaction 529.5 µs 544.1 µs 14.6 µs

An extra 10 µs per request at 10,000 requests/s uses 0.1 CPU core, or 10% of one core. The added work is constructing and cleaning up caller-owned connection state. Reusing that state across requests would break retained builders and other caller-owned objects.

For HTTP requests that never use the database, before/after CPU and throughput ranges overlap. A separate control-adjusted probe measured the empty-context release check at about 0.18 µs. There is no database initialization on that path.

Select read endpoints with their matching metadata

Read/write connection construction could select one read endpoint for the PDO and another for its configuration metadata. The factory now selects once and uses that record for both.

Explicit ::read pools also selected a read record when the worker constructed the pool, keeping that choice for the worker lifetime. Selection now happens when a physical connection is created. Read records must agree on effective pool options, so pool behavior does not depend on whichever record was chosen first.

Release database resources promptly and safely

Disconnecting or replacing a PDO could leave a hidden reference in a discarded connection object's grammar cycle. The cleanup paths now clear those holders explicitly, including failed preparation and SQLite pool bootstrap.

Lease settlement also preserves ownership during lifecycle callbacks and clears lost resources before rollback callbacks can reconnect. Tests cover retained builders, read/write routing, nested pins, custom drivers, failed acquisition, reconnects and coroutine cleanup.

Preserve reconnects made by connection listeners

A ConnectionEstablished listener can reconnect before the original PDO resolver returns. Returning the earlier PDO would overwrite the replacement session. The resolver now returns the logical connection's current PDO after notifying listeners. Tests cover both write and read resolution, including a replacement read connection that remains lazy until the outer resolver resumes.

Keep transaction callbacks isolated between coroutines

Copying coroutine context could share mutable transaction records with a child. The child could then alter its parent's records or run callbacks belonging to the parent.

Pending, committed and current transaction records now live together in a non-copyable DatabaseTransactionState. Testbench explicitly transfers the same state across setup, test and teardown; normal child coroutines do not inherit transaction ownership.

Deliver HTTP streams incrementally

The PHP stream-wrapper path can batch body data instead of exposing each available chunk. Hooked coroutine requests now use a pull-driven cURL streaming body. Reads return available bytes immediately, and a bounded buffer pauses network reads when the consumer falls behind. There is no background producer coroutine.

$response = Http::withOptions(['stream' => true])->get($url);

try {
    foreach ($response->jsonLines() as $event) {
        processEvent($event);
    }
} finally {
    $response->close();
}

Each active response owns its transfer. Completion, close, cancellation, errors and abandoned consumption release it. Named connections reuse idle transports without retaining request callbacks, credentials or cookies. Proxy tunnel reuse requires the same ownership signature.

For streaming requests, timeout bounds the header phase. When cURL negotiates authentication, this includes waiting for the first body bytes or transfer completion, since even a successful status can be an intermediate response. read_timeout bounds idle gaps during headers and body reads; its default is 60 seconds, and zero disables that idle limit. Long streams can continue beyond the header timeout. Custom handlers/clients retain their existing ownership, and the PHP-stream fallback remains available for supported options that require it.

Bounded connection reuse

Named streaming connections retain up to 32 idle transports per worker, created only when needed. This does not limit active requests. Larger bursts can still open additional connections, which are discarded when the idle cache is full.

Ten alternating paired measurements used 256 verified-TLS requests at concurrency 32, unpaced 4 KiB responses, and the same transport with only the retention bound changed:

Idle retention Requests/s CPU/request New connections after warmup Retained process descriptors
3 525.5 1,731.8 µs 232 18
32 1,262.2 621.3 µs 0 105

On this Linux build, each retained transport accounts for a socket and both ends of a wakeup pipe. The higher bound trades idle resources for less connection setup. Descriptor counts returned to their starting values after cleanup. Runs using the final default also verified reuse at concurrency 32 and bounded retention at concurrency 128.

Swoole's native curl_multi_select() currently has a separate readiness defect that can delay a streamed event or completion by up to one second. It appeared in paced measurements. The regression is captured, and post-fix measurements are tracked in docs/todo.md; this PR adds no polling or shorter-wait workaround. No general performance claim is made for the buffering change while that runtime issue remains.

Release discarded streaming handles without cyclic collection

Guzzle 7 can discard a cURL handle without clearing its callbacks. A receive callback holding that same handle creates a cycle that retains native and request resources until garbage collection. The callback now holds a weak reference; the active transfer remains the owner. The abandoned-response test verifies that both the response body and native handle are released with cyclic collection disabled.

Preserve streaming callback order and cancellation

A small response can finish before the handler exposes its headers. Its trailer callback must still run after on_headers, rather than before it. Completion now preserves that order and delivers the original Guzzle callback arguments.

Pre-header retries retain the original callbacks. Callback failures retain their response where applicable, and coroutine cancellation passes through Guzzle and routing exception conversion unchanged. Statistics are reported once at the handler-return boundary.

Cancel opted-in response producers when their client disconnects

An iterable response can now cancel its producer when the client connection closes:

return response()->eventStream(function () use ($events) {
    yield from $events;
})->cancelOnDisconnect();

Cancellation applies only while producing that response. Application close callbacks still run, and unrelated requests or later termination work are unaffected. The connection map retains every active producer, including pipelined HTTP/1 responses. Tests verify that cancellation interrupts a silent upstream wait and releases the transfer.

Existing Swoole releases do not notify PHP when one HTTP/2 stream is reset while its connection remains open. A quiet producer detects that case on a later failed write or operation completion/timeout. Whole-connection closes are handled now; the separate stream-cancel event is tracked as a future runtime integration.

Avoid repeated coroutine-context lookups

Container scoped resolution, auth defaults, Inertia state, static instances and Blade rendering now retrieve context values once instead of checking and retrieving them separately. Null-as-absent behavior, false/zero values and lazy fallbacks are preserved. Blade does not touch an uninitialized fallback property when context already contains the value.

Expose file and directory checks through the filesystem contract

Filesystem now declares fileExists() and directoryExists(). Concrete adapters already support these checks; contract consumers and custom implementations can now rely on them without capability probes. exists() still accepts either a file or a directory.

$disk = Storage::disk('s3');

$disk->fileExists('reports/latest.json');
$disk->directoryExists('reports');

Custom contract implementations must provide both methods. The Storage facade and porting documentation reflect the contract.

Trace existence checks on all Sentry filesystem decorators

Existence-check instrumentation moves to the base filesystem decorator. Plain and cloud contract implementations now receive the same tracing as adapters, without duplicate instrumentation methods on the adapter decorator.

Expose object property schemas without serialization

ObjectType::getProperties() returns the original named Type objects. Consumers can inspect or reuse them without serializing the schema or bypassing protected state.

use Hypervel\JsonSchema\JsonSchema;

$schema = JsonSchema::object([
    'name' => JsonSchema::string()->required(),
]);

$properties = $schema->getProperties();

Property-array annotations also accept integer keys produced by PHP's normalization of numeric strings. Schema serialization is unchanged.

Cache recursive trait membership

ClassMetadataCache::usesTrait() resolves direct, nested and inherited traits once per class:

use App\Models\Order;
use Hypervel\Database\Eloquent\SoftDeletes;
use Hypervel\Support\ClassMetadataCache;

$usesSoftDeletes = ClassMetadataCache::usesTrait(Order::class, SoftDeletes::class);

The cache stores class metadata rather than every queried class/trait pair, so negative lookups do not keep adding entries. Existing static cleanup clears it between tests.

Watch directory arrivals and skill paths

Fswatch event mapping now recognizes watched roots, their ancestors and recursive descendant directories. Linux watches include the shallow ancestors needed to notice an initially missing root. Glob filtering and sibling exclusions remain intact, without broadening macOS watch operands.

resources/skills/** is included in the default watch paths. The docs explain custom skill paths, conservative reloads on directory arrivals, and Linux fswatch event loss around missing roots and directory moves. Polling drivers remain the appropriate option for those workflows; no rescanning or process-recycling workaround is added.

Restore Testbench environment values through their original repository

Flushing the dotenv repository while masking APP_ENV discarded the writer ownership needed to remove values loaded by an earlier application. Scoped environment helpers could also replace the repository before a WithEnv restorer ran.

The redundant flushes are removed. WithEnv captures the repository that owns its value and restores the raw prior value through that repository. Nested class/method restorers run in reverse order, preserving external environment values and preventing leakage between tests.

Verification and reproducibility

Formatting, source/type static analysis, the framework suite, Testbench package mode and the package-consumer suite pass locally. Focused streaming checks also cover the supported Guzzle 7 and 8 branches. Tests exercise real loopback I/O, database pool contention, cancellation, isolation and cleanup; performance thresholds are not part of CI.

tests/Benchmarks/Database and tests/Benchmarks/HttpTransport include standalone harnesses and reproduction instructions. They report actual concurrency, latency, CPU, connection reuse, pool checkout/hold times, memory, descriptors and GC. Known dependency regressions have explicit tests and tracked follow-ups rather than production workarounds.

Note

Release idle database sessions and add native coroutine HTTP streaming

  • Introduces session leases: a pooled slot exposes a logical PdoConnection whose PDO is resolved lazily through the pool, so coroutine-owned connection objects can survive physical session replacement. See ConnectionLease.php and PooledConnection.php.
  • Adds automatic idle-session release: HTTP requests call ConnectionResolver::releaseIdleConnections() before transmission via middleware in PendingRequest.php; transactions and explicitly pinned sessions stay held via withPinnedSession() in Connection.php.
  • Adds CurlStreamingHandler in CurlStreamingHandler.php for native Swoole cURL streaming with backpressure, trailers, and a PHP-stream fallback, plus DatabaseManager::releaseIdleConnections().
  • Adds opt-in client-disconnect cancellation for IterableStreamedResponse through a per-connection producer registry in ResponseCancellation.php.
  • Risk: filesystem contract Filesystem.php now requires fileExists() and directoryExists() — custom implementations break until updated. Pools without extensions enable session leases only when all selectable endpoints share one logical identity; read records with conflicting pool options or in-memory SQLite now throw InvalidArgumentException.

Macroscope summarized 6d1f614.

Group the public implementation into seven review checkpoints with explicit implementer and reviewer responsibilities. Allow dependencies to move forward while requiring complete files, meaningful tests and signoff before advancing. Preserve copy-first porting and permit coherent whole-file commits with detailed rationale and validation.

Build incremental HTTP streaming against the current framework handler boundaries and existing tests. Remove the unrelated transport-worktree prerequisite while preserving cancellation, resource ownership and performance acceptance requirements.

Validation: reviewed both plan updates and ran git diff --check. Documentation-only change; no source tests or benchmarks were run.
Keep coroutine-owned concrete connections stable while allowing idle physical sessions to return to the pool before long external waits. Add releaseIdleConnections and scoped session pinning, preserving retained builders, sticky routing, query state, transactions, cursors and event callbacks.

Keep config-first extensions and endpoints with different logical identities under whole-connection ownership. Select read endpoints per physical generation, preserve selected configuration metadata, settle leases exactly once, and clear hidden PDO holders promptly on disconnect and failed replacement. Preserve the current owner during terminal callbacks and forget lost resources before rollback callbacks can reconnect.

Cover ownership, read/write routing, custom drivers, session configuration, failure recovery and teardown with regression tests. Document extension and pinning behavior and add an independently runnable database lifecycle benchmark. Benchmarks remain required before opening the framework PR; reviewed checkpoints may proceed before the idle measurement window.

Validation of the complete checkpoint: formatting and source/type PHPStan passed; 40,181 framework tests, 632 Testbench tests and 6 package-consumer tests passed with expected service skips.
Group pending, committed and current transaction records in one non-copyable DatabaseTransactionState. Child coroutines acquire independent transaction ownership instead of sharing parent records and running parent callbacks prematurely or twice.

Preserve transaction callback extension points while retrieving related state together. Keep the intentional Testbench setup-to-test transfer explicit through manager copy methods and a tests-only non-coroutine context setter, so normal child context propagation cannot inherit transactions.

Add regressions for interleaved parent and child callbacks and explicit lifecycle transfer with nested transactions. Validation of the complete checkpoint passed formatting, source/type PHPStan, 40,181 framework tests, 632 Testbench tests and 6 package-consumer tests with expected service skips.
Retrieve scoped container instances, auth defaults, Inertia state, static instances and Blade context values once instead of repeating has/get lookups on request paths.

Preserve null-as-absent behavior, false and zero scoped values, identity-based scoped removal and lazy fallback evaluation. In particular, Blade must not read an uninitialized fallback property when coroutine context already supplies the path.

Existing behavioral coverage verifies the supported values and scoped resolution semantics. The complete checkpoint passed formatting, source/type PHPStan, 40,181 framework tests, 632 Testbench tests and 6 package-consumer tests with expected service skips.
Use a pull-driven cURL transport for streamed requests inside hooked
coroutines. Each active response owns its transfer and applies backpressure
while the caller consumes buffered bytes. Named connections retain at most
three idle transports, matched by proxy ownership and replaced in least
recently used order. Keep custom clients and handlers, PHP-stream fallback,
destination policies, middleware and supported Guzzle 7/8 behavior intact.

Separate the response-header deadline from idle read timeouts, preserve
received responses on timeout and callback failure, and retain native
transfer statistics. Settle transfers on completion, close, cancellation
and abandonment without keeping request callbacks in worker-owned caches.

Add opt-in cancellation for iterable response production when a client
connection closes. Track every active producer, including pipelined HTTP/1
responses, and compose cancellation with application close callbacks only
in supported server modes. Preserve cancellation through Guzzle and routing
exception conversion. Document the existing quiet HTTP/2 stream-reset limit
and expose fluent event-stream typing without narrowing public signatures.

Validation: full composer fix passed, including 40,237 framework tests,
632 Testbench tests, six dogfood tests, PHPStan and formatting. Focused
minimum-version Guzzle 7 coverage passed 75 cases; the original Guzzle 8
dependencies were restored. Controlled performance benchmarks remain a
separate prerequisite before opening the framework PR.
Specify pull-driven streaming, bounded exact-signature idle reuse, timeout
phases, Guzzle compatibility and transfer cleanup in the active port plan.
Keep disconnect registration scoped to response production and distinguish
whole-connection cancellation from the missing native HTTP/2 reset event.

Record the verified stable-runtime limitation accurately and retain the
pre-PR performance acceptance requirements. The framework changes remain
independent of the later AI package implementation.
Improve JWT revocation, guard security, claims and token verification
Declare fileExists() and directoryExists() on the shared contract while preserving the file-or-directory behavior of exists(). Existing adapters and pooled/scoped implementations already provide both operations; update the in-memory fixture and remove the obsolete read-through analysis suppression.

Move Sentry instrumentation to the base filesystem decorator so plain and cloud contract implementations receive the same tracing as adapters. Regenerate the Storage facade annotations and document the required methods for custom implementations.

Validation: filesystem cache fixtures, Sentry storage integration and facade tests pass; formatting, full PHPStan and the full framework suite pass.
Add ObjectType::getProperties() so schema consumers can inspect the original Type objects without serializing them or bypassing protected state. Preserve property names, object identity and empty-object behavior.

Correct property-array annotations across the factory, contract and static API to include integer keys produced by PHP numeric-string normalization. Document the accessor without changing schema serialization.

Validation: focused object-schema tests, formatting, full PHPStan and the full framework suite pass.
Add ClassMetadataCache::usesTrait() using the existing reflection cache. Resolve direct, nested and inherited traits once per queried class, then answer membership checks from that complete set.

Keep worker-lifetime keys bounded by classes rather than arbitrary class-and-trait lookup pairs, so negative lookups do not grow the cache. Clear the new metadata through the existing flushState() test-cleanup registration and avoid introducing a reverse dependency on Support helpers.

Validation: direct, nested, inherited, absent-trait and cache-reset coverage passes, together with formatting, full PHPStan and the full framework suite.
Watch resources/skills by default. Map native events for exact roots, their ancestors and recursive descendant directories so directory arrivals can trigger reloads even when native events omit newly created files. Exact-file watches use their parent as the arrival root and do not match nested directories.

On Linux, retain explicit missing-root operands and add shallow ancestors through the first existing directory. Preserve canonical and literal mappings, glob filtering, sibling exclusions and the existing process grouping; do not broaden Darwin operands.

Document conservative directory-arrival restarts and Linux fswatch limitations for initially absent roots and directory moves or renames. Recommend existing polling drivers for those workflows instead of adding rescans, latency tuning or process recycling. Record the resulting watcher requirements in the active plan.

Validation: scripted mapping and real directory-arrival/subsequent-edit tests pass. Formatting, full PHPStan and all 183 Watcher tests pass after review corrections; the preceding full framework suite also passes. Native macOS missing-root behavior has not been verified.
Remove redundant repository flushes around APP_ENV masking. Environment adapters already read the live globals; replacing the repository discarded the immutable writer ownership needed to forget values loaded by earlier application bootstraps, leaking state into later tests.

Have WithEnv capture its originating repository and raw prior value so cleanup survives scoped environment helpers replacing the active repository. Restore nested class and method attributes in reverse order while preserving external-value protection and leaving other lifecycle callbacks unchanged.

Extend existing tests for scoped-helper composition, class/method attributes sharing a key in both environment-loading modes, and standalone application cleanup. The previously failing application-then-migration test sequence now passes.

Validation: formatting and full PHPStan pass; the framework suite passes with 40,246 tests, package-mode Testbench with 635 tests, and dogfood with 6 tests. Existing service-dependent skips remain.
Keep CURLMOPT_MAXCONNECTS set to one on each streaming multi handle, but remove the dead failure branch around this fixed supported option and its unused exception import. Connection-cache ownership and limits are unchanged.

Validation: formatting, full PHPStan and the full framework suite pass, including the HTTP streaming coverage.
Return the current execution's idle database sessions to their pools before
real framework HTTP requests. Run after application request callbacks and
fake selection, before destination resolution and transport I/O, so redirects
and retries also release sessions borrowed by callbacks.

Make the resolver operation static and delegate to it directly from the
database manager. This avoids resolving database services or opening a
connection merely to release sessions. Existing transactions, cursors,
scoped foreign-key suppression and explicit pins retain their sessions.

Document automatic release, lazy reacquisition, named/default session pins,
manual release before coroutine fan-out and caller-supplied Guzzle clients.
Explain the session-dependent compatibility cases in the porting guide.

Verify real requests against a silent loopback origin with a one-slot pool,
sibling database access, callbacks, retries, redirects and pin preservation.
Formatting, both full PHPStan checks, the full framework suite, Testbench
package mode and dogfood pass. Performance acceptance remains separate.
Pause native receives at BufferStream's high-water mark instead of after
every body chunk. Resume only after draining the buffer, clearing the shared
pause flag before resuming because cURL may synchronously fill and pause the
buffer again. Reads continue returning available bytes immediately.

Defer trailer callbacks when a transfer completes before response exposure.
Deliver their original Guzzle arguments after successful header processing
and before statistics, preserving response-bearing errors and cancellation.
Restore the original trailer callback during pre-header retries and clear
pending arguments during cleanup. Keep post-exposure completion on Guzzle's
existing callback path.

Extend streaming tests for bounded buffering, small and streamed trailers,
header rejection, callback failure, retry and cancellation. Add synchronized
reproductions for the native select readiness defect, concurrent PHP-stream
header retention and Guzzle's shared fallback deadline. Keep the approved
dependency-version skips and upstream-fix TODOs, without runtime workarounds.
Remove deprecated no-op cURL close calls from the affected tests.

Focused callback and streaming checks pass with Guzzle 7 and 8. Formatting,
both full PHPStan checks, 40,255 framework tests, 635 Testbench package tests
and six dogfood tests pass, with configured skips. The first full run hit the
known Swoole #6280 manager shutdown race; the unchanged suite passed on rerun.
High-water performance acceptance remains pending controlled benchmarks and
the separate native select dependency decision.
Extend the database lifecycle harness with independently sized pools,
optional checkout/hold/request latency measurements and a real HTTP wait.
The HTTP scenario exercises automatic release without a manual release call;
transaction mode provides the pinned comparison. Keep instrumentation
optional so CPU and allocation measurements avoid its sample-array cost.

Add a separate verified-TLS loopback origin and HTTP transport harness for
cold and warm requests, paced streams, slow consumers, named transport reuse,
buffered requests and bytewise baseline readers. Report first-event latency,
throughput, client CPU, actual concurrency, native connection identities,
heap, RSS, descriptors and garbage collection. Keep connection identities
cumulative across samples and explain measurement bookkeeping costs.

Document alternating paired runs with identical dependencies and runtime
settings, an idle machine, raw reports outside the repository and no timing
thresholds in CI. Keep simulated waits distinct from integrated HTTP results.
Update the active port plan for default database release, callback ordering,
dependency regressions and the remaining performance acceptance work.

Harness syntax and command-line help checks pass, as do repository formatting,
static analysis and the framework/Testbench/dogfood suites. These commits do
not claim completion of the remaining controlled benchmarks or PR readiness.
Raise the lazy named streaming retention bound from three to 32. Bursts above the old bound repeatedly discarded reusable TLS connections, adding substantial setup cost. Preserve the existing ownership, exact proxy-signature matching and least-recently-used eviction; active requests remain uncapped and unnamed streams retain no idle transports.

Extend the existing direct/proxy integration test through the configured retention bound, preserving credential and cookie isolation assertions and verifying reuse and eviction. Document the bound in the HTTP client guide and its measured descriptor cost in the benchmark guide. Correct the streaming buffering description and record the separate Guzzle buffered-retention dependency decision in the active plan.

Validation: formatting and both full PHPStan configurations pass. The HTTP suite passes with 1,053 tests and 3,143 assertions, retaining 11 existing skips. Verified-TLS runs with the actual default reused all 32 warm connections without further connections and returned descriptors to baseline after cleanup. A concurrency-128 stability run retained the same bounded idle resources; the known Swoole select timing defect remains a separate native dependency.
Add a synchronized real-client regression for two bursts of four buffered requests through one named connection. Guzzle retains only three idle easy handles, so the second burst unnecessarily opens a fifth connection. Keep the owner-requested skip until configurable retention is available and adopted; no dependency workaround is introduced.

Track both Guzzle-dependent skips and the post-fix Swoole native-select regression and performance checks in docs/todo.md. Update the active plan to allow the framework PR before the native fix while withholding unqualified buffering performance claims.

Validation: the final default-configuration test failed its connection-count assertion in 20 of 20 runs, then passed in 20 of 20 runs with only a temporary vendor retention increase from three to four. The complete test file passed with that change and with the skip restored. Vendor was restored byte-for-byte. Formatting and full static analysis passed; the HTTP suite passed with 1,054 tests and 12 intentional or existing skips.
Exclude the imported, unported AI SDK documentation and the AI implementation plan from this framework change. Preserve both on feature/ai for the separate package port. Runtime code, framework documentation, regression tests and dependency follow-ups remain included.
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: hypervel/components-backup/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e4ac11dc-b327-47af-b804-87c8506ae33e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Release idle database sessions and stream coroutine HTTP responses

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Return idle database sessions before outgoing HTTP calls without invalidating request-owned
 connections.
Diagram

graph TD
  Request["Request"] --> Pending["Pending HTTP"] --> Curl["Streaming handler"] --> Body["Response body"] --> Bridge["Response bridge"] --> Cancel["Disconnect cancellation"]
  Pending --> Lease["Database lease"] --> Pool["Database pool"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Retain entire database connections until coroutine completion
  • ➕ Preserves physical session identity without new lease machinery.
  • ➖ Continues holding scarce pool slots during external waits.
2. Produce HTTP streams in background coroutines
  • ➕ Separates network progress from consumer reads.
  • ➖ Adds producer lifecycle, cancellation, and backpressure coordination.

Recommendation: Keep the PR's stable logical-connection leases and pull-driven streaming. They address the measured pool contention and incremental delivery without making retained builders invalid or introducing a background producer. Review the new ownership and cancellation boundaries particularly closely.

Files changed (104) +6330 / -620

Enhancement (25) +1795 / -264
Filesystem.phpDeclare file and directory existence methods +11/-1

Declare file and directory existence methods

• Adds fileExists() and directoryExists() to the filesystem contract, making both checks available to contract consumers.

src/contracts/src/Filesystem/Filesystem.php

ResponseFactory.phpDeclare iterable response factory method +3/-0

Declare iterable response factory method

• Exposes iterable streamed-response creation through the response factory contract.

src/contracts/src/Routing/ResponseFactory.php

Connection.phpPin sessions during database operations +140/-92

Pin sessions during database operations

• Adds scoped pinning for queries and streaming operations, plus an explicit withPinnedSession() API. Active transactions and foreign-key suppression also prevent idle release.

src/database/src/Connection.php

ConnectionResolver.phpRetain logical connections while releasing physical leases +77/-40

Retain logical connections while releasing physical leases

• Stores stable caller-owned connections and their leases separately, and adds a context-only releaseIdleConnections() scan. Terminal cleanup settles either lease-backed or whole-connection owners.

src/database/src/ConnectionResolver.php

DatabaseManager.phpExpose idle release and pinned default sessions +12/-3

Expose idle release and pinned default sessions

• Adds manager entry points for releasing idle sessions and pinning the default connection while preserving purge behavior.

src/database/src/DatabaseManager.php

PdoConnection.phpAttach and detach physical PDO resources safely +129/-40

Attach and detach physical PDO resources safely

• Adds physical-handle transfer between driver and logical connections, pins open cursors, and clears temporary or disconnected PDO holders promptly.

src/database/src/PdoConnection.php

ConnectionLease.phpOwn a reusable physical session separately from its caller +157/-0

Own a reusable physical session separately from its caller

• Introduces the lease that reborrows PDO resources on demand while preserving a stable logical connection. It settles idle or failed sessions and detaches references before reuse.

src/database/src/Pool/ConnectionLease.php

PooledConnection.phpSeparate driver resources from lease ownership +195/-76

Separate driver resources from lease ownership

• Creates and attaches logical leases, preserves ownership through release callbacks, and clears discarded PDO holders without retaining caller state between borrowers.

src/database/src/Pool/PooledConnection.php

ResponseBridge.phpScope cancellation to active iterable production +19/-1

Scope cancellation to active iterable production

• Registers opted-in iterable responses while their content is sent, rejects an already disconnected client, and releases registration afterward.

src/http-server/src/ResponseBridge.php

Server.phpPass stream identity to the response bridge +1/-0

Pass stream identity to the response bridge

• Supplies response-stream context needed for disconnect-aware production.

src/http-server/src/Server.php

CurlStreamingBody.phpPull and bound one streaming cURL transfer +565/-0

Pull and bound one streaming cURL transfer

• Implements consumer-driven body reads with bounded buffering, deadlines, callback ordering, and cleanup on completion, close, error, or cancellation.

src/http/src/Client/CurlStreamingBody.php

CurlStreamingConnection.phpOwn an exclusive streaming transport +26/-0

Own an exclusive streaming transport

• Adds a transport holder used by active streaming responses and the idle reuse cache.

src/http/src/Client/CurlStreamingConnection.php

CurlStreamingHandler.phpRoute supported coroutine streams through cURL +214/-0

Route supported coroutine streams through cURL

• Adds native streaming with fallback for unsupported options and a bounded idle-transport cache for named connections.

src/http/src/Client/CurlStreamingHandler.php

Factory.phpReuse the named streaming handler +5/-1

Reuse the named streaming handler

• Wraps named connection handlers so supported streams share bounded idle transport retention.

src/http/src/Client/Factory.php

PendingRequest.phpRelease idle database sessions before physical HTTP sends +30/-2

Release idle database sessions before physical HTTP sends

• Places idle release after request stubs and before destination resolution for sends and redirects. Selects native streaming when supported and preserves cancellation through exception conversion.

src/http/src/Client/PendingRequest.php

IterableStreamedResponse.phpOffer opt-in disconnect cancellation +20/-0

Offer opt-in disconnect cancellation

• Adds cancelOnDisconnect() and a flag read by server response production.

src/http/src/IterableStreamedResponse.php

ObjectType.phpExpose original object property types +12/-2

Expose original object property types

• Adds getProperties() for direct access to named Type objects without changing schema serialization.

src/json-schema/src/Types/ObjectType.php

ClassMetadataCache.phpCache recursive trait membership +42/-0

Cache recursive trait membership

• Resolves direct, nested, and inherited traits once per class; static cache cleanup now clears that metadata.

src/reflection/src/ClassMetadataCache.php

ResponseFactory.phpConstruct iterable streamed responses +11/-1

Construct iterable streamed responses

• Adds factory support for iterable responses used by disconnect-aware event streaming.

src/routing/src/ResponseFactory.php

FilesystemDecorator.phpTrace existence checks for every filesystem +17/-1

Trace existence checks for every filesystem

• Moves file and directory existence instrumentation to the shared decorator so non-adapter implementations receive tracing.

src/sentry/src/Features/Storage/FilesystemDecorator.php

ResponseCancellation.phpTrack active producers by client connection +79/-0

Track active producers by client connection

• Registers active response coroutines, cancels all producers on a closed connection, and removes registrations after production.

src/server/src/ResponseCancellation.php

Server.phpInstall safe connection-close cancellation hooks +25/-1

Install safe connection-close cancellation hooks

• Registers close handling for supported coroutine dispatch modes, cancels active producers before the application close callback, and configures the server before event registration.

src/server/src/Server.php

DB.phpExpose database release and pinning on the facade +2/-0

Expose database release and pinning on the facade

• Adds facade declarations for releaseIdleConnections() and withPinnedSession().

src/support/src/Facades/DB.php

Response.phpType iterable response factory access +1/-1

Type iterable response factory access

• Aligns facade annotations with iterable streamed-response creation.

src/support/src/Facades/Response.php

Storage.phpExpose explicit existence checks on Storage +2/-2

Expose explicit existence checks on Storage

• Updates facade declarations for fileExists() and directoryExists().

src/support/src/Facades/Storage.php

Bug fix (19) +317 / -173
CoroutineContext.phpExclude transaction ownership from copied contexts +15/-0

Exclude transaction ownership from copied contexts

• Adjusts context-copy preparation so child coroutines do not inherit mutable transaction state.

src/context/src/CoroutineContext.php

JsonSchema.phpAccept normalized numeric property keys +1/-1

Accept normalized numeric property keys

• Widens object-property array annotations to account for PHP converting numeric-string keys to integers.

src/contracts/src/JsonSchema/JsonSchema.php

ManagesTransactions.phpDetach lost transaction resources before callbacks +3/-2

Detach lost transaction resources before callbacks

• Changes lost-connection rollback handling so callbacks cannot inadvertently reuse stale driver resources.

src/database/src/Concerns/ManagesTransactions.php

ConnectionFactory.phpConstruct lease-backed connections consistently +32/-0

Construct lease-backed connections consistently

• Creates logical connections from PDO resolvers and selects each read endpoint once for both its handle and configuration metadata.

src/database/src/Connectors/ConnectionFactory.php

DatabaseTransactionState.phpKeep transaction records in one non-copyable owner +41/-0

Keep transaction records in one non-copyable owner

• Introduces a state object containing pending, committed, and current transaction records to prevent accidental child-coroutine sharing.

src/database/src/DatabaseTransactionState.php

DatabaseTransactionsManager.phpIsolate transaction callback state +64/-109

Isolate transaction callback state

• Moves transaction-record access to DatabaseTransactionState while retaining explicit test-lifecycle transfer of the same state.

src/database/src/DatabaseTransactionsManager.php

DatabasePool.phpSelect read endpoints at physical creation +78/-11

Select read endpoints at physical creation

• Avoids fixing a read endpoint when constructing its pool, checks effective pool-option consistency, and restricts early release to compatible logical identities.

src/database/src/Pool/DatabasePool.php

ReadThroughFilesystem.phpAlign read-through existence handling +1/-1

Align read-through existence handling

• Adjusts an existence check to use the explicit filesystem operation.

src/filesystem/src/ReadThroughFilesystem.php

InteractsWithTestCaseLifecycle.phpPreserve transaction state through test teardown +2/-1

Preserve transaction state through test teardown

• Updates test lifecycle handling to carry the owning transaction state across application teardown.

src/foundation/src/Testing/Concerns/InteractsWithTestCaseLifecycle.php

RunTestsInCoroutine.phpTransfer transaction state into test execution +1/-0

Transfer transaction state into test execution

• Explicitly shares Testbench transaction ownership with the coroutine running the test method.

src/foundation/src/Testing/Concerns/RunTestsInCoroutine.php

Server.phpPass response identity through gRPC server handling +2/-2

Pass response identity through gRPC server handling

• Adjusts server response handling for the response-cancellation lifecycle.

src/grpc/src/Server/Server.php

JsonSchema.phpAlign schema property key annotations +1/-1

Align schema property key annotations

• Accepts integer keys resulting from PHP numeric-string key normalization.

src/json-schema/src/JsonSchema.php

JsonSchemaTypeFactory.phpAlign factory property key annotations +1/-1

Align factory property key annotations

• Updates object-property typing to match normalized PHP array keys.

src/json-schema/src/JsonSchemaTypeFactory.php

WithEnv.phpRestore variables through their owning repository +7/-6

Restore variables through their owning repository

• Captures the dotenv repository and prior raw value for each scoped environment override, preserving nested restorations.

src/testbench/src/Attributes/WithEnv.php

CreatesApplication.phpStop flushing masked environment ownership +1/-4

Stop flushing masked environment ownership

• Removes dotenv flushes that discarded the repository state needed for restoration.

src/testbench/src/Concerns/CreatesApplication.php

Application.phpPreserve standalone environment ownership +0/-3

Preserve standalone environment ownership

• Removes redundant repository flushing during standalone environment masking.

src/testbench/src/Foundation/Application.php

BladeCompiler.phpRead Blade context values once +2/-7

Read Blade context values once

• Eliminates redundant context lookups and avoids touching an uninitialized fallback when context already supplies a value.

src/view/src/Compilers/BladeCompiler.php

FswatchDriver.phpRecognize watched directory arrivals +64/-23

Recognize watched directory arrivals

• Matches watched roots, ancestors, and recursive directory descendants; adds shallow Linux ancestor watches for initially missing roots.

src/watcher/src/Driver/FswatchDriver.php

Server.phpAdjust WebSocket response handling +1/-1

Adjust WebSocket response handling

• Aligns server response handling with the updated response bridge interface.

src/websocket-server/src/Server.php

Refactor (6) +19 / -36
AuthManager.phpAvoid duplicate auth context lookups +2/-5

Avoid duplicate auth context lookups

• Retrieves the contextual auth configuration once while preserving fallback behavior.

src/auth/src/AuthManager.php

PasswordBrokerManager.phpAvoid duplicate broker context lookups +4/-2

Avoid duplicate broker context lookups

• Uses one context retrieval when resolving a guard's password broker.

src/auth/src/Passwords/PasswordBrokerManager.php

Container.phpResolve scoped values with one lookup +9/-5

Resolve scoped values with one lookup

• Removes redundant context presence checks from container resolution and extension paths.

src/container/src/Container.php

InertiaState.phpReduce Inertia context lookups +3/-3

Reduce Inertia context lookups

• Reads contextual Inertia state once without changing fallback semantics.

src/inertia/src/InertiaState.php

FilesystemAdapterDecorator.phpRemove adapter-only existence tracing +0/-16

Remove adapter-only existence tracing

• Drops duplicate existence instrumentation now supplied by the base decorator.

src/sentry/src/Features/Storage/FilesystemAdapterDecorator.php

StaticInstance.phpAvoid duplicate static-instance context reads +1/-5

Avoid duplicate static-instance context reads

• Uses one context lookup when retrieving a contextual static instance.

src/support/src/Traits/StaticInstance.php

Tests (38) +3985 / -139
benchmark.phpBenchmark database ownership and release +305/-0

Benchmark database ownership and release

• Adds standalone workloads for connection use, contention, checkout and hold times, and request costs.

tests/Benchmarks/Database/benchmark.php

server.phpServe HTTP transport benchmark fixtures +63/-0

Serve HTTP transport benchmark fixtures

• Provides controlled responses for connection reuse and transport measurements.

tests/Benchmarks/HttpTransport/Fixtures/server.php

benchmark.phpMeasure streaming transport reuse +239/-0

Measure streaming transport reuse

• Adds a standalone HTTP transport harness covering concurrency, throughput, CPU, connections, and retained resources.

tests/Benchmarks/HttpTransport/benchmark.php

ArrayFilesystem.phpImplement explicit fixture existence methods +17/-1

Implement explicit fixture existence methods

• Adds fileExists() and directoryExists() to the test filesystem implementation.

tests/Cache/Fixtures/ArrayFilesystem.php

ConnectionResolverTest.phpAdapt resolver ownership assertions +22/-20

Adapt resolver ownership assertions

• Updates resolver tests for lease-backed connections, role aliases, cleanup, and failure precedence.

tests/Database/ConnectionResolverTest.php

DatabaseConnectionFactoryTest.phpVerify selected read endpoint metadata +40/-0

Verify selected read endpoint metadata

• Tests that a read connection's configuration matches its selected physical endpoint.

tests/Database/DatabaseConnectionFactoryTest.php

DatabaseConnectionLeaseLifecycleTest.phpTest lease cleanup through lifecycle callbacks +153/-0

Test lease cleanup through lifecycle callbacks

• Exercises release and failure paths that could retain resources or change connection ownership.

tests/Database/DatabaseConnectionLeaseLifecycleTest.php

DatabaseConnectionLeaseTest.phpCover logical connection and physical lease separation +600/-0

Cover logical connection and physical lease separation

• Tests idle release, reborrrowing, pins, transactions, cursors, retained state, child coroutines, and pool contention.

tests/Database/DatabaseConnectionLeaseTest.php

DatabasePdoConnectionTest.phpTest PDO replacement and rollback cleanup +85/-1

Test PDO replacement and rollback cleanup

• Checks resource release on preparation failure, lost connections, reconnects, and terminal transaction handling.

tests/Database/DatabasePdoConnectionTest.php

DatabaseReadPoolTest.phpTest read endpoint selection and pool settings +132/-0

Test read endpoint selection and pool settings

• Verifies per-physical-connection read selection, matching metadata, and effective pool-option consistency.

tests/Database/DatabaseReadPoolTest.php

DatabaseTransactionsManagerTest.phpTest coroutine transaction-state isolation +93/-0

Test coroutine transaction-state isolation

• Ensures child coroutines cannot mutate or execute their parent's transaction callbacks.

tests/Database/DatabaseTransactionsManagerTest.php

PoolManagerTest.phpAdjust read-pool configuration expectations +3/-1

Adjust read-pool configuration expectations

• Updates pool assertions for endpoint selection when physical connections are created.

tests/Database/PoolManagerTest.php

CurlStreamingHandlerTest.phpExercise native streaming handler lifecycle +531/-0

Exercise native streaming handler lifecycle

• Tests buffering, transport ownership, callback ordering, retry behavior, timeouts, and cleanup.

tests/Http/Client/CurlStreamingHandlerTest.php

PublicDestinationPolicyTest.phpAdapt destination pinning assertions +25/-42

Adapt destination pinning assertions

• Updates direct and proxy pinning tests for the supported streaming transport path.

tests/Http/Client/Destinations/PublicDestinationPolicyTest.php

concurrent-stream-deadlines.phpProvide concurrent timeout fixture +93/-0

Provide concurrent timeout fixture

• Serves controlled streaming timings for header and idle-read deadline tests.

tests/Http/Fixtures/concurrent-stream-deadlines.php

concurrent-stream-headers.phpProvide concurrent header fixture +82/-0

Provide concurrent header fixture

• Supplies controlled header delivery for streaming concurrency tests.

tests/Http/Fixtures/concurrent-stream-headers.php

streaming-server.phpServe paced and incremental stream fixtures +80/-4

Serve paced and incremental stream fixtures

• Expands loopback response modes for chunk timing, callback, and timeout tests.

tests/Http/Fixtures/streaming-server.php

HttpClientDestinationPolicyTest.phpCover policy-approved streaming routes +31/-11

Cover policy-approved streaming routes

• Tests streamed requests through authorized destinations, redirects, and proxies while retaining pinning safeguards.

tests/Http/HttpClientDestinationPolicyTest.php

HttpClientStreamingTest.phpVerify incremental coroutine stream delivery +417/-12

Verify incremental coroutine stream delivery

• Adds real-I/O tests for early records, concurrent reads, idle deadlines, cancellation, and response cleanup.

tests/Http/HttpClientStreamingTest.php

HttpConnectionTest.phpVerify named streaming transport reuse +64/-0

Verify named streaming transport reuse

• Tests reuse across fresh clients and concurrent requests with bounded idle retention.

tests/Http/HttpConnectionTest.php

PackageMetadataTest.phpCheck HTTP package dependencies +4/-0

Check HTTP package dependencies

• Asserts streaming transport runtime and optional dependencies are declared.

tests/Http/PackageMetadataTest.php

disconnect-server.phpServe disconnect cancellation fixture +102/-0

Serve disconnect cancellation fixture

• Provides a loopback server for testing interruption of an active response producer.

tests/HttpServer/Fixtures/disconnect-server.php

PackageMetadataTest.phpCheck HTTP server package metadata +1/-1

Check HTTP server package metadata

• Updates dependency assertions for response cancellation.

tests/HttpServer/PackageMetadataTest.php

ResponseBridgeTest.phpTest response-producer registration scope +130/-0

Test response-producer registration scope

• Covers opt-in registration, pre-disconnected clients, and cleanup after iterable production.

tests/HttpServer/ResponseBridgeTest.php

ResponseCancellationTest.phpTest active-producer cancellation ownership +225/-0

Test active-producer cancellation ownership

• Checks close cancellation, multiple producers, registration release, and isolation from later work.

tests/HttpServer/ResponseCancellationTest.php

PooledConnectionTest.phpCheck pooled connection lifecycle cleanup +15/-4

Check pooled connection lifecycle cleanup

• Adjusts integration assertions for leases and verifies release resets per-borrow error state.

tests/Integration/Database/PooledConnectionTest.php

ConnectionEstablishedTest.phpVerify generation-specific connection events +17/-6

Verify generation-specific connection events

• Tests connection-established notifications when SQLite physical generations are replaced.

tests/Integration/Database/Sqlite/ConnectionEstablishedTest.php

ObjectTypeTest.phpTest original schema property retrieval +11/-0

Test original schema property retrieval

• Verifies getProperties() returns named Type objects.

tests/JsonSchema/ObjectTypeTest.php

ResponseFactoryTest.phpTest iterable response creation +38/-0

Test iterable response creation

• Covers response factory creation and cancellation opt-in behavior.

tests/Routing/ResponseFactoryTest.php

StorageIntegrationTest.phpTest existence tracing across filesystem implementations +35/-0

Test existence tracing across filesystem implementations

• Verifies plain and cloud filesystem checks receive decorator tracing.

tests/Sentry/Features/StorageIntegrationTest.php

ServerTest.phpTest close-hook registration and configuration order +72/-1

Test close-hook registration and configuration order

• Covers eligible server dispatch modes and close-callback behavior for response cancellation.

tests/Server/ServerTest.php

ClassMetadataCacheTest.phpTest nested and inherited trait caching +29/-0

Test nested and inherited trait caching

• Verifies positive and negative trait membership and cache clearing.

tests/Support/ClassMetadataCacheTest.php

WithEnvTest.phpTest nested environment restoration +31/-0

Test nested environment restoration

• Checks scoped overrides restore original raw values without leaking between tests.

tests/Testbench/Attributes/WithEnvTest.php

ApplicationTest.phpTest standalone environment preservation +4/-1

Test standalone environment preservation

• Verifies masked application environment values are restored after bootstrap.

tests/Testbench/Foundation/ApplicationTest.php

TestCaseTest.phpTest environment ownership across test lifecycle +8/-3

Test environment ownership across test lifecycle

• Covers bootstrap failure, scoped values during requests, and final restoration.

tests/Testbench/TestCaseTest.php

FswatchDriverTest.phpTest root and ancestor directory events +165/-31

Test root and ancestor directory events

• Adds cases for missing roots, recursive arrivals, Linux ancestor operands, symlinks, and sibling exclusions.

tests/Watcher/Driver/FswatchDriverTest.php

Connection.phpType-check pinned session callbacks +1/-0

Type-check pinned session callbacks

• Adds a type fixture for withPinnedSession() callback return preservation.

types/Database/Connection.php

ResponseFactory.phpType-check iterable response creation +22/-0

Type-check iterable response creation

• Adds static-analysis coverage for response factory return types and cancellation chaining.

types/Routing/ResponseFactory.php

Documentation (12) +209 / -8
todo.mdTrack transport follow-ups +10/-0

Track transport follow-ups

• Records known Swoole streaming readiness and buffered HTTP connection-retention follow-ups instead of adding workarounds.

docs/todo.md

README.mdPoint readers to session-lifecycle guidance +1/-0

Point readers to session-lifecycle guidance

• Adds a documentation reference for database connection behavior.

src/database/README.md

database.mdDocument idle release and session pinning +58/-2

Document idle release and session pinning

• Explains automatic HTTP release, explicit release and pinning, retained connection behavior, and endpoint constraints.

src/docs/database.md

filesystem.mdDocument precise filesystem existence checks +4/-2

Document precise filesystem existence checks

• Explains fileExists() and directoryExists() alongside the broader exists() operation.

src/docs/filesystem.md

http-client.mdDocument incremental streaming and release boundaries +14/-4

Document incremental streaming and release boundaries

• Describes streaming consumption, timeout behavior, transport ownership, and database release around outgoing requests.

src/docs/http-client.md

json-schema.mdDocument object property inspection +6/-0

Document object property inspection

• Shows how to obtain the original Type objects through ObjectType::getProperties().

src/docs/json-schema.md

porting-from-laravel.mdNote new contract and database porting requirements +4/-0

Note new contract and database porting requirements

• Calls out compatibility considerations for filesystem implementations and database session behavior.

src/docs/porting-from-laravel.md

responses.mdDocument disconnect-aware iterable responses +18/-0

Document disconnect-aware iterable responses

• Adds guidance for opting an event-stream producer into cancellation when its client disconnects.

src/docs/responses.md

watcher.mdExplain skill watches and directory-arrival limits +7/-0

Explain skill watches and directory-arrival limits

• Documents watched skill paths, directory arrivals, and fswatch event-loss cases where polling is preferable.

src/docs/watcher.md

README.mdDescribe filesystem contract additions +2/-0

Describe filesystem contract additions

• Notes the explicit file and directory existence methods available to implementations.

src/filesystem/README.md

README.mdDocument database benchmark reproduction +39/-0

Document database benchmark reproduction

• Explains workloads, setup, and reported pool and request measurements.

tests/Benchmarks/Database/README.md

README.mdDocument HTTP benchmark reproduction +46/-0

Document HTTP benchmark reproduction

• Describes transport workloads, configuration, and interpretation of latency and resource measurements.

tests/Benchmarks/HttpTransport/README.md

Other (4) +5 / -0
composer.jsonDeclare response-cancellation runtime dependency +1/-0

Declare response-cancellation runtime dependency

• Adds the package dependency needed by HTTP server response cancellation.

src/http-server/composer.json

composer.jsonDeclare native streaming dependencies +2/-0

Declare native streaming dependencies

• Updates package metadata for the cURL-based streaming implementation.

src/http/composer.json

AfterEachTestSubscriber.phpClear response cancellation state after tests +1/-0

Clear response cancellation state after tests

• Includes active-producer registrations in framework static-state cleanup.

src/testing/src/PHPUnit/AfterEachTestSubscriber.php

watcher.phpWatch framework skill resources by default +1/-0

Watch framework skill resources by default

• Adds resources/skills/** to default watcher paths.

src/watcher/config/watcher.php

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Streaming downloads ignore their destination 🐞 Bug ≡ Correctness
Description
CurlStreamingHandler::__invoke() replaces the caller's sink option with its internal buffer sink
and never writes to the original destination. When a request combines sink($destination) with
stream => true on the native coroutine transport, it returns a response while leaving the
destination unwritten; fake responses still honor it.
Code

src/http/src/Client/CurlStreamingHandler.php[R133-135]

+            $transferOptions = $options;
+            unset($transferOptions['on_headers'], $transferOptions['on_stats']);
+            $transferOptions['sink'] = $sink;
Evidence
sink() stores the destination in request options, but the new native handler unconditionally
replaces that option with a sink that writes only to its private buffer. The fake-response path
separately processes the original sink, so it does not share this behavior.

src/http/src/Client/PendingRequest.php[591-602]
src/http/src/Client/CurlStreamingHandler.php[64-71]
src/http/src/Client/CurlStreamingHandler.php[115-138]
src/http/src/Client/PendingRequest.php[1886-1895]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Native streaming silently replaces an explicitly configured response sink, leaving the caller's destination unwritten.
## Fix Focus Areas
- src/http/src/Client/CurlStreamingHandler.php[133-138]
- src/http/src/Client/PendingRequest.php[597-602]
## Recommended Fix
Preserve the caller's sink semantics when native streaming is selected, or explicitly route requests with a caller-provided sink to a transport that supports them. Add a test combining `stream => true` with `sink()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/http/src/Client/CurlStreamingHandler.php
@greptile-apps

greptile-apps Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[High risk] Restructures database connection pooling and session lifecycle.

The PR still needs to prevent retained builders from leaking database slots after their owning coroutine ends.

Fix All in Claude CodeFindings

  1. P1 Retained builders leak database slots ▶

Summary

This PR returns idle database sessions before outgoing HTTP requests and adds incremental cURL streaming. The latest changes tighten endpoint identity checks and resource cleanup.

  • ConnectionLease now returns the replacement PDO when a connection listener reconnects.
  • DatabasePool includes the driver when comparing selectable endpoints.
  • CurlStreamingHandler avoids retaining discarded handles through its write callback.
  • Documentation explains coroutine ownership and streamed authentication timing.

Reviews (2) · Last reviewed commit: "Release discarded streaming handles with..." · Reviewed by Greptile

Comment thread src/database/src/Pool/ConnectionLease.php
Include the effective driver when deciding whether selectable read or
write records can share a logical connection across session leases.
Different drivers need their own connection class and grammar even when
the database and table prefix match.

Resolve the current logical PDO after publishing ConnectionEstablished.
A listener may reconnect during publication; returning the earlier PDO
would otherwise overwrite its replacement. Cover both write and read
resolution, including a replacement read PDO that remains lazy until
the outer resolver resumes.

Extend the identity tests so each case varies only its selected role,
and verify that listener reconnects retain and return one pool slot.
Clarify that connections and their builders belong to the coroutine
that resolved them and must not be passed to another coroutine.

Validated with formatting, full static analysis, and the database and
database integration suites. The reconnect regressions fail against the
previous implementation and pass with this change.
Keep a weak reference to the native handle in the receive callback.
Guzzle 7 can discard a handle without clearing its callbacks, so a strong
capture retained the handle, request and native resources until cyclic
garbage collection. The active transfer continues to own the handle;
the callback only dereferences it when pausing at the buffer limit.

Extend the abandoned-response test to check native handle lifetime with
cyclic collection disabled. Observe the real factory through a small
decorator so the test itself does not retain callback-bearing options.
The regression fails on Guzzle 7 before the change and passes on both
supported Guzzle majors afterwards.

Document that native cURL authentication negotiation can delay response
exposure until body bytes arrive or the transfer completes, and that
the request timeout covers this wait. A successful status alone cannot
identify the final response during POST authentication negotiation.

Validated with formatting, full static analysis, the HTTP client suite,
and the streaming-handler tests on Guzzle 7.15.2 and Guzzle 8.2.
@binaryfire
binaryfire merged commit 23633a0 into 0.4 Oct 7, 2026
53 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