Hydration lag pada aplikasi SSR/Next.js sering kali disalahartikan murni sebagai masalah performa JavaScript di sisi klien. Ketika sebuah halaman web mengalami freezing sesaat setelah first paint, masalah utamanya biasanya terbagi menjadi dua ranah yang saling berhubungan: eksekusi synchronous rendering task yang memblokir Main Thread browser, dan degradasi waktu komputasi akibat alokasi resource CPU di container host yang dibatasi oleh CFS (Completely Fair Scheduler) atau EEVDF (Earliest Eligible Virtual Deadline First) Linux Scheduler.
Artikel ini membahas cara melacak root cause long task pada proses hydration, mengkorelasikan CPU throttling container terhadap responsivitas aplikasi, serta strategi mitigasi melalui task slicing dan dynamic boundaries.
Korelasi Linux Scheduler (CFS/EEVDF) dan Event Loop Browser
Di lingkungan container (seperti Kubernetes atau Docker), pembatasan CPU dilakukan melalui mekanika Control Groups (cgroups). Linux CFS menggunakan dua parameter utama: cpu.cfs_period_us (periode kuota waktu, default 100ms) dan cpu.cfs_quota_us (total waktu eksekusi yang diizinkan dalam periode tersebut). Pada kernel Linux yang lebih baru (6.6+), scheduler EEVDF menggantikan CFS, namun mekanisme kuota via cgroup tetap serupa.
Jika node backend SSR (Node.js) atau environment container pengujian di cloud kehabisan jatah kuota sebelum periodenya berakhir, thread akan dihentikan sementara (CPU throttling). Pada sisi server, hal ini menunda pengiriman chunk HTML streaming atau kalkulasi pre-render data. Di sisi klien, keterlambatan transfer chunk ini memecah alur streaming dan memaksa browser menjalankan hydration pass berukuran besar sekaligus dalam satu execution frame alih-alih terdistribusi secara bertahap.
Dampaknya pada Event Loop JavaScript
Ketika React Concurrent Scheduler berjalan di browser, tugas dibagi ke dalam time slices berdurasi ~5ms menggunakan MessageChannel atau requestIdleCallback. Namun, jika hydration pass menangani komponen monolitik tanpa boundary, React tidak dapat memecah pohon DOM tersebut ke dalam frame berbeda. Akibatnya:
- Task slicing gagal berjalan karena traversal komponen bersifat synchronous dan non-interruptible saat merekonsiliasi DOM statis SSR.
- Main Thread terblokir melebihi ambang 50ms, memicu peringatan Long Task di browser.
- Interaksi pengguna (klik, input) diabaikan atau tertunda sampai seluruh pohon komponen selesai diproses.
Langkah Diagnostik: Tracing dan cgroup Metrics
Sebelum melakukan refactoring kode, verifikasi apakah CPU throttling terjadi di level container host sebelum memeriksa runtime klien.
1. Cek CPU Throttling pada Container
Jalankan perintah berikut di dalam container Next.js/Node.js untuk membaca metrik cgroup v2:
cat /sys/fs/cgroup/cpu.statOutput tipikal cgroup v2:
usage_usec 145028492
user_usec 112003820
system_usec 33024672
nr_periods 4520
nr_throttled 890
throttled_usec 48920194Jika nilai nr_throttled dan throttled_usec meningkat secara signifikan selama traffic spike, instance SSR Anda kehabisan kuota CFS. Respons HTML dan chunk script akan tiba secara tidak beraturan di browser, memperburuk waktu eksekusi hydration.
2. Profiling via Chrome DevTools Tracing
- Buka Chrome DevTools, masuk ke tab Performance.
- Aktifkan opsi CPU throttling (misalnya 4x atau 6x slowdown) untuk mensimulasikan perangkat mobile/low-end.
- Mulai profiling, refresh halaman, lalu interaksikan UI segera setelah konten muncul.
- Identifikasi blok task berwarna merah pada track Main. Cari call stack yang menunjukkan fungsi rekursif React seperti
workLoopConcurrent,performConcurrentWorkOnRoot, atauhydrateInstance.
Jika call stack hydrateInstance berjalan terus-menerus tanpa ada celah frame (idle gap) lebih dari 50ms, komponen tersebut memerlukan boundary slicing.
Solusi Perbaikan: Task Slicing dan Hydration Boundary
Untuk mengatasi long task saat hydration, pecah komponen monolitik menjadi komponen terisolasi menggunakan suspense boundaries dan delegasikan komputasi berat non-kritis ke Web Worker.
1. Selective Hydration dengan Suspense Dinamis
Gunakan dynamic import dan React.Suspense agar React dapat memprioritaskan hydration bagian penting terlebih dahulu dan menunda komponen non-kritis.
// components/HeavyAnalyticsView.jsx
import dynamic from 'next/dynamic';
import { Suspense } from 'react';
// Defer hydration komponen berat hingga runtime klien siap
const DynamicChart = dynamic(() => import('./DataChart'), {
ssr: true,
loading: () => <div className="h-64 animate-pulse bg-gray-100" />,
});
export default function AnalyticsDashboard({ initialData }) {
return (
<div className="dashboard-container">
<header className="critical-ui">
<h1>Dashboard Metrik</h1>
</header>
{/* Suspense boundary memungkinkan React memecah task hydration */}
<Suspense fallback={<p>Memuat visualisasi...</p>}>
<DynamicChart dataset={initialData} />
</Suspense>
</div>
);
}2. Delegasi Transformasi Data ke Web Worker
Jika hydration tertunda karena parsing atau formatting data state awal yang besar, pindahkan komputasi tersebut keluar dari Main Thread.
// hooks/useWorkerData.js
import { useState, useEffect } from 'react';
export function useWorkerData(rawPayload) {
const [processedData, setProcessedData] = useState(null);
useEffect(() => {
// Jalankan worker setelah initial paint untuk menghindari interferensi hydration
const worker = new Worker(new URL('../workers/parser.worker.js', import.meta.url));
worker.postMessage(rawPayload);
worker.onmessage = (event) => {
setProcessedData(event.data);
worker.terminate();
};
return () => worker.terminate();
}, [rawPayload]);
return processedData;
}Kesimpulan
Hydration lag adalah masalah multi-layer. Sering kali degradasi bukan hanya disebabkan oleh struktur DOM yang kompleks, melainkan juga alokasi resource CPU di container host yang terkena throttling oleh CFS/EEVDF Linux Scheduler. Dengan mengamati metrik cpu.stat di container, mengidentifikasi long task via Chrome DevTools, serta mengimplementasikan React Suspense boundaries, Main Thread tetap responsif terhadap input pengguna saat proses rekonsiliasi state berlangsung.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!