Skip to content

Commit 5704c61

Browse files
authored
Merge pull request #18605 from lucasrod16/use-single-function-to-create-WAL-files
Add function to create WAL files
2 parents 59cfd7a + 680eadf commit 5704c61

File tree

4 files changed

+97
-3
lines changed

4 files changed

+97
-3
lines changed

server/storage/wal/file_pipeline.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (fp *filePipeline) Close() error {
7575
func (fp *filePipeline) alloc() (f *fileutil.LockedFile, err error) {
7676
// count % 2 so this file isn't the same as the one last published
7777
fpath := filepath.Join(fp.dir, fmt.Sprintf("%d.tmp", fp.count%2))
78-
if f, err = fileutil.LockFile(fpath, os.O_CREATE|os.O_WRONLY, fileutil.PrivateFileMode); err != nil {
78+
if f, err = createNewWALFile[*fileutil.LockedFile](fpath, false); err != nil {
7979
return nil, err
8080
}
8181
if err = fileutil.Preallocate(f.File, fp.size, true); err != nil {

server/storage/wal/repair.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func Repair(lg *zap.Logger, dirpath string) bool {
6767

6868
case errors.Is(err, io.ErrUnexpectedEOF):
6969
brokenName := f.Name() + ".broken"
70-
bf, bferr := os.OpenFile(brokenName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fileutil.PrivateFileMode)
70+
bf, bferr := createNewWALFile[*os.File](brokenName, true)
7171
if bferr != nil {
7272
lg.Warn("failed to create backup file", zap.String("path", brokenName), zap.Error(bferr))
7373
return false

server/storage/wal/wal.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error) {
126126
}
127127

128128
p := filepath.Join(tmpdirpath, walName(0, 0))
129-
f, err := fileutil.LockFile(p, os.O_WRONLY|os.O_CREATE, fileutil.PrivateFileMode)
129+
f, err := createNewWALFile[*fileutil.LockedFile](p, false)
130130
if err != nil {
131131
lg.Warn(
132132
"failed to flock an initial WAL file",
@@ -233,6 +233,31 @@ func Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error) {
233233
return w, nil
234234
}
235235

236+
// createNewWALFile creates a WAL file.
237+
// To create a locked file, use *fileutil.LockedFile type parameter.
238+
// To create a standard file, use *os.File type parameter.
239+
// If forceNew is true, the file will be truncated if it already exists.
240+
func createNewWALFile[T *os.File | *fileutil.LockedFile](path string, forceNew bool) (T, error) {
241+
flag := os.O_WRONLY | os.O_CREATE
242+
if forceNew {
243+
flag |= os.O_TRUNC
244+
}
245+
246+
if _, isLockedFile := any(T(nil)).(*fileutil.LockedFile); isLockedFile {
247+
lockedFile, err := fileutil.LockFile(path, flag, fileutil.PrivateFileMode)
248+
if err != nil {
249+
return nil, err
250+
}
251+
return any(lockedFile).(T), nil
252+
}
253+
254+
file, err := os.OpenFile(path, flag, fileutil.PrivateFileMode)
255+
if err != nil {
256+
return nil, err
257+
}
258+
return any(file).(T), nil
259+
}
260+
236261
func (w *WAL) Reopen(lg *zap.Logger, snap walpb.Snapshot) (*WAL, error) {
237262
err := w.Close()
238263
if err != nil {

server/storage/wal/wal_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,75 @@ func TestNew(t *testing.T) {
9696
}
9797
}
9898

99+
func TestCreateNewWALFile(t *testing.T) {
100+
tests := []struct {
101+
name string
102+
fileType interface{}
103+
forceNew bool
104+
}{
105+
{
106+
name: "creating standard file should succeed and not truncate file",
107+
fileType: &os.File{},
108+
forceNew: false,
109+
},
110+
{
111+
name: "creating locked file should succeed and not truncate file",
112+
fileType: &fileutil.LockedFile{},
113+
forceNew: false,
114+
},
115+
{
116+
name: "creating standard file with forceNew should truncate file",
117+
fileType: &os.File{},
118+
forceNew: true,
119+
},
120+
{
121+
name: "creating locked file with forceNew should truncate file",
122+
fileType: &fileutil.LockedFile{},
123+
forceNew: true,
124+
},
125+
}
126+
127+
for i, tt := range tests {
128+
t.Run(tt.name, func(t *testing.T) {
129+
p := filepath.Join(t.TempDir(), walName(0, uint64(i)))
130+
131+
// create initial file with some data to verify truncate behavior
132+
err := os.WriteFile(p, []byte("test data"), fileutil.PrivateFileMode)
133+
require.NoError(t, err)
134+
135+
var f interface{}
136+
switch tt.fileType.(type) {
137+
case *os.File:
138+
f, err = createNewWALFile[*os.File](p, tt.forceNew)
139+
require.IsType(t, &os.File{}, f)
140+
case *fileutil.LockedFile:
141+
f, err = createNewWALFile[*fileutil.LockedFile](p, tt.forceNew)
142+
require.IsType(t, &fileutil.LockedFile{}, f)
143+
default:
144+
panic("unknown file type")
145+
}
146+
147+
require.NoError(t, err)
148+
149+
// validate the file permissions
150+
fi, err := os.Stat(p)
151+
require.NoError(t, err)
152+
expectedPerms := fmt.Sprintf("%o", os.FileMode(fileutil.PrivateFileMode))
153+
actualPerms := fmt.Sprintf("%o", fi.Mode().Perm())
154+
require.Equal(t, expectedPerms, actualPerms, "unexpected file permissions on %q", p)
155+
156+
content, err := os.ReadFile(p)
157+
require.NoError(t, err)
158+
159+
if tt.forceNew {
160+
require.Empty(t, string(content), "file content should be truncated but it wasn't")
161+
} else {
162+
require.Equal(t, "test data", string(content), "file content should not be truncated but it was")
163+
}
164+
})
165+
}
166+
}
167+
99168
func TestCreateFailFromPollutedDir(t *testing.T) {
100169
p := t.TempDir()
101170
os.WriteFile(filepath.Join(p, "test.wal"), []byte("data"), os.ModeTemporary)

0 commit comments

Comments
 (0)