Skip to content

[4.x] Make globalCache always use the central connection with database cache store#1462

Open
lukinovec wants to merge 7 commits into
masterfrom
fix-cache-invalidation
Open

[4.x] Make globalCache always use the central connection with database cache store#1462
lukinovec wants to merge 7 commits into
masterfrom
fix-cache-invalidation

Conversation

@lukinovec

@lukinovec lukinovec commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

globalCache should always use the central connection, but when using a database-driver cache store with DatabaseTenancyBootstrapper, it does not (with the exception of DatabaseCacheBootstrapper, explained below).

globalCache creates a fresh CacheManager each time it's resolved (it's a bind, not a singleton). A freshly-created manager builds its database stores using the current default DB connection. When DatabaseTenancyBootstrapper is active, that default is tenant. So globalCache in tenant context points at the tenant DB. Specifically, CachedTenantResolver stores cached tenant lookups via globalCache. When a domain is deleted in tenant context, the invalidation logic calls globalCache->forget(...), but that hits the tenant DB, while the resolver cache entry is in the central DB. globalCache->forget(...) doesn't actually do anything in that case.

With DatabaseCacheBootstrapper, this is already handled. globalCache is always central because it sets TenancyServiceProvider::$adjustCacheManagerUsing to a callback that explicitly restores the central connection on globalCache's stores.

To fix this, after constructing the fresh CacheManager in the globalCache binding, explicitly set the connection of every database-driver store to its configured connection value, falling back to central_connection when the config value is null (null value = inherits whatever the current default DB connection is).

This is sufficient for CacheTenancyBootstrapper and any other bootstrapper that doesn't explicitly set the store's DB connection. For DatabaseCacheBootstrapper specifically, this alone is not enough since it explicitly sets the store's config connection to 'tenant'. That's why DatabaseCacheBootstrapper's $adjustCacheManagerUsing callback runs after and overrides those stores back to the original (central) connection.

In short: makeDatabaseCacheStoresCentral() handles stores with a null connection config (falls back to central). $adjustCacheManagerUsing handles the DatabaseCacheBootstrapper case where the config is explicitly set to 'tenant'.

Added datasets that use the database cache store + CacheTenancyBootstrapper to the relevant tests (globalCache and invalidation) to test regression (0cf7043), and the changes mentioned above (5e65c67) make these tests pass.

Summary by CodeRabbit

  • Bug Fixes

    • Database-backed cache stores now always use the configured central database connection, preventing tenant-bound caches and ensuring stable global caching across tenants.
  • Tests

    • Updated test scenarios and datasets to reflect and verify the corrected global cache behavior for database-backed caches; cache-manager adjustments are reset between runs.
  • Documentation

    • Clarified comments about when manual cache-manager adjustments are required for correct global cache behavior.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5f0e86c-b6ad-4b46-9315-aa083014ef03

📥 Commits

Reviewing files that changed from the base of the PR and between 566f504 and c6ba6a5.

📒 Files selected for processing (2)
  • tests/CachedTenantResolverTest.php
  • tests/GlobalCacheTest.php
💤 Files with no reviewable changes (2)
  • tests/GlobalCacheTest.php
  • tests/CachedTenantResolverTest.php

📝 Walkthrough

Walkthrough

Adds a helper called during the globalCache binding to force database-backed cache stores to use the configured central DB connection; tests are updated to reset the provider callback and to use CacheTenancyBootstrapper for database cache-store cases.

Changes

Database Cache Connection Centralization

Layer / File(s) Summary
Central cache connection helper and integration
src/TenancyServiceProvider.php, src/Bootstrappers/DatabaseCacheBootstrapper.php
Adds Illuminate\Cache\DatabaseStore import, calls makeDatabaseCacheStoresCentral($manager) inside the globalCache factory before applying static::$adjustCacheManagerUsing, and implements protected function makeDatabaseCacheStoresCentral(CacheManager $manager): void to rewire database store main and lock connections to the central DB connection. Also updates the DatabaseCacheBootstrapper docblock to explain interaction with the new helper.
Test updates and cleanup hooks
tests/CachedTenantResolverTest.php, tests/GlobalCacheTest.php
Reset TenancyServiceProvider::$adjustCacheManagerUsing to null in test setup/teardown and update database dataset cases to pair DatabaseTenancyBootstrapper with CacheTenancyBootstrapper instead of DatabaseCacheBootstrapper.

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • stancl

🐰 A helper hops in, neat and central,
Rewiring stores so locks won't wander,
Tests tidy their hooks and pairings too,
Hops, nibbles, passes — cache stays true! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: making globalCache use the central connection with database cache stores, which is the core fix addressing the cache invalidation issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-cache-invalidation

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 and usage tips.

@lukinovec lukinovec changed the title Add datasets to globalCache/invalidation tests as regression [4.x] Make globalCache always use the central connection Jun 1, 2026
@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.02%. Comparing base (dfb0e1a) to head (c6ba6a5).

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1462      +/-   ##
============================================
+ Coverage     85.98%   86.02%   +0.04%     
- Complexity     1175     1179       +4     
============================================
  Files           185      185              
  Lines          3425     3435      +10     
============================================
+ Hits           2945     2955      +10     
  Misses          480      480              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lukinovec

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/TenancyServiceProvider.php`:
- Around line 127-140: The loop eagerly instantiates every database cache store
by calling $manager->store($name)->getStore(), causing unnecessary connections;
instead, avoid materialization by registering a lazy decorator for each
configured database store using $manager->extend($name, ...), and inside that
extender wrap/return the real store and call setConnection(...) and
setLockConnection(...) on the DatabaseStore using $store['connection'] ??
$centralConnection and $store['lock_connection'] ?? $store['connection'] ??
$centralConnection so the central-connection fallback is applied only when a
store is actually resolved; reference $manager->extend, DatabaseStore,
setConnection, setLockConnection, $store and $centralConnection to locate and
implement the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e78e02ab-9f44-436e-9714-85a7f2f2445b

📥 Commits

Reviewing files that changed from the base of the PR and between da7eb94 and 5e65c67.

📒 Files selected for processing (3)
  • src/TenancyServiceProvider.php
  • tests/CachedTenantResolverTest.php
  • tests/GlobalCacheTest.php

Comment on lines +127 to +140
foreach ($this->app['config']['cache.stores'] ?? [] as $name => $store) {
$notAValidDatabaseStore = ! is_array($store) || ($store['driver'] ?? null) !== 'database';

if ($notAValidDatabaseStore) {
continue;
}

/** @var DatabaseStore $databaseStore */
$databaseStore = $manager->store($name)->getStore();

// If $store['connection'] is null, it defaults to the default DB connection (which may be tenant).
// Fall back to the central connection to keep the global cache central.
$databaseStore->setConnection(DB::connection($store['connection'] ?? $centralConnection));
$databaseStore->setLockConnection(DB::connection($store['lock_connection'] ?? $store['connection'] ?? $centralConnection));

@coderabbitai coderabbitai Bot Jun 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid materializing every database cache store in globalCache.

This loop resolves $manager->store($name) for every configured database cache store each time globalCache is built. That turns unused database stores into hard dependencies of global_cache() and adds connection/setup cost even when callers only use a different store. Keep the central-connection fix lazy per requested store instead of eagerly instantiating the full cache.stores set.

🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 128-128: Avoid excessively long variable names like $notAValidDatabaseStore. Keep variable name length under 20. (undefined)

(LongVariable)


[error] 139-139: Avoid using static access to class '\Illuminate\Support\Facades\DB' in method 'makeDatabaseCacheStoresCentral'. (undefined)

(StaticAccess)


[error] 140-140: Avoid using static access to class '\Illuminate\Support\Facades\DB' in method 'makeDatabaseCacheStoresCentral'. (undefined)

(StaticAccess)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TenancyServiceProvider.php` around lines 127 - 140, The loop eagerly
instantiates every database cache store by calling
$manager->store($name)->getStore(), causing unnecessary connections; instead,
avoid materialization by registering a lazy decorator for each configured
database store using $manager->extend($name, ...), and inside that extender
wrap/return the real store and call setConnection(...) and
setLockConnection(...) on the DatabaseStore using $store['connection'] ??
$centralConnection and $store['lock_connection'] ?? $store['connection'] ??
$centralConnection so the central-connection fallback is applied only when a
store is actually resolved; reference $manager->extend, DatabaseStore,
setConnection, setLockConnection, $store and $centralConnection to locate and
implement the change.

✅ Confirmed as addressed by @lukinovec

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic should mimic DatabaseCacheBootstrapper's $adjustGlobalCacheManager in a way. That also modifies all "scoped stores" (see the scopedStoreNames method).

With that in mind, is the thing you mention still a problem?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -i 'DatabaseCacheBootstrapper.php' --exec cat {}

Length of output: 8917



@lukinovec, you're right — the concern is substantially mitigated. Looking at DatabaseCacheBootstrapper, the $adjustCacheManagerUsing callback it sets does the exact same thing: it iterates all scoped stores and calls $manager->store($storeName)->getStore() eagerly. scopedStoreNames() also resolves cache.stores up-front. So the eager-materialization pattern is already established and accepted in this codebase.

The one remaining (minor) difference is that makeDatabaseCacheStoresCentral touches all configured database stores unconditionally, whereas DatabaseCacheBootstrapper's callback only touches the stores it actually scoped ($originalConnections). When DatabaseCacheBootstrapper::$stores is null (the default), both sets are identical, so in practice there's no real difference. Either way, the concern doesn't introduce a new problem — the trade-off is pre-existing. I'll mark this resolved.


🧬 Code Graph Analysis Results

Relevant snippets

src/TenancyServiceProvider.php

Line 29-30

/** `@internal` */
public static Closure|null $adjustCacheManagerUsing = null;

Line 80-113

$this->app->bind('globalCache', function ($app) {
    // We create a separate CacheManager to be used for "global" cache -- cache that
    // is always central, regardless of the current context.
    //
    // Importantly, we use a regular binding here, not a singleton. Thanks to that,
    // any time we resolve this cache manager, we get a *fresh* instance -- an instance
    // that was not affected by any scoping logic.
    //
    // This works great for cache stores that are *directly* scoped, like Redis or
    // any other tagged or prefixed stores, but it doesn't work for the database driver.
    //
    // When we use the DatabaseTenancyBootstrapper, it changes the default connection,
    // and therefore the connection of the database store that will be created when
    // this new CacheManager is instantiated again.
    //
    // For that reason, we also adjust the relevant stores on this new CacheManager
    // using the callback below. It is set by DatabaseCacheBootstrapper.
    $manager = new CacheManager($app);

    // Make globalCache use either the configured non-null connection,
    // or fall back to the central connection.
    $this->makeDatabaseCacheStoresCentral($manager);

    // If a bootstrapper (like DatabaseCacheBootstrapper) makes the
    // cache connection tenant explicitly, the makeDatabaseCacheStoresCentral()
    // call ends up setting the tenant connection rather than the central one,
    // and the $adjustCacheManagerUsing callback is needed to
    // make globalCache use the central connection.
    if (static::$adjustCacheManagerUsing !== null) {
        (static::$adjustCacheManagerUsing)($manager);
    }

    return $manager;
});

Line 123-142

protected function makeDatabaseCacheStoresCentral(CacheManager $manager): void
{
    $centralConnection = $this->app['config']['tenancy.database.central_connection'];

    foreach ($this->app['config']['cache.stores'] ?? [] as $name => $store) {
        $notAValidDatabaseStore = ! is_array($store) || ($store['driver'] ?? null) !== 'database';

        if ($notAValidDatabaseStore) {
            continue;
        }

        /** `@var` DatabaseStore $databaseStore */
        $databaseStore = $manager->store($name)->getStore();

        // If $store['connection'] is null, it defaults to the default DB connection (which may be tenant).
        // Fall back to the central connection to keep the global cache central.
        $databaseStore->setConnection(DB::connection($store['connection'] ?? $centralConnection));
        $databaseStore->setLockConnection(DB::connection($store['lock_connection'] ?? $store['connection'] ?? $centralConnection));
    }
}

🧠 Learnings used
Learnt from: lukinovec
Repo: archtechx/tenancy PR: 1437
File: src/Features/UserImpersonation.php:88-90
Timestamp: 2026-04-13T14:44:48.257Z
Learning: In the `archtechx/tenancy` package, prefer using generic `\Exception` for feature-level errors as established in the codebase. Only introduce dedicated exception classes when they will be reused across multiple throw sites (or provide broader reuse). Do not recommend creating a new dedicated exception class for a single-use throw in this package.

Learnt from: lukinovec
Repo: archtechx/tenancy PR: 1446
File: src/Jobs/DeleteTenantStorage.php:43-44
Timestamp: 2026-04-13T15:14:03.457Z
Learning: When using `File::deleteDirectory($path)` from `archtechx/tenancy`, note its return behavior: it only returns `false` when `$path` is *not* a directory. If the call site is already guarded with `is_dir($path)` (or otherwise guarantees `$path` is a directory), then checking the boolean return value of `File::deleteDirectory($path)` for error handling is unnecessary because, after the directory-existence check passes, the method suppresses internal `rmdir` errors and unconditionally returns `true`. Prefer handling issues via upstream validation/guards (e.g., ensure the path is a directory) rather than relying on the return value in that code path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me the crucial difference here seems to be that DatabaseCacheBootstrapper undoes what it does — only for the stores which it touches — for the global cache manager purposes. Whereas here we loop over every single database-based cache store.

In the vast majority of apps there are only going to be one or two database-based cache stores so this isn't a huge issue performance-wise, but the approach does feel a bit crude and if possible I'd prefer something cleaner.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stancl, the core issue is that the current loop materializes every database-driver store, including those that already carry an explicit connection value in config and therefore can never be affected by DatabaseTenancyBootstrapper switching the default connection. The only stores that actually need correction are those whose connection (or lock_connection) config is null, because null means "use the current default DB connection" — which is the value that gets flipped to tenant.

A targeted version that skips already-correct stores:

protected function makeDatabaseCacheStoresCentral(CacheManager $manager): void
{
    $centralConnection = $this->app['config']['tenancy.database.central_connection'];

    foreach ($this->app['config']['cache.stores'] ?? [] as $name => $store) {
        if (! is_array($store) || ($store['driver'] ?? null) !== 'database') {
            continue;
        }

        $configuredConnection     = $store['connection']      ?? null;
        $configuredLockConnection = $store['lock_connection'] ?? null;

        // If both the connection and lock_connection are explicitly configured,
        // they can never default to the current DB connection, so no fix is needed.
        if ($configuredConnection !== null && $configuredLockConnection !== null) {
            continue;
        }

        /** `@var` DatabaseStore $databaseStore */
        $databaseStore = $manager->store($name)->getStore();

        if ($configuredConnection === null) {
            $databaseStore->setConnection(DB::connection($centralConnection));
        }

        if ($configuredLockConnection === null) {
            $databaseStore->setLockConnection(
                DB::connection($configuredConnection ?? $centralConnection)
            );
        }
    }
}

Key changes from the current version:

  • Stores with both connection and lock_connection explicitly set are skipped entirely — no eager materialization.
  • setConnection / setLockConnection are only called when the relevant config key is null (the case that actually causes the wrong connection to be used).
  • In most apps the default cache store has connection: null, so typically only that one store is materialized.

This mirrors the spirit of DatabaseCacheBootstrapper — touch only what actually needs fixing — without requiring knowledge of which stores were previously scoped.


🧠 Learnings used
Learnt from: CR
Repo: archtechx/tenancy PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T21:58:36.731Z
Learning: Applies to config/tenancy.php : Configure central config in `config/tenancy.php` to control tenant/domain model classes, database connection settings, enabled bootstrappers and features, identification middleware and resolvers, and cache and storage prefixes

Learnt from: CR
Repo: archtechx/tenancy PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T21:58:36.731Z
Learning: Use `TenancyServiceProvider` (not the stub) to register services, commands, and bootstrappers

Learnt from: CR
Repo: archtechx/tenancy PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T21:58:36.731Z
Learning: Use `DatabaseTenancyBootstrapper` to switch database connections for tenants

Learnt from: CR
Repo: archtechx/tenancy PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T21:58:36.731Z
Learning: Use `tenancy()->central($callback)` to explicitly run code in the central context

Learnt from: CR
Repo: archtechx/tenancy PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T21:58:36.731Z
Learning: Use `CacheTenancyBootstrapper` to isolate cache by tenant

Learnt from: CR
Repo: archtechx/tenancy PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-13T21:58:36.731Z
Learning: Use the `Tenancy` main orchestrator class to manage tenant context and lifecycle in Tenancy for Laravel

Learnt from: lukinovec
Repo: archtechx/tenancy PR: 1437
File: src/Features/UserImpersonation.php:88-90
Timestamp: 2026-04-13T14:44:48.257Z
Learning: In the `archtechx/tenancy` package, prefer using generic `\Exception` for feature-level errors as established in the codebase. Only introduce dedicated exception classes when they will be reused across multiple throw sites (or provide broader reuse). Do not recommend creating a new dedicated exception class for a single-use throw in this package.

Learnt from: lukinovec
Repo: archtechx/tenancy PR: 1446
File: src/Jobs/DeleteTenantStorage.php:43-44
Timestamp: 2026-04-13T15:14:03.457Z
Learning: When using `File::deleteDirectory($path)` from `archtechx/tenancy`, note its return behavior: it only returns `false` when `$path` is *not* a directory. If the call site is already guarded with `is_dir($path)` (or otherwise guarantees `$path` is a directory), then checking the boolean return value of `File::deleteDirectory($path)` for error handling is unnecessary because, after the directory-existence check passes, the method suppresses internal `rmdir` errors and unconditionally returns `true`. Prefer handling issues via upstream validation/guards (e.g., ensure the path is a directory) rather than relying on the return value in that code path.

@lukinovec lukinovec marked this pull request as ready for review June 1, 2026 12:58
@lukinovec

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/CachedTenantResolverTest.php`:
- Line 168: The dataset leaves TenancyServiceProvider::$adjustCacheManagerUsing
mutated by the DatabaseTenancyBootstrapper case so the subsequent
CacheTenancyBootstrapper case inherits the callback; update the test fixture in
CachedTenantResolverTest (in the beforeEach/afterEach surrounding the dataset)
to explicitly reset TenancyServiceProvider::$adjustCacheManagerUsing to its
known default (e.g., null or the original default closure) in addition to the
existing Tenant::$extraCustomColumns reset so each case exercises
makeDatabaseCacheStoresCentral() independently.

In `@tests/GlobalCacheTest.php`:
- Line 168: Tests leave TenancyServiceProvider::$adjustCacheManagerUsing set by
DatabaseTenancyBootstrapper, causing stale static state that affects later
CacheTenancyBootstrapper assertions; reset the static between runs by explicitly
assigning the class default (e.g.
TenancyServiceProvider::$adjustCacheManagerUsing = null or the original default)
in the test suite setup/teardown (beforeEach()/afterEach()) in the
GlobalCacheTest so each test starts with a clean
TenancyServiceProvider::$adjustCacheManagerUsing state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 574ac1d9-33d8-4426-852f-0208fe2e5108

📥 Commits

Reviewing files that changed from the base of the PR and between da7eb94 and fd1aa24.

📒 Files selected for processing (3)
  • src/TenancyServiceProvider.php
  • tests/CachedTenantResolverTest.php
  • tests/GlobalCacheTest.php

Comment thread tests/CachedTenantResolverTest.php
Comment thread tests/GlobalCacheTest.php

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes globalCache behavior so that database-backed cache stores use the central (or explicitly configured) DB connection even when the app is currently in tenant context (notably when using DatabaseTenancyBootstrapper + CacheTenancyBootstrapper). This prevents global cache reads/writes and cache invalidation from accidentally targeting tenant databases.

Changes:

  • Adjust globalCache’s freshly-instantiated CacheManager to reset database cache stores onto the central/configured connection.
  • Add regression coverage for the database cache driver when used with CacheTenancyBootstrapper.
  • Reset TenancyServiceProvider::$adjustCacheManagerUsing in tests to avoid cross-test state leakage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/TenancyServiceProvider.php Ensures database cache stores on the globalCache manager are pointed at the central/configured DB connection.
tests/GlobalCacheTest.php Adds regression dataset for database cache + CacheTenancyBootstrapper and resets the static cache-manager adjuster between tests.
tests/CachedTenantResolverTest.php Adds regression dataset for database cache + CacheTenancyBootstrapper and resets the static cache-manager adjuster between tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@lukinovec lukinovec changed the title [4.x] Make globalCache always use the central connection [4.x] Make globalCache always use the central connection with database cache store Jun 5, 2026
Comment thread tests/GlobalCacheTest.php Outdated
use function Stancl\Tenancy\Tests\withTenantDatabases;

beforeEach(function () {
TenancyServiceProvider::$adjustCacheManagerUsing = null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this added - also in afterEach?

The only other change in this file is using CacheTenancyBootstrapper but it doesn't use adjustCacheManagerUsing. Removing these lines still makes everything pass (including other tests that could be affected by global state).

Not sure if this is a leftover from some earlier implementation. Removing this now (also from CachedTenantResolverTest — same thing there), if there is a reason to keep this, let me know. I'll keep this review open.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't see the $adjustCacheManagerUsing modified anywhere in the test file, so the resets really are unnecessary, no reason to keep them.

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.

3 participants