Opening a database
gr.Open, Options, the file lifecycle, in-memory databases, and db.Info().
gr.Open
gr.Open is the entry point for the library.
It opens a .gr file and returns a *gr.DB.
db, err := gr.Open("graph.gr", gr.Options{})
if err != nil {
log.Fatal(err)
}
defer db.Close()
If the file does not exist, gr.Open creates it as a new, empty database.
If the file exists, gr opens it, validates the header, and replays the WAL if the previous session did not close cleanly.
*gr.DB is safe to use concurrently from multiple goroutines.
Create one instance and share it across your program.
Options
gr.Options controls the database at open time.
db, err := gr.Open("graph.gr", gr.Options{
ReadOnly: false,
CacheSize: 64 * 1024 * 1024, // 64 MB buffer pool
Synchronous: gr.SyncNormal,
MaxRetries: 5,
BusyTimeout: 5 * time.Second,
})
| Field | Default | Description |
|---|---|---|
ReadOnly |
false |
Open the database in read-only mode; all writes fail |
CacheSize |
system default | Buffer pool size in bytes |
Synchronous |
SyncFull |
Durability level for fsync calls |
MaxRetries |
3 |
Retry limit for write-write conflicts |
BusyTimeout |
0 |
Duration to wait when the write slot is busy |
VFS |
disk VFS | Virtual filesystem — use vfs.NewMem() for in-memory databases |
SyncFull is the safest: gr fsyncs after every WAL write.
SyncNormal skips the extra fsync after the checkpoint write, which is safe on most filesystems.
SyncOff turns off all fsyncing — fast but loses data on power failure.
The file lifecycle
When gr.Open returns, the database is ready.
Three files may exist on disk:
| File | Purpose |
|---|---|
graph.gr |
The main database file — the source of truth |
graph.gr-wal |
Write-ahead log — grows during writes, folded back on checkpoint |
graph.gr-shm |
Shared memory file for WAL index — always reproducible from the WAL |
Back up only graph.gr for a closed database.
For a live backup of an open database, use gr backup or db.Backup().
See the backup guide.
db.Close() checkpoints the WAL, fsyncs the database file, and removes the sidecar files.
Always call it, even in an error path.
defer db.Close() is the right pattern.
In-memory databases
For tests, use an in-memory VFS so the test does not touch the disk:
import "github.com/tamnd/gr/vfs"
db, err := gr.Open(":memory:.gr", gr.Options{
VFS: vfs.NewMem(),
})
An in-memory database is not shared between gr.Open calls.
Create a helper function that returns a fresh *gr.DB for each test.
Read-only open
Open an existing database for reading only:
db, err := gr.Open("graph.gr", gr.Options{ReadOnly: true})
Write queries return an error. This is safe to open concurrently alongside a read-write open from the same process (the WAL protocol handles it), or from a separate process (file-level reader-writer locking applies).
db.Info
db.Info() returns runtime statistics for the open database:
info, err := db.Info()
if err != nil {
log.Fatal(err)
}
fmt.Printf("nodes: %d, relationships: %d, file size: %d bytes\n",
info.NodeCount, info.RelationshipCount, info.FileSize)
Fields include NodeCount, RelationshipCount, PageCount, FileSize, WALFrameCount, FreelistPages, and DatabaseUUID.
Errors
gr.Open returns typed errors:
| Error | Meaning |
|---|---|
*gr.ErrVersionMismatch |
The file was created by a newer version of gr; upgrade gr |
*gr.ErrConfigConflict |
The open options conflict with the file's create-time config |
*gr.ErrNotADatabase |
The file exists but is not a gr database file |