github.com/m3db/m3@v1.5.0/src/dbnode/storage/index/compaction/plan_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package compaction
    22  
    23  import (
    24  	"fmt"
    25  	"sort"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/dbnode/storage/index/segments"
    30  
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestDefaultOptsValidate(t *testing.T) {
    35  	require.NoError(t, DefaultOptions.Validate())
    36  }
    37  
    38  func TestSingleMutableCompaction(t *testing.T) {
    39  	opts := DefaultOptions
    40  	candidates := []Segment{
    41  		Segment{
    42  			Age:  (opts.MutableCompactionAgeThreshold + time.Second),
    43  			Size: 10,
    44  			Type: segments.MutableType,
    45  		},
    46  	}
    47  	plan, err := NewPlan(candidates, opts)
    48  	require.NoError(t, err)
    49  	requirePlansEqual(t, &Plan{
    50  		Tasks: []Task{
    51  			Task{
    52  				Segments: candidates,
    53  			},
    54  		},
    55  		OrderBy: opts.OrderBy,
    56  	}, plan)
    57  }
    58  
    59  func TestReportNewMutableSegmentUnused(t *testing.T) {
    60  	opts := DefaultOptions
    61  	var (
    62  		s1 = Segment{
    63  			Age:  (opts.MutableCompactionAgeThreshold - time.Second),
    64  			Size: 10,
    65  			Type: segments.MutableType,
    66  		}
    67  		s2 = Segment{
    68  			Age:  (opts.MutableCompactionAgeThreshold + time.Second),
    69  			Size: 10,
    70  			Type: segments.MutableType,
    71  		}
    72  	)
    73  	candidates := []Segment{s1, s2}
    74  	plan, err := NewPlan(candidates, opts)
    75  	require.NoError(t, err)
    76  	requirePlansEqual(t, &Plan{
    77  		Tasks: []Task{
    78  			Task{Segments: []Segment{s1, s2}},
    79  		},
    80  		OrderBy: opts.OrderBy,
    81  	}, plan)
    82  }
    83  
    84  func TestMarkUnusedSegmentsSingleTier(t *testing.T) {
    85  	opts := testOptions()
    86  	var (
    87  		s1 = Segment{
    88  			Age:  (opts.MutableCompactionAgeThreshold + time.Second),
    89  			Size: 10,
    90  			Type: segments.MutableType,
    91  		}
    92  		s2 = Segment{
    93  			Size: 60,
    94  			Type: segments.FSTType,
    95  		}
    96  		s3 = Segment{
    97  			Size: 61,
    98  			Type: segments.FSTType,
    99  		}
   100  	)
   101  	candidates := []Segment{s1, s3, s2}
   102  	plan, err := NewPlan(candidates, opts)
   103  	require.NoError(t, err)
   104  	require.Equal(t, &Plan{
   105  		UnusedSegments: []Segment{s3},
   106  		Tasks: []Task{
   107  			Task{Segments: []Segment{s1, s2}},
   108  		},
   109  		OrderBy: opts.OrderBy,
   110  	}, plan)
   111  }
   112  
   113  func TestDontCompactSegmentTooLarge(t *testing.T) {
   114  	opts := testOptions()
   115  	sort.Sort(ByMinSize(opts.Levels))
   116  	maxBucketSize := opts.Levels[len(opts.Levels)-1].MaxSizeExclusive
   117  	var (
   118  		s1 = Segment{
   119  			Age:  (opts.MutableCompactionAgeThreshold + time.Second),
   120  			Size: maxBucketSize + 1,
   121  			Type: segments.MutableType,
   122  		}
   123  		s2 = Segment{
   124  			Size: maxBucketSize + 1,
   125  			Type: segments.FSTType,
   126  		}
   127  		s3 = Segment{
   128  			Age:  (opts.MutableCompactionAgeThreshold + time.Second),
   129  			Size: 61,
   130  			Type: segments.MutableType,
   131  		}
   132  		s4 = Segment{
   133  			Size: 128,
   134  			Type: segments.FSTType,
   135  		}
   136  	)
   137  	candidates := []Segment{s1, s2, s3, s4}
   138  	plan, err := NewPlan(candidates, opts)
   139  	require.NoError(t, err)
   140  	require.Equal(t, &Plan{
   141  		UnusedSegments: []Segment{s2, s4}, // s2 is too large to be compacted, s4 is by itself in the second tier
   142  		Tasks: []Task{
   143  			Task{Segments: []Segment{s3}}, // s3 is small enough to be compacted
   144  			Task{Segments: []Segment{s1}}, // s1 should be compacted regardless of size because its mutable
   145  		},
   146  		OrderBy: opts.OrderBy,
   147  	}, plan)
   148  }
   149  
   150  func TestPlanOrderByMutableAge(t *testing.T) {
   151  	var (
   152  		s1 = Segment{
   153  			Age:  10,
   154  			Size: 1,
   155  			Type: segments.MutableType,
   156  		}
   157  		s2 = Segment{
   158  			Age:  10,
   159  			Size: 10,
   160  			Type: segments.MutableType,
   161  		}
   162  		s3 = Segment{
   163  			Age:  100,
   164  			Size: 10,
   165  			Type: segments.MutableType,
   166  		}
   167  	)
   168  	p := &Plan{
   169  		Tasks: []Task{
   170  			Task{Segments: []Segment{s1, s2}},
   171  			Task{Segments: []Segment{s3}},
   172  		},
   173  		OrderBy: TasksOrderedByOldestMutableAndSize,
   174  	}
   175  	sort.Sort(p)
   176  	requirePlansEqual(t, &Plan{
   177  		Tasks: []Task{
   178  			Task{Segments: []Segment{s3}},
   179  			Task{Segments: []Segment{s1, s2}},
   180  		},
   181  		OrderBy: TasksOrderedByOldestMutableAndSize,
   182  	}, p)
   183  }
   184  func TestPlanOrderByNumMutable(t *testing.T) {
   185  	var (
   186  		s1 = Segment{
   187  			Age:  5,
   188  			Size: 1,
   189  			Type: segments.MutableType,
   190  		}
   191  		s2 = Segment{
   192  			Age:  5,
   193  			Size: 10,
   194  			Type: segments.MutableType,
   195  		}
   196  		s3 = Segment{
   197  			Age:  10,
   198  			Size: 10,
   199  			Type: segments.MutableType,
   200  		}
   201  	)
   202  	p := &Plan{
   203  		Tasks: []Task{
   204  			Task{Segments: []Segment{s3}},
   205  			Task{Segments: []Segment{s1, s2}},
   206  		},
   207  		OrderBy: TasksOrderedByOldestMutableAndSize,
   208  	}
   209  	sort.Sort(p)
   210  	requirePlansEqual(t, &Plan{
   211  		Tasks: []Task{
   212  			Task{Segments: []Segment{s1, s2}},
   213  			Task{Segments: []Segment{s3}},
   214  		},
   215  		OrderBy: TasksOrderedByOldestMutableAndSize,
   216  	}, p)
   217  }
   218  
   219  func TestPlanOrderByMutableCumulativeSize(t *testing.T) {
   220  	var (
   221  		s1 = Segment{
   222  			Age:  10,
   223  			Size: 10,
   224  			Type: segments.MutableType,
   225  		}
   226  		s2 = Segment{
   227  			Age:  10,
   228  			Size: 2,
   229  			Type: segments.MutableType,
   230  		}
   231  		s3 = Segment{
   232  			Age:  15,
   233  			Size: 10,
   234  			Type: segments.MutableType,
   235  		}
   236  		s4 = Segment{
   237  			Age:  5,
   238  			Size: 6,
   239  			Type: segments.MutableType,
   240  		}
   241  	)
   242  	p := &Plan{
   243  		Tasks: []Task{
   244  			Task{Segments: []Segment{s3, s4}},
   245  			Task{Segments: []Segment{s1, s2}},
   246  		},
   247  		OrderBy: TasksOrderedByOldestMutableAndSize,
   248  	}
   249  	sort.Sort(p)
   250  	requirePlansEqual(t, &Plan{
   251  		Tasks: []Task{
   252  			Task{Segments: []Segment{s1, s2}},
   253  			Task{Segments: []Segment{s3, s4}},
   254  		},
   255  		OrderBy: TasksOrderedByOldestMutableAndSize,
   256  	}, p)
   257  }
   258  
   259  func requirePlansEqual(t *testing.T, expected, observed *Plan) {
   260  	if expected == nil {
   261  		require.Nil(t, observed)
   262  		return
   263  	}
   264  	require.Equal(t, len(expected.UnusedSegments), len(observed.UnusedSegments),
   265  		fmt.Sprintf("exp [%+v]\nobs[%+v]", expected.UnusedSegments, observed.UnusedSegments))
   266  	for i := range expected.UnusedSegments {
   267  		require.Equal(t, expected.UnusedSegments[i], observed.UnusedSegments[i], i)
   268  	}
   269  	require.Equal(t, len(expected.Tasks), len(observed.Tasks),
   270  		fmt.Sprintf("exp [%+v]\nobs[%+v]", expected.Tasks, observed.Tasks))
   271  	for i := range expected.Tasks {
   272  		require.Equal(t, expected.Tasks[i], observed.Tasks[i])
   273  	}
   274  	require.Equal(t, expected.OrderBy, observed.OrderBy)
   275  }
   276  
   277  func testOptions() PlannerOptions {
   278  	opts := DefaultOptions
   279  	opts.Levels = []Level{ // i.e. tiers for compaction [0, 64), [64, 524), [524, 4000)
   280  		Level{
   281  			MinSizeInclusive: 0,
   282  			MaxSizeExclusive: 64,
   283  		},
   284  		Level{
   285  			MinSizeInclusive: 64,
   286  			MaxSizeExclusive: 524,
   287  		},
   288  		Level{
   289  			MinSizeInclusive: 524,
   290  			MaxSizeExclusive: 4000,
   291  		},
   292  	}
   293  	return opts
   294  }