Skip to content

Add qualified Laravel FPM and optional Octane recipe - #169

Merged
rmcdaniel merged 32 commits into
mainfrom
docs/laravel-service-octane
Oct 18, 2026
Merged

rmcdaniel merged 32 commits into
mainfrom
docs/laravel-service-octane

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Outcome

Add #112's runnable Laravel service HTTP application with ordinary PHP-FPM and optional Octane/RoadRunner. Independent supervised Artisan workers execute durable work. No core SDK requirement, package version or runtime dependency changes.

Start with FPM for this recipe. Both profiles pass the bounded functional/recovery/load qualification, but Octane does not demonstrate a consistent application CPU or workflow-latency gain and uses about 185 MiB more charged memory. Production capacity remains workload-specific.

Exact source and published inputs

Final source 516d13a22a35adedd71c67095cf7f33176e9ef44, whole tree 70dea88627833b67c7c6978f9880c8cd111333a7. Measured application runtime/configuration remains e81b7c80, common driver af741d1; final changes are documentation only. Normally installed SDK 2.4.2 / 25d796ad, PHP 8.3.35, Laravel 12.69.3, Octane 2.21.0, RoadRunner 2025.1.15 and published Server 2.8.7 / 964f9e3d / immutable index 27c08e15, Workflow 2.5.32 / 78075b6f. All 166 installed SDK files and 51 application files are checked before admission.

The tested override has four HTTP workers and two independent CLI workers under the combined 2 CPU / 1 GiB application allocation. Separate backend and generator costs are disclosed. Fixed offers are six context reads and one workflow per second, with original 35-second observers and 36-second drain.

Verified results

  • Eager/lazy resolution, repeated identities/headers/logs, authorization refusals, exact cold typed reads, real reloads, TTL/capacity eviction, CLI SIGKILL and ten-second API outage controls pass. Outage recovery uses actual supervised process replacement. Intentional stale registrations and restart counts remain explicit.
  • Both 300-second mixed reload soaks pass 300 original workflows and 1,800 contexts, independent histories/deadlines, zero errors/drops/repairs/retries/pending work and two verified four-worker reloads. Final stack roles exit zero without OOM; normal registrations return to zero.
  • Three alternating pairs plus fresh FPM drift pass seven 120-workflow/720-context cells, each with 360 first-attempt tasks and the same strict checks.
  • Three-cell application CPU means are 16.75 seconds FPM and 16.58 seconds Octane, a 1.05% difference with different pair directions. FPM baseline drift is 5.34%, larger than that difference. Charged memory means are 138.60 / 323.38 MiB. Both record zero history-cache hits.

The qualification report includes p50/p95/p99, component CPU/throttling/RSS/charged memory, backend response bytes, timer latency, cache counters, commands and linked failed originals. Earlier failures remain failed; no common cause or replay/execution saving is claimed.

Delivery

Merged at d3f9dd7373876148b8a356241c4af93434ba31a6, with the identical qualified whole tree. Fresh-main CI 39088255960, Sonar 39088255959, boundary and portal 39088255969 pass: all 37 applicable checks succeed, with only the opt-in connected-cancellation job skipped.

The live Laravel guide links the runnable recipe; its deployment audit reports the exact merged revision. Served recipe/report bytes match the qualified source. Experiment stacks/databases/raw samples are deleted after immediate audit. The temporary host and task access key were deleted at 01:51:48 UTC, remaining local task data at 01:54:20, and the completed clean source worktree/branch are retired. #112 is delivered. No SDK release, native/shared adoption or frozen tuple change is implied.

@rmcdaniel

Copy link
Copy Markdown
Member Author

HTTP request-isolation preparation now passes on exact candidate f8aa08ae1c5bbec1271c0dc6a02f5a5daf7c407e in draft #169.

Profile Alternating requests Refusals Resolution at boot
Octane lazy 40 3 false
Octane eager 40 3 true
Octane after standard reload 40 3 true, new HTTP worker PIDs
FPM eager 40 3 true
FPM lazy 40 3 false

All 200 requests preserve the current user, fixed namespace, request ID and optional header. Independent readback verifies exactly one matching log-context record for each request, including null context when the header was omitted. Each cohort refuses absent application auth, another namespace and invalid request IDs. Both HTTP workers were observed in every profile. Final FPM/nginx shutdowns exit 0, no OOM or container restart; the task containers and network are removed.

Normally installed PHP SDK 2.4.0 source/dist is 821716a92f179ec360e045c28979a86a5f587075; PHP 8.3.35 / Laravel 12.69.3 / Octane 2.21.0. Standard RoadRunner 2025.1.15 binary SHA-256 is c67dd4dc6d14c0120a7f8a2edcb035eddc693db880d349f3be33a38728b89efb. Credentials remain absent from the cached configuration. Initial strict Composer validation stops on intentional exact-pin warnings; ordinary validation and normal installation pass with no security advisories.

Preparation caught and corrected an example guard: Octane bootstraps through CLI, so runningInConsole() cannot select its eager HTTP profile. The example now uses an explicit HTTP process role and reports actual boot resolution. This is a fixture correction, not an SDK leak finding.

These context requests perform no runtime operation. No Server worker or workflow was admitted. They do not qualify backend namespace authorization, durable execution, CLI recovery, cache replay, timers or performance. Those are the next gates on #112. Exact-head hosted normal checks remain pending; the draft stays open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The service application has actual normal-installed lifecycle results on immutable PHP Server 2.7.1, image sha256:38a67d4591675c227299dfe7f70c79c0fe401332c51ebc7d51fec36252df8afe, embedded Workflow 2.5.19, PHP SDK 2.4.0 / 821716a, Laravel 12.69.3 / PHP 8.3.35 / Octane 2.21.0 / RoadRunner 2025.1.15.

Exact application 221dcee passed two separate five-original cohorts, one Octane and one FPM, using the same independently running Artisan worker. Each has four completions, one terminal cancellation, two queries, one signal and a foreign-user 404. Independent PHP result reads preserve exact int64/null/Unicode. Read-only SQLite audit verifies ten original runs, ten single activity completions, eight workflow completions, two fired timers, first attempts and zero repairs. Accepted run/execution budgets remain 600/900 seconds and status observers remain 35 seconds. The actual client role independently refuses another runtime namespace with 403. Application limits stay 2 CPU/1 GiB/no swap; the separate SQLite-backed runtime uses 1 CPU/1 GiB API and 0.5 CPU/512 MiB queue resources.

Teardown caught an example defect: the FPM base image defaults to SIGQUIT, so the original durable CLI worker exited 131/no OOM. Those originals keep that failed shutdown result. Current candidate a121ff4 explicitly selects SIGTERM. A separate fresh namespace/role startup and shutdown control exits 0, has no OOM/restart, and records actual deregistration HTTP 200. It does not replace or rescue the original teardown. No SDK library behavior changed.

Setup failures are explicit: the first empty database-directory bind hid published migrations/seeder; the named-volume correction precedes any admission. The first shutdown-control provision reused a revoked token and was refused before registration; its unadmitted partial namespace was retired, then fresh credentials were used. All roles/namespaces are now retired and task containers/networks/volume are absent.

This remains a draft qualification slice. CLI SIGKILL/cold recovery, cache/lost-connection controls, the full namespace/lifecycle matrix, three performance pairs, drift and soak remain open. No measured Octane benefit or full #112 delivery is claimed. The original Server 2.7.1 tuple is retained as its own result, independently of the later 2.7.2 dependency work.

@rmcdaniel

rmcdaniel commented Oct 14, 2026 •

Copy link
Copy Markdown
Member Author

The separate worker replacement control passes on application source 5b50e9e108c50c277afd84f759b99cee000539f4 with normally installed PHP SDK 2.4.0 / 821716a, PHP 8.3.35, Laravel 12.69.3, Octane 2.21.0 and RoadRunner 2025.1.15. Runtime is immutable published Server 2.7.1 / sha256:38a67d4591675c227299dfe7f70c79c0fe401332c51ebc7d51fec36252df8afe, with Workflow 2.5.19 and isolated SQLite/database queue/file cache.

One original recovery-timer run was admitted at 18:31:46 UTC. Before SIGKILL, canonical history had one completed activity and one persisted unfired ten-second timer. The actual workflow task completion responses were HTTP 200. Automatic container restart was disabled before admission. The original worker exited 137 with no OOM or restart; a fresh process registered normally, without seeding another workflow or manual repair.

The same original workflow/run completed within its declared 90-second observation budget, observed at 18:32:22 UTC. The entire six-event prefix and accepted 600/900-second deadline fields stayed unchanged. Final history has eight events, one ActivityCompleted, one TimerFired and one WorkflowCompleted. The installed SDK result preserves int64 9007199254740993, null and Unicode. Read-only SQL independently verifies five completed first-attempt tasks and zero repairs. The replacement shut down with SIGTERM, exit 0/no OOM/restarts, and actual deregistration HTTP 200.

An initial control-plane setup request used the worker-protocol version in the independent control-plane header and received 400 before namespace or workflow admission. Correcting that fixture header to control-plane version 2 allowed fresh namespace/role creation; no runtime/SDK source correction occurred.

Both roles were revoked with HTTP 200 and namespace absence verified as 404. All application/runtime containers, database, network, unique tool image/stages and disposable dependencies/data/credentials were removed by 18:35 UTC. Existing failed SIGQUIT teardown remains failed. This qualifies this one CLI replacement path; cache expiry/eviction, runtime connection loss, broader namespaces, paired performance and soak remain open under #112.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The fresh cache qualification attempt is unqualified on unchanged example source 7113527ba0a775c70e82a7eef4541c9e57c1559f, normal PHP SDK 2.4.0 / 821716a9, and published Server 2.7.2 / sha256:de4302dd4e636a0d71b898e3fadcf86708912ed8bdb94514a12123675186029c.

The first declared profile uses cache capacity 100 / TTL 1 second and a 90-second original observation budget. Its original reached one completed activity and a persisted signal wait. The counter checkpoint then refused because the active worker's public process_metrics contained only process_id and process_uptime_seconds, with no sticky_cache counters. The worker advertises sticky_execution. Missing telemetry was not treated as zero or a cache pass.

The planned expiry assertion stopped at that failed checkpoint. A later diagnostic signal completed the same original outside the qualification budget; SQL reads show completed status, 11 events, four completed first-attempt tasks and zero repairs. That does not rescue the failed cache qualification, and no decoded-output acceptance is claimed here. Capacity-one eviction and network interruption were never run or admitted.

Read-only PHP source inspection shows that workflow completion can write process_metrics.sticky_cache, while a later worker heartbeat replaces the process metrics with its normalized heartbeat snapshot. Whether that path explains this original's missing counters needs a focused reproduction; cache execution itself is not established as defective.

Next: establish a trustworthy counter observation using the normal SDK/HTTP path, reproduce any projection loss, then rerun separate new expiry/eviction/connection originals before performance work. No runtime/SDK fix, deadline change, historical cause or full #112 acceptance is claimed.

Normal worker shutdown exits 0/no OOM/restarts. Both roles revoke 200, namespace absence is 404, and all task containers, database, networks, images, dependencies, credentials and raw scratch are removed before the coordinated host interval.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Published-profile follow-through for #112: PR #169 now pins only SDK 2.4.1 (source/dist 520a53f2c4c7fe19358556375302e8e47e09e519); all 33 applicable normal checks pass, with connected cooperation still skipped. Laravel 12.69.3 / PHP 8.3.35 and the other locked dependencies are unchanged.

Before admission, the first new cache profile is fixed to normal Artisan CLI, immutable Server 2.7.2 / Workflow 2.5.20, isolated SQLite/file cache, SDK cache capacity 100 / TTL 1 second, worker 1 CPU / 320 MiB / no swap / no restart. One original signal-waiting workflow retains its 600-second run and 900-second execution budgets; observation is 90 seconds, with a three-second idle interval before release. Require exact typed output, preserved full history prefix and original deadlines, same worker process/registration, and actual shutdown cache counters. No rescue, reseed or repair. The SDK's published shutdown diagnostics now supply counters; absence is a failed observation, not zero. Eviction and connection-loss profiles remain separate and unadmitted.

An initial preparation command used port 80 instead of this image's 8080 listener and failed before namespace/workflow admission. The endpoint was corrected before the new original. This is setup, not a product failure or rescued workflow. No Octane performance, full #112 acceptance, Cloud or frozen-cohort claim follows.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The new published 2.4.1 / Server 2.7.2 CLI expiry original passes: cache-expiry-2054 / 01m4y31hp23wbwdqdsrma7pyay, 5.236 seconds including the fixed three-second idle interval. The original 11-event history, complete pre-release prefix, 600/900-second deadlines and int64/null/Unicode output are exact; one activity and workflow completion. All four tasks complete first attempt with zero repairs under the same worker registration/process. Standard SIGTERM exits 0, no OOM/restart, registration removed.

Actual published shutdown diagnostics report hit=0, miss=3, eviction=1, forced_cold_replay=0. TTL expiry therefore occurred; the runtime supplied complete authoritative histories, so this is not a suffix-fetch or nonzero forced-cold-counter claim.

The next separately named original profile freezes capacity 1 / TTL 300, the same immutable installed tuple and CLI resources, two signal-waiting originals (target then neighbor), each retaining 600/900-second deadlines within one 90-second observation. It requires actual capacity eviction counters, exact prefixes/results and surviving neighbor, same process, and normal shutdown. Connection loss remains separate and unadmitted. No full #112 or performance claim.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Capacity eviction now passes both actual originals: cache-eviction-2056-first / 01m4y34scqjn4nh39vc2y2ah72 and neighbor 01m4y34td0427xfkjhzvr63yzx. Both preserve complete prefixes, exact typed results, original deadlines and one activity/workflow completion. Published worker shutdown reports hit=0, miss=6, eviction=3, forced_cold_replay=4; normal SIGTERM exits 0/no OOM/restart. The aggregate pair finishes in 5.212 seconds under the original 90-second observation.

Before the separate connection-loss admission: same published SDK 2.4.1 / immutable Server 2.7.2, cache capacity100/TTL1, one original signal-waiting workflow, 600/900-second deadlines, 90-second observation and identical CLI resource limits. Use the recipe's normal unless-stopped supervision. After canonical waiting/activity completion, disconnect only the CLI container's private network for ten seconds, restore it, then release the same selected run. Require actual transport-error evidence, original prefix/deadlines/exact output and report any automatic worker restart/registration replacement explicitly. No manual worker restart, reseed, repair or original deadline extension. This tests supervised connection recovery, not a promised same-process outage policy or performance.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The ten-second Docker network-disconnect original completed in 27.722 seconds with preserved prefix/deadlines/exact result, no automatic restart. However no SDK transport error was observed, so it does not qualify the stated connection-error recovery gate.

A distinct prospective control now freezes the same tuple/resources/cache/90-second observation and 600/900-second original deadlines, with the normal CLI supervisor. Once a fresh original is canonically waiting after its activity, stop only this isolated test API for ten seconds, then start the same API container on its unchanged database/image. Require a real SDK connection refusal, report automatic worker replacements, then release the same selected original. No production runtime, manual worker restart/repair, seed replacement or deadline extension. The earlier network observation remains separately limited.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Published cache/reconnection controls and reusable driver

PR #169 source 50259e6 adds qualification/cache.php and the bounded recipe. The application normally installs SDK 2.4.1 / 520a53f2; PHP 8.3.35, Laravel 12.69.3 and immutable Server 2.7.2 / Workflow 2.5.20 are fixed. Normal CLI limits stay 1 CPU / 320 MiB / no swap. No HTTP performance comparison ran in this phase.

Actual controls pass:

  • TTL expiry: capacity100/TTL1, three-second idle, same original completes in 5.236s. Shutdown counters: hit0/miss3/eviction1/forced-cold0; complete runtime history explains zero forced-cold, without fabricating a hit.
  • Capacity eviction: capacity1/TTL300, target plus waiting neighbor both complete in 5.212s aggregate; hit0/miss6/eviction3/forced-cold4. Both histories/deadlines survive.
  • Observed connection failure: a separately admitted waiting original survives the isolated API stop/start on its unchanged database. Published SDK logs actual cURL connection refusals and heartbeat retry/backoff. Same CLI process/registration, restart0, exact original completes in 51.429s. Normal SIGTERM exits0/no OOM. The earlier ten-second network-disconnect original also completes, but its lack of observed transport error remains an explicit limitation, not error-recovery acceptance.
  • The committed driver itself passes actual expiry, two-phase outage checkpoint/resume, and two-original eviction through the normal installed SDK. It retains admitted IDs before waiting, refuses checkpoint reuse/authority mismatch/incomplete waiting checkpoints, and preserves the original observation cutoff across outage phases.

All nine original runs complete exactly once with the complete pre-release history prefix, one echo activity, exact int64/null/Unicode result and unchanged 600/900-second deadlines inside their 90-second observations. Independent SQLite audit: 36 completed first-attempt tasks, zero repairs, one worker owner per run. Every CLI exits0/no OOM/restart; all registrations are removed. Final recipe-driver eviction repeats actual hit0/miss6/eviction3/forced-cold4. Two isolated roles revoked200; namespace deletion200 and absence404 verified.

The initial wrong-port preparation failure and the network-only observation remain separately limited. Fresh exact-head hosted checks are pending. This qualifies CLI cache/reconnection behavior; FPM/Octane paired measurement, soak and overall #112 acceptance remain pending. No Cloud, native/shared, frozen cohort, authority policy or SDK-major claim. Disposable local data/logs/dependencies and runtime resources are being deleted; the driver and this readable outcome are retained.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Current candidate is 7110a50869150fa5cf138ebf42937adbeab3ab5f (tree 56fbccd6f73ca60737a7a666f57dbc9668aa3170), including current main and the released 2.4.1 shutdown cache diagnostics. All 33 applicable normal checks pass; the conditional connected cooperative check is skipped.

The load fixture uses standard k6 arrival-rate executors, counts every expected admission and terminal result, keeps the original 35-second observations, and records HTTP worker identities. Its native k6 configuration inspection passes. The separate installed PHP reader checks exact typed outputs, canonical event counts and persisted timer timestamps.

The actual HTTP load/reader smoke and the fixed FPM/Octane pairs, drift recheck and mixed soak remain pending. No performance result or Octane recommendation follows from these source checks. Published-package cache and connection controls remain the separate qualified receipt.

@rmcdaniel

rmcdaniel commented Oct 14, 2026 •

Copy link
Copy Markdown
Member Author

Functional qualification of the example driver is complete at e907a97c989362c17013ad207b5798da981881a2 / tree 8941934309ddabbaf8e83bda52ae0c2b34bfb9b9.

Three original calibrations remain unsuccessful: the first dropped two offers, the second dropped one context offer while the client pool was growing, and full preallocation then exposed a sixth workflow admission at the terminal scheduler boundary. Those outcomes are not relabeled.

The correction preallocates the existing maximum client pool (8 context / 16 workflow VUs) and checks the finite offer index and original admission window before HTTP mutation. Maximum concurrency, 6 context reads + 1 workflow per second, 35-second workflow observations, 36-second drain and exact count/error/zero-drop gates are unchanged. Extra terminal scheduler iterations issue no request.

Two subsequent five-second functional profiles pass, first FPM and then Octane:

Check FPM Octane
Workflow offers/admissions/terminal observations 5/5/5 5/5/5
Completed / cancelled 4/1 4/1
Context requests 30 30
Dropped offers / failed checks / HTTP errors 0/0/0 0/0/0
Canonical events independently read through the installed SDK 40 40

Both installed readers verify exact int64/null/Unicode outputs and the actual timer event. These are short functional controls on one isolated backend, not independent measured pairs or evidence of an Octane performance benefit.

The normal installed tuple is PHP SDK 2.4.1 / 520a53f2c4c7fe19358556375302e8e47e09e519, Server 2.7.2 at immutable image index de4302dd4e636a0d71b898e3fadcf86708912ed8bdb94514a12123675186029c, PHP 8.3.35, Laravel 12.69.3, Octane 2.21.0 and RoadRunner 2025.1.15. Loaded Worker SHA-256 is e04cc86edb36a952508cd07faaac46e4b40783d31d385fe777e5a026306dc077.

After stopping the SDK/HTTP/queue/scheduler roles, an independent read-only SQL audit accounts for all five profiles: 26 runs (21 completed / 5 cancelled), 206 history events, 62 completed workflow tasks, 26 completed activity tasks and 5 completed timers, all first-attempt; 5 cancelled timers never claimed. Repairs, retried tasks, live tasks, registrations and failed jobs are all zero. Original run timeout remains 600 seconds. The normal CLI and Octane shutdowns exit 0 with no OOM/restarts. Shutdown cache counters are retained-history observations, not live Fiber-retention measurements.

Both roles are revoked and the namespace independently returns 404. All task containers, volumes, network, built application image and task-pulled Server image are removed; preexisting/foreign images remain untouched. Disposable raw records, dependencies and the test sandbox are deleted after verifying this readable outcome.

All 33 applicable exact-head hosted checks now pass in 38647502357; conditional connected cancellation is explicitly skipped. Three independent measured FPM/Octane pairs, baseline drift, mixed-request soak and final review remain required. PR #169 remains draft; no full #112 acceptance or capacity claim.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Current published-tuple controls pass their workflow/history criteria on SDK 2.4.2 / 25d796ad, immutable PHP Server 2.8.6 / b3c53408, loaded Workflow 2.5.22 and unchanged application/tool locks:

Control Originals / completed tasks Actual cache counters: hit / miss / eviction / forced cold
Three-second TTL expiry, capacity100/TTL1 1 / 4 0 / 3 / 2 / 0
Capacity-one eviction, TTL300 2 / 8 0 / 6 / 3 / 4
Ten-second isolated API outage, normal supervisor 1 / 4 Final replacement only: 0 / 1 / 0 / 0

All four originals retain complete prefixes, one scheduled/started/completed activity each, exact typed results and original deadlines. Read-only SQL confirms every task first-attempt, zero repairs/pending tasks and exact 600/900-second deadline offsets.

The outage records real cURL error 56 and three automatic CLI restarts, with no manual CLI restart or history repair. The same original completes inside its original 90-second observer. This qualifies supervised recovery, not same-process recovery. The final replacement's counters do not stand in for missing counters from crashed processes.

The normal expiry/eviction profiles stop with zero registrations and all roles exit0/no OOM/restart. The intentional outage leaves one retained registration, and its otherwise clean teardown triggers the wrapper's overly strong zero-registration/zero-restart predicate. That wrapper result remains failed; isolated database, containers and network are removed at 19:23:22 UTC. The checker now permits explicitly declared crash-control observations while keeping zero registrations/restarts mandatory for ordinary measured cells. Workflow deadlines, histories and driver assertions are unchanged.

FPM's current-tuple lifecycle, eager/lazy reload and SIGKILL/cold replacement are recorded separately. Final source CI/Sonar/boundaries pass, zero unresolved Sonar PR issues. The fresh current-tuple three pairs, baseline drift and mixed reload soaks are now proceeding; full #112 acceptance remains open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The three current published-tuple pairs are complete on runtime source f027697c83e753f708386d3487746c13a9a8c1e5, normal SDK 2.4.2 and pinned Server 2.8.6. Each of the six fresh 120-second windows completes 120 workflows and 720 context reads: 360 completed first-attempt tasks, exact original deadlines, zero errors, drops, repairs, retries, pending tasks, OOM or swap. All six normal teardowns have zero registrations and automatic restarts.

Pair order FPM app CPU seconds Octane app CPU seconds FPM / Octane workflow p95 ms
FPM, Octane 18.089 15.774 3312 / 2515
Octane, FPM 18.528 14.809 3146 / 1554
FPM, Octane 14.843 14.791 1583 / 1555

The third pair's application CPU differs by only 0.35%. Mean charged application memory across the three pairs is 88.82 MiB FPM versus 191.57 MiB Octane. These observations do not justify a universal speedup or durable execution gain. FPM remains the ordinary starting profile.

The fresh FPM drift check is now running. Both 300-second mixed soaks, each with two real HTTP reloads, remain required before final recommendation and delivery. No failed or older-package observation has been relabeled.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The first two-worker FPM mixed soak on f027697c is FAIL, not an accepted soak. At about 120 seconds it had offered/admitted 100 originals, observed 70 completions/18 cancellations and 457 context responses, with 282 dropped iterations and two HTTP 502s. nginx independently reports two upstream connection resets at the actual first FPM master reload. No OOM or automatic process restart occurred. Subsequent canonical readback has 78 completed/18 cancelled/four waiting originals and 16 repairs. That failed observation remains unchanged; its task stack is retired.

The two-worker short windows and drift remain their own passing workload. They do not establish capacity for synchronous query waits under mixed traffic.

Prospective runtime source e81b7c8 changes both HTTP pools to four workers inside the same HTTP 0.9 CPU/640 MiB and aggregate application 2 CPU/1 GiB limits. FPM also gets an explicit 35-second process-control grace period for in-flight requests, rather than the default zero-second escalation during reload. PHP's process-control implementation explains that setting. The new profile needs fresh qualification; no earlier result is transferred. Offered rates, virtual users, per-original deadlines, drain, independent CLI and published packages stay fixed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Four-worker source e81b7c8 passes all 35 applicable source/CI/Sonar/boundary checks (connected cancellation skips), and direct Sonar API review finds zero unresolved PR issues. Its FPM eager/lazy controls exercise all four HTTP PIDs, and both real master reloads under mixed traffic replace all four workers without HTTP errors or OOM.

The complete 300-second mixed run with a two-CPU backend API still FAILS: 233 admitted, 174 observed completions/44 cancellations, 1728 context responses, 140 dropped iterations and one original observer failure. Canonical readback has 185 completed/44 cancelled/four waiting originals and 118 repairs. Original deadlines remain exact. Actual API CPU is 514.73 seconds over 302.99 seconds, with 38.32 throttled seconds. No failed observation is being converted into acceptance.

I checked the maintenance command against exact published Server 2.8.6: its broad recovery pass is also in the published Compose stack. This does not establish a fixture-only maintenance defect or an SDK defect. No maintenance/runtime/dependency patch follows from that inspection.

The next fresh isolated run changes only the separately declared API allocation to four CPUs/1 GiB on the same host. Application limits, four-worker source, database allocation, public packages, rates, VUs, original observers and drain remain unchanged. Any final FPM/Octane comparison must use that same declared backend allocation for both profiles. The prior two-CPU results stay separately scoped. All ten roles from the failed run exited0/noOOM and were removed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The corrective package is now included in fully published and independently qualified Server 2.8.7 / 964f9e3d, immutable image index 27c08e15, loaded Workflow 2.5.32 / 78075b6f. Server #485 records its exact source, image, loaded package and ordinary upgrade acceptance. That is distinct from affected MySQL HTTP acceptance.

The next SDK #112 consumer preparation is 8df5e88fd3c6c6a5ea29e07ccc0e240d8a4ae9c3 / tree c6c7ebb0748ab7e7ef34525bc2d4cf06f514666c. It changes only the selected published backend and clarifies the report; application code, normal SDK 2.4.2 lock, four HTTP workers, workload, original deadlines and prior four-CPU API allocation are unchanged. Source checks are running; no old result is carried to this tuple.

I am preparing fresh ordinary context/lifecycle/cold/typed checks and the unchanged mixed/reload cohort. It uses a separate test host and synthetic namespace, with every role and teardown owned by this qualification. Earlier mixed runs remain failed. The issue stays open until the affected published HTTP consumer is actually qualified; no renewed hosting recommendation, capacity, speedup or broader historical-cause claim follows from the version update alone.

@rmcdaniel

rmcdaniel commented Oct 17, 2026 •

Copy link
Copy Markdown
Member Author

Published Server 2.8.7 / Workflow 2.5.32 and normal PHP SDK 2.4.2, application preparation 8df5e88f, unchanged four-worker HTTP recipe and resource limits:

  • FPM lifecycle/context and real eager/lazy reload controls pass: five lifecycle originals, four cold typed reads, 120 post-reload/context requests, nine intended refusals and disjoint four-worker PID sets. The separate 30-second warm run completes all 30 originals and 180 context requests without errors or drops.
  • Killing the independent CLI and starting a replacement completes the same original with its six-event prefix preserved in the final eight events, exact typed result/deadlines and zero repairs. The killed CLI is explicitly exit 137/no OOM and leaves one original registration. Normal roles stop successfully.
  • Fresh effective TTL 1 / capacity 100 completes one original. Actual stopped cache counters hit/miss/eviction/forced-cold are 0/3/1/1.
  • Fresh effective capacity 1 / TTL 300 completes two originals. Actual counters are 0/6/3/4.

The cache cells preserve exact histories, typed outputs and deadlines, with twelve completed first-attempt tasks, zero repairs, retried or pending tasks. Each normal ten-role stack exits 0 without OOM/restarts and deregisters fully; its containers, volumes and network are removed. Effective process and loaded Laravel settings are read before each admission.

An earlier intended TTL control used the default TTL 300 because the private experiment helper exported defaults over Compose's requested settings. Its workflow completed, but its expiry assertion failed and it remains unqualified for expiry. A second setup used the same defaults and was retired before business admission. The helper was corrected before the fresh cells above; application and vendor bytes were unchanged. These new originals do not relabel the earlier attempts.

The separate actual ten-second API outage also passes on the same original within its unchanged 90-second observation cutoff: exact history prefix, typed output/deadlines, four first-attempt tasks and zero repairs, retries or pending tasks. It records real transport errors and three automatic CLI restarts. This is supervised replacement, not same-process recovery. The intentional failures leave one stale registration; the replacement and all other roles exit 0/no OOM, and the entire stack is removed at 23:25:05 UTC. Final-replacement cache counters are 0/1/0/0.

The fresh 300-second mixed/reload gate is now preparing its fresh stack and warmup. This remains scoped functional progress, not final hosting, load, capacity or Workflow #819 affected-consumer acceptance. Earlier mixed-run failures remain failed.

@rmcdaniel

rmcdaniel commented Oct 17, 2026 •

Copy link
Copy Markdown
Member Author

The new published Server 2.8.7 / Workflow 2.5.32 mixed FPM original remains FAILED. The report at f6ac71a records 265/300 admissions, 252 observed terminal outcomes, 92 dropped offers, 82 canonical repairs and four waiting originals at its original audit. Both real reloads replace all four HTTP workers. All 51 cancellation and 51 signal requests return their expected 204; no cancellation HTTP 500 is observed. This is a scoped result for #819, not complete load acceptance.

There is also a confirmed fixture defect: late-window assertions trigger immediate k6 abort, interrupting thirteen observers before the declared drain. The correction refuses late admissions, preserves every success threshold and original 35-second observer/36-second drain, and evaluates failure after in-flight observers finish. The real synthetic HTTP counterfactual fails on the parent (only2/5 terminal observations), passes on the correction (5/5, with injected error still requiring exit99), and passes its clean case (5/5,exit0). It is a driver regression check, not workflow qualification. Normal CI now runs both cases.

New head f6ac71a3568f8ae31f4f2e77915ed830434bb982 / whole 4da09601297172540352a76544808ba4e964e0be; exact normal CI39079851381 now PASSES all 32 applicable jobs, including the real drain regression. Connected cancellation is the one explicit conditional skip. Sonar39079851422 and its app check PASS; direct API review confirms 0.0% new duplication, no OPEN/CONFIRMED issues and no ignored conditions. Application/SDK runtime and lock bytes, offered rates, VUs and allocations are unchanged. No fresh mixed rerun, Octane comparison, capacity or performance recommendation is claimed.

All phase containers, networks, volumes, database/raw data, dependencies and the isolated measurement host are removed. Next is exact source qualification and diagnosis of delayed first claims/repairs before another load submission. Earlier failed originals retain their original outcomes.

@rmcdaniel

rmcdaniel commented Oct 17, 2026 •

Copy link
Copy Markdown
Member Author

The published-SDK claim diagnostic is complete. New head 79b0a6f4179c1b1ccbe965bce52a9a373b5b3852 / whole eda6956307703f3e56f1062deb405f8a25adb32a adds a prospective two-worker Compose override and the readable result.

Normal SDK 2.4.2 / 25d796ad matches all 166 released source, resource and schema files. Two fresh eight-original batches on published Server 2.8.7 / Workflow 2.5.32 each finish seven completions and one cancellation, eight activities, thirty completed tasks and one cancelled task. Both have zero repairs, retries or final registrations, exact typed results and unchanged 35-second observation / 600-second run / 900-second execution limits. Every process exits 0 without OOM or automatic restart. This exercises the example's pure SDK definitions; Laravel HTTP and offered-load acceptance remain open.

The single worker stays busy with actual HTTP requests during the backlog; workflow poll replies while work is queued take at most 0.470 seconds. Cross-kind wake returns the existing task_queue_changed hint and the next sweep leases work. One ready resume waits 5.402 seconds; a 5.451-second query sees its own pending resume and completes after that task. This does not establish a missing wakeup or explain the earlier 82 repairs. A separate two-process batch, under the same combined 0.5 CPU / 320 MiB diagnostic allocation, has a 2.009-second maximum observed workflow-ready wait and a 2.046-second longest query. The shared host was not reserved for measurements, so these observations establish no performance gain or capacity.

The prospective override uses two independent Artisan workers at 0.5 CPU / 160 MiB each, keeping the full application at 2 CPUs / 1 GiB. Rendered FPM and Octane Compose configurations verify replicas, total CPU/memory, no-swap limits, UID 1000 and SIGTERM. This configuration still needs actual Laravel lifecycle, cache, recovery, shutdown and fresh strict load/pair acceptance. Base one-worker defaults and all failed originals remain unchanged. New-head normal CI 39080884852 is pending. Sonar 39080884894 and the exact-head app check pass; direct API review confirms all A ratings, 0.0% new duplication, 100% hotspot review, zero OPEN/CONFIRMED findings and no ignored conditions. The public boundary passes. Prior f6ac71a results remain separate.

All diagnostic apps, databases, volumes, network, dependencies, caches, raw observations and temporary credentials are removed. Next: qualify the pool with the normal Laravel application, then use a separate isolated host for fresh load with the corrected drain driver and unchanged rates/budgets/thresholds. Draft #169 and all #112 acceptance criteria stay open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The first two-process FPM mixed run remains FAILED on 79b0a6f with published Server 2.8.7 / Workflow 2.5.32 / normally installed SDK 2.4.2.

  • Original 300-second offer: 300 admitted, 239 observed completed, 59 cancelled, two waiting at the canonical read. All 300 activities completed once. There are zero repairs, retried tasks, OOMs or swapping, and the 600/900-second deadlines remain exact.
  • All 4,046 HTTP responses have their expected status. Two query assertions stop those originals before their signal/cancellation. The driver wrongly assumes run status waiting means the activity is already committed. It can also mean waiting for the activity itself. The example query explicitly distinguishes these phases.
  • The generator reports exhausting its eight context virtual users during reload: 1,797/1,800 context requests and three dropped offers. Both scheduled reloads replace all four FPM workers. No capacity or speedup claim follows.
  • All eleven stack roles exit zero with no automatic restarts or remaining registrations. The stack retires at 00:26:34 UTC; its database, raw logs and samples are deleted at 00:28:19 after the immediate audit. A separate diagnostic SQL reader used a nonexistent column and failed; it establishes no further history or lock cause.

The prospective driver waits for the explicit activity-ready query phase inside the same original 35-second cutoff, and reserves sixteen context virtual users to sustain the unchanged six context requests per second through reload latency. Workflow VUs remain sixteen. Offered workflow rate, application allocations, deadlines, drain and strict success criteria are unchanged. These are new inputs requiring new originals. The prior run stays failed.

New constructed HTTP controls reproduce both old-driver failures before testing the correction. This is qualification-driver work, with no SDK or Server runtime edit. Full pool cache/crash/outage acceptance, both mixed soaks, three alternating pairs, drift and final onboarding remain open.

Final source is 530c98bfe55998bdd6c86e652e530031a482be29 / whole tree 17aeb8ce19d15c7c09e25b68d409b32be97bfa20. Actual constructed clean, injected-error, mixed readiness and delayed-context controls all pass their required outcomes; the injected error retains exit 99. The first test wrapper incorrectly applied the injected-error expectation to its new mixed mode; that wrapper failure was corrected before final qualification. No application pass is inferred from synthetic controls.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Fresh two-process FPM cohort PASS on exact source 530c98bfe55998bdd6c86e652e530031a482be29 / tree 17aeb8ce19d15c7c09e25b68d409b32be97bfa20, normally installed SDK 2.4.2 / 25d796ad, published Server 2.8.7 / 964f9e3d / image 27c08e15, Workflow 2.5.32 / 78075b6f.

  • Original 300-second mixed offer begins 00:32:37 UTC: 300 offered/admitted/terminal, 240 completed + 60 cancelled, all 1,800 context requests, zero errors or dropped offers. All 29,070 checks pass.
  • Independent cold reader verifies 2,400 history events, all 240 typed outputs and 60 cancellations. SQL verifies 300 completed activities, 1,080 completed tasks + 60 cancelled tasks, zero repairs/retries/pending tasks, exact original 600/900-second deadlines. The 60 unclaimed tasks are cancelled, not active backlog.
  • Both 120/240-second reloads replace all four FPM workers. All 60 ordinary timers fire without advancing their authored due time. Due-to-claim spans 196,747–1,397,549 microseconds; this includes scheduling and acquisition.
  • Aggregate application allocation stays 2 CPUs / 1 GiB with two independent 0.5 CPU/160 MiB CLI workers. Application CPU is 57.149392 seconds during load/drain. Charged memory samples average 156,216,399 bytes and peak at 168,611,840; process RSS peaks at 361,029,632 bytes and can count shared pages repeatedly. Sampling has 78 complete component batches, maximum gap 4.056 seconds. No OOM or swap; observed host steal peaks at 3%.
  • Final cache counters across both processes are hit 0 / miss 792 / eviction 312 / forced cold replay 457, including prerequisites and warmup. No cache-hit or replay-saving claim follows.
  • All eleven stack roles exit zero with no automatic restarts or remaining registrations. Stack retirement is verified 00:38:36 UTC; bulk logs/samples/database are deleted after immediate audit, retaining only small derived results needed for the final report.

Exact normal CI 39082974883 passes all 32 applicable jobs, with only the conditional connected-cancellation skip. Sonar 39082974879 and its app check pass; direct API review finds all A ratings, 0.0% new duplication, 100% hotspot review, zero OPEN/CONFIRMED issues and no ignored conditions. Both boundary checks pass.

Earlier failed originals stay failed. This is one successful FPM cohort, not final #112 acceptance or a comparison. The separate two-process cache/recovery controls are running next. Octane, repeated pairs, drift and final onboarding/main qualification remain open.

@rmcdaniel

rmcdaniel commented Oct 18, 2026 •

Copy link
Copy Markdown
Member Author

The two-process FPM cache and recovery controls also pass on 530c98b with the same normally installed SDK 2.4.2 / published Server 2.8.7 / Workflow 2.5.32 tuple.

Separate control Original completion / canonical tasks Cache hit / miss / eviction / forced cold
Capacity100 / TTL1 One original / four first-attempt tasks 0 / 3 / 1 / 1
Capacity1 / TTL300 Four originals / sixteen first-attempt tasks 0 / 12 / 7 / 8
Kill both CLI workers, cold replacements Same original / five first-attempt tasks, eight exact events Killed-process counters unavailable
API held stopped for ten seconds, then restored Same original / four first-attempt tasks Recovery uses supervised replacements

All seven originals retain their whole history prefixes, exact int64/null/Unicode results and original 600/900-second deadlines. Their fixed 90-second observation cutoffs are not reset. SQL shows zero repairs, retried tasks or pending work. The four capacity originals are checkpointed before any release, exercising two independent caches.

Both deliberately killed CLI containers have exit137/noOOM, followed by successful normal replacements. The API-outage workers restart three and six times. It establishes supervised recovery, not same-process recovery. The stopped API-to-healthy interval is 00:45:14.595260–00:45:31.093651 UTC, including the ten-second stopped hold and startup. Transport errors are actually observed. Final live roles exit zero/noOOM. Two original registrations remain after each intentional interruption; the outage's two stale IDs match recorded deregistration failures. They are removed with the disposable database. Normal cache-only cells deregister fully.

All four stacks and their raw databases/logs/checkpoints are retired after immediate audit, retaining only small derived results for the final report. Octane's independently named fresh cohort is now qualifying its prerequisites and mixed soak. No earlier failed result is relabelled, and paired comparisons/drift/final delivery remain pending.

@rmcdaniel

rmcdaniel commented Oct 18, 2026 •

Copy link
Copy Markdown
Member Author

The separate Octane mixed original on 530c98b is failed: all 300 original workflows reach their expected terminal states (240 completed, 60 cancelled), every response/history/deadline check passes, and no repairs/retries/pending work occur, but the generator drops 12 of 1,800 context offers during reload. Actual request latency reaches 4.002 seconds within the existing ten-second transport limit; k6 explicitly exhausts its 16 context VUs. All eleven roles then exit 0 without OOM/restarts and registrations return to zero. Database and raw samples are deleted after readback.

Correction af741d1a0905279b65851a9d80e63c1cf96d096a / tree a106193b79a082afc9a66416959d73cb4e960bb5 reserves 64 context VUs, covering the fixed six-per-second rate through the unchanged ten-second limit. The unchanged 16-VU parent fails the new nine-second response-delay control. All four corrected driver controls pass, including the intentionally injected HTTP failure retaining exit 99. Workflow VUs remain 16; rates, original observers, drain, application limits and strict acceptance thresholds are unchanged.

Fresh FPM and Octane cohorts are required with this common driver. The earlier successful FPM 16-VU cohort retains its exact scope. No application/runtime defect, speedup or Octane hosting acceptance is inferred from these generator observations. FPM's separate pool cache/replacement/outage controls remain valid component results.

Fresh common-driver FPM result: cohort fpm-pq3 on exact af741d1 passes. All 300 offered originals are admitted and terminal (240 completed / 60 cancelled), all 1,800 contexts complete, and all 28,057 checks pass. Independent installed typed/history audit and SQL pass with 300 once-completed activities, 1,080 completed first-attempt tasks and 60 cancelled tasks, zero repairs/retries/pending work, and exact original 600/900-second deadline offsets. The sixty unclaimed tasks are cancelled terminal tasks, not waiting backlog.

Both scheduled reloads replace all four HTTP workers. The measured application uses 53.507 CPU seconds and mean/peak charged memory of 150.10/162.93 MiB; these are workload-specific costs. All eleven stack roles exit 0/no OOM/no restarts, registrations are zero, and runtime/database/raw are deleted at 01:07:23 UTC.

Fresh common-driver Octane result: independent cohort octane-oq2 on the same exact af741d1 also passes 300 admitted/terminal originals (240 completed / 60 cancelled), 1,800 contexts and all 29,546 checks. The independent audit has the same exact activity/task/deadline results, with zero repairs/retries/pending work. Both reloads replace all four workers. Application CPU is 56.177 seconds, mean/peak charged memory 333.59/375.79 MiB. All eleven roles exit 0/no OOM/no restarts, registrations are zero, and stack/database/raw are deleted at 01:14:59 UTC. Neither profile swaps. Remaining Octane pool controls and repeated paired windows/drift still gate final hosting acceptance.

Exact-source checks are complete: 36 SUCCESS and one explicitly conditional connected-cancellation SKIP. Sonar's app check and direct API pass with all A ratings, zero new duplication, 100% reviewed hotspots, no ignored gate conditions and zero OPEN/CONFIRMED findings. No profile/threshold adjustment is used.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Both two-CLI-worker profiles now pass the cache/recovery controls against published Server 2.8.7 / Workflow 2.5.32 and normally installed SDK 2.4.2. The application and dependency bytes remain unchanged from the admitted common load-driver cohort af741d1.

  • Actual TTL 1-second expiry: one original/four first-attempt tasks per profile, stopped counters hit/miss/eviction/forced-cold 0/3/1/1 on each.
  • Actual capacity-one eviction: four originals checkpointed before any release, sixteen first-attempt tasks per profile. FPM counters 0/12/7/8; Octane 0/12/9/8.
  • Killing both CLI workers: deliberate exit137/noOOM, cold replacements complete the same original with the entire prefix, exact typed result/deadlines, eight events, one activity and five first-attempt tasks. The two killed registrations remain explicit.
  • Ten-second API outage: actual transport errors, same original/four first-attempt tasks, preserved prefix/result/deadlines. FPM worker restarts are 3/6; Octane 3/3. This is supervised replacement, not same-process recovery. Only the exact original failed-deregistration IDs remain.

All controls have zero repairs/retries/pending work and individually checked final shutdown. Both profiles' eager/lazy request-context and actual reload controls pass. Every completed stack/database/raw set is deleted after readback. The first Octane expiry helper incorrectly required forced cold replay even after TTL expiry with a complete-history path; its original stays failed, actual 0/3/1/0 remains recorded, and a separate fresh original qualifies the corrected helper matching the existing documented contract. Capacity still requires forced cold replay.

The fixed three-pair comparison plus FPM drift is now running with fresh projects/databases and the unchanged rates, 35-second original observers, 36-second drain, 600/900-second deadlines and 2 CPU/1 GiB application allocation. First FPM short window passes all 120 workflows/720 contexts/360 tasks with zero errors/drops/repairs/retries/pending work; its stack/raw is retired. Final source/report/served-onboarding acceptance remains open. No SDK release or workflow execution speedup is claimed.

@rmcdaniel rmcdaniel changed the title Prepare optional Laravel service HTTP profile Add qualified Laravel FPM and optional Octane recipe Oct 18, 2026
@rmcdaniel

Copy link
Copy Markdown
Member Author

The published-SDK hosting qualification is complete for the exact documented two-CLI-worker configuration. Final report/source is 516d13a22a35adedd71c67095cf7f33176e9ef44 / whole 70dea88627833b67c7c6978f9880c8cd111333a7; measured application and driver bytes stay unchanged from the admitted cohorts.

Three alternating FPM/Octane pairs and a fresh FPM drift cell all pass: seven cells, 120 original completions / 720 contexts / 360 first-attempt tasks each, exact typed output/history/deadlines, zero errors/drops/repairs/retries/pending work. Both previously reported common-driver mixed soaks and all final pool isolation/cache/recovery/reload controls also pass.

Three-cell mean application CPU is 16.75 s FPM / 16.58 s Octane. That 1.05% difference changes direction by pair and is smaller than the final FPM baseline's 5.34% drift. Charged memory is 138.60 / 323.38 MiB. Octane improves small context reads, but these results establish no consistent CPU or workflow execution gain. Both profiles record zero history-cache hits. The report therefore recommends starting with FPM and retains Octane as an explicitly qualified optional hosting choice.

The report includes separate p50/p95/p99, all component CPU/throttling/RSS/charged memory, response-body transport counts, timer latency and actual cache counters. Backend body bytes include metadata and inline history, so they are not represented as pure history bytes. All failed originals stay failed and linked.

Every final cell shuts down with all eleven roles exit 0/no OOM/no restart and zero registrations. Actual experiment VM and task SSH key deletion is verified at 01:51:48 UTC, after all cell databases/raw samples were removed. Final exact-source CI/Sonar and fresh-main/served-onboarding acceptance remain pending. No package release, production-capacity or execution-speedup claim is made.

@rmcdaniel
rmcdaniel marked this pull request as ready for review October 18, 2026 01:53
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel
rmcdaniel merged commit d3f9dd7 into main Oct 18, 2026
37 checks passed
@rmcdaniel
rmcdaniel deleted the docs/laravel-service-octane branch October 18, 2026 01:56
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