209 lines
4.2 KiB
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)
|
||
|
}
|
||
|
}
|
||
|
}
|