Skip to content

Performance degrades with hundreds of sites #14670

@msuphilipgales

Description

@msuphilipgales

Bug description

I am running Statamic with 800 sites and have hit some expected performance bottlenecks. These are three small changes that have helped improve my page load time.

  1. Nav::existsIn() and CoreNav load all trees to check one site.

existsIn($site) calls trees()->has($site), and trees() calls $this->in() for every site, then filters. Same thing happens in CoreNav via $nav->sites()->contains(), since sites() goes through trees() too.

src/Structures/Nav.php

 public function existsIn($site)
 {
-    return $this->trees()->has($site);
+    return $this->in($site) !== null;
 }

src/CP/Navigation/CoreNav.php (lines 114 and 118)

-$nav->sites()->contains(Site::selected()->handle())
+$nav->existsIn(Site::selected()->handle())

  1. Sites::authorized() gate overhead for super users. Our super users need everysite but most of our users have access to less than 30 sites.

authorized() runs can('view', $site) on every site. SitePolicy::before() already returns true for super users, but the gate framework still has per-call overhead.

src/Sites/Sites.php

 public function authorized()
 {
+    if (User::current()->isSuper()) {
+        return $this->sites;
+    }
+
     return $this->sites->filter(fn ($site) => User::current()->can('view', $site));
 }

  1. Site::resolveAntlersValue() parses plain strings through the Antlers runtime

Every site config value goes through Parse::config() and RuntimeParser, even plain strings like en_US or /about/.

src/Sites/Site.php (after the existing is_array guard)

+    if (! is_string($value) || ! Str::contains($value, ['{', '@'])) {
+        return $value;
+    }
+
     $value = Parse::config($value);

The import is use Statamic\Support\Str;

How to reproduce

I created a seeder to create a lot of sites. Seed the sites and then navigate the CP.

<?php

namespace Database\Seeders;

use App\Actions\CreateSiteAction;
use Faker\Factory as Faker;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;

class SiteSeeder extends Seeder
{
    public function run(): void
    {
        $action = new CreateSiteAction;
        $faker = Faker::create();

        for ($i = 1; $i <= 1500; $i++) {
            $name = $faker->unique()->company();
            $handle = Str::slug($name);

            $action->handle(
                handle: $handle,
                name: $name,
                url: "/{$handle}/",
            );
        }
    }
}

Our CreateSiteAction handles the creation of the site, role, asset container, etc.

Logs

Environment

Environment
Laravel Version: 13.8.0
PHP Version: 8.5.5
Composer Version: 2.9.5
Environment: local
Debug Mode: ENABLED
Maintenance Mode: OFF
Timezone: UTC
Locale: en

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: file
Database: mariadb
Logs: stack / daily
Mail: log
Queue: database
Session: file

Storage
public/storage: NOT LINKED

Statamic
Addons: 4
License Key: Set
Sites: 1522 (Johnson, Tillman and Cremin, Dach-Weber, Howell, Beier and Ortiz, and 1519 more)
Stache Watcher: Enabled (auto)
Static Caching: Disabled
Version: 6.18.0 PRO

Statamic Addons
ndx/statamic-simple-redirects: 1.1.0
statamic/audit-log: 1.1.0
statamic/collaboration: 2.0.1
statamic/eloquent-driver: 5.7.0

Statamic Eloquent Driver
Addon Settings: file
Asset Containers: eloquent
Assets: eloquent
Blueprints: eloquent
Collection Trees: eloquent
Collections: file
Entries: eloquent
Fieldsets: file
Form Submissions: eloquent
Forms: eloquent
Global Sets: eloquent
Global Variables: eloquent
Navigation Trees: eloquent
Navigations: eloquent
Revisions: eloquent
Sites: eloquent
Taxonomies: eloquent
Terms: eloquent
Tokens: eloquent

Installation

Existing Laravel app

Additional details

I recognize this is an atypical use case and that these changes (particularly #1) may have implications I'm not aware of.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions