Belajar WebAssembly - Persistence & State Management
Episode 18 of 23

Belajar WebAssembly - Persistence & State Management

Strategi penyimpanan dan state untuk WebAssembly: IndexedDB dan localStorage lewat interop JS di browser, Redis dan object storage via host functions di server, pola stateless dengan external state KV, serta snapshot/rehydrate memory Wasm

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Setelah di episode 17 kalian membuka jalur networking — pada episode ini kita membahas salah satu kelemahan klasik Wasm: ia tidak menyimpan apa pun sendiri. Memory linear mati bersama instance; tidak ada disk, tidak ada database. Semua data yang harus bertahan harus disimpan di luar.

Mengapa topik ini penting? Karena hampir setiap aplikasi nyata butuh state: cache hasil komputasi, preferensi user, atau progress pekerjaan panjang. Memahami pola penyimpanan — dari IndexedDB di browser hingga Redis di server — menentukan apakah aplikasi Wasm kalian bisa berfungsi seperti aplikasi native, atau hanya playground sesaat.

Tantangan: Memory Wasm Bersifat Volatile

Ciri arsitektur yang harus selalu diingat:

  • Memory Wasm hidup selama instance hidup.
  • Instansiasi ulang (refresh halaman, cold start edge) menghapus semuanya.
  • Wasm tidak punya akses disk/kernel — hanya apa yang diberikan host.

Jadi pertanyaan state selalu sama: di mana data disimpan ketika instance mati? Jawabannya tergantung konteks: browser, server, atau edge.

Di Browser: IndexedDB dan localStorage

Browser menyediakan dua penyimpanan utama, keduanya diakses lewat interop JS (modul Wasm tidak bisa memanggil IndexedDB langsung):

StorageVolumeCocok untukCatatan
localStorage~5 MBPreferensi, token kecilSynchronous, blocking
IndexedDB~GBData besar, cache, blobAsync, transactional

Pola standar: modul Wasm mengekspor fungsi untuk serialize state, lalu JS menyimpannya:

state.rs
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
 
#[derive(Serialize, Deserialize)]
struct Progress {
    tahap: String,
    persen: u32,
    hasil: Vec<f64>,
}
 
#[wasm_bindgen]
pub fn simpan_state(progress: Progress) -> Result<JsValue, JsValue> {
    Ok(serde_wasm_bindgen::to_value(&progress)
        .map_err(|e| JsValue::from_str(&e.to_string()))?)
}
 
#[wasm_bindgen]
pub fn muat_state(json: &JsValue) -> Result<Progress, JsValue> {
    serde_wasm_bindgen::from_value(json.clone())
        .map_err(|e| JsValue::from_str(&e.to_string()))
}

Di JS:

JSsimpan-di-indexeddb.js
import { simpan_state, muat_state } from "./wasm_state.js"
 
const db = await openDB("wasm-app", 1, {
  upgrade(db) { db.createObjectStore("state"); },
})
 
// simpan: serialize di wasm, tulis ke IndexedDB
const json = simpan_state({ tahap: "kompresi", persen: 67, hasil: [] })
await db.put("state", JSON.stringify(json), "progress")
 
// muat: baca dari IndexedDB, parse di wasm
const raw = await db.get("state", "progress")
const progress = muat_state(JSON.parse(raw))

Pola ini memakai kekuatan masing-masing: Wasm untuk serialisasi cepat dan deterministik, IndexedDB untuk persistensi volume besar.

Tip

Untuk hasil komputasi besar yang jarang berubah (mis. model ML terkompresi), simpan hasilnya sebagai blob di IndexedDB — bukan menyimpan data mentah lalu menghitung ulang. Load-blob jauh lebih murah daripada komputasi ulang setiap kunjungan.

Di Server: Redis / Object Storage via Host Functions

Di server, persistensi disediakan host melalui host functions — imports yang dibungkus API Wasm yang aman:

kv.wit
package app:kv;
 
interface kv {
  set: func(key: string, value: string) -> result;
  get: func(key: string) -> result<string, string>;
  del: func(key: string) -> result;
}
 
world service {
  import kv;
}

Host mengimplementasikan interface ini — misalnya memakai Redis:

host-redis.rs (embedding)
use wasmtime::component::Resource;
use redis::Client;
 
impl crate::app::kv::Host for MyHost {
    async fn set(&mut self, key: String, value: String) -> anyhow::Result<()> {
        self.redis.set(&key, &value).await?;
        Ok(())
    }
 
    async fn get(&mut self, key: String) -> anyhow::Result<Result<String, String>> {
        let v: Option<String> = self.redis.get(&key).await?;
        Ok(v.ok_or_else(|| "key tidak ada".to_string()))
    }
}

Modul Wasm tinggal memanggil kv::set/kv::get — ia tidak tahu (dan tidak perlu tahu) apakah di baliknya ada Redis, Memcached, atau S3.

Pola Stateless + External State (KV)

Untuk skala horizontal di edge/serverless, pola yang dianjurkan adalah stateless module + external KV:

  1. Fungsi tidak menyimpan state di memori instance.
  2. Setiap state yang perlu bertahan ditulis ke KV (Redis/Cloudflare KV/D1).
  3. Fungsi membaca state saat dibutuhkan, memproses, menulis balik.
100%

Keuntungannya: instance bisa dibuat/hancur kapan pun tanpa kehilangan data, dan skala horizontal tidak perlu sinkronisasi memori antar instance.

Kapan memakai stateful?

Saat koordinasi state perlu deterministik dan real-time — workflow panjang, WebSocket broadcast, counter terdistribusi. Di edge, ini yang ditangani Durable Objects (episode 12); di server, arsitektur actor/single-writer. Jangan menjadikan stateful sebagai default — mulai dari stateless.

Snapshot dan Rehydrate Memory

Ada kalanya seluruh memory perlu dipertahankan — misalnya menyimpan state aplikasi lengkap antara sesi. Tekniknya: ambil snapshot byte dari memory, simpan sebagai blob, dan rekonstruksi saat dimuat.

JSsnapshot-rehydrate.js
async function snapshotMemory(instance, namaSesi) {
  const memory = instance.exports.memory
  const snapshot = new Uint8Array(memory.buffer) // salin seluruh memory
 
  await db.put("snapshots", snapshot.buffer, namaSesi)
}
 
async function rehydrateMemory(namaSesi, wasmBytes) {
  const snapshot = await db.get("snapshots", namaSesi)
  const { instance } = await WebAssembly.instantiate(wasmBytes)
 
  // salin balik ke memory instance baru
  const buf = new Uint8Array(instance.exports.memory.buffer)
  buf.set(new Uint8Array(snapshot))
  return instance
}

Peringatan penting: snapshot hanya valid jika modul tidak punya state eksternal (socket, file handle, imported host state) — teknik ini hanya untuk state murni dalam memory linear.

Warning

Snapshot memory tidak menangkap state host: koneksi jaringan, file descriptor, atau variabel global di sisi JS hilang saat rehydrate. Batasi teknik ini untuk state data murni, dan rancang ulang resource eksternal setelah instance baru lahir.

Common Pitfalls

  • Menyimpan pointer/memory address — address memory valid hanya untuk instance yang sama; jangan simpan sebagai data persisten.
  • lokalStorage untuk data besar — cepat penuh (5 MB) dan blocking; pakai IndexedDB.
  • Mengandalkan state instance di edge — cold start menghapusnya; selalu external state.
  • Snapshot ukuran besar — menyalin memory puluhan MB setiap detik itu mahal; snapshot hanya saat benar-benar dibutuhkan (mis. sebelum close).

Penutup

Pada episode 18 ini, kalian telah memahami strategi persistence dan state management untuk modul WebAssembly.

Inti yang harus dibawa pulang:

  • Memory Wasm volatile — semua data yang bertahan harus disimpan di host.
  • Browser: IndexedDB untuk volume besar, localStorage untuk hal kecil; akses lewat interop JS.
  • Server: host functions membungkus Redis/object storage; modul hanya melihat interface.
  • Default stateless + external KV; stateful hanya untuk koordinasi yang benar-benar perlu.
  • Snapshot/rehydrate cocok untuk state memory murni, bukan state host.

Di episode 19 selanjutnya kita akan membahas plugin systems & embedding — isolasi plugin dengan V8/SpiderMonkey, model plugin Envoy/extism dan proxy-wasm, aplikasi host + wasm plugin, serta ekosistem Extism dan Kubernetes. Sampai jumpa di episode 19!