coverage~bigbes/sr-ht-dolt3523280cbrowse/open.go

Coverage
0.0% 0/9 statements
Δ
+0.0
Blob
00d6df4
1 // Package browse is the read-only web-browsing data layer over bare Dolt
2 // chunk stores. It is deliberately the only package in dolt.sr.ht that reaches
3 // into low-level dolthub/dolt internals (nbs, prolly, durable, diff), so that
4 // all version-fragile code stays contained here and a future module bump only
5 // needs to be re-verified against this one package.
6 //
7 // # Bare stores have no working set
8 //
9 // dolt.sr.ht serves bare NBS chunk-store directories (created via
10 // doltdb.WriteEmptyRepo and grown by pushes over the remotesapi). They have no
11 // .dolt/ working set, so the sqle engine and the embedded driver cannot open
12 // them. Everything here reads from committed roots only.
13 //
14 // # Open discipline (why not doltdb.LoadDoltDB)
15 //
16 // The obvious entry point, doltdb.LoadDoltDB, routes through the file
17 // dbfactory, which wraps the store in a GenerationalNBS (newgen + an oldgen
18 // subdir + a ghost gen). In the pinned dolt/go version
19 // (v0.40.5-0.20260626152440-45335d44ad79), calling Close() on such a
20 // generational store over one of our bare stores panics deep in nbs:
21 //
22 // panic: Close() called and reduced ref count to < 0.
23 // store/nbs/table_index.go:532 onHeapTableIndex.Close
24 // ... GenerationalNBS.Close -> NomsBlockStore.Close -> tableSet.close
25 //
26 // It reproduces on a plain open-read-only-then-Close, independent of any
27 // reads, so the documented open-per-request + Close pattern would crash the
28 // web process, not just tests. A single (non-generational) nbs.NewLocalStore
29 // — the exact construction storage.Cache uses to serve pushes — closes
30 // cleanly. We therefore build the DoltDB by hand from one NewLocalStore via
31 // doltdb.DoltDBFromCS and never touch the generational path.
32 //
33 // # Freshness and concurrency
34 //
35 // Open is called once per request and Close releases the handle, so every
36 // request gets a fresh read of the on-disk manifest and observes commits
37 // landed by the push writer since the last request — no stale cache, no
38 // refresh dance. NBS readers are manifest-based and append-only: opening a
39 // second NewLocalStore over a directory the push writer is also serving is
40 // safe (no exclusive lock is held for the store's lifetime; the manifest lock
41 // is taken only briefly during atomic updates), so a reader always sees a
42 // consistent snapshot.
43 package browse
44
45 import (
46 "context"
47 "errors"
48 "fmt"
49 "path/filepath"
50
51 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
52 "github.com/dolthub/dolt/go/store/nbs"
53 "github.com/dolthub/dolt/go/store/types"
54 )
55
56 // readStoreMemTableSize bounds the in-memory memtable of the read handle. It
57 // matches the value storage.Cache uses for served stores; browsing never
58 // writes, so the memtable stays empty in practice.
59 const readStoreMemTableSize = 128 * 1024 * 1024
60
61 // ErrRefNotFound is returned when a ref string matches neither an existing
62 // branch nor a resolvable commit hash.
63 var ErrRefNotFound = errors.New("browse: ref not found")
64
65 // ErrTableNotFound is returned when a table does not exist in the resolved
66 // root value.
67 var ErrTableNotFound = errors.New("browse: table not found")
68
69 // DB is a read-only handle to a single bare Dolt chunk store. It is not safe
70 // for concurrent use; open one per request and Close it when done.
71 type DB struct {
72 ddb *doltdb.DoltDB
73 path string
74 }
75
76 // Open opens the bare Dolt chunk store at diskPath for read-only browsing.
77 // diskPath is the store directory itself (the dir that holds the NBS manifest
78 // and table files), not a parent. The caller must Close the returned DB.
79 //
80 // See the package doc for why this bypasses doltdb.LoadDoltDB.
81 0 func Open(ctx context.Context, diskPath string) (*DB, error) {
82 0 cs, err := nbs.NewLocalStore(
83 0 ctx,
84 0 types.Format_DOLT.VersionString(),
85 0 diskPath,
86 0 readStoreMemTableSize,
87 0 nbs.NewUnlimitedMemQuotaProvider(),
88 0 false,
89 0 )
90 0 if err != nil {
91 0 return nil, fmt.Errorf("browse: open store %q: %w", diskPath, err)
92 0 }
93
94 0 ddb, err := doltdb.DoltDBFromCS(cs, filepath.Base(diskPath))
95 0 if err != nil {
96 0 // cs is not yet owned by a DoltDB, close it directly.
97 0 _ = cs.Close()
98 0 return nil, fmt.Errorf("browse: build doltdb for %q: %w", diskPath, err)
99 0 }
100
101 0 return &DB{ddb: ddb, path: diskPath}, nil
102 }
103
104 // Close releases the underlying chunk store. It closes the single
105 // non-generational NBS store, which is safe in the pinned version (unlike the
106 // generational store LoadDoltDB would have produced).
107 0 func (db *DB) Close() error {
108 0 return db.ddb.Close()
109 0 }