Contoh Penggunaan Concurrency di Laravel 12: Dashboard, API Paralel, dan Defer

Artikel sebelumnya membahas konsep Concurrency di Laravel 12. Artikel ini fokus pada implementasi: studi kasus nyata bagaimana Concurrency bisa mempercepat aplikasi secara signifikan.

Studi Kasus 1: Dashboard dengan Banyak Data Source

Dashboard admin yang butuh data dari beberapa tabel berbeda. Ini biasanya jadi bottleneck karena diquery satu per satu.

Sebelum (sequential — sekitar 800ms):

public function dashboard()
{
    $totalOrders    = Order::thisMonth()->count();          // ~200ms
    $totalRevenue   = Order::thisMonth()->sum('total');     // ~200ms
    $pendingOrders  = Order::where('status', 'pending')->count(); // ~150ms
    $newCustomers   = User::thisMonth()->count();           // ~150ms
    $topProducts    = Product::topSelling(5)->get();       // ~100ms

    return view('dashboard', compact(...));
}

Sesudah (paralel — sekitar 200ms):

use IlluminateSupportFacadesConcurrency;

public function dashboard()
{
    [$totalOrders, $totalRevenue, $pendingOrders, $newCustomers, $topProducts] =
        Concurrency::run([
            fn() => Order::thisMonth()->count(),
            fn() => Order::thisMonth()->sum('total'),
            fn() => Order::where('status', 'pending')->count(),
            fn() => User::thisMonth()->count(),
            fn() => Product::topSelling(5)->get(),
        ]);

    return view('dashboard', compact(
        'totalOrders', 'totalRevenue', 'pendingOrders', 'newCustomers', 'topProducts'
    ));
}

Studi Kasus 2: Multiple API Calls

Halaman product detail yang butuh data dari beberapa API eksternal:

use IlluminateSupportFacadesConcurrency;

public function productDetail(Product $product): View
{
    [$reviews, $stock, $shippingOptions] = Concurrency::run([
        fn() => $this->reviewApi->getProductReviews($product->id),    // API 1
        fn() => $this->inventoryApi->getStock($product->sku),         // API 2
        fn() => $this->shippingApi->getOptions($product->weight),     // API 3
    ]);

    return view('products.detail', compact('product', 'reviews', 'stock', 'shippingOptions'));
}

Kalau masing-masing API butuh 500ms, tanpa concurrency total 1.5 detik. Dengan concurrency, cukup ~500ms.

Studi Kasus 3: Defer untuk Aksi Non-Blocking

User logout — beberapa aksi perlu terjadi tapi tidak perlu selesai sebelum response dikirim:

use IlluminateSupportFacadesConcurrency;

public function logout(Request $request): RedirectResponse
{
    $user = $request->user();

    Auth::logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();

    // Jalankan setelah response dikirim
    Concurrency::defer([
        fn() => $this->activityLog->record($user, 'logout'),
        fn() => $this->deviceTokenService->revokeAll($user),
        fn() => $this->sessionCleanup->cleanup($user),
    ]);

    return redirect('/');
}

Studi Kasus 4: Generate Laporan Parallel

Generate beberapa bagian laporan sekaligus lalu gabungkan:

public function generateAnnualReport(int $year): array
{
    [$salesData, $customerData, $productData, $regionData] = Concurrency::run([
        fn() => $this->salesReport->compile($year),
        fn() => $this->customerReport->compile($year),
        fn() => $this->productReport->compile($year),
        fn() => $this->regionReport->compile($year),
    ]);

    return [
        'year'     => $year,
        'sales'    => $salesData,
        'customers' => $customerData,
        'products' => $productData,
        'regions'  => $regionData,
        'generated_at' => now()->toIso8601String(),
    ];
}

Error Handling di Concurrency

Kalau salah satu closure melempar exception, Concurrency akan meneruskan exception tersebut:

use IlluminateSupportFacadesConcurrency;

try {
    [$data1, $data2] = Concurrency::run([
        fn() => riskyApiCall(),
        fn() => anotherApiCall(),
    ]);
} catch (Exception $e) {
    // Handle error — biasanya fallback ke data cached atau default
    Log::warning("Concurrency error: {$e->getMessage()}");
    [$data1, $data2] = [getFromCache('data1'), getFromCache('data2')];
}

Kapan Tidak Pakai Concurrency

Jangan pakai untuk operasi yang sangat cepat. Overhead membuka child process (~50-100ms) lebih mahal dari manfaatnya kalau setiap closure selesai dalam 5ms.

Gunakan untuk operasi yang masing-masing butuh 100ms ke atas, terutama I/O seperti HTTP request, query database, atau baca file besar.

Baca Juga

Butuh tim yang bantu optimasi performa aplikasi Laravel Anda? Lihat layanan pengembangan aplikasi kami.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *