1package nomad
2
3import (
4	"bytes"
5	"testing"
6	"time"
7
8	"github.com/hashicorp/nomad/nomad/mock"
9	"github.com/hashicorp/nomad/nomad/structs"
10	"github.com/stretchr/testify/assert"
11	"github.com/ugorji/go/codec"
12)
13
14// This test compares the size of the normalized + OmitEmpty raft plan log entry
15// with the earlier denormalized log.
16//
17// Whenever this test is changed, care should be taken to ensure the older msgpack size
18// is recalculated when new fields are introduced in ApplyPlanResultsRequest
19func TestPlanNormalize(t *testing.T) {
20	// This size was calculated using the older ApplyPlanResultsRequest format, in which allocations
21	// didn't use OmitEmpty and only the job was normalized in the stopped and preempted allocs.
22	// The newer format uses OmitEmpty and uses a minimal set of fields for the diff of the
23	// stopped and preempted allocs. The file for the older format hasn't been checked in, because
24	// it's not a good idea to check-in a 20mb file to the git repo.
25	unoptimizedLogSize := 19460168
26
27	numUpdatedAllocs := 10000
28	numStoppedAllocs := 8000
29	numPreemptedAllocs := 2000
30	mockAlloc := mock.Alloc()
31	mockAlloc.Job = nil
32
33	mockUpdatedAllocSlice := make([]*structs.Allocation, numUpdatedAllocs)
34	for i := 0; i < numUpdatedAllocs; i++ {
35		mockUpdatedAllocSlice = append(mockUpdatedAllocSlice, mockAlloc)
36	}
37
38	now := time.Now().UTC().UnixNano()
39	mockStoppedAllocSlice := make([]*structs.AllocationDiff, numStoppedAllocs)
40	for i := 0; i < numStoppedAllocs; i++ {
41		mockStoppedAllocSlice = append(mockStoppedAllocSlice, normalizeStoppedAlloc(mockAlloc, now))
42	}
43
44	mockPreemptionAllocSlice := make([]*structs.AllocationDiff, numPreemptedAllocs)
45	for i := 0; i < numPreemptedAllocs; i++ {
46		mockPreemptionAllocSlice = append(mockPreemptionAllocSlice, normalizePreemptedAlloc(mockAlloc, now))
47	}
48
49	// Create a plan result
50	applyPlanLogEntry := structs.ApplyPlanResultsRequest{
51		AllocUpdateRequest: structs.AllocUpdateRequest{
52			AllocsUpdated: mockUpdatedAllocSlice,
53			AllocsStopped: mockStoppedAllocSlice,
54		},
55		AllocsPreempted: mockPreemptionAllocSlice,
56	}
57
58	handle := structs.MsgpackHandle
59	var buf bytes.Buffer
60	if err := codec.NewEncoder(&buf, handle).Encode(applyPlanLogEntry); err != nil {
61		t.Fatalf("Encoding failed: %v", err)
62	}
63
64	optimizedLogSize := buf.Len()
65	assert.True(t, float64(optimizedLogSize)/float64(unoptimizedLogSize) < 0.62)
66}
67