This is an automated email from the ASF dual-hosted git repository.
cshannon 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 7aba4f984a Update offline operation to wait for no opid or ecomp
(#4414)
7aba4f984a is described below
commit 7aba4f984a35a4ae5c351b0b4716d4ed22151b51
Author: Christopher L. Shannon <[email protected]>
AuthorDate: Tue Mar 26 06:44:01 2024 -0400
Update offline operation to wait for no opid or ecomp (#4414)
Prior to elasticity a compaction or split could never run unless a
tablet was hosted and how these operations can run on unhosted tablets.
Taking a table offline should now wait for no OPID and ECOMP columns to
exist in addition to the existing check for no location.
This addresses a TODO in #3412
---
.../core/clientImpl/TableOperationsImpl.java | 12 ++-
.../org/apache/accumulo/test/OfflineTableIT.java | 119 +++++++++++++++++++++
2 files changed, 128 insertions(+), 3 deletions(-)
diff --git
a/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java
b/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java
index 2ea43612f1..293177a557 100644
---
a/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java
+++
b/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java
@@ -27,11 +27,13 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toSet;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.AVAILABILITY;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.DIR;
+import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.ECOMP;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.FILES;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.HOSTING_REQUESTED;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.LAST;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.LOCATION;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.LOGS;
+import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.OPID;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.PREV_ROW;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.SUSPEND;
import static
org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.TIME;
@@ -1305,19 +1307,23 @@ public class TableOperationsImpl extends
TableOperationsHelper {
Text continueRow = null;
MapCounter<String> serverCounts = new MapCounter<>();
- try (TabletsMetadata tablets =
TabletsMetadata.builder(context).scanMetadataTable()
- .overRange(range).fetch(AVAILABILITY, HOSTING_REQUESTED, LOCATION,
PREV_ROW).build()) {
+ try (TabletsMetadata tablets =
+ TabletsMetadata.builder(context).scanMetadataTable().overRange(range)
+ .fetch(AVAILABILITY, HOSTING_REQUESTED, LOCATION, PREV_ROW,
OPID, ECOMP).build()) {
for (TabletMetadata tablet : tablets) {
total++;
Location loc = tablet.getLocation();
TabletAvailability availability = tablet.getTabletAvailability();
+ var opid = tablet.getOperationId();
+ var externalCompactions = tablet.getExternalCompactions();
if ((expectedState == TableState.ONLINE
&& (availability == TabletAvailability.HOSTED
|| (availability == TabletAvailability.ONDEMAND) &&
tablet.getHostingRequested())
&& (loc == null || loc.getType() == LocationType.FUTURE))
- || (expectedState == TableState.OFFLINE && loc != null)) {
+ || (expectedState == TableState.OFFLINE
+ && (loc != null || opid != null ||
!externalCompactions.isEmpty()))) {
if (continueRow == null) {
continueRow = tablet.getExtent().toMetaRow();
}
diff --git a/test/src/main/java/org/apache/accumulo/test/OfflineTableIT.java
b/test/src/main/java/org/apache/accumulo/test/OfflineTableIT.java
index 1ddab10e52..9cdc2e3777 100644
--- a/test/src/main/java/org/apache/accumulo/test/OfflineTableIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/OfflineTableIT.java
@@ -18,19 +18,42 @@
*/
package org.apache.accumulo.test;
+import static
org.apache.accumulo.test.compaction.ExternalCompactionTestUtils.GROUP1;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.TreeSet;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
import org.apache.accumulo.core.client.Accumulo;
import org.apache.accumulo.core.client.AccumuloClient;
import org.apache.accumulo.core.client.TableOfflineException;
import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.data.TableId;
+import org.apache.accumulo.core.dataImpl.KeyExtent;
+import org.apache.accumulo.core.fate.FateId;
+import org.apache.accumulo.core.fate.FateInstanceType;
+import org.apache.accumulo.core.metadata.ReferencedTabletFile;
+import org.apache.accumulo.core.metadata.schema.Ample.TabletsMutator;
+import org.apache.accumulo.core.metadata.schema.CompactionMetadata;
+import org.apache.accumulo.core.metadata.schema.ExternalCompactionId;
+import org.apache.accumulo.core.metadata.schema.TabletOperationId;
+import org.apache.accumulo.core.metadata.schema.TabletOperationType;
import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.spi.compaction.CompactionKind;
+import org.apache.accumulo.core.spi.compaction.CompactorGroupId;
import org.apache.accumulo.harness.MiniClusterConfigurationCallback;
import org.apache.accumulo.harness.SharedMiniClusterBase;
import org.apache.accumulo.miniclusterImpl.MiniAccumuloConfigImpl;
+import org.apache.accumulo.server.ServerContext;
+import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -104,4 +127,100 @@ public class OfflineTableIT extends SharedMiniClusterBase
{
}
}
+ @Test
+ public void testEcompWaitForOffline() throws Exception {
+ final var ctx = getCluster().getServerContext();
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ ScanServerIT.createTableAndIngest(client, tableName, null, 10, 10,
"colf");
+ TableId tableId =
TableId.of(client.tableOperations().tableIdMap().get(tableName));
+ final var tabletMeta = ctx.getAmple().readTablet(new KeyExtent(tableId,
null, null));
+
+ final var ecid = ExternalCompactionId.generate(UUID.randomUUID());
+
+ // Insert a fake External compaction which should prevent the wait for
offline
+ // from returning
+ try (var mutator = ctx.getAmple().mutateTablets()) {
+ var tabletDir =
+
tabletMeta.getFiles().stream().findFirst().orElseThrow().getPath().getParent();
+ var tmpFile = new Path(tabletDir, "C1234.rf_tmp");
+ var cm = new CompactionMetadata(tabletMeta.getFiles(),
ReferencedTabletFile.of(tmpFile),
+ "localhost:16789", CompactionKind.SYSTEM, (short) 10,
CompactorGroupId.of(GROUP1),
+ false, null);
+
mutator.mutateTablet(tabletMeta.getExtent()).putExternalCompaction(ecid,
cm).mutate();
+ }
+
+ // test the ecomp prevents the wait for the offline() table operation
from finishing
+ // until the ecomp is deleted
+ testWaitForOffline(ctx, client, tableId, tableName, mutator -> mutator
+
.mutateTablet(tabletMeta.getExtent()).deleteExternalCompaction(ecid).mutate());
+ }
+ }
+
+ @Test
+ public void testOpidWaitForOffline() throws Exception {
+ final var ctx = getCluster().getServerContext();
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ String tableName = getUniqueNames(1)[0];
+ ScanServerIT.createTableAndIngest(client, tableName, null, 10, 10,
"colf");
+ TableId tableId =
TableId.of(client.tableOperations().tableIdMap().get(tableName));
+ final var tabletMeta = ctx.getAmple().readTablet(new KeyExtent(tableId,
null, null));
+
+ // Insert a fake opid to prevent going offline
+ try (var mutator = ctx.getAmple().mutateTablets()) {
+ mutator.mutateTablet(tabletMeta.getExtent())
+ .putOperation(TabletOperationId.from(TabletOperationType.SPLITTING,
+ FateId.from(FateInstanceType.META, UUID.randomUUID())))
+ .mutate();
+ }
+
+ // test the opid prevents the wait for the offline() table operation
from finishing
+ // until the opid is deleted
+ testWaitForOffline(ctx, client, tableId, tableName,
+ mutator ->
mutator.mutateTablet(tabletMeta.getExtent()).deleteOperation().mutate());
+ }
+ }
+
+ private void testWaitForOffline(ServerContext ctx, AccumuloClient client,
TableId tableId,
+ String tableName, Consumer<TabletsMutator> clear) throws Exception {
+
+ // Try and take the table offline. At this point this should hang because
there is a condition
+ // preventing waitForTableStateTransition call from returning (either opid
or ecomp)
+ final var service = Executors.newSingleThreadExecutor();
+ try {
+ var tabletMeta = ctx.getAmple().readTablet(new KeyExtent(tableId, null,
null));
+ Future<?> f = service.submit(() -> {
+ try {
+ client.tableOperations().offline(tableName, true);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ // Check that the wait times out for the offline operation
+ assertThrows(TimeoutException.class, () -> f.get(10, TimeUnit.SECONDS));
+
+ // Clear the condition that is preventing the
waitForTableStateTransition method
+ // in TableOperationsImpl from finishing
+ try (var mutator = ctx.getAmple().mutateTablets()) {
+ clear.accept(mutator);
+ }
+
+ // The future should now finish and we should be offline
+ f.get();
+ tabletMeta = ctx.getAmple().readTablet(new KeyExtent(tableId, null,
null));
+
+ // Should have no location, ecomp, or opid
+ assertFalse(tabletMeta.hasCurrent());
+ assertNull(tabletMeta.getLocation());
+ assertTrue(tabletMeta.getExternalCompactions().isEmpty());
+ assertNull(tabletMeta.getOperationId());
+ assertFalse(client.tableOperations().isOnline(tableName));
+ } finally {
+ service.shutdownNow();
+ }
+ }
+
}