Skip to content

Commit 2b1a950

Browse files
authored
Merge pull request #97 from barrydeen/lmdb-migration-and-bootstrap-tuning
Migrate eventstore to LMDB and tune WoT bootstrap fetches
2 parents e868939 + 50b1403 commit 2b1a950

9 files changed

Lines changed: 755 additions & 468 deletions

File tree

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,83 @@ Once everything is set up, the relay will be running on `localhost:3334`.
244244
http://localhost:3334
245245
```
246246

247+
## Migrating from Badger to LMDB
248+
249+
Older versions of wot-relay stored events in a Badger database. The relay now
250+
uses LMDB via `fiatjaf.com/nostr/eventstore/lmdb` and Badger is no longer
251+
supported. Because the two backends use different on-disk formats, an
252+
in-place upgrade is not possible — events must be exported to JSONL from the
253+
old store and re-imported into a fresh LMDB store.
254+
255+
1. Stop the relay.
256+
2. Build the legacy exporter (separate module, uses the old Badger code):
257+
258+
```bash
259+
cd tools/export-badger
260+
go build -o ../../export-badger .
261+
cd ../..
262+
```
263+
3. Export every event to JSONL:
264+
265+
```bash
266+
./export-badger -db ./db > events.jsonl
267+
```
268+
4. Move the old database aside (keep it until the new one is verified):
269+
270+
```bash
271+
mv ./db ./db.badger.bak
272+
```
273+
5. Build the new relay and importer:
274+
275+
```bash
276+
go build -ldflags "-X main.version=$(git describe --tags --always)"
277+
go build -o import-jsonl ./cmd/import-jsonl
278+
```
279+
6. Populate a fresh LMDB directory:
280+
281+
```bash
282+
./import-jsonl -db ./db < events.jsonl
283+
```
284+
7. Start the relay as normal. `DB_PATH` now points at the LMDB directory.
285+
286+
### Migrating with Docker
287+
288+
If you run wot-relay via Docker Compose, you don't need Go installed on the
289+
host — the steps above can be run inside the `golang:bookworm` image against
290+
the same `./db` bind mount your compose file already uses.
291+
292+
1. Stop the relay:
293+
294+
```bash
295+
docker compose down
296+
```
297+
2. Export the Badger DB to JSONL:
298+
299+
```bash
300+
docker run --rm -v "$PWD:/src" -w /src golang:bookworm sh -c \
301+
"cd tools/export-badger && go build -o /tmp/export-badger . && /tmp/export-badger -db /src/db > /src/events.jsonl"
302+
```
303+
3. Move the old database aside:
304+
305+
```bash
306+
mv db db.badger.bak
307+
```
308+
4. Import the JSONL into a fresh LMDB directory:
309+
310+
```bash
311+
docker run --rm -v "$PWD:/src" -w /src golang:bookworm sh -c \
312+
"go build -o /tmp/import-jsonl ./cmd/import-jsonl && /tmp/import-jsonl -db /src/db < /src/events.jsonl"
313+
```
314+
5. Rebuild and start the relay:
315+
316+
```bash
317+
docker compose up -d --build
318+
```
319+
320+
Files written by the `docker run` steps will be owned by root on the host
321+
because the container runs as root by default. If that's a problem, `chown`
322+
them back after migration.
323+
247324
## License
248325

249326
This project is licensed under the MIT License.

arm64.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ ENV STATIC_PATH="templates/static"
2222
RUN touch .env
2323

2424
# Build the Go application
25-
RUN go build -tags badger -o main .
25+
RUN go build -o main .
2626

2727
# Expose the port that the application will run on
2828
EXPOSE 3334

cmd/import-jsonl/main.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
"log"
9+
"os"
10+
11+
"fiatjaf.com/nostr"
12+
"fiatjaf.com/nostr/eventstore/lmdb"
13+
)
14+
15+
func main() {
16+
path := flag.String("db", "", "path to the LMDB directory (created if missing)")
17+
skipErrors := flag.Bool("skip-errors", false, "skip events that fail to parse or save instead of aborting")
18+
flag.Parse()
19+
if *path == "" {
20+
fmt.Fprintln(os.Stderr, "usage: import-jsonl -db <path> [-skip-errors] < events.jsonl")
21+
os.Exit(2)
22+
}
23+
24+
db := &lmdb.LMDBBackend{Path: *path}
25+
if err := db.Init(); err != nil {
26+
log.Fatalf("init: %v", err)
27+
}
28+
defer db.Close()
29+
30+
scanner := bufio.NewScanner(os.Stdin)
31+
// events can exceed the default 64KB line limit
32+
scanner.Buffer(make([]byte, 0, 1024*1024), 16*1024*1024)
33+
34+
var saved, failed uint64
35+
var line uint64
36+
for scanner.Scan() {
37+
line++
38+
b := scanner.Bytes()
39+
if len(b) == 0 {
40+
continue
41+
}
42+
43+
var evt nostr.Event
44+
if err := json.Unmarshal(b, &evt); err != nil {
45+
failed++
46+
msg := fmt.Sprintf("line %d: unmarshal: %v", line, err)
47+
if *skipErrors {
48+
log.Println(msg)
49+
continue
50+
}
51+
log.Fatal(msg)
52+
}
53+
54+
if err := db.SaveEvent(evt); err != nil {
55+
failed++
56+
msg := fmt.Sprintf("line %d (id=%s): save: %v", line, evt.ID, err)
57+
if *skipErrors {
58+
log.Println(msg)
59+
continue
60+
}
61+
log.Fatal(msg)
62+
}
63+
saved++
64+
65+
if saved%10000 == 0 {
66+
log.Printf("imported %d events...", saved)
67+
}
68+
}
69+
if err := scanner.Err(); err != nil {
70+
log.Fatalf("scan: %v", err)
71+
}
72+
log.Printf("done: imported %d events, %d failures, %d lines read", saved, failed, line)
73+
}

go.mod

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,53 @@
11
module github.com/bitvora/wot-relay
22

3-
go 1.23.1
3+
go 1.25
44

55
require (
6-
github.com/fiatjaf/eventstore v0.14.4
7-
github.com/fiatjaf/khatru v0.14.0
6+
fiatjaf.com/nostr v0.0.0-20260422002040-5b28d08e4720
87
github.com/joho/godotenv v1.5.1
9-
github.com/nbd-wtf/go-nostr v0.44.2
108
)
119

1210
require (
13-
fiatjaf.com/lib v0.2.0 // indirect
11+
fiatjaf.com/lib v0.3.6 // indirect
12+
github.com/FastFilter/xorfilter v0.2.1 // indirect
13+
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
14+
github.com/PowerDNS/lmdb-go v1.9.3 // indirect
1415
github.com/andybalholm/brotli v1.1.1 // indirect
1516
github.com/bep/debounce v1.2.1 // indirect
17+
github.com/btcsuite/btcd v0.24.2 // indirect
1618
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
19+
github.com/btcsuite/btcd/btcutil v1.1.5 // indirect
1720
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
1821
github.com/cespare/xxhash/v2 v2.3.0 // indirect
22+
github.com/coder/websocket v1.8.13 // indirect
1923
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
20-
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
21-
github.com/dgraph-io/badger/v4 v4.5.0 // indirect
22-
github.com/dgraph-io/ristretto v1.0.0 // indirect
23-
github.com/dgraph-io/ristretto/v2 v2.0.0 // indirect
24+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
25+
github.com/dgraph-io/ristretto/v2 v2.3.0 // indirect
2426
github.com/dustin/go-humanize v1.0.1 // indirect
25-
github.com/fasthttp/websocket v1.5.11 // indirect
26-
github.com/gobwas/httphead v0.1.0 // indirect
27-
github.com/gobwas/pool v0.2.1 // indirect
28-
github.com/gobwas/ws v1.4.0 // indirect
29-
github.com/gogo/protobuf v1.3.2 // indirect
30-
github.com/golang/glog v1.1.2 // indirect
31-
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
32-
github.com/golang/protobuf v1.5.4 // indirect
33-
github.com/golang/snappy v0.0.4 // indirect
34-
github.com/google/flatbuffers v24.3.25+incompatible // indirect
27+
github.com/elnosh/gonuts v0.4.2 // indirect
28+
github.com/fasthttp/websocket v1.5.12 // indirect
29+
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
3530
github.com/josharian/intern v1.0.0 // indirect
3631
github.com/json-iterator/go v1.1.12 // indirect
37-
github.com/klauspost/compress v1.17.11 // indirect
32+
github.com/klauspost/compress v1.18.0 // indirect
3833
github.com/mailru/easyjson v0.9.0 // indirect
3934
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
4035
github.com/modern-go/reflect2 v1.0.2 // indirect
41-
github.com/pkg/errors v0.9.1 // indirect
42-
github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
36+
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
4337
github.com/rs/cors v1.11.1 // indirect
4438
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
39+
github.com/templexxx/cpu v0.0.1 // indirect
40+
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b // indirect
4541
github.com/tidwall/gjson v1.18.0 // indirect
4642
github.com/tidwall/match v1.1.1 // indirect
4743
github.com/tidwall/pretty v1.2.1 // indirect
4844
github.com/valyala/bytebufferpool v1.0.0 // indirect
49-
github.com/valyala/fasthttp v1.58.0 // indirect
50-
go.opencensus.io v0.24.0 // indirect
51-
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
52-
golang.org/x/net v0.33.0 // indirect
53-
golang.org/x/sys v0.28.0 // indirect
54-
google.golang.org/protobuf v1.35.2 // indirect
45+
github.com/valyala/fasthttp v1.59.0 // indirect
46+
github.com/x448/float16 v0.8.4 // indirect
47+
golang.org/x/crypto v0.39.0 // indirect
48+
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
49+
golang.org/x/net v0.41.0 // indirect
50+
golang.org/x/sync v0.15.0 // indirect
51+
golang.org/x/sys v0.35.0 // indirect
52+
gopkg.in/yaml.v3 v3.0.1 // indirect
5553
)

0 commit comments

Comments
 (0)