Laravel Octane Bikin Aplikasi 10x Lebih Cepat dengan Swoole/RoadRunner

Pelajari cara implement Laravel Octane dengan Swoole dan RoadRunner untuk meningkatkan performa aplikasi hingga 10x lipat. Artikel ini membahas studi kasus nyata dari pengalaman saya sendiri mengelola BuildWithAngga — platform online courses untuk developers Indonesia. Dari response time 3 detik yang memalukan sampai 200ms yang membanggakan.

Lengkap dengan step-by-step setup, code examples, gotchas yang harus dihindari, dan benchmark results yang bisa kamu expect. Kalau kamu pernah struggle dengan Laravel yang lambat meski sudah optimize database dan caching, artikel ini untuk kamu.


Bagian 1: The Embarrassing Truth — Website Sendiri yang Lambat

Saya founder platform online courses. Setiap hari, saya mengajar ribuan developers Indonesia cara bikin website yang bagus, cepat, dan professional.

Tapi website saya sendiri?

Lambat. Embarrassingly lambat.

Ini bukan cerita tentang client atau project orang lain. Ini cerita tentang platform saya sendiri — BuildWithAngga. Platform yang seharusnya menjadi showcase kemampuan saya. Platform yang seharusnya membuktikan bahwa saya tau apa yang saya ajarkan.

Response time homepage: 3.2 detik. Course detail page: 4 detik lebih. Video player initialization: hampir 6 detik. Peak hour saat ada promo? Timeout.

Ironi yang menyakitkan.

The Wake-Up Call

Semuanya dimulai dari DM yang masuk bertubi-tubi.

"Kak Angga, websitenya kok lambat ya? Mau beli course tapi loading terus."

Awalnya saya pikir itu cuma satu dua orang dengan koneksi jelek. Tapi kemudian:

"Mas, checkout timeout terus. Udah coba 3x gagal. Akhirnya beli di platform lain."

Itu sakit. Bukan cuma kehilangan satu sale — tapi kehilangan trust.

Lalu ada review yang masuk:

"Kontennya bagus, sangat membantu belajar Laravel. Tapi websitenya lemot banget, kadang males bukanya."

Bintang 4. Padahal harusnya bisa bintang 5 kalau bukan karena performa.

Saya buka Google Analytics. Data tidak berbohong:

  • Bounce rate naik 40% dalam 2 bulan terakhir
  • Average session duration turun dari 8 menit ke 3 menit
  • Conversion rate drop dari 3.2% ke 1.8%
  • Page views per session turun drastis

The Numbers That Hurt

Saya jalankan profiling dan benchmarking untuk dapat gambaran jelas:

MetricBuildWithAngga (Before)Industry StandardStatus
Homepage load3.2s< 2s❌ Bad
Course listing3.8s< 2s❌ Bad
Course detail4.1s< 2s❌ Bad
Video player init5.8s< 3s❌ Bad
Checkout page3.5s< 1.5s❌ Bad
API responses (avg)800ms - 2s< 200ms❌ Bad
Requests/sec (max)~100500+❌ Bad
Server CPU at peak95%+< 70%❌ Bad

Setiap metric merah. Semuanya di bawah standard.

Dan ini bukan server kentang. Ini VPS dengan 8 core CPU, 16GB RAM, SSD NVMe. Bukan masalah hardware.

What I Had Already Tried

Yang bikin frustrating, saya sudah melakukan "semua" optimization yang biasa direkomendasikan:

OPTIMIZATIONS YANG SUDAH DILAKUKAN:
✓ Redis untuk cache dan session
✓ Database queries optimized (no N+1)
✓ Eager loading implemented
✓ Route caching (php artisan route:cache)
✓ Config caching (php artisan config:cache)
✓ View caching (php artisan view:cache)
✓ OPcache enabled dan configured
✓ CDN untuk static assets
✓ Database indexes proper
✓ Queue untuk heavy jobs
✓ Image optimization

RESULT: Masih lambat. 🤦

Saya sudah optimize di level aplikasi. Sudah optimize di level database. Sudah optimize di level infrastructure.

Tapi tetap lambat.

The Realization

Setelah deep-dive ke profiling, saya akhirnya paham. Masalahnya bukan di code saya. Masalahnya bukan di query saya. Masalahnya di level yang lebih fundamental:

PHP-FPM harus bootstrap Laravel dari NOL untuk setiap single request.

Dengan 60+ service providers, 200+ routes, puluhan middleware, dan ratusan config files... setiap request harus load semuanya. Lagi. Dan lagi. Dan lagi.

Tidak peduli seberapa optimal code saya, kalau fondasi arsitekturnya memang lambat by design.

"Saya bikin courses tentang Laravel performance. Saya ngajarin best practices. Tapi platform saya sendiri lambat. Kalau ada definisi sempurna untuk kata 'ironi', ini contohnya. Dan ini harus di-fix. Sekarang."


Bagian 2: Understanding the Problem — Kenapa PHP-FPM Lambat

Untuk benar-benar fix masalah, saya harus paham dulu kenapa lambat. Bukan cuma symptoms, tapi root cause.

How Traditional PHP-FPM Works

Setiap kali ada request masuk ke aplikasi Laravel yang jalan di PHP-FPM, ini yang terjadi:

REQUEST LIFECYCLE DI PHP-FPM (Setiap Request):

┌─────────────────────────────────────────────────────────────────┐
│  CLIENT REQUEST MASUK                                           │
│         ↓                                                       │
│  Nginx menerima request                                         │
│         ↓                                                       │
│  Nginx forward ke PHP-FPM                                       │
│         ↓                                                       │
│  PHP-FPM spawn atau reuse worker                                │
│         ↓                                                       │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  BOOTSTRAP PHASE (terjadi SETIAP request):                │  │
│  │                                                           │  │
│  │  1. Load Composer autoloader              (~15ms)         │  │
│  │  2. Load semua config files (50+)         (~25ms)         │  │
│  │  3. Create service container              (~20ms)         │  │
│  │  4. Register 60+ service providers        (~80ms)         │  │
│  │  5. Boot 60+ service providers            (~60ms)         │  │
│  │  6. Load dan parse routes (200+)          (~40ms)         │  │
│  │  7. Setup middleware stack                (~20ms)         │  │
│  │  8. Resolve facades                       (~30ms)         │  │
│  │  9. Setup error handlers                  (~10ms)         │  │
│  │                                                           │  │
│  │  TOTAL BOOTSTRAP: ~300ms                                  │  │
│  │  (Sebelum satu baris code kamu jalan!)                    │  │
│  └───────────────────────────────────────────────────────────┘  │
│         ↓                                                       │
│  Execute controller logic                    (~50-200ms)        │
│         ↓                                                       │
│  Render view                                 (~20-50ms)         │
│         ↓                                                       │
│  Send response ke client                                        │
│         ↓                                                       │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  SHUTDOWN PHASE:                                          │  │
│  │  • Garbage collection                                     │  │
│  │  • Close database connections                             │  │
│  │  • Free all memory                                        │  │
│  │  • Terminate service providers                            │  │
│  │  • Worker siap untuk request berikutnya                   │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

REQUEST BERIKUTNYA: ULANGI SEMUANYA DARI AWAL!

Lihat itu? 300ms hanya untuk bootstrap — sebelum satu baris code aplikasi saya dijalankan.

Dan ini terjadi untuk setiap. single. request.

Analogi: Restaurant yang Absurd

Bayangkan kamu punya restaurant. Setiap kali ada customer order:

RESTAURANT VERSI PHP-FPM:

Order #1 masuk: Nasi Goreng
├── Bongkar semua peralatan dapur
├── Bangun ulang kitchen dari nol
├── Setup kompor, wajan, spatula
├── Panaskan semua kompor
├── Ambil semua bahan dari gudang
├── Briefing ulang semua chef tentang menu
├── BARU MULAI MASAK nasi goreng
├── Serve ke customer
├── BONGKAR SEMUA dan bersihkan total
└── Kitchen kembali kosong

Order #2 masuk: Mie Goreng
├── Bongkar semua... (wait, kan tadi sudah dibongkar?)
├── Bangun ulang kitchen LAGI dari nol
├── Setup kompor, wajan, spatula LAGI
├── Panaskan semua kompor LAGI
├── ... (repeat everything)
└── Kitchen kembali kosong

Order #100 masuk:
└── MASIH HARUS REBUILD KITCHEN DARI NOL!

Gila? Ya. Tidak masuk akal? Absolutely.

Tapi itulah exactly yang PHP-FPM lakukan untuk setiap request.

The Bootstrap Tax Calculation

Mari hitung berapa "pajak" yang kita bayar untuk arsitektur ini:

ComponentLoad TimePer Request?Per 100 req/sec
Composer Autoloader15ms✅ Yes1.5 detik
Config Files (50+)25ms✅ Yes2.5 detik
Service Providers (60+)140ms✅ Yes14 detik
Route Registration40ms✅ Yes4 detik
Middleware Setup20ms✅ Yes2 detik
Facades & Container30ms✅ Yes3 detik
Error Handlers10ms✅ Yes1 detik
TOTAL BOOTSTRAP~300msEvery request30 detik

30 detik CPU time per second hanya untuk bootstrapping.

Belum termasuk actual work — query database, process logic, render views.

Dengan kalkulasi ini, di 100 requests per second:

  • CPU spend 30 detik untuk bootstrap
  • CPU cuma punya "sisa waktu" untuk actual work
  • Result: Bottleneck, queueing, timeouts

Why Scaling Doesn't Help

"Tapi kan bisa tambah server?"

Bisa. Tapi itu cuma memindahkan masalah.

SCALING HORIZONTAL:

1 Server (100 req/sec capacity):
└── Bootstrap overhead: 30 CPU-seconds/second
└── Status: Maxed out ❌

2 Servers (200 req/sec capacity):
└── Bootstrap overhead: 60 CPU-seconds/second (total)
└── Status: Handle 2x traffic, tapi juga 2x cost ❌

10 Servers:
└── Bootstrap overhead: 300 CPU-seconds/second
└── Status: Handle 10x traffic, 10x cost
└── Efficiency: SAMA BURUKNYA

Kamu tidak solve the problem.
Kamu cuma throwing money at it.

The Fundamental Question

Pertanyaan yang muncul di kepala saya:

Bagaimana kalau kita bootstrap Laravel SEKALI, simpan di memory, dan serve requests dari yang sudah di-boot?

Tidak rebuild kitchen setiap order. Kitchen sudah siap. Tinggal masak dan serve.

Itu exactly apa yang Laravel Octane lakukan.

"PHP-FPM itu seperti pegawai yang pulang ke rumah setelah selesaikan SATU task, lalu besok pagi harus commute lagi, setup meja lagi, buka laptop lagi, login lagi — untuk setiap task. Octane itu pegawai yang tetap di kantor, meja sudah siap, laptop sudah nyala, tinggal kerjakan task berikutnya. Obviously yang kedua lebih efisien."


Bagian 3: What is Laravel Octane

Setelah paham masalahnya, saatnya paham solusinya.

Octane Explained Simply

Laravel Octane adalah package official dari Laravel yang mengubah fundamental cara Laravel serve requests.

Konsep intinya sederhana:

  • PHP-FPM: Boot → Serve → Shutdown → Boot → Serve → Shutdown → ...
  • Octane: Boot ONCE → Serve → Serve → Serve → Serve → ... (keep in memory)

Octane mem-boot Laravel satu kali di awal, menyimpan application state di memory, dan kemudian serve ratusan atau ribuan requests tanpa perlu bootstrap ulang.

Visual: Octane Request Lifecycle

OCTANE SERVER STARTUP (Terjadi SEKALI):

┌─────────────────────────────────────────────────────────────────┐
│  php artisan octane:start                                       │
│         ↓                                                       │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  BOOTSTRAP (sekali saja, saat server start):              │  │
│  │                                                           │  │
│  │  ✓ Load Composer autoloader                               │  │
│  │  ✓ Load semua config files                                │  │
│  │  ✓ Create service container                               │  │
│  │  ✓ Register semua service providers                       │  │
│  │  ✓ Boot semua service providers                           │  │
│  │  ✓ Load dan cache routes                                  │  │
│  │  ✓ Setup middleware stack                                 │  │
│  │  ✓ Resolve facades                                        │  │
│  │                                                           │  │
│  │  Laravel sekarang READY dan IN MEMORY                     │  │
│  └───────────────────────────────────────────────────────────┘  │
│         ↓                                                       │
│  Spawn N workers (usually = CPU cores)                          │
│  Each worker = 1 Laravel instance in memory                     │
│  Server listening on port 8000                                  │
└─────────────────────────────────────────────────────────────────┘

SETIAP REQUEST (sangat cepat):

┌─────────────────────────────────────────────────────────────────┐
│  Request #1 masuk                                               │
│         ↓                                                       │
│  Worker #1 (sudah booted!) menerima request                     │
│         ↓                                                       │
│  Clone request-specific state                      (~2ms)       │
│         ↓                                                       │
│  Execute controller logic                          (~50ms)      │
│         ↓                                                       │
│  Send response                                                  │
│         ↓                                                       │
│  Reset request state (bukan full shutdown)         (~2ms)       │
│         ↓                                                       │
│  Worker siap untuk request berikutnya                           │
│                                                                 │
│  TOTAL: ~54ms (bandingkan dengan 350ms+ di PHP-FPM!)           │
└─────────────────────────────────────────────────────────────────┘

Request #2, #3, #4, ... #500: Same worker, same speed (~54ms each)

Setelah 500 requests: Worker di-restart untuk prevent memory leaks
Request #501: Worker baru (sudah di-pre-boot), tetap cepat

Analogi Updated: Restaurant yang Proper

RESTAURANT VERSI OCTANE:

PAGI HARI (Sebelum buka, sekali saja):
├── Setup kitchen lengkap
├── Panaskan semua kompor
├── Siapkan semua bahan di station
├── Brief semua chef tentang menu
├── Semua peralatan di posisi ready
└── KITCHEN SIAP!

Order #1 masuk: Nasi Goreng
├── Chef langsung ambil bahan (sudah ready di station)
├── Langsung masak (kompor sudah panas)
├── Serve ke customer
└── Station tetap ready untuk order berikutnya

Order #2 masuk: Mie Goreng
├── Chef langsung masak (tidak perlu setup ulang)
├── Serve ke customer
└── Station tetap ready

Order #100 masuk:
├── MASIH LANGSUNG BISA MASAK
└── Tidak perlu rebuild kitchen!

CLOSING TIME:
└── Baru bersihkan dan shutdown

Ini jauh lebih masuk akal, kan?

The Technology Behind Octane

Octane bukan magic. Octane adalah wrapper elegant di atas high-performance application servers yang sudah proven:

DriverTypeMaintained ByBest For
SwoolePHP Extension (C)Swoole TeamMaximum performance
OpenSwoolePHP Extension (Fork)OpenSwoole TeamAlternative to Swoole
RoadRunnerGo BinarySpiral ScoutEasy setup
FrankenPHPGo + PHPKévin DunglasModern alternative

Swoole vs RoadRunner — Detailed Comparison

Karena ini dua pilihan paling populer, mari bandingkan detail:

FeatureSwooleRoadRunner
TechnologyPHP Extension written in CGo binary
Installationpecl install swoole (compile)Download binary / Composer
DifficultyMedium (perlu compile extension)Easy (just download)
Raw Performance⭐⭐⭐⭐⭐ Fastest⭐⭐⭐⭐ Very Fast
Concurrent Tasks✅ Native support❌ Not available
Octane Cache✅ 2M operations/sec❌ Not available
Swoole Tables✅ Shared memory structures❌ Not available
Tick/Interval✅ Background task scheduling❌ Not available
WebSocket✅ Built-in support⚠️ Needs extension
Coroutines✅ Full async/await style❌ Not available
Windows Support❌ Linux/macOS only✅ Full support
Docker Setup⚠️ Needs PHP extension✅ Very easy
Memory Management⚠️ Needs attention✅ Generally easier
Learning CurveMedium-HighLow-Medium

Visual Comparison:

PERFORMANCE SPECTRUM:

PHP-FPM          RoadRunner              Swoole
    │                │                      │
    ▼                ▼                      ▼
────┼────────────────┼──────────────────────┼────────►
  Slow            Fast                 Fastest

100 req/s        700 req/s            1000+ req/s
(baseline)       (7x faster)          (10x faster)

EASE OF USE SPECTRUM:

Swoole           RoadRunner            PHP-FPM
    │                │                      │
    ▼                ▼                      ▼
────┼────────────────┼──────────────────────┼────────►
Complex         Moderate               Simple
(extension)     (binary)            (just works)

My Decision: Why Swoole for BuildWithAngga

Untuk BuildWithAngga, saya memilih Swoole karena beberapa alasan:

  1. Maximum Performance — Dengan ribuan students dan video streaming, saya butuh setiap millisecond.
  2. Concurrent Tasks — Bisa process video metadata, generate thumbnails, dan update analytics secara parallel.
  3. Octane Cache — Cache dengan 2 juta operasi per detik untuk real-time features seperti view counts dan progress tracking.
  4. Background Ticks — Schedule periodic tasks tanpa cron, seperti persist cache ke database setiap 10 detik.
  5. Server Environment — Semua server production Linux, jadi tidak ada Windows compatibility issue.

Tapi saya akan tunjukkan setup untuk keduanya di artikel ini, karena banyak situasi dimana RoadRunner lebih appropriate — terutama untuk:

  • Tim yang baru mulai dengan Octane
  • Development di Windows
  • Projek yang tidak butuh fitur Swoole-specific
  • Easier deployment dan maintenance

What to Expect: Realistic Numbers

Sebelum lanjut ke implementation, let me set realistic expectations:

ScenarioPHP-FPMOctane (RoadRunner)Octane (Swoole)
Simple API100 req/s600-800 req/s800-1200 req/s
Dynamic Page50 req/s300-400 req/s400-600 req/s
Heavy Page20 req/s150-200 req/s200-300 req/s
Response Time (avg)300-500ms50-100ms30-80ms

Important notes:

  • Angka di atas adalah typical improvements, bukan guaranteed
  • Actual results depend on your application
  • Database queries tetap butuh waktu yang sama
  • External API calls tetap butuh waktu yang sama
  • Yang di-eliminate adalah bootstrap overhead

"Octane bukan magic yang bikin semua code kamu instant. Octane menghilangkan bootstrap tax yang sebelumnya kamu bayar setiap request. Kalau bootstrap kamu 300ms dan code execution 200ms, total sebelumnya 500ms. Dengan Octane bisa jadi 200ms — improvement signifikan, tapi bukan infinite."


Next: Bagian 4 — Implementation dengan Swoole

Di bagian selanjutnya, saya akan walk through step-by-step setup Octane dengan Swoole untuk BuildWithAngga, termasuk configuration yang saya gunakan di production.


Bagian 4: Implementation — Setup Octane dengan Swoole

Sekarang hands-on. Saya akan walk through exactly bagaimana saya setup Octane dengan Swoole untuk BuildWithAngga.

Prerequisites

Sebelum mulai, pastikan:

# Check PHP version (8.2+ required, 8.3+ recommended)
php -v
# PHP 8.3.x

# Check OS (Swoole requires Linux/macOS)
uname -s
# Linux atau Darwin (macOS)

# Check Laravel version (10+ required)
php artisan --version
# Laravel Framework 12.x

# Pastikan sudah punya Redis (untuk session/cache fallback)
redis-cli ping
# PONG

Step 1: Install Swoole Extension

Swoole adalah PHP extension yang perlu di-compile. Ini yang paling "tricky" part, tapi sebenarnya straightforward:

Ubuntu/Debian:

# Install dependencies untuk compile
sudo apt-get update
sudo apt-get install -y \\
    php8.3-dev \\
    php8.3-curl \\
    php8.3-openssl \\
    libcurl4-openssl-dev \\
    libssl-dev

# Install Swoole via PECL
sudo pecl install swoole

# Selama instalasi, akan ada pertanyaan:
# enable sockets supports? [no] : yes
# enable openssl support? [no] : yes
# enable http2 support? [no] : yes
# enable curl support? [no] : yes
# enable cares support? [no] : no
# enable brotli support? [no] : no

# Add ke PHP CLI config
echo "extension=swoole.so" | sudo tee /etc/php/8.3/cli/conf.d/99-swoole.ini

# Verify installation
php -m | grep swoole
# Output: swoole

# Check Swoole info
php --ri swoole | head -20
# Swoole version, settings, etc.

macOS (untuk development):

# Via Homebrew + PECL
brew install [email protected]
pecl install swoole

# Atau via Homebrew tap
brew tap openswoole/swoole
brew install openswoole

Docker (recommended untuk consistency):

# Dockerfile
FROM php:8.3-cli

# Install dependencies
RUN apt-get update && apt-get install -y \\
    libcurl4-openssl-dev \\
    libssl-dev \\
    && pecl install swoole \\
    && docker-php-ext-enable swoole

# Verify
RUN php -m | grep swoole

Step 2: Install Laravel Octane Package

# Di project Laravel kamu
cd /var/www/buildwithangga

# Install Octane
composer require laravel/octane

# Run installer
php artisan octane:install

# Pilih driver: swoole
# Output:
#  Which application server you would like to use?
#  [0] frankenphp
#  [1] roadrunner
#  [2] swoole
# > 2

# Ini akan create: config/octane.php

Step 3: Configuration Deep-Dive

File config/octane.php adalah jantung konfigurasi Octane. Ini config yang saya gunakan untuk BuildWithAngga:

<?php

// config/octane.php

use Laravel\\Octane\\Events\\RequestHandled;
use Laravel\\Octane\\Events\\RequestReceived;
use Laravel\\Octane\\Events\\RequestTerminated;
use Laravel\\Octane\\Events\\TaskReceived;
use Laravel\\Octane\\Events\\WorkerStarting;
use Laravel\\Octane\\Events\\WorkerStopping;
use Laravel\\Octane\\Listeners\\DisconnectFromDatabases;
use Laravel\\Octane\\Listeners\\FlushTemporaryContainerInstances;
use Laravel\\Octane\\Listeners\\ReportException;

return [
    /*
    |--------------------------------------------------------------------------
    | Octane Server
    |--------------------------------------------------------------------------
    |
    | Driver yang digunakan. Options: swoole, roadrunner, frankenphp
    |
    */
    'server' => env('OCTANE_SERVER', 'swoole'),

    /*
    |--------------------------------------------------------------------------
    | Force HTTPS
    |--------------------------------------------------------------------------
    |
    | Set true jika behind reverse proxy dengan SSL termination
    |
    */
    'https' => env('OCTANE_HTTPS', true),

    /*
    |--------------------------------------------------------------------------
    | Octane Workers
    |--------------------------------------------------------------------------
    |
    | Jumlah workers. null = auto-detect (biasanya = CPU cores)
    | Untuk BuildWithAngga (8 core server): 8 workers
    |
    */
    'workers' => env('OCTANE_WORKERS', null),

    /*
    |--------------------------------------------------------------------------
    | Task Workers
    |--------------------------------------------------------------------------
    |
    | Workers untuk Concurrent Tasks (Swoole only)
    | 'auto' = half of main workers
    |
    */
    'task_workers' => env('OCTANE_TASK_WORKERS', 'auto'),

    /*
    |--------------------------------------------------------------------------
    | Max Requests
    |--------------------------------------------------------------------------
    |
    | Setelah N requests, worker di-restart untuk prevent memory leaks
    | 500 adalah sweet spot untuk most applications
    |
    */
    'max_requests' => env('OCTANE_MAX_REQUESTS', 500),

    /*
    |--------------------------------------------------------------------------
    | Services to Warm
    |--------------------------------------------------------------------------
    |
    | Services yang di-instantiate saat worker boot
    | Berguna untuk services yang heavy to construct
    |
    */
    'warm' => [
        // Core services yang sering dipakai
        \\Illuminate\\Contracts\\Http\\Kernel::class,
        \\Illuminate\\Contracts\\Console\\Kernel::class,
        \\Illuminate\\Contracts\\Debug\\ExceptionHandler::class,

        // Application services (BuildWithAngga specific)
        \\App\\Services\\CourseService::class,
        \\App\\Services\\VideoService::class,
        \\App\\Services\\PaymentService::class,
        \\App\\Services\\AnalyticsService::class,

        // Repositories
        \\App\\Repositories\\CourseRepository::class,
        \\App\\Repositories\\UserRepository::class,
    ],

    /*
    |--------------------------------------------------------------------------
    | Flush Bindings
    |--------------------------------------------------------------------------
    |
    | Bindings yang harus di-flush setiap request
    | Penting untuk avoid state leakage
    |
    */
    'flush' => [
        // Auth dan session harus fresh setiap request
        'auth',
        'auth.driver',
        'session',
        'session.store',
        'translator',
        'view.finder',
    ],

    /*
    |--------------------------------------------------------------------------
    | Garbage Collection
    |--------------------------------------------------------------------------
    |
    | Jalankan garbage collection setiap N requests
    | null = PHP handles it automatically
    |
    */
    'garbage' => 50,

    /*
    |--------------------------------------------------------------------------
    | Listeners
    |--------------------------------------------------------------------------
    |
    | Event listeners untuk berbagai Octane lifecycle events
    |
    */
    'listeners' => [
        WorkerStarting::class => [
            // Saat worker mulai
            // Good for: initialize connections, warm caches
        ],

        RequestReceived::class => [
            // Setiap request masuk, sebelum di-handle
        ],

        RequestHandled::class => [
            // Setelah request di-handle, sebelum response dikirim
        ],

        RequestTerminated::class => [
            // Setelah response dikirim ke client
            FlushTemporaryContainerInstances::class,
            DisconnectFromDatabases::class,

            // Custom: Reset application state
            \\App\\Listeners\\ResetApplicationState::class,
        ],

        TaskReceived::class => [
            ReportException::class,
        ],

        WorkerStopping::class => [
            // Saat worker mau stop
            // Good for: cleanup, flush buffers
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Swoole Tables
    |--------------------------------------------------------------------------
    |
    | Shared memory tables (Swoole only)
    | Bisa diakses dari semua workers
    |
    */
    'tables' => [
        // Real-time view counter
        'course_views' => [
            'columns' => [
                ['name' => 'views', 'type' => 'int'],
                ['name' => 'updated_at', 'type' => 'int'],
            ],
            'rows' => 10000, // Max 10k courses tracked
        ],

        // Active users tracking
        'active_users' => [
            'columns' => [
                ['name' => 'user_id', 'type' => 'int'],
                ['name' => 'last_seen', 'type' => 'int'],
            ],
            'rows' => 50000, // Max 50k concurrent users
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Ticks
    |--------------------------------------------------------------------------
    |
    | Enable tick functionality untuk periodic background tasks
    |
    */
    'tick' => true,

    /*
    |--------------------------------------------------------------------------
    | Swoole Options
    |--------------------------------------------------------------------------
    |
    | Low-level Swoole configuration
    |
    */
    'swoole' => [
        'options' => [
            // Logging
            'log_file' => storage_path('logs/swoole_http.log'),
            'log_level' => env('APP_DEBUG') ? SWOOLE_LOG_DEBUG : SWOOLE_LOG_WARNING,

            // Package size (untuk file uploads)
            'package_max_length' => 20 * 1024 * 1024, // 20MB

            // Buffer sizes
            'buffer_output_size' => 2 * 1024 * 1024, // 2MB
            'socket_buffer_size' => 128 * 1024 * 1024, // 128MB

            // Timeouts
            'max_wait_time' => 60,

            // Document root untuk static files (optional)
            'document_root' => public_path(),
            'enable_static_handler' => false, // Nginx handles static files
        ],
    ],
];

Step 4: Environment Configuration

# .env

# Octane Configuration
OCTANE_SERVER=swoole
OCTANE_WORKERS=8
OCTANE_TASK_WORKERS=4
OCTANE_MAX_REQUESTS=500
OCTANE_HTTPS=true

# Session harus pakai Redis (bukan file/database)
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=true

# Cache juga Redis
CACHE_STORE=redis

# Queue Redis
QUEUE_CONNECTION=redis

# Redis Configuration
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=null
REDIS_DB=0
REDIS_CACHE_DB=1
REDIS_SESSION_DB=2
REDIS_QUEUE_DB=3

Step 5: Create Reset State Listener

Ini penting untuk avoid state leakage:

<?php

// app/Listeners/ResetApplicationState.php

namespace App\\Listeners;

use Laravel\\Octane\\Events\\RequestTerminated;
use Illuminate\\Support\\Facades\\Auth;

class ResetApplicationState
{
    public function handle(RequestTerminated $event): void
    {
        // Reset authentication state
        Auth::forgetGuards();

        // Reset any static properties in your services
        // Example:
        // \\App\\Services\\SomeService::reset();

        // Clear any request-scoped caches
        // app('request.cache')->flush();
    }
}

Register di config/octane.php listeners (sudah include di config di atas).

Step 6: Running Octane

# Development mode (dengan hot reload)
php artisan octane:start --watch

# Production mode
php artisan octane:start \\
    --host=127.0.0.1 \\
    --port=8000 \\
    --workers=8 \\
    --task-workers=4 \\
    --max-requests=500

# Check status
php artisan octane:status

# Reload workers (tanpa downtime)
php artisan octane:reload

# Stop server
php artisan octane:stop

Step 7: Nginx Configuration

Nginx sebagai reverse proxy di depan Octane:

# /etc/nginx/sites-available/buildwithangga

upstream octane {
    server 127.0.0.1:8000;
    keepalive 64;
}

server {
    listen 80;
    listen [::]:80;
    server_name buildwithangga.com www.buildwithangga.com;

    # Redirect HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name buildwithangga.com www.buildwithangga.com;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/buildwithangga.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/buildwithangga.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    root /var/www/buildwithangga/public;
    index index.php;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript
               application/x-javascript application/xml
               application/javascript application/json;

    # Static files served directly by Nginx (bypass Octane)
    location ~* \\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|mp4|webm)$ {
        expires 1y;
        access_log off;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

    # Everything else goes to Octane
    location / {
        proxy_pass <http://octane>;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header Connection "";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 300s;

        # Buffering
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }
}
# Test config
sudo nginx -t

# Reload
sudo systemctl reload nginx

Step 8: Supervisor Configuration (Process Manager)

Supervisor memastikan Octane tetap jalan dan auto-restart jika crash:

# /etc/supervisor/conf.d/octane.conf

[program:octane]
process_name=%(program_name)s
command=php /var/www/buildwithangga/artisan octane:start --host=127.0.0.1 --port=8000 --workers=8 --task-workers=4 --max-requests=500
directory=/var/www/buildwithangga
user=www-data
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
redirect_stderr=true
stdout_logfile=/var/log/octane.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopwaitsecs=60
stopsignal=SIGTERM
# Apply configuration
sudo supervisorctl reread
sudo supervisorctl update

# Start Octane
sudo supervisorctl start octane

# Check status
sudo supervisorctl status octane

# View logs
sudo tail -f /var/log/octane.log

Step 9: Verify Everything Works

# Check Octane is running
php artisan octane:status
# Output: Octane server is running.

# Test response
curl -I <http://127.0.0.1:8000>
# HTTP/1.1 200 OK

# Check through Nginx
curl -I <https://buildwithangga.com>
# HTTP/2 200

# Quick benchmark
ab -n 1000 -c 50 <https://buildwithangga.com/>
# Should see ~500-1000 req/sec

Bagian 5: Implementation — RoadRunner Alternative

Kalau Swoole terasa terlalu complex (compile extension, etc.), RoadRunner adalah alternatif yang lebih mudah dengan performance yang tetap excellent.

Why Choose RoadRunner

CHOOSE ROADRUNNER WHEN:
✓ Tim belum familiar dengan PHP extensions
✓ Development di Windows
✓ Ingin setup yang lebih simple
✓ Tidak butuh Concurrent Tasks
✓ Tidak butuh Swoole Tables
✓ Docker-first deployment
✓ Lebih prefer Go ecosystem

Installation

# Install Octane (sama dengan Swoole)
composer require laravel/octane

# Run installer, pilih RoadRunner
php artisan octane:install
# Pilih: roadrunner

# Ini akan:
# 1. Download RoadRunner binary ke ./rr
# 2. Create config/octane.php
# 3. Create .rr.yaml

RoadRunner Configuration

# .rr.yaml

version: "3"

server:
  command: "php artisan octane:start --server=roadrunner --host=127.0.0.1 --port=8000"
  relay: pipes

http:
  address: "0.0.0.0:8000"

  middleware:
    - gzip
    - static

  static:
    dir: "public"
    forbid:
      - ".php"
      - ".htaccess"

  pool:
    num_workers: 8
    max_jobs: 500
    supervisor:
      watch_tick: 1s
      ttl: 0s
      idle_ttl: 10s
      max_worker_memory: 128

  headers:
    response:
      X-Powered-By: "BuildWithAngga"

logs:
  mode: production
  level: warn
  output: stderr
  err_output: stderr

Running RoadRunner

# Development
php artisan octane:start --server=roadrunner --watch

# Production via RoadRunner binary directly
./rr serve

# Atau via artisan
php artisan octane:start --server=roadrunner --workers=8

# Status
php artisan octane:status

Supervisor for RoadRunner

# /etc/supervisor/conf.d/octane-roadrunner.conf

[program:octane]
process_name=%(program_name)s
command=/var/www/buildwithangga/rr serve
directory=/var/www/buildwithangga
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/octane.log
stopwaitsecs=60

When to Use Which

ScenarioRecommendation
Maximum performance neededSwoole
Need concurrent tasksSwoole
Need in-memory cache (2M ops/sec)Swoole
Simple setup priorityRoadRunner
Windows developmentRoadRunner
New to OctaneRoadRunner (start here)
Enterprise/Production at scaleSwoole
Docker-based deploymentEither (RoadRunner slightly easier)

Bagian 6: The Gotchas — Mistakes I Made

Ini bagian paling penting. Octane bukan drop-in replacement. Ada fundamental changes dalam cara aplikasi bekerja, dan saya belajar ini the hard way.

Gotcha #1: Static Properties Persist Across Requests

The Problem:

// ❌ CODE YANG BERMASALAH

class CartService
{
    private static array $items = [];

    public function addItem(string $productId): void
    {
        self::$items[] = $productId;
    }

    public function getItems(): array
    {
        return self::$items;
    }
}

// Request 1: User A adds item "course-laravel"
// self::$items = ['course-laravel']

// Request 2: User B (DIFFERENT USER!) adds item "course-vue"
// self::$items = ['course-laravel', 'course-vue']  ← WRONG!
// User B sees User A's cart items!

// Request 3: User C just views cart
// self::$items STILL = ['course-laravel', 'course-vue']
// User C sees items from both A and B!

Impact di BuildWithAngga: User melihat cart orang lain. Security nightmare.

The Fix:

// ✅ SOLUSI 1: Use scoped binding

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->scoped(CartService::class, function () {
        return new CartService();
    });
}

// Setiap request dapat instance baru

// ✅ SOLUSI 2: Store di session instead of static

class CartService
{
    public function addItem(string $productId): void
    {
        $items = session('cart.items', []);
        $items[] = $productId;
        session(['cart.items' => $items]);
    }

    public function getItems(): array
    {
        return session('cart.items', []);
    }
}

// ✅ SOLUSI 3: Reset di listener

// config/octane.php listeners
RequestTerminated::class => [
    function ($event) {
        CartService::reset();
    },
],

// Di service
class CartService
{
    private static array $items = [];

    public static function reset(): void
    {
        self::$items = [];
    }
}

Gotcha #2: Resolving Request/Auth in Constructor

The Problem:

// ❌ CODE YANG BERMASALAH

class CourseController extends Controller
{
    private User $user;
    private int $userId;

    public function __construct()
    {
        // Constructor runs ONCE saat worker boot
        // BUKAN setiap request!
        $this->user = auth()->user();     // NULL atau user pertama!
        $this->userId = auth()->id();      // 0 atau ID user pertama!
    }

    public function show(Course $course)
    {
        // $this->userId adalah ID dari REQUEST PERTAMA
        // bukan current request!
        return view('course', [
            'course' => $course,
            'progress' => $this->getProgress($this->userId), // WRONG USER!
        ]);
    }
}

Impact di BuildWithAngga: User melihat progress orang lain. Privacy violation.

The Fix:

// ✅ FIX: Resolve di method, bukan constructor

class CourseController extends Controller
{
    public function show(Course $course, Request $request)
    {
        // Resolve per-request, selalu correct
        $user = $request->user();
        $userId = $user?->id;

        return view('course', [
            'course' => $course,
            'progress' => $this->getProgress($userId),
        ]);
    }
}

// ✅ ATAU: Use method injection

class CourseController extends Controller
{
    public function show(Course $course)
    {
        // auth() helper works correctly in method context
        $userId = auth()->id();

        return view('course', [
            'course' => $course,
            'progress' => $this->getProgress($userId),
        ]);
    }
}

Gotcha #3: Config/ENV Changes Not Reflected

The Problem:

# Saya update .env
APP_DEBUG=false
[email protected]

# Restart Octane
php artisan octane:reload

# Tapi masih pakai config lama!
# Karena config di-cache saat worker boot

The Fix:

# Untuk .env changes, perlu FULL restart, bukan reload

# Step 1: Clear config cache
php artisan config:clear

# Step 2: Rebuild config cache
php artisan config:cache

# Step 3: STOP and START (bukan reload)
php artisan octane:stop
php artisan octane:start

# ATAU via supervisor
sudo supervisorctl restart octane

Best Practice untuk Deployment:

# deployment.sh

#!/bin/bash
set -e

# Pull latest code
git pull origin main

# Install dependencies
composer install --optimize-autoloader --no-dev

# Clear all caches
php artisan config:clear
php artisan route:clear
php artisan view:clear

# Rebuild caches
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Restart Octane (full restart untuk env changes)
php artisan octane:stop || true
sleep 2
sudo supervisorctl start octane

echo "Deployment complete!"

Gotcha #4: Memory Leaks from Unbounded Collections

The Problem:

// ❌ CODE YANG BERMASALAH

class AnalyticsService
{
    private array $events = [];

    public function track(string $event, array $data): void
    {
        // Array grows with every request
        $this->events[] = [
            'event' => $event,
            'data' => $data,
            'time' => now(),
        ];

        // Tidak pernah di-clear!
        // Request 1: 1 event
        // Request 100: 100 events
        // Request 10000: 10000 events ← MEMORY EXPLODES
    }
}

Impact: Worker memory usage naik terus sampai crash atau di-restart.

The Fix:

// ✅ SOLUSI 1: Flush periodically

class AnalyticsService
{
    private array $events = [];
    private int $maxEvents = 100;

    public function track(string $event, array $data): void
    {
        $this->events[] = [
            'event' => $event,
            'data' => $data,
            'time' => now(),
        ];

        // Auto-flush when reaching threshold
        if (count($this->events) >= $this->maxEvents) {
            $this->flush();
        }
    }

    public function flush(): void
    {
        if (empty($this->events)) {
            return;
        }

        // Send to analytics service atau database
        dispatch(new ProcessAnalyticsEvents($this->events));

        // Clear the array
        $this->events = [];
    }
}

// ✅ SOLUSI 2: Use scoped binding
$this->app->scoped(AnalyticsService::class);

// ✅ SOLUSI 3: Flush di request termination
// config/octane.php
RequestTerminated::class => [
    function ($event) {
        app(AnalyticsService::class)->flush();
    },
],

Gotcha #5: Database "MySQL Has Gone Away"

The Problem:

Production logs:
[2024-01-15 03:45:22] SQLSTATE[HY000]: General error: 2006 MySQL server has gone away

# Kenapa?
# - Octane workers hidup lama (handle ratusan requests)
# - MySQL close idle connections setelah wait_timeout (default 8 jam)
# - Worker masih hold reference ke dead connection
# - Next query fails

The Fix:

// config/database.php

'mysql' => [
    // ... other config

    'options' => [
        // Jangan gunakan persistent connections dengan Octane
        PDO::ATTR_PERSISTENT => false,

        // Timeout settings
        PDO::ATTR_TIMEOUT => 10,
    ],
],

// config/octane.php
'listeners' => [
    RequestTerminated::class => [
        // Disconnect setelah setiap request
        \\Laravel\\Octane\\Listeners\\DisconnectFromDatabases::class,
    ],
],

Alternative: Adjust MySQL wait_timeout:

-- Increase wait_timeout (tapi ini workaround, bukan fix)
SET GLOBAL wait_timeout = 28800; -- 8 hours
SET GLOBAL interactive_timeout = 28800;

Gotcha #6: Third-Party Package Incompatibility

Packages yang saya temukan bermasalah:

❌ Laravel Telescope (memory leaks tanpa proper config)
   Fix: Disable di production atau configure properly

   // config/telescope.php
   'enabled' => env('TELESCOPE_ENABLED', false),

❌ Laravel Debugbar (breaks under Octane)
   Fix: Disable di production

   // .env (production)
   DEBUGBAR_ENABLED=false

❌ Some PDF generators (static state issues)
   Fix: Use queue untuk PDF generation

❌ Some old payment gateways (singleton issues)
   Fix: Check documentation, wrap dengan scoped binding

❌ Inertia.js (older versions had issues)
   Fix: Update to latest version (v1.0+)

How to Check Package Compatibility:

# Search GitHub issues
# Go to package repo, search "octane"
# Example: github.com/spatie/laravel-permission/issues?q=octane

# Test thoroughly
# Run load tests dengan package enabled
# Monitor memory usage over time

Prevention Checklist

Sebelum deploy Octane ke production:

CODE REVIEW CHECKLIST:
□ No static properties storing request-specific data
□ No auth/request resolved in constructors
□ No unbounded arrays/collections in singletons
□ All services either stateless or properly scoped
□ No file handles kept open across requests

PACKAGE AUDIT:
□ All packages tested dengan Octane
□ Telescope disabled atau properly configured
□ Debugbar disabled di production
□ Payment gateways tested
□ PDF generators tested

INFRASTRUCTURE:
□ Session driver = redis (bukan file/database)
□ Cache driver = redis
□ Queue driver = redis
□ Database connections properly managed
□ Supervisor configured
□ Nginx configured
□ Log rotation setup

TESTING:
□ Load testing completed
□ Memory leak testing (run 1+ jam)
□ Auth/session tested dengan multiple users
□ File upload tested
□ All critical flows tested

"Octane mengubah PHP dari stateless ke stateful. Ini bukan upgrade — ini paradigm shift. Kamu sekarang harus THINK about state, memory, dan lifecycle. Kalau kamu skip bagian gotchas ini, kamu akan punya production incidents yang sangat painful."


Next: Bagian 7-8 — Octane-Specific Optimizations dan Results

Di bagian selanjutnya, saya akan tunjukkan optimizations khusus Octane (Concurrent Tasks, Octane Cache, Ticks) dan hasil benchmark sebelum/sesudah di BuildWithAngga.


Bagian 7: Octane-Specific Optimizations

Setelah Octane running stable, saatnya maximize performance dengan fitur-fitur yang hanya available di Octane — khususnya dengan Swoole.

Concurrent Tasks — Parallel Processing

Ini salah satu fitur paling powerful di Swoole. Execute multiple operations simultaneously instead of sequentially.

Case di BuildWithAngga: Course detail page perlu load banyak data.

// ❌ BEFORE: Sequential (lambat)
// app/Http/Controllers/CourseController.php

public function show(Course $course)
{
    // Setiap query harus selesai sebelum yang berikutnya mulai
    $courseDetails = $this->courseService->getDetails($course);      // 80ms
    $instructor = $this->instructorService->get($course->user_id);   // 60ms
    $curriculum = $this->curriculumService->get($course->id);        // 100ms
    $reviews = $this->reviewService->getLatest($course->id, 10);     // 70ms
    $relatedCourses = $this->courseService->getRelated($course);     // 90ms
    $studentProgress = $this->progressService->get($course, auth()->user()); // 50ms
    $discussionCount = $this->discussionService->count($course->id); // 40ms

    // TOTAL: 80+60+100+70+90+50+40 = 490ms
    // Padahal semua query INDEPENDENT satu sama lain!

    return view('courses.show', compact(
        'courseDetails', 'instructor', 'curriculum',
        'reviews', 'relatedCourses', 'studentProgress', 'discussionCount'
    ));
}
// ✅ AFTER: Concurrent dengan Octane (cepat!)
// app/Http/Controllers/CourseController.php

use Laravel\\Octane\\Facades\\Octane;

public function show(Course $course)
{
    $user = auth()->user();

    // Semua operations jalan PARALLEL
    [
        $courseDetails,
        $instructor,
        $curriculum,
        $reviews,
        $relatedCourses,
        $studentProgress,
        $discussionCount,
    ] = Octane::concurrently([
        fn () => $this->courseService->getDetails($course),
        fn () => $this->instructorService->get($course->user_id),
        fn () => $this->curriculumService->get($course->id),
        fn () => $this->reviewService->getLatest($course->id, 10),
        fn () => $this->courseService->getRelated($course),
        fn () => $this->progressService->get($course, $user),
        fn () => $this->discussionService->count($course->id),
    ]);

    // TOTAL: max(80,60,100,70,90,50,40) = 100ms
    // Hanya selambat operation yang paling lama!

    return view('courses.show', compact(
        'courseDetails', 'instructor', 'curriculum',
        'reviews', 'relatedCourses', 'studentProgress', 'discussionCount'
    ));
}

// IMPROVEMENT: 490ms → 100ms = 4.9x faster!

Visual Explanation:

SEQUENTIAL (Before):
Timeline: 0ms ──────────────────────────────────────────────── 490ms

courseDetails  ████████ (80ms)
instructor              ██████ (60ms)
curriculum                     ██████████ (100ms)
reviews                                   ███████ (70ms)
relatedCourses                                   █████████ (90ms)
studentProgress                                            █████ (50ms)
discussionCount                                                 ████ (40ms)
                                                                    │
                                                                    ▼
                                                              TOTAL: 490ms

CONCURRENT (After):
Timeline: 0ms ────────────────────── 100ms

courseDetails  ████████ (80ms)
instructor     ██████ (60ms)
curriculum     ██████████ (100ms)  ← Slowest determines total
reviews        ███████ (70ms)
relatedCourses █████████ (90ms)
studentProgress █████ (50ms)
discussionCount ████ (40ms)
               │
               ▼
         TOTAL: 100ms (5x faster!)

Real Implementation di BuildWithAngga:

// app/Services/CoursePageService.php

namespace App\\Services;

use App\\Models\\Course;
use App\\Models\\User;
use Laravel\\Octane\\Facades\\Octane;
use Illuminate\\Support\\Facades\\Cache;

class CoursePageService
{
    public function __construct(
        private CourseService $courseService,
        private InstructorService $instructorService,
        private CurriculumService $curriculumService,
        private ReviewService $reviewService,
        private ProgressService $progressService,
        private DiscussionService $discussionService,
    ) {}

    public function getPageData(Course $course, ?User $user): array
    {
        // Cache key untuk data yang jarang berubah
        $cacheKey = "course:{$course->id}:page_data";

        // Check cache dulu
        $cachedData = Cache::get($cacheKey);

        if ($cachedData && !$user) {
            // Guest users dapat cached data
            return $cachedData;
        }

        // Untuk logged-in users atau cache miss, load fresh
        $tasks = [
            'details' => fn () => $this->courseService->getDetails($course),
            'instructor' => fn () => $this->instructorService->get($course->user_id),
            'curriculum' => fn () => $this->curriculumService->get($course->id),
            'reviews' => fn () => $this->reviewService->getLatest($course->id, 10),
            'related' => fn () => $this->courseService->getRelated($course),
            'discussions' => fn () => $this->discussionService->count($course->id),
        ];

        // User-specific data (tidak di-cache)
        if ($user) {
            $tasks['progress'] = fn () => $this->progressService->get($course, $user);
            $tasks['enrolled'] = fn () => $user->enrolledCourses()->where('course_id', $course->id)->exists();
        }

        // Execute all concurrently
        $results = Octane::concurrently($tasks);

        // Cache non-user-specific data
        $cacheableData = array_diff_key($results, ['progress' => true, 'enrolled' => true]);
        Cache::put($cacheKey, $cacheableData, now()->addMinutes(15));

        return $results;
    }
}

Octane Cache — Lightning Fast In-Memory Cache

Swoole menyediakan in-memory cache yang bisa mencapai 2 juta operasi per detik. Jauh lebih cepat dari Redis untuk hot data.

use Illuminate\\Support\\Facades\\Cache;

// ✅ Octane Cache untuk frequently accessed data

// Store
Cache::store('octane')->put('active_users_count', $count, 60);

// Retrieve (microseconds, bukan milliseconds!)
$count = Cache::store('octane')->get('active_users_count');

// Increment (atomic, thread-safe)
Cache::store('octane')->increment('page_views');

// Remember pattern
$popularCourses = Cache::store('octane')->remember('popular_courses', 60, function () {
    return Course::popular()->limit(10)->get();
});

Real Use Cases di BuildWithAngga:

// app/Services/RealTimeStatsService.php

namespace App\\Services;

use Illuminate\\Support\\Facades\\Cache;

class RealTimeStatsService
{
    /**
     * Track course view (called on every course page load)
     */
    public function trackView(int $courseId): void
    {
        // Increment in Octane cache (super fast)
        $key = "views:course:{$courseId}";
        Cache::store('octane')->increment($key);

        // Track timestamp for batching
        $batchKey = "views:pending";
        $pending = Cache::store('octane')->get($batchKey, []);
        $pending[$courseId] = ($pending[$courseId] ?? 0) + 1;
        Cache::store('octane')->put($batchKey, $pending, 300);
    }

    /**
     * Get real-time view count (for display)
     */
    public function getViewCount(int $courseId): int
    {
        $cached = Cache::store('octane')->get("views:course:{$courseId}", 0);
        $persisted = Cache::store('redis')->get("views:persisted:{$courseId}", 0);

        return $cached + $persisted;
    }

    /**
     * Track active users (for "X people viewing" feature)
     */
    public function trackActiveUser(int $courseId, int $userId): void
    {
        $key = "active:course:{$courseId}";
        $activeUsers = Cache::store('octane')->get($key, []);

        $activeUsers[$userId] = time();

        // Remove users inactive > 5 minutes
        $activeUsers = array_filter($activeUsers, fn ($time) =>
            time() - $time < 300
        );

        Cache::store('octane')->put($key, $activeUsers, 600);
    }

    /**
     * Get active user count
     */
    public function getActiveUserCount(int $courseId): int
    {
        $activeUsers = Cache::store('octane')->get("active:course:{$courseId}", []);
        return count($activeUsers);
    }
}

⚠️ IMPORTANT WARNING:

// Octane Cache data HILANG saat server restart!

// ❌ JANGAN gunakan untuk:
// - User sessions (pakai Redis)
// - Shopping cart data (pakai Redis)
// - Anything yang harus persist

// ✅ GUNAKAN untuk:
// - Real-time counters
// - Temporary rate limiting
// - Hot data yang bisa di-rebuild
// - View counts sebelum di-persist ke database

Ticks and Intervals — Background Tasks

Dengan Swoole, kita bisa run periodic tasks tanpa cron:

// app/Providers/OctaneServiceProvider.php

namespace App\\Providers;

use App\\Services\\RealTimeStatsService;
use Illuminate\\Support\\Facades\\Cache;
use Illuminate\\Support\\ServiceProvider;
use Laravel\\Octane\\Facades\\Octane;

class OctaneServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Hanya register ticks saat running di Octane
        if (!$this->app->bound('octane')) {
            return;
        }

        $this->registerTicks();
    }

    private function registerTicks(): void
    {
        // Persist view counts ke database setiap 30 detik
        Octane::tick('persist-views', function () {
            $pending = Cache::store('octane')->pull('views:pending', []);

            if (empty($pending)) {
                return;
            }

            foreach ($pending as $courseId => $count) {
                // Update database
                \\DB::table('courses')
                    ->where('id', $courseId)
                    ->increment('views', $count);

                // Update Redis (persisted count)
                Cache::store('redis')->increment("views:persisted:{$courseId}", $count);
            }

            logger()->info('Persisted view counts', ['count' => count($pending)]);

        }, seconds: 30);

        // Cleanup expired active user sessions setiap menit
        Octane::tick('cleanup-active-users', function () {
            // Cleanup logic
            $keys = Cache::store('octane')->get('active:keys', []);

            foreach ($keys as $key) {
                $activeUsers = Cache::store('octane')->get($key, []);
                $cleaned = array_filter($activeUsers, fn ($time) =>
                    time() - $time < 300
                );

                if (count($cleaned) !== count($activeUsers)) {
                    Cache::store('octane')->put($key, $cleaned, 600);
                }
            }

        }, seconds: 60);

        // Health check setiap 5 menit
        Octane::tick('health-check', function () {
            // Check database connection
            try {
                \\DB::select('SELECT 1');
            } catch (\\Exception $e) {
                logger()->error('Database health check failed', [
                    'error' => $e->getMessage(),
                ]);
            }

            // Check Redis connection
            try {
                Cache::store('redis')->put('health', now()->timestamp, 60);
            } catch (\\Exception $e) {
                logger()->error('Redis health check failed', [
                    'error' => $e->getMessage(),
                ]);
            }

        }, seconds: 300);
    }
}

Register provider:

// bootstrap/providers.php

return [
    App\\Providers\\AppServiceProvider::class,
    App\\Providers\\OctaneServiceProvider::class, // Add this
];

Swoole Tables — Shared Memory Structures

Untuk data yang perlu diakses dari semua workers:

// config/octane.php

'tables' => [
    'course_stats' => [
        'columns' => [
            ['name' => 'views', 'type' => 'int', 'size' => 8],
            ['name' => 'enrollments', 'type' => 'int', 'size' => 8],
            ['name' => 'last_updated', 'type' => 'int', 'size' => 8],
        ],
        'rows' => 10000, // Pre-allocate for 10k courses
    ],

    'rate_limits' => [
        'columns' => [
            ['name' => 'count', 'type' => 'int'],
            ['name' => 'window_start', 'type' => 'int'],
        ],
        'rows' => 100000, // 100k rate limit entries
    ],
],
// Menggunakan Swoole Table

use Laravel\\Octane\\Facades\\Octane;

// Get table instance
$table = Octane::table('course_stats');

// Set data
$table->set('course:123', [
    'views' => 5000,
    'enrollments' => 500,
    'last_updated' => time(),
]);

// Get data
$stats = $table->get('course:123');
// ['views' => 5000, 'enrollments' => 500, 'last_updated' => ...]

// Increment
$table->incr('course:123', 'views', 1);

// Check exists
if ($table->exists('course:123')) {
    // ...
}

// Delete
$table->del('course:123');

// Iterate all entries
foreach ($table as $key => $value) {
    echo "{$key}: " . json_encode($value) . "\\n";
}

Warming Services for Instant First Requests

Pre-instantiate heavy services saat worker boot:

// config/octane.php

'warm' => [
    // Framework services
    \\Illuminate\\Contracts\\Http\\Kernel::class,
    \\Illuminate\\Contracts\\Console\\Kernel::class,

    // Application services (heavy constructors)
    \\App\\Services\\CourseService::class,
    \\App\\Services\\VideoService::class,
    \\App\\Services\\PaymentService::class,
    \\App\\Services\\SearchService::class,

    // Services with database connections
    \\App\\Repositories\\CourseRepository::class,
    \\App\\Repositories\\UserRepository::class,

    // External service clients
    \\App\\Services\\Integrations\\StripeService::class,
    \\App\\Services\\Integrations\\VimeoService::class,
],

Impact: First request ke worker baru sama cepatnya dengan subsequent requests — tidak ada cold start penalty.

Summary: Optimizations Applied to BuildWithAngga

OptimizationBeforeAfterImprovement
Concurrent Tasks490ms (sequential)100ms (parallel)4.9x faster
Octane Cache5ms (Redis)0.1ms (memory)50x faster
Background TicksCron every minuteTick every 30sSmoother, realtime
Service Warming50ms cold start0ms (pre-warmed)Instant
Swoole TablesRedis lookupsIn-memory10x faster

Bagian 8: The Results — Before and After

Sekarang moment of truth. Bagaimana performance BuildWithAngga sebelum dan sesudah Octane?

Benchmark Methodology

TEST SETUP:
├── Tool: k6, wrk, dan custom benchmarks
├── Server: Same production server (8 core, 16GB RAM)
├── Database: Same MySQL instance
├── Cache: Same Redis instance
├── Test duration: 5 menit per scenario
├── Concurrent users: 50, 100, 200, 500
├── Tested pages: Homepage, Course List, Course Detail, API

WHAT CHANGED:
├── PHP-FPM → Octane + Swoole
├── Added Concurrent Tasks
├── Added Octane Cache for hot data
├── Added Background Ticks
└── Applied all optimizations dari Bagian 7

Benchmark Results

Homepage (Marketing Landing Page):

HOMEPAGE BENCHMARK (200 concurrent users, 5 minutes):

┌────────────────────────────────────────────────────────────────┐
│                         PHP-FPM                                │
├────────────────────────────────────────────────────────────────┤
│  Requests/sec:        89                                       │
│  Avg Response:        1,850ms                                  │
│  p50 Response:        1,620ms                                  │
│  p95 Response:        3,450ms                                  │
│  p99 Response:        5,200ms                                  │
│  Error Rate:          3.2%                                     │
│  CPU Usage:           94%                                      │
└────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│                      OCTANE + SWOOLE                           │
├────────────────────────────────────────────────────────────────┤
│  Requests/sec:        892                                      │
│  Avg Response:        185ms                                    │
│  p50 Response:        142ms                                    │
│  p95 Response:        380ms                                    │
│  p99 Response:        520ms                                    │
│  Error Rate:          0.1%                                     │
│  CPU Usage:           42%                                      │
└────────────────────────────────────────────────────────────────┘

IMPROVEMENT: 10x throughput, 10x faster response, 50% less CPU

Course Detail Page (Heavy Page):

COURSE DETAIL BENCHMARK (100 concurrent users, 5 minutes):

┌────────────────────────────────────────────────────────────────┐
│                         PHP-FPM                                │
├────────────────────────────────────────────────────────────────┤
│  Requests/sec:        38                                       │
│  Avg Response:        2,450ms                                  │
│  p95 Response:        4,800ms                                  │
│  Error Rate:          5.1%                                     │
└────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│                      OCTANE + SWOOLE                           │
│                   (with Concurrent Tasks)                      │
├────────────────────────────────────────────────────────────────┤
│  Requests/sec:        485                                      │
│  Avg Response:        195ms                                    │
│  p95 Response:        410ms                                    │
│  Error Rate:          0.05%                                    │
└────────────────────────────────────────────────────────────────┘

IMPROVEMENT: 12.7x throughput, 12.5x faster response

API Endpoints (JSON Responses):

API BENCHMARK (/api/courses, 200 concurrent):

PHP-FPM:
├── Requests/sec: 142
├── Avg Response: 680ms
└── Error Rate: 2.8%

OCTANE + SWOOLE:
├── Requests/sec: 1,450
├── Avg Response: 65ms
└── Error Rate: 0.02%

IMPROVEMENT: 10.2x throughput, 10.5x faster

Complete Before/After Comparison

╔══════════════════════════════════════════════════════════════════════════╗
║                   BUILDWITHANGGA PERFORMANCE TRANSFORMATION              ║
╠══════════════════════════════════════════════════════════════════════════╣
║                                                                          ║
║   METRIC                      PHP-FPM         OCTANE         CHANGE      ║
║   ────────────────────────────────────────────────────────────────────   ║
║                                                                          ║
║   RESPONSE TIMES                                                         ║
║   Homepage (avg)              1,850ms         185ms          ↓ 90%       ║
║   Course List (avg)           2,100ms         210ms          ↓ 90%       ║
║   Course Detail (avg)         2,450ms         195ms          ↓ 92%       ║
║   Video Player Init           3,200ms         280ms          ↓ 91%       ║
║   API Endpoints (avg)         680ms           65ms           ↓ 90%       ║
║   Checkout Page               1,900ms         180ms          ↓ 91%       ║
║                                                                          ║
║   THROUGHPUT                                                             ║
║   Homepage (req/sec)          89              892            ↑ 10x       ║
║   Course Detail (req/sec)     38              485            ↑ 12.7x     ║
║   API (req/sec)               142             1,450          ↑ 10.2x     ║
║   Max Concurrent Users        ~200            ~2,000         ↑ 10x       ║
║                                                                          ║
║   RELIABILITY                                                            ║
║   Error Rate (peak)           5.1%            0.05%          ↓ 99%       ║
║   Timeout Rate                2.3%            0.01%          ↓ 99.5%     ║
║   Uptime                      99.2%           99.95%         ↑ 0.75%     ║
║                                                                          ║
║   SERVER RESOURCES                                                       ║
║   CPU Usage (peak)            94%             42%            ↓ 55%       ║
║   Memory Usage                14GB            10GB           ↓ 29%       ║
║   PHP Workers Needed          32              8              ↓ 75%       ║
║                                                                          ║
╚══════════════════════════════════════════════════════════════════════════╝

Business Impact — The Numbers That Matter

Performance improvement is nice, tapi yang penting adalah business impact:

BUSINESS METRICS (3 Months After Octane):

User Experience:
├── Bounce Rate:           45% → 18%         (↓ 60%)
├── Session Duration:      2.1 min → 7.2 min (↑ 243%)
├── Pages per Session:     2.3 → 8.5         (↑ 270%)
├── Mobile Experience:     "Acceptable" → "Fast" (Lighthouse)
└── Core Web Vitals:       Failed → Passed

Conversion:
├── Homepage → Course View:     25% → 45%    (↑ 80%)
├── Course View → Enrollment:   4% → 8%      (↑ 100%)
├── Overall Conversion:         1.8% → 4.1%  (↑ 128%)
├── Cart Abandonment:           68% → 42%    (↓ 38%)
└── Checkout Success Rate:      89% → 98%    (↑ 10%)

Student Satisfaction:
├── Complaints about speed:     12/week → 0-1/week (↓ 95%)
├── Support tickets (perf):     8/week → 1/month   (↓ 97%)
├── Reviews mentioning speed:   Negative → Positive
└── NPS Score:                  32 → 58            (↑ 81%)

Infrastructure Cost:
├── Servers needed:             4 → 2              (↓ 50%)
├── Monthly server cost:        $400 → $200        (↓ 50%)
├── Can handle traffic:         400 → 2000+ users  (↑ 5x)
└── Headroom for growth:        Always maxed → 60% available

Response Time Distribution

BEFORE (PHP-FPM):
Response Time Distribution for Course Detail Page

0-500ms:     ████ 8%
500ms-1s:    ██████████ 18%
1s-2s:       ██████████████████████████ 45%
2s-3s:       ████████████ 20%
3s-5s:       ████ 7%
>5s:         ██ 2% (timeouts)

Median: 1,850ms  |  p95: 4,200ms

AFTER (Octane):
Response Time Distribution for Course Detail Page

0-100ms:     ████████████████████ 35%
100-200ms:   ██████████████████████████████████████ 45%
200-300ms:   ██████████ 12%
300-500ms:   ████ 5%
500ms-1s:    ██ 2.5%
>1s:         ░ 0.5%

Median: 152ms  |  p95: 380ms

Real User Feedback

Setelah launch Octane, feedback dari students berubah drastis:

"Finally! Dulu buka course page sambil bikin kopi dulu. Sekarang instant."

"Nggak tau ada yang berubah, tapi sekarang lebih enak aja browsingnya." — Ini feedback terbaik. User nggak notice technology, cuma notice experience.

"Video player-nya loading cepet banget sekarang. Dulu suka buffering lama."

Dan yang paling penting: complaints tentang speed turun dari 12 per minggu menjadi hampir zero.

Cost Analysis

INFRASTRUCTURE COST COMPARISON:

BEFORE (PHP-FPM):
├── 4x VPS (4 core, 8GB RAM each)
├── Load balancer
├── Monthly cost: ~$400
├── Can handle: ~400 concurrent users
├── CPU at peak: 90-95%
└── Status: Always near limit, scary

AFTER (Octane):
├── 2x VPS (4 core, 8GB RAM each)
├── Load balancer
├── Monthly cost: ~$200
├── Can handle: ~2000 concurrent users
├── CPU at peak: 40-50%
└── Status: Comfortable headroom

SAVINGS: $200/month = $2,400/year
PLUS: 5x more capacity for future growth

"Octane bukan cuma bikin website lebih cepat. Octane bikin bisnis lebih profitable. Faster site = better UX = higher conversion = more revenue. Plus server cost turun. Ini win-win-win."


Bagian 9: When to Use (and NOT Use) Octane

Setelah pengalaman dengan BuildWithAngga, saya punya perspective yang lebih nuanced tentang kapan Octane is right choice.

Ideal Use Cases for Octane

✅ GUNAKAN OCTANE KETIKA:

HIGH TRAFFIC APPLICATIONS
├── 500+ requests per minute sustained
├── Peak traffic significantly higher than baseline
├── Need to handle traffic spikes
└── Growth trajectory requires scalability

RESPONSE TIME CRITICAL
├── E-commerce (every ms = conversion)
├── Real-time applications
├── API backends untuk mobile apps
├── Interactive web applications
└── Competitive market where speed matters

SPECIFIC FEATURES NEEDED
├── Concurrent processing (Swoole)
├── In-memory caching (2M ops/sec)
├── Background tasks without cron
├── WebSocket support
└── Real-time statistics/counters

COST OPTIMIZATION
├── Want to reduce server count
├── Need more headroom on existing servers
├── High server costs relative to revenue
└── Planning for growth without linear cost increase

TECHNICAL READINESS
├── Team understands stateful PHP
├── Have time for proper testing
├── Can audit codebase for compatibility
├── Production environment is controllable
└── Using Redis for session/cache

When NOT to Use Octane

❌ JANGAN GUNAKAN OCTANE KETIKA:

LOW TRAFFIC / SIMPLE APPLICATIONS
├── < 100 requests per minute
├── Simple CRUD applications
├── Internal tools with few users
├── MVP / early stage products
└── Already fast enough with PHP-FPM + proper optimization

TEAM/ENVIRONMENT CONSTRAINTS
├── Team tidak familiar dengan stateful PHP
├── No time for thorough testing
├── Shared hosting (no supervisor access)
├── Windows production servers (Swoole)
├── Cannot audit entire codebase
└── Heavy reliance on incompatible packages

ALREADY OPTIMIZED
├── Already using route/config/view caching
├── Already using Redis properly
├── Already optimized database queries
├── Response times already < 300ms
├── No bootstrap overhead (already minimized)

COMPLEXITY NOT JUSTIFIED
├── Added complexity tidak worth the gain
├── Team prefers simplicity
├── Maintenance burden too high
├── Deployment complexity increase not acceptable

Decision Flowchart

START: Should I Use Octane?
          │
          ▼
┌─────────────────────────────────┐
│ Have you optimized basics?      │
│ (cache, queries, Redis, OPcache)│
└───────────────┬─────────────────┘
                │
        ┌───────┴───────┐
        │               │
       NO              YES
        │               │
        ▼               ▼
┌───────────────┐ ┌─────────────────────────────┐
│ DO THAT FIRST │ │ Response time acceptable?   │
│ (Octane won't │ │ (< 300ms for most pages?)   │
│ fix bad code) │ └─────────────┬───────────────┘
└───────────────┘               │
                        ┌───────┴───────┐
                        │               │
                       YES              NO
                        │               │
                        ▼               ▼
              ┌─────────────────┐ ┌─────────────────────┐
              │ Traffic high?   │ │ Identify bottleneck:│
              │ (> 500 req/min) │ │ Bootstrap overhead? │
              └────────┬────────┘ └──────────┬──────────┘
                       │                     │
               ┌───────┴───────┐     ┌───────┴───────┐
               │               │     │               │
              NO              YES   YES             NO
               │               │     │               │
               ▼               ▼     ▼               ▼
         ┌──────────┐   ┌──────────────────┐  ┌────────────────┐
         │ Probably │   │ CONSIDER OCTANE  │  │ Fix the actual │
         │ don't    │   │                  │  │ bottleneck     │
         │ need it  │   │ Check:           │  │ (not Octane    │
         │          │   │ • Team readiness │  │  problem)      │
         │ BUT: if  │   │ • Package compat │  └────────────────┘
         │ expecting│   │ • Testing time   │
         │ growth,  │   │ • Infrastructure │
         │ prepare  │   └──────────────────┘
         └──────────┘

Alternatives to Octane

Sebelum jump ke Octane, pastikan sudah maximize these:

OPTIMIZATION CHECKLIST (Before Octane):

Level 1: Basic Caching
□ php artisan config:cache
□ php artisan route:cache
□ php artisan view:cache
□ OPcache enabled dan configured

Level 2: Application Caching
□ Redis untuk session
□ Redis untuk cache
□ Query result caching
□ Fragment caching (views)

Level 3: Database Optimization
□ Proper indexes
□ No N+1 queries
□ Eager loading
□ Query optimization

Level 4: Infrastructure
□ CDN untuk static assets
□ HTTP/2 enabled
□ Gzip compression
□ Browser caching headers

Level 5: Advanced
□ Queue untuk heavy operations
□ Database read replicas
□ Horizontal scaling

If ALL above done and STILL need more speed → Octane

Gradual Adoption Strategy

Tidak harus all-or-nothing. Bisa gradual:

PHASE 1: Test Environment
├── Setup Octane di staging
├── Run full test suite
├── Load testing
├── Memory leak testing
└── Duration: 2-4 weeks

PHASE 2: Non-Critical Production
├── Deploy ke subset of servers
├── Route sebagian traffic ke Octane
├── Monitor closely
├── Fix issues yang muncul
└── Duration: 2-4 weeks

PHASE 3: Full Production
├── Migrate semua traffic
├── Keep PHP-FPM as fallback
├── Monitor 24/7 first week
├── Document learnings
└── Duration: 1-2 weeks

PHASE 4: Optimization
├── Implement concurrent tasks
├── Add Octane cache
├── Setup background ticks
├── Fine-tune configuration
└── Duration: Ongoing

"Octane powerful, tapi bukan solusi untuk semua masalah. Kalau website lambat karena N+1 queries, Octane tidak akan fix itu — database masih kena 1000 queries. Fix fundamentals dulu. Octane adalah optimization layer di atas fondasi yang sudah solid."


Next: Bagian 10-11 — Production Checklist dan Closing

Di bagian terakhir, saya akan berikan production checklist lengkap dan rekomendasi untuk learning lebih lanjut.


Bagian 10: Production Checklist & Best Practices

Sebelum deploy Octane ke production, pastikan semua item di checklist ini sudah di-address.

Pre-Deployment Checklist

═══════════════════════════════════════════════════════════════════
                    OCTANE PRODUCTION CHECKLIST
═══════════════════════════════════════════════════════════════════

CODE REVIEW:
□ No static properties storing request-specific data
□ No auth/request resolved in constructors
□ No unbounded arrays/collections in singletons
□ All services properly scoped or reset
□ No file handles kept open across requests
□ No global state mutations
□ All closures properly handle state

PACKAGE AUDIT:
□ All packages tested with Octane
□ Telescope disabled or properly configured
□ Debugbar disabled in production
□ Payment gateways tested
□ PDF generators tested
□ Image processors tested
□ Mail packages tested
□ Broadcasting packages tested

SESSION & AUTH:
□ Session driver = redis (NOT file/database)
□ Auth guards tested with concurrent requests
□ Remember me functionality tested
□ Social login packages tested
□ API token authentication tested

DATABASE:
□ Connection pooling configured
□ DisconnectFromDatabases listener enabled
□ Long query timeouts handled
□ Transaction handling verified
□ Queue connections separate from web

CACHE:
□ Cache driver = redis
□ Octane cache used appropriately (ephemeral only)
□ Cache tags working if used
□ Cache clear strategy documented

INFRASTRUCTURE:
□ Supervisor configured and tested
□ Nginx reverse proxy configured
□ SSL termination working
□ Health check endpoint created
□ Log rotation configured
□ Monitoring alerts set up

TESTING:
□ Load testing completed (minimum 1 hour)
□ Memory leak testing done
□ Auth/session tested with multiple users
□ File upload tested
□ All critical user flows tested
□ Error handling verified
□ Graceful degradation tested

DEPLOYMENT:
□ Deployment script created and tested
□ Rollback procedure documented
□ Zero-downtime deployment verified
□ Environment variables documented
□ Secrets properly managed

Health Check Endpoint

Buat endpoint untuk monitoring:

// routes/web.php

Route::get('/health', function () {
    $checks = [
        'status' => 'ok',
        'timestamp' => now()->toIso8601String(),
        'checks' => [],
    ];

    // Database check
    try {
        DB::select('SELECT 1');
        $checks['checks']['database'] = 'ok';
    } catch (\\Exception $e) {
        $checks['checks']['database'] = 'error';
        $checks['status'] = 'degraded';
    }

    // Redis check
    try {
        Cache::store('redis')->set('health_check', 'ok', 10);
        $checks['checks']['redis'] = 'ok';
    } catch (\\Exception $e) {
        $checks['checks']['redis'] = 'error';
        $checks['status'] = 'degraded';
    }

    // Octane check
    if (app()->bound('octane')) {
        $checks['checks']['octane'] = 'running';
        $checks['server'] = 'octane';
    } else {
        $checks['checks']['octane'] = 'not_running';
        $checks['server'] = 'php-fpm';
    }

    // Memory usage
    $checks['memory'] = [
        'used' => round(memory_get_usage(true) / 1024 / 1024, 2) . ' MB',
        'peak' => round(memory_get_peak_usage(true) / 1024 / 1024, 2) . ' MB',
    ];

    $statusCode = $checks['status'] === 'ok' ? 200 : 503;

    return response()->json($checks, $statusCode);
});

Deployment Script

#!/bin/bash
# deploy.sh - Zero-downtime deployment for Octane

set -e

APP_DIR="/var/www/buildwithangga"
BRANCH="${1:-main}"

echo "🚀 Starting deployment..."
echo "📁 Directory: $APP_DIR"
echo "🌿 Branch: $BRANCH"

cd $APP_DIR

# 1. Pull latest code
echo "📥 Pulling latest code..."
git fetch origin
git checkout $BRANCH
git pull origin $BRANCH

# 2. Install dependencies
echo "📦 Installing dependencies..."
composer install --optimize-autoloader --no-dev --no-interaction

# 3. Run migrations
echo "🗄️ Running migrations..."
php artisan migrate --force

# 4. Clear all caches
echo "🧹 Clearing caches..."
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear

# 5. Rebuild caches
echo "🔨 Building caches..."
php artisan config:cache
php artisan route:cache
php artisan view:cache

# 6. Build frontend assets (if applicable)
if [ -f "package.json" ]; then
    echo "🎨 Building frontend assets..."
    npm ci
    npm run build
fi

# 7. Restart Octane (graceful)
echo "♻️ Restarting Octane..."
php artisan octane:reload || {
    echo "⚠️ Reload failed, doing full restart..."
    php artisan octane:stop || true
    sleep 2
    sudo supervisorctl start octane
}

# 8. Restart queue workers
echo "👷 Restarting queue workers..."
php artisan queue:restart

# 9. Clear opcache (if using PHP-FPM as fallback)
echo "🧠 Clearing OPcache..."
curl -s "<http://127.0.0.1/opcache-clear.php>" || true

# 10. Health check
echo "❤️ Running health check..."
sleep 3
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" <http://127.0.0.1:8000/health>)

if [ "$HTTP_STATUS" -eq 200 ]; then
    echo "✅ Deployment successful! Health check passed."
else
    echo "❌ Health check failed with status $HTTP_STATUS"
    echo "🔄 Rolling back..."
    git checkout HEAD~1
    php artisan octane:reload
    exit 1
fi

echo "🎉 Deployment complete!"
echo "📊 Check status: php artisan octane:status"

Monitoring Recommendations

METRICS TO MONITOR:

Application:
├── Response time (p50, p95, p99)
├── Request rate (req/sec)
├── Error rate (%)
├── Active connections
└── Queue depth

Octane Specific:
├── Worker count (active vs configured)
├── Requests per worker
├── Worker restarts (should be low)
├── Memory per worker
└── Worker lifetime

System:
├── CPU usage (per core)
├── Memory usage
├── Disk I/O
├── Network I/O
└── Open file descriptors

Database:
├── Connection count
├── Query time (slow queries)
├── Connection pool usage
└── Replication lag (if applicable)

ALERTING THRESHOLDS:

Critical (page immediately):
├── Error rate > 5%
├── Response time p99 > 5s
├── CPU > 90% for 5 min
├── Memory > 90%
└── Health check failing

Warning (notify during business hours):
├── Error rate > 1%
├── Response time p95 > 2s
├── Worker restarts > 10/hour
├── Memory growth trend
└── Queue depth increasing

Common Production Issues & Solutions

ISSUE: Workers restarting too frequently
CAUSE: Memory leaks, max_requests too low
FIX:
├── Increase max_requests (500 → 1000)
├── Profile memory usage
├── Check for unbounded collections
└── Review third-party packages

ISSUE: "MySQL has gone away" errors
CAUSE: Long-idle connections closed by MySQL
FIX:
├── Enable DisconnectFromDatabases listener
├── Set PDO::ATTR_PERSISTENT => false
└── Increase MySQL wait_timeout if needed

ISSUE: Session data mixing between users
CAUSE: Static properties, improper state management
FIX:
├── Audit all static properties
├── Use scoped bindings
├── Reset state in RequestTerminated listener
└── Test with multiple concurrent users

ISSUE: Config changes not applied
CAUSE: Config cached at worker boot
FIX:
├── Full restart (not just reload) for .env changes
├── octane:stop then octane:start
└── Update deployment script

ISSUE: High memory usage over time
CAUSE: Memory not being freed properly
FIX:
├── Lower max_requests
├── Enable garbage collection
├── Profile with memory_get_usage()
└── Check for circular references

Best Practices Summary

CategoryBest Practice
StateNever store request data in static properties
ServicesUse scoped bindings for stateful services
AuthAlways resolve auth in methods, not constructors
DatabaseUse DisconnectFromDatabases listener
CacheUse Octane cache for ephemeral data only
SessionsAlways use Redis, never file/database
DeploymentFull restart for .env changes, reload for code
MonitoringTrack memory per worker, restart frequency
TestingLoad test for minimum 1 hour before production
FallbackKeep PHP-FPM ready as fallback

Bagian 11: Closing — The Transformation Complete

Recap: The Journey

THE BUILDWITHANGGA TRANSFORMATION:

BEFORE (The Embarrassment):
├── Response time: 3+ seconds
├── Throughput: ~100 req/sec
├── Max users: ~200 concurrent
├── Student complaints: 15/week
├── Conversion rate: 1.8%
├── Server CPU: 95% at peak
└── Status: "Founder yang websitenya lambat"

AFTER (The Pride):
├── Response time: 200-400ms
├── Throughput: ~1000 req/sec
├── Max users: ~2000 concurrent
├── Student complaints: 1/week
├── Conversion rate: 3.6%
├── Server CPU: 42% at peak
└── Status: "Platform yang responsive dan reliable"

THE IMPROVEMENT:
├── 10x faster response time
├── 10x more throughput
├── 10x more concurrent users
├── 100% higher conversion
├── 50% less servers needed
└── Irony → Proof of concept

Key Takeaways

WHAT I LEARNED:

1. UNDERSTAND BEFORE OPTIMIZE
   └── Profiling revealed PHP-FPM bootstrap overhead
   └── Without understanding, I'd keep throwing servers at it

2. OCTANE IS A PARADIGM SHIFT
   └── Stateless → Stateful PHP
   └── Requires different mental model
   └── Gotchas are real and painful if ignored

3. SWOOLE VS ROADRUNNER
   └── Swoole: Max performance, more features, more complex
   └── RoadRunner: Easier setup, good performance
   └── Start with RoadRunner if new to Octane

4. CONCURRENT TASKS ARE GAME-CHANGER
   └── 520ms → 120ms for course page
   └── Parallel execution changes everything
   └── Design your code to leverage this

5. THE GOTCHAS ARE REAL
   └── Static properties will bite you
   └── Constructor injection will fail
   └── Memory leaks will happen
   └── Test thoroughly before production

6. BUSINESS IMPACT IS TANGIBLE
   └── Conversion doubled
   └── Bounce rate halved
   └── Complaints nearly eliminated
   └── Server costs reduced

7. IT'S NOT MAGIC
   └── Database queries still take the same time
   └── External APIs still take the same time
   └── You're eliminating bootstrap overhead, not everything

Rekomendasi Kelas di BuildWithAngga

Untuk menguasai Laravel dari fundamental sampai production-level seperti yang dibahas di artikel ini, saya punya beberapa kelas yang bisa membantu:

KelasFokusLevel
Laravel FundamentalsCore concepts, MVC, EloquentBeginner
Laravel API DevelopmentRESTful APIs, authentication, rate limitingIntermediate
Laravel AdvancedCaching, queues, events, testingAdvanced
Laravel PerformanceQuery optimization, profiling, scalingAdvanced
Full-Stack Laravel + VueComplete modern web applicationIntermediate
DevOps untuk LaravelDocker, CI/CD, server managementAdvanced

Kenapa Belajar di BuildWithAngga:

  • Project-based — Bukan teori, tapi build real applications
  • Production-ready code — Standards yang saya pakai di platform ini
  • Complete source code — Clone, modify, deploy
  • Indonesian language — Penjelasan yang mudah dipahami
  • Lifetime access — Belajar sesuai pace kamu
  • Community support — Diskusi dengan ribuan developers
  • Up-to-date — Selalu update untuk versi Laravel terbaru

Platform yang sekarang 10x lebih cepat ini dibangun dengan knowledge yang sama yang diajarkan di kelas-kelas tersebut.

Quick Links:

👉 Kelas Laravel Premium: buildwithangga.com/kelas?category=laravel

👉 Semua Kelas Premium: buildwithangga.com/kelas

👉 Kelas Gratis untuk Mulai: buildwithangga.com/kelas?type=free

Final Message

Performance bukan luxury — performance adalah expectation.

Di era attention span 8 detik, website yang load 3 detik adalah website yang ditinggalkan.

Users tidak peduli stack kamu apa. Users tidak peduli server kamu berapa. Users peduli: "Kenapa ini lambat?"

Laravel Octane memberikan tools untuk menjawab pertanyaan itu. Tapi tools tanpa understanding adalah resep untuk disaster. Pahami dulu, implement dengan hati-hati, test dengan thorough.

Dan yang paling penting:

Start with the basics. Master them. Then level up.

Jangan loncat ke Octane sebelum kamu paham Laravel fundamentals. Jangan loncat ke Swoole sebelum kamu paham Octane basics. Jangan deploy ke production sebelum kamu test dengan proper load.

BuildWithAngga adalah living proof bahwa transformation dari slow ke fast itu possible. Dari embarrassment ke pride. Dari irony ke proof of concept.

Kamu juga bisa.


Artikel ini ditulis oleh Angga Risky Setiawan, AI Product Engineer & Founder BuildWithAngga — platform online courses untuk developers Indonesia.

Platform yang dulu lambat dan memalukan, sekarang cepat dan membanggakan.

Proof that eating your own cooking makes the recipe better.

👉 buildwithangga.com — Now 10x faster.


Appendix: Quick Reference

Essential Commands:

# Install
composer require laravel/octane
php artisan octane:install

# Run
php artisan octane:start --watch              # Development
php artisan octane:start --workers=8          # Production

# Manage
php artisan octane:status
php artisan octane:reload                     # Code changes
php artisan octane:stop

# Supervisor
sudo supervisorctl start octane
sudo supervisorctl restart octane
sudo supervisorctl status octane

Key Config Options:

// config/octane.php
'server' => 'swoole',           // or 'roadrunner'
'workers' => 8,                 // Usually = CPU cores
'task_workers' => 4,            // For concurrent tasks
'max_requests' => 500,          // Restart after N requests
'tick' => true,                 // Enable background ticks

Must-Have Listeners:

RequestTerminated::class => [
    FlushTemporaryContainerInstances::class,
    DisconnectFromDatabases::class,
],

Session & Cache (Required):

SESSION_DRIVER=redis
CACHE_STORE=redis
QUEUE_CONNECTION=redis