Connection¶
Connection string format¶
Parameters¶
| Parameter | Default | Description |
|---|---|---|
wal_checkpoint_threshold |
1000 |
Auto-checkpoint after N WAL frames. Set to 0 to disable automatic checkpointing. |
wal_write_buffer_size |
65536 |
WAL write-buffer size in bytes. Set to 0 to flush every commit (lowest throughput, lowest exposure). |
log_level |
warn |
Log verbosity: debug, info, warn, error |
max_cached_pages |
2000 |
Maximum pages to keep in the in-memory LRU page cache. Each page is 4 096 bytes; default ≈ 8 MB. |
slow_query_threshold |
0 (disabled) |
Log queries at WARN level when elapsed time meets or exceeds this value. Accepts Go duration strings: 50ms, 2s. |
synchronous |
normal |
WAL fsync mode. See WAL durability modes. |
parallel_scan |
off |
Enable concurrent leaf-page scanning for full table scans. See Parallel scan. |
sort_mem_limit |
4194304 |
Maximum bytes of row data held in memory before an ORDER BY sort spills to disk. Set to 0 to disable disk spill (all sorted rows stay in memory). See Disk-backed sort. |
encryption_key |
(none) | Hex-encoded AES-256-CTR encryption key. See Encryption. |
Examples¶
// Enable debug logging
db, err := sql.Open("minisql", "./my.db?log_level=debug")
// Larger page cache (16 000 pages ≈ 64 MB)
db, err := sql.Open("minisql", "./my.db?max_cached_pages=16000")
// Disable auto-checkpoint (run PRAGMA wal_checkpoint manually)
db, err := sql.Open("minisql", "./my.db?wal_checkpoint_threshold=0")
// Maximum write durability (fsync after every commit)
db, err := sql.Open("minisql", "./my.db?synchronous=full")
// Log queries taking more than 50 ms
db, err := sql.Open("minisql", "./my.db?slow_query_threshold=50ms")
// Enable parallel full table scans
db, err := sql.Open("minisql", "./my.db?parallel_scan=on")
// Raise ORDER BY sort memory to 64 MiB before spilling to disk
db, err := sql.Open("minisql", "./my.db?sort_mem_limit=67108864")
// Disable disk spill entirely (all sorted rows stay in memory)
db, err := sql.Open("minisql", "./my.db?sort_mem_limit=0")
// Encrypted database
import "encoding/hex"
key := []byte("my-32-byte-secret-key")
db, err := sql.Open("minisql", "./my.db?encryption_key="+hex.EncodeToString(key))
// Multiple parameters
db, err := sql.Open("minisql", "./my.db?log_level=info&max_cached_pages=4000&synchronous=full")
Connection pooling¶
Single connection required
MiniSQL requires exactly one open connection per database file. Multiple connections to the same file do not share page cache, WAL index, or transaction state, and will produce inconsistent results or corrupt the database.
Always configure the pool to use exactly one connection:
db, err := sql.Open("minisql", "./my.db")
if err != nil {
log.Fatal(err)
}
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
Read concurrency is still handled within the single connection. Concurrent goroutines running SELECT through the same *sql.DB each get their own MVCC snapshot and execute without blocking each other. The single-connection constraint does not reduce read throughput.
WAL durability modes¶
The synchronous parameter controls when fsync() is called on the WAL file, trading durability for write performance.
| Mode | Parameter | Description |
|---|---|---|
normal |
synchronous=normal |
Default. No fsync per commit. fsync only at checkpoint. Matches SQLite WAL default. Data loss possible only during OS crash/power failure in the narrow window between commit and next checkpoint. |
full |
synchronous=full |
fsync after every WAL commit. Survives OS crash between commits. Significantly slower for write-heavy workloads. |
off |
synchronous=off |
No fsyncs at all. Fastest, but uncommitted data may be lost on OS crash or power failure. |
Read and change the current mode at runtime with PRAGMA:
PRAGMA synchronous; -- returns 0 (off), 1 (normal), 2 (full)
PRAGMA synchronous = full;
PRAGMA synchronous = normal;
PRAGMA synchronous = off;
Parallel scan¶
When enabled, full table scans split the leaf-page chain across runtime.NumCPU() goroutines so multiple pages are decoded and filtered concurrently.
Or toggle at runtime:
PRAGMA parallel_scan = on;
PRAGMA parallel_scan; -- returns 0 (off) or 1 (on)
PRAGMA parallel_scan = off;
Note
Parallel scan does not guarantee row-ID ordering. If your query depends on insertion order, always add an explicit ORDER BY.
Disk-backed sort¶
ORDER BY queries accumulate matching rows in memory to sort them. When the total size of those rows exceeds sort_mem_limit bytes (default 4 MiB), MiniSQL flushes the current sorted batch to a temporary file and continues accumulating. At the end of the scan all temp files are merged with a min-heap into a single sorted stream.
// Raise the threshold to 64 MiB for analytics workloads with large result sets
db, err := sql.Open("minisql", "./my.db?sort_mem_limit=67108864")
// Disable disk spill entirely (use when you know result sets are small)
db, err := sql.Open("minisql", "./my.db?sort_mem_limit=0")
Or adjust at runtime per-session with PRAGMA:
PRAGMA sort_mem_limit; -- returns current limit in bytes
PRAGMA sort_mem_limit = 67108864; -- set to 64 MiB
PRAGMA sort_mem_limit = 0; -- disable disk spill
Note
Disk spill applies universally to all ORDER BY queries — sequential scans, joins, DISTINCT, and multi-column sorts — whenever the accumulated row data exceeds sort_mem_limit. The only exception is ORDER BY with LIMIT (and no DISTINCT), which uses a bounded min-heap sized to OFFSET + LIMIT rows and never needs to spill.
Parallel scan is most beneficial for large tables on multi-core machines running filter-heavy queries. For small tables or single-CPU environments the overhead typically outweighs the benefit.