Skip to content

Commit 8f9a6ca

Browse files
authored
Litestream lightweight read-replicas. (#328)
1 parent 99b097d commit 8f9a6ca

File tree

8 files changed

+675
-1
lines changed

8 files changed

+675
-1
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
- name: Test GORM
7575
shell: bash
7676
run: gormlite/test.sh
77-
if: matrix.os != 'windows-latest'
77+
if: matrix.os == 'ubuntu-latest'
7878

7979
- name: Collect coverage
8080
run: |

litestream/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Litestream lightweight read-replicas
2+
3+
This package implements the **EXPERIMENTAL** `"litestream"` SQLite VFS
4+
that offers Litestream [lightweight read-replicas](https://fly.io/blog/litestream-revamped/#lightweight-read-replicas).
5+
6+
See the [example](vfs_test.go) for how to use.
7+
8+
To improve performance,
9+
increase `PollInterval` (and `MinLevel`) as much as you can,
10+
and set [`PRAGMA cache_size=N`](https://www.sqlite.org/pragma.html#pragma_cache_size)
11+
(or use `_pragma=cache_size(N)`).

litestream/api.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Package litestream implements a Litestream lightweight read-replica VFS.
2+
package litestream
3+
4+
import (
5+
"log/slog"
6+
"sync"
7+
"time"
8+
9+
"github.com/benbjohnson/litestream"
10+
"github.com/ncruces/go-sqlite3/vfs"
11+
)
12+
13+
// The default poll interval.
14+
const DefaultPollInterval = 1 * time.Second
15+
16+
func init() {
17+
vfs.Register("litestream", liteVFS{})
18+
}
19+
20+
var (
21+
liteMtx sync.RWMutex
22+
// +checklocks:liteMtx
23+
liteDBs = map[string]*liteDB{}
24+
)
25+
26+
// ReplicaOptions represents options for [NewReplica].
27+
type ReplicaOptions struct {
28+
// Where to log error messages. May be nil.
29+
Logger *slog.Logger
30+
// Minimum compaction level to track.
31+
MinLevel int
32+
// Replica poll interval. Must be less than the compaction interval
33+
// used by the replica at MinLevel+1.
34+
PollInterval time.Duration
35+
}
36+
37+
// NewReplica creates a read-replica from a Litestream client.
38+
func NewReplica(name string, client litestream.ReplicaClient, options ReplicaOptions) {
39+
if options.Logger != nil {
40+
options.Logger = options.Logger.With("name", name)
41+
} else {
42+
options.Logger = slog.New(slog.DiscardHandler)
43+
}
44+
if options.PollInterval <= 0 {
45+
options.PollInterval = DefaultPollInterval
46+
}
47+
options.MinLevel = max(0, min(options.MinLevel, litestream.SnapshotLevel))
48+
49+
liteMtx.Lock()
50+
defer liteMtx.Unlock()
51+
liteDBs[name] = &liteDB{
52+
client: client,
53+
opts: &options,
54+
}
55+
}
56+
57+
// RemoveReplica removes a replica by name.
58+
func RemoveReplica(name string) {
59+
liteMtx.Lock()
60+
defer liteMtx.Unlock()
61+
delete(liteDBs, name)
62+
}

litestream/go.mod

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
module github.com/ncruces/go-sqlite3/litestream
2+
3+
go 1.24.4
4+
5+
require (
6+
github.com/benbjohnson/litestream v0.5.2
7+
github.com/ncruces/go-sqlite3 v0.30.1
8+
github.com/ncruces/wbt v0.2.0
9+
github.com/superfly/ltx v0.5.0
10+
)
11+
12+
// github.com/ncruces/go-sqlite3
13+
require (
14+
github.com/ncruces/julianday v1.0.0 // indirect
15+
github.com/tetratelabs/wazero v1.10.0 // indirect
16+
golang.org/x/sys v0.38.0 // indirect
17+
)
18+
19+
// github.com/superfly/ltx
20+
require github.com/pierrec/lz4/v4 v4.1.22 // indirect
21+
22+
// github.com/benbjohnson/litestream
23+
require (
24+
filippo.io/age v1.2.1 // indirect
25+
github.com/beorn7/perks v1.0.1 // indirect
26+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
27+
github.com/kr/text v0.2.0 // indirect
28+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
29+
github.com/prometheus/client_golang v1.23.2 // indirect
30+
github.com/prometheus/client_model v0.6.2 // indirect
31+
github.com/prometheus/common v0.67.2 // indirect
32+
github.com/prometheus/procfs v0.19.2 // indirect
33+
github.com/psanford/sqlite3vfs v0.0.0-20240315230605-24e1d98cf361 // indirect
34+
go.yaml.in/yaml/v2 v2.4.3 // indirect
35+
golang.org/x/crypto v0.43.0 // indirect
36+
google.golang.org/protobuf v1.36.10 // indirect
37+
modernc.org/sqlite v1.40.0 // indirect
38+
)
39+
40+
// github.com/benbjohnson/litestream/s3
41+
require (
42+
github.com/aws/aws-sdk-go-v2 v1.37.1 // indirect
43+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect
44+
github.com/aws/aws-sdk-go-v2/config v1.30.2 // indirect
45+
github.com/aws/aws-sdk-go-v2/credentials v1.18.2 // indirect
46+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 // indirect
47+
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.2 // indirect
48+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1 // indirect
49+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1 // indirect
50+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
51+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.1 // indirect
52+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect
53+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1 // indirect
54+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1 // indirect
55+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1 // indirect
56+
github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1 // indirect
57+
github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 // indirect
58+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 // indirect
59+
github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 // indirect
60+
github.com/aws/smithy-go v1.22.5 // indirect
61+
)
62+
63+
replace modernc.org/sqlite => github.com/ncruces/go-sqlite3/litestream/modernc v0.0.0-20251109124432-99b097de3b79

0 commit comments

Comments
 (0)