Skip to content

Commit 4986387

Browse files
committed
fix: don't create value gc closer to avoid goroutine leak and add more ReadOnly guards on :
- Sync - RunValueLogGC - Flatten - BanNamespace - GetSequence
1 parent 12f7f45 commit 4986387

4 files changed

Lines changed: 43 additions & 6 deletions

File tree

db.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ func Open(opt Options) (*DB, error) {
378378
db.closers.writes = z.NewCloser(1)
379379
go db.doWrites(db.closers.writes)
380380

381-
if !db.opt.InMemory {
381+
if !db.opt.InMemory && !db.opt.ReadOnly {
382382
db.closers.valueGC = z.NewCloser(1)
383383
go db.vlog.waitOnGC(db.closers.valueGC)
384384
}
@@ -671,8 +671,8 @@ const (
671671
// Sync syncs database content to disk. This function provides
672672
// more control to user to sync data whenever required.
673673
func (db *DB) Sync() error {
674-
if db.opt.InMemory {
675-
// InMemory mode does not use WAL/vlog files, so Sync is a no-op.
674+
if db.opt.InMemory || db.opt.ReadOnly {
675+
// InMemory and read-only modes do not use WAL/vlog files, so Sync is a no-op.
676676
return nil
677677
}
678678

@@ -1235,6 +1235,9 @@ func (db *DB) RunValueLogGC(discardRatio float64) error {
12351235
if db.opt.InMemory {
12361236
return ErrGCInMemoryMode
12371237
}
1238+
if db.opt.ReadOnly {
1239+
return ErrGCInReadOnlyMode
1240+
}
12381241
if discardRatio >= 1.0 || discardRatio <= 0.0 {
12391242
return ErrInvalidRequest
12401243
}
@@ -1355,6 +1358,9 @@ func (db *DB) GetSequence(key []byte, bandwidth uint64) (*Sequence, error) {
13551358
if db.opt.managedTxns {
13561359
panic("Cannot use GetSequence with managedDB=true.")
13571360
}
1361+
if db.opt.ReadOnly {
1362+
panic("Cannot use GetSequence in read-only mode.")
1363+
}
13581364

13591365
switch {
13601366
case len(key) == 0:
@@ -1563,6 +1569,9 @@ func (db *DB) startMemoryFlush() {
15631569
// stopped. Ideally, no writes are going on during Flatten. Otherwise, it would create competition
15641570
// between flattening the tree and new tables being created at level zero.
15651571
func (db *DB) Flatten(workers int) error {
1572+
if db.opt.ReadOnly {
1573+
panic("Cannot flatten in read-only mode.")
1574+
}
15661575

15671576
db.stopCompactions()
15681577
defer db.startCompactions()
@@ -1851,6 +1860,9 @@ func (db *DB) BanNamespace(ns uint64) error {
18511860
if db.opt.NamespaceOffset < 0 {
18521861
return ErrNamespaceMode
18531862
}
1863+
if db.opt.ReadOnly {
1864+
panic("Cannot ban namespace in read-only mode.")
1865+
}
18541866
db.opt.Infof("Banning namespace: %d", ns)
18551867
// First set the banned namespaces in DB and then update the in-memory structure.
18561868
key := y.KeyWithTs(append(bannedNsKey, y.U64ToBytes(ns)...), 1)

db_test.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,12 +1769,10 @@ func TestReadOnly(t *testing.T) {
17691769
opts.ReadOnly = true
17701770
kv1, err := Open(opts)
17711771
require.NoError(t, err)
1772-
defer kv1.Close()
17731772

17741773
// Open another read-only
17751774
kv2, err := Open(opts)
17761775
require.NoError(t, err)
1777-
defer kv2.Close()
17781776

17791777
// Attempt a read-write open while it's open for read-only
17801778
opts.ReadOnly = false
@@ -1791,6 +1789,10 @@ func TestReadOnly(t *testing.T) {
17911789
require.Equal(t, b1, []byte("value1"))
17921790
err = txn1.Commit()
17931791
require.NoError(t, err)
1792+
err = kv1.RunValueLogGC(0.5)
1793+
require.Error(t, err, ErrGCInReadOnlyMode)
1794+
err = kv1.Sync()
1795+
require.NoError(t, err)
17941796

17951797
// Get a thing from the DB via the other connection
17961798
txn2 := kv2.NewTransaction(true)
@@ -1809,6 +1811,26 @@ func TestReadOnly(t *testing.T) {
18091811
require.Contains(t, err.Error(), "No sets or deletes are allowed in a read-only transaction")
18101812
err = txn.Commit()
18111813
require.NoError(t, err)
1814+
1815+
// Close
1816+
require.NoError(t, kv1.Close())
1817+
require.NoError(t, kv2.Close())
1818+
1819+
// Test os permission read-only open
1820+
// We don't directly chmod the directory to still be able to aquire the lock
1821+
err = os.Chmod(dir, 0o500)
1822+
require.NoError(t, err)
1823+
opts.ReadOnly = true
1824+
kv3, err := Open(opts)
1825+
require.NoError(t, err)
1826+
txn3 := kv3.NewTransaction(true)
1827+
_, err = txn3.Get([]byte("key1"))
1828+
require.NoError(t, err)
1829+
require.NoError(t, kv3.Close())
1830+
require.NoError(t, txn3.Commit())
1831+
1832+
// Restore permissions for cleanup
1833+
require.NoError(t, os.Chmod(dir, 0o700))
18121834
}
18131835

18141836
func TestLSMOnly(t *testing.T) {

errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ var (
109109
// ErrGCInMemoryMode is returned when db.RunValueLogGC is called in in-memory mode.
110110
ErrGCInMemoryMode = stderrors.New("Cannot run value log GC when DB is opened in InMemory mode")
111111

112+
// ErrGCInReadOnlyMode is returned when db.RunValueLogGC is called in read-only mode.
113+
ErrGCInReadOnlyMode = stderrors.New("Cannot run value log GC when DB is opened in ReadOnly mode")
114+
112115
// ErrDBClosed is returned when a get operation is performed after closing the DB.
113116
ErrDBClosed = stderrors.New("DB Closed")
114117
)

value.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,7 @@ func (vlog *valueLog) runGC(discardRatio float64) error {
10991099
}
11001100

11011101
func (vlog *valueLog) updateDiscardStats(stats map[uint32]int64) {
1102-
if vlog.opt.InMemory {
1102+
if vlog.opt.InMemory || vlog.opt.ReadOnly {
11031103
return
11041104
}
11051105
for fid, discard := range stats {

0 commit comments

Comments
 (0)