This is an automated email from the ASF dual-hosted git repository.
ddanielr 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 3ce9fead76 Fixes the shell fate delete command (#6051)
3ce9fead76 is described below
commit 3ce9fead761efdb4ee9f6208f456428d70451b5d
Author: Daniel Roberts <[email protected]>
AuthorDate: Tue Feb 10 16:31:50 2026 -0500
Fixes the shell fate delete command (#6051)
* Fixes the shell fate delete command
Changes the delete action of the `shell fate --delete` to actually
remove the fate transaction id from table_locks instead of looking for
the zlock under the manager lock.
This matches the same delete behavior as the `admin fate --delete` command
* fix-formatting
* Added test for fate table lock deletes
Added a test for deleting table locks from a hung fate transaction
---
.../org/apache/accumulo/core/fate/AdminUtil.java | 10 ++++-
.../org/apache/accumulo/core/fate/FateTxId.java | 13 ++++++
.../accumulo/shell/commands/FateCommand.java | 9 ++--
.../accumulo/shell/commands/FateCommandTest.java | 3 +-
.../accumulo/test/fate/zookeeper/FateIT.java | 48 ++++++++++++++++++++++
5 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/core/src/main/java/org/apache/accumulo/core/fate/AdminUtil.java
b/core/src/main/java/org/apache/accumulo/core/fate/AdminUtil.java
index 3ea322ef78..c1acd45fe2 100644
--- a/core/src/main/java/org/apache/accumulo/core/fate/AdminUtil.java
+++ b/core/src/main/java/org/apache/accumulo/core/fate/AdminUtil.java
@@ -519,7 +519,13 @@ public class AdminUtil<T> {
return state;
}
- public void deleteLocks(ZooReaderWriter zk, ServiceLock.ServiceLockPath
path, String txidStr)
+ /**
+ * Removes locks at specific serviceLockPaths
+ * <p>
+ * When removing fate table locks, the txIdStr value must be the fate txId
in hex format. See
+ * {@link FateTxId#toHexString(String)}
+ */
+ public void deleteLocks(ZooReaderWriter zk, ServiceLock.ServiceLockPath
path, String txIdStr)
throws KeeperException, InterruptedException {
// delete any locks assoc w/ fate operation
List<String> lockedIds = zk.getChildren(path.toString());
@@ -530,7 +536,7 @@ public class AdminUtil<T> {
String lockPath = path + "/" + id + "/" + node;
byte[] data = zk.getData(path + "/" + id + "/" + node);
String[] lda = new String(data, UTF_8).split(":");
- if (lda[1].equals(txidStr)) {
+ if (lda[1].equals(txIdStr)) {
zk.recursiveDelete(lockPath, NodeMissingPolicy.SKIP);
}
}
diff --git a/core/src/main/java/org/apache/accumulo/core/fate/FateTxId.java
b/core/src/main/java/org/apache/accumulo/core/fate/FateTxId.java
index 8468eda355..6c646baac9 100644
--- a/core/src/main/java/org/apache/accumulo/core/fate/FateTxId.java
+++ b/core/src/main/java/org/apache/accumulo/core/fate/FateTxId.java
@@ -22,6 +22,7 @@ import java.util.regex.Pattern;
import org.apache.accumulo.core.util.FastFormat;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
public class FateTxId {
@@ -51,6 +52,18 @@ public class FateTxId {
return Long.parseLong(getHex(fmtTid), 16);
}
+ /**
+ * Returns the hex value of the FateTxId from a formatted fate transaction
+ *
+ * @param fmtTid formatted fate transaction
+ * @return hex value of long txId
+ */
+ @VisibleForTesting
+ public static String toHexString(String fmtTid) {
+ Preconditions.checkArgument(isFormatedTid(fmtTid));
+ return getHex(fmtTid);
+ }
+
/**
* Formats transaction ids in a consistent way that is useful for logging
and persisting.
*/
diff --git
a/shell/src/main/java/org/apache/accumulo/shell/commands/FateCommand.java
b/shell/src/main/java/org/apache/accumulo/shell/commands/FateCommand.java
index 881b2f99a1..82f9366748 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/commands/FateCommand.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/commands/FateCommand.java
@@ -170,7 +170,8 @@ public class FateCommand extends Command {
} else if (cl.hasOption(delete.getOpt())) {
String[] txids = cl.getOptionValues(delete.getOpt());
validateArgs(txids);
- failedCommand = deleteTx(shellState.getWriter(), admin, zs, zk,
managerLockPath, txids);
+ failedCommand =
+ deleteTx(shellState.getWriter(), admin, zs, zk, managerLockPath,
tableLocksPath, txids);
} else if (cl.hasOption(list.getOpt())) {
printTx(shellState, admin, zs, zk, tableLocksPath,
cl.getOptionValues(list.getOpt()), cl);
} else if (cl.hasOption(print.getOpt())) {
@@ -237,11 +238,11 @@ public class FateCommand extends Command {
}
protected boolean deleteTx(PrintWriter out, AdminUtil<FateCommand> admin,
- ZooStore<FateCommand> zs, ZooReaderWriter zk, ServiceLockPath
zLockManagerPath, String[] args)
- throws InterruptedException, KeeperException {
+ ZooStore<FateCommand> zs, ZooReaderWriter zk, ServiceLockPath
zLockManagerPath,
+ ServiceLockPath zTableLocksPath, String[] args) throws
InterruptedException, KeeperException {
for (int i = 1; i < args.length; i++) {
if (admin.prepDelete(zs, zk, zLockManagerPath, args[i])) {
- admin.deleteLocks(zk, zLockManagerPath, args[i]);
+ admin.deleteLocks(zk, zTableLocksPath, args[i]);
} else {
out.printf("Could not delete transaction: %s%n", args[i]);
return false;
diff --git
a/shell/src/test/java/org/apache/accumulo/shell/commands/FateCommandTest.java
b/shell/src/test/java/org/apache/accumulo/shell/commands/FateCommandTest.java
index 2419cbf9d8..fc02a83d82 100644
---
a/shell/src/test/java/org/apache/accumulo/shell/commands/FateCommandTest.java
+++
b/shell/src/test/java/org/apache/accumulo/shell/commands/FateCommandTest.java
@@ -100,7 +100,8 @@ public class FateCommandTest {
@Override
protected boolean deleteTx(PrintWriter out, AdminUtil<FateCommand> admin,
ZooStore<FateCommand> zs, ZooReaderWriter zk, ServiceLockPath
zLockManagerPath,
- String[] args) throws InterruptedException, KeeperException {
+ ServiceLockPath zTableLocksPath, String[] args)
+ throws InterruptedException, KeeperException {
deleteCalled = true;
return true;
}
diff --git
a/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
b/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
index e6029d5649..0fa610c509 100644
--- a/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/fate/zookeeper/FateIT.java
@@ -51,12 +51,14 @@ import org.apache.accumulo.core.conf.ConfigurationCopy;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.NamespaceId;
import org.apache.accumulo.core.data.TableId;
+import org.apache.accumulo.core.fate.AdminUtil;
import org.apache.accumulo.core.fate.AgeOffStore;
import org.apache.accumulo.core.fate.Fate;
import org.apache.accumulo.core.fate.FateTxId;
import org.apache.accumulo.core.fate.ReadOnlyTStore.TStatus;
import org.apache.accumulo.core.fate.Repo;
import org.apache.accumulo.core.fate.ZooStore;
+import org.apache.accumulo.core.fate.zookeeper.ServiceLock;
import org.apache.accumulo.core.fate.zookeeper.ZooCache;
import org.apache.accumulo.core.fate.zookeeper.ZooReaderWriter;
import org.apache.accumulo.core.util.UtilWaitThread;
@@ -65,6 +67,7 @@ import org.apache.accumulo.manager.tableOps.ManagerRepo;
import org.apache.accumulo.manager.tableOps.TraceRepo;
import org.apache.accumulo.manager.tableOps.Utils;
import org.apache.accumulo.server.ServerContext;
+import org.apache.accumulo.server.util.Admin;
import org.apache.accumulo.test.util.Wait;
import org.apache.accumulo.test.zookeeper.ZooKeeperTestingServer;
import org.apache.zookeeper.KeeperException;
@@ -404,6 +407,51 @@ public class FateIT {
assertFalse(fate.cancel(txid));
}
+ /**
+ * Test that verifies that fate table locks are created and deleted from the
correct ZooKeeper
+ * path
+ */
+ @Test
+ public void testTableLockDeleteUsesCorrectZKPath() throws Exception {
+
+ ConfigurationCopy config = new ConfigurationCopy();
+ config.set(Property.GENERAL_THREADPOOL_SIZE, "2");
+ config.set(Property.MANAGER_FATE_THREADPOOL_SIZE, "1");
+ AdminUtil<Admin> admin = new AdminUtil<>(true);
+
+ callStarted = new CountDownLatch(1);
+ finishCall = new CountDownLatch(1);
+
+ long txId = fate.startTransaction();
+ String formattedTxId = FateTxId.formatTid(txId);
+ LOG.debug("Starting test testDeleteUsesCorrectZKPath {}", formattedTxId);
+ assertEquals(NEW, getTxStatus(zk, txId));
+ fate.seedTransaction("TestOperation", txId, new TestOperation(NS, TID),
false,
+ "Test Delete Op");
+ assertEquals(SUBMITTED, getTxStatus(zk, txId));
+ fate.startTransactionRunners(config, new ScheduledThreadPoolExecutor(2));
+ // Wait for the transaction runner to be in progress
+ Wait.waitFor(() -> IN_PROGRESS == getTxStatus(zk, txId));
+
+ assertFalse(fate.cancel(txId));
+
+ var tableLocksPath = ServiceLock.path(ZK_ROOT + Constants.ZTABLE_LOCKS);
+ assertFalse(zk.getChildren(tableLocksPath.toString()).isEmpty(),
+ "Table locks at " + tableLocksPath + "do not exist");
+ for (var tableName : zk.getChildren(tableLocksPath.toString())) {
+ for (var tableLock : zk.getChildren(
+ ServiceLock.path(ZK_ROOT + Constants.ZTABLE_LOCKS + "/" +
tableName).toString())) {
+ LOG.debug("Found table {} with fate lock {}", tableName, tableLock);
+ }
+ }
+
+ admin.deleteLocks(zk, tableLocksPath, FateTxId.toHexString(formattedTxId));
+ for (var tableName : zk.getChildren(tableLocksPath.toString())) {
+ var fateTableLocks = tableLocksPath + "/" + tableName;
+ assertTrue(zk.getChildren(fateTableLocks).isEmpty(), " table fate locks
are still present");
+ }
+ }
+
@Test
public void testRepoFails() throws Exception {
/*