Langsung ke konten
AFM Studio
  • Halaman

    AFM Studio
  • Karya
  • Lab
  • Tentang
  • CV
  • Kontak
  • Proyek

    AksaraPerpustakaan pengetahuan Otorita IKN yang bisa dicari, dengan server memeriksa akses setiap pembaca di tiga tingkat
  • Daily TaqwaSistem presensi salat anti-manipulasi untuk ~2.000 pegawai OIKN di Masjid Negara IKN
  • Lantara v2Mesin perizinan Otorita IKN tempat administrator menambah jenis izin baru dari layar, tanpa developer
  • eKiosk IKNDua ruang kendali untuk Nusantara: konten kios dan pelacakan bus live dengan estimasi kedatangan yang menilai dirinya sendiri
  • E-Monev IKNPlatform pemantauan pembangunan Nusantara selama 23 tahun, melacak progres triwulan setiap unit terhadap 24 KPI
  • JDIH OIKNPortal dokumen hukum publik OIKN, dengan alur penerbitan terkendali dan ringkasan AI yang tidak pernah keluar dari server
  • Jejak PenanamanAplikasi absen acara penanaman pohon IKN yang membuktikan peserta hadir di lokasi lewat selfie ber-geofence
  • Dashboard SAKTIPipeline malam dan dashboard yang menggantikan unduhan anggaran manual di keenam satker OIKN
  • DoseRxKalkulator dosis untuk 127 obat yang membulatkan dosis ke tablet dan sirup yang dijual apotek Indonesia
  • CubiqPelatih speedcubing dengan timer setara kompetisi dan solver buatan sendiri untuk 8 puzzle utama
  • ScimotionPerpustakaan sains interaktif berisi 86 artikel, masing-masing dijelaskan bersama animasi yang bisa dikendalikan pembaca
  • WA Daily Scrum BotBot WhatsApp yang menagih laporan standup tim 14 orang dan menandai tugas yang macet berhari-hari

↑ ↓ untuk berpindah · Enter untuk membuka · Esc untuk menutup

Query Planner

Mesin database di browser yang menampilkan setiap rencana yang dipertimbangkan dan letak taksiran barisnya meleset

Tangkapan layar
galat taksiran di puncak, terlihat per node
300×
untuk merencanakan ulang dan menjalankan join empat arah
< 16 ms
visualisasi berbeda, semuanya dibuat sendiri
8
permintaan jaringan; tidak ada yang keluar dari browser
0
Di halaman ini

Masalahnya

Saat sebuah query database lambat, saran yang biasa muncul adalah menambah index. Sering kali masalahnya bukan di situ. Sebelum menjalankan query, database menaksir berapa baris yang akan dihasilkan setiap langkah, berdasarkan ringkasan statistik kecil dari tabelnya. Kalau taksirannya salah, database dengan yakin memilih rencana yang lambat. Tidak ada yang rusak dan tidak ada index yang kurang. Rencananya dipilih dengan benar, hanya saja dari keyakinan yang keliru.

Salah satu penyebab umumnya adalah asumsi independensi. Saat diminta baris dengan kota Balikpapan dan provinsi Kalimantan Timur, database mengalikan dua peluangnya seolah kota tidak memberi petunjuk apa pun soal provinsi. Tebakannya satu baris dari sepuluh ribu. Kenyataannya satu dari seratus.

Alat standarnya, EXPLAIN, hanya menampilkan rencana pemenang. Kandidat yang kalah dan selisih antara taksiran dan kenyataan tidak terlihat. Saya membuat Query Planner untuk engineer backend dan data, administrator database, serta siapa pun yang mengajar atau mempelajari optimasi query.

Yang saya bangun

Saya menulis mesin database utuh yang jalan di browser

Membungkus mesin yang sudah ada jauh lebih mudah. Tapi tidak ada mesin yang menunjukkan rencana yang dibuangnya, atau menaruh taksirannya di samping jumlah sebenarnya di setiap langkah. Hanya mesin yang saya kendalikan sendiri yang bisa melakukan keduanya. Jadi aplikasi ini punya parser query, penyimpanan tabel, index, statistik, model biaya, pencarian rencana, dan eksekutor dengan sembilan operator sendiri. Nama operatornya sama dengan Postgres, jadi yang dipelajari di sini langsung terpakai saat membaca EXPLAIN sungguhan.

Planner tidak boleh melihat datanya

Planner yang bisa mengintip baris aslinya tidak sedang merencanakan. Saya membuat kode planner tidak bisa mengimpor kode penyimpanan atau eksekutor, dan linter menggagalkan build kalau aturan itu dilanggar. Planner hanya membaca statistik. Setiap taksiran juga membawa penjelasannya sendiri: metode yang dipakai, inputnya, dan asumsinya. Layar menggambar penjelasan itu apa adanya, jadi tampilan tidak mungkin berbeda dari hitungannya.

Setiap rencana yang kalah saya simpan

Pencarian rencana klasik membuang kandidat begitu ada yang lebih murah. Saya menyimpan semua kandidat, beserta urutan pengisiannya. Untuk delapan tabel, itu 255 sel dengan memori yang sangat kecil. Inilah yang membuat animasinya bisa memutar ulang pencarian yang sebenarnya. Saya juga menyimpan rencana yang unggul hanya karena urutan hasilnya. Tanpa itu, merge join tidak akan pernah menang, dan aplikasi ini akan mengajarkan hal yang keliru.

Saya mengujinya terhadap Postgres asli

Postgres yang dikompilasi agar jalan di browser menjadi rujukan saat tes. Data yang sama dimasukkan ke kedua mesin, dan tes memastikan planner saya memilih bentuk rencana yang sama. Kalau keduanya berbeda, penyebabnya saya dokumentasikan di dalam aplikasi, tidak saya akali sampai cocok. Tes terpisah memastikan setiap rencana yang mungkin untuk satu query memberi hasil yang sama, dan tes inilah yang menangkap bug di eksekutor.

Satu detail data hampir menyembunyikan efeknya

Dataset Indonesia yang realistis awalnya menyusun kota berkelompok per provinsi. Generatornya memilih kota berdasarkan posisi di daftar, jadi pengelompokan itu menyusutkan galat independensi dari sekitar 30× menjadi 3×. Saya mengurutkan ulang daftarnya berdasarkan jumlah penduduk, yang juga lebih realistis, dan efeknya kembali muncul.

Hasilnya

Query Planner sudah live dan terbuka untuk umum. Geser pengaturan biaya penyimpanan dari 4,0 ke arah 1,1, penyesuaian yang biasa dipakai untuk SSD, dan batang biaya saling menyilang saat rencana pemenang berganti. Join empat tabel direncanakan dan dijalankan ulang dalam waktu kurang dari 16 milidetik, satu frame di 60 frame per detik. Pohon rencana menampilkan taksiran di samping jumlah sebenarnya, dan galatnya membesar: 1,2× di pemindaian, 8× setelah satu join, 300× di puncak.

Grafik korelasi menggambar asumsi yang salah sebagai persegi panjang yang terlepas dari data asli. Satu klik menambah statistik multi-kolom, dan taksirannya langsung menempel ke kenyataan. Tidak ada data yang keluar dari browser, dan setiap rencana bisa dibagikan sebagai tautan. Saya membangunnya dalam satu sesi sekitar 16,5 jam pada 3 September 2026.

Di balik layarDetail teknis untuk engineer
  • Mesin: parser SQL recursive-descent untuk subset yang sempit, penyimpanan kolumnar, index B+tree yang dimuat sekaligus, reservoir sampling (dengan seed, default 30.000 baris), histogram equi-depth, daftar MCV, dan ketiga jenis statistik multivariat (functional dependency, n-distinct, MCV multivariat).
  • Biaya berupa pasangan (startup, total), sehingga LIMIT 10 bisa memilih rencana berbeda. Dynamic programming Selinger dengan penyimpanan interesting order; tests/orders.test.ts mencakup kasus yang terbalik kalau penyimpanan itu dimatikan.
  • Eksekutor model volcano: seq scan, index scan, nested loop, hash join, merge join, sort, hash aggregate, group aggregate, limit. Waktu per node diambil dari penghitung kasar supaya instrumentasi tidak mengacaukan pengukuran.
  • ESLint no-restricted-imports mencegah src/planner/ mengimpor src/storage/ atau src/executor/. Math.random dilarang di engine; semua keacakan memakai seed.
  • Setiap selektivitas mengembalikan SelectivityTrace (mcv, histogram, independence, dependency, …) yang dirender oleh tampilan histogram dan korelasi.
  • PGlite (Postgres di WebAssembly) hanya devDependency. src/planner/divergences.ts mencatat setiap perbedaan, dan tes oracle memastikan masing-masing tetap berbeda. Commit 5ba8322 berjudul "the PGlite oracle, and what it caught".
  • Daftar kota ada di src/storage/datasets/wilayah.ts; sampler-nya Zipf berdasarkan posisi di daftar.
  • Delapan instrumen SVG buatan sendiri, termasuk tidy tree Reingold–Tilford, satu loop requestAnimationFrame, dan sistem token dua tema yang dibangun ulang dalam revisi desain di tengah proyek (a073496).
  • tests/copy.test.ts melarang em-dash dan kalimat pemasaran di teks yang dirilis. Tema tidak ikut disimpan di URL yang dibagikan.
  • Tiga dependensi runtime (React, React DOM, typeface Geist). JS 98 KB gzip dari anggaran 350 KB. Nol permintaan jaringan saat runtime.
  • CI: typecheck, lint, test, oracle, build, lalu deploy ke GitHub Pages.
  • 294 tes (261 unit dan integrasi, 33 oracle) di 13 file. ~13.500 baris. 36 commit.

Tangkapan layar

Beranda
Evidence - Correlation
Evidence - Histogram
Evidence - Sample
Evidence - Timeline
Evidence - Recovery

Sedang merekrut, atau membangun hal serupa?

Saya terbuka untuk posisi full-time dan proyek freelance tertentu.