Tampilan
9.1 Storage Produksi
Tujuan bab
Setelah bab ini kamu bisa:
- Menyebutkan sembilan domain storage Mastra dan apa yang disimpan masing-masing
- Memilih backend berdasarkan pola akses tiap domain
- Merutekan domain berbeda ke backend berbeda dengan composite storage
- Menjelaskan kenapa libSQL berbasis file tidak memadai untuk produksi multi-proses
- Mengenali gejala adapter yang tidak mendukung domain tertentu
Prasyarat
Storage bukan satu hal
Sejak 3.1 kamu memakai satu baris konfigurasi storage dan tidak memikirkannya lagi. Untuk pengembangan lokal itu tepat. Untuk produksi, ada satu hal yang perlu dibongkar.
Storage Mastra terbagi menjadi domain. Tiap domain memiliki satu jenis data runtime, dan sebuah adapter mengimplementasikan satu atau beberapa domain.
| Domain | Yang disimpan |
|---|---|
memory | Thread, pesan, resource, working memory, dan state memory agent lainnya |
workflows | Snapshot workflow untuk suspend dan resume |
observability | Trace, span, metrik, log, dan feedback |
scores | Catatan skor hasil evaluasi |
datasets | Dataset dan itemnya, dipakai eval dan eksperimen |
experiments | Run eksperimen dan hasil per item |
backgroundTasks | Catatan dan state eksekusi background task |
schedules | Definisi schedule dan riwayat pemicunya |
threadState | State task, goal, dan thread yang durable |
Daftar itu menjelaskan banyak hal yang kamu temui sebelumnya. Ingat peringatan di 7.2 bahwa schedules memerlukan adapter yang mendukung domain schedules — sekarang alasannya jelas.
Dukungan adapter berbeda per domain
Dokumentasi menyatakan bahwa dukungan adapter bervariasi menurut domain. Sebuah adapter yang bagus untuk memory belum tentu mengimplementasikan schedules atau observability.
Gejalanya sering membingungkan: fitur yang kamu pakai tiba-tiba tidak bekerja padahal storage "sudah dikonfigurasi". Periksa dukungan domain sebelum memilih.
Memilih backend berdasarkan pola akses
Inilah inti bab ini. Tiap domain menulis dan membaca dengan cara yang berbeda, dan backend yang tepat mengikuti pola itu.
| Domain | Pola aksesnya | Backend yang cocok |
|---|---|---|
memory | Baca-tulis baris di setiap panggilan agent yang diingat | Basis data transaksional: libSQL, PostgreSQL, MongoDB |
observability | Menulis telemetri bervolume tinggi, sering dikueri secara agregat | Store observability khusus, atau OLAP seperti ClickHouse atau DuckDB |
workflows | Snapshot durable yang harus tersedia saat run dilanjutkan | Basis data persisten yang andal |
scores, datasets, experiments | Data evaluasi berfrekuensi rendah, dibaca belakangan untuk analisis | Basis data biasa |
schedules | Definisi schedule dan riwayat pemicu | Adapter yang mengimplementasikan domain schedules |
Perhatikan baris kedua. Data observability punya sifat yang sangat berbeda dari data memory: volumenya jauh lebih besar, ditulis sekali dan jarang diubah, dan dibaca sebagai agregat. Basis data transaksional bisa menyimpannya, tapi bukan yang terbaik untuknya.
Composite storage — satu domain, satu backend
Karena domain punya kebutuhan berbeda, Mastra menyediakan composite storage untuk merutekan tiap domain ke backend yang tepat.
Ini contoh nyata dari proyek Mastra standar:
ts
// src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { MastraCompositeStore } from '@mastra/core/storage'
import { LibSQLStore } from '@mastra/libsql'
import { DuckDBStore } from '@mastra/duckdb'
export const mastra = new Mastra({
storage: new MastraCompositeStore({
id: 'composite-storage',
default: new LibSQLStore({
id: 'mastra-storage',
url: process.env.TURSO_DATABASE_URL || 'file:./mastra.db',
authToken: process.env.TURSO_AUTH_TOKEN || undefined,
}),
domains: {
observability: await new DuckDBStore().getStore('observability'),
},
}),
})Bacalah konfigurasi itu sebagai keputusan desain, bukan sekadar sintaks:
defaultmenangani semua domain yang tidak disebut secara khusus — memory, workflows, scores, dan seterusnya — memakai libSQLdomains.observabilitymerutekan telemetri ke DuckDB, sebuah basis data analitik yang memang dirancang untuk pola tulis-banyak-baca-agregat
Satu baris domains itu adalah penerapan langsung tabel pola akses di atas.
Pola url yang layak ditiru
Perhatikan juga baris ini:
ts
url: process.env.TURSO_DATABASE_URL || 'file:./mastra.db',
authToken: process.env.TURSO_AUTH_TOKEN || undefined,Ini pola yang rapi: berkas lokal saat pengembangan, Turso saat produksi, tanpa mengubah satu baris kode. Cukup mengisi variabel lingkungan.
Turso adalah libSQL yang di-host, sehingga adapter yang sama bekerja untuk keduanya — kamu tidak berpindah adapter, hanya berpindah tujuan.
Kenapa file:./mastra.db tidak cukup untuk produksi
libSQL berbasis file sempurna untuk pengembangan: tidak butuh server terpisah, dan datanya bertahan antar restart. Tapi ia punya batas yang menjadi masalah begitu masuk produksi.
Satu berkas hanya hidup di satu mesin. Begitu kamu menjalankan dua instance aplikasi — untuk penskalaan atau ketersediaan — keduanya memakai berkas yang berbeda. Pengguna yang permintaannya mendarat di instance A tidak akan melihat ingatan yang tersimpan di instance B.
Dokumentasi menyebut kriteria yang jelas untuk kapan kamu butuh storage persisten sungguhan:
- Agent mengingat pesan atau fakta pengguna
- Workflow suspend dan resume setelah restart
- Trace, metrik, log, skor, atau feedback perlu tetap tersedia untuk analisis
- Schedule dan background task berlanjut melewati deployment
- Beberapa proses runtime membaca dan menulis state yang sama
Poin terakhir yang mematikan libSQL berbasis file.
Pengembangan Produksi (2 instance)
┌──────────────┐ ┌──────────┐ ┌──────────┐
│ app │ │ app A │ │ app B │
│ ↓ │ │ ↓ │ │ ↓ │
│ mastra.db │ │ file A │ │ file B │ ← state terpecah
└──────────────┘ └──────────┘ └──────────┘
Yang benar:
┌──────────┐ ┌──────────┐
│ app A │ │ app B │
└────┬─────┘ └────┬─────┘
└──────┬──────┘
┌────▼─────┐
│ Postgres │
└──────────┘Pilihan backend
| Backend | Paket | Catatan |
|---|---|---|
| libSQL / Turso | @mastra/libsql | Sama untuk lokal dan produksi terkelola |
| PostgreSQL | @mastra/pg | Pilihan aman kalau kamu sudah memakai Postgres |
| MongoDB | @mastra/mongodb | Untuk yang sudah memakai MongoDB |
| DuckDB | @mastra/duckdb | Analitik — cocok untuk domain observability |
| ClickHouse | @mastra/clickhouse | OLAP untuk telemetri bervolume sangat tinggi |
| Redis, Upstash | @mastra/redis, @mastra/upstash | Untuk kebutuhan tertentu |
Saran praktis: pilih yang sudah kamu operasikan. Basis data yang timmu sudah tahu cara memantau, mencadangkan, dan memulihkannya lebih berharga daripada yang secara teori sedikit lebih cepat.
Retensi
Satu hal yang sering baru disadari setelah tagihan datang: data observability tumbuh terus. Trace dari tiga bulan lalu jarang berguna, tapi tetap memakan tempat.
Mastra menyediakan konfigurasi retensi untuk ini. Tetapkan kebijakannya sebelum produksi, bukan setelah basis datamu penuh — karena menghapus data lama dari tabel yang sudah membengkak jauh lebih merepotkan daripada tidak pernah menyimpannya terlalu lama.
Kesalahan umum
Gejala: Setelah menskalakan aplikasi ke beberapa instance, agent kadang mengingat percakapan dan kadang tidak — tergantung permintaan mendarat di mana. Penyebab: Storage memakai libSQL berbasis file. Tiap instance punya berkasnya sendiri, sehingga state terpecah. Perbaikan: Pindah ke backend yang bisa diakses bersama — PostgreSQL, MongoDB, atau libSQL terkelola seperti Turso. Kalau kamu sudah memakai pola process.env.TURSO_DATABASE_URL || 'file:./mastra.db', perpindahannya hanya mengisi variabel lingkungan.
Gejala: Sebuah fitur — schedules, misalnya — tidak bekerja di produksi meskipun storage jelas terkonfigurasi dan fitur lain berjalan normal. Penyebab: Adapter yang kamu pakai tidak mengimplementasikan domain yang dibutuhkan fitur itu. Dukungan adapter berbeda-beda per domain. Perbaikan: Periksa dukungan domain adapter tersebut. Kalau tidak mendukung, pakai composite storage dan rutekan domain itu ke adapter lain yang mendukungnya.
Coba sendiri
Tantangan: Ubah salah satu aplikasi yang kamu bangun di bagian sebelumnya menjadi konfigurasi storage yang siap produksi, dan buktikan perbedaannya.
Ketentuan:
- Ganti konfigurasi storage tunggal menjadi
MastraCompositeStoredengandefaultuntuk sebagian besar domain dan satu domain dirutekan terpisah - Rutekan
observabilitykeDuckDBStore, dan jelaskan alasannya dengan merujuk pola aksesnya - Pakai pola
process.env.X || 'file:./mastra.db'untukdefault, sehingga berkas lokal dipakai bila variabel tidak diisi - Buktikan masalah state terpecah: jalankan dua instance aplikasi di port berbeda, keduanya memakai libSQL berbasis file. Simpan sesuatu ke memory lewat instance A, lalu coba baca lewat instance B
- Arahkan keduanya ke satu basis data bersama, ulangi langkah 4, dan bandingkan
- Daftar domain mana saja yang aplikasimu benar-benar pakai, dan periksa apakah adapter pilihanmu mendukung semuanya
Checklist penerimaan:
- [ ] Composite storage berjalan, dan kamu bisa menunjukkan domain mana yang dirutekan ke mana
- [ ] Langkah 4 memperlihatkan state terpecah secara nyata — instance B tidak melihat data instance A
- [ ] Langkah 5 memperbaikinya, dan kamu tidak mengubah kode untuk itu, hanya konfigurasi
- [ ] Kamu punya daftar domain yang dipakai aplikasimu, bukan tebakan
- [ ] Kamu bisa menjelaskan kenapa
observabilitylayak backend terpisah sementaramemorytidak
Petunjuk: Langkah 4 adalah bagian yang mengubah pemahaman. Banyak orang tahu secara teori bahwa berkas lokal tidak cocok untuk multi-instance, tapi melihat sendiri instance B kehilangan ingatan yang jelas tersimpan di instance A membuat kesimpulannya melekat. Lakukan sebelum kamu benar-benar menskalakan — bukan sesudahnya, saat penggunamu yang menemukannya.
Ikhtisar
- Storage Mastra terbagi menjadi sembilan domain, dan dukungan adapter berbeda-beda per domain. Periksa sebelum memilih.
- Pilih backend berdasarkan pola akses: transaksional untuk
memory, analitik (DuckDB, ClickHouse) untukobservability. MastraCompositeStoremerutekan tiap domain ke backend yang tepat, lewatdefaultdandomains.- Pola
process.env.TURSO_DATABASE_URL || 'file:./mastra.db'memberi berkas lokal saat pengembangan, layanan terkelola saat produksi tanpa mengubah kode. - libSQL berbasis file tidak memadai untuk multi-proses — tiap instance punya berkasnya sendiri, dan state terpecah.
- Tetapkan kebijakan retensi sebelum produksi, bukan setelah basis data membengkak.
- Pilih backend yang timmu sudah tahu cara mengoperasikannya.
Lanjut ke mana
Datamu sudah punya rumah yang layak. Sekarang bagaimana dunia luar berbicara dengan agent-mu: 9.2 Server & Custom Routes.