~bigbes/sr-ht-dolt

ref: a674ddb16a28934ee97a48a3938c866ad1bc508f sr-ht-dolt/browse/open.go -rw-r--r-- 4.3 KiB
a674ddb1 — Eugene Blikh ci: publish the apk into artifacts.sr.ht as well 4 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Package browse is the read-only web-browsing data layer over bare Dolt
// chunk stores. It is deliberately the only package in dolt.sr.ht that reaches
// into low-level dolthub/dolt internals (nbs, prolly, durable, diff), so that
// all version-fragile code stays contained here and a future module bump only
// needs to be re-verified against this one package.
//
// # Bare stores have no working set
//
// dolt.sr.ht serves bare NBS chunk-store directories (created via
// doltdb.WriteEmptyRepo and grown by pushes over the remotesapi). They have no
// .dolt/ working set, so the sqle engine and the embedded driver cannot open
// them. Everything here reads from committed roots only.
//
// # Open discipline (why not doltdb.LoadDoltDB)
//
// The obvious entry point, doltdb.LoadDoltDB, routes through the file
// dbfactory, which wraps the store in a GenerationalNBS (newgen + an oldgen
// subdir + a ghost gen). In the pinned dolt/go version
// (v0.40.5-0.20260626152440-45335d44ad79), calling Close() on such a
// generational store over one of our bare stores panics deep in nbs:
//
//	panic: Close() called and reduced ref count to < 0.
//	  store/nbs/table_index.go:532 onHeapTableIndex.Close
//	  ... GenerationalNBS.Close -> NomsBlockStore.Close -> tableSet.close
//
// It reproduces on a plain open-read-only-then-Close, independent of any
// reads, so the documented open-per-request + Close pattern would crash the
// web process, not just tests. A single (non-generational) nbs.NewLocalStore
// — the exact construction storage.Cache uses to serve pushes — closes
// cleanly. We therefore build the DoltDB by hand from one NewLocalStore via
// doltdb.DoltDBFromCS and never touch the generational path.
//
// # Freshness and concurrency
//
// Open is called once per request and Close releases the handle, so every
// request gets a fresh read of the on-disk manifest and observes commits
// landed by the push writer since the last request — no stale cache, no
// refresh dance. NBS readers are manifest-based and append-only: opening a
// second NewLocalStore over a directory the push writer is also serving is
// safe (no exclusive lock is held for the store's lifetime; the manifest lock
// is taken only briefly during atomic updates), so a reader always sees a
// consistent snapshot.
package browse

import (
	"context"
	"errors"
	"fmt"
	"path/filepath"

	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
	"github.com/dolthub/dolt/go/store/nbs"
	"github.com/dolthub/dolt/go/store/types"
)

// readStoreMemTableSize bounds the in-memory memtable of the read handle. It
// matches the value storage.Cache uses for served stores; browsing never
// writes, so the memtable stays empty in practice.
const readStoreMemTableSize = 128 * 1024 * 1024

// ErrRefNotFound is returned when a ref string matches neither an existing
// branch nor a resolvable commit hash.
var ErrRefNotFound = errors.New("browse: ref not found")

// ErrTableNotFound is returned when a table does not exist in the resolved
// root value.
var ErrTableNotFound = errors.New("browse: table not found")

// DB is a read-only handle to a single bare Dolt chunk store. It is not safe
// for concurrent use; open one per request and Close it when done.
type DB struct {
	ddb  *doltdb.DoltDB
	path string
}

// Open opens the bare Dolt chunk store at diskPath for read-only browsing.
// diskPath is the store directory itself (the dir that holds the NBS manifest
// and table files), not a parent. The caller must Close the returned DB.
//
// See the package doc for why this bypasses doltdb.LoadDoltDB.
func Open(ctx context.Context, diskPath string) (*DB, error) {
	cs, err := nbs.NewLocalStore(
		ctx,
		types.Format_DOLT.VersionString(),
		diskPath,
		readStoreMemTableSize,
		nbs.NewUnlimitedMemQuotaProvider(),
		false,
	)
	if err != nil {
		return nil, fmt.Errorf("browse: open store %q: %w", diskPath, err)
	}

	ddb, err := doltdb.DoltDBFromCS(cs, filepath.Base(diskPath))
	if err != nil {
		// cs is not yet owned by a DoltDB, close it directly.
		_ = cs.Close()
		return nil, fmt.Errorf("browse: build doltdb for %q: %w", diskPath, err)
	}

	return &DB{ddb: ddb, path: diskPath}, nil
}

// Close releases the underlying chunk store. It closes the single
// non-generational NBS store, which is safe in the pinned version (unlike the
// generational store LoadDoltDB would have produced).
func (db *DB) Close() error {
	return db.ddb.Close()
}