diff --git a/datalog.go b/datalog.go index 0719f07..64b22b6 100644 --- a/datalog.go +++ b/datalog.go @@ -79,7 +79,7 @@ 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) if err != nil { return nil, err } @@ -87,7 +87,7 @@ func (dl *datalog) openSegment(name string, id uint16, seqID uint64) (*segment, 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? } @@ -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 } } diff --git a/db.go b/db.go index 1296359..b2220c6 100644 --- a/db.go +++ b/db.go @@ -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. @@ -121,7 +129,9 @@ func Open(path string, opts *Options) (*DB, error) { db.startBackgroundWorker() } - clean = nil + if !opts.ReadOnly { + clean = nil + } return db, nil } @@ -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 diff --git a/file.go b/file.go index 3d09a1b..e956d71 100644 --- a/file.go +++ b/file.go @@ -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{} diff --git a/gobfile.go b/gobfile.go index 60e6f4f..2bb2f28 100644 --- a/gobfile.go +++ b/gobfile.go @@ -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 } @@ -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 { + f, err := openFile(fsys, name, true, readOnly) if err != nil { return err } diff --git a/index.go b/index.go index 50803f4..f0d6355 100644 --- a/index.go +++ b/index.go @@ -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") @@ -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 diff --git a/options.go b/options.go index 3cb8048..0720f66 100644 --- a/options.go +++ b/options.go @@ -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