github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/vfs/file_lock_test.go (about) 1 // Copyright 2014 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package vfs_test 6 7 import ( 8 "bytes" 9 "flag" 10 "os" 11 "os/exec" 12 "testing" 13 14 "github.com/cockroachdb/pebble/vfs" 15 "github.com/stretchr/testify/require" 16 ) 17 18 var lockFilename = flag.String("lockfile", "", "File to lock. A non-empty value implies a child process.") 19 20 func spawn(prog, filename string) ([]byte, error) { 21 return exec.Command(prog, "-lockfile", filename, "-test.v", 22 "-test.run=TestLock$").CombinedOutput() 23 } 24 25 // TestLock locks a file, spawns a second process that attempts to grab the 26 // lock to verify it fails. 27 // Then it closes the lock, and spawns a third copy to verify it can be 28 // relocked. 29 func TestLock(t *testing.T) { 30 child := *lockFilename != "" 31 var filename string 32 if child { 33 filename = *lockFilename 34 } else { 35 f, err := os.CreateTemp("", "golang-pebble-db-testlock-") 36 require.NoError(t, err) 37 38 filename = f.Name() 39 // NB: On Windows, locking will fail if the file is already open by the 40 // current process, so we close the lockfile here. 41 require.NoError(t, f.Close()) 42 defer os.Remove(filename) 43 } 44 45 // Avoid truncating an existing, non-empty file. 46 fi, err := os.Stat(filename) 47 if err == nil && fi.Size() != 0 { 48 t.Fatalf("The file %s is not empty", filename) 49 } 50 51 t.Logf("Locking: %s", filename) 52 lock, err := vfs.Default.Lock(filename) 53 if err != nil { 54 t.Fatalf("Could not lock %s: %v", filename, err) 55 } 56 57 if !child { 58 t.Logf("Spawning child, should fail to grab lock.") 59 out, err := spawn(os.Args[0], filename) 60 if err == nil { 61 t.Fatalf("Attempt to grab open lock should have failed.\n%s", out) 62 } 63 if !bytes.Contains(out, []byte("Could not lock")) { 64 t.Fatalf("Child failed with unexpected output: %s", out) 65 } 66 t.Logf("Child failed to grab lock as expected.") 67 } 68 69 t.Logf("Unlocking %s", filename) 70 if err := lock.Close(); err != nil { 71 t.Fatalf("Could not unlock %s: %v", filename, err) 72 } 73 74 if !child { 75 t.Logf("Spawning child, should successfully grab lock.") 76 if out, err := spawn(os.Args[0], filename); err != nil { 77 t.Fatalf("Attempt to re-open lock should have succeeded: %v\n%s", 78 err, out) 79 } 80 t.Logf("Child grabbed lock.") 81 } 82 } 83 84 func TestLockSameProcess(t *testing.T) { 85 f, err := os.CreateTemp("", "pebble-testlocksameprocess-") 86 require.NoError(t, err) 87 filename := f.Name() 88 89 // NB: On Windows, locking will fail if the file is already open by the 90 // current process, so we close the lockfile here. 91 require.NoError(t, f.Close()) 92 defer os.Remove(filename) 93 94 lock1, err := vfs.Default.Lock(filename) 95 require.NoError(t, err) 96 97 // Locking the file again from within the same process should fail. 98 // On Unix, Lock should detect the file in the global map of 99 // process-locked files. 100 // On Windows, locking will fail since the file is already open by the 101 // current process. 102 _, err = vfs.Default.Lock(filename) 103 require.Error(t, err) 104 105 require.NoError(t, lock1.Close()) 106 }