Skip to content

Metrics

MiniSQL exposes a native Go metrics API that returns a point-in-time snapshot of engine statistics. All counters are cumulative since the database was opened; gauges reflect instantaneous state.


Reading metrics

import (
    "context"
    "database/sql"
    "github.com/RichardKnop/minisql"
    _ "github.com/RichardKnop/minisql"
)

db, err := sql.Open("minisql", "./my.db")
// ...
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)

m, err := minisql.ReadMetrics(context.Background(), db)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("cache hit rate: %.2f%%\n",
    100*float64(m.PageCacheHits)/float64(m.PageCacheHits+m.PageCacheMisses))
fmt.Printf("tx commits: %d  rollbacks: %d\n", m.TxCommits, m.TxRollbacks)
fmt.Printf("queries total: %d  slow: %d\n", m.QueriesTotal, m.QueriesSlow)

ReadMetrics acquires the engine's connection briefly to copy the counters; it does not block query execution.


Fields

Page cache

Field Kind Description
PageCacheHits counter Requests served from the in-memory LRU cache
PageCacheMisses counter Requests that required a WAL or disk read
PageCacheEvictions counter Pages removed to make room for incoming pages
PageCacheSize gauge Pages currently held in the cache
PageCacheCapacity gauge Maximum pages the cache can hold (max_cached_pages setting)

The cache hit rate is PageCacheHits / (PageCacheHits + PageCacheMisses). A rate below ~95% on a read-heavy workload is a signal to raise max_cached_pages.

WAL

Field Kind Description
WALFramesWritten counter Cumulative frames written to the WAL since open
WALCheckpoints counter Cumulative checkpoint runs (automatic + PRAGMA wal_checkpoint)
WALCurrentFrames gauge Frames currently in the WAL; resets to 0 after each checkpoint and truncate

WALCurrentFrames approaching the wal_checkpoint_threshold (default 1000) means a checkpoint will run soon.

Transactions

Field Kind Description
TxCommits counter Write transactions successfully committed
TxRollbacks counter Transactions rolled back (explicit or due to error)

Read-only transactions are not counted — they produce no WAL frames and have zero commit overhead.

Queries

Field Kind Description
QueriesTotal counter Total calls to ExecContext and QueryContext since open
QueriesSlow counter Calls that met or exceeded slow_query_threshold

QueriesSlow is only meaningful when slow_query_threshold is set in the DSN. See Connection.

Sort

Field Kind Description
SortsInMemory counter ORDER BY queries completed entirely in memory
SortSpillRuns counter Sorted run files written to disk for external merge sort
SortSpillBytes counter Cumulative bytes written to disk run files

SortSpillRuns > 0 means at least one ORDER BY query exceeded sort_mem_limit. Raise the limit or investigate query result sizes. See Disk-backed sort.


Periodic polling

Metrics are in-process counters — there is no background thread accumulating them. Poll on your own schedule:

func reportMetrics(ctx context.Context, db *sql.DB, interval time.Duration) {
    ticker := time.NewTicker(interval)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            m, err := minisql.ReadMetrics(ctx, db)
            if err != nil {
                log.Printf("metrics error: %v", err)
                continue
            }
            log.Printf("cache=%.1f%% wal_frames=%d commits=%d slow_queries=%d spill_runs=%d",
                100*float64(m.PageCacheHits)/float64(max(m.PageCacheHits+m.PageCacheMisses, 1)),
                m.WALCurrentFrames,
                m.TxCommits,
                m.QueriesSlow,
                m.SortSpillRuns,
            )
        case <-ctx.Done():
            return
        }
    }
}

Prometheus integration

There is no built-in Prometheus exporter. To expose metrics via Prometheus, wrap ReadMetrics in a custom collector:

type miniSQLCollector struct {
    db           *sql.DB
    cacheHits    *prometheus.Desc
    cacheMisses  *prometheus.Desc
    txCommits    *prometheus.Desc
    // ... one Desc per field
}

func (c *miniSQLCollector) Collect(ch chan<- prometheus.Metric) {
    m, err := minisql.ReadMetrics(context.Background(), c.db)
    if err != nil {
        return
    }
    ch <- prometheus.MustNewConstMetric(c.cacheHits, prometheus.CounterValue, float64(m.PageCacheHits))
    ch <- prometheus.MustNewConstMetric(c.cacheMisses, prometheus.CounterValue, float64(m.PageCacheMisses))
    ch <- prometheus.MustNewConstMetric(c.txCommits, prometheus.CounterValue, float64(m.TxCommits))
    // ...
}