2018-01-21 01:19:55 +00:00
|
|
|
package framer
|
|
|
|
|
|
|
|
import (
|
2018-01-26 22:49:19 +00:00
|
|
|
"bytes"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"testing"
|
|
|
|
"time"
|
2018-01-21 01:19:55 +00:00
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
"github.com/hashicorp/nomad/testutil"
|
2018-05-02 16:47:08 +00:00
|
|
|
"github.com/kr/pretty"
|
2018-01-26 22:49:19 +00:00
|
|
|
)
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
// This test checks, that even if the frame size has not been hit, a flush will
|
|
|
|
// periodically occur.
|
|
|
|
func TestStreamFramer_Flush(t *testing.T) {
|
|
|
|
// Create the stream framer
|
2018-01-26 22:49:19 +00:00
|
|
|
frames := make(chan *StreamFrame, 10)
|
2018-01-21 01:19:55 +00:00
|
|
|
hRate, bWindow := 100*time.Millisecond, 100*time.Millisecond
|
2018-01-26 22:49:19 +00:00
|
|
|
sf := NewStreamFramer(frames, hRate, bWindow, 100)
|
2018-01-21 01:19:55 +00:00
|
|
|
sf.Run()
|
|
|
|
|
|
|
|
f := "foo"
|
|
|
|
fe := "bar"
|
|
|
|
d := []byte{0xa}
|
|
|
|
o := int64(10)
|
|
|
|
|
|
|
|
// Start the reader
|
|
|
|
resultCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
for {
|
2018-01-26 22:49:19 +00:00
|
|
|
frame := <-frames
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
if frame.IsHeartbeat() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if reflect.DeepEqual(frame.Data, d) && frame.Offset == o && frame.File == f && frame.FileEvent == fe {
|
|
|
|
resultCh <- struct{}{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Write only 1 byte so we do not hit the frame size
|
|
|
|
if err := sf.Send(f, fe, d, o); err != nil {
|
|
|
|
t.Fatalf("Send() failed %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-resultCh:
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * bWindow):
|
|
|
|
t.Fatalf("failed to flush")
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
// Shutdown
|
|
|
|
sf.Destroy()
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sf.ExitCh():
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate):
|
|
|
|
t.Fatalf("exit channel should close")
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
if _, ok := <-frames; ok {
|
|
|
|
t.Fatal("out channel should be closed")
|
2018-01-21 01:19:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test checks that frames will be batched till the frame size is hit (in
|
|
|
|
// the case that is before the flush).
|
|
|
|
func TestStreamFramer_Batch(t *testing.T) {
|
|
|
|
// Ensure the batch window doesn't get hit
|
|
|
|
hRate, bWindow := 100*time.Millisecond, 500*time.Millisecond
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
// Create the stream framer
|
|
|
|
frames := make(chan *StreamFrame, 10)
|
|
|
|
sf := NewStreamFramer(frames, hRate, bWindow, 3)
|
|
|
|
sf.Run()
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
f := "foo"
|
|
|
|
fe := "bar"
|
|
|
|
d := []byte{0xa, 0xb, 0xc}
|
|
|
|
o := int64(10)
|
|
|
|
|
|
|
|
// Start the reader
|
|
|
|
resultCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
for {
|
2018-01-26 22:49:19 +00:00
|
|
|
frame := <-frames
|
2018-01-21 01:19:55 +00:00
|
|
|
if frame.IsHeartbeat() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if reflect.DeepEqual(frame.Data, d) && frame.Offset == o && frame.File == f && frame.FileEvent == fe {
|
|
|
|
resultCh <- struct{}{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Write only 1 byte so we do not hit the frame size
|
|
|
|
if err := sf.Send(f, fe, d[:1], o); err != nil {
|
|
|
|
t.Fatalf("Send() failed %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure we didn't get any data
|
|
|
|
select {
|
|
|
|
case <-resultCh:
|
|
|
|
t.Fatalf("Got data before frame size reached")
|
|
|
|
case <-time.After(bWindow / 2):
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the rest so we hit the frame size
|
|
|
|
if err := sf.Send(f, fe, d[1:], o); err != nil {
|
|
|
|
t.Fatalf("Send() failed %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure we get data
|
|
|
|
select {
|
|
|
|
case <-resultCh:
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * bWindow):
|
|
|
|
t.Fatalf("Did not receive data after batch size reached")
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
// Shutdown
|
|
|
|
sf.Destroy()
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sf.ExitCh():
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate):
|
|
|
|
t.Fatalf("exit channel should close")
|
|
|
|
}
|
|
|
|
|
2018-05-02 16:47:08 +00:00
|
|
|
if f, ok := <-frames; ok {
|
|
|
|
t.Fatalf("out channel should be closed. recv: %s", pretty.Sprint(f))
|
2018-01-21 01:19:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStreamFramer_Heartbeat(t *testing.T) {
|
|
|
|
// Create the stream framer
|
2018-01-26 22:49:19 +00:00
|
|
|
frames := make(chan *StreamFrame, 10)
|
2018-01-21 01:19:55 +00:00
|
|
|
hRate, bWindow := 100*time.Millisecond, 100*time.Millisecond
|
2018-01-26 22:49:19 +00:00
|
|
|
sf := NewStreamFramer(frames, hRate, bWindow, 100)
|
2018-01-21 01:19:55 +00:00
|
|
|
sf.Run()
|
|
|
|
|
|
|
|
// Start the reader
|
|
|
|
resultCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
for {
|
2018-01-26 22:49:19 +00:00
|
|
|
frame := <-frames
|
2018-01-21 01:19:55 +00:00
|
|
|
if frame.IsHeartbeat() {
|
|
|
|
resultCh <- struct{}{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-resultCh:
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate):
|
|
|
|
t.Fatalf("failed to heartbeat")
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
// Shutdown
|
|
|
|
sf.Destroy()
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sf.ExitCh():
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate):
|
|
|
|
t.Fatalf("exit channel should close")
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
if _, ok := <-frames; ok {
|
|
|
|
t.Fatal("out channel should be closed")
|
2018-01-21 01:19:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test checks that frames are received in order
|
|
|
|
func TestStreamFramer_Order(t *testing.T) {
|
|
|
|
// Ensure the batch window doesn't get hit
|
|
|
|
hRate, bWindow := 100*time.Millisecond, 10*time.Millisecond
|
2018-01-26 22:49:19 +00:00
|
|
|
// Create the stream framer
|
|
|
|
frames := make(chan *StreamFrame, 10)
|
|
|
|
sf := NewStreamFramer(frames, hRate, bWindow, 10)
|
2018-01-21 01:19:55 +00:00
|
|
|
sf.Run()
|
|
|
|
|
|
|
|
files := []string{"1", "2", "3", "4", "5"}
|
|
|
|
input := bytes.NewBuffer(make([]byte, 0, 100000))
|
|
|
|
for i := 0; i <= 1000; i++ {
|
|
|
|
str := strconv.Itoa(i) + ","
|
|
|
|
input.WriteString(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := bytes.NewBuffer(make([]byte, 0, 100000))
|
|
|
|
for range files {
|
|
|
|
expected.Write(input.Bytes())
|
|
|
|
}
|
|
|
|
receivedBuf := bytes.NewBuffer(make([]byte, 0, 100000))
|
|
|
|
|
|
|
|
// Start the reader
|
|
|
|
resultCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
for {
|
2018-01-26 22:49:19 +00:00
|
|
|
frame := <-frames
|
2018-01-21 01:19:55 +00:00
|
|
|
if frame.IsHeartbeat() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
receivedBuf.Write(frame.Data)
|
|
|
|
|
|
|
|
if reflect.DeepEqual(expected, receivedBuf) {
|
|
|
|
resultCh <- struct{}{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Send the data
|
|
|
|
b := input.Bytes()
|
|
|
|
shards := 10
|
|
|
|
each := len(b) / shards
|
|
|
|
for _, f := range files {
|
|
|
|
for i := 0; i < shards; i++ {
|
|
|
|
l, r := each*i, each*(i+1)
|
|
|
|
if i == shards-1 {
|
|
|
|
r = len(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := sf.Send(f, "", b[l:r], 0); err != nil {
|
|
|
|
t.Fatalf("Send() failed %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure we get data
|
|
|
|
select {
|
|
|
|
case <-resultCh:
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * bWindow):
|
|
|
|
if reflect.DeepEqual(expected, receivedBuf) {
|
|
|
|
got := receivedBuf.String()
|
|
|
|
want := expected.String()
|
|
|
|
t.Fatalf("Got %v; want %v", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
// Shutdown
|
|
|
|
sf.Destroy()
|
2018-01-21 01:19:55 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-sf.ExitCh():
|
|
|
|
case <-time.After(10 * time.Duration(testutil.TestMultiplier()) * hRate):
|
|
|
|
t.Fatalf("exit channel should close")
|
|
|
|
}
|
|
|
|
|
2018-01-26 22:49:19 +00:00
|
|
|
if _, ok := <-frames; ok {
|
|
|
|
t.Fatal("out channel should be closed")
|
2018-01-21 01:19:55 +00:00
|
|
|
}
|
|
|
|
}
|