Sesi login, keranjang belanja sementara, dan kode OTP berbagi satu sifat: sekali lewat batas waktunya, datanya tidak lagi bernilai dan justru menjadi beban penyimpanan. Menulis cron job penghapus di sisi aplikasi selalu bisa, tetapi solusi yang lebih rapi sudah tersedia di server: TTL index — index yang menghapus document secara otomatis setelah usianya melampaui batas waktu. Chapter ini menguji mekanismenya secara nyata, termasuk menunggu thread TTL benar-benar bekerja.
Peta materi Chapter ini:
- Membuat TTL index dengan opsi
expireAfterSeconds. - Uji nyata: menunggu thread TTL (berjalan tiap 60 detik) menghapus document.
- Perilaku khusus: field berupa array
Datedan nilai non-Date. - Use case umum: sesi login, OTP, dan log.
Membuat TTL Index #
TTL index adalah single field index biasa plus satu opsi: expireAfterSeconds. Field yang di-index wajib berisi nilai Date — tanggal kedaluwarsa dihitung dari nilai field tersebut. Mari siapkan collection sesi berisi empat document yang masing-masing menguji satu perilaku TTL:
use latihan_index
db.sesi.drop()
db.sesi.insertMany([
{ pengguna: "Budi", sesiBerakhir: new Date() },
{ pengguna: "Siti", sesiBerakhir: new Date(Date.now() + 3600000) },
{ pengguna: "Dewi", sesiBerakhir: [new Date(Date.now() - 120000), new Date(Date.now() + 3600000)] },
{ pengguna: "Eka", sesiBerakhir: "tidak pernah" }
])
db.sesi.createIndex({ sesiBerakhir: 1 }, { expireAfterSeconds: 5 })Output yang Dihasilkan:
switched to db latihan_index
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bc8ab02ae8bf3e41cb3c'),
'1': ObjectId('6ad3bc8ab02ae8bf3e41cb3d'),
'2': ObjectId('6ad3bc8ab02ae8bf3e41cb3e'),
'3': ObjectId('6ad3bc8ab02ae8bf3e41cb3f')
}
}
sesiBerakhir_1Perhatikan bahwa kita meng-insert document terlebih dahulu, lalu membuat index — keduanya sama sahnya. Nilai 5 detik dipilih agar uji berlangsung cepat; pada produksi, sesi login lazim memakai 1800 (30 menit) dan OTP 300 (5 menit). Pada collection uji di atas, satu-satunya field Date yang usianya melewati 5 detik adalah milik Budi, sehingga hanya document Budi yang calon untuk dihapus.
Thread TTL Bekerja Tiap 60 Detik #
Penghapusan TTL tidak terjadi pada detik yang sama ketika document kedaluwarsa. Server mongod menjalankan TTL monitor — thread latar yang bangun setiap 60 detik, mencari index bertanda expireAfterSeconds, lalu menghapus document yang sudah melewati batas. Konsekuensinya: penghapusan dijamin terjadi paling lama sekitar satu menit setelah kedaluwarsa. Mari buktikan dengan benar-benar menunggu 65 detik:
print("Sebelum menunggu: " + db.sesi.countDocuments({}) + " document")
sleep(65000)
db.sesi.find({}, { _id: 0, pengguna: 1, sesiBerakhir: 1 })Output yang Dihasilkan:
Sebelum menunggu: 4 document
[
{ pengguna: 'Siti', sesiBerakhir: ISODate('2026-10-17T19:20:58.948Z') },
{ pengguna: 'Eka', sesiBerakhir: 'tidak pernah' }
]Dua document bertahan dan dua terhapus. Tabel berikut merangkum nasib keempatnya:
| Document | Nilai sesiBerakhir | Hasil setelah 65 detik |
|---|---|---|
| Budi | Date saat insert | Terhapus — usia melewati 5 detik |
| Siti | Date satu jam ke depan | Bertahan — kedaluwarsa 1 jam lagi |
| Dewi | Array dua Date | Terhapus — tanggal terlama sudah 2 menit lalu |
| Eka | String "tidak pernah" | Bertahan selamanya — non-Date tak pernah kedaluwarsa |
Dua baris terakhir pantas digarisbawahi. Pada array Date, TTL memakai elemen terlama sebagai patokan — document Dewi dianggap kedaluwarsa meskipun elemen keduanya masih dua jam di masa depan. Sedangkan nilai selain Date (string, angka, bahkan timestamp Unix sebagai angka) tidak pernah dianggap kedaluwarsa, termasuk juga document yang tidak memiliki field tersebut sama sekali.
Batasan dan Catatan TTL #
Aturan-aturan berikut berlaku pada TTL index:
| Aturan | Penjelasan |
|---|---|
| Hanya single field | Compound index tidak mendukung TTL — galat kode 67 |
Tidak untuk _id | Index _id tidak boleh memakai expireAfterSeconds — galat kode 197 |
Field wajib Date | Non-Date dan field yang absen tidak pernah dihapus |
| Boleh lebih dari satu | Satu collection boleh punya beberapa TTL index pada field berbeda |
| Penghapusan best-effort | Beban server tinggi dapat menunda eksekusi thread TTL |
Mengubah masa hidup tanpa membangun ulang index juga mungkin lewat collMod — topik yang dibahas di Chapter Index Maintenance:
db.runCommand({
collMod: "sesi",
index: { keyPattern: { sesiBerakhir: 1 }, expireAfterSeconds: 3600 }
})
db.sesi.getIndexes()Output yang Dihasilkan:
{
expireAfterSeconds_old: Long('5'),
expireAfterSeconds_new: Long('3600'),
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1792261324, i: 1 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1792261324, i: 1 })
}
[
{
v: 2,
key: { _id: 1 },
name: '_id_',
collation: { locale: 'simple' }
},
{
v: 2,
key: { sesiBerakhir: 1 },
name: 'sesiBerakhir_1',
expireAfterSeconds: 3600,
collation: { locale: 'simple' }
}
]Respon collMod melaporkan expireAfterSeconds_old dan expireAfterSeconds_new, dan getIndexes() kini menampilkan nilai 3600 pada spesifikasi index. Kita baru saja mengubah batas sesi dari 5 detik menjadi 1 jam tanpa menghapus indexnya.
Rangkuman #
createIndex({ field: 1 }, { expireAfterSeconds: N })menghapus document otomatis setelahNdetik dihitung dari nilaiDatepada field.- Thread TTL monitor bangun tiap 60 detik, sehingga penghapusan terjadi paling lama sekitar satu menit setelah kedaluwarsa — dibuktikan nyata dengan
sleep(65000). - Array
Datekedaluwarsa mengikuti elemen terlama; nilai non-Datedan field yang absen tidak pernah dihapus. - TTL hanya untuk single field index, tidak berlaku pada
_id, dan nilainya bisa diubah kapan pun lewatcollMod.