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