Skip to content

Instantly share code, notes, and snippets.

@trevordixon
Created August 20, 2026 17:36
Show Gist options
  • Select an option

  • Save trevordixon/1bd9f9e0a88b1c94e0bd3b5efb475b77 to your computer and use it in GitHub Desktop.

Select an option

Save trevordixon/1bd9f9e0a88b1c94e0bd3b5efb475b77 to your computer and use it in GitHub Desktop.
Minimal PocketBase backup split-visibility reproducer

Minimal PocketBase backup reproducer

Requires Docker:

docker build -t pocketbase-backup-repro .
docker run --rm pocketbase-backup-repro

Expected output:

PocketBase sees 2 records; fresh SQLite sees 1
REPRODUCED: readers disagree
FROM golang:1.25-bookworm
RUN apt-get update && apt-get install -y --no-install-recommends sqlite3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /repro
COPY go.mod main.go ./
RUN go mod tidy && go build -o /repro/run .
CMD ["/repro/run"]
module pocketbase-backup-posix-locks-minimal
go 1.25.0
require github.com/pocketbase/pocketbase v0.39.11
package main
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
)
func main() {
dir, err := os.MkdirTemp("", "pb-backup-repro-")
must(err)
defer os.RemoveAll(dir)
app := pocketbase.NewWithConfig(pocketbase.Config{
DefaultDataDir: dir,
DataMaxOpenConns: 1,
DataMaxIdleConns: 1,
})
must(app.Bootstrap())
defer app.ResetBootstrapState()
collection := core.NewBaseCollection("events")
collection.Fields.Add(&core.TextField{Name: "value", Required: true})
must(app.Save(collection))
save(app, collection, "before")
must(app.CreateBackup(context.Background(), "repro.zip"))
dbPath := filepath.Join(dir, "data.db")
backupPath := filepath.Join(dir, "online-backup.db")
runSQLite(dbPath, fmt.Sprintf(".backup %q", backupPath))
save(app, collection, "after")
records, err := app.FindRecordsByFilter(collection, "", "", 0, 0)
must(err)
freshCount := strings.TrimSpace(runSQLite(dbPath, "SELECT count(*) FROM events;"))
fmt.Printf("PocketBase sees %d records; fresh SQLite sees %s\n", len(records), freshCount)
if len(records) == 2 && freshCount == "1" {
fmt.Println("REPRODUCED: readers disagree")
return
}
fmt.Println("not reproduced")
os.Exit(1)
}
func save(app core.App, collection *core.Collection, value string) {
record := core.NewRecord(collection)
record.Set("value", value)
must(app.Save(record))
}
func runSQLite(dbPath, statement string) string {
out, err := exec.Command("sqlite3", dbPath, ".timeout 10000", statement).CombinedOutput()
if err != nil {
panic(fmt.Sprintf("sqlite3: %v: %s", err, out))
}
return string(out)
}
func must(err error) {
if err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment