This is an automated email from the ASF dual-hosted git repository.
kturner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/main by this push:
new a0d68172dc Moves set of shutting down tservers from manager memory to
ZK (#6224)
a0d68172dc is described below
commit a0d68172dc00268e67a91166761e8407c1c1a43f
Author: Keith Turner <[email protected]>
AuthorDate: Thu Apr 2 14:57:14 2026 -0700
Moves set of shutting down tservers from manager memory to ZK (#6224)
The set of shutting down tservers was causing system fate operations to
have to run on the primary manager because this was an in memory set.
This caused fate to have different code paths to user vs system fate,
this in turn caused problems when trying to distribute compaction
coordination.
To fix this problem moved the set from an in memory set to a set in
zookeeper. The set is managed by fate operations which simplifies the
existing code. Only fate operations add and remove from the set and fate
keys are used to ensure only one fate operation runs at a time for a
tserver instance. The previous in memory set had a lot of code to try to
keep
it in sync with reality, that is all gone now. There were many bugs with
this code in the past.
After this change is made fate can be simplified in a follow on commit
to remove all specialization for the primary manager. Also the monitor
can now directly access this set instead of making an RPC to the
manager, will open a follow on issue for this.
---
.../java/org/apache/accumulo/core/Constants.java | 2 +
.../org/apache/accumulo/core/fate/FateKey.java | 43 ++++++++-
.../accumulo/server/init/ZooKeeperInitializer.java | 2 +
.../java/org/apache/accumulo/manager/Manager.java | 76 +++++++---------
.../manager/ManagerClientServiceHandler.java | 39 ++++----
.../manager/tserverOps/BeginTserverShutdown.java | 80 +++++++++++++++++
.../manager/tserverOps/ShutdownTServer.java | 75 +++++++++-------
.../accumulo/manager/upgrade/Upgrader11to12.java | 11 +++
.../manager/tableOps/ShutdownTServerTest.java | 100 ---------------------
9 files changed, 228 insertions(+), 200 deletions(-)
diff --git a/core/src/main/java/org/apache/accumulo/core/Constants.java
b/core/src/main/java/org/apache/accumulo/core/Constants.java
index eb8ba1059e..d2c556f2c2 100644
--- a/core/src/main/java/org/apache/accumulo/core/Constants.java
+++ b/core/src/main/java/org/apache/accumulo/core/Constants.java
@@ -74,6 +74,8 @@ public class Constants {
public static final String ZDEAD = "/dead";
public static final String ZDEADTSERVERS = ZDEAD + "/tservers";
+ public static final String ZSHUTTING_DOWN_TSERVERS =
"/shutting-down-tservers";
+
public static final String ZFATE = "/fate";
public static final String ZNEXT_FILE = "/next_file";
diff --git a/core/src/main/java/org/apache/accumulo/core/fate/FateKey.java
b/core/src/main/java/org/apache/accumulo/core/fate/FateKey.java
index 650dca01af..19641c000a 100644
--- a/core/src/main/java/org/apache/accumulo/core/fate/FateKey.java
+++ b/core/src/main/java/org/apache/accumulo/core/fate/FateKey.java
@@ -27,6 +27,7 @@ import java.util.Objects;
import java.util.Optional;
import org.apache.accumulo.core.dataImpl.KeyExtent;
+import org.apache.accumulo.core.metadata.TServerInstance;
import org.apache.accumulo.core.metadata.schema.ExternalCompactionId;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -41,12 +42,14 @@ public class FateKey {
private final FateKeyType type;
private final Optional<KeyExtent> keyExtent;
private final Optional<ExternalCompactionId> compactionId;
+ private final Optional<TServerInstance> tServerInstance;
private final byte[] serialized;
private FateKey(FateKeyType type, KeyExtent keyExtent) {
this.type = Objects.requireNonNull(type);
this.keyExtent = Optional.of(keyExtent);
this.compactionId = Optional.empty();
+ this.tServerInstance = Optional.empty();
this.serialized = serialize(type, keyExtent);
}
@@ -54,15 +57,25 @@ public class FateKey {
this.type = Objects.requireNonNull(type);
this.keyExtent = Optional.empty();
this.compactionId = Optional.of(compactionId);
+ this.tServerInstance = Optional.empty();
this.serialized = serialize(type, compactionId);
}
+ private FateKey(FateKeyType type, TServerInstance tServerInstance) {
+ this.type = Objects.requireNonNull(type);
+ this.keyExtent = Optional.empty();
+ this.compactionId = Optional.empty();
+ this.tServerInstance = Optional.of(tServerInstance);
+ this.serialized = serialize(type, tServerInstance);
+ }
+
private FateKey(byte[] serialized) {
try (DataInputBuffer buffer = new DataInputBuffer()) {
buffer.reset(serialized, serialized.length);
this.type = FateKeyType.valueOf(buffer.readUTF());
this.keyExtent = deserializeKeyExtent(type, buffer);
this.compactionId = deserializeCompactionId(type, buffer);
+ this.tServerInstance = deserializeTserverId(type, buffer);
this.serialized = serialized;
} catch (IOException e) {
throw new UncheckedIOException(e);
@@ -127,8 +140,12 @@ public class FateKey {
return new FateKey(FateKeyType.MERGE, extent);
}
+ public static FateKey forShutdown(TServerInstance tServerInstance) {
+ return new FateKey(FateKeyType.TSERVER_SHUTDOWN, tServerInstance);
+ }
+
public enum FateKeyType {
- SPLIT, COMPACTION_COMMIT, MERGE
+ SPLIT, COMPACTION_COMMIT, MERGE, TSERVER_SHUTDOWN
}
private static byte[] serialize(FateKeyType type, KeyExtent ke) {
@@ -155,22 +172,42 @@ public class FateKey {
}
}
+ private static byte[] serialize(FateKeyType type, TServerInstance
tServerInstance) {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos)) {
+ dos.writeUTF(type.toString());
+ dos.writeUTF(tServerInstance.getHostPortSession());
+ dos.close();
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private static Optional<KeyExtent> deserializeKeyExtent(FateKeyType type,
DataInputBuffer buffer)
throws IOException {
return switch (type) {
case SPLIT, MERGE -> Optional.of(KeyExtent.readFrom(buffer));
- case COMPACTION_COMMIT -> Optional.empty();
+ case COMPACTION_COMMIT, TSERVER_SHUTDOWN -> Optional.empty();
};
}
private static Optional<ExternalCompactionId>
deserializeCompactionId(FateKeyType type,
DataInputBuffer buffer) throws IOException {
return switch (type) {
- case SPLIT, MERGE -> Optional.empty();
+ case SPLIT, MERGE, TSERVER_SHUTDOWN -> Optional.empty();
case COMPACTION_COMMIT ->
Optional.of(ExternalCompactionId.of(buffer.readUTF()));
};
}
+ private static Optional<TServerInstance> deserializeTserverId(FateKeyType
type,
+ DataInputBuffer buffer) throws IOException {
+ return switch (type) {
+ case SPLIT, MERGE, COMPACTION_COMMIT -> Optional.empty();
+ case TSERVER_SHUTDOWN -> Optional.of(new
TServerInstance(buffer.readUTF()));
+ };
+ }
+
@Override
public String toString() {
var buf = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/init/ZooKeeperInitializer.java
b/server/base/src/main/java/org/apache/accumulo/server/init/ZooKeeperInitializer.java
index 464a6dae5a..b23c674680 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/init/ZooKeeperInitializer.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/init/ZooKeeperInitializer.java
@@ -178,6 +178,8 @@ public class ZooKeeperInitializer {
ZooUtil.NodeExistsPolicy.FAIL);
zrwChroot.putPersistentData(Constants.ZMANAGER_ASSISTANT_LOCK,
EMPTY_BYTE_ARRAY,
ZooUtil.NodeExistsPolicy.FAIL);
+ zrwChroot.putPersistentData(Constants.ZSHUTTING_DOWN_TSERVERS,
EMPTY_BYTE_ARRAY,
+ ZooUtil.NodeExistsPolicy.FAIL);
}
/**
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/Manager.java
b/server/manager/src/main/java/org/apache/accumulo/manager/Manager.java
index 60e09f3dd8..b1edc70857 100644
--- a/server/manager/src/main/java/org/apache/accumulo/manager/Manager.java
+++ b/server/manager/src/main/java/org/apache/accumulo/manager/Manager.java
@@ -58,6 +58,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.cli.ServerOpts;
@@ -205,7 +206,6 @@ public class Manager extends AbstractServer
private final List<TabletGroupWatcher> watchers = new ArrayList<>();
final Map<TServerInstance,AtomicInteger> badServers =
Collections.synchronizedMap(new HashMap<>());
- final Set<TServerInstance> serversToShutdown =
Collections.synchronizedSet(new HashSet<>());
final EventCoordinator nextEvent = new EventCoordinator();
RecoveryManager recoveryManager = null;
private final ManagerTime timeKeeper;
@@ -556,10 +556,6 @@ public class Manager extends AbstractServer
aconf.getTimeInMillis(Property.MANAGER_RECOVERY_WAL_EXISTENCE_CACHE_TIME);
}
- public TServerConnection getConnection(TServerInstance server) {
- return tserverSet.getConnection(server);
- }
-
void setManagerGoalState(ManagerGoalState state) {
try {
getContext().getZooSession().asReaderWriter().putPersistentData(Constants.ZMANAGER_GOAL_STATE,
@@ -661,6 +657,7 @@ public class Manager extends AbstractServer
}
boolean canBalance(DataLevel dataLevel, TServerStatus tServerStatus) {
+ Set<TServerInstance> serversToShutdown;
if (!badServers.isEmpty()) {
log.debug("not balancing {} because the balance information is
out-of-date {}", dataLevel,
badServers.keySet());
@@ -668,7 +665,7 @@ public class Manager extends AbstractServer
} else if (getManagerGoalState() == ManagerGoalState.CLEAN_STOP) {
log.debug("not balancing {} because the manager is attempting to stop
cleanly", dataLevel);
return false;
- } else if (!serversToShutdown.isEmpty()) {
+ } else if (!(serversToShutdown = shutdownServers()).isEmpty()) {
log.debug("not balancing {} while shutting down servers {}", dataLevel,
serversToShutdown);
return false;
} else {
@@ -760,7 +757,6 @@ public class Manager extends AbstractServer
log.debug("stopping {} tablet servers",
currentServers.size());
for (TServerInstance server : currentServers) {
try {
- serversToShutdown.add(server);
tserverSet.getConnection(server).fastHalt(primaryManagerLock);
} catch (TException e) {
// its probably down, and we don't care
@@ -1585,38 +1581,35 @@ public class Manager extends AbstractServer
obit.delete(up.getHostPort());
}
}
- for (TServerInstance dead : deleted) {
- String cause = "unexpected failure";
- if (serversToShutdown.contains(dead)) {
- cause = "clean shutdown"; // maybe an incorrect assumption
+
+ if (!deleted.isEmpty()) {
+ // This set is read from zookeeper, so only get it if its actually
needed
+ var serversToShutdown = shutdownServers();
+ for (TServerInstance dead : deleted) {
+ String cause = "unexpected failure";
+ if (serversToShutdown.contains(dead)) {
+ cause = "clean shutdown"; // maybe an incorrect assumption
+ }
+ if (!getManagerGoalState().equals(ManagerGoalState.CLEAN_STOP)) {
+ obit.post(dead.getHostPort(), cause);
+ }
}
- if (!getManagerGoalState().equals(ManagerGoalState.CLEAN_STOP)) {
- obit.post(dead.getHostPort(), cause);
+
+ Set<TServerInstance> unexpected = new HashSet<>(deleted);
+ unexpected.removeAll(serversToShutdown);
+ if (!unexpected.isEmpty()
+ && (stillManager() &&
!getManagerGoalState().equals(ManagerGoalState.CLEAN_STOP))) {
+ log.warn("Lost servers {}", unexpected);
}
+ badServers.keySet().removeAll(deleted);
}
- Set<TServerInstance> unexpected = new HashSet<>(deleted);
- unexpected.removeAll(this.serversToShutdown);
- if (!unexpected.isEmpty()
- && (stillManager() &&
!getManagerGoalState().equals(ManagerGoalState.CLEAN_STOP))) {
- log.warn("Lost servers {}", unexpected);
- }
- serversToShutdown.removeAll(deleted);
- badServers.keySet().removeAll(deleted);
// clear out any bad server with the same host/port as a new server
synchronized (badServers) {
cleanListByHostAndPort(badServers.keySet(), deleted, added);
}
- synchronized (serversToShutdown) {
- cleanListByHostAndPort(serversToShutdown, deleted, added);
- }
nextEvent.event("There are now %d tablet servers", current.size());
}
-
- // clear out any servers that are no longer current
- // this is needed when we are using a fate operation to shutdown a tserver
as it
- // will continue to add the server to the serversToShutdown (ACCUMULO-4410)
- serversToShutdown.retainAll(current.getCurrentServers());
}
static void cleanListByHostAndPort(Collection<TServerInstance> badServers,
@@ -1663,15 +1656,6 @@ public class Manager extends AbstractServer
return tserverSet.getSnapshot();
}
- // recovers state from the persistent transaction to shutdown a server
- public boolean shutdownTServer(TServerInstance server) {
- if (serversToShutdown.add(server)) {
- nextEvent.event("Tablet Server shutdown requested for %s", server);
- return true;
- }
- return false;
- }
-
public EventCoordinator getEventCoordinator() {
return nextEvent;
}
@@ -1719,12 +1703,8 @@ public class Manager extends AbstractServer
result.state = getManagerState();
result.goalState = getManagerGoalState();
result.unassignedTablets = displayUnassigned();
- result.serversShuttingDown = new HashSet<>();
- synchronized (serversToShutdown) {
- for (TServerInstance server : serversToShutdown) {
- result.serversShuttingDown.add(server.getHostPort());
- }
- }
+ result.serversShuttingDown =
+
shutdownServers().stream().map(TServerInstance::getHostPort).collect(Collectors.toSet());
DeadServerList obit = new DeadServerList(getContext());
result.deadTabletServers = obit.getList();
return result;
@@ -1738,8 +1718,12 @@ public class Manager extends AbstractServer
}
public Set<TServerInstance> shutdownServers() {
- synchronized (serversToShutdown) {
- return Set.copyOf(serversToShutdown);
+ try {
+ List<String> children =
+
getContext().getZooSession().asReader().getChildren(Constants.ZSHUTTING_DOWN_TSERVERS);
+ return
children.stream().map(TServerInstance::new).collect(Collectors.toSet());
+ } catch (KeeperException | InterruptedException e) {
+ throw new RuntimeException(e);
}
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/ManagerClientServiceHandler.java
b/server/manager/src/main/java/org/apache/accumulo/manager/ManagerClientServiceHandler.java
index f96332dc9d..d88a94516d 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/ManagerClientServiceHandler.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/ManagerClientServiceHandler.java
@@ -33,7 +33,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.apache.accumulo.core.Constants;
@@ -62,6 +64,7 @@ import org.apache.accumulo.core.fate.Fate;
import org.apache.accumulo.core.fate.FateClient;
import org.apache.accumulo.core.fate.FateId;
import org.apache.accumulo.core.fate.FateInstanceType;
+import org.apache.accumulo.core.fate.FateKey;
import org.apache.accumulo.core.fate.TraceRepo;
import org.apache.accumulo.core.fate.zookeeper.ZooReaderWriter;
import org.apache.accumulo.core.manager.state.tables.TableState;
@@ -85,7 +88,7 @@ import
org.apache.accumulo.core.securityImpl.thrift.TDelegationToken;
import org.apache.accumulo.core.securityImpl.thrift.TDelegationTokenConfig;
import org.apache.accumulo.core.util.ByteBufferUtil;
import org.apache.accumulo.manager.tableOps.FateEnv;
-import org.apache.accumulo.manager.tserverOps.ShutdownTServer;
+import org.apache.accumulo.manager.tserverOps.BeginTserverShutdown;
import org.apache.accumulo.server.ServerContext;
import org.apache.accumulo.server.client.ClientServiceHandler;
import org.apache.accumulo.server.conf.store.NamespacePropKey;
@@ -336,16 +339,16 @@ public class ManagerClientServiceHandler implements
ManagerClientService.Iface {
}
FateClient<FateEnv> fate = manager.fateClient(FateInstanceType.META);
- FateId fateId = fate.startTransaction();
- String msg = "Shutdown tserver " + tabletServer;
+ var repo = new TraceRepo<>(
+ new BeginTserverShutdown(doomed,
manager.tserverSet.getResourceGroup(doomed), force));
- fate.seedTransaction(Fate.FateOperation.SHUTDOWN_TSERVER, fateId,
- new TraceRepo<>(
- new ShutdownTServer(doomed,
manager.tserverSet.getResourceGroup(doomed), force)),
- false, msg);
- fate.waitForCompletion(fateId);
- fate.delete(fateId);
+ CompletableFuture<Optional<FateId>> future;
+ try (var seeder = fate.beginSeeding()) {
+ future =
seeder.attemptToSeedTransaction(Fate.FateOperation.SHUTDOWN_TSERVER,
+ FateKey.forShutdown(doomed), repo, true);
+ }
+ future.join().ifPresent(fate::waitForCompletion);
log.debug("FATE op shutting down " + tabletServer + " finished");
}
@@ -360,17 +363,15 @@ public class ManagerClientServiceHandler implements
ManagerClientService.Iface {
}
log.info("Tablet Server {} has reported it's shutting down", tabletServer);
var tserver = new TServerInstance(tabletServer);
- if (manager.shutdownTServer(tserver)) {
- // If there is an exception seeding the fate tx this should cause the
RPC to fail which should
- // cause the tserver to halt. Because of that not making an attempt to
handle failure here.
- FateClient<FateEnv> fate = manager.fateClient(FateInstanceType.META);
- var tid = fate.startTransaction();
- String msg = "Shutdown tserver " + tabletServer;
+ // If there is an exception seeding the fate tx this should cause the RPC
to fail which should
+ // cause the tserver to halt. Because of that not making an attempt to
handle failure here.
+ FateClient<FateEnv> fate = manager.fateClient(FateInstanceType.META);
- fate.seedTransaction(Fate.FateOperation.SHUTDOWN_TSERVER, tid,
- new TraceRepo<>(new ShutdownTServer(tserver,
ResourceGroupId.of(resourceGroup), false)),
- true, msg);
- }
+ var repo = new TraceRepo<>(
+ new BeginTserverShutdown(tserver, ResourceGroupId.of(resourceGroup),
false));
+ // only seed a new transaction if nothing is running for this tserver
+ fate.seedTransaction(Fate.FateOperation.SHUTDOWN_TSERVER,
FateKey.forShutdown(tserver), repo,
+ true);
}
@Override
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/BeginTserverShutdown.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/BeginTserverShutdown.java
new file mode 100644
index 0000000000..358595ebca
--- /dev/null
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tserverOps/BeginTserverShutdown.java
@@ -0,0 +1,80 @@
+/*
+ * 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
+ *
+ * https://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.accumulo.manager.tserverOps;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.data.ResourceGroupId;
+import org.apache.accumulo.core.fate.FateId;
+import org.apache.accumulo.core.fate.Repo;
+import org.apache.accumulo.core.fate.zookeeper.ZooUtil;
+import org.apache.accumulo.core.metadata.TServerInstance;
+import org.apache.accumulo.manager.tableOps.AbstractFateOperation;
+import org.apache.accumulo.manager.tableOps.FateEnv;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.net.HostAndPort;
+
+public class BeginTserverShutdown extends AbstractFateOperation {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger log =
LoggerFactory.getLogger(BeginTserverShutdown.class);
+
+ private final ResourceGroupId resourceGroup;
+ private final HostAndPort hostAndPort;
+ private final String serverSession;
+ private final boolean force;
+
+ public BeginTserverShutdown(TServerInstance server, ResourceGroupId
resourceGroup,
+ boolean force) {
+ this.hostAndPort = server.getHostAndPort();
+ this.resourceGroup = resourceGroup;
+ this.serverSession = server.getSession();
+ this.force = force;
+ }
+
+ static String createPath(HostAndPort hostPort, String session) {
+ return Constants.ZSHUTTING_DOWN_TSERVERS + "/"
+ + new TServerInstance(hostPort, session).getHostPortSession();
+ }
+
+ @Override
+ public Repo<FateEnv> call(FateId fateId, FateEnv environment) throws
Exception {
+ if (!force) {
+ String path = createPath(hostAndPort, serverSession);
+ // Because these fate operation are seeded with a fate key there should
only ever be one fate
+ // operation running for a tserver instance, so do not need to worry
about race conditions
+ // here
+
environment.getContext().getZooSession().asReaderWriter().putPersistentData(path,
new byte[0],
+ ZooUtil.NodeExistsPolicy.SKIP);
+ log.trace("{} created {}", fateId, path);
+ }
+ return new ShutdownTServer(hostAndPort, serverSession, resourceGroup,
force);
+ }
+
+ @Override
+ public void undo(FateId fateId, FateEnv environment) throws Exception {
+ if (!force) {
+ String path = createPath(hostAndPort, serverSession);
+ environment.getContext().getZooSession().asReaderWriter().delete(path);
+ log.trace("{} removed {}", fateId, path);
+ }
+ }
+}
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 147b8206cc..eb456a8a3c 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
@@ -19,6 +19,7 @@
package org.apache.accumulo.manager.tserverOps;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static
org.apache.accumulo.manager.tserverOps.BeginTserverShutdown.createPath;
import org.apache.accumulo.core.data.ResourceGroupId;
import org.apache.accumulo.core.fate.FateId;
@@ -28,10 +29,12 @@ import
org.apache.accumulo.core.fate.zookeeper.ZooUtil.NodeExistsPolicy;
import org.apache.accumulo.core.lock.ServiceLock;
import org.apache.accumulo.core.manager.thrift.TabletServerStatus;
import org.apache.accumulo.core.metadata.TServerInstance;
-import org.apache.accumulo.manager.Manager;
+import org.apache.accumulo.core.rpc.ThriftUtil;
+import org.apache.accumulo.core.rpc.clients.ThriftClientTypes;
+import org.apache.accumulo.core.tabletserver.thrift.TabletServerClientService;
+import org.apache.accumulo.core.trace.TraceUtil;
import org.apache.accumulo.manager.tableOps.AbstractFateOperation;
import org.apache.accumulo.manager.tableOps.FateEnv;
-import org.apache.accumulo.server.manager.LiveTServerSet.TServerConnection;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,10 +50,11 @@ public class ShutdownTServer extends AbstractFateOperation {
private final String serverSession;
private final boolean force;
- public ShutdownTServer(TServerInstance server, ResourceGroupId
resourceGroup, boolean force) {
- this.hostAndPort = server.getHostAndPort();
+ public ShutdownTServer(HostAndPort hostAndPort, String serverSession,
+ ResourceGroupId resourceGroup, boolean force) {
+ this.hostAndPort = hostAndPort;
this.resourceGroup = resourceGroup;
- this.serverSession = server.getSession();
+ this.serverSession = serverSession;
this.force = force;
}
@@ -62,35 +66,38 @@ public class ShutdownTServer extends AbstractFateOperation {
return 0;
}
- // Inform the manager that we want this server to shutdown
- Manager manager = (Manager) env;
- manager.shutdownTServer(server);
-
- if (manager.onlineTabletServers().contains(server)) {
- TServerConnection connection = manager.getConnection(server);
- if (connection != null) {
- try {
- TabletServerStatus status = connection.getTableMap(false);
- if (status.tableMap != null && status.tableMap.isEmpty()) {
- log.info("tablet server hosts no tablets {}", server);
- connection.halt(manager.getServiceLock());
- 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
- } catch (Exception ex) {
- log.error("Error talking to tablet server {}: ", server, ex);
- }
+ if (env.onlineTabletServers().contains(server)) {
+
+ TabletServerClientService.Client client = null;
- // If the connection was non-null and we could communicate with it
- // give the manager some more time to tell it to stop and for the
- // tserver to ack the request and stop itself.
- return 1000;
+ try {
+ client =
+ ThriftUtil.getClient(ThriftClientTypes.TABLET_SERVER, hostAndPort,
env.getContext());
+ TabletServerStatus status =
+ client.getTabletServerStatus(TraceUtil.traceInfo(),
env.getContext().rpcCreds());
+ if (status.tableMap != null && status.tableMap.isEmpty()) {
+ log.info("tablet server hosts no tablets {}", server);
+ client.halt(TraceUtil.traceInfo(), env.getContext().rpcCreds(),
+ env.getServiceLock().getLockID().serialize());
+ 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
+ } catch (Exception ex) {
+ log.error("Error talking to tablet server {}: ", server, ex);
+ } finally {
+ ThriftUtil.returnClient(client, env.getContext());
}
+
+ // If the connection was non-null and we could communicate with it
+ // give the manager some more time to tell it to stop and for the
+ // tserver to ack the request and stop itself.
+ return 1000;
+
}
return 0;
@@ -108,6 +115,10 @@ public class ShutdownTServer extends AbstractFateOperation
{
env.getContext().getServerPaths().createDeadTabletServerPath(resourceGroup,
hostAndPort);
zoo.putPersistentData(path.toString(), "forced down".getBytes(UTF_8),
NodeExistsPolicy.OVERWRITE);
+ } else {
+ String path = createPath(hostAndPort, serverSession);
+ env.getContext().getZooSession().asReaderWriter().delete(path);
+ log.trace("{} removed {}", fateId, path);
}
return null;
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader11to12.java
b/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader11to12.java
index a230dc7149..f88a9d59c4 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader11to12.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader11to12.java
@@ -255,6 +255,8 @@ public class Upgrader11to12 implements Upgrader {
moveTableProperties(context);
LOG.info("Add assistant manager node");
addAssistantManager(context);
+ LOG.info("Adding shutting down tservers node");
+ addShuttingDownTservers(context);
}
@Override
@@ -308,6 +310,15 @@ public class Upgrader11to12 implements Upgrader {
}
}
+ private static void addShuttingDownTservers(ServerContext context) {
+ try {
+
context.getZooSession().asReaderWriter().putPersistentData(Constants.ZSHUTTING_DOWN_TSERVERS,
+ new byte[0], ZooUtil.NodeExistsPolicy.SKIP);
+ } catch (KeeperException | InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
private static void addCompactionsNode(ServerContext context) {
try {
context.getZooSession().asReaderWriter().putPersistentData(Constants.ZCOMPACTIONS,
diff --git
a/server/manager/src/test/java/org/apache/accumulo/manager/tableOps/ShutdownTServerTest.java
b/server/manager/src/test/java/org/apache/accumulo/manager/tableOps/ShutdownTServerTest.java
deleted file mode 100644
index 8dbe53bf12..0000000000
---
a/server/manager/src/test/java/org/apache/accumulo/manager/tableOps/ShutdownTServerTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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
- *
- * https://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.accumulo.manager.tableOps;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.UUID;
-
-import org.apache.accumulo.core.data.ResourceGroupId;
-import org.apache.accumulo.core.fate.FateId;
-import org.apache.accumulo.core.fate.FateInstanceType;
-import org.apache.accumulo.core.manager.thrift.TableInfo;
-import org.apache.accumulo.core.manager.thrift.TabletServerStatus;
-import org.apache.accumulo.core.metadata.TServerInstance;
-import org.apache.accumulo.manager.Manager;
-import org.apache.accumulo.manager.tserverOps.ShutdownTServer;
-import org.apache.accumulo.server.manager.LiveTServerSet.TServerConnection;
-import org.easymock.EasyMock;
-import org.junit.jupiter.api.Test;
-
-import com.google.common.net.HostAndPort;
-
-public class ShutdownTServerTest {
-
- @Test
- public void testSingleShutdown() throws Exception {
- HostAndPort hap = HostAndPort.fromParts("localhost", 1234);
- final TServerInstance tserver = new TServerInstance(hap, "fake");
- final boolean force = false;
-
- final ShutdownTServer op = new ShutdownTServer(tserver,
ResourceGroupId.DEFAULT, force);
-
- final Manager manager = EasyMock.createMock(Manager.class);
- final FateId fateId = FateId.from(FateInstanceType.USER,
UUID.randomUUID());
-
- final TServerConnection tserverCnxn =
EasyMock.createMock(TServerConnection.class);
- final TabletServerStatus status = new TabletServerStatus();
- status.tableMap = new HashMap<>();
- // Put in a table info record, don't care what
- status.tableMap.put("a_table", new TableInfo());
-
- EasyMock.expect(manager.shutdownTServer(tserver)).andReturn(true).once();
-
EasyMock.expect(manager.onlineTabletServers()).andReturn(Collections.singleton(tserver));
- EasyMock.expect(manager.getConnection(tserver)).andReturn(tserverCnxn);
- EasyMock.expect(tserverCnxn.getTableMap(false)).andReturn(status);
-
- EasyMock.replay(tserverCnxn, manager);
-
- // FATE op is not ready
- long wait = op.isReady(fateId, manager);
- assertTrue(wait > 0, "Expected wait to be greater than 0");
-
- EasyMock.verify(tserverCnxn, manager);
-
- // Reset the mocks
- EasyMock.reset(tserverCnxn, manager);
-
- // reset the table map to the empty set to simulate all tablets unloaded
- status.tableMap = new HashMap<>();
- EasyMock.expect(manager.shutdownTServer(tserver)).andReturn(false).once();
-
EasyMock.expect(manager.onlineTabletServers()).andReturn(Collections.singleton(tserver));
- EasyMock.expect(manager.getConnection(tserver)).andReturn(tserverCnxn);
- EasyMock.expect(tserverCnxn.getTableMap(false)).andReturn(status);
- EasyMock.expect(manager.getServiceLock()).andReturn(null);
- tserverCnxn.halt(null);
- EasyMock.expectLastCall().once();
-
- EasyMock.replay(tserverCnxn, manager);
-
- // FATE op is not ready
- wait = op.isReady(fateId, manager);
- assertEquals(0, wait, "Expected wait to be 0");
-
- var op2 = op.call(fateId, manager);
- assertNull(op2, "Expected no follow on step");
-
- EasyMock.verify(tserverCnxn, manager);
- }
-
-}