Skip to content

feat(server): reload worker configuration safely - #511

Merged
binaryfire merged 38 commits into
0.4from
feature/server-reloader
Aug 14, 2026
Merged

feat(server): reload worker configuration safely#511
binaryfire merged 38 commits into
0.4from
feature/server-reloader

Conversation

@binaryfire

Copy link
Copy Markdown
Collaborator

Summary

This makes server reloads rebuild worker-owned configuration state before replacement workers become ready.

The server still preserves master-owned topology such as ports, worker counts, callbacks, routes, process definitions, and Swoole table definitions. Those changes continue to require a full restart. Config-backed services inside event and task workers now refresh from the current environment without changing request hot paths.

Motivation

Hypervel registers and boots the application before Swoole forks its workers. A reload replaces event and task workers, but those workers inherit the master's container, providers, resolved managers, callbacks, and cached objects.

Reloading dotenv and the config repository alone was not enough. Services that copied config into properties or resolved drivers before the fork could keep stale state after server:reload. This produced a reload command that replaced workers without consistently applying the new worker configuration.

Changes

  • Add an injectable ServerReloader service and make server:reload a thin console adapter over it.
  • Add the ReloadsConfiguration provider contract and invoke registered hooks after dotenv, configuration, and tracked config mutations have been rebuilt.
  • Refresh config-derived state across core and optional providers while preserving retained object identities, application extensions, callbacks, routes, and custom manager creators.
  • Add Laravel-style reset and mutation methods for manager caches and retained services that need worker-start refresh.
  • Replay derived Fortify, Horizon, and Sentry configuration as operations so replacement workers use their current environment.
  • Refresh retained routing, session, cookie, translation, view, Telescope, Sentry, JWT, Reverb, Saloon, and other package state at the correct owning boundary.
  • Clear inherited HTTP connection handlers and object pools before the server forks workers, while preserving registered definitions and presets.
  • Seal Cache Swoole tables after master initialization so a table added only after fork fails explicitly instead of becoming process-private state.
  • Keep background and deferred queue exception reporting on their connectors so connections remain lazy and rebuild correctly.
  • Preserve Queue, Mail, and Notification fakes during worker refresh while resetting the real managers wrapped by Queue and Mail fakes.
  • Fix container alias eviction so forgetting an alias clears the canonical cached instance.
  • Separate the CLI dumper's source output target from Symfony's native output property.

Lifecycle boundaries

The refresh runs once during replacement worker startup and adds no work to request handling.

ServerReloader signals event workers and configured task workers. Queue workers, Horizon, the scheduler, and custom server processes retain their own lifecycle controls and must be restarted separately.

Changes to ports, Swoole settings, worker counts, callback registration, routes, middleware, listeners, package topology, custom process definitions, preloaded PHP code, and Swoole table definitions still require a full server restart.

Documentation

The provider and deployment guides now explain master bootstrap, worker configuration refresh, per-worker startup events, programmatic reloads, restart-only topology, and failure recovery. The HTTP and Queue guides document connection refresh behavior and in-process exception reporting.

Verification

  • Ran the repository's full formatter, PHPStan, parallel test suite, and Testbench checks.
  • Ran focused provider, lifecycle, manager, fake, translation, view, queue, and optional-package tests while implementing each slice.
  • Started the Reverb integration server through its production bootstrap path and verified worker startup after Queue::fake().
  • Ran targeted PHPStan and facade documenter checks over the final follow-up changes.
  • Traced provider ordering, retained-object relationships, lazy resolution, fake bindings, and request-path cost during final review.

Define a provider-owned configuration refresh lifecycle for replacement event and task workers. The plan preserves retained service identities, resets lazily rebuilt manager state, and keeps per-worker startup work after configuration is current.

Specify the injectable ServerReloader API, Laravel-style reset and mutation methods, derived configuration replay, restart-owned topology, documentation updates, and complete verification strategy.

Also capture the Swoole cache-table sealing correction so newly configured tables fail explicitly after fork instead of creating private state in each worker.
Add a small provider lifecycle contract for refreshing configuration-derived worker state after dotenv and configuration are rebuilt.

Keep refresh ownership with each provider and require synchronous completion so invalid replacement-worker configuration fails before the worker becomes ready.
Canonicalize container aliases before clearing singleton, scoped, and auto-singleton caches. Forgetting a service through any public alias now removes the same cached object that normal resolution returns.

Add regression coverage proving the next resolution builds a fresh canonical instance.
Introduce an injectable ServerReloader that validates the configured PID and signals event workers plus configured task workers with explicit failures.

Reduce server:reload to a console adapter over the service. This gives applications one strict reload implementation without adding a facade, alias, retry loop, or readiness protocol.
Add a fluent forgetInstances operation to the shared named-instance manager while preserving registered creators and other worker configuration.

Use it from Concurrency and Rate Limiter configuration hooks, with coverage for cache replacement, creator preservation, and facade metadata.
Clear resolved guards and password brokers after worker configuration changes while retaining their manager construction rules and registered callbacks.

Keep auth cache validation in the later worker-start phase, after cache providers have finished refreshing, and cover both provider hooks and broker reset behavior.
Clear resolved broadcast connections and the cached default broadcaster when replacement workers load new configuration.

Preserve the manager and its registered drivers while proving unresolved services are not constructed during refresh.
Forget both the batch repository contract and its separately cached database implementation so refreshed batching configuration cannot leave either container path stale.

Cover both cache keys and avoid replacing the dispatcher or other bus registrations.
Add a fluent cache-driver reset that preserves custom creators and the shared serialization policy, then use it when worker configuration changes.

Seal Swoole cache tables after pre-fork initialization. Existing tables remain shared across reloads, while a newly configured table fails clearly instead of being created privately inside one worker.
Forget the database resolver contract and its auto-singletoned concrete after replacement workers load new configuration.

Keep connection cleanup in its existing pre-refresh lifecycle and verify the next resolver observes the refreshed default connection.
Clear cached hash drivers and rebuild the encrypter after worker configuration changes. Reset the Serializable Closure secret alongside the encrypter so both consumers use the same refreshed key.

Add coverage for refreshed algorithms, keys, and lazily rebuilt service instances without adding request-path work.
Add a fluent disk reset that clears resolved adapters while preserving custom filesystem creators.

Refresh both named disks and the cached default disk in replacement workers, with coverage for lazy reconstruction and facade metadata.
Record Fortify, Horizon, and provider-owned derived configuration as operations that are evaluated again after replacement workers rebuild their environment.

Keep provider lists and gRPC server definitions as explicit master snapshots because those values describe topology already installed before workers are forked.
Add a fluent channel reset that preserves custom creators and shared logging context.

Make the Log provider own worker configuration refresh, clearing application channels and updating the framework stdout logger in place so infrastructure logs use the latest format and levels.
Clear cached mailers, Markdown rendering state, notification channels, and the retained mail channel after worker configuration reload.

Preserve manager registrations and rebuild only services that copied configuration, with unresolved-service coverage for both providers.
Flush an already resolved PoolManager immediately before Swoole forks workers so no master-created pool resources leak into child processes.

Keep recycler startup on AfterWorkerStart and avoid a second worker-start flush that could close pools created by earlier listeners.
Add a fluent connection reset and make the Queue provider restore the configured background and deferred exception callbacks on refreshed eager connections.

Preserve connectors and manager callbacks, clear the default connection and failed-job provider, and keep eager work limited to connections the provider already initialized.
Update the shared CookieJar in place, clear resolved session drivers, and rebuild the default session store after worker configuration changes.

When the Redirector already exists, point that retained object at the refreshed store so ResponseFactory and middleware references remain valid.
Update the URL generator's fallback request, asset root, and HTTPS policy from replacement-worker configuration without replacing the object retained by routing and response services.

Cover both HTTPS directions, identity preservation, current application URLs, and the new boot-only asset-root mutator.
Clear config-derived Inertia view finding, Permission cache setup, Scout engines and clients, and Socialite drivers only when their owning managers were already in use.

Preserve custom creators and request-scoped state, and avoid constructing optional services merely to refresh them.
Clear resolved Reverb application drivers and the webhook batch buffer when replacement workers load new configuration.

Keep the WebSocket listener definition as an explicit pre-fork snapshot because ports, callbacks, and server topology require a full restart.
Make JWT claim and manager configuration reloadable, clear stale parsers and blacklist instances, and rebuild enabled blacklist validation from current worker settings.

Preserve custom JWT creators while covering refresh order, issuer and subject settings, blacklist enablement, validation resets, and facade metadata.
Recompute derived Sentry log-channel defaults from replacement-worker configuration and extract one client construction path for bootstrap and reload.

Bind a fresh client onto the retained framework Hub, preserve the global SDK Hub and Telescope's dump handler wrapper, and clear backtrace configuration that depends on the old client.
Add boot-time setters for database connection and chunk size, then update each repository contract view that Telescope and its watchers retain.

Avoid constructing a separate concrete repository during refresh and keep every retained contract pointing at the same updated storage object.
Separate the worker base locale from coroutine-local overrides and validate base, request, and fallback locales through one path.

Record addLines operations in call order, replay them after fresh loader results, and clear only loaded groups during worker refresh. This preserves package registrations without suppressing new language files or changing parent-child overwrite semantics.
Update the existing View Factory's finder paths and Blade compiler settings in place so engines, directives, namespaces, components, and decorators keep their identities.

Clear only stale lookup and compile-check caches, document boot-only finder mutations, and cover refreshed behavior through retained factory, finder, compiler, resolver, and engine objects.
Rebuild dotenv and configuration, replay tracked mutations, and invoke registered configuration-reload providers in their existing order before replacement workers become ready. Hooks remain synchronous and fail fast.

Move stdout refresh into its provider, update Foundation-owned timezone, maintenance, and dump-source state, and retain installed dumper and Telescope wrapper identities. Separate editor-link behavior from dump-source state so exception frames no longer inherit unrelated worker-global APIs.

Add lifecycle ordering, retained-logger composition, invalid-config, dumper-format, maintenance, static-cleanup, and provider filtering coverage. Declare Foundation's direct POSIX signal dependency.
Document programmatic and command-driven reloads, provider-owned ReloadsConfiguration hooks, and the difference between worker configuration refresh and pre-fork server topology.

Explain restart-only changes, Swoole table behavior, invalid replacement-worker recovery, and separate process lifecycles in Laravel-style prose. Remove the completed framework reload item from the todo list.
Capture the final ServerReloader and provider-owned worker configuration refresh design, including verified lifecycle facts, object identity rules, provider behavior, restart boundaries, and test requirements.

Record the anti-overengineering constraints and completed corrections so future maintenance can preserve the intended worker-start boundary without rebuilding speculative orchestration machinery.
Symfony documents its inherited constructor output as a dump destination that may be a callback, resource, string, or null. Promoting Hypervel's same-named OutputInterface property lets PHPStan attach that inherited PHPDoc to the property and reject the native type.

Declare the console output property separately and assign it after the parent constructor. This preserves the constructor signature, named arguments, protected property contract, and runtime behavior while keeping Symfony's dump destination distinct from Hypervel's console output.

Document why the property must remain separate so a future promotion does not restore the analyzer conflict.
# Conflicts:
#	src/database/src/DatabaseServiceProvider.php
#	src/support/src/Facades/Blade.php
#	src/support/src/Facades/Cache.php
#	src/support/src/Facades/Concurrency.php
#	src/support/src/Facades/Lang.php
#	src/support/src/Facades/Password.php
#	src/support/src/Facades/Queue.php
#	src/support/src/Facades/RateLimiter.php
#	src/support/src/Facades/Storage.php
#	src/support/src/Facades/URL.php
#	tests/Database/DatabaseServiceProviderTest.php
Registered HTTP connections may be resolved during master bootstrap, leaving live transport handlers in the process image inherited by workers. Clear only those handlers before the server fork so presets and factory configuration survive while each process builds its own transport resources.

Re-register Saloon's configured connection after worker configuration reloads. The shared boot path validates before mutation, refreshes same-name handlers, supports changed names without deleting application presets, and keeps console registration on initial boot.

Document the provider-owned reload path, regenerate facade metadata, and cover resolved guards, multi-connection handler cleanup, refreshed options, changed names, and failure atomicity.
Describe Horizon and custom server-process restarts directly instead of referring to lifecycle controls. This keeps the deployment guidance clear for application developers without changing the documented reload boundary.
Move background and deferred exception reporting into their connectors so queue connections configure themselves when they are constructed. This removes eager connection creation during manager registration and avoids assuming that the canonical queue binding still contains QueueManager after Queue::fake().

Make QueueFake and MailFake forward worker-refresh resets to the real managers they wrap while keeping their recorded fake state. Let NotificationFake satisfy the reset API used through the canonical notification manager key without adding fake-aware provider branches.

Add regressions for lazy connection construction, fake identity and recorded state, and wrapped manager rebuilding. Document exception reporting for in-process queue connections and record the canonical-key ownership rules in the implementation plan.
@binaryfire
binaryfire merged commit 5e6a1c4 into 0.4 Aug 14, 2026
37 of 38 checks passed
@coderabbitai

coderabbitai Bot commented Aug 14, 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 68188717-1d59-46ec-8d9c-5eb07b56e739

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

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.

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