This repository has been archived on 2022-07-30. You can view files and clone it, but cannot push or open issues/pull-requests.
mdb/kvstore/wal/shipping_test.go

209 lines
4.2 KiB
Go

package wal
import (
"fmt"
"math/rand"
"os"
"testing"
"time"
"git.crumpington.com/private/mdb/testconn"
)
func TestShipping(t *testing.T) {
run := func(name string, inner func(
t *testing.T,
wWALPath string,
fWALPath string,
w *Writer,
nw *testconn.Network,
)) {
t.Run(name, func(t *testing.T) {
wWALPath := randPath() + ".wal"
fWALPath := randPath() + ".wal"
w := NewWriterPrimary(wWALPath)
defer w.Close()
nw := testconn.NewNetwork()
defer nw.CloseClient()
defer nw.CloseServer()
defer os.RemoveAll(wWALPath)
defer os.RemoveAll(fWALPath)
inner(t, wWALPath, fWALPath, w, nw)
})
}
run("simple", func(t *testing.T, wWALPath, fWALPath string, w *Writer, nw *testconn.Network) {
// Write 100 entries in background.
go func() {
for i := 0; i < 100; i++ {
time.Sleep(10 * time.Millisecond)
w.Store("x", uint64(i+10), _b(fmt.Sprintf("data %d", i)))
}
}()
// Run a sender in the background.
go func() {
f := NewFollower(wWALPath)
conn := nw.Accept()
f.SendWAL(conn)
}()
// Run the follower.
go func() {
w := NewWriterSecondary(fWALPath)
conn := nw.Dial()
w.RecvWAL(conn)
}()
time.Sleep(time.Second)
// Wait for follower to get 100 entries, then close connection.
f := NewFollower(fWALPath)
defer f.Close()
f.waitForSeqNum(100)
if err := walsEqual(wWALPath, fWALPath); err != nil {
t.Fatal(err)
}
})
run("net failures", func(t *testing.T, wWALPath, fWALPath string, w *Writer, nw *testconn.Network) {
defer nw.CloseClient()
defer nw.CloseServer()
N := 4000
sleepTime := time.Millisecond
go func() {
for i := 0; i < N; i++ {
time.Sleep(sleepTime)
if rand.Float64() < 0.9 {
w.Store(randString(), randID(), _b(randString()))
} else {
w.Delete(randString(), randID())
}
}
}()
// Run a sender in the background.
go func() {
sender := NewFollower(wWALPath)
f := NewFollower(fWALPath)
defer f.Close()
for f.MaxSeqNum() < uint64(N) {
if conn := nw.Accept(); conn != nil {
sender.SendWAL(conn)
}
}
}()
// Run the follower in the background.
go func() {
f := NewFollower(fWALPath)
defer f.Close()
w := NewWriterSecondary(fWALPath)
for f.MaxSeqNum() < uint64(N) {
if conn := nw.Dial(); conn != nil {
w.RecvWAL(conn)
}
}
}()
// Disconnect the network randomly.
go func() {
f := NewFollower(fWALPath)
defer f.Close()
for f.MaxSeqNum() < uint64(N) {
time.Sleep(time.Duration(rand.Intn(10 * int(sleepTime))))
if rand.Float64() < 0.5 {
nw.CloseClient()
} else {
nw.CloseServer()
}
}
}()
f := NewFollower(fWALPath)
defer f.Close()
// Wait for follower to get 100 entries, then close connection.
f.waitForSeqNum(uint64(N))
if err := walsEqual(wWALPath, fWALPath); err != nil {
t.Fatal(err)
}
})
run("secondary too far behind", func(t *testing.T, wWALPath, fWALPath string, w *Writer, nw *testconn.Network) {
// Write some entries to the primary.
// MaxSeqNum will be 10.
for i := 0; i < 10; i++ {
w.Store(randString(), randID(), _b(randString()))
}
// Delete everything.
w.DeleteBefore(-1)
// Run a sender in the background.
go func() {
f := NewFollower(wWALPath)
defer f.Close()
conn := nw.Accept()
f.SendWAL(conn)
}()
// Run the follower.
go func() {
w := NewWriterSecondary(fWALPath)
defer w.Close()
conn := nw.Dial()
w.RecvWAL(conn)
}()
time.Sleep(time.Second)
f := NewFollower(fWALPath)
defer f.Close()
if f.MaxSeqNum() != 0 {
t.Fatal(f.MaxSeqNum())
}
})
}
func TestShippingEncoding(t *testing.T) {
recs := []Record{
{SeqNum: 10, Collection: "x", ID: 44, Store: true, Data: _b("Hello")},
{SeqNum: 24, Collection: "abc", ID: 3, Store: true, Data: _b("x")},
{SeqNum: 81, Collection: "qrs", ID: 102, Store: false},
}
buf := make([]byte, recHeaderSize)
for _, rec := range recs {
encodeRecordHeader(rec, buf)
out, colLen, dataLen := decodeRecHeader(buf)
if out.SeqNum != rec.SeqNum {
t.Fatal(out, rec)
}
if out.ID != rec.ID {
t.Fatal(out, rec)
}
if out.Store != rec.Store {
t.Fatal(out, rec)
}
if colLen != len(rec.Collection) {
t.Fatal(out, rec)
}
if dataLen != len(rec.Data) {
t.Fatal(out, rec)
}
}
}