/* * 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 com.facebook.presto.raptor.storage.organization; import com.facebook.presto.raptor.metadata.Table; import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import io.airlift.units.DataSize; import org.testng.annotations.Test; import java.time.Duration; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Set; import java.util.UUID; import static com.facebook.presto.spi.type.DateType.DATE; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.google.common.collect.Iterables.getOnlyElement; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; public class TestCompactionSetCreator { private static final long MAX_SHARD_ROWS = 100; private static final DataSize MAX_SHARD_SIZE = new DataSize(100, DataSize.Unit.BYTE); private static final Table tableInfo = new Table(1L, OptionalLong.empty(), Optional.empty(), OptionalInt.empty(), OptionalLong.empty(), false); private static final Table temporalTableInfo = new Table(1L, OptionalLong.empty(), Optional.empty(), OptionalInt.empty(), OptionalLong.of(1), false); private static final Table bucketedTableInfo = new Table(1L, OptionalLong.empty(), Optional.empty(), OptionalInt.of(3), OptionalLong.empty(), false); private static final Table bucketedTemporalTableInfo = new Table(1L, OptionalLong.empty(), Optional.empty(), OptionalInt.of(3), OptionalLong.of(1), false); private final CompactionSetCreator compactionSetCreator = new CompactionSetCreator(MAX_SHARD_SIZE, MAX_SHARD_ROWS); @Test public void testNonTemporalOrganizationSetSimple() throws Exception { List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithSize(10, 10), shardWithSize(10, 10), shardWithSize(10, 10)); Set<OrganizationSet> compactionSets = compactionSetCreator.createCompactionSets(tableInfo, inputShards); assertEquals(compactionSets.size(), 1); assertEquals(getOnlyElement(compactionSets).getShards(), extractIndexes(inputShards, 0, 1, 2)); } @Test public void testNonTemporalSizeBasedOrganizationSet() throws Exception { List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithSize(10, 70), shardWithSize(10, 20), shardWithSize(10, 30), shardWithSize(10, 120)); Set<OrganizationSet> compactionSets = compactionSetCreator.createCompactionSets(tableInfo, inputShards); Set<UUID> actual = new HashSet<>(); for (OrganizationSet set : compactionSets) { actual.addAll(set.getShards()); } assertTrue(extractIndexes(inputShards, 0, 1, 2).containsAll(actual)); } @Test public void testNonTemporalRowCountBasedOrganizationSet() throws Exception { List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithSize(50, 10), shardWithSize(100, 10), shardWithSize(20, 10), shardWithSize(30, 10)); Set<OrganizationSet> compactionSets = compactionSetCreator.createCompactionSets(tableInfo, inputShards); Set<UUID> actual = new HashSet<>(); for (OrganizationSet set : compactionSets) { actual.addAll(set.getShards()); } assertTrue(extractIndexes(inputShards, 0, 2, 3).containsAll(actual)); } @Test public void testTemporalCompactionNoCompactionAcrossDays() throws Exception { long day1 = Duration.ofDays(Duration.ofNanos(System.nanoTime()).toDays()).toMillis(); long day2 = Duration.ofDays(Duration.ofMillis(day1).toDays() + 1).toMillis(); long day3 = Duration.ofDays(Duration.ofMillis(day1).toDays() + 2).toMillis(); List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithTemporalRange(TIMESTAMP, day1, day1), shardWithTemporalRange(TIMESTAMP, day2, day2), shardWithTemporalRange(TIMESTAMP, day2, day2), shardWithTemporalRange(TIMESTAMP, day1, day1), shardWithTemporalRange(TIMESTAMP, day3, day3)); Set<OrganizationSet> actual = compactionSetCreator.createCompactionSets(temporalTableInfo, inputShards); assertEquals(actual.size(), 2); Set<OrganizationSet> expected = ImmutableSet.of( new OrganizationSet(temporalTableInfo.getTableId(), extractIndexes(inputShards, 0, 3), OptionalInt.empty()), new OrganizationSet(temporalTableInfo.getTableId(), extractIndexes(inputShards, 1, 2), OptionalInt.empty())); assertEquals(actual, expected); } @Test public void testTemporalCompactionSpanningDays() throws Exception { long day1 = Duration.ofDays(Duration.ofNanos(System.nanoTime()).toDays()).toMillis(); long day2 = Duration.ofDays(Duration.ofMillis(day1).toDays() + 1).toMillis(); long day3 = Duration.ofDays(Duration.ofMillis(day1).toDays() + 2).toMillis(); long day4 = Duration.ofDays(Duration.ofMillis(day1).toDays() + 3).toMillis(); List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithTemporalRange(TIMESTAMP, day1, day3), // day2 shardWithTemporalRange(TIMESTAMP, day2, day2), // day2 shardWithTemporalRange(TIMESTAMP, day1, day1), // day1 shardWithTemporalRange(TIMESTAMP, day1 + 100, day2 + 100), // day1 shardWithTemporalRange(TIMESTAMP, day1 - 100, day2 - 100), // day1 shardWithTemporalRange(TIMESTAMP, day2 - 100, day3 - 100), // day2 shardWithTemporalRange(TIMESTAMP, day1, day4) // day2 ); long tableId = temporalTableInfo.getTableId(); Set<OrganizationSet> compactionSets = compactionSetCreator.createCompactionSets(temporalTableInfo, inputShards); assertEquals(compactionSets.size(), 2); Set<OrganizationSet> expected = ImmutableSet.of( new OrganizationSet(tableId, extractIndexes(inputShards, 0, 1, 5, 6), OptionalInt.empty()), new OrganizationSet(tableId, extractIndexes(inputShards, 2, 3, 4), OptionalInt.empty())); assertEquals(compactionSets, expected); } @Test public void testTemporalCompactionDate() throws Exception { long day1 = Duration.ofNanos(System.nanoTime()).toDays(); long day2 = day1 + 1; long day3 = day1 + 2; List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithTemporalRange(DATE, day1, day1), shardWithTemporalRange(DATE, day2, day2), shardWithTemporalRange(DATE, day3, day3), shardWithTemporalRange(DATE, day1, day3), shardWithTemporalRange(DATE, day2, day3), shardWithTemporalRange(DATE, day1, day2)); long tableId = temporalTableInfo.getTableId(); Set<OrganizationSet> actual = compactionSetCreator.createCompactionSets(temporalTableInfo, inputShards); assertEquals(actual.size(), 2); Set<OrganizationSet> expected = ImmutableSet.of( new OrganizationSet(tableId, extractIndexes(inputShards, 0, 3, 5), OptionalInt.empty()), new OrganizationSet(tableId, extractIndexes(inputShards, 1, 4), OptionalInt.empty())); assertEquals(actual, expected); } @Test public void testBucketedTableCompaction() throws Exception { List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithBucket(1), shardWithBucket(2), shardWithBucket(2), shardWithBucket(1), shardWithBucket(2), shardWithBucket(1)); long tableId = bucketedTableInfo.getTableId(); Set<OrganizationSet> actual = compactionSetCreator.createCompactionSets(bucketedTableInfo, inputShards); assertEquals(actual.size(), 2); Set<OrganizationSet> expected = ImmutableSet.of( new OrganizationSet(tableId, extractIndexes(inputShards, 0, 3, 5), OptionalInt.of(1)), new OrganizationSet(tableId, extractIndexes(inputShards, 1, 2, 4), OptionalInt.of(2))); assertEquals(actual, expected); } static Set<UUID> extractIndexes(List<ShardIndexInfo> inputShards, int... indexes) { ImmutableSet.Builder<UUID> builder = ImmutableSet.builder(); for (int index : indexes) { builder.add(inputShards.get(index).getShardUuid()); } return builder.build(); } @Test public void testBucketedTemporalTableCompaction() throws Exception { long day1 = 1; long day2 = 2; long day3 = 3; long day4 = 4; List<ShardIndexInfo> inputShards = ImmutableList.of( shardWithTemporalBucket(OptionalInt.of(1), DATE, day1, day1), shardWithTemporalBucket(OptionalInt.of(2), DATE, day2, day2), shardWithTemporalBucket(OptionalInt.of(1), DATE, day1, day1), shardWithTemporalBucket(OptionalInt.of(2), DATE, day2, day2), shardWithTemporalBucket(OptionalInt.of(1), DATE, day3, day3), shardWithTemporalBucket(OptionalInt.of(2), DATE, day4, day4)); long tableId = bucketedTemporalTableInfo.getTableId(); Set<OrganizationSet> actual = compactionSetCreator.createCompactionSets(bucketedTemporalTableInfo, inputShards); assertEquals(actual.size(), 2); Set<OrganizationSet> expected = ImmutableSet.of( new OrganizationSet(tableId, extractIndexes(inputShards, 0, 2), OptionalInt.of(1)), new OrganizationSet(tableId, extractIndexes(inputShards, 1, 3), OptionalInt.of(2))); assertEquals(actual, expected); } private static ShardIndexInfo shardWithSize(long rows, long size) { return new ShardIndexInfo( 1, OptionalInt.empty(), UUID.randomUUID(), rows, size, Optional.empty(), Optional.empty()); } private static ShardIndexInfo shardWithTemporalRange(Type type, Long start, Long end) { return shardWithTemporalBucket(OptionalInt.empty(), type, start, end); } private static ShardIndexInfo shardWithBucket(int bucketNumber) { return new ShardIndexInfo( 1, OptionalInt.of(bucketNumber), UUID.randomUUID(), 1, 1, Optional.empty(), Optional.empty()); } private static ShardIndexInfo shardWithTemporalBucket(OptionalInt bucketNumber, Type type, Long start, Long end) { if (type.equals(DATE)) { return new ShardIndexInfo( 1, bucketNumber, UUID.randomUUID(), 1, 1, Optional.empty(), Optional.of(ShardRange.of(new Tuple(type, start.intValue()), new Tuple(type, end.intValue())))); } return new ShardIndexInfo( 1, bucketNumber, UUID.randomUUID(), 1, 1, Optional.empty(), Optional.of(ShardRange.of(new Tuple(type, start), new Tuple(type, end)))); } }