diff --git a/eventstore/mmm/lockfile_test.go b/eventstore/mmm/lockfile_test.go new file mode 100644 index 0000000..a25f488 --- /dev/null +++ b/eventstore/mmm/lockfile_test.go @@ -0,0 +1,35 @@ +package mmm + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLockfile(t *testing.T) { + // create a temporary directory for the test + tmpDir, err := os.MkdirTemp("", "mmm-lockfile-test-*") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + + // initialize first MMM instance + mmmm1 := &MultiMmapManager{Dir: tmpDir} + err = mmmm1.Init() + require.NoError(t, err) + defer mmmm1.Close() + + // try to initialize second MMM instance on the same directory + mmmm2 := &MultiMmapManager{Dir: tmpDir} + err = mmmm2.Init() + require.Error(t, err) + require.Contains(t, err.Error(), "already in use by another instance") + + // close first instance + mmmm1.Close() + + // now second instance should be able to open + err = mmmm2.Init() + require.NoError(t, err) + mmmm2.Close() +} diff --git a/eventstore/mmm/mmmm.go b/eventstore/mmm/mmmm.go index 43dc2b5..3e82613 100644 --- a/eventstore/mmm/mmmm.go +++ b/eventstore/mmm/mmmm.go @@ -56,6 +56,15 @@ func (b *MultiMmapManager) Init() error { b.Logger = &nopLogger } + // 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 { + if os.IsExist(err) { + return fmt.Errorf("database at %s is already in use by another instance", b.Dir) + } + return fmt.Errorf("failed to create lockfile %s: %w", lockfilePath, err) + } + // create directory if it doesn't exist dbpath := filepath.Join(b.Dir, "mmmm") if err := os.MkdirAll(dbpath, 0755); err != nil { @@ -333,4 +342,8 @@ func (b *MultiMmapManager) Close() { } syscall.Munmap(b.mmapf) + + // remove lockfile + lockfilePath := filepath.Join(b.Dir, "mmmm.lock") + os.Remove(lockfilePath) }