github.com/evdatsion/aphelion-dpos-bft@v0.32.1/consensus/wal_test.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 // "sync" 11 "testing" 12 "time" 13 14 "github.com/evdatsion/aphelion-dpos-bft/consensus/types" 15 "github.com/evdatsion/aphelion-dpos-bft/libs/autofile" 16 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 17 tmtypes "github.com/evdatsion/aphelion-dpos-bft/types" 18 tmtime "github.com/evdatsion/aphelion-dpos-bft/types/time" 19 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 const ( 25 walTestFlushInterval = time.Duration(100) * time.Millisecond 26 ) 27 28 func TestWALTruncate(t *testing.T) { 29 walDir, err := ioutil.TempDir("", "wal") 30 require.NoError(t, err) 31 defer os.RemoveAll(walDir) 32 33 walFile := filepath.Join(walDir, "wal") 34 35 // this magic number 4K can truncate the content when RotateFile. 36 // defaultHeadSizeLimit(10M) is hard to simulate. 37 // this magic number 1 * time.Millisecond make RotateFile check frequently. 38 // defaultGroupCheckDuration(5s) is hard to simulate. 39 wal, err := NewWAL(walFile, 40 autofile.GroupHeadSizeLimit(4096), 41 autofile.GroupCheckDuration(1*time.Millisecond), 42 ) 43 require.NoError(t, err) 44 wal.SetLogger(log.TestingLogger()) 45 err = wal.Start() 46 require.NoError(t, err) 47 defer func() { 48 wal.Stop() 49 // wait for the wal to finish shutting down so we 50 // can safely remove the directory 51 wal.Wait() 52 }() 53 54 // 60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), 55 // when headBuf is full, truncate content will Flush to the file. at this 56 // time, RotateFile is called, truncate content exist in each file. 57 err = WALGenerateNBlocks(t, wal.Group(), 60) 58 require.NoError(t, err) 59 60 time.Sleep(1 * time.Millisecond) //wait groupCheckDuration, make sure RotateFile run 61 62 wal.FlushAndSync() 63 64 h := int64(50) 65 gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) 66 assert.NoError(t, err, "expected not to err on height %d", h) 67 assert.True(t, found, "expected to find end height for %d", h) 68 assert.NotNil(t, gr) 69 defer gr.Close() 70 71 dec := NewWALDecoder(gr) 72 msg, err := dec.Decode() 73 assert.NoError(t, err, "expected to decode a message") 74 rs, ok := msg.Msg.(tmtypes.EventDataRoundState) 75 assert.True(t, ok, "expected message of type EventDataRoundState") 76 assert.Equal(t, rs.Height, h+1, "wrong height") 77 } 78 79 func TestWALEncoderDecoder(t *testing.T) { 80 now := tmtime.Now() 81 msgs := []TimedWALMessage{ 82 {Time: now, Msg: EndHeightMessage{0}}, 83 {Time: now, Msg: timeoutInfo{Duration: time.Second, Height: 1, Round: 1, Step: types.RoundStepPropose}}, 84 } 85 86 b := new(bytes.Buffer) 87 88 for _, msg := range msgs { 89 b.Reset() 90 91 enc := NewWALEncoder(b) 92 err := enc.Encode(&msg) 93 require.NoError(t, err) 94 95 dec := NewWALDecoder(b) 96 decoded, err := dec.Decode() 97 require.NoError(t, err) 98 99 assert.Equal(t, msg.Time.UTC(), decoded.Time) 100 assert.Equal(t, msg.Msg, decoded.Msg) 101 } 102 } 103 104 func TestWALWritePanicsIfMsgIsTooBig(t *testing.T) { 105 walDir, err := ioutil.TempDir("", "wal") 106 require.NoError(t, err) 107 defer os.RemoveAll(walDir) 108 walFile := filepath.Join(walDir, "wal") 109 110 wal, err := NewWAL(walFile) 111 require.NoError(t, err) 112 err = wal.Start() 113 require.NoError(t, err) 114 defer func() { 115 wal.Stop() 116 // wait for the wal to finish shutting down so we 117 // can safely remove the directory 118 wal.Wait() 119 }() 120 121 assert.Panics(t, func() { wal.Write(make([]byte, maxMsgSizeBytes+1)) }) 122 } 123 124 func TestWALSearchForEndHeight(t *testing.T) { 125 walBody, err := WALWithNBlocks(t, 6) 126 if err != nil { 127 t.Fatal(err) 128 } 129 walFile := tempWALWithData(walBody) 130 131 wal, err := NewWAL(walFile) 132 require.NoError(t, err) 133 wal.SetLogger(log.TestingLogger()) 134 135 h := int64(3) 136 gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) 137 assert.NoError(t, err, "expected not to err on height %d", h) 138 assert.True(t, found, "expected to find end height for %d", h) 139 assert.NotNil(t, gr) 140 defer gr.Close() 141 142 dec := NewWALDecoder(gr) 143 msg, err := dec.Decode() 144 assert.NoError(t, err, "expected to decode a message") 145 rs, ok := msg.Msg.(tmtypes.EventDataRoundState) 146 assert.True(t, ok, "expected message of type EventDataRoundState") 147 assert.Equal(t, rs.Height, h+1, "wrong height") 148 } 149 150 func TestWALPeriodicSync(t *testing.T) { 151 walDir, err := ioutil.TempDir("", "wal") 152 require.NoError(t, err) 153 defer os.RemoveAll(walDir) 154 155 walFile := filepath.Join(walDir, "wal") 156 wal, err := NewWAL(walFile, autofile.GroupCheckDuration(1*time.Millisecond)) 157 require.NoError(t, err) 158 159 wal.SetFlushInterval(walTestFlushInterval) 160 wal.SetLogger(log.TestingLogger()) 161 162 // Generate some data 163 err = WALGenerateNBlocks(t, wal.Group(), 5) 164 require.NoError(t, err) 165 166 // We should have data in the buffer now 167 assert.NotZero(t, wal.Group().Buffered()) 168 169 require.NoError(t, wal.Start()) 170 defer func() { 171 wal.Stop() 172 wal.Wait() 173 }() 174 175 time.Sleep(walTestFlushInterval + (10 * time.Millisecond)) 176 177 // The data should have been flushed by the periodic sync 178 assert.Zero(t, wal.Group().Buffered()) 179 180 h := int64(4) 181 gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) 182 assert.NoError(t, err, "expected not to err on height %d", h) 183 assert.True(t, found, "expected to find end height for %d", h) 184 assert.NotNil(t, gr) 185 if gr != nil { 186 gr.Close() 187 } 188 } 189 190 /* 191 var initOnce sync.Once 192 193 func registerInterfacesOnce() { 194 initOnce.Do(func() { 195 var _ = wire.RegisterInterface( 196 struct{ WALMessage }{}, 197 wire.ConcreteType{[]byte{}, 0x10}, 198 ) 199 }) 200 } 201 */ 202 203 func nBytes(n int) []byte { 204 buf := make([]byte, n) 205 n, _ = rand.Read(buf) 206 return buf[:n] 207 } 208 209 func benchmarkWalDecode(b *testing.B, n int) { 210 // registerInterfacesOnce() 211 212 buf := new(bytes.Buffer) 213 enc := NewWALEncoder(buf) 214 215 data := nBytes(n) 216 enc.Encode(&TimedWALMessage{Msg: data, Time: time.Now().Round(time.Second).UTC()}) 217 218 encoded := buf.Bytes() 219 220 b.ResetTimer() 221 for i := 0; i < b.N; i++ { 222 buf.Reset() 223 buf.Write(encoded) 224 dec := NewWALDecoder(buf) 225 if _, err := dec.Decode(); err != nil { 226 b.Fatal(err) 227 } 228 } 229 b.ReportAllocs() 230 } 231 232 func BenchmarkWalDecode512B(b *testing.B) { 233 benchmarkWalDecode(b, 512) 234 } 235 236 func BenchmarkWalDecode10KB(b *testing.B) { 237 benchmarkWalDecode(b, 10*1024) 238 } 239 func BenchmarkWalDecode100KB(b *testing.B) { 240 benchmarkWalDecode(b, 100*1024) 241 } 242 func BenchmarkWalDecode1MB(b *testing.B) { 243 benchmarkWalDecode(b, 1024*1024) 244 } 245 func BenchmarkWalDecode10MB(b *testing.B) { 246 benchmarkWalDecode(b, 10*1024*1024) 247 } 248 func BenchmarkWalDecode100MB(b *testing.B) { 249 benchmarkWalDecode(b, 100*1024*1024) 250 } 251 func BenchmarkWalDecode1GB(b *testing.B) { 252 benchmarkWalDecode(b, 1024*1024*1024) 253 }