Documentation
¶
Overview ¶
Package opfsvfs implements an OPFS VFS for github.com/ncruces/go-sqlite3, enabling SQLite persistence in browser WebAssembly applications via the Origin Private File System.
Usage ¶
Blank-import to register the "opfs" VFS, then open databases with the vfs=opfs query parameter:
import (
"database/sql"
_ "github.com/danmestas/go-sqlite3-opfs"
_ "github.com/ncruces/go-sqlite3/driver"
)
db, err := sql.Open("sqlite3", "file:mydb.db?vfs=opfs")
db.SetMaxOpenConns(1)
JavaScript Worker Setup ¶
OPFS sync access handles must be pre-opened in a dedicated Worker before the Go WASM binary starts. The Worker creates named file handles and passes them to Go via the _opfs_init callback:
const handles = {};
for (const suffix of ["", "-journal", "-wal"]) {
const fh = await dir.getFileHandle("mydb.db" + suffix, { create: true });
handles["mydb.db" + suffix] = await fh.createSyncAccessHandle();
}
_opfs_init(handles);
WAL Mode ¶
WAL mode is supported with exclusive locking (required because OPFS does not provide shared memory):
db.SetMaxOpenConns(1)
db.Exec("PRAGMA locking_mode=EXCLUSIVE")
db.Exec("PRAGMA journal_mode=WAL")
Requirements ¶
- GOOS=js GOARCH=wasm build target
- Dedicated Worker (FileSystemSyncAccessHandle is Worker-only)
- COOP/COEP headers (required by wazero's SharedArrayBuffer usage)
- Chrome 102+, Firefox 111+, Safari 16.4+
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handle ¶
type Handle interface {
Read(buf []byte, offset int64) (int, error)
Write(buf []byte, offset int64) (int, error)
GetSize() (int64, error)
Truncate(size int64) error
Flush() error
Close() error
}
Handle abstracts a FileSystemSyncAccessHandle. On js builds, wraps syscall/js. All methods are synchronous.
type Observer ¶
type Observer interface {
OnRead(file string, offset int64, bytes int, duration time.Duration, err error)
OnWrite(file string, offset int64, bytes int, duration time.Duration, err error)
OnFlush(file string, duration time.Duration, err error)
}
Observer receives callbacks for I/O operations. Pass nil for no-op. Implementations must be safe for concurrent use.
type OpfsError ¶
type OpfsError struct {
Op string // read, write, truncate, flush, getSize, close
File string // OPFS filename (e.g. "test.db", "test.db-wal")
Offset int64 // byte offset, -1 if not applicable
Size int // requested bytes, -1 if not applicable
Err error // underlying error
}
OpfsError provides structured context for OPFS I/O failures.
type RecordingObserver ¶
type RecordingObserver struct {
Reads []ReadEvent
Writes []WriteEvent
Flushes []FlushEvent
}
RecordingObserver captures events for test assertions. Stops recording after maxRecordedEvents to prevent unbounded growth.
func (*RecordingObserver) OnFlush ¶
func (r *RecordingObserver) OnFlush(file string, dur time.Duration, err error)
type Stats ¶
type Stats struct {
Reads atomic.Int64
Writes atomic.Int64
Flushes atomic.Int64
BytesRead atomic.Int64
BytesWritten atomic.Int64
ReadTimeNs atomic.Int64
WriteTimeNs atomic.Int64
FlushTimeNs atomic.Int64
}
Stats holds atomic performance counters. Zero allocation on read. Snapshot and Reset are best-effort (not atomic across fields) — acceptable because Go WASM is single-threaded, so no concurrent modifications occur.
func (*Stats) Snapshot ¶
func (s *Stats) Snapshot() StatsSnapshot
Snapshot returns a point-in-time copy of the counters.
type StatsSnapshot ¶
type StatsSnapshot struct {
Reads, Writes, Flushes int64
BytesRead, BytesWritten int64
ReadTimeNs, WriteTimeNs, FlushTimeNs int64
}
StatsSnapshot is a non-atomic copy of all counters.