/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.accumulo.core.util; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.accumulo.core.client.Connector; import org.apache.accumulo.core.data.impl.KeyExtent; import org.apache.accumulo.core.util.Merge.Size; import org.apache.hadoop.io.Text; import org.junit.Test; public class MergeTest { static class MergeTester extends Merge { public List<List<Size>> merges = new ArrayList<>(); public List<Size> tablets = new ArrayList<>(); MergeTester(Integer... sizes) { Text start = null; for (Integer size : sizes) { Text end; if (tablets.size() == sizes.length - 1) end = null; else end = new Text(String.format("%05d", tablets.size())); KeyExtent extent = new KeyExtent("table", end, start); start = end; tablets.add(new Size(extent, size)); } } @Override protected void message(String format, Object... args) {} @Override protected Iterator<Size> getSizeIterator(Connector conn, String tablename, final Text start, final Text end) throws MergeException { final Iterator<Size> impl = tablets.iterator(); return new Iterator<Size>() { Size next = skip(); @Override public boolean hasNext() { return next != null; } private Size skip() { while (impl.hasNext()) { Size candidate = impl.next(); if (start != null) { if (candidate.extent.getEndRow() != null && candidate.extent.getEndRow().compareTo(start) < 0) continue; } if (end != null) { if (candidate.extent.getPrevEndRow() != null && candidate.extent.getPrevEndRow().compareTo(end) >= 0) continue; } return candidate; } return null; } @Override public Size next() { Size result = next; next = skip(); return result; } @Override public void remove() { impl.remove(); } }; } @Override protected void merge(Connector conn, String table, List<Size> sizes, int numToMerge) throws MergeException { List<Size> merge = new ArrayList<>(); for (int i = 0; i < numToMerge; i++) { merge.add(sizes.get(i)); } merges.add(merge); } } static private int[] sizes(List<Size> sizes) { int result[] = new int[sizes.size()]; int i = 0; for (Size s : sizes) { result[i++] = (int) s.size; } return result; } @Test public void testMergomatic() throws Exception { // Merge everything to the last tablet int i; MergeTester test = new MergeTester(10, 20, 30); test.mergomatic(null, "table", null, null, 1000, false); assertEquals(1, test.merges.size()); assertArrayEquals(new int[] {10, 20, 30}, sizes(test.merges.get(i = 0))); // Merge ranges around tablets that are big enough test = new MergeTester(1, 2, 100, 1000, 17, 1000, 4, 5, 6, 900); test.mergomatic(null, "table", null, null, 1000, false); assertEquals(2, test.merges.size()); assertArrayEquals(new int[] {1, 2, 100}, sizes(test.merges.get(i = 0))); assertArrayEquals(new int[] {4, 5, 6, 900}, sizes(test.merges.get(++i))); // Test the force option test = new MergeTester(1, 2, 100, 1000, 17, 1000, 4, 5, 6, 900); test.mergomatic(null, "table", null, null, 1000, true); assertEquals(3, test.merges.size()); assertArrayEquals(new int[] {1, 2, 100}, sizes(test.merges.get(i = 0))); assertArrayEquals(new int[] {17, 1000}, sizes(test.merges.get(++i))); assertArrayEquals(new int[] {4, 5, 6, 900}, sizes(test.merges.get(++i))); // Limit the low-end of the merges test = new MergeTester(1, 2, 1000, 17, 1000, 4, 5, 6, 900); test.mergomatic(null, "table", new Text("00004"), null, 1000, false); assertEquals(1, test.merges.size()); assertArrayEquals(new int[] {4, 5, 6, 900}, sizes(test.merges.get(i = 0))); // Limit the upper end of the merges test = new MergeTester(1, 2, 1000, 17, 1000, 4, 5, 6, 900); test.mergomatic(null, "table", null, new Text("00004"), 1000, false); assertEquals(1, test.merges.size()); assertArrayEquals(new int[] {1, 2}, sizes(test.merges.get(i = 0))); // Limit both ends test = new MergeTester(1, 2, 1000, 17, 1000, 4, 5, 6, 900); test.mergomatic(null, "table", new Text("00002"), new Text("00004"), 1000, true); assertEquals(1, test.merges.size()); assertArrayEquals(new int[] {17, 1000}, sizes(test.merges.get(i = 0))); // Clump up tablets into larger values test = new MergeTester(100, 250, 500, 600, 100, 200, 500, 200); test.mergomatic(null, "table", null, null, 1000, false); assertEquals(3, test.merges.size()); assertArrayEquals(new int[] {100, 250, 500}, sizes(test.merges.get(i = 0))); assertArrayEquals(new int[] {600, 100, 200}, sizes(test.merges.get(++i))); assertArrayEquals(new int[] {500, 200}, sizes(test.merges.get(++i))); } }