Artifact ce27694f4f53f025d4118b6c536d1d80c029d0930dba8c3585234b404d7f29cf:
- File
docs/site/content/docs/production.md
— part of check-in
[f30849a466]
at
2026-04-26 14:14:53
on branch trunk
— docs: usability improvements from DX audit (#9)
Implements all seven recommended changes from the docs audit:
1. SDK pages now show package names in nav. Add scripts/prepend-sdk-frontmatter.sh that prepends '--- title: <pkg> ---' to each gomarkdoc-generated api.md; wired into 'make docs-gen-sdk'. Sidebar previously showed five undifferentiated 'API' entries; now shows cli, db, dst, libfossil, observer/otel.
2. /examples/ populated with paragraphs + GitHub deep-links for embed-repo-api and wasm. No longer a placeholder pointing back at the repo.
3. Architecture page gets an inline SVG mirroring the landing-page diagram. Self-contained CSS (uses currentColor + ember accent) so it tracks Hextra's light/dark theme without depending on the landing's CSS scope.
4. Cross-links added: quickstart -> production + extension-points, migration -> architecture, architecture -> extension-points. Extension Points and Production patterns are now reachable via prose, not just the sidebar.
5. /docs/ landing expanded from 3 sentences to a 7-line subsection map calling out each page's purpose.
6. Version indicator in navbar (libfossil v0.4.2). Override layouts/_partials/navbar-title.html mimics Hextra's default and appends a small mono version tag from .Site.Params.version.
7. New /docs/production/ page covering error handling, context cancellation, telemetry wiring, repo-handle lifetime, and driver selection — the patterns the quickstart's log.Fatal shortcuts gloss over. (user: [email protected], size: 3457) [annotate] [blame] [check-ins using]
title: Production patterns
weight: 60
The Quickstart uses log.Fatal to keep the snippets short. This page shows the patterns you'll actually want when libfossil is embedded in a service.
Error handling
Don't log.Fatal from a long-running process — return errors up to a caller that can log, retry, or fail the request.
func openRepo(path string) (*libfossil.Repo, error) {
repo, err := libfossil.Open(path)
if err != nil {
return nil, fmt.Errorf("open %s: %w", path, err)
}
return repo, nil
}
The errors you get back are wrapped Go errors; use errors.Is and errors.As against the sentinel errors exported from the libfossil package when you need to branch on a specific failure (e.g., "repo not found" vs. "schema mismatch").
Context cancellation
Sync, Pull, and Clone accept a context.Context. Pass a request-scoped context with a deadline so a stalled remote doesn't tie up your goroutine forever.
ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second)
defer cancel()
if _, err := repo.Sync(ctx, transport, libfossil.SyncOpts{Pull: true}); err != nil {
return fmt.Errorf("sync: %w", err)
}
The HTTP transport honors the deadline at the network boundary; the rest of the sync state machine checks ctx.Done() between rounds.
Telemetry
Wire an observer to your Repo's operations to surface sync rounds, checkout events, and per-error events to your existing logging or metrics stack. See Extension Points for the full interface, and observer/otel for an OpenTelemetry-ready implementation that emits spans + counters.
import (
libfossil "github.com/danmestas/libfossil"
otelobs "github.com/danmestas/libfossil/observer/otel"
)
obs := otelobs.NewSyncObserver()
_, err := repo.Sync(ctx, transport, libfossil.SyncOpts{
Pull: true,
Observer: obs,
})
observer/otel is a separate Go module so the OpenTelemetry SDK doesn't leak into consumers that don't want it.
Repo handle lifetime
A *Repo wraps a SQLite database connection pool. Open it once at service startup, keep it for the lifetime of the process, and Close() it during shutdown — don't open and close per request.
type service struct {
repo *libfossil.Repo
}
func newService(path string) (*service, error) {
repo, err := libfossil.Open(path)
if err != nil {
return nil, err
}
return &service{repo: repo}, nil
}
func (s *service) Shutdown() error {
return s.repo.Close()
}
Concurrent reads through one Repo are safe — SQLite handles the locking. Writes serialize at the database level; if your service has heavy write traffic, queue write operations through a single goroutine rather than relying on lock contention to back-pressure.
Driver selection
For services targeting Linux/macOS/Windows, the default modernc driver is the right choice — pure Go, static binaries, no surprises. Switch to ncruces only when you're shipping a WASI/browser binary or when you need ncruces-specific behavior:
import _ "github.com/danmestas/libfossil/db/driver/ncruces"
// instead of:
// import _ "github.com/danmestas/libfossil/db/driver/modernc"
Drivers are mutually exclusive — a process can register exactly one. See db for the registry contract.