-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathwal_test.go
220 lines (182 loc) · 5.4 KB
/
wal_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package bitcask
import (
"os"
"testing"
"github.com./stretchr/testify/assert"
)
func setupWal(name string, t *testing.T) *Wal {
wal, err := NewWal(name, 0, -1)
assert.Nil(t, err)
return wal
}
// Test basic WAL operations: writing and reading records
func TestWal_BasicOperations(t *testing.T) {
wal := setupWal("test_wal_basic.wal", t)
defer wal.Unref()
data := []byte("hello world")
offset, err := wal.WriteRecord(data)
if err != nil {
t.Fatalf("Failed to write record: %v", err)
}
assert.Nil(t, wal.Flush())
readData, err := wal.ReadRecord(offset, uint64(len(data)), true)
if err != nil {
t.Fatalf("Failed to read record: %v", err)
}
if string(readData) != string(data) {
t.Fatalf("Data mismatch: expected %s, got %s", string(data), string(readData))
}
readData, err = wal.ReadRecord(offset, uint64(len(data)), false)
assert.Nil(t, err)
assert.Equal(t, data, readData)
}
// Test WAL behavior with multiple writes and reads
func TestWal_MultipleRecords(t *testing.T) {
wal := setupWal("test_wal_multiple.wal", t)
defer wal.Unref()
records := [][]byte{
[]byte("first record"),
[]byte("second record"),
[]byte("third record"),
}
var offsets []uint64
for _, record := range records {
offset, err := wal.WriteRecord(record)
if err != nil {
t.Fatalf("Failed to write record: %v", err)
}
offsets = append(offsets, offset)
}
assert.Nil(t, wal.Flush())
for i, offset := range offsets {
readData, err := wal.ReadRecord(offset, uint64(len(records[i])), true)
if err != nil {
t.Fatalf("Failed to read record at offset %d: %v", offset, err)
}
assert.Equal(t, readData, records[i])
}
}
// Test WAL record spanning multiple blocks
func TestWal_LargeRecord(t *testing.T) {
wal := setupWal("test_wal_large.wal", t)
defer wal.Unref()
largeData := make([]byte, BlockSize*2) // A record spanning multiple blocks
for i := range largeData {
largeData[i] = byte(i % 256)
}
offset, err := wal.WriteRecord(largeData)
if err != nil {
t.Fatalf("Failed to write large record: %v", err)
}
assert.Nil(t, wal.Flush())
readData, err := wal.ReadRecord(offset, uint64(len(largeData)), true)
if err != nil {
t.Fatalf("Failed to read large record: %v", err)
}
assert.Equal(t, readData, largeData)
}
func TestWal_LargeRecord2(t *testing.T) {
wal := setupWal("test_wal_large2.wal", t)
defer wal.Unref()
data := GenNKBytes(5)
offsets := make([]uint64, 1000)
for i := 0; i < 1000; i++ {
off, err := wal.WriteRecord(data)
assert.Nil(t, err)
assert.Nil(t, wal.Flush())
offsets[i] = off
}
// check
for i := 0; i < 1000; i++ {
readData, err := wal.ReadRecord(offsets[i], uint64(len(data)), true)
assert.Nil(t, err)
assert.Equal(t, readData, data)
}
}
// Test handling of corrupted WAL records
func TestWal_CorruptedRead(t *testing.T) {
filename := "test_wal_corrupt.wal"
wal := setupWal(filename, t)
defer os.Remove(wal.Path())
data := []byte("valid record")
offset, err := wal.WriteRecord(data)
if err != nil {
t.Fatalf("Failed to write record: %v", err)
}
assert.Nil(t, wal.Flush())
// Close WAL before corrupting the file
wal.Close()
// Manually corrupt the file
file, err := os.OpenFile(filename, os.O_RDWR, 0o644)
if err != nil {
t.Fatalf("Failed to open WAL file for corruption: %v", err)
}
_, err = file.WriteAt([]byte{0xFF, 0xFF}, int64(offset+2)) // Corrupt part of the record
if err != nil {
t.Fatalf("Failed to corrupt WAL file: %v", err)
}
// Reopen WAL and try to read
wal, err = LoadWal(filename, 0)
if err != nil {
t.Fatalf("Failed to reopen WAL: %v", err)
}
defer wal.Close()
_, err = wal.ReadRecord(offset, uint64(len(data)), true)
if err == nil {
t.Fatalf("Expected error when reading corrupted record, but got none")
}
}
// Test padding when block space is insufficient
func TestWal_BlockPadding(t *testing.T) {
wal := setupWal("test_wal_padding.wal", t)
defer wal.Unref()
// Write a record that nearly fills a block
data := make([]byte, BlockSize-RecordHeaderSize)
offset, err := wal.WriteRecord(data)
if err != nil {
t.Fatalf("Failed to write record: %v", err)
}
assert.Nil(t, wal.Flush())
// Write another record that should go into the next block due to padding
secondData := []byte("new block record")
secondOffset, err := wal.WriteRecord(secondData)
if err != nil {
t.Fatalf("Failed to write second record: %v", err)
}
assert.Nil(t, wal.Flush())
// Ensure both records can be read correctly
readData, err := wal.ReadRecord(offset, uint64(len(data)), true)
if err != nil {
t.Fatalf("Failed to read first record: %v", err)
}
assert.Equal(t, readData, data)
readData, err = wal.ReadRecord(secondOffset, uint64(len(secondData)), true)
if err != nil {
t.Fatalf("Failed to read second record: %v", err)
}
assert.Equal(t, readData, secondData)
}
// Test reopening WAL and ensuring persistence
func TestWal_ReopenPersistence(t *testing.T) {
filename := "test_wal_persistence.wal"
wal := setupWal(filename, t)
defer os.Remove(filename)
data := []byte("persistent data")
offset, err := wal.WriteRecord(data)
if err != nil {
t.Fatalf("Failed to write record: %v", err)
}
assert.Nil(t, wal.Flush())
// Close and reopen WAL
wal.Close()
wal, err = LoadWal(filename, 0)
if err != nil {
t.Fatalf("Failed to reopen WAL: %v", err)
}
defer wal.Close()
readData, err := wal.ReadRecord(offset, uint64(len(data)), true)
if err != nil {
t.Fatalf("Failed to read record after reopening: %v", err)
}
assert.Equal(t, readData, data)
}