mmm: read-only mode.

This commit is contained in:
fiatjaf
2026-01-18 17:56:59 -03:00
parent df64c5b6ec
commit b559828c72
6 changed files with 90 additions and 46 deletions

View File

@@ -86,7 +86,8 @@ var app = &cli.Command{
end = db.Close
case "mmm":
var err error
if db, err, end = doMmmInit(path); err != nil {
readonly := c.Args().First() == "query" || c.Args().First() == "count"
if db, err, end = doMmmInit(path, readonly); err != nil {
if end != nil {
end()
}

View File

@@ -11,13 +11,14 @@ import (
"github.com/rs/zerolog"
)
func doMmmInit(path string) (eventstore.Store, error, func()) {
func doMmmInit(path string, readonly bool) (eventstore.Store, error, func()) {
logger := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
w.Out = os.Stderr
}))
mmmm := mmm.MultiMmapManager{
Dir: filepath.Dir(path),
Logger: &logger,
ReadOnly: readonly,
}
if err := mmmm.Init(); err != nil {
return nil, err, nil

View File

@@ -11,6 +11,10 @@ import (
)
func (il *IndexingLayer) DeleteEvent(id nostr.ID) error {
if il.mmmm.ReadOnly {
return ReadOnly
}
il.mmmm.writeMutex.Lock()
defer il.mmmm.writeMutex.Unlock()

View File

@@ -2,6 +2,7 @@ package mmm
import (
"encoding/binary"
"errors"
"fmt"
"os"
"path/filepath"
@@ -20,9 +21,12 @@ type mmap []byte
func (_ mmap) String() string { return "<memory-mapped file>" }
var ReadOnly = errors.New("mmm in read-only mode")
type MultiMmapManager struct {
Dir string
Logger *zerolog.Logger
ReadOnly bool
layers IndexingLayers
@@ -62,6 +66,7 @@ func (b *MultiMmapManager) Init() error {
return fmt.Errorf("failed to create directory %s: %w", dbpath, err)
}
if !b.ReadOnly {
// create lockfile to prevent multiple instances
lockfilePath := filepath.Join(b.Dir, "mmmm.lock")
if _, err := os.OpenFile(lockfilePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644); err != nil {
@@ -70,6 +75,7 @@ func (b *MultiMmapManager) Init() error {
}
return fmt.Errorf("failed to create lockfile %s: %w", lockfilePath, err)
}
}
// open a huge mmapped file
b.mmapfPath = filepath.Join(b.Dir, "events")
@@ -131,6 +137,7 @@ func (b *MultiMmapManager) Init() error {
b.indexId = dbi
}
if !b.ReadOnly {
// scan index table to calculate free ranges from used positions
b.freeRanges, err = b.gatherFreeRanges(txn)
if err != nil {
@@ -144,6 +151,7 @@ func (b *MultiMmapManager) Init() error {
}
}
logOp.Msg("calculated free ranges from index scan")
}
return nil
}); err != nil {
@@ -162,7 +170,9 @@ func (b *MultiMmapManager) EnsureLayer(name string) (*IndexingLayer, error) {
name: name,
}
err := b.lmdbEnv.Update(func(txn *lmdb.Txn) error {
var err error
if b.ReadOnly {
err = b.lmdbEnv.View(func(txn *lmdb.Txn) error {
txn.RawRead = true
nameb := []byte(name)
@@ -190,6 +200,22 @@ func (b *MultiMmapManager) EnsureLayer(name string) (*IndexingLayer, error) {
return err
}
})
} else {
err = b.lmdbEnv.Update(func(txn *lmdb.Txn) error {
txn.RawRead = true
nameb := []byte(name)
if idv, err := txn.Get(b.knownLayers, nameb); err == nil {
if err := il.Init(); err != nil {
return fmt.Errorf("failed to init read-only layer %s: %w", name, err)
}
il.id = binary.BigEndian.Uint16(idv)
return nil
} else {
return err
}
})
}
if err != nil {
return nil, err
}
@@ -199,6 +225,10 @@ func (b *MultiMmapManager) EnsureLayer(name string) (*IndexingLayer, error) {
}
func (b *MultiMmapManager) DropLayer(name string) error {
if b.ReadOnly {
return ReadOnly
}
b.writeMutex.Lock()
defer b.writeMutex.Unlock()

View File

@@ -11,6 +11,10 @@ import (
)
func (il *IndexingLayer) ReplaceEvent(evt nostr.Event) error {
if il.mmmm.ReadOnly {
return ReadOnly
}
il.mmmm.writeMutex.Lock()
defer il.mmmm.writeMutex.Unlock()

View File

@@ -16,6 +16,10 @@ import (
)
func (il *IndexingLayer) SaveEvent(evt nostr.Event) error {
if il.mmmm.ReadOnly {
return ReadOnly
}
il.mmmm.writeMutex.Lock()
defer il.mmmm.writeMutex.Unlock()