Di mana skema basis data sebenarnya berada
Semua yang dilakukan layanan ini ada di dalam kontrol versi. Setidaknya kodenya. Basis data tempatnya berjalan terdiri dari 32 tabel, dan 15 di antaranya tidak memiliki definisi di mana pun di repositori — tidak ada CREATE TABLE, tidak ada berkas skema, tidak ada apa pun. Sebuah dump memulihkan ke-32 tabel itu dengan strukturnya utuh; yang tidak disimpan repositori adalah salinan kedua yang independen dari struktur tersebut.
Angka-angka itu berasal dari perbandingan basis data yang sedang berjalan dengan 736 berkas dan 5.671.710 karakter repositori. Angka itu bukan perkiraan.
Bagaimana setengah skema bisa hilang
Ini bukan kecerobohan, dan justru itulah yang membuatnya layak ditulis. Setiap langkah secara terpisah masuk akal.
Tabel yang dibuat sebelum basis kode saat ini ada tidak pernah dicatat, karena pada waktu itu tabel-tabel itu memang sudah ada begitu saja. Tabel yang ditambahkan sejak itu datang melalui skrip migrasi, dan skrip-skrip itu memang ada di repositori — dari situlah 17 definisi itu berasal. Kolom yang ditambahkan belakangan ditambahkan dengan satu baris ALTER TABLE yang diketik di prompt, karena mengetiknya lebih cepat daripada menulis skrip untuk perubahan yang hanya butuh satu detik.
Indeks adalah kasus terburuk. Indeks ditambahkan ketika sesuatu lambat, tepat pada saat sesuatu itu lambat, dan indeks itu langsung menyelesaikan masalahnya. Tidak ada artefak yang tertinggal, tidak ada yang bisa ditinjau, tidak ada yang gagal ketika indeks itu terlupakan. Sebelas dari tiga puluh dua indeks sekunder di sini ada persis karena alasan itu dan tidak tercatat di mana pun.
Tabel yang menyimpan penghitung itu sendiri, sebanyak 166.438 baris, termasuk di antara lima belas tabel tanpa definisi di repositori.
Apa yang sebenarnya rusak
Tidak banyak, sampai satu momen tertentu: ketika basis data dipulihkan dari dump.
Dump membawa strukturnya, jadi pemulihan langsung tidak bermasalah. Bahayanya ada pada setiap jalur yang membangun ulang tabel alih-alih memulihkannya — pindah ke mesin lain, mengimpor tabel tertentu, membuat ulang tabel dari skrip. Datanya kembali, dan indeksnya diam-diam tidak. Tidak ada yang gagal. Kueri mengembalikan jawaban yang benar. Hanya saja waktunya lebih lama, dan penyebabnya tidak terlihat, karena satu-satunya catatan tentang indeks itu adalah mesin yang sudah tidak memilikinya lagi.
Itulah jenis kegagalan yang layak diberi nama: indeks yang hilang tidak menghasilkan kesalahan, melainkan jawaban benar yang lebih lambat. Tidak ada pengujian untuk itu, karena pengujiannya lolos.
Apa yang kami lakukan sementara ini
Skema itu saat ini tidak ada di repositori, dan itulah gambaran akurat tentang keadaannya. Yang sudah diterapkan lebih sempit daripada perbaikan menyeluruh, dan mencakup kasus yang benar-benar menimbulkan kerugian:
Dump sebelum melakukan apa pun yang merusak, disimpan di luar web root. Pemulihan yang benar-benar pernah dilakukan setidaknya sekali, bukan sekadar diasumsikan. Dan daftar pendek, yang diperiksa setelah setiap pemulihan, berisi indeks yang diketahui hanya ada di basis data yang sedang berjalan — karena mesin bisa ditanya indeks apa saja yang dimilikinya, dan jawabannya bisa dibandingkan dengan jawaban terakhir kali ditanya.
Versi umumnya berlaku lebih dari sekadar untuk basis data. Jika sebagian dari suatu sistem hanya ada di sistem yang sedang berjalan, tidak ada salinan kedua darinya. Pertanyaan yang layak diajukan tentang setiap bagian infrastruktur bukanlah apakah bagian itu dicadangkan, melainkan apa yang tidak dapat dibangun ulang dari repositori jika mesinnya hilang. Di sini jawabannya adalah lima belas tabel dan sebelas indeks, butuh satu sore untuk memastikannya, dan jawaban itu kini berupa daftar, bukan sesuatu yang tidak diketahui.
Pembaruan: September 2026
Lima hari setelah artikel ini terbit, direktori kerja di-commit: 26 berkas dan 4.058 baris, di antaranya sebelas skrip migrasi yang ditulis antara 28 Agustus dan 1 September yang hingga saat itu hanya ada di mesin yang sedang berjalan. Itulah separuh yang mudah — kode yang sudah ditulis tetapi tidak pernah dicatat.
Pengukuran diulang pada 1 September 2026 dengan cara yang sama: basis data yang sedang berjalan dibandingkan dengan semua yang dilacak git.
| Tabel | 46 | 30 dengan CREATE TABLE di repositori, 16 tanpa |
| Indeks sekunder | 43 | 32 dibuat oleh kode di repositori, 11 hanya di basis data yang sedang berjalan |
| Skrip migrasi | 109 | dalam kontrol versi |
Angka yang penting tidak bergerak. Pada 27 Agustus, sebelas indeks sekunder tidak ada di mana pun selain di basis data yang sedang berjalan. Pada 1 September masih ada sebelas. Sebelas indeks lain ditambahkan di antara kedua tanggal itu, dan setiap indeks tersebut datang bersama skrip migrasi: kebiasaannya berubah, utangnya tidak.
Kesebelas indeks itu berada di lima tabel — penghitung, bahasa, perujuk, daftar pengecualian, dan log peristiwa. Kelima tabel itu lebih tua daripada basis kode saat ini, dan justru karena itulah tidak pernah ada yang dicatat untuknya. Menulis berkas sekarang berarti merekonstruksi, dari server yang sedang berjalan, apa yang diketik di prompt bertahun-tahun lalu.
Jadi posisi dari bulan Agustus tetap berlaku. Skemanya masih belum ada di repositori, dan pemulihan yang membangun ulang tabel alih-alih memulihkannya masih akan membuang sebelas indeks tanpa sepatah kata pun. Yang berubah lebih sempit: segala sesuatu yang ditambahkan sejak itu meninggalkan sebuah berkas.