Identify the hostname per request, so the package works on Octane - #1056
Open
dazza-dev wants to merge 1 commit into
Open
Identify the hostname per request, so the package works on Octane#1056dazza-dev wants to merge 1 commit into
dazza-dev wants to merge 1 commit into
Conversation
On a long lived process the second request an application serves is given the tenant identified for the first one, and its database with it. PHP-FPM never sees it, because every request builds an application. identifyHostname() does not identify: it registers a lazy singleton and returns. What identifies is the environment's constructor, resolving that binding right after, and the constructor runs while the providers boot -- once per application, not once per request. Once resolved, the binding stays resolved and no later request re-identifies. The environment is also built twice per application, because the singleton is registered from a booted callback while the deferred HostnameProvider resolves it from boot, which runs earlier. So: - TenancyProvider registers the singleton in register() - the constructor only arranges for identification, since identification reads the request and boot may run without one - identifyHostname() registers and resolves, doing what it says - EagerIdentification asks for it per request, honouring both auto-identification and early-identification Kernel::sendRequestThroughRouter() binds the request before it bootstraps, so a request identifies with its own Host. tests/Test.php rebuilds the environment after migrateSystem(). The harness creates its schema after the application boots, which no real application does, so the environment concluded tenancy was not installed and cached that. 128 tests green, the same count and assertions as 5.x without this. Closes tenancy#1015
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the question in #1015, which has been open since 2021: the package does not work on Octane, and this is why.
The symptom
On a long lived process — Octane, Swoole, RoadRunner — the second request an application serves is given the tenant identified for the first one, and its database with it.
Measured by driving the lifecycle a real request goes through (bind the request, bootstrap, boot providers, run the middleware), one application, two requests, different hosts:
Measured on this branch against
5.xitself, on thelaravel/laravelskeleton the suite already uses.PHP-FPM never sees it, because every request builds an application. That is why the suite has never caught it either: it boots one application per test.
Why it happens
Environment::identifyHostname()does not identify anything. It registers a lazy singleton and returns:What actually identifies is the constructor, through
$app->make(CurrentHostname::class)right after. And the constructor runs while the providers boot — once per application, not once per request. After that the binding is resolved and stays resolved, so no later request re-identifies.There is a second, quieter effect.
TenancyProvider::register()registers theEnvironmentsingleton inside abootedcallback, while the deferredHostnameProvider::boot()resolves it earlier. The first resolution finds no singleton and builds an instance through reflection that is then discarded; a second is built once the singleton exists. Two constructions per application, measured with a backtrace in the constructor.The change
TenancyProviderregisters the singleton inregister(), so one environment is built.identifyHostname()registers and resolves, so it does what its name says.EagerIdentificationasks for it on every request, honouring bothauto-identificationandearly-identification.Kernel::sendRequestThroughRouter()binds the request before it bootstraps, so a request still identifies with its ownHost.No change for applications on PHP-FPM. Every request builds an application there, so identification happened per request already.
The one test change
tests/Test.phprebuilds the environment aftermigrateSystem(). The harness creates its schema after the application boots, which no real application does, so the environment concluded tenancy was not installed and cached that. Leaving the rebuild to whoever resolved it first made the request under test look unlike a real one.Notes
I also chased whether this doubled
Websites\Switchedper request, since two constructions each identify. It does not: the constructions happen back to back and the second identification nests inside the first, so oneIdentifiedand oneSwitchedare emitted either way. Measured, not assumed.This is also the answer to the
make(CurrentHostname::class)that #1050 and #1051 add insideidentifyHostname()without being able to say why — I left the detail on both PRs.For context: I maintain a fork of 5.9 at dazza-dev/hyn-multi-tenant, where this is released as v0.10.4, and have been sending back what applies here — #1054 for the isolation and provisioning fixes, #1055 for the test harness.