This is an automated email from the ASF dual-hosted git repository.
ctubbsii pushed a commit to branch 3.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/3.1 by this push:
new e560e0e0c3 Add ZooKeeper Namespace Id-to-name mapping (#4996)
e560e0e0c3 is described below
commit e560e0e0c3d83bfea17e49fb7135ef9c7f99642c
Author: John K <[email protected]>
AuthorDate: Fri Nov 15 01:01:58 2024 -0500
Add ZooKeeper Namespace Id-to-name mapping (#4996)
Add mapping for ZooKeeper Namespace Ids to Names in a single ZK node
- Implement Json-formatted mapping of namespace Ids to Names in
ZooKeeper. Stored on the "/namespaces" node. Initially created in
ZooKeeperInitializer.
- Create NamespaceMapping.java to hold methods initializing, adding,
retrieving, serializing, and deserializing namespace Id to Name map
in ZooKeeper.
- Build namespace id to name mapping of current ZooKeeper namespaces
in Upgrader11to12. Delete all current "/name" nodes.
- Add relevant test code to Upgrader11to12Test.
- Sort the mapping in serialize() for testing reliability and readability
- Remove now-unneeded Utils.checkNamespaceDoesNotExist()
This is part of #4698, but only for the namespace mapping. It does not yet
address table mappings.
---------
Co-authored-by: Christopher Tubbs <[email protected]>
---
.../java/org/apache/accumulo/core/Constants.java | 1 -
.../accumulo/core/clientImpl/ClientContext.java | 6 +
.../accumulo/core/clientImpl/NamespaceMapping.java | 164 +++++++++++++++++++++
.../accumulo/core/clientImpl/Namespaces.java | 56 +------
.../core/clientImpl/NamespaceMappingTest.java | 72 +++++++++
.../accumulo/server/init/ZooKeeperInitializer.java | 7 +-
.../accumulo/server/tables/TableManager.java | 34 +++--
.../apache/accumulo/manager/tableOps/Utils.java | 12 --
.../create/PopulateZookeeperWithNamespace.java | 19 ++-
.../tableOps/namespace/rename/RenameNamespace.java | 23 +--
.../accumulo/manager/upgrade/Upgrader11to12.java | 29 +++-
.../manager/upgrade/Upgrader11to12Test.java | 43 +++++-
12 files changed, 353 insertions(+), 113 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 411af753cf..ca9e0b62ff 100644
--- a/core/src/main/java/org/apache/accumulo/core/Constants.java
+++ b/core/src/main/java/org/apache/accumulo/core/Constants.java
@@ -48,7 +48,6 @@ public class Constants {
public static final String ZTABLE_NAMESPACE = "/namespace";
public static final String ZNAMESPACES = "/namespaces";
- public static final String ZNAMESPACE_NAME = "/name";
public static final String ZMANAGERS = "/managers";
public static final String ZMANAGER_LOCK = ZMANAGERS + "/lock";
diff --git
a/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
b/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
index 899bad6c10..298a14329d 100644
--- a/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
+++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java
@@ -142,6 +142,7 @@ public class ClientContext implements AccumuloClient {
private final Supplier<SaslConnectionParams> saslSupplier;
private final Supplier<SslConnectionParams> sslSupplier;
private final Supplier<ScanServerSelector> scanServerSelectorSupplier;
+ private final NamespaceMapping namespaces;
private TCredentials rpcCreds;
private ThriftTransportPool thriftTransportPool;
private ZookeeperLockChecker zkLockChecker;
@@ -249,6 +250,7 @@ public class ClientContext implements AccumuloClient {
clientThreadPools = ThreadPools.getClientThreadPools(ueh);
}
}
+ this.namespaces = new NamespaceMapping(this);
}
public Ample getAmple() {
@@ -1114,4 +1116,8 @@ public class ClientContext implements AccumuloClient {
return this.zkLockChecker;
}
+ public NamespaceMapping getNamespaces() {
+ return namespaces;
+ }
+
}
diff --git
a/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceMapping.java
b/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceMapping.java
new file mode 100644
index 0000000000..7a6ce7ab6b
--- /dev/null
+++
b/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceMapping.java
@@ -0,0 +1,164 @@
+/*
+ * 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.core.clientImpl;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Collections.emptySortedMap;
+import static java.util.Objects.requireNonNull;
+import static org.apache.accumulo.core.util.LazySingletons.GSON;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.clientImpl.thrift.TableOperation;
+import org.apache.accumulo.core.clientImpl.thrift.TableOperationExceptionType;
+import org.apache.accumulo.core.data.NamespaceId;
+import org.apache.accumulo.core.fate.zookeeper.ZooCache;
+import org.apache.accumulo.core.fate.zookeeper.ZooReaderWriter;
+import org.apache.zookeeper.KeeperException;
+
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.gson.reflect.TypeToken;
+
+public class NamespaceMapping {
+ // type token must represent a mutable type, so it can be altered in the
mutateExisting methods
+ // without needing to make a copy
+ private static final Type MAP_TYPE = new TypeToken<TreeMap<String,String>>()
{}.getType();
+ private final ClientContext context;
+ private volatile SortedMap<NamespaceId,String> currentNamespaceMap =
emptySortedMap();
+ private volatile SortedMap<String,NamespaceId> currentNamespaceReverseMap =
emptySortedMap();
+ private volatile long lastMzxid;
+
+ public NamespaceMapping(ClientContext context) {
+ this.context = context;
+ }
+
+ public static void put(ZooReaderWriter zoo, String zPath, NamespaceId
namespaceId,
+ String namespaceName)
+ throws InterruptedException, KeeperException,
AcceptableThriftTableOperationException {
+ if (Namespace.DEFAULT.id().equals(namespaceId) ||
Namespace.ACCUMULO.id().equals(namespaceId)) {
+ throw new AssertionError(
+ "Putting built-in namespaces in map should not be possible after
init");
+ }
+ zoo.mutateExisting(zPath, data -> {
+ var namespaces = deserialize(data);
+ if (namespaces.containsKey(namespaceId.canonical())) {
+ throw new AcceptableThriftTableOperationException(null,
namespaceId.canonical(),
+ TableOperation.CREATE,
TableOperationExceptionType.NAMESPACE_EXISTS,
+ "Namespace Id already exists");
+ }
+ namespaces.put(namespaceId.canonical(), namespaceName);
+ return serialize(namespaces);
+ });
+ }
+
+ public static void remove(ZooReaderWriter zoo, String zPath, NamespaceId
namespaceId)
+ throws InterruptedException, KeeperException,
AcceptableThriftTableOperationException {
+ if (Namespace.DEFAULT.id().equals(namespaceId) ||
Namespace.ACCUMULO.id().equals(namespaceId)) {
+ throw new AssertionError("Removing built-in namespaces in map should not
be possible");
+ }
+ zoo.mutateExisting(zPath, data -> {
+ var namespaces = deserialize(data);
+ if (!namespaces.containsKey(namespaceId.canonical())) {
+ throw new AcceptableThriftTableOperationException(null,
namespaceId.canonical(),
+ TableOperation.DELETE,
TableOperationExceptionType.NAMESPACE_NOTFOUND,
+ "Namespace already removed while processing");
+ }
+ namespaces.remove(namespaceId.canonical());
+ return serialize(namespaces);
+ });
+ }
+
+ public static void rename(ZooReaderWriter zoo, String zPath, NamespaceId
namespaceId,
+ String oldName, String newName)
+ throws InterruptedException, KeeperException,
AcceptableThriftTableOperationException {
+ if (Namespace.DEFAULT.id().equals(namespaceId) ||
Namespace.ACCUMULO.id().equals(namespaceId)) {
+ throw new AssertionError("Renaming built-in namespaces in map should not
be possible");
+ }
+ zoo.mutateExisting(zPath, current -> {
+ var currentNamespaceMap = deserialize(current);
+ final String currentName =
currentNamespaceMap.get(namespaceId.canonical());
+ if (currentName.equals(newName)) {
+ return null; // assume in this case the operation is running again, so
we are done
+ }
+ if (!currentName.equals(oldName)) {
+ throw new AcceptableThriftTableOperationException(null, oldName,
TableOperation.RENAME,
+ TableOperationExceptionType.NAMESPACE_NOTFOUND, "Name changed
while processing");
+ }
+ if (currentNamespaceMap.containsValue(newName)) {
+ throw new AcceptableThriftTableOperationException(null, newName,
TableOperation.RENAME,
+ TableOperationExceptionType.NAMESPACE_EXISTS, "Namespace name
already exists");
+ }
+ currentNamespaceMap.put(namespaceId.canonical(), newName);
+ return serialize(currentNamespaceMap);
+ });
+ }
+
+ public static byte[] serialize(Map<String,String> map) {
+ return GSON.get().toJson(new TreeMap<>(map), MAP_TYPE).getBytes(UTF_8);
+ }
+
+ public static Map<String,String> deserialize(byte[] data) {
+ requireNonNull(data, "/namespaces node should not be null");
+ return GSON.get().fromJson(new String(data, UTF_8), MAP_TYPE);
+ }
+
+ private synchronized void update() {
+ final ZooCache zc = context.getZooCache();
+ final String zPath = context.getZooKeeperRoot() + Constants.ZNAMESPACES;
+ final ZooCache.ZcStat stat = new ZooCache.ZcStat();
+
+ byte[] data = zc.get(zPath, stat);
+ if (stat.getMzxid() > lastMzxid) {
+ if (data == null) {
+ throw new IllegalStateException("namespaces node should not be null");
+ } else {
+ Map<String,String> idToName = deserialize(data);
+ if (!idToName.containsKey(Namespace.DEFAULT.id().canonical())
+ || !idToName.containsKey(Namespace.ACCUMULO.id().canonical())) {
+ throw new IllegalStateException("Built-in namespace is not present
in map");
+ }
+ var converted = ImmutableSortedMap.<NamespaceId,String>naturalOrder();
+ var convertedReverse =
ImmutableSortedMap.<String,NamespaceId>naturalOrder();
+ idToName.forEach((idString, name) -> {
+ var id = NamespaceId.of(idString);
+ converted.put(id, name);
+ convertedReverse.put(name, id);
+ });
+ currentNamespaceMap = converted.build();
+ currentNamespaceReverseMap = convertedReverse.build();
+ }
+ lastMzxid = stat.getMzxid();
+ }
+ }
+
+ public SortedMap<NamespaceId,String> getIdToNameMap() {
+ update();
+ return currentNamespaceMap;
+ }
+
+ public SortedMap<String,NamespaceId> getNameToIdMap() {
+ update();
+ return currentNamespaceReverseMap;
+ }
+
+}
diff --git
a/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java
b/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java
index 4368b31896..366375f82f 100644
--- a/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java
+++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java
@@ -18,21 +18,14 @@
*/
package org.apache.accumulo.core.clientImpl;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.function.BiConsumer;
-import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.NamespaceNotFoundException;
import org.apache.accumulo.core.data.NamespaceId;
import org.apache.accumulo.core.data.TableId;
-import org.apache.accumulo.core.fate.zookeeper.ZooCache;
import org.apache.accumulo.core.util.tables.TableNameUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,9 +34,7 @@ public class Namespaces {
private static final Logger log = LoggerFactory.getLogger(Namespaces.class);
public static boolean exists(ClientContext context, NamespaceId namespaceId)
{
- ZooCache zc = context.getZooCache();
- List<String> namespaceIds = zc.getChildren(context.getZooKeeperRoot() +
Constants.ZNAMESPACES);
- return namespaceIds.contains(namespaceId.canonical());
+ return context.getNamespaces().getIdToNameMap().containsKey(namespaceId);
}
public static List<TableId> getTableIds(ClientContext context, NamespaceId
namespaceId)
@@ -70,39 +61,18 @@ public class Namespaces {
return names;
}
- /**
- * Gets all the namespaces from ZK. The first arg (t) the BiConsumer accepts
is the ID and the
- * second (u) is the namespaceName.
- */
- private static void getAllNamespaces(ClientContext context,
- BiConsumer<String,String> biConsumer) {
- final ZooCache zc = context.getZooCache();
- List<String> namespaceIds = zc.getChildren(context.getZooKeeperRoot() +
Constants.ZNAMESPACES);
- for (String id : namespaceIds) {
- byte[] path = zc.get(context.getZooKeeperRoot() + Constants.ZNAMESPACES
+ "/" + id
- + Constants.ZNAMESPACE_NAME);
- if (path != null) {
- biConsumer.accept(id, new String(path, UTF_8));
- }
- }
- }
-
/**
* Return sorted map with key = ID, value = namespaceName
*/
public static SortedMap<NamespaceId,String> getIdToNameMap(ClientContext
context) {
- SortedMap<NamespaceId,String> idMap = new TreeMap<>();
- getAllNamespaces(context, (id, name) -> idMap.put(NamespaceId.of(id),
name));
- return idMap;
+ return context.getNamespaces().getIdToNameMap();
}
/**
* Return sorted map with key = namespaceName, value = ID
*/
public static SortedMap<String,NamespaceId> getNameToIdMap(ClientContext
context) {
- SortedMap<String,NamespaceId> nameMap = new TreeMap<>();
- getAllNamespaces(context, (id, name) -> nameMap.put(name,
NamespaceId.of(id)));
- return nameMap;
+ return context.getNamespaces().getNameToIdMap();
}
/**
@@ -110,17 +80,12 @@ public class Namespaces {
*/
public static NamespaceId getNamespaceId(ClientContext context, String
namespaceName)
throws NamespaceNotFoundException {
- final ArrayList<NamespaceId> singleId = new ArrayList<>(1);
- getAllNamespaces(context, (id, name) -> {
- if (name.equals(namespaceName)) {
- singleId.add(NamespaceId.of(id));
- }
- });
- if (singleId.isEmpty()) {
+ var id = context.getNamespaces().getNameToIdMap().get(namespaceName);
+ if (id == null) {
throw new NamespaceNotFoundException(null, namespaceName,
"getNamespaceId() failed to find namespace");
}
- return singleId.get(0);
+ return id;
}
/**
@@ -150,13 +115,8 @@ public class Namespaces {
*/
public static String getNamespaceName(ClientContext context, NamespaceId
namespaceId)
throws NamespaceNotFoundException {
- String name;
- ZooCache zc = context.getZooCache();
- byte[] path = zc.get(context.getZooKeeperRoot() + Constants.ZNAMESPACES +
"/"
- + namespaceId.canonical() + Constants.ZNAMESPACE_NAME);
- if (path != null) {
- name = new String(path, UTF_8);
- } else {
+ String name = getIdToNameMap(context).get(namespaceId);
+ if (name == null) {
throw new NamespaceNotFoundException(namespaceId.canonical(), null,
"getNamespaceName() failed to find namespace");
}
diff --git
a/core/src/test/java/org/apache/accumulo/core/clientImpl/NamespaceMappingTest.java
b/core/src/test/java/org/apache/accumulo/core/clientImpl/NamespaceMappingTest.java
new file mode 100644
index 0000000000..833f17ca4a
--- /dev/null
+++
b/core/src/test/java/org/apache/accumulo/core/clientImpl/NamespaceMappingTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.core.clientImpl;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.junit.jupiter.api.Test;
+
+import com.google.gson.JsonSyntaxException;
+
+class NamespaceMappingTest {
+
+ @Test
+ void testSerialize() {
+ assertThrows(NullPointerException.class, () ->
NamespaceMapping.serialize(null));
+ assertEquals("{}", new String(NamespaceMapping.serialize(Map.of()),
UTF_8));
+ assertEquals("{\"a\":\"b\"}", new
String(NamespaceMapping.serialize(Map.of("a", "b")), UTF_8));
+ assertEquals("{\"a\":\"b\",\"c\":\"d\"}",
+ new String(NamespaceMapping.serialize(Map.of("a", "b", "c", "d")),
UTF_8));
+ }
+
+ @Test
+ void testDeserialize() {
+ assertThrows(NullPointerException.class, () ->
NamespaceMapping.deserialize(null));
+ assertEquals(null, NamespaceMapping.deserialize(new byte[0]));
+ assertTrue(NamespaceMapping.deserialize("{}".getBytes(UTF_8)) instanceof
TreeMap);
+ assertEquals(Map.of(), NamespaceMapping.deserialize("{}".getBytes(UTF_8)));
+ assertEquals(Map.of("a", "b"),
NamespaceMapping.deserialize("{\"a\":\"b\"}".getBytes(UTF_8)));
+ assertEquals(Map.of("a", "b", "c", "d"),
+
NamespaceMapping.deserialize("{\"a\":\"b\",\"c\":\"d\"}".getBytes(UTF_8)));
+
+ // check malformed json
+ assertThrows(JsonSyntaxException.class,
+ () -> NamespaceMapping.deserialize("-".getBytes(UTF_8)));
+ // check incorrect json type for string value
+ assertThrows(JsonSyntaxException.class,
+ () -> NamespaceMapping.deserialize("{\"a\":{}}".getBytes(UTF_8)));
+ // check valid json, but not a map
+ assertThrows(JsonSyntaxException.class,
+ () -> NamespaceMapping.deserialize("\"[\"a\"]\"".getBytes(UTF_8)));
+
+ // strange edge case because empty json array can be converted into an
empty map; we don't ever
+ // expect this to be found, but there's no easy way to check for this edge
case that is worth
+ // the effort
+ assertTrue(NamespaceMapping.deserialize("[]".getBytes(UTF_8)) instanceof
SortedMap);
+ assertEquals(Map.of(), NamespaceMapping.deserialize("[]".getBytes(UTF_8)));
+ }
+
+}
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 50dfc76b23..1fc771dc09 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
@@ -21,10 +21,12 @@ package org.apache.accumulo.server.init;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
+import java.util.Map;
import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.admin.TimeType;
import org.apache.accumulo.core.clientImpl.Namespace;
+import org.apache.accumulo.core.clientImpl.NamespaceMapping;
import org.apache.accumulo.core.data.InstanceId;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.Value;
@@ -110,7 +112,10 @@ public class ZooKeeperInitializer {
String zkInstanceRoot = Constants.ZROOT + "/" + instanceId;
zoo.putPersistentData(zkInstanceRoot + Constants.ZTABLES,
Constants.ZTABLES_INITIAL_ID,
ZooUtil.NodeExistsPolicy.FAIL);
- zoo.putPersistentData(zkInstanceRoot + Constants.ZNAMESPACES, new byte[0],
+ zoo.putPersistentData(zkInstanceRoot + Constants.ZNAMESPACES,
+ NamespaceMapping
+ .serialize(Map.of(Namespace.DEFAULT.id().canonical(),
Namespace.DEFAULT.name(),
+ Namespace.ACCUMULO.id().canonical(),
Namespace.ACCUMULO.name())),
ZooUtil.NodeExistsPolicy.FAIL);
TableManager.prepareNewNamespaceState(context, Namespace.DEFAULT.id(),
Namespace.DEFAULT.name(),
diff --git
a/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java
b/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java
index 380942b8aa..15ce8d5941 100644
---
a/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java
+++
b/server/base/src/main/java/org/apache/accumulo/server/tables/TableManager.java
@@ -29,6 +29,9 @@ import java.util.Set;
import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.NamespaceNotFoundException;
+import
org.apache.accumulo.core.clientImpl.AcceptableThriftTableOperationException;
+import org.apache.accumulo.core.clientImpl.NamespaceMapping;
+import org.apache.accumulo.core.clientImpl.thrift.TableOperationExceptionType;
import org.apache.accumulo.core.data.InstanceId;
import org.apache.accumulo.core.data.NamespaceId;
import org.apache.accumulo.core.data.TableId;
@@ -67,14 +70,14 @@ public class TableManager {
private final ZooReaderWriter zoo;
private final ZooCache zooStateCache;
- public static void prepareNewNamespaceState(ZooReaderWriter zoo, final
PropStore propStore,
- InstanceId instanceId, NamespaceId namespaceId, String namespace,
- NodeExistsPolicy existsPolicy) throws KeeperException,
InterruptedException {
+ public static void prepareNewNamespaceState(final ServerContext context,
NamespaceId namespaceId,
+ String namespace, NodeExistsPolicy existsPolicy)
+ throws KeeperException, InterruptedException {
+ final PropStore propStore = context.getPropStore();
+ final InstanceId instanceId = context.getInstanceID();
log.debug("Creating ZooKeeper entries for new namespace {} (ID: {})",
namespace, namespaceId);
- String zPath = Constants.ZROOT + "/" + instanceId + Constants.ZNAMESPACES
+ "/" + namespaceId;
-
- zoo.putPersistentData(zPath, new byte[0], existsPolicy);
- zoo.putPersistentData(zPath + Constants.ZNAMESPACE_NAME,
namespace.getBytes(UTF_8),
+ context.getZooReaderWriter().putPersistentData(
+ Constants.ZROOT + "/" + instanceId + Constants.ZNAMESPACES + "/" +
namespaceId, new byte[0],
existsPolicy);
var propKey = NamespacePropKey.of(instanceId, namespaceId);
if (!propStore.exists(propKey)) {
@@ -82,13 +85,6 @@ public class TableManager {
}
}
- public static void prepareNewNamespaceState(final ServerContext context,
NamespaceId namespaceId,
- String namespace, NodeExistsPolicy existsPolicy)
- throws KeeperException, InterruptedException {
- prepareNewNamespaceState(context.getZooReaderWriter(),
context.getPropStore(),
- context.getInstanceID(), namespaceId, namespace, existsPolicy);
- }
-
public static void prepareNewTableState(ZooReaderWriter zoo, PropStore
propStore,
InstanceId instanceId, TableId tableId, NamespaceId namespaceId, String
tableName,
TableState state, NodeExistsPolicy existsPolicy)
@@ -329,7 +325,15 @@ public class TableManager {
}
public void removeNamespace(NamespaceId namespaceId)
- throws KeeperException, InterruptedException {
+ throws KeeperException, InterruptedException,
AcceptableThriftTableOperationException {
+ try {
+ NamespaceMapping.remove(zoo, zkRoot + Constants.ZNAMESPACES,
namespaceId);
+ } catch (AcceptableThriftTableOperationException e) {
+ // ignore not found, because that's what we're trying to do anyway
+ if (e.getType() != TableOperationExceptionType.NAMESPACE_NOTFOUND) {
+ throw e;
+ }
+ }
zoo.recursiveDelete(zkRoot + Constants.ZNAMESPACES + "/" + namespaceId,
NodeMissingPolicy.SKIP);
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/Utils.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/Utils.java
index be051943a3..8fc09eac2c 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/Utils.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/Utils.java
@@ -258,18 +258,6 @@ public class Utils {
return Utils.getLock(env.getContext(), id, tid, LockType.READ);
}
- public static void checkNamespaceDoesNotExist(ServerContext context, String
namespace,
- NamespaceId namespaceId, TableOperation operation)
- throws AcceptableThriftTableOperationException {
-
- NamespaceId n = Namespaces.lookupNamespaceId(context, namespace);
-
- if (n != null && !n.equals(namespaceId)) {
- throw new AcceptableThriftTableOperationException(null, namespace,
operation,
- TableOperationExceptionType.NAMESPACE_EXISTS, null);
- }
- }
-
/**
* Given a fully-qualified Path and a flag indicating if the file info is
base64 encoded or not,
* retrieve the data from a file on the file system. It is assumed that the
file is textual and
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/create/PopulateZookeeperWithNamespace.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/create/PopulateZookeeperWithNamespace.java
index 939b6f0916..b47223f151 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/create/PopulateZookeeperWithNamespace.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/create/PopulateZookeeperWithNamespace.java
@@ -18,6 +18,8 @@
*/
package org.apache.accumulo.manager.tableOps.namespace.create;
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.clientImpl.NamespaceMapping;
import org.apache.accumulo.core.clientImpl.thrift.TableOperation;
import org.apache.accumulo.core.fate.Repo;
import
org.apache.accumulo.core.fate.zookeeper.DistributedReadWriteLock.LockType;
@@ -50,17 +52,17 @@ class PopulateZookeeperWithNamespace extends ManagerRepo {
Utils.getTableNameLock().lock();
try {
- Utils.checkNamespaceDoesNotExist(manager.getContext(),
namespaceInfo.namespaceName,
- namespaceInfo.namespaceId, TableOperation.CREATE);
-
- TableManager.prepareNewNamespaceState(manager.getContext(),
namespaceInfo.namespaceId,
+ var context = manager.getContext();
+ NamespaceMapping.put(context.getZooReaderWriter(),
+ Constants.ZROOT + "/" + context.getInstanceID() +
Constants.ZNAMESPACES,
+ namespaceInfo.namespaceId, namespaceInfo.namespaceName);
+ TableManager.prepareNewNamespaceState(context, namespaceInfo.namespaceId,
namespaceInfo.namespaceName, NodeExistsPolicy.OVERWRITE);
- PropUtil.setProperties(manager.getContext(),
- NamespacePropKey.of(manager.getContext(), namespaceInfo.namespaceId),
+ PropUtil.setProperties(context, NamespacePropKey.of(context,
namespaceInfo.namespaceId),
namespaceInfo.props);
- manager.getContext().clearTableListCache();
+ context.clearTableListCache();
return new FinishCreateNamespace(namespaceInfo);
} finally {
@@ -70,8 +72,9 @@ class PopulateZookeeperWithNamespace extends ManagerRepo {
@Override
public void undo(long tid, Manager manager) throws Exception {
+ var context = manager.getContext();
manager.getTableManager().removeNamespace(namespaceInfo.namespaceId);
- manager.getContext().clearTableListCache();
+ context.clearTableListCache();
Utils.unreserveNamespace(manager, namespaceInfo.namespaceId, tid,
LockType.WRITE);
}
diff --git
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/rename/RenameNamespace.java
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/rename/RenameNamespace.java
index 3fdddf0758..b1fcb9f30b 100644
---
a/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/rename/RenameNamespace.java
+++
b/server/manager/src/main/java/org/apache/accumulo/manager/tableOps/namespace/rename/RenameNamespace.java
@@ -18,12 +18,9 @@
*/
package org.apache.accumulo.manager.tableOps.namespace.rename;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import org.apache.accumulo.core.Constants;
-import
org.apache.accumulo.core.clientImpl.AcceptableThriftTableOperationException;
+import org.apache.accumulo.core.clientImpl.NamespaceMapping;
import org.apache.accumulo.core.clientImpl.thrift.TableOperation;
-import org.apache.accumulo.core.clientImpl.thrift.TableOperationExceptionType;
import org.apache.accumulo.core.data.NamespaceId;
import org.apache.accumulo.core.fate.Repo;
import
org.apache.accumulo.core.fate.zookeeper.DistributedReadWriteLock.LockType;
@@ -59,23 +56,9 @@ public class RenameNamespace extends ManagerRepo {
Utils.getTableNameLock().lock();
try {
- Utils.checkNamespaceDoesNotExist(manager.getContext(), newName,
namespaceId,
- TableOperation.RENAME);
-
- final String tap = manager.getZooKeeperRoot() + Constants.ZNAMESPACES +
"/" + namespaceId
- + Constants.ZNAMESPACE_NAME;
+ NamespaceMapping.rename(zoo, manager.getZooKeeperRoot() +
Constants.ZNAMESPACES, namespaceId,
+ oldName, newName);
- zoo.mutateExisting(tap, current -> {
- final String currentName = new String(current, UTF_8);
- if (currentName.equals(newName)) {
- return null; // assume in this case the operation is running again,
so we are done
- }
- if (!currentName.equals(oldName)) {
- throw new AcceptableThriftTableOperationException(null, oldName,
TableOperation.RENAME,
- TableOperationExceptionType.NAMESPACE_NOTFOUND, "Name changed
while processing");
- }
- return newName.getBytes(UTF_8);
- });
manager.getContext().clearTableListCache();
} finally {
Utils.getTableNameLock().unlock();
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 a130d11445..60d80aaa44 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
@@ -26,17 +26,20 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
+import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.BatchDeleter;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.IsolatedScanner;
import org.apache.accumulo.core.client.MutationsRejectedException;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.clientImpl.NamespaceMapping;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.Range;
@@ -81,7 +84,10 @@ public class Upgrader11to12 implements Upgrader {
static final Set<Text> UPGRADE_FAMILIES =
Set.of(DataFileColumnFamily.NAME, CHOPPED,
ExternalCompactionColumnFamily.NAME);
- public static final String ZTRACERS = "/tracers";
+ private static final String ZTRACERS = "/tracers";
+
+ @VisibleForTesting
+ static final String ZNAMESPACE_NAME = "/name";
@Override
public void upgradeZookeeper(@NonNull ServerContext context) {
@@ -117,6 +123,27 @@ public class Upgrader11to12 implements Upgrader {
zrw.overwritePersistentData(rootBase, rtm.toJson().getBytes(UTF_8),
stat.getVersion());
log.info("Root metadata in ZooKeeper after upgrade: {}", rtm.toJson());
}
+
+ String zPath = Constants.ZROOT + "/" + context.getInstanceID() +
Constants.ZNAMESPACES;
+ byte[] namespacesData = zrw.getData(zPath);
+ if (namespacesData.length != 0) {
+ throw new IllegalStateException(
+ "Unexpected data found under namespaces node: " + new
String(namespacesData, UTF_8));
+ }
+ List<String> namespaceIdList = zrw.getChildren(zPath);
+ Map<String,String> namespaceMap = new HashMap<>();
+ for (String namespaceId : namespaceIdList) {
+ String namespaceNamePath = zPath + "/" + namespaceId + ZNAMESPACE_NAME;
+ namespaceMap.put(namespaceId, new
String(zrw.getData(namespaceNamePath), UTF_8));
+ }
+ byte[] mapping = NamespaceMapping.serialize(namespaceMap);
+ zrw.putPersistentData(zPath, mapping,
ZooUtil.NodeExistsPolicy.OVERWRITE);
+
+ for (String namespaceId : namespaceIdList) {
+ String namespaceNamePath = zPath + "/" + namespaceId + ZNAMESPACE_NAME;
+ zrw.delete(namespaceNamePath);
+ }
+
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(
diff --git
a/server/manager/src/test/java/org/apache/accumulo/manager/upgrade/Upgrader11to12Test.java
b/server/manager/src/test/java/org/apache/accumulo/manager/upgrade/Upgrader11to12Test.java
index d176a4c56c..bb21b4efec 100644
---
a/server/manager/src/test/java/org/apache/accumulo/manager/upgrade/Upgrader11to12Test.java
+++
b/server/manager/src/test/java/org/apache/accumulo/manager/upgrade/Upgrader11to12Test.java
@@ -20,8 +20,11 @@ package org.apache.accumulo.manager.upgrade;
import static java.nio.charset.StandardCharsets.UTF_8;
import static
org.apache.accumulo.manager.upgrade.Upgrader11to12.UPGRADE_FAMILIES;
+import static
org.apache.accumulo.manager.upgrade.Upgrader11to12.ZNAMESPACE_NAME;
+import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
@@ -40,10 +43,13 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.client.BatchWriter;
import org.apache.accumulo.core.client.MutationsRejectedException;
+import org.apache.accumulo.core.clientImpl.NamespaceMapping;
import org.apache.accumulo.core.data.ColumnUpdate;
import org.apache.accumulo.core.data.InstanceId;
import org.apache.accumulo.core.data.Key;
@@ -346,14 +352,18 @@ public class Upgrader11to12Test {
Upgrader11to12 upgrader = new Upgrader11to12();
ServerContext context = createMock(ServerContext.class);
- ZooReaderWriter zrw = createMock(ZooReaderWriter.class);
+ ZooReaderWriter zrw = createStrictMock(ZooReaderWriter.class);
expect(context.getInstanceID()).andReturn(iid).anyTimes();
expect(context.getZooReaderWriter()).andReturn(zrw).anyTimes();
+ zrw.recursiveDelete(Constants.ZROOT + "/" + iid.canonical() + "/tracers",
+ ZooUtil.NodeMissingPolicy.SKIP);
+ expectLastCall().once();
+
Capture<Stat> statCapture = newCapture();
- expect(zrw.getData(eq("/accumulo/" + iid.canonical() + "/root_tablet"),
capture(statCapture)))
- .andAnswer(() -> {
+ expect(zrw.getData(eq(Constants.ZROOT + "/" + iid.canonical() +
"/root_tablet"),
+ capture(statCapture))).andAnswer(() -> {
Stat stat = statCapture.getValue();
stat.setCtime(System.currentTimeMillis());
stat.setMtime(System.currentTimeMillis());
@@ -364,12 +374,31 @@ public class Upgrader11to12Test {
}).once();
Capture<byte[]> byteCapture = newCapture();
- expect(zrw.overwritePersistentData(eq("/accumulo/" + iid.canonical() +
"/root_tablet"),
+ expect(zrw.overwritePersistentData(eq(Constants.ZROOT + "/" +
iid.canonical() + "/root_tablet"),
capture(byteCapture), eq(123))).andReturn(true).once();
- zrw.recursiveDelete("/accumulo/" + iid.canonical() + "/tracers",
- ZooUtil.NodeMissingPolicy.SKIP);
- expectLastCall().once();
+ expect(zrw.getData(eq(Constants.ZROOT + "/" + iid.canonical() +
Constants.ZNAMESPACES)))
+ .andReturn(new byte[0]).once();
+ Map<String,String> mockNamespaces = Map.of("ns1", "ns1name", "ns2",
"ns2name");
+ expect(zrw.getChildren(eq(Constants.ZROOT + "/" + iid.canonical() +
Constants.ZNAMESPACES)))
+ .andReturn(List.copyOf(mockNamespaces.keySet())).once();
+ for (String ns : mockNamespaces.keySet()) {
+ Supplier<String> pathMatcher = () -> eq(Constants.ZROOT + "/" +
iid.canonical()
+ + Constants.ZNAMESPACES + "/" + ns + ZNAMESPACE_NAME);
+
expect(zrw.getData(pathMatcher.get())).andReturn(mockNamespaces.get(ns).getBytes(UTF_8))
+ .once();
+ }
+ byte[] mapping = NamespaceMapping.serialize(mockNamespaces);
+ expect(
+ zrw.putPersistentData(eq(Constants.ZROOT + "/" + iid.canonical() +
Constants.ZNAMESPACES),
+ aryEq(mapping), eq(ZooUtil.NodeExistsPolicy.OVERWRITE)))
+ .andReturn(true).once();
+ for (String ns : mockNamespaces.keySet()) {
+ Supplier<String> pathMatcher = () -> eq(Constants.ZROOT + "/" +
iid.canonical()
+ + Constants.ZNAMESPACES + "/" + ns + ZNAMESPACE_NAME);
+ zrw.delete(pathMatcher.get());
+ expectLastCall().once();
+ }
replay(context, zrw);