Laravel Octane 04: Advanced Caching, Database Optimization, and Monitoring with Swoole

July 17, 2025 (1d ago)

So far in our series, we've set up a high-performance environment with Laravel Octane and Swoole, and we've explored the power of concurrency to run tasks in parallel. These are huge steps, but to build a truly robust and scalable application, we need to master two more areas: advanced data management and observability.

In this article, we'll cover three critical topics:

  1. Supercharging with the Octane Cache: A blazing-fast, in-memory cache exclusive to Swoole.
  2. Database Optimization Essentials: Fundamental techniques to prevent your database from becoming a bottleneck.
  3. Monitoring Your App: An introduction to the tools that give you visibility into your application's health and performance.

Let's get started.

1. Supercharging with the Octane Cache

When using Octane with Swoole, you gain access to an incredibly fast caching driver: Cache::store('octane'). This isn't just another cache; it's a high-performance, in-memory cache powered by Swoole Tables, capable of handling up to 2 million read/write operations per second.

It's important to understand that this cache is volatile. This means the data is shared across all of Octane's workers, providing incredible speed, but it's lost whenever the server is restarted. It's perfect for caching data that is expensive to compute but can be easily regenerated.

Current Setup Review

Looking at our current codebase, we have two controllers demonstrating the difference between sequential and concurrent execution:

Both controllers currently return simple timing information. Let's enhance them with caching capabilities.

Practical Example: Caching Concurrent Queries with remember()

Let's create a new controller that demonstrates caching with Laravel's remember() method. This method is brilliant: it checks if a key exists in the cache. If it does, it returns the value. If not, it executes a closure to get the value, stores it in the cache, and then returns it.

First, let's create a new controller for cached dashboard operations:

<?php
 
namespace App\Http\Controllers;
 
use App\Models\Event;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Laravel\Octane\Facades\Octane;
use Laravel\Octane\Exceptions\TaskTimeoutException;
use Exception;
 
class ShowCachedDashboardController extends Controller
{
    /**
     * Handle the incoming request with caching.
     */
    public function __invoke(Request $request)
    {
        $time = hrtime(true);
 
        try {
            // We wrap our entire concurrent operation in a cache block.
            [$count, $eventsInfo, $eventsWarning, $eventsAlert] =
            Cache::store('octane')->remember(
                key: 'dashboard-events-cache', // A unique key for our cache item
                ttl: 20, // Time-to-live in seconds
                callback: function () {
                    // This function only runs if the cache is empty or expired.
                    return Octane::concurrently([
                        fn() => Event::query()->count(),
                        fn() => Event::query()->ofType('INFO')->get(),
                        fn() => Event::query()->ofType('WARNING')->get(),
                        fn() => Event::query()->ofType('ALERT')->get(),
                    ]);
                }
            );
        } catch (Exception $e) {
            // Handle potential exceptions from the cache or concurrent tasks
            return 'Error: '.$e->getMessage();
        }
 
        $time = (hrtime(true) - $time) / 1_000_000;
 
        return "Fetched from cache in {$time}ms. Count: {$count}, Info: {$eventsInfo->count()}, Warning: {$eventsWarning->count()}, Alert: {$eventsAlert->count()}";
    }
}

The first time you load the page associated with this controller, it will run the concurrent queries and take about a second. But for the next 20 seconds, every subsequent page load will be almost instantaneous because the results are being served directly from memory.

A Quick Tip on Cache Configuration: If you ever encounter a Value is too large error when caching, it means you're trying to store more data than the cache is configured to handle. You can easily fix this by increasing the bytes value in your config/octane.php file under the cache key. Currently, it's set to 10,000 bytes with 1,000 rows.

2. The Cache-First Architecture with Octane::tick()

The remember() method is great, but we can take our caching strategy a step further with a powerful, Swoole-only feature: Octane::tick(). This function allows you to execute a task asynchronously at a regular interval, completely independent of any user requests.

This enables a powerful cache-first architecture. The idea is simple:

This decouples your user's request from the latency of your data sources. The user experience is always fast because they never have to wait for a slow API or database query.

Why Cache-First Architecture Matters

Traditional request-response cycles look like this:

User Request → Database Query → Processing → Response (slow)

Cache-first architecture transforms this into:

Background: Ticker → Database Query → Cache Storage
User Request → Cache Read → Response (lightning fast!)

Key Benefits:

Step-by-Step Implementation

Step 1: Create the Ticker in a Service Provider

The best place to define a ticker is in the boot() method of a service provider, like AppServiceProvider. Let's create a ticker that refreshes our dashboard data every 60 seconds.

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use Laravel\Octane\Facades\Octane;
use App\Models\Event;
 
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }
 
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // Only register tickers when using Octane
        if (class_exists('Laravel\Octane\Facades\Octane')) {
            $this->registerDashboardTicker();
        }
    }
 
    private function registerDashboardTicker(): void
    {
        Octane::tick('cache-dashboard-query', function () {
            Log::info('Refreshing dashboard cache...');
 
            try {
                $result = Octane::concurrently([
                    'count' => fn() => Event::query()->count(),
                    'eventsInfo' => fn() => Event::query()->ofType('INFO')->get(),
                    'eventsWarning' => fn() => Event::query()->ofType('WARNING')->get(),
                    'eventsAlert' => fn() => Event::query()->ofType('ALERT')->get(),
                ]);
 
                Cache::store('octane')->put('dashboard-tick-cache', $result, 300); // 5-minute TTL
                Log::info('Dashboard cache refreshed successfully');
            } catch (Exception $e) {
                Log::error('Failed to refresh dashboard cache: ' . $e->getMessage());
            }
        })
        ->seconds(60)  // Run this task every 60 seconds
        ->immediate(); // Also run it immediately when the server starts
    }
}

Understanding the Ticker Configuration:

Advanced Ticker Patterns

1. Multiple Tickers for Different Data:

// In AppServiceProvider.php
Octane::tick('cache-user-stats', function () {
    // Update user statistics every 5 minutes
})->seconds(300)->immediate();
 
Octane::tick('cache-live-data', function () {
    // Update live data every 30 seconds
})->seconds(30)->immediate();

2. Error Handling and Fallbacks:

Octane::tick('cache-dashboard-query', function () {
    try {
        $result = Octane::concurrently([...]);
        Cache::store('octane')->put('dashboard-tick-cache', $result, 300);
 
        // Store a "last updated" timestamp
        Cache::store('octane')->put('dashboard-last-updated', now(), 300);
    } catch (Exception $e) {
        Log::error('Ticker failed: ' . $e->getMessage());
 
        // Keep old cache alive longer if refresh fails
        Cache::store('octane')->put('dashboard-cache-error', true, 60);
    }
});

3. Conditional Ticker Logic:

Octane::tick('smart-cache-refresh', function () {
    // Only run expensive operations during low-traffic hours
    if (now()->hour >= 2 && now()->hour <= 6) {
        // Run expensive data aggregation
        $heavyData = performExpensiveCalculation();
        Cache::store('octane')->put('heavy-data', $heavyData, 3600);
    }
 
    // Always refresh lightweight data
    $lightData = Event::query()->count();
    Cache::store('octane')->put('light-data', $lightData, 300);
})->seconds(120);

#### Step 2: Create a Controller to Read from the Tick Cache

Now, let's create a controller that reads from our tick-warmed cache:

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

class ShowTickCachedDashboardController extends Controller
{
    /**
     * Handle the incoming request using tick-warmed cache.
     */
    public function __invoke(Request $request)
    {
        $time = hrtime(true);

        $result = Cache::store('octane')->get('dashboard-tick-cache');

        $time = (hrtime(true) - $time) / 1_000_000;

        // Provide helpful feedback if cache isn't warmed
        if (!$result) {
            return "Cache not yet warmed. Please warm it by visiting: /test-ticker";
        }

        return "Fetched from tick cache in {$time}ms. Count: {$result['count']}, Info: {$result['eventsInfo']->count()}, Warning: {$result['eventsWarning']->count()}, Alert: {$result['eventsAlert']->count()}";
    }
}

This controller is incredibly simple and fast because it only reads from the pre-warmed cache—no database queries at all!

With this pattern, your response times can drop to just a few milliseconds, as the heavy lifting is always handled in the background.

Step 3: Manual Cache Warming (Alternative Approach)

While the automatic ticker is ideal for production, during development with Docker and Laravel Sail, you might need to manually warm the cache. We've included practical approaches for this:

Via Test Route (for quick testing):

// Add to routes/web.php
Route::get('/test-ticker', function () {
    try {
        $result = \Laravel\Octane\Facades\Octane::concurrently([
            'count' => fn() => \App\Models\Event::query()->count(),
            'eventsInfo' => fn() => \App\Models\Event::query()->ofType('INFO')->get(),
            'eventsWarning' => fn() => \App\Models\Event::query()->ofType('WARNING')->get(),
            'eventsAlert' => fn() => \App\Models\Event::query()->ofType('ALERT')->get(),
        ]);
 
        \Illuminate\Support\Facades\Cache::store('octane')->put('dashboard-tick-cache', $result, 300);
 
        return 'Manually triggered ticker logic. Cache warmed successfully!';
    } catch (Exception $e) {
        return 'Error: ' . $e->getMessage();
    }
});

Via Artisan Command (for development workflow):

./vendor/bin/sail artisan dashboard:warm-cache

The command approach is particularly useful during development and can be integrated into deployment scripts or scheduled tasks.

3. Database Optimization Essentials

Even the fastest application server will be crippled by a slow database. Here are some fundamental optimizations every developer should know.

Database Indexing

Looking at our current Event model and migration, we can see that our queries filter by type and sort by date. The current migration in database/migrations/2025_07_14_140014_create_events_table.php already has an index on user_id, but we need additional indexes for performance optimization.

Without proper database indexes, your database has to perform a full table scan to find data, which is like reading a book from cover to cover to find one word. An index is like the book's index page—it allows the database to jump directly to the data it needs.

In our dashboard example, our Event model's ofType scope filters by type and sorts by date. These are perfect candidates for indexes.

How to Create Additional Indexes:

  1. Create a new migration:
./vendor/bin/sail artisan make:migration add_indexes_to_events_table
  1. Define the indexes in the migration file:
<?php
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('events', function (Blueprint $table) {
            $table->index('type');
            $table->index('date');
            $table->index('description');
            // Composite index for type + date queries (most efficient for our ofType scope)
            $table->index(['type', 'date']);
        });
    }
 
    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('events', function (Blueprint $table) {
            $table->dropIndex(['events_type_index']);
            $table->dropIndex(['events_date_index']);
            $table->dropIndex(['events_description_index']);
            $table->dropIndex(['events_type_date_index']);
        });
    }
};
  1. Run the migration:
./vendor/bin/sail artisan migrate

Adding these indexes can dramatically reduce query times, often turning a multi-second query into one that runs in milliseconds.

Preventing N+1 Queries with Eager Loading

The N+1 query problem is one of the most common performance pitfalls in Laravel. It happens when you loop through a model and access a related model inside the loop.

The Problem (N+1 Queries):

<?php
// This runs 1 query to get all events...
$events = Event::all();
 
foreach ($events as $event) {
    // ...and then N additional queries to get the user for each event.
    echo $event->user->name;
}

The Solution (Eager Loading):

By using the with() method, you can tell Eloquent to fetch all the related models in a single, additional query.

<?php
// This runs only 2 queries, regardless of the number of events.
$events = Event::with('user')->get();
 
foreach ($events as $event) {
    echo $event->user->name;
}

To implement this in our Event model, we need to define the relationship. Let's update our Event model:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Event extends Model
{
    use HasFactory;
 
    /**
     * Get the user that owns the event.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
 
    /**
     * This simulates a complex, time-consuming query.
     */
    public function scopeOfType(Builder $query, string $type)
    {
        sleep(1); // Simulate a 1-second query time
        return $query->where('type', $type)
            ->orderBy('date', 'desc')
            ->limit(5);
    }
}

This simple change from Event::all() to Event::with('user')->get() can reduce hundreds of queries to just two.

4. Monitoring & Profiling Your High-Performance App

With a high-performance application, you need the right tools to see what's going on under the hood. Here's a quick guide to the essential monitoring tools for a junior developer.

Laravel Telescope (The Local X-Ray)

Telescope is your best friend for local development. It gives you a beautiful dashboard to inspect incoming requests, database queries, exceptions, queued jobs, and much more. It's perfect for debugging a specific feature and understanding what your application is doing on a request-by-request basis.

Installation:

./vendor/bin/sail composer require laravel/telescope --dev
./vendor/bin/sail artisan telescope:install
./vendor/bin/sail artisan migrate

Laravel Horizon (The Queue Control Tower)

If you're using Redis for your queues (which you should be!), Horizon is an indispensable dashboard for monitoring your queues. It shows you job throughput, runtimes, failures, and worker status, giving you complete control over your background processes.

Installation:

./vendor/bin/sail composer require laravel/horizon
./vendor/bin/sail artisan horizon:install

Swoole Metrics (The Engine Vitals)

For advanced diagnostics, Swoole provides a low-level stats() method to get raw metrics about the server itself, such as active connections, memory usage, and total requests.

You can create a simple route to view these stats:

<?php
// In routes/web.php
 
Route::get('/swoole-stats', function () {
    if (app()->bound('swoole.server')) {
        $server = app('swoole.server');
        return response()->json($server->stats());
    }
 
    return response()->json(['error' => 'Swoole server not available']);
});

Performance Testing Routes

To test our optimizations, add these routes to routes/web.php:

<?php
// Performance comparison routes
Route::get('/dashboard-cached', ShowCachedDashboardController::class)
    ->name('dashboard.cached');
 
Route::get('/dashboard-tick-cached', ShowTickCachedDashboardController::class)
    ->name('dashboard.tick-cached');
 
// Manual cache warming route for testing
Route::get('/test-ticker', function () {
    try {
        $result = \Laravel\Octane\Facades\Octane::concurrently([
            'count' => fn() => \App\Models\Event::query()->count(),
            'eventsInfo' => fn() => \App\Models\Event::query()->ofType('INFO')->get(),
            'eventsWarning' => fn() => \App\Models\Event::query()->ofType('WARNING')->get(),
            'eventsAlert' => fn() => \App\Models\Event::query()->ofType('ALERT')->get(),
        ]);
 
        \Illuminate\Support\Facades\Cache::store('octane')->put('dashboard-tick-cache', $result, 300);
 
        return 'Manually triggered ticker logic. Cache warmed successfully!';
    } catch (Exception $e) {
        return 'Error: ' . $e->getMessage();
    }
});

What's Next?

We've now covered the complete spectrum of high-performance Laravel development:

  1. Octane Cache: Ultra-fast, in-memory caching with Cache::store('octane')
  2. Cache Strategies: Both reactive (remember()) and proactive (tick()) approaches
  3. Database Indexes: Proper indexing strategies for faster queries
  4. Eager Loading: Preventing N+1 query problems with with()
  5. Monitoring Tools: Telescope, Horizon, and Swoole stats for comprehensive observability

Key Performance Achievements:

Real-World Applications

These techniques aren't just academic—they solve real problems:

In our final article, we'll tackle deploying our high-performance Laravel application to a production environment with proper configuration, monitoring, and scaling strategies.

Testing Your Implementation

To see the performance differences, follow this step-by-step testing sequence:

Step 1: Test Sequential vs Concurrent

  1. Visit /dashboard-sequential to see sequential execution (~3+ seconds)
  2. Visit /dashboard-concurrent to see concurrent execution (~1 second)

Step 2: Test Caching Benefits

  1. Visit /dashboard-cached to see cached concurrent execution (first load ~1 second)
  2. Immediately visit /dashboard-cached again (should be <5ms - cached!)

Step 3: Test Cache-First Architecture

  1. Warm the tick cache by visiting /test-ticker first
  2. Visit /dashboard-tick-cached to see tick-warmed cache execution (consistently <2ms)

Expected Performance Results:

Pro Tip for Students:

Try refreshing the cached route multiple times quickly - you'll see the dramatic difference between cache hits and misses when the TTL expires!

Advanced Performance Strategies Beyond Octane

1. Database Query Optimization

Eager Loading to Prevent N+1 Queries:

// ❌ Bad: N+1 query problem
$events = Event::query()->ofType('INFO')->get();
foreach ($events as $event) {
    echo $event->user->name; // This triggers a query for each event
}
 
// ✅ Good: Eager loading
$events = Event::query()->ofType('INFO')->with('user')->get();
foreach ($events as $event) {
    echo $event->user->name; // No additional queries
}

Advanced Database Indexing:

// In your migration
Schema::table('events', function (Blueprint $table) {
    $table->index(['type', 'created_at']); // Composite index for filtered queries
    $table->index('user_id'); // Foreign key index
    $table->index('description'); // For full-text searches
 
    // Partial index for specific conditions (PostgreSQL)
    $table->index('status')->where('status', '!=', 'archived');
});

2. Advanced Caching Strategies

Cache Tags for Organized Invalidation:

// Tag related data together
Cache::tags(['user', 'events'])->put('user-events-' . $userId, $events, 3600);
 
// Invalidate all user-related cache when data changes
Cache::tags(['user'])->flush();

Cache Locks to Prevent Cache Stampede:

public function getExpensiveData()
{
    return Cache::lock('expensive-calculation', 10)->get(function () {
        return Cache::remember('expensive-data', 3600, function () {
            return performExpensiveCalculation();
        });
    });
}

3. Memory and Resource Management

Memory-Efficient Data Processing:

// ❌ Memory intensive: Loads all records at once
$allEvents = Event::all();
 
// ✅ Memory efficient: Process in chunks
Event::chunk(1000, function ($events) {
    foreach ($events as $event) {
        // Process each event without memory overflow
    }
});

Background Job Processing:

// Offload heavy tasks to queues
ProcessAnalyticsJob::dispatch($analyticsData)
    ->onQueue('analytics')
    ->delay(now()->addMinutes(5));

Production Monitoring and Metrics

Application Performance Monitoring (APM)

Laravel Telescope Integration:

composer require laravel/telescope
php artisan telescope:install
php artisan migrate

Custom Performance Tracking:

use Illuminate\Support\Facades\Log;
 
class DashboardController extends Controller
{
    public function index()
    {
        $startTime = microtime(true);
 
        // Your application logic here
        $result = $this->getDashboardData();
 
        $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds
 
        Log::info('Dashboard performance', [
            'execution_time_ms' => round($executionTime, 2),
            'cache_hit' => $result['cache_hit'] ?? false,
            'memory_usage_mb' => round(memory_get_usage() / 1024 / 1024, 2)
        ]);
 
        return response()->json($result);
    }
}

Key Performance Indicators (KPIs) to Monitor

  1. Response Time: Target < 200ms for cached responses
  2. Throughput: Requests per second your application can handle
  3. Memory Usage: Monitor for memory leaks in long-running processes
  4. Cache Hit Ratio: Aim for > 90% for frequently accessed data
  5. Database Query Count: Minimize queries per request
  6. Error Rate: Keep below 0.1% for production applications

Real-World Production Considerations

Deployment Strategies

Health Checks for Load Balancers:

// routes/web.php
Route::get('/health', function () {
    return response()->json([
        'status' => 'healthy',
        'octane' => extension_loaded('swoole'),
        'cache' => Cache::store('octane')->get('health-check', 'ok'),
        'database' => DB::connection()->getPdo() ? 'connected' : 'disconnected',
        'memory_usage' => round(memory_get_usage() / 1024 / 1024, 2) . 'MB',
        'timestamp' => now()->toISOString()
    ]);
});

Environment-Specific Configurations:

// config/octane.php - Production optimizations
return [
    'workers' => env('OCTANE_WORKERS', 4), // Set based on CPU cores
    'task_workers' => env('OCTANE_TASK_WORKERS', 6),
    'max_requests' => 1000, // Restart workers after X requests
    'cache' => [
        'rows' => 1000, // Increase for production
        'bytes' => 10000,
    ],
    'tables' => [
        'example:1000', // Swoole table for shared data
    ],
];

Scaling Strategies

Horizontal Scaling with Multiple Instances:

# docker-compose.prod.yml
version: "3.8"
services:
  app-1:
    build: .
    environment:
      - OCTANE_WORKERS=4
    ports:
      - "8001:8000"
 
  app-2:
    build: .
    environment:
      - OCTANE_WORKERS=4
    ports:
      - "8002:8000"
 
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf

Redis for Shared Cache Across Instances:

// config/cache.php
'stores' => [
    'redis' => [
        'driver' => 'redis',
        'connection' => 'cache',
        'lock_connection' => 'default',
    ],
],

Conclusion: Your High-Performance Laravel Journey

Throughout this comprehensive guide, we've transformed a simple Laravel application from basic sequential queries to a high-performance, cache-optimized powerhouse. Here's what we accomplished:

Performance Transformation Summary

Strategy Response Time Improvement Factor
Sequential Queries ~3+ seconds Baseline
Concurrent Queries ~1 second 3x faster
Cache + Concurrent <5ms 600x faster
Tick Cache (Cache-First) <1ms 3000x faster

Key Learning Outcomes for Junior Developers

🎯 Technical Skills Gained:

  1. Octane Integration: Understanding how to properly set up and configure Laravel Octane with Swoole
  2. Concurrent Programming: Using Octane::concurrently() for parallel database operations
  3. Advanced Caching: Implementing both reactive (remember()) and proactive (tick()) caching strategies
  4. Database Optimization: Creating indexes and preventing N+1 queries
  5. Performance Monitoring: Setting up proper metrics and logging

🔧 Development Best Practices:

Next Steps in Your Learning Journey

  1. Experiment with Your Own Projects: Apply these concepts to real applications
  2. Explore Advanced Swoole Features: Dive into coroutines, connection pooling, and async I/O
  3. Study Production Systems: Learn about load balancing, auto-scaling, and monitoring
  4. Contribute to Open Source: Share your optimizations with the Laravel community

Real-World Impact

These performance improvements aren't just numbers—they translate to:

Final Thoughts

High-performance web applications are built through understanding, measurement, and incremental improvement. The techniques you've learned here—from basic concurrent queries to sophisticated cache-first architectures—form the foundation of modern, scalable web applications.

Remember: Performance optimization is a journey, not a destination. Keep learning, keep measuring, and keep improving!

Happy coding, and may your applications be fast and your databases be optimized! 🚀


This post is part of our High-Performance Laravel series for junior developers. Follow us for more advanced Laravel techniques, performance tips, and best practices.


Works cited

  1. What Is Laravel Octane? - How It Works & Benchmarks - Redberry International, accessed July 13, 2025, https://redberry.international/what-is-octane-in-laravel-things-to-know/
  2. Upgrading Web App Performance With Laravel Octane Features - Pattem Digital, accessed July 13, 2025, https://pattemdigital.com/insight/laravel-octane-for-your-web-apps/
  3. Free Performance + Extra features for Laravel using Octane and Swoole | AriaieBOY Blog, accessed July 13, 2025, https://ariaieboy.ir/blog/10-free-performance-using-laravel-octane-and-swoole/
  4. High Performance with Laravel Octane - Roberto Butti, accessed July 13, 2025, https://subscription.packtpub.com
  5. Laravel Octane - What It Is, Why It Matters & Getting Started - RunCloud, accessed July 13, 2025, https://runcloud.io/blog/laravel-octane
  6. A Quick Guide on using Laravel Octane to Scale Your App - Bacancy Technology, accessed July 13, 2025, https://www.bacancytechnology.com/blog/laravel-octane
  7. Laravel with(): How to Solve the N+1 Problem with Eager Loading Relationships | Fajarwz, accessed July 13, 2025, https://fajarwz.com/blog/laravel-with-how-to-solve-the-n+1-problem-with-eager-loading-relationships/
  8. Preventing N+1 issues globally in Laravel using Auto Eager Loading - Amit Merchant, accessed July 13, 2025, https://www.amitmerchant.com/laravel-auto-eager-loading/
  9. How to Force Eager Loading and Prevent N+1 Issues in Laravel - Ash Allen Design, accessed July 13, 2025, https://ashallendesign.co.uk/blog/how-to-force-eager-loading-and-prevent-n-1-issues-in-laravel
  10. N+1 Query Problem in Laravel: Causes, Effects, and Solutions | by Moumen Alisawe, accessed July 13, 2025, https://medium.com/@moumenalisawe/n-1-query-problem-in-laravel-causes-effects-and-solutions-740cefa44306
  11. Using Laravel Octane for High-Performance Applications - 200OK Solutions, accessed July 13, 2025, https://200oksolutions.com/blog/using-laravel-octane-high-performance-applications/
  12. Laravel Telescope - Laravel 12.x - The PHP Framework For Web Artisans, accessed July 13, 2025, https://laravel.com/docs/12.x/telescope
  13. How to Set up and Configure Telescope in Laravel - Interserver Tips, accessed July 13, 2025, https://www.interserver.net/tips/kb/how-to-set-up-and-configure-telescope-in-laravel/
  14. Laravel 12 Telescope: Installation & Configuration Tutorial - YouTube, accessed July 13, 2025, https://www.youtube.com/watch?v=HiLqvouwkYs
  15. Setup and Configure Telescope in Laravel - YouTube, accessed July 13, 2025, https://www.youtube.com/watch?v=nsLNLDsjcxo
  16. Laravel Horizon - Laravel 12.x - The PHP Framework For Web Artisans, accessed July 13, 2025, https://laravel.com/docs/12.x/horizon

Frequently Asked Questions

What is the Octane cache and how fast is it compared to regular caching?

The Octane cache is a high-performance, in-memory cache powered by Swoole Tables, capable of handling up to 2 million read/write operations per second. It's significantly faster than traditional caching methods because data is stored directly in memory and shared across all Octane workers.

What's the difference between the remember() method and Octane::tick() for caching?

The remember() method is reactive - it caches data when requested and serves from cache on subsequent requests. Octane::tick() is proactive - it runs background tasks at intervals to pre-warm the cache, creating a cache-first architecture where users always get fast responses.

Is the Octane cache data persistent across server restarts?

No, the Octane cache is volatile. Data is lost when the server restarts because it's stored in memory. It's perfect for caching expensive computations that can be easily regenerated, but not for critical data that must persist.

What is the N+1 query problem and how does eager loading solve it?

The N+1 problem occurs when you fetch N records and then make an additional query for each record's relationship (N+1 total queries). Eager loading with with() solves this by fetching all relationships in just 2 queries total, regardless of the number of records.

How much performance improvement can I expect with these optimizations?

Performance improvements are dramatic: from 3+ seconds (sequential) to ~1 second (concurrent) to <5ms (cached) to <1ms (tick-cached). That's potentially a 3000x improvement in response times with proper implementation.

When should I use database indexes and what types should I create?

Create indexes for columns you frequently filter by (WHERE clauses), sort by (ORDER BY), or join on. For our Event model, we need indexes on 'type', 'date', and a composite index on ['type', 'date'] for the ofType scope that filters and sorts simultaneously.

What happens if I get a 'Value is too large' error with Octane cache?

This means you're trying to store more data than the cache is configured to handle. Increase the 'bytes' value in your config/octane.php file under the cache key. The default is 10,000 bytes with 1,000 rows.

Can I use Octane::tick() with Docker and Laravel Sail during development?

Yes, but tickers work best in production. During development with Docker/Sail, you might need to manually warm the cache using test routes or artisan commands, as shown in the article with the /test-ticker route.

What monitoring tools should I use for a high-performance Laravel application?

Use Laravel Telescope for local development debugging, Laravel Horizon for queue monitoring with Redis, and Swoole's stats() method for low-level server metrics. These tools provide comprehensive visibility into your application's performance.

How do I choose the right interval for Octane::tick() tasks?

Consider three factors: how fresh your data needs to be, how expensive your queries are, and your server resources. Start with 60 seconds for most use cases, use 30 seconds for live data, and 5+ minutes for heavy computations. Always include error handling and fallbacks.