/* * 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.hive.hcatalog.streaming.mutate.worker; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.ql.io.RecordIdentifier; import org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat; import org.apache.hive.hcatalog.streaming.mutate.client.AcidTable; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TestMutatorCoordinator { private static final List<String> UNPARTITIONED = Collections.<String> emptyList(); private static final List<String> PARTITION_B = Arrays.asList("B"); private static final List<String> PARTITION_A = Arrays.asList("A"); private static final long TRANSACTION_ID = 2L; private static final int BUCKET_ID = 0; private static final Path PATH_A = new Path("X"); private static final Path PATH_B = new Path("B"); private static final Object RECORD = "RECORD"; private static final RecordIdentifier ROW__ID_B0_R0 = new RecordIdentifier(10L, BUCKET_ID, 0L); private static final RecordIdentifier ROW__ID_B0_R1 = new RecordIdentifier(10L, BUCKET_ID, 1L); private static final RecordIdentifier ROW__ID_B1_R0 = new RecordIdentifier(10L, BUCKET_ID + 1, 0L); private static final RecordIdentifier ROW__ID_INSERT = new RecordIdentifier(-1L, BUCKET_ID, -1L); @Mock private MutatorFactory mockMutatorFactory; @Mock private PartitionHelper mockPartitionHelper; @Mock private GroupingValidator mockGroupingValidator; @Mock private SequenceValidator mockSequenceValidator; @Mock private AcidTable mockAcidTable; @Mock private RecordInspector mockRecordInspector; @Mock private BucketIdResolver mockBucketIdResolver; @Mock private Mutator mockMutator; private MutatorCoordinator coordinator; private HiveConf configuration = new HiveConf(); @Before public void createCoordinator() throws Exception { when(mockAcidTable.getOutputFormatName()).thenReturn(OrcOutputFormat.class.getName()); when(mockAcidTable.getTotalBuckets()).thenReturn(1); when(mockAcidTable.getTransactionId()).thenReturn(TRANSACTION_ID); when(mockAcidTable.createPartitions()).thenReturn(true); when(mockMutatorFactory.newRecordInspector()).thenReturn(mockRecordInspector); when(mockMutatorFactory.newBucketIdResolver(anyInt())).thenReturn(mockBucketIdResolver); when(mockMutatorFactory.newMutator(any(OrcOutputFormat.class), anyLong(), any(Path.class), anyInt())).thenReturn( mockMutator); when(mockPartitionHelper.getPathForPartition(any(List.class))).thenReturn(PATH_A); when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_INSERT); when(mockSequenceValidator.isInSequence(any(RecordIdentifier.class))).thenReturn(true); when(mockGroupingValidator.isInSequence(any(List.class), anyInt())).thenReturn(true); coordinator = new MutatorCoordinator(configuration, mockMutatorFactory, mockPartitionHelper, mockGroupingValidator, mockSequenceValidator, mockAcidTable, false); } @Test public void insert() throws Exception { coordinator.insert(UNPARTITIONED, RECORD); verify(mockPartitionHelper).createPartitionIfNotExists(UNPARTITIONED); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutator).insert(RECORD); } @Test public void multipleInserts() throws Exception { coordinator.insert(UNPARTITIONED, RECORD); coordinator.insert(UNPARTITIONED, RECORD); coordinator.insert(UNPARTITIONED, RECORD); verify(mockPartitionHelper).createPartitionIfNotExists(UNPARTITIONED); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutator, times(3)).insert(RECORD); } @Test public void insertPartitionChanges() throws Exception { when(mockPartitionHelper.getPathForPartition(PARTITION_A)).thenReturn(PATH_A); when(mockPartitionHelper.getPathForPartition(PARTITION_B)).thenReturn(PATH_B); coordinator.insert(PARTITION_A, RECORD); coordinator.insert(PARTITION_B, RECORD); verify(mockPartitionHelper).createPartitionIfNotExists(PARTITION_A); verify(mockPartitionHelper).createPartitionIfNotExists(PARTITION_B); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_B), eq(BUCKET_ID)); verify(mockMutator, times(2)).insert(RECORD); } @Test public void bucketChanges() throws Exception { when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_B0_R0, ROW__ID_B1_R0); when(mockBucketIdResolver.computeBucketId(RECORD)).thenReturn(0, 1); coordinator.update(UNPARTITIONED, RECORD); coordinator.delete(UNPARTITIONED, RECORD); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutatorFactory) .newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID + 1)); verify(mockMutator).update(RECORD); verify(mockMutator).delete(RECORD); } @Test public void partitionThenBucketChanges() throws Exception { when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_B0_R0, ROW__ID_B0_R1, ROW__ID_B1_R0, ROW__ID_INSERT); when(mockBucketIdResolver.computeBucketId(RECORD)).thenReturn(0, 0, 1, 0); when(mockPartitionHelper.getPathForPartition(PARTITION_A)).thenReturn(PATH_A); when(mockPartitionHelper.getPathForPartition(PARTITION_B)).thenReturn(PATH_B); coordinator.update(PARTITION_A, RECORD); /* PaB0 */ coordinator.insert(PARTITION_B, RECORD); /* PbB0 */ coordinator.delete(PARTITION_B, RECORD); /* PbB0 */ coordinator.update(PARTITION_B, RECORD); /* PbB1 */ verify(mockPartitionHelper).createPartitionIfNotExists(PARTITION_B); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutatorFactory, times(2)).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_B), eq(BUCKET_ID)); verify(mockMutatorFactory) .newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_B), eq(BUCKET_ID + 1)); verify(mockMutator, times(2)).update(RECORD); verify(mockMutator).delete(RECORD); verify(mockMutator).insert(RECORD); verify(mockSequenceValidator, times(4)).reset(); } @Test public void partitionThenBucketChangesNoCreateAsPartitionEstablished() throws Exception { when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_B0_R0, ROW__ID_INSERT); when(mockBucketIdResolver.computeBucketId(RECORD)).thenReturn(0, 0); when(mockPartitionHelper.getPathForPartition(PARTITION_B)).thenReturn(PATH_B); coordinator.delete(PARTITION_B, RECORD); /* PbB0 */ coordinator.insert(PARTITION_B, RECORD); /* PbB0 */ verify(mockPartitionHelper, never()).createPartitionIfNotExists(anyList()); } @Test(expected = RecordSequenceException.class) public void outOfSequence() throws Exception { when(mockSequenceValidator.isInSequence(any(RecordIdentifier.class))).thenReturn(false); coordinator.update(UNPARTITIONED, RECORD); coordinator.delete(UNPARTITIONED, RECORD); verify(mockPartitionHelper).createPartitionIfNotExists(UNPARTITIONED); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutator).update(RECORD); verify(mockMutator).delete(RECORD); } @Test(expected = GroupRevisitedException.class) public void revisitGroup() throws Exception { when(mockGroupingValidator.isInSequence(any(List.class), anyInt())).thenReturn(false); coordinator.update(UNPARTITIONED, RECORD); coordinator.delete(UNPARTITIONED, RECORD); verify(mockPartitionHelper).createPartitionIfNotExists(UNPARTITIONED); verify(mockMutatorFactory).newMutator(any(OrcOutputFormat.class), eq(TRANSACTION_ID), eq(PATH_A), eq(BUCKET_ID)); verify(mockMutator).update(RECORD); verify(mockMutator).delete(RECORD); } @Test(expected = BucketIdException.class) public void insertWithBadBucket() throws Exception { when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_B0_R0); when(mockBucketIdResolver.computeBucketId(RECORD)).thenReturn(1); coordinator.insert(UNPARTITIONED, RECORD); } @Test(expected = BucketIdException.class) public void updateWithBadBucket() throws Exception { when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_B0_R0); when(mockBucketIdResolver.computeBucketId(RECORD)).thenReturn(1); coordinator.update(UNPARTITIONED, RECORD); } @Test public void deleteWithBadBucket() throws Exception { when(mockRecordInspector.extractRecordIdentifier(RECORD)).thenReturn(ROW__ID_B0_R0); when(mockBucketIdResolver.computeBucketId(RECORD)).thenReturn(1); coordinator.delete(UNPARTITIONED, RECORD); } @Test public void closeNoRecords() throws Exception { coordinator.close(); // No mutator created verifyZeroInteractions(mockMutator); } @Test public void closeUsedCoordinator() throws Exception { coordinator.insert(UNPARTITIONED, RECORD); coordinator.close(); verify(mockMutator).close(); verify(mockPartitionHelper).close(); } }