Audit Performa CMS Dokumentasi
18

Audit Performa CMS Dokumentasi

Updated: Sep 2026Oleh: SystemSistem ERP & Teknologi Inti
Menyiapkan Audio...
Protected View (Read Only)

⚡ 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:

  1. 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.
  2. Overload DOM Browser: Komponen client DocsList.tsx me-render seluruh 517 artikel ke dalam satu tabel HTML raksasa tanpa paginasi, menghasilkan >13.000 elemen DOM yang membekukan thread utama browser.
  3. Looping I/O Disk Sinkron: Sistem secara dinamis membaca fisik 251 berkas .md di hard drive pada setiap request (force-dynamic).
  4. Pemborosan Komputasi Kategori: Halaman kategori CMS mengunduh 3,34 MB data artikel lengkap hanya demi menghitung panjang array (.length) jumlah artikel.
  5. Ketiadaan Database Index: Tabel documentation_articles tidak memiliki indeks komposit untuk category_id, slug, dan sort_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 PengujianQuery Eksisting (Dengan content)Query Ringan (Tanpa content)Efisiensi / Peningkatan
Waktu Respon Query Database4.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 Mentah0 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 MemoryMencegah 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:
    typescript
    let 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 content berisi 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. Mentransfer content saat 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) dan lib/docs.ts
  • Deskripsi: Sistem mengimplementasikan penggabungan otomatis antara artikel di database dan artikel berbasis file markdown fisik di folder docs/. Pada setiap request:
    1. Sistem meloop 251 entri dokumen di lib/docs.ts.
    2. Mengecek apakah dokumen sudah ada di database (!dbSlugs.has(item.slug)).
    3. Membaca fisik file dari media penyimpanan menggunakan getDocContent().
  • 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_articles belum memiliki database index untuk kolom filter utama: category_id, slug, sort_order, dan is_published.
    • Kebijakan RLS auth_write saat ini menggunakan USING (true), yang di level database mengizinkan setiap pengguna terautentikasi untuk menulis/mengubah artikel jika mengakses API Supabase secara langsung tanpa melalui middleware Next.js.

4. Matriks Dampak & Tingkat Keparahan#

ID TemuanArea MasalahFile UtamaSeverityDampak Terhadap Sistem
B1Over-fetching Teks Penuhlib/actions/docs-cms.ts🔴 KritisTransfer data 3,34 MB per request; latensi query > 4,7 detik.
B2DOM Render Tanpa Paginasicomponents/cms/DocsList.tsx🔴 KritisBrowser hang/freeze; 13.000+ elemen DOM dirender bersamaan.
B3Agregasi Kategori Inefisienapp/dashboard/cms/categories/page.tsx🟠 TinggiLoading halaman kategori lambat tanpa alasan teknis yang valid.
B4Ketiadaan Filter Server Kategoriapp/panduan/[category]/page.tsx🟠 Tinggi95% data yang ditarik dari database langsung dibuang percuma.
B5Disk I/O Blocking Hybridlib/actions/docs-cms.ts🟡 SedangOverhead komputasi I/O filesystem pada setiap refresh halaman.
B6Ketiadaan Database Index & RLSsupabase/migrations/🟡 SedangSequential 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:

tsx
const [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:

typescript
const { data: articleCounts } = await supabase
    .from('documentation_articles')
    .select('category_id')

Langkah 4: Tambahkan Indeks PostgreSQL

Eksekusi migrasi indeks database:

sql
CREATE 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 KinerjaKondisi Sebelum AuditTarget Setelah Remediasi
Ukuran Transfer Data Halaman CMS3,34 MB< 250 KB (Turun 92,5%)
Waktu Respon Query Database4,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 / freezeInstan dan mulus (< 50 ms)
Beban RAM Browser Pengguna80–120 MB< 20 MB

Laporan audit ini disahkan oleh Tim Arsitektur Sistem ERP BUMDes Mandiri untuk segera ditindaklanjuti ke fase eksekusi optimasi.

Apakah panduan ini membantu Anda?

Bacaan Selanjutnya