From 5a58dffcfab2defd6bcb2e04be0282790e43cb9e Mon Sep 17 00:00:00 2001 From: Mano Sriram Date: Sun, 19 May 2024 02:34:35 +0530 Subject: [PATCH] feat(filelock): add filelock for dirPath so that other process will not be able to read/write to the dir --- Makefile | 1 + db.go | 23 ++++++++++++++++++++++- examples/db/db.go | 8 +++++--- go.mod | 2 ++ go.sum | 4 ++++ types.go | 4 ++++ 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 4aa0b6b..988a9ad 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ build: clean: rm -rf ./nimbusdb_temp* benchmark/nimbusdb_temp* rm -rf ~/nimbusdb/test_data + mkdir -p ~/nimbusdb/test_data test: go test -v -failfast diff --git a/db.go b/db.go index 9d06e6f..cc7e6fd 100644 --- a/db.go +++ b/db.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + "github.com/gofrs/flock" "github.com/google/btree" "github.com/hashicorp/golang-lru/v2/expirable" utils "github.com/manosriram/nimbusdb/utils" @@ -67,6 +68,7 @@ func NewDb(dirPath string, opts ...*Options) *Db { segments: segments, opts: &Options{ ShouldWatch: false, + Flock: opts[0].Flock, }, } @@ -482,6 +484,7 @@ func (db *Db) createActiveDatafile(dirPath string) error { } func (db *Db) handleInterrupt() { + defer db.opts.Flock.Close() terminateSignal := make(chan os.Signal, 1) signal.Notify(terminateSignal, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM) for { @@ -509,10 +512,27 @@ func Open(opts *Options) (*Db, error) { dirPath = utils.JoinPaths(home, DefaultDataDir) } + + var builder strings.Builder + _, err := fmt.Fprintf(&builder, "%s.%s", dirPath, FlockSuffix) + if err != nil { + return nil, err + } + + flock := flock.New(builder.String()) + tryLock, err := flock.TryLock() + if err != nil { + return nil, err + } + if !tryLock { + return nil, ERROR_DIRPATH_ALREADY_IN_USE + } + opts.Flock = flock + db := NewDb(dirPath, opts) go db.handleInterrupt() - err := os.MkdirAll(dirPath, os.ModePerm) + err = os.MkdirAll(dirPath, os.ModePerm) if err != nil { return nil, err } @@ -554,6 +574,7 @@ func Open(opts *Options) (*Db, error) { // Closes the database. Closes the file pointer used to read/write the activeDataFile. // Closes all file inactiveDataFile pointers and marks them as closed. func (db *Db) Close() error { + defer db.opts.Flock.Unlock() err := db.closeActiveDataFileReader() if err != nil { return err diff --git a/examples/db/db.go b/examples/db/db.go index 53c047c..603474e 100644 --- a/examples/db/db.go +++ b/examples/db/db.go @@ -11,8 +11,7 @@ import ( ) const ( - // DirPath = "/Users/manosriram/nimbusdb/test_data" - DirPath = "./dd/" + DirPath = "/Users/manosriram/nimbusdb/test_data" ) func watchKeyChange(ch chan nimbusdb.WatcherEvent) { @@ -32,7 +31,10 @@ func watchKeyChange(ch chan nimbusdb.WatcherEvent) { } func main() { - d, _ := nimbusdb.Open(&nimbusdb.Options{Path: DirPath, WatchQueueSize: 10}) + d, err := nimbusdb.Open(&nimbusdb.Options{Path: DirPath, WatchQueueSize: 10}) + if err != nil { + log.Fatalf("error opening dirpath: %s\n", err.Error()) + } defer d.Close() ch, _ := d.NewWatch() diff --git a/go.mod b/go.mod index d4ed452..9247d85 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,9 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/segmentio/ksuid v1.0.4 // indirect + golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8d5bd17..b759ec9 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -19,6 +21,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/types.go b/types.go index b2b21aa..b15f2e9 100644 --- a/types.go +++ b/types.go @@ -7,6 +7,7 @@ import ( "sync" "time" + flock "github.com/gofrs/flock" "github.com/segmentio/ksuid" ) @@ -28,6 +29,7 @@ const ( TempDataFilePattern = "*.dfile" TempInactiveDataFilePattern = "*.idfile" DefaultDataDir = "nimbusdb" + FlockSuffix = "flock" DatafileThreshold = 1 * MB BlockSize = 32 * KB @@ -67,6 +69,7 @@ var ( ERROR_DATA_FILE_WRITER_NOT_CLOSED = errors.New("data file writer is not closed") ERROR_DATA_FILE_READER_NOT_OPEN = errors.New("data file reader is not open") ERROR_DATA_FILE_WRITER_NOT_OPEN = errors.New("data file writer is not open") + ERROR_DIRPATH_ALREADY_IN_USE = errors.New("dirpath already in use") ) var ( @@ -95,6 +98,7 @@ type Options struct { Path string ShouldWatch bool WatchQueueSize int + Flock *flock.Flock } type KeyValuePair struct {