Files
upstream-bleve/index/scorch/mergeplan/merge_plan_test.go
upstream-mirror f8d3d9f0cf
Some checks failed
Coverage / coverage (push) Failing after 6s
Tests / test (1.24.x, ubuntu-latest) (push) Failing after 42s
Tests / test (1.26.x, ubuntu-latest) (push) Failing after 38s
Tests / test (1.25.x, ubuntu-latest) (push) Failing after 35s
Tests / test (1.25.x, windows-latest) (push) Has been cancelled
Tests / test (1.26.x, macos-latest) (push) Has been cancelled
Tests / test (1.26.x, windows-latest) (push) Has been cancelled
Tests / test (1.24.x, macos-latest) (push) Has been cancelled
Tests / test (1.24.x, windows-latest) (push) Has been cancelled
Tests / test (1.25.x, macos-latest) (push) Has been cancelled
snapshot of https://github.com/blevesearch/bleve.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 22:27:16 +05:00

1128 lines
25 KiB
Go

// Copyright (c) 2017 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mergeplan
import (
"encoding/json"
"fmt"
"math/rand"
"os"
"reflect"
"slices"
"sort"
"testing"
"time"
)
// Implements the Segment interface for testing,
type segment struct {
MyId uint64
MyFullSize int64
MyLiveSize int64
MyHasVector bool
MyFileSize int64
}
func (s *segment) Id() uint64 { return s.MyId }
func (s *segment) FullSize() int64 { return s.MyFullSize }
func (s *segment) LiveSize() int64 { return s.MyLiveSize }
func (s *segment) HasVector() bool { return s.MyHasVector }
func (s *segment) FileSize() int64 { return s.MyFileSize }
func (s *segment) LiveFileSize() int64 {
// LiveFileSize is an estimate of the live portion of the file size,
// based on the ratio of live to full size.
fullSize := float64(s.MyFullSize)
if fullSize <= 0 {
return 0
}
liveSize := float64(s.MyLiveSize)
if liveSize <= 0 {
return 0
}
fileSize := float64(s.MyFileSize)
if fileSize <= 0 {
return 0
}
liveRatio := liveSize / fullSize
return int64(fileSize * liveRatio)
}
func makeLinearSegments(n int) (rv []Segment) {
for i := 0; i < n; i++ {
rv = append(rv, &segment{
MyId: uint64(i),
MyFullSize: int64(i),
MyLiveSize: int64(i),
})
}
return rv
}
// ----------------------------------------
func TestSimplePlan(t *testing.T) {
segs := makeLinearSegments(10)
tests := []struct {
Desc string
Segments []Segment
Options *MergePlanOptions
ExpectPlan *MergePlan
ExpectErr error
}{
{
"nil segments",
nil, nil, nil, nil,
},
{
"empty segments",
[]Segment{},
nil, nil, nil,
},
{
"1 segment",
[]Segment{segs[1]},
nil,
nil,
nil,
},
{
"2 segments",
[]Segment{
segs[1],
segs[2],
},
nil,
&MergePlan{
Tasks: []*MergeTask{
{
Segments: []Segment{
segs[2],
segs[1],
},
},
},
},
nil,
},
{
"3 segments",
[]Segment{
segs[1],
segs[2],
segs[9],
},
nil,
&MergePlan{
Tasks: []*MergeTask{
{
Segments: []Segment{
segs[9],
segs[2],
segs[1],
},
},
},
},
nil,
},
{
"many segments",
[]Segment{
segs[1],
segs[2],
segs[3],
segs[4],
segs[5],
segs[6],
},
&MergePlanOptions{
MaxSegmentsPerTier: 1,
MaxSegmentSize: 1000,
TierGrowth: 2.0,
SegmentsPerMergeTask: 2,
FloorSegmentSize: 1,
},
&MergePlan{
Tasks: []*MergeTask{
{
Segments: []Segment{
segs[6],
segs[5],
},
},
},
},
nil,
},
}
for testi, test := range tests {
plan, err := Plan(test.Segments, test.Options)
if err != test.ExpectErr {
testj, _ := json.Marshal(&test)
t.Errorf("testi: %d, test: %s, got err: %v", testi, testj, err)
}
if !reflect.DeepEqual(plan, test.ExpectPlan) {
testj, _ := json.Marshal(&test)
planj, _ := json.Marshal(&plan)
t.Errorf("testi: %d, test: %s, got plan: %s",
testi, testj, planj)
}
}
}
// ----------------------------------------
func TestSort(t *testing.T) {
segs := makeLinearSegments(10)
sort.Sort(byLiveSizeDescending(segs))
for i := 1; i < len(segs); i++ {
if segs[i].LiveSize() >= segs[i-1].LiveSize() {
t.Errorf("not descending")
}
}
}
// ----------------------------------------
func TestCalcBudget(t *testing.T) {
tests := []struct {
totalSize int64
firstTierSize int64
o MergePlanOptions
expect int
}{
{0, 0, MergePlanOptions{}, 0},
{1, 0, MergePlanOptions{}, 1},
{9, 0, MergePlanOptions{}, 9},
{
1, 1,
MergePlanOptions{
MaxSegmentsPerTier: 1,
MaxSegmentSize: 1000,
TierGrowth: 2.0,
SegmentsPerMergeTask: 2,
FloorSegmentSize: 1,
},
1,
},
{
21, 1,
MergePlanOptions{
MaxSegmentsPerTier: 1,
MaxSegmentSize: 1000,
TierGrowth: 2.0,
SegmentsPerMergeTask: 2,
FloorSegmentSize: 1,
},
5,
},
{
21, 1,
MergePlanOptions{
MaxSegmentsPerTier: 2,
MaxSegmentSize: 1000,
TierGrowth: 2.0,
SegmentsPerMergeTask: 2,
FloorSegmentSize: 1,
},
7,
},
{
1000, 2000, DefaultMergePlanOptions,
1,
},
{
5000, 2000, DefaultMergePlanOptions,
3,
},
{
10000, 2000, DefaultMergePlanOptions,
5,
},
{
30000, 2000, DefaultMergePlanOptions,
11,
},
{
1000000, 2000, DefaultMergePlanOptions,
24,
},
{
1000000000, 2000, DefaultMergePlanOptions,
54,
},
}
for testi, test := range tests {
res := CalcBudget(test.totalSize, test.firstTierSize, &test.o)
if res != test.expect {
t.Errorf("testi: %d, test: %#v, res: %v",
testi, test, res)
}
}
}
func TestCalcBudgetForSingleSegmentMergePolicy(t *testing.T) {
mpolicy := MergePlanOptions{
MaxSegmentsPerTier: 1,
MaxSegmentSize: 1 << 30, // ~ 1 Billion
SegmentsPerMergeTask: 10,
FloorSegmentSize: 1 << 30,
}
tests := []struct {
totalSize int64
firstTierSize int64
o MergePlanOptions
expect int
}{
{0, mpolicy.RaiseToFloorSegmentSize(0), mpolicy, 0},
{1, mpolicy.RaiseToFloorSegmentSize(1), mpolicy, 1},
{9, mpolicy.RaiseToFloorSegmentSize(0), mpolicy, 1},
{1, mpolicy.RaiseToFloorSegmentSize(1), mpolicy, 1},
{21, mpolicy.RaiseToFloorSegmentSize(21), mpolicy, 1},
{21, mpolicy.RaiseToFloorSegmentSize(21), mpolicy, 1},
{1000, mpolicy.RaiseToFloorSegmentSize(2000), mpolicy, 1},
{5000, mpolicy.RaiseToFloorSegmentSize(5000), mpolicy, 1},
{10000, mpolicy.RaiseToFloorSegmentSize(10000), mpolicy, 1},
{30000, mpolicy.RaiseToFloorSegmentSize(30000), mpolicy, 1},
{1000000, mpolicy.RaiseToFloorSegmentSize(1000000), mpolicy, 1},
{1000000000, 1 << 30, mpolicy, 1},
{1013423541, 1 << 30, mpolicy, 1},
{98765442, 1 << 30, mpolicy, 1},
}
for testi, test := range tests {
res := CalcBudget(test.totalSize, test.firstTierSize, &test.o)
if res != test.expect {
t.Errorf("testi: %d, test: %#v, res: %v",
testi, test, res)
}
}
}
// ----------------------------------------
func TestInsert1SameSizedSegmentBetweenMerges(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 1000,
MaxSegmentsPerTier: 3,
TierGrowth: 3.0,
SegmentsPerMergeTask: 3,
}
spec := testCyclesSpec{
descrip: "i1sssbm",
verbose: os.Getenv("VERBOSE") == "i1sssbm" || os.Getenv("VERBOSE") == "y",
n: 200,
o: o,
beforePlan: func(spec *testCyclesSpec) {
spec.segments = append(spec.segments, &segment{
MyId: spec.nextSegmentId,
MyFullSize: 1,
MyLiveSize: 1,
})
spec.nextSegmentId++
},
}
spec.runCycles(t)
}
func TestInsertManySameSizedSegmentsBetweenMerges(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 1000,
MaxSegmentsPerTier: 3,
TierGrowth: 3.0,
SegmentsPerMergeTask: 3,
}
spec := testCyclesSpec{
descrip: "imsssbm",
verbose: os.Getenv("VERBOSE") == "imsssbm" || os.Getenv("VERBOSE") == "y",
n: 20,
o: o,
beforePlan: func(spec *testCyclesSpec) {
for i := 0; i < 10; i++ {
spec.segments = append(spec.segments, &segment{
MyId: spec.nextSegmentId,
MyFullSize: 1,
MyLiveSize: 1,
})
spec.nextSegmentId++
}
},
}
spec.runCycles(t)
}
func TestInsertManySameSizedSegmentsWithDeletionsBetweenMerges(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 1000,
MaxSegmentsPerTier: 3,
TierGrowth: 3.0,
SegmentsPerMergeTask: 3,
}
spec := testCyclesSpec{
descrip: "imssswdbm",
verbose: os.Getenv("VERBOSE") == "imssswdbm" || os.Getenv("VERBOSE") == "y",
n: 20,
o: o,
beforePlan: func(spec *testCyclesSpec) {
for i := 0; i < 10; i++ {
// Deletions are a shrinking of the live size.
for i, seg := range spec.segments {
if (spec.cycle+i)%5 == 0 {
s := seg.(*segment)
if s.MyLiveSize > 0 {
s.MyLiveSize -= 1
}
}
}
spec.segments = append(spec.segments, &segment{
MyId: spec.nextSegmentId,
MyFullSize: 1,
MyLiveSize: 1,
})
spec.nextSegmentId++
}
},
}
spec.runCycles(t)
}
func TestInsertManyDifferentSizedSegmentsBetweenMerges(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 1000,
MaxSegmentsPerTier: 3,
TierGrowth: 3.0,
SegmentsPerMergeTask: 3,
}
spec := testCyclesSpec{
descrip: "imdssbm",
verbose: os.Getenv("VERBOSE") == "imdssbm" || os.Getenv("VERBOSE") == "y",
n: 20,
o: o,
beforePlan: func(spec *testCyclesSpec) {
for i := 0; i < 10; i++ {
spec.segments = append(spec.segments, &segment{
MyId: spec.nextSegmentId,
MyFullSize: int64(1 + (i % 5)),
MyLiveSize: int64(1 + (i % 5)),
})
spec.nextSegmentId++
}
},
}
spec.runCycles(t)
}
func TestManySameSizedSegmentsWithDeletesBetweenMerges(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 1000,
MaxSegmentsPerTier: 3,
TierGrowth: 3.0,
SegmentsPerMergeTask: 3,
}
var numPlansWithTasks int
spec := testCyclesSpec{
descrip: "mssswdbm",
verbose: os.Getenv("VERBOSE") == "mssswdbm" || os.Getenv("VERBOSE") == "y",
n: 20,
o: o,
beforePlan: func(spec *testCyclesSpec) {
// Deletions are a shrinking of the live size.
for i, seg := range spec.segments {
if (spec.cycle+i)%5 == 0 {
s := seg.(*segment)
if s.MyLiveSize > 0 {
s.MyLiveSize -= 1
}
}
}
for i := 0; i < 10; i++ {
spec.segments = append(spec.segments, &segment{
MyId: spec.nextSegmentId,
MyFullSize: 1,
MyLiveSize: 1,
})
spec.nextSegmentId++
}
},
afterPlan: func(spec *testCyclesSpec, plan *MergePlan) {
if plan != nil && len(plan.Tasks) > 0 {
numPlansWithTasks++
}
},
}
spec.runCycles(t)
if numPlansWithTasks <= 0 {
t.Errorf("expected some plans with tasks")
}
}
func TestValidateMergePlannerOptions(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 1 << 32,
MaxSegmentsPerTier: 3,
TierGrowth: 3.0,
SegmentsPerMergeTask: 3,
}
err := ValidateMergePlannerOptions(o)
if err != ErrMaxSegmentSizeTooLarge {
t.Error("Validation expected to fail as the MaxSegmentSize exceeds limit")
}
}
func TestPlanMaxSegmentSizeLimit(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentSize: 20,
MaxSegmentsPerTier: 5,
TierGrowth: 3.0,
SegmentsPerMergeTask: 5,
FloorSegmentSize: 5,
}
segments := makeLinearSegments(20)
s := rand.NewSource(time.Now().UnixNano())
r := rand.New(s)
max := 20
min := 5
randomInRange := func() int64 {
return int64(r.Intn(max-min) + min)
}
for i := 1; i < 20; i++ {
o.MaxSegmentSize = randomInRange()
plans, err := Plan(segments, o)
if err != nil {
t.Errorf("Plan failed, err: %v", err)
}
if len(plans.Tasks) == 0 {
t.Errorf("expected some plans with tasks")
}
for _, task := range plans.Tasks {
var totalLiveSize int64
for _, segs := range task.Segments {
totalLiveSize += segs.LiveSize()
}
if totalLiveSize >= o.MaxSegmentSize {
t.Errorf("merged segments size: %d exceeding the MaxSegmentSize"+
"limit: %d", totalLiveSize, o.MaxSegmentSize)
}
}
}
}
// ----------------------------------------
type testCyclesSpec struct {
descrip string
verbose bool
n int // Number of cycles to run.
o *MergePlanOptions
converge bool
beforePlan func(*testCyclesSpec)
afterPlan func(*testCyclesSpec, *MergePlan)
cycle int
segments []Segment
nextSegmentId uint64
}
func (spec *testCyclesSpec) runCycles(t *testing.T) {
numPlansWithTasks := 0
for spec.cycle < spec.n {
if spec.verbose {
emit(spec.descrip, spec.cycle, 0, spec.segments, nil)
}
if spec.beforePlan != nil {
spec.beforePlan(spec)
}
if spec.verbose {
emit(spec.descrip, spec.cycle, 1, spec.segments, nil)
}
plan, err := Plan(spec.segments, spec.o)
if err != nil {
t.Fatalf("expected no err, got: %v", err)
}
if spec.afterPlan != nil {
spec.afterPlan(spec, plan)
}
if spec.verbose {
emit(spec.descrip, spec.cycle, 2, spec.segments, plan)
}
if plan == nil || len(plan.Tasks) == 0 {
if spec.converge {
return
}
spec.cycle++
continue
}
numPlansWithTasks++
before := len(spec.segments)
var beforeFullSize int64
for _, s := range spec.segments {
beforeFullSize += s.FullSize()
}
for _, task := range plan.Tasks {
spec.segments = removeSegments(spec.segments, task.Segments)
var totLiveSize int64
var totLiveFileSize int64
var hasVector bool
for _, segment := range task.Segments {
totLiveSize += segment.LiveSize()
totLiveFileSize += segment.LiveFileSize()
if segment.HasVector() {
hasVector = true
}
}
if totLiveSize > 0 {
spec.segments = append(spec.segments, &segment{
MyId: spec.nextSegmentId,
MyFullSize: totLiveSize,
MyLiveSize: totLiveSize,
MyFileSize: totLiveFileSize,
MyHasVector: hasVector,
})
spec.nextSegmentId++
}
}
after := len(spec.segments)
var afterFullSize int64
for _, s := range spec.segments {
afterFullSize += s.FullSize()
}
if after > before {
t.Errorf("cycle %d: plan produced %d task(s) but increased segment count (%d segs -> %d segs)",
spec.cycle, len(plan.Tasks), before, after)
}
if after == before && afterFullSize >= beforeFullSize {
t.Errorf("cycle %d: plan produced %d task(s) but made no progress "+
"(%d segs / %d bytes -> %d segs / %d bytes)",
spec.cycle, len(plan.Tasks), before, beforeFullSize,
len(spec.segments), afterFullSize)
}
spec.cycle++
}
if spec.converge {
t.Errorf("index did not converge within %d cycles (%d segments remain); "+
"the planner keeps producing tasks, likely an infinite loop",
spec.n, len(spec.segments))
}
if numPlansWithTasks <= 0 {
t.Errorf("expected some plans with tasks")
}
}
func emit(descrip string, cycle int, step int, segments []Segment, plan *MergePlan) {
if os.Getenv("VERBOSE") == "" {
return
}
suffix := ""
if plan != nil && len(plan.Tasks) > 0 {
suffix = "hasPlan"
}
fmt.Printf("%s %d.%d ---------- %s\n", descrip, cycle, step, suffix)
fmt.Printf("%s\n", ToBarChart(descrip, 100, segments, plan))
}
// ----------------------------------------
func TestPlanMaxSegmentFileSize(t *testing.T) {
tests := []struct {
segments []Segment
o *MergePlanOptions
expectedFinalSegments []Segment
}{
{
[]Segment{
&segment{ // ineligible
MyId: 1,
MyFullSize: 4000,
MyLiveSize: 3900,
MyHasVector: true,
MyFileSize: 4000 * 1000 * 4, // > 2MB
},
&segment{ // ineligible
MyId: 2,
MyFullSize: 6000,
MyLiveSize: 5500, // > 5000
MyHasVector: true,
MyFileSize: 6000 * 1000 * 4, // > 2MB
},
&segment{ // eligible
MyId: 3,
MyFullSize: 500,
MyLiveSize: 490,
MyHasVector: true,
MyFileSize: 500 * 1000 * 4,
},
&segment{ // eligible
MyId: 4,
MyFullSize: 500,
MyLiveSize: 480,
MyHasVector: true,
MyFileSize: 500 * 1000 * 4,
},
&segment{ // eligible
MyId: 5,
MyFullSize: 500,
MyLiveSize: 300,
MyHasVector: true,
MyFileSize: 500 * 1000 * 4,
},
&segment{ // eligible
MyId: 6,
MyFullSize: 500,
MyLiveSize: 400,
MyHasVector: true,
MyFileSize: 500 * 1000 * 4,
},
},
&MergePlanOptions{
MaxSegmentSize: 5000, // number of documents
// considering vector dimension as 1000
// vectorBytes = 5000 * 1000 * 4 = 20MB, which is too large
// So, let's set the fileSize limit to 4MB
MaxSegmentFileSize: 4000000, // 4MB
MaxSegmentsPerTier: 1,
SegmentsPerMergeTask: 2,
TierGrowth: 2.0,
ReclaimDeletesWeight: 2,
FloorSegmentSize: 1,
},
[]Segment{
&segment{
MyId: 1,
MyFullSize: 4000,
MyLiveSize: 3900,
MyHasVector: true,
MyFileSize: 4000 * 1000 * 4,
},
&segment{
MyId: 2,
MyFullSize: 6000,
MyLiveSize: 5500,
MyHasVector: true,
MyFileSize: 6000 * 1000 * 4,
},
&segment{
MyId: 3,
MyFullSize: 500,
MyLiveSize: 490,
MyHasVector: true,
MyFileSize: 500 * 1000 * 4,
},
&segment{
MyId: 4,
MyFullSize: 500,
MyLiveSize: 480,
MyHasVector: true,
MyFileSize: 500 * 1000 * 4,
},
&segment{
MyId: 7,
MyFullSize: 700,
MyLiveSize: 700,
MyHasVector: true,
MyFileSize: 700 * 1000 * 4,
},
},
},
}
for testi, test := range tests {
t.Run(fmt.Sprintf("Test-%d", testi), func(t *testing.T) {
var nextID uint64
for _, seg := range test.segments {
if seg.Id() >= nextID {
nextID = seg.Id() + 1
}
}
spec := testCyclesSpec{
descrip: fmt.Sprintf("pmsfs-%d", testi),
verbose: os.Getenv("VERBOSE") == "pmsfs" || os.Getenv("VERBOSE") == "y",
n: 10,
o: test.o,
converge: true,
segments: slices.Clone(test.segments),
nextSegmentId: nextID,
}
spec.runCycles(t)
if !reflect.DeepEqual(spec.segments, test.expectedFinalSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
})
}
}
func TestSingleSegmentTaskMergePlan(t *testing.T) {
o := DefaultMergePlanOptions
o.FloorSegmentFileSize = 209715200
spec := testCyclesSpec{
descrip: "stmp",
verbose: os.Getenv("VERBOSE") == "stmp" || os.Getenv("VERBOSE") == "y",
n: 10,
o: &o,
converge: true,
segments: []Segment{
&segment{
MyId: 2,
MyFullSize: 78059,
MyLiveSize: 78059,
MyFileSize: 129475914,
},
&segment{
MyId: 1,
MyFullSize: 3959,
MyLiveSize: 3959,
MyFileSize: 24805725,
},
},
nextSegmentId: 3,
}
spec.runCycles(t)
expectedSegments := []Segment{
&segment{
MyId: 3,
MyFullSize: 78059 + 3959,
MyLiveSize: 78059 + 3959,
MyFileSize: 129475914 + 24805725,
},
}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}
func TestPlanLiveFileSizeEligibility(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentFileSize: 4 * 1000 * 1000,
MaxSegmentSize: 100 * 1000,
SegmentsPerMergeTask: 2,
TierGrowth: 2.0,
FloorSegmentSize: 2000,
}
segments := []Segment{
&segment{
MyId: 1,
MyFullSize: 1000,
MyLiveSize: 500,
MyHasVector: true,
MyFileSize: 3 * 1000 * 1000,
},
&segment{
MyId: 2,
MyFullSize: 1000,
MyLiveSize: 500,
MyHasVector: true,
MyFileSize: 3 * 1000 * 1000,
},
&segment{
MyId: 3,
MyFullSize: 1000,
MyLiveSize: 1000,
MyHasVector: true,
MyFileSize: 3 * 1000 * 1000,
},
}
spec := testCyclesSpec{
descrip: "plfse",
verbose: os.Getenv("VERBOSE") == "plfse" || os.Getenv("VERBOSE") == "y",
n: 10,
o: o,
converge: true,
segments: slices.Clone(segments),
nextSegmentId: 4,
}
spec.runCycles(t)
expectedSegments := []Segment{
&segment{
MyId: 3,
MyFullSize: 1000,
MyLiveSize: 1000,
MyHasVector: true,
MyFileSize: 3 * 1000 * 1000,
},
&segment{
MyId: 4,
MyFullSize: 1000,
MyLiveSize: 1000,
MyHasVector: true,
MyFileSize: 3 * 1000 * 1000,
},
}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}
func TestPlanLiveFileSizeBudget(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentsPerTier: 1,
SegmentsPerMergeTask: 2,
TierGrowth: 2.0,
FloorSegmentSize: 1,
FloorSegmentFileSize: 10 * 1000 * 1000,
MaxSegmentSize: 100 * 1000,
MaxSegmentFileSize: 4 * 1000 * 1000 * 1000,
}
segments := []Segment{
&segment{
MyId: 1,
MyFullSize: 1000,
MyLiveSize: 100,
MyFileSize: 10 * 1000 * 1000,
},
&segment{
MyId: 2,
MyFullSize: 1000,
MyLiveSize: 100,
MyFileSize: 10 * 1000 * 1000,
},
}
spec := testCyclesSpec{
descrip: "plfsb",
verbose: os.Getenv("VERBOSE") == "plfsb" || os.Getenv("VERBOSE") == "y",
n: 10,
o: o,
converge: true,
segments: slices.Clone(segments),
nextSegmentId: 3,
}
spec.runCycles(t)
expectedSegments := []Segment{
&segment{
MyId: 3,
MyFullSize: 200,
MyLiveSize: 200,
MyFileSize: 2 * 1000 * 1000,
},
}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}
func TestEmptySegmentsDoNotForceOverMerge(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentsPerTier: 10,
MaxSegmentSize: 1000000,
SegmentsPerMergeTask: 10,
FloorSegmentSize: 1,
CalcBudget: func(totalSize, firstTierSize int64, o *MergePlanOptions) int {
return 2
},
}
spec := testCyclesSpec{
descrip: "esdnfom",
verbose: os.Getenv("VERBOSE") == "esdnfom" || os.Getenv("VERBOSE") == "y",
n: 10,
o: o,
converge: true,
segments: []Segment{
&segment{MyId: 1, MyFullSize: 100, MyLiveSize: 100},
&segment{MyId: 2, MyFullSize: 100, MyLiveSize: 100},
&segment{MyId: 3, MyFullSize: 100, MyLiveSize: 0},
},
nextSegmentId: 4,
}
spec.runCycles(t)
expectedSegments := []Segment{
&segment{MyId: 1, MyFullSize: 100, MyLiveSize: 100},
&segment{MyId: 2, MyFullSize: 100, MyLiveSize: 100},
}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}
func TestOnlyEmptySegments(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentsPerTier: 10,
MaxSegmentSize: 1000000,
SegmentsPerMergeTask: 10,
FloorSegmentSize: 1,
}
spec := testCyclesSpec{
descrip: "oes",
verbose: os.Getenv("VERBOSE") == "oes" || os.Getenv("VERBOSE") == "y",
n: 10,
o: o,
converge: true,
segments: []Segment{
&segment{MyId: 1, MyFullSize: 100, MyLiveSize: 0},
&segment{MyId: 2, MyFullSize: 100, MyLiveSize: 0},
&segment{MyId: 3, MyFullSize: 100, MyLiveSize: 0},
},
nextSegmentId: 4,
}
spec.runCycles(t)
expectedSegments := []Segment{}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}
func TestEligibleAndFilteredSegments(t *testing.T) {
o := &MergePlanOptions{
MaxSegmentsPerTier: 10,
MaxSegmentSize: 1000000,
MaxSegmentFileSize: 1000,
SegmentsPerMergeTask: 10,
FloorSegmentSize: 1,
FloorSegmentFileSize: 2000,
}
spec := testCyclesSpec{
descrip: "eafs",
verbose: os.Getenv("VERBOSE") == "eafs" || os.Getenv("VERBOSE") == "y",
n: 10,
o: o,
converge: true,
segments: []Segment{
&segment{MyId: 1, MyFullSize: 100, MyLiveSize: 100, MyFileSize: 400, MyHasVector: true},
&segment{MyId: 2, MyFullSize: 100, MyLiveSize: 100, MyFileSize: 400, MyHasVector: true},
&segment{MyId: 3, MyFullSize: 100, MyLiveSize: 100, MyFileSize: 400, MyHasVector: true},
&segment{MyId: 4, MyFullSize: 100, MyLiveSize: 100, MyFileSize: 400, MyHasVector: true},
},
nextSegmentId: 5,
}
spec.runCycles(t)
expectedSegments := []Segment{
&segment{MyId: 5, MyFullSize: 200, MyLiveSize: 200, MyFileSize: 800, MyHasVector: true},
&segment{MyId: 6, MyFullSize: 200, MyLiveSize: 200, MyFileSize: 800, MyHasVector: true},
}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}
func TestPlanWithNoBestRoster(t *testing.T) {
o := DefaultMergePlanOptions
o.SegmentsPerMergeTask = 0
spec := testCyclesSpec{
descrip: "pwnbr",
verbose: os.Getenv("VERBOSE") == "pwnbr" || os.Getenv("VERBOSE") == "y",
n: 10,
o: &o,
converge: true,
segments: []Segment{
&segment{MyId: 1, MyFullSize: 100, MyLiveSize: 0, MyFileSize: 400},
&segment{MyId: 2, MyFullSize: 100, MyLiveSize: 100, MyFileSize: 400},
&segment{MyId: 3, MyFullSize: 100, MyLiveSize: 50, MyFileSize: 4000},
},
nextSegmentId: 5,
}
spec.runCycles(t)
expectedSegments := []Segment{
&segment{MyId: 2, MyFullSize: 100, MyLiveSize: 100, MyFileSize: 400},
&segment{MyId: 3, MyFullSize: 100, MyLiveSize: 50, MyFileSize: 4000},
}
if !reflect.DeepEqual(spec.segments, expectedSegments) {
t.Fatalf("final layout mismatch, got: %+v", spec.segments)
}
}