Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ReadOnly config option for read-only filesystems #53

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions datalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ func parseSegmentName(name string) (uint16, uint64, error) {
}

func (dl *datalog) openSegment(name string, id uint16, seqID uint64) (*segment, error) {
f, err := openFile(dl.opts.FileSystem, name, false)
f, err := openFile(dl.opts.FileSystem, name, false, dl.opts.ReadOnly)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if rather than modifying all openFile calls it would be cleaner to implement a new file system wrapper similar to fs.Sub? The wrapper would set os.O_RDONLY flags and implement a noop CreateLockFile.

if err != nil {
return nil, err
}

meta := &segmentMeta{}
if !f.empty() {
metaName := name + metaExt
if err := readGobFile(dl.opts.FileSystem, metaName, &meta); err != nil {
if err := readGobFile(dl.opts.FileSystem, metaName, &meta, dl.opts.ReadOnly); err != nil {
logger.Printf("error reading segment meta %d: %v", id, err)
// TODO: rebuild meta?
}
Expand Down Expand Up @@ -235,7 +235,7 @@ func (dl *datalog) close() error {
return err
}
metaName := seg.name + metaExt
if err := writeGobFile(dl.opts.FileSystem, metaName, seg.meta); err != nil {
if err := writeGobFile(dl.opts.FileSystem, metaName, seg.meta, dl.opts.ReadOnly); err != nil {
return err
}
}
Expand Down
32 changes: 21 additions & 11 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,28 @@ func Open(path string, opts *Options) (*DB, error) {
return nil, err
}

// Try to acquire a file lock.
lock, acquiredExistingLock, err := createLockFile(opts)
var lock fs.LockFile
var err error
var acquiredExistingLock bool
if !opts.ReadOnly {
// Try to acquire a file lock.
lock, acquiredExistingLock, err = createLockFile(opts)
}
if err != nil {
if err == os.ErrExist {
err = errLocked
}
return nil, errors.Wrap(err, "creating lock file")
}
clean := lock.Unlock
defer func() {
if clean != nil {
_ = clean()
}
}()
var clean func() error
if !opts.ReadOnly {
clean = lock.Unlock
defer func() {
if clean != nil {
_ = clean()
}
}()
}

if acquiredExistingLock {
// Lock file already existed, but the process managed to acquire it.
Expand Down Expand Up @@ -121,7 +129,9 @@ func Open(path string, opts *Options) (*DB, error) {
db.startBackgroundWorker()
}

clean = nil
if !opts.ReadOnly {
clean = nil
}
return db, nil
}

Expand All @@ -135,12 +145,12 @@ func (db *DB) writeMeta() error {
m := dbMeta{
HashSeed: db.hashSeed,
}
return writeGobFile(db.opts.FileSystem, dbMetaName, m)
return writeGobFile(db.opts.FileSystem, dbMetaName, m, db.opts.ReadOnly)
}

func (db *DB) readMeta() error {
m := dbMeta{}
if err := readGobFile(db.opts.FileSystem, dbMetaName, &m); err != nil {
if err := readGobFile(db.opts.FileSystem, dbMetaName, &m, db.opts.ReadOnly); err != nil {
return err
}
db.hashSeed = m.HashSeed
Expand Down
11 changes: 7 additions & 4 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ type file struct {
size int64
}

func openFile(fsyst fs.FileSystem, name string, truncate bool) (*file, error) {
flag := os.O_CREATE | os.O_RDWR
if truncate {
flag |= os.O_TRUNC
func openFile(fsyst fs.FileSystem, name string, truncate bool, readOnly bool) (*file, error) {
var flag int = os.O_RDONLY
if !readOnly {
flag = os.O_CREATE | os.O_RDWR
if truncate {
flag |= os.O_TRUNC
}
}
fi, err := fsyst.OpenFile(name, flag, os.FileMode(0640))
f := &file{}
Expand Down
8 changes: 4 additions & 4 deletions gobfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"github.com/akrylysov/pogreb/fs"
)

func readGobFile(fsys fs.FileSystem, name string, v interface{}) error {
f, err := openFile(fsys, name, false)
func readGobFile(fsys fs.FileSystem, name string, v interface{}, readOnly bool) error {
f, err := openFile(fsys, name, false, readOnly)
if err != nil {
return err
}
Expand All @@ -16,8 +16,8 @@ func readGobFile(fsys fs.FileSystem, name string, v interface{}) error {
return dec.Decode(v)
}

func writeGobFile(fsys fs.FileSystem, name string, v interface{}) error {
f, err := openFile(fsys, name, true)
func writeGobFile(fsys fs.FileSystem, name string, v interface{}, readOnly bool) error {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the desired effect of writing to a file opened in read-only mode? Wouldn't it always fail?

f, err := openFile(fsys, name, true, readOnly)
if err != nil {
return err
}
Expand Down
8 changes: 4 additions & 4 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ type indexMeta struct {
type matchKeyFunc func(slot) (bool, error)

func openIndex(opts *Options) (*index, error) {
main, err := openFile(opts.FileSystem, indexMainName, false)
main, err := openFile(opts.FileSystem, indexMainName, false, opts.ReadOnly)
if err != nil {
return nil, errors.Wrap(err, "opening main index")
}
overflow, err := openFile(opts.FileSystem, indexOverflowName, false)
overflow, err := openFile(opts.FileSystem, indexOverflowName, false, opts.ReadOnly)
if err != nil {
_ = main.Close()
return nil, errors.Wrap(err, "opening overflow index")
Expand Down Expand Up @@ -76,12 +76,12 @@ func (idx *index) writeMeta() error {
SplitBucketIndex: idx.splitBucketIdx,
FreeOverflowBuckets: idx.freeBucketOffs,
}
return writeGobFile(idx.opts.FileSystem, indexMetaName, m)
return writeGobFile(idx.opts.FileSystem, indexMetaName, m, idx.opts.ReadOnly)
}

func (idx *index) readMeta() error {
m := indexMeta{}
if err := readGobFile(idx.opts.FileSystem, indexMetaName, &m); err != nil {
if err := readGobFile(idx.opts.FileSystem, indexMetaName, &m, idx.opts.ReadOnly); err != nil {
return err
}
idx.level = m.Level
Expand Down
3 changes: 3 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ type Options struct {
// Default: fs.OSMMap.
FileSystem fs.FileSystem

// Does DB reside on read-only filesystem?
ReadOnly bool

maxSegmentSize uint32
compactionMinSegmentSize uint32
compactionMinFragmentation float32
Expand Down