Audit Performa CMS Dokumentasi
⚡ Laporan Audit Performa Komprehensif: CMS Dokumentasi & Panduan
Nomor Dokumen: PERF/AUDIT/2026-09-11/001
Tanggal Audit: 11 September 2026
Klasifikasi: Internal Teknis & Manajemen Sistem
Target Modul: CMS Panduan (/dashboard/cms/panduan), CMS Kategori (/dashboard/cms/categories), Portal Publik (/panduan), Server Actions (lib/actions/docs-cms.ts)
Status Evaluasi: ⚠️ Ditemukan 6 Bottleneck Performa Kritis Memerlukan Optimasi Arsitektur
1. Ringkasan Eksekutif (Executive Summary)#
Pengguna dan administrator melaporkan bahwa halaman CMS Dokumentasi dan Pusat Panduan terasa sangat berat, lambat dibuka (loading > 5 detik), serta mengalami stuttering/freeze saat pencarian.
Audit teknis mendalam terhadap kode sumber, alur transmisi data (React Server Components), dan database Supabase mengonfirmasi bahwa akar permasalahan bukan karena infrastruktur server yang lemah, melainkan arsitektur data fetching yang mengalami over-fetching masif:
- Over-fetching Kolom
content: Setiap pemanggilan daftar artikel (getDocArticles()) menarik seluruh teks dokumen Markdown/HTML lengkap dari database untuk 517 artikel sekaligus, mentransfer 3,34 MB JSON mentah per request. - Overload DOM Browser: Komponen client
DocsList.tsxme-render seluruh 517 artikel ke dalam satu tabel HTML raksasa tanpa paginasi, menghasilkan >13.000 elemen DOM yang membekukan thread utama browser. - Looping I/O Disk Sinkron: Sistem secara dinamis membaca fisik 251 berkas
.mddi hard drive pada setiap request (force-dynamic). - Pemborosan Komputasi Kategori: Halaman kategori CMS mengunduh 3,34 MB data artikel lengkap hanya demi menghitung panjang array (
.length) jumlah artikel. - Ketiadaan Database Index: Tabel
documentation_articlestidak memiliki indeks komposit untukcategory_id,slug, dansort_order, memicu Full Table Scan.
2. Metrik Uji Benchmark Riil (Live Database Testing)#
Pengujian dilakukan pada lingkungan aktif dengan 517 artikel dokumentasi dan 43 kategori:
| Parameter Pengujian | Query Eksisting (Dengan content) | Query Ringan (Tanpa content) | Efisiensi / Peningkatan |
|---|---|---|---|
| Waktu Respon Query Database | 4.723 ms (4,72 detik) | 1.092 ms (1,09 detik) | ~4,3x Lebih Cepat ⚡ |
| Ukuran Payload Jaringan (Wire Size) | 3.340 KB (3,34 MB) | 249 KB (0,24 MB) | Penyusutan Payload 92,5% 📉 |
| Volume Teks Markdown Ditransfer | ~2,99 MB Teks Mentah | 0 KB (hanya metadata) | Bebas Beban Parsing JSON |
| Jumlah Elemen DOM Dirender Sekaligus | ~13.442 Node DOM | ~650 Node DOM (jika 25/page) | Penyusutan 95% Beban Browser |
| Alokasi Memori Heap JS Browser | ~85 MB Heap Memory | ~12 MB Heap Memory | Mencegah Crash di Perangkat Mobile/Laptop |
3. Rincian 6 Temuan Kritis (Detailed Audit Findings)#
🔴 Temuan 1: Database Over-fetching Kolom content pada Listing
- Lokasi Kode:
lib/actions/docs-cms.ts(Baris 77–85) - Deskripsi:
Query
getDocArticles()dirancang untuk mengambil seluruh kolom:typescriptlet query = supabase .from('documentation_articles') .select(` id, slug, title, excerpt, content, is_published, access_level, view_count, updated_at, category_id, category:documentation_categories(slug, title, color, icon), author:profiles(full_name) `) .order('sort_order', { ascending: true }) - Dampak:
Kolom
contentberisi ratusan paragraf penjelasan teknis per dokumen (artikel terberat mencapai 43,3 KB per satu artikel). Padahal tabel manajemen CMS dan daftar panduan hanya memerlukan judul, slug, kategori, status publikasi, dan tanggal pembaruan. Mentransfercontentsaat hanya menampilkan tabel merupakan pemborosan bandwidth dan waktu CPU yang sangat besar.
🔴 Temuan 2: Browser DOM Overload & Ketiadaan Paginasi di DocsList.tsx
- Lokasi Kode:
components/cms/DocsList.tsx(Baris 348–506) - Deskripsi:
Seluruh hasil query (517 artikel) langsung di-map ke elemen baris tabel HTML:
tsx
{filteredArticles.map((article: Article) => { // Me-render baris <tr>, checkbox, teks judul, tombol reorder, // badge status, 3 tombol level akses, tanggal, dan 3 tombol aksi })} - Dampak: Browser harus membuat dan mempertahankan lebih dari 13.000 elemen DOM, puluhan SVG Lucide Icon, dan event listener. Setiap pengguna mengetik satu karakter pada input pencarian, JavaScript menjalankan filter dan memicu reflow serta repaint pada seluruh 13.000 elemen secara instan tanpa debounce, mengakibatkan efek antarmuka terasa macet (freezing UI).
🟠 Temuan 3: Pemborosan Transaksi di Halaman Kelola Kategori
- Lokasi Kode:
app/dashboard/cms/categories/page.tsx(Baris 13–44) - Deskripsi:
Untuk menampilkan daftar kategori beserta jumlah artikelnya, halaman memanggil:
typescript
const [categories, allArticles] = await Promise.all([ getDocCategories(), getDocArticles(), // ⛔ Mengunduh 3,34 MB data teks mentah seluruh sistem! ]) const mappedCategories = categories.map(cat => ({ ...cat, articleCount: allArticles.filter((a: any) => a.category_id === cat.id).length })) - Dampak:
Pengguna yang hanya ingin menata nama kategori terpaksa menunggu unduhan seluruh isi buku panduan BUMDes hanya demi menghasilkan sebuah angka integer (
articleCount).
🟠 Temuan 4: Unduhan Global Tanpa Filter Sisi Server di /panduan/[category]
- Lokasi Kode:
app/panduan/[category]/page.tsx(Baris 99–102) - Deskripsi:
Ketika pengguna membuka halaman kategori tertentu (misalnya
/panduan/keuangan), halaman tersebut mengeksekusi:typescript// ⛔ Memanggil seluruh artikel tanpa mengirimkan parameter category_id ke Supabase const allArticles = await getDocArticles({ status: 'published' }) let articles = allArticles.filter((a: any) => a.category_id === category.id) - Dampak: Server mengunduh 517 artikel dari cloud database, memprosesnya di RAM, lalu membuang 95% dari artikel tersebut karena hanya 5% yang sesuai dengan kategori yang sedang dibuka.
🟡 Temuan 5: Disk I/O Blocking Berulang pada Arsitektur Hybrid
- Lokasi Kode:
lib/actions/docs-cms.ts(Baris 115–158) danlib/docs.ts - Deskripsi:
Sistem mengimplementasikan penggabungan otomatis antara artikel di database dan artikel berbasis file markdown fisik di folder
docs/. Pada setiap request:- Sistem meloop 251 entri dokumen di
lib/docs.ts. - Mengecek apakah dokumen sudah ada di database (
!dbSlugs.has(item.slug)). - Membaca fisik file dari media penyimpanan menggunakan
getDocContent().
- Sistem meloop 251 entri dokumen di
- Dampak:
Karena rute Next.js dikonfigurasi
export const dynamic = 'force-dynamic', disk walk dan I/O filesystem ini dieksekusi terus-menerus pada setiap page request, menghabiskan siklus I/O server.
🟡 Temuan 6: Ketiadaan Database Index & Longgarnya RLS
- Lokasi Kode:
supabase/migrations/004_operational_business_modules.sql&006_row_level_security_policies.sql - Deskripsi:
- Tabel
documentation_articlesbelum memiliki database index untuk kolom filter utama:category_id,slug,sort_order, danis_published. - Kebijakan RLS
auth_writesaat ini menggunakanUSING (true), yang di level database mengizinkan setiap pengguna terautentikasi untuk menulis/mengubah artikel jika mengakses API Supabase secara langsung tanpa melalui middleware Next.js.
- Tabel
4. Matriks Dampak & Tingkat Keparahan#
| ID Temuan | Area Masalah | File Utama | Severity | Dampak Terhadap Sistem |
|---|---|---|---|---|
| B1 | Over-fetching Teks Penuh | lib/actions/docs-cms.ts | 🔴 Kritis | Transfer data 3,34 MB per request; latensi query > 4,7 detik. |
| B2 | DOM Render Tanpa Paginasi | components/cms/DocsList.tsx | 🔴 Kritis | Browser hang/freeze; 13.000+ elemen DOM dirender bersamaan. |
| B3 | Agregasi Kategori Inefisien | app/dashboard/cms/categories/page.tsx | 🟠 Tinggi | Loading halaman kategori lambat tanpa alasan teknis yang valid. |
| B4 | Ketiadaan Filter Server Kategori | app/panduan/[category]/page.tsx | 🟠 Tinggi | 95% data yang ditarik dari database langsung dibuang percuma. |
| B5 | Disk I/O Blocking Hybrid | lib/actions/docs-cms.ts | 🟡 Sedang | Overhead komputasi I/O filesystem pada setiap refresh halaman. |
| B6 | Ketiadaan Database Index & RLS | supabase/migrations/ | 🟡 Sedang | Sequential scan pada database dan celah proteksi RLS layer data. |
5. Blueprint Rencana Aksi Remediasi (Action Plan)#
Perbaikan dibagi menjadi 3 fase terstruktur untuk memulihkan performa modul secara bertahap:
Memuat diagram alur...
Detail Langkah Implementasi:
Langkah 1: Buat getDocArticlesList() Ringan
Tambahkan fungsi query khusus yang mengabaikan kolom content:
typescript// Hanya mengambil metadata untuk keperluan list/tabel export async function getDocArticlesList(options?: { category_id?: string, status?: 'published' | 'draft' }) { const supabase = await createClient() let query = supabase .from('documentation_articles') .select(` id, slug, title, excerpt, is_published, access_level, view_count, updated_at, category_id, sort_order, category:documentation_categories(slug, title, color, icon), author:profiles(full_name) `) .order('sort_order', { ascending: true }) // ... filter category dan status di query Supabase langsung }
Langkah 2: Paginasi Sisi Klien di DocsList.tsx
Bagi 517 artikel menjadi 20 atau 25 artikel per halaman dengan pagination bar:
tsxconst [currentPage, setCurrentPage] = useState(1) const itemsPerPage = 25 const totalPages = Math.ceil(filteredArticles.length / itemsPerPage) const displayedArticles = filteredArticles.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage)
Langkah 3: Query Agregasi Hitungan Artikel Kategori
Ganti fetch seluruh artikel di app/dashboard/cms/categories/page.tsx dengan:
typescriptconst { data: articleCounts } = await supabase .from('documentation_articles') .select('category_id')
Langkah 4: Tambahkan Indeks PostgreSQL
Eksekusi migrasi indeks database:
sqlCREATE INDEX IF NOT EXISTS idx_doc_articles_category_sort ON public.documentation_articles (category_id, sort_order); CREATE INDEX IF NOT EXISTS idx_doc_articles_slug ON public.documentation_articles (slug); CREATE INDEX IF NOT EXISTS idx_doc_articles_status_access ON public.documentation_articles (is_published, access_level);
6. Target Hasil Setelah Remediasi#
| Metrik Kinerja | Kondisi Sebelum Audit | Target Setelah Remediasi |
|---|---|---|
| Ukuran Transfer Data Halaman CMS | 3,34 MB | < 250 KB (Turun 92,5%) |
| Waktu Respon Query Database | 4,72 detik | < 0,4 detik (10x Lebih Cepat) |
| Jumlah Node DOM Dirender | > 13.000 node | ~600 node (Turun 95%) |
| Responsivitas Pencarian (Search Input) | Terasa macet / freeze | Instan dan mulus (< 50 ms) |
| Beban RAM Browser Pengguna | 80–120 MB | < 20 MB |
Laporan audit ini disahkan oleh Tim Arsitektur Sistem ERP BUMDes Mandiri untuk segera ditindaklanjuti ke fase eksekusi optimasi.