This is an automated email from the ASF dual-hosted git repository.
dlmarion pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/2.1 by this push:
new 9bbf42f4dc Fix TabletGroupWatcher shutdown order (#3571)
9bbf42f4dc is described below
commit 9bbf42f4dc3ebcf21d70c837f577c60708d3d2de
Author: Dave Marion <[email protected]>
AuthorDate: Mon Jul 10 11:40:12 2023 -0400
Fix TabletGroupWatcher shutdown order (#3571)
Modified TabletGroupWatcher such that when shutting
down all TabletServers it unloads user tables before the
metadata table and the metadata table before the root
table.
---
.../manager/state/LoggingTabletStateStore.java | 6 +
.../server/manager/state/MetaDataStateStore.java | 15 +-
.../server/manager/state/RootTabletStateStore.java | 5 +-
.../server/manager/state/TabletStateStore.java | 11 +-
.../server/manager/state/ZooTabletStateStore.java | 10 +-
.../manager/state/RootTabletStateStoreTest.java | 3 +-
.../accumulo/manager/TabletGroupWatcher.java | 49 +++++-
.../apache/accumulo/manager/state/TableCounts.java | 9 ++
.../apache/accumulo/manager/state/TableStats.java | 6 +
.../manager/tserverOps/ShutdownTServer.java | 3 +
.../org/apache/accumulo/tserver/TabletServer.java | 1 +
.../accumulo/tserver/UnloadTabletHandler.java | 1 +
.../org/apache/accumulo/tserver/tablet/Tablet.java | 1 +
.../test/functional/ManagerAssignmentIT.java | 166 +++++++++++++++++++++
test/src/main/resources/log4j2-test.properties | 3 +
15 files changed, 276 insertions(+), 13 deletions(-)
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/LoggingTabletStateStore.java
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/LoggingTabletStateStore.java
index 6ef48fdc07..84c62254ca 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/LoggingTabletStateStore.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/LoggingTabletStateStore.java
@@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.logging.TabletLogger;
import org.apache.accumulo.core.metadata.TServerInstance;
import org.apache.accumulo.core.metadata.TabletLocationState;
+import org.apache.accumulo.core.metadata.schema.Ample.DataLevel;
import org.apache.accumulo.core.util.HostAndPort;
import org.apache.hadoop.fs.Path;
@@ -40,6 +41,11 @@ class LoggingTabletStateStore implements TabletStateStore {
this.wrapped = tss;
}
+ @Override
+ public DataLevel getLevel() {
+ return wrapped.getLevel();
+ }
+
@Override
public String name() {
return wrapped.name();
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/MetaDataStateStore.java
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/MetaDataStateStore.java
index bb27b32cda..8c7bc888eb 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/MetaDataStateStore.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/MetaDataStateStore.java
@@ -27,6 +27,7 @@ import org.apache.accumulo.core.metadata.MetadataTable;
import org.apache.accumulo.core.metadata.TServerInstance;
import org.apache.accumulo.core.metadata.TabletLocationState;
import org.apache.accumulo.core.metadata.schema.Ample;
+import org.apache.accumulo.core.metadata.schema.Ample.DataLevel;
import org.apache.accumulo.core.metadata.schema.Ample.TabletMutator;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
import org.apache.accumulo.core.metadata.schema.TabletMetadata.Location;
@@ -40,16 +41,24 @@ class MetaDataStateStore implements TabletStateStore {
protected final CurrentState state;
private final String targetTableName;
private final Ample ample;
+ private final DataLevel level;
- protected MetaDataStateStore(ClientContext context, CurrentState state,
String targetTableName) {
+ protected MetaDataStateStore(DataLevel level, ClientContext context,
CurrentState state,
+ String targetTableName) {
+ this.level = level;
this.context = context;
this.state = state;
this.ample = context.getAmple();
this.targetTableName = targetTableName;
}
- MetaDataStateStore(ClientContext context, CurrentState state) {
- this(context, state, MetadataTable.NAME);
+ MetaDataStateStore(DataLevel level, ClientContext context, CurrentState
state) {
+ this(level, context, state, MetadataTable.NAME);
+ }
+
+ @Override
+ public DataLevel getLevel() {
+ return level;
}
@Override
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/RootTabletStateStore.java
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/RootTabletStateStore.java
index 9acd1cc48a..98123e1c2f 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/RootTabletStateStore.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/RootTabletStateStore.java
@@ -21,12 +21,13 @@ package org.apache.accumulo.server.manager.state;
import org.apache.accumulo.core.clientImpl.ClientContext;
import org.apache.accumulo.core.metadata.RootTable;
import org.apache.accumulo.core.metadata.TabletLocationState;
+import org.apache.accumulo.core.metadata.schema.Ample.DataLevel;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
class RootTabletStateStore extends MetaDataStateStore {
- RootTabletStateStore(ClientContext context, CurrentState state) {
- super(context, state, RootTable.NAME);
+ RootTabletStateStore(DataLevel level, ClientContext context, CurrentState
state) {
+ super(level, context, state, RootTable.NAME);
}
@Override
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/TabletStateStore.java
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/TabletStateStore.java
index c96912ee58..05072fd1b0 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/TabletStateStore.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/TabletStateStore.java
@@ -36,6 +36,11 @@ import org.apache.hadoop.fs.Path;
*/
public interface TabletStateStore extends Iterable<TabletLocationState> {
+ /**
+ * Get the level for this state store
+ */
+ DataLevel getLevel();
+
/**
* Identifying name for this tablet state store.
*/
@@ -112,13 +117,13 @@ public interface TabletStateStore extends
Iterable<TabletLocationState> {
TabletStateStore tss;
switch (level) {
case ROOT:
- tss = new ZooTabletStateStore(context);
+ tss = new ZooTabletStateStore(level, context);
break;
case METADATA:
- tss = new RootTabletStateStore(context, state);
+ tss = new RootTabletStateStore(level, context, state);
break;
case USER:
- tss = new MetaDataStateStore(context, state);
+ tss = new MetaDataStateStore(level, context, state);
break;
default:
throw new IllegalArgumentException("Unknown level " + level);
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/ZooTabletStateStore.java
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/ZooTabletStateStore.java
index ae7643d32d..398e67c112 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/manager/state/ZooTabletStateStore.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/manager/state/ZooTabletStateStore.java
@@ -29,6 +29,7 @@ import org.apache.accumulo.core.metadata.RootTable;
import org.apache.accumulo.core.metadata.TServerInstance;
import org.apache.accumulo.core.metadata.TabletLocationState;
import org.apache.accumulo.core.metadata.schema.Ample;
+import org.apache.accumulo.core.metadata.schema.Ample.DataLevel;
import org.apache.accumulo.core.metadata.schema.Ample.ReadConsistency;
import org.apache.accumulo.core.metadata.schema.Ample.TabletMutator;
import org.apache.accumulo.core.metadata.schema.TabletMetadata;
@@ -45,12 +46,19 @@ class ZooTabletStateStore implements TabletStateStore {
private static final Logger log =
LoggerFactory.getLogger(ZooTabletStateStore.class);
private final Ample ample;
private final ClientContext context;
+ private final DataLevel level;
- ZooTabletStateStore(ClientContext context) {
+ ZooTabletStateStore(DataLevel level, ClientContext context) {
+ this.level = level;
this.context = context;
this.ample = context.getAmple();
}
+ @Override
+ public DataLevel getLevel() {
+ return level;
+ }
+
@Override
public ClosableIterator<TabletLocationState> iterator() {
diff --git
a/server/base/src/test/java/org/apache/accumulo/server/manager/state/RootTabletStateStoreTest.java
b/server/base/src/test/java/org/apache/accumulo/server/manager/state/RootTabletStateStoreTest.java
index a6fdda4a0b..68dd6c5f86 100644
---
a/server/base/src/test/java/org/apache/accumulo/server/manager/state/RootTabletStateStoreTest.java
+++
b/server/base/src/test/java/org/apache/accumulo/server/manager/state/RootTabletStateStoreTest.java
@@ -37,6 +37,7 @@ import org.apache.accumulo.core.metadata.TServerInstance;
import org.apache.accumulo.core.metadata.TabletLocationState;
import
org.apache.accumulo.core.metadata.TabletLocationState.BadLocationStateException;
import org.apache.accumulo.core.metadata.schema.Ample;
+import org.apache.accumulo.core.metadata.schema.Ample.DataLevel;
import org.apache.accumulo.core.metadata.schema.RootTabletMetadata;
import org.apache.accumulo.core.metadata.schema.TabletMetadata;
import org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType;
@@ -93,7 +94,7 @@ public class RootTabletStateStoreTest {
ServerContext context = MockServerContext.get();
expect(context.getAmple()).andReturn(new TestAmple()).anyTimes();
EasyMock.replay(context);
- ZooTabletStateStore tstore = new ZooTabletStateStore(context);
+ ZooTabletStateStore tstore = new ZooTabletStateStore(DataLevel.ROOT,
context);
KeyExtent root = RootTable.EXTENT;
String sessionId = "this is my unique session data";
TServerInstance server =
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/TabletGroupWatcher.java
b/server/manager/src/main/java/org/apache/accumulo/manager/TabletGroupWatcher.java
index 1b7f21d7b1..482bbd2f44 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/TabletGroupWatcher.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/TabletGroupWatcher.java
@@ -65,6 +65,7 @@ import org.apache.accumulo.core.metadata.TabletLocationState;
import
org.apache.accumulo.core.metadata.TabletLocationState.BadLocationStateException;
import org.apache.accumulo.core.metadata.TabletState;
import org.apache.accumulo.core.metadata.schema.Ample;
+import org.apache.accumulo.core.metadata.schema.Ample.DataLevel;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
import
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ChoppedColumnFamily;
import
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.CurrentLocationColumnFamily;
@@ -257,12 +258,47 @@ abstract class TabletGroupWatcher extends
AccumuloDaemonThread {
if (state == TabletState.ASSIGNED) {
goal = TabletGoalState.HOSTED;
}
+ if (Manager.log.isTraceEnabled()) {
+ Manager.log.trace(
+ "[{}] Shutting down all Tservers: {}, dependentCount: {}
Extent: {}, state: {}, goal: {}",
+ store.name(),
manager.serversToShutdown.equals(currentTServers.keySet()),
+ dependentWatcher == null ? "null" :
dependentWatcher.assignedOrHosted(), tls.extent,
+ state, goal);
+ }
// if we are shutting down all the tabletservers, we have to do it
in order
if ((goal == TabletGoalState.SUSPENDED && state ==
TabletState.HOSTED)
&& manager.serversToShutdown.equals(currentTServers.keySet())) {
- if (dependentWatcher != null &&
dependentWatcher.assignedOrHosted() > 0) {
- goal = TabletGoalState.HOSTED;
+ if (dependentWatcher != null) {
+ // If the dependentWatcher is for the user tables, check to see
+ // that user tables exist.
+ DataLevel dependentLevel = dependentWatcher.store.getLevel();
+ boolean userTablesExist = true;
+ switch (dependentLevel) {
+ case USER:
+ Set<TableId> onlineTables = manager.onlineTables();
+ onlineTables.remove(RootTable.ID);
+ onlineTables.remove(MetadataTable.ID);
+ userTablesExist = !onlineTables.isEmpty();
+ break;
+ case METADATA:
+ case ROOT:
+ default:
+ break;
+ }
+ // If the stats object in the dependentWatcher is empty, then it
+ // currently does not have data about what is hosted or not. In
+ // that case host these tablets until the dependent watcher can
+ // gather some data.
+ final Map<TableId,TableCounts> stats =
dependentWatcher.getStats();
+ if (dependentLevel == DataLevel.USER) {
+ if (userTablesExist
+ && (stats == null || stats.isEmpty() ||
assignedOrHosted(stats) > 0)) {
+ goal = TabletGoalState.HOSTED;
+ }
+ } else if (stats == null || stats.isEmpty() ||
assignedOrHosted(stats) > 0) {
+ goal = TabletGoalState.HOSTED;
+ }
}
}
@@ -308,6 +344,8 @@ abstract class TabletGroupWatcher extends
AccumuloDaemonThread {
TServerConnection client =
manager.tserverSet.getConnection(location.getServerInstance());
if (client != null) {
+ Manager.log.trace("[{}] Requesting TabletServer {} unload {}
{}", store.name(),
+ location.getServerInstance(), tls.extent,
goal.howUnload());
client.unloadTablet(manager.managerLock, tls.extent,
goal.howUnload(),
manager.getSteadyTime());
unloaded++;
@@ -327,6 +365,7 @@ abstract class TabletGroupWatcher extends
AccumuloDaemonThread {
// provide stats after flushing changes to avoid race conditions w/
delete table
stats.end(managerState);
+ Manager.log.trace("[{}] End stats collection: {}", store.name(),
stats);
// Report changes
for (TabletState state : TabletState.values()) {
@@ -507,8 +546,12 @@ abstract class TabletGroupWatcher extends
AccumuloDaemonThread {
}
private int assignedOrHosted() {
+ return assignedOrHosted(stats.getLast());
+ }
+
+ private int assignedOrHosted(Map<TableId,TableCounts> last) {
int result = 0;
- for (TableCounts counts : stats.getLast().values()) {
+ for (TableCounts counts : last.values()) {
result += counts.assigned() + counts.hosted();
}
return result;
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/state/TableCounts.java
b/server/manager/src/main/java/org/apache/accumulo/manager/state/TableCounts.java
index 5a9b98d471..0f1b4ca6fa 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/state/TableCounts.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/state/TableCounts.java
@@ -42,4 +42,13 @@ public class TableCounts {
public int suspended() {
return counts[TabletState.SUSPENDED.ordinal()];
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append("unassigned:
").append(unassigned()).append(", assigned: ")
+ .append(assigned()).append(", assignedToDeadServers:
").append(assignedToDeadServers())
+ .append(", hosted: ").append(hosted()).append(", suspended:
").append(suspended())
+ .toString();
+ }
+
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/state/TableStats.java
b/server/manager/src/main/java/org/apache/accumulo/manager/state/TableStats.java
index 89997e12b0..3e2380a57c 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/state/TableStats.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/state/TableStats.java
@@ -79,4 +79,10 @@ public class TableStats {
public synchronized long lastScanFinished() {
return endScan;
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append("last:
").append(last.toString()).toString();
+ }
+
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/ShutdownTServer.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/ShutdownTServer.java
index 0cf05ac0c0..7c50d8236f 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/ShutdownTServer.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/ShutdownTServer.java
@@ -70,6 +70,9 @@ public class ShutdownTServer extends ManagerRepo {
connection.halt(manager.getManagerLock());
log.info("tablet server asked to halt {}", server);
return 0;
+ } else {
+ log.info("tablet server {} still has tablets for tables: {}",
server,
+ (status.tableMap == null) ? "null" : status.tableMap.keySet());
}
} catch (TTransportException ex) {
// expected
diff --git
a/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
b/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
index 3fd3dfb5e7..74176696e2 100644
--- a/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
+++ b/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
@@ -401,6 +401,7 @@ public class TabletServer extends AbstractServer implements
TabletHostingServer
}
void requestStop() {
+ log.info("Stop requested.");
serverStopRequested = true;
}
diff --git
a/server/tserver/src/main/java/org/apache/accumulo/tserver/UnloadTabletHandler.java
b/server/tserver/src/main/java/org/apache/accumulo/tserver/UnloadTabletHandler.java
index 0b3c611a05..1eaeb5acf3 100644
---
a/server/tserver/src/main/java/org/apache/accumulo/tserver/UnloadTabletHandler.java
+++
b/server/tserver/src/main/java/org/apache/accumulo/tserver/UnloadTabletHandler.java
@@ -55,6 +55,7 @@ class UnloadTabletHandler implements Runnable {
public void run() {
Tablet t = null;
+ log.info("Tablet unload for extent {} requested.", extent);
synchronized (server.unopenedTablets) {
if (server.unopenedTablets.contains(extent)) {
diff --git
a/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
b/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
index 1b818d8759..8097c3660b 100644
---
a/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
+++
b/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/Tablet.java
@@ -894,6 +894,7 @@ public class Tablet extends TabletBase {
public void close(boolean saveState) throws IOException {
initiateClose(saveState);
completeClose(saveState, true);
+ log.info("Tablet {} closed.", this.extent);
}
void initiateClose(boolean saveState) {
diff --git
a/test/src/main/java/org/apache/accumulo/test/functional/ManagerAssignmentIT.java
b/test/src/main/java/org/apache/accumulo/test/functional/ManagerAssignmentIT.java
index c3fc72d242..04dc8490d2 100644
---
a/test/src/main/java/org/apache/accumulo/test/functional/ManagerAssignmentIT.java
+++
b/test/src/main/java/org/apache/accumulo/test/functional/ManagerAssignmentIT.java
@@ -21,22 +21,43 @@ package org.apache.accumulo.test.functional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
import java.time.Duration;
+import java.util.Collections;
+import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.Accumulo;
import org.apache.accumulo.core.client.AccumuloClient;
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchWriter;
+import org.apache.accumulo.core.client.IsolatedScanner;
+import org.apache.accumulo.core.client.admin.Locations;
import org.apache.accumulo.core.clientImpl.ClientContext;
+import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.TableId;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.fate.zookeeper.ServiceLock;
import org.apache.accumulo.core.metadata.MetadataTable;
+import org.apache.accumulo.core.metadata.RootTable;
import org.apache.accumulo.core.metadata.TabletLocationState;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection;
+import org.apache.accumulo.core.rpc.clients.ThriftClientTypes;
+import org.apache.accumulo.core.trace.TraceUtil;
+import org.apache.accumulo.core.util.HostAndPort;
import org.apache.accumulo.core.util.UtilWaitThread;
import org.apache.accumulo.harness.AccumuloClusterHarness;
+import org.apache.accumulo.minicluster.ServerType;
+import org.apache.accumulo.miniclusterImpl.MiniAccumuloClusterControl;
import org.apache.accumulo.server.manager.state.MetaDataTableScanner;
+import org.apache.accumulo.test.util.Wait;
import org.junit.jupiter.api.Test;
public class ManagerAssignmentIT extends AccumuloClusterHarness {
@@ -91,6 +112,151 @@ public class ManagerAssignmentIT extends
AccumuloClusterHarness {
}
}
+ @Test
+ public void testShutdownOnlyTServerWithUserTable() throws Exception {
+
+ // 2 TabletServers started for this test, shut them down so we only have 1.
+ getClusterControl().stopAllServers(ServerType.TABLET_SERVER);
+ ((MiniAccumuloClusterControl)
getClusterControl()).start(ServerType.TABLET_SERVER,
+ Collections.emptyMap(), 1);
+
+ String tableName = getUniqueNames(1)[0];
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+
+ Wait.waitFor(() -> client.instanceOperations().getTabletServers().size()
== 1);
+
+ client.tableOperations().create(tableName);
+
+ // wait for everything to be hosted and balanced
+ client.instanceOperations().waitForBalance();
+
+ try (var writer = client.createBatchWriter(tableName)) {
+ for (int i = 0; i < 1000000; i++) {
+ Mutation m = new Mutation(String.format("%08d", i));
+ m.put("", "", "");
+ writer.addMutation(m);
+ }
+ }
+ client.tableOperations().flush(tableName, null, null, true);
+
+ final CountDownLatch latch = new CountDownLatch(10);
+
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try (var scanner = new
IsolatedScanner(client.createScanner(tableName))) {
+ // TODO maybe do not close scanner? The following limit was
placed on the stream to
+ // avoid reading all the data possibly leaving a scan session
active on the tserver
+ int count = 0;
+ for (Entry<Key,Value> e : scanner) {
+ count++;
+ // let the test thread know that this thread has read some data
+ if (count == 1_000) {
+ latch.countDown();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ break;
+ }
+ }
+ }
+ };
+
+ ExecutorService service = Executors.newFixedThreadPool(10);
+ for (int i = 0; i < 10; i++) {
+ service.execute(task);
+ }
+
+ // Wait until all threads are reading some data
+ latch.await();
+
+ // getClusterControl().stopAllServers(ServerType.TABLET_SERVER)
+ // could potentially send a kill -9 to the process. Shut the tablet
+ // servers down in a more graceful way.
+
+ Locations locs = client.tableOperations().locate(tableName,
+ Collections.singletonList(TabletsSection.getRange()));
+ locs.groupByTablet().keySet().stream().map(tid ->
locs.getTabletLocation(tid))
+ .forEach(location -> {
+ HostAndPort address = HostAndPort.fromString(location);
+ String addressWithSession = address.toString();
+ var zLockPath =
ServiceLock.path(getCluster().getServerContext().getZooKeeperRoot()
+ + Constants.ZTSERVERS + "/" + address.toString());
+ long sessionId =
+
ServiceLock.getSessionId(getCluster().getServerContext().getZooCache(),
zLockPath);
+ if (sessionId != 0) {
+ addressWithSession = address.toString() + "[" +
Long.toHexString(sessionId) + "]";
+ }
+
+ final String finalAddress = addressWithSession;
+ System.out.println("Attempting to shutdown TabletServer at: " +
address.toString());
+ try {
+ ThriftClientTypes.MANAGER.executeVoid((ClientContext) client,
+ c -> c.shutdownTabletServer(TraceUtil.traceInfo(),
+ getCluster().getServerContext().rpcCreds(),
finalAddress, false));
+ } catch (AccumuloException | AccumuloSecurityException e) {
+ fail("Error shutting down TabletServer", e);
+ }
+
+ });
+
+ Wait.waitFor(() -> client.instanceOperations().getTabletServers().size()
== 0);
+
+ }
+ }
+
+ @Test
+ public void testShutdownOnlyTServerWithoutUserTable() throws Exception {
+
+ // 2 TabletServers started for this test, shut them down so we only have 1.
+ getClusterControl().stopAllServers(ServerType.TABLET_SERVER);
+ ((MiniAccumuloClusterControl)
getClusterControl()).start(ServerType.TABLET_SERVER,
+ Collections.emptyMap(), 1);
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+
+ Wait.waitFor(() -> client.instanceOperations().getTabletServers().size()
== 1);
+
+ client.instanceOperations().waitForBalance();
+
+ // getClusterControl().stopAllServers(ServerType.TABLET_SERVER)
+ // could potentially send a kill -9 to the process. Shut the tablet
+ // servers down in a more graceful way.
+
+ Locations locs = client.tableOperations().locate(RootTable.NAME,
+ Collections.singletonList(TabletsSection.getRange()));
+ locs.groupByTablet().keySet().stream().map(tid ->
locs.getTabletLocation(tid))
+ .forEach(location -> {
+ HostAndPort address = HostAndPort.fromString(location);
+ String addressWithSession = address.toString();
+ var zLockPath =
ServiceLock.path(getCluster().getServerContext().getZooKeeperRoot()
+ + Constants.ZTSERVERS + "/" + address.toString());
+ long sessionId =
+
ServiceLock.getSessionId(getCluster().getServerContext().getZooCache(),
zLockPath);
+ if (sessionId != 0) {
+ addressWithSession = address.toString() + "[" +
Long.toHexString(sessionId) + "]";
+ }
+
+ final String finalAddress = addressWithSession;
+ System.out.println("Attempting to shutdown TabletServer at: " +
address.toString());
+ try {
+ ThriftClientTypes.MANAGER.executeVoid((ClientContext) client,
+ c -> c.shutdownTabletServer(TraceUtil.traceInfo(),
+ getCluster().getServerContext().rpcCreds(),
finalAddress, false));
+ } catch (AccumuloException | AccumuloSecurityException e) {
+ fail("Error shutting down TabletServer", e);
+ }
+
+ });
+
+ Wait.waitFor(() -> client.instanceOperations().getTabletServers().size()
== 0);
+
+ }
+ }
+
private TabletLocationState getTabletLocationState(AccumuloClient c, String
tableId) {
try (MetaDataTableScanner s = new MetaDataTableScanner((ClientContext) c,
new Range(TabletsSection.encodeRow(TableId.of(tableId), null)),
MetadataTable.NAME)) {
diff --git a/test/src/main/resources/log4j2-test.properties
b/test/src/main/resources/log4j2-test.properties
index cf09c1b90a..f3a23b7474 100644
--- a/test/src/main/resources/log4j2-test.properties
+++ b/test/src/main/resources/log4j2-test.properties
@@ -149,6 +149,9 @@ logger.37.level = warn
logger.38.name = org.apache.hadoop.fs.TrashPolicyDefault
logger.38.level = debug
+logger.39.name = org.apache.accumulo.manager.Manager
+logger.39.level = trace
+
property.metricsFilename = ./target/test-metrics
# appender.metrics.type = Console