Advanced Laravel

Laravel Octane & Performance

18 min Lesson 27 of 40

Laravel Octane & Performance

Laravel Octane supercharges your application's performance by serving it using high-powered application servers like Swoole and RoadRunner. These servers keep your application loaded in memory, dramatically reducing response times and increasing throughput.

Performance Gains: Octane can handle 2-10x more requests per second compared to traditional PHP-FPM deployments, with response times reduced by 50-80% in many cases.

Understanding Swoole vs RoadRunner

Swoole: A production-grade async programming framework for PHP with coroutines, event loops, and fiber support.

RoadRunner: A high-performance PHP application server written in Go, using workers to serve requests.

<?php // Installation - Swoole // composer require laravel/octane // php artisan octane:install --server=swoole // Installation - RoadRunner // composer require laravel/octane // php artisan octane:install --server=roadrunner // Start Octane server // php artisan octane:start // Start with custom port and workers // php artisan octane:start --port=8000 --workers=4 // Production mode with higher worker count // php artisan octane:start --workers=8 --task-workers=6 --max-requests=500
Choosing a Server: Use Swoole if you need advanced features like coroutines, WebSockets, and async operations. Use RoadRunner for simpler deployments with excellent Go-based performance and easier debugging.

Octane Setup and Configuration

<?php // config/octane.php return [ 'server' => env('OCTANE_SERVER', 'swoole'), 'https' => env('OCTANE_HTTPS', false), 'listeners' => [ WorkerStarting::class => [ EnsureUploadedFilesAreValid::class, EnsureUploadedFilesCanBeMoved::class, ], RequestReceived::class => [ ...Octane::prepareApplicationForNextOperation(), ...Octane::prepareApplicationForNextRequest(), ], RequestHandled::class => [], RequestTerminated::class => [ FlushUploadedFiles::class, ], TaskReceived::class => [ ...Octane::prepareApplicationForNextOperation(), ], TaskTerminated::class => [], TickReceived::class => [], TickTerminated::class => [], OperationTerminated::class => [ FlushTemporaryContainerInstances::class, ], WorkerErrorOccurred::class => [ ReportException::class, StopWorkerIfNecessary::class, ], WorkerStopping::class => [], ], 'warm' => [ // Classes to warm on worker boot ...Octane::defaultServicesToWarm(), ], 'cache' => [ 'driver' => env('OCTANE_CACHE_DRIVER', 'array'), ], 'tables' => [ 'example' => [ 'size' => 1000, 'columns' => [ ['name' => 'id', 'type' => Table::TYPE_INT], ['name' => 'name', 'type' => Table::TYPE_STRING, 'size' => 1000], ], ], ], ];

Memory Management in Octane

Since your application stays in memory, you must be careful about memory leaks and state pollution:

<?php // WRONG - Static properties persist across requests class UserController extends Controller { protected static $users = []; public function index() { // This will accumulate data across requests! self::$users[] = User::all(); return view('users.index', ['users' => self::$users]); } } // CORRECT - Use request-scoped data class UserController extends Controller { public function index() { $users = User::all(); return view('users.index', ['users' => $users]); } } // Safe patterns for Octane class CacheService { protected $cache; public function __construct() { // Injected dependencies are safe $this->cache = app('cache'); } public function remember($key, $value) { // Use Octane's cache tables for shared state return Cache::store('octane')->remember($key, 3600, function () use ($value) { return $value(); }); } }
Memory Leak Warning: Avoid static properties, global state, and singleton services that accumulate data. Always reset or clear state between requests. Use Octane's cache tables for shared data across workers.

Concurrent Tasks with Octane

<?php use Laravel\Octane\Facades\Octane; // Sequential execution (slow) public function dashboard() { $users = User::count(); $posts = Post::count(); $comments = Comment::count(); return view('dashboard', compact('users', 'posts', 'comments')); } // Concurrent execution with Octane (fast!) public function dashboard() { [$users, $posts, $comments] = Octane::concurrently([ fn () => User::count(), fn () => Post::count(), fn () => Comment::count(), ]); return view('dashboard', compact('users', 'posts', 'comments')); } // Concurrent API calls public function aggregateData() { [$github, $twitter, $analytics] = Octane::concurrently([ fn () => Http::get('https://api.github.com/user/repos')->json(), fn () => Http::get('https://api.twitter.com/tweets')->json(), fn () => Http::get('https://analytics.example.com/stats')->json(), ], 5000); // 5 second timeout return response()->json([ 'github' => $github, 'twitter' => $twitter, 'analytics' => $analytics, ]); }

Caching Strategies in Octane

<?php // Use Octane's cache tables for high-performance caching use Laravel\Octane\Facades\Octane; // Define cache table in config/octane.php 'tables' => [ 'users' => [ 'size' => 10000, 'columns' => [ ['name' => 'id', 'type' => Table::TYPE_INT], ['name' => 'name', 'type' => Table::TYPE_STRING, 'size' => 255], ['name' => 'email', 'type' => Table::TYPE_STRING, 'size' => 255], ['name' => 'created_at', 'type' => Table::TYPE_INT], ], ], ], // Access Swoole tables $table = Octane::table('users'); // Set data $table->set('user:1', [ 'id' => 1, 'name' => 'John Doe', 'email' => 'john@example.com', 'created_at' => time(), ]); // Get data $user = $table->get('user:1'); // Check existence if ($table->exists('user:1')) { // User exists } // Delete $table->del('user:1'); // Use interval tasks for cache warming Octane::tick('warm-cache', function () { $popularPosts = Post::popular()->limit(10)->get(); $table = Octane::table('posts'); foreach ($popularPosts as $post) { $table->set('post:' . $post->id, [ 'id' => $post->id, 'title' => $post->title, 'views' => $post->views, ]); } })->seconds(60); // Run every 60 seconds

Benchmarking and Monitoring

<?php // Basic load testing with Apache Bench // ab -n 10000 -c 100 http://localhost:8000/ // More advanced with wrk // wrk -t12 -c400 -d30s http://localhost:8000/ // Monitor Octane metrics public function metrics() { return response()->json([ 'memory_usage' => memory_get_usage(true), 'peak_memory' => memory_get_peak_usage(true), 'uptime' => $this->getUptime(), 'requests_handled' => $this->getRequestCount(), ]); } // Middleware for request timing class PerformanceMonitor { public function handle($request, Closure $next) { $start = microtime(true); $response = $next($request); $duration = (microtime(true) - $start) * 1000; Log::info('Request completed', [ 'url' => $request->fullUrl(), 'method' => $request->method(), 'duration_ms' => round($duration, 2), 'memory_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2), ]); return $response; } }
Production Optimization: Use `--max-requests=500` to automatically restart workers after a set number of requests, preventing memory leaks. Monitor memory usage and adjust worker count based on your server's RAM.
Exercise 1: Convert an existing Laravel application to use Octane. Benchmark the performance difference between PHP-FPM and Octane under load. Document response times, requests per second, and memory usage.
Exercise 2: Create a dashboard that makes 10+ API calls to external services. Implement both sequential and concurrent approaches using Octane::concurrently(). Measure and compare the performance difference.
Exercise 3: Build a real-time analytics system using Octane's cache tables and tick intervals. Store and update metrics like active users, request counts, and error rates in Swoole tables, accessible across all workers.