This is an automated email from the ASF dual-hosted git repository.
keith-turner 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 224104de96 Fixes fate ranged lock encoding (#6350)
224104de96 is described below
commit 224104de966bff8b5e6f29a5cf8568ca601588dc
Author: Keith Turner <[email protected]>
AuthorDate: Thu Apr 30 10:53:12 2026 -0700
Fixes fate ranged lock encoding (#6350)
Encoding the fate ranged lock to store in zookeeper was using standard
Base64 which could include a '/'. When this happened the code would go
into an infinite loop. Changed to using base64 URL encoding and added a
few checks to make the fate lock encoding/decoding more strict.
---
.../accumulo/core/fate/zookeeper/FateLock.java | 27 ++++++++++++++--------
.../accumulo/core/fate/zookeeper/FateLockTest.java | 2 +-
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git
a/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/FateLock.java
b/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/FateLock.java
index 5e8c541818..fcd02f3bd4 100644
--- a/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/FateLock.java
+++ b/core/src/main/java/org/apache/accumulo/core/fate/zookeeper/FateLock.java
@@ -74,7 +74,7 @@ public class FateLock implements QueueLock {
public final static class FateLockEntry {
- private static final String DELIMITER = "_";
+ private static final String DELIMITER = "~";
final LockType lockType;
final FateId fateId;
@@ -87,7 +87,8 @@ public class FateLock implements QueueLock {
}
private FateLockEntry(String entry) {
- var fields = entry.split(DELIMITER, 4);
+ var fields = entry.split(DELIMITER);
+ Preconditions.checkArgument(fields.length == 4, entry);
this.lockType = LockType.valueOf(fields[0]);
this.fateId = FateId.from(fields[1]);
this.range = LockRange.of(decodeRow(fields[2]), decodeRow(fields[3]));
@@ -109,13 +110,13 @@ public class FateLock implements QueueLock {
if (row == null) {
return "N";
} else {
- return "P" +
Base64.getEncoder().encodeToString(TextUtil.getBytes(row));
+ return "P" +
Base64.getUrlEncoder().encodeToString(TextUtil.getBytes(row));
}
}
private Text decodeRow(String enc) {
if (enc.charAt(0) == 'P') {
- return new Text(Base64.getDecoder().decode(enc.substring(1)));
+ return new Text(Base64.getUrlDecoder().decode(enc.substring(1)));
} else if (enc.charAt(0) == 'N') {
return null;
} else {
@@ -123,9 +124,15 @@ public class FateLock implements QueueLock {
}
}
+ private String checkForDelimiter(String s) {
+ Preconditions.checkArgument(!s.contains(DELIMITER), s);
+ return s;
+ }
+
public String serialize() {
- return lockType.name() + DELIMITER + fateId.canonical() + DELIMITER
- + encodeRow(range.getStartRow()) + DELIMITER +
encodeRow(range.getEndRow());
+ return checkForDelimiter(lockType.name()) + DELIMITER +
checkForDelimiter(fateId.canonical())
+ + DELIMITER + checkForDelimiter(encodeRow(range.getStartRow())) +
DELIMITER
+ + checkForDelimiter(encodeRow(range.getEndRow()));
}
public static FateLockEntry from(LockType lockType, FateId fateId,
LockRange range) {
@@ -183,19 +190,21 @@ public class FateLock implements QueueLock {
public long addEntry(FateLockEntry entry) {
String dataString = entry.serialize();
- Preconditions.checkState(!dataString.contains("#"));
+ Preconditions.checkState(!dataString.contains("#") &&
!dataString.contains("/"), dataString);
String newPath;
try {
while (true) {
try {
- newPath =
- zoo.putPersistentSequential(path + "/" + PREFIX + dataString +
"#", new byte[0]);
+ var prefix = path + "/" + PREFIX + dataString + "#";
+ log.trace("Attempting to create {} in zookeeper", prefix);
+ newPath = zoo.putPersistentSequential(prefix, new byte[0]);
String[] parts = newPath.split("/");
String last = parts[parts.length - 1];
return new NodeName(last).sequence;
} catch (NoNodeException nne) {
// the parent does not exist so try to create it
+ log.trace("Attempting to create parent {}", path.toString(), nne);
zoo.putPersistentData(path.toString(), new byte[] {},
NodeExistsPolicy.SKIP);
}
}
diff --git
a/core/src/test/java/org/apache/accumulo/core/fate/zookeeper/FateLockTest.java
b/core/src/test/java/org/apache/accumulo/core/fate/zookeeper/FateLockTest.java
index 3ae75e33af..eea6f610ac 100644
---
a/core/src/test/java/org/apache/accumulo/core/fate/zookeeper/FateLockTest.java
+++
b/core/src/test/java/org/apache/accumulo/core/fate/zookeeper/FateLockTest.java
@@ -34,7 +34,7 @@ public class FateLockTest {
public void testParsing() {
var fateId = FateId.from(FateInstanceType.USER, UUID.randomUUID());
// ZooKeeper docs state that sequence numbers are formatted using %010d
- String lockData = "WRITE_" + fateId.canonical() + "_N_N";
+ String lockData = "WRITE~" + fateId.canonical() + "~N~N";
var lockNode =
new FateLock.NodeName(FateLock.PREFIX + lockData + "#" +
String.format("%010d", 40));
assertEquals(40, lockNode.sequence);