This is an automated email from the ASF dual-hosted git repository. kturner pushed a commit to branch elasticity in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/elasticity by this push: new 01b0ff9356 Unit test compaction reservation and deny offline (#4334) 01b0ff9356 is described below commit 01b0ff9356b851d8f47368989407fa610441c23d Author: Keith Turner <ktur...@apache.org> AuthorDate: Mon Mar 4 10:30:10 2024 -0500 Unit test compaction reservation and deny offline (#4334) Changes the compaction coordinator to not return jobs for a table that is offline. The point where this check was added to the coordinator needed unit test, so also added the unit test. --- .../coordinator/CompactionCoordinator.java | 18 ++- .../compaction/CompactionCoordinatorTest.java | 148 +++++++++++++++++++++ 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/server/manager/src/main/java/org/apache/accumulo/manager/compaction/coordinator/CompactionCoordinator.java b/server/manager/src/main/java/org/apache/accumulo/manager/compaction/coordinator/CompactionCoordinator.java index ebd5d8f764..a07ff50bc4 100644 --- a/server/manager/src/main/java/org/apache/accumulo/manager/compaction/coordinator/CompactionCoordinator.java +++ b/server/manager/src/main/java/org/apache/accumulo/manager/compaction/coordinator/CompactionCoordinator.java @@ -74,6 +74,7 @@ import org.apache.accumulo.core.fate.FateInstanceType; import org.apache.accumulo.core.fate.zookeeper.ZooReaderWriter; import org.apache.accumulo.core.iterators.user.HasExternalCompactionsFilter; import org.apache.accumulo.core.iteratorsImpl.system.SystemIteratorUtil; +import org.apache.accumulo.core.manager.state.tables.TableState; import org.apache.accumulo.core.metadata.CompactableFileImpl; import org.apache.accumulo.core.metadata.ReferencedTabletFile; import org.apache.accumulo.core.metadata.StoredTabletFile; @@ -124,6 +125,7 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.Weigher; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.common.net.HostAndPort; @@ -382,9 +384,9 @@ public class CompactionCoordinator } - // ELASTICITY_TODO unit test this code - private boolean canReserveCompaction(TabletMetadata tablet, CompactionJob job, - Set<StoredTabletFile> jobFiles) { + @VisibleForTesting + public static boolean canReserveCompaction(TabletMetadata tablet, CompactionKind kind, + Set<StoredTabletFile> jobFiles, ServerContext ctx) { if (tablet == null) { // the tablet no longer exist @@ -395,6 +397,10 @@ public class CompactionCoordinator return false; } + if (ctx.getTableState(tablet.getTableId()) != TableState.ONLINE) { + return false; + } + if (!tablet.getFiles().containsAll(jobFiles)) { return false; } @@ -406,7 +412,7 @@ public class CompactionCoordinator return false; } - switch (job.getKind()) { + switch (kind) { case SYSTEM: var userRequestedCompactions = tablet.getUserCompactionsRequested().size(); if (userRequestedCompactions > 0) { @@ -427,7 +433,7 @@ public class CompactionCoordinator } break; default: - throw new UnsupportedOperationException("Not currently handling " + job.getKind()); + throw new UnsupportedOperationException("Not currently handling " + kind); } return true; @@ -508,7 +514,7 @@ public class CompactionCoordinator try (var tabletsMutator = ctx.getAmple().conditionallyMutateTablets()) { var extent = metaJob.getTabletMetadata().getExtent(); - if (!canReserveCompaction(tabletMetadata, metaJob.getJob(), jobFiles)) { + if (!canReserveCompaction(tabletMetadata, metaJob.getJob().getKind(), jobFiles, ctx)) { return null; } diff --git a/server/manager/src/test/java/org/apache/accumulo/manager/compaction/CompactionCoordinatorTest.java b/server/manager/src/test/java/org/apache/accumulo/manager/compaction/CompactionCoordinatorTest.java index 770405863e..4c0b2b1d52 100644 --- a/server/manager/src/test/java/org/apache/accumulo/manager/compaction/CompactionCoordinatorTest.java +++ b/server/manager/src/test/java/org/apache/accumulo/manager/compaction/CompactionCoordinatorTest.java @@ -18,10 +18,18 @@ */ package org.apache.accumulo.manager.compaction; +import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.ECOMP; +import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.OPID; +import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.SELECTED; +import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.USER_COMPACTION_REQUESTED; +import static org.apache.accumulo.manager.compaction.coordinator.CompactionCoordinator.canReserveCompaction; import static org.easymock.EasyMock.expect; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -47,12 +55,17 @@ import org.apache.accumulo.core.fate.Fate; import org.apache.accumulo.core.fate.FateId; import org.apache.accumulo.core.fate.FateInstanceType; import org.apache.accumulo.core.iteratorsImpl.system.SystemIteratorUtil; +import org.apache.accumulo.core.manager.state.tables.TableState; import org.apache.accumulo.core.metadata.CompactableFileImpl; import org.apache.accumulo.core.metadata.ReferencedTabletFile; import org.apache.accumulo.core.metadata.StoredTabletFile; import org.apache.accumulo.core.metadata.schema.CompactionMetadata; +import org.apache.accumulo.core.metadata.schema.DataFileValue; import org.apache.accumulo.core.metadata.schema.ExternalCompactionId; +import org.apache.accumulo.core.metadata.schema.SelectedFiles; import org.apache.accumulo.core.metadata.schema.TabletMetadata; +import org.apache.accumulo.core.metadata.schema.TabletOperationId; +import org.apache.accumulo.core.metadata.schema.TabletOperationType; import org.apache.accumulo.core.securityImpl.thrift.TCredentials; import org.apache.accumulo.core.spi.compaction.CompactionJob; import org.apache.accumulo.core.spi.compaction.CompactionKind; @@ -374,4 +387,139 @@ public class CompactionCoordinatorTest { EasyMock.verify(context, creds, security); } + + @Test + public void testCanReserve() throws Exception { + TableId tableId1 = TableId.of("5"); + TableId tableId2 = TableId.of("6"); + + var file1 = StoredTabletFile.of(new URI("file:///accumulo/tables/1/default_tablet/F00001.rf")); + var file2 = StoredTabletFile.of(new URI("file:///accumulo/tables/1/default_tablet/F00002.rf")); + var file3 = StoredTabletFile.of(new URI("file:///accumulo/tables/1/default_tablet/F00003.rf")); + var file4 = StoredTabletFile.of(new URI("file:///accumulo/tables/1/default_tablet/F00004.rf")); + + ServerContext context = EasyMock.mock(ServerContext.class); + EasyMock.expect(context.getTableState(tableId1)).andReturn(TableState.ONLINE).atLeastOnce(); + EasyMock.expect(context.getTableState(tableId2)).andReturn(TableState.OFFLINE).atLeastOnce(); + + FateId fateId1 = FateId.from(FateInstanceType.USER, 1234L); + + CompactorGroupId cgid = CompactorGroupId.of("G1"); + ReferencedTabletFile tmp1 = + ReferencedTabletFile.of(new Path("file:///accumulo/tables/1/default_tablet/C00005.rf_tmp")); + CompactionMetadata cm1 = new CompactionMetadata(Set.of(file1, file2), tmp1, "localhost:4444", + CompactionKind.SYSTEM, (short) 5, cgid, false, null); + + ReferencedTabletFile tmp2 = + ReferencedTabletFile.of(new Path("file:///accumulo/tables/1/default_tablet/C00006.rf_tmp")); + CompactionMetadata cm2 = new CompactionMetadata(Set.of(file3), tmp2, "localhost:5555", + CompactionKind.USER, (short) 5, cgid, false, fateId1); + + EasyMock.replay(context); + + KeyExtent extent1 = new KeyExtent(tableId1, null, null); + + var dfv = new DataFileValue(1000, 100); + + var cid1 = ExternalCompactionId.generate(UUID.randomUUID()); + var cid2 = ExternalCompactionId.generate(UUID.randomUUID()); + + var selected = new SelectedFiles(Set.of(file1, file2, file3), false, fateId1); + + // should not be able to compact an offline table + var tabletOffline = TabletMetadata.builder(new KeyExtent(tableId2, null, null)) + .putFile(file1, dfv).putFile(file2, dfv).putFile(file3, dfv).putFile(file4, dfv) + .build(OPID, ECOMP, USER_COMPACTION_REQUESTED, SELECTED); + assertFalse( + canReserveCompaction(tabletOffline, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + + // nothing should prevent this compaction + var tablet1 = + TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv).putFile(file3, dfv) + .putFile(file4, dfv).build(OPID, ECOMP, USER_COMPACTION_REQUESTED, SELECTED); + assertTrue(canReserveCompaction(tablet1, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + + // should not be able to do a user compaction unless selected files are present + assertFalse(canReserveCompaction(tablet1, CompactionKind.USER, Set.of(file1, file2), context)); + + // should not be able to compact a tablet with user compaction request in place + var tablet3 = + TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv).putFile(file3, dfv) + .putFile(file4, dfv).putUserCompactionRequested(fateId1).build(OPID, ECOMP, SELECTED); + assertFalse( + canReserveCompaction(tablet3, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + + // should not be able to compact a tablet when the job has files not present in the tablet + var tablet4 = TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv) + .putFile(file3, dfv).build(OPID, ECOMP, USER_COMPACTION_REQUESTED, SELECTED); + assertFalse( + canReserveCompaction(tablet4, CompactionKind.SYSTEM, Set.of(file1, file2, file4), context)); + + // should not be able to compact a tablet with an operation id present + TabletOperationId opid = TabletOperationId.from(TabletOperationType.SPLITTING, fateId1); + var tablet5 = TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv) + .putFile(file3, dfv).putFile(file4, dfv).putOperation(opid) + .build(ECOMP, USER_COMPACTION_REQUESTED, SELECTED); + assertFalse( + canReserveCompaction(tablet5, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + + // should not be able to compact a tablet if the job files overlaps with running compactions + var tablet6 = TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv) + .putFile(file3, dfv).putFile(file4, dfv).putExternalCompaction(cid1, cm1) + .putExternalCompaction(cid2, cm2).build(OPID, USER_COMPACTION_REQUESTED, SELECTED); + assertFalse( + canReserveCompaction(tablet6, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + // should be able to compact the file that is outside of the set of files currently compacting + assertTrue(canReserveCompaction(tablet6, CompactionKind.SYSTEM, Set.of(file4), context)); + + // create a tablet with a selected set of files + var selTablet = TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv) + .putFile(file3, dfv).putFile(file4, dfv).putSelectedFiles(selected) + .build(OPID, USER_COMPACTION_REQUESTED, ECOMP); + // should not be able to start a system compaction if the set of files overlaps with the + // selected files + assertFalse( + canReserveCompaction(selTablet, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + assertFalse( + canReserveCompaction(selTablet, CompactionKind.SYSTEM, Set.of(file3, file4), context)); + // should be able to start a system compaction on the set of files not in the selected set + assertTrue(canReserveCompaction(selTablet, CompactionKind.SYSTEM, Set.of(file4), context)); + // should be able to start user compactions on files that are selected + assertTrue(canReserveCompaction(selTablet, CompactionKind.USER, Set.of(file1, file2), context)); + assertTrue(canReserveCompaction(selTablet, CompactionKind.USER, Set.of(file2, file3), context)); + assertTrue( + canReserveCompaction(selTablet, CompactionKind.USER, Set.of(file1, file2, file3), context)); + // should not be able to start user compactions on files that fall outside of the selected set + assertFalse( + canReserveCompaction(selTablet, CompactionKind.USER, Set.of(file1, file4), context)); + assertFalse(canReserveCompaction(selTablet, CompactionKind.USER, Set.of(file4), context)); + assertFalse(canReserveCompaction(selTablet, CompactionKind.USER, + Set.of(file1, file2, file3, file4), context)); + + // test selected files and running compaction + var selRunningTablet = TabletMetadata.builder(extent1).putFile(file1, dfv).putFile(file2, dfv) + .putFile(file3, dfv).putFile(file4, dfv).putSelectedFiles(selected) + .putExternalCompaction(cid2, cm2).build(OPID, USER_COMPACTION_REQUESTED); + // should be able to compact files that are in the selected set and not in the running set + assertTrue( + canReserveCompaction(selRunningTablet, CompactionKind.USER, Set.of(file1, file2), context)); + // should not be able to compact because files overlap the running set + assertFalse( + canReserveCompaction(selRunningTablet, CompactionKind.USER, Set.of(file2, file3), context)); + // should not be able to start a system compaction if the set of files overlaps with the + // selected files and/or the running set + assertFalse(canReserveCompaction(selRunningTablet, CompactionKind.SYSTEM, Set.of(file1, file2), + context)); + assertFalse(canReserveCompaction(selRunningTablet, CompactionKind.SYSTEM, Set.of(file3, file4), + context)); + // should be able to start a system compaction on the set of files not in the selected set + assertTrue( + canReserveCompaction(selRunningTablet, CompactionKind.SYSTEM, Set.of(file4), context)); + + // should not be able to compact a tablet that does not exists + assertFalse(canReserveCompaction(null, CompactionKind.SYSTEM, Set.of(file1, file2), context)); + assertFalse(canReserveCompaction(null, CompactionKind.USER, Set.of(file1, file2), context)); + + EasyMock.verify(context); + } }