You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kw...@apache.org on 2018/01/26 13:39:36 UTC
qpid-broker-j git commit: QPID-8086: [BDB HA] Standalone tool to
delete orphan configuration records from a BDB JE database - for exceptional
use only
Repository: qpid-broker-j
Updated Branches:
refs/heads/master 8ca74c59a -> 557fe9c1c
QPID-8086: [BDB HA] Standalone tool to delete orphan configuration records from a BDB JE database - for exceptional use only
Project: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/commit/557fe9c1
Tree: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/tree/557fe9c1
Diff: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/diff/557fe9c1
Branch: refs/heads/master
Commit: 557fe9c1c8245ad447ae7722d2631586cb358f78
Parents: 8ca74c5
Author: Keith Wall <kw...@apache.org>
Authored: Fri Jan 26 10:53:38 2018 +0000
Committer: Keith Wall <kw...@apache.org>
Committed: Fri Jan 26 13:39:18 2018 +0000
----------------------------------------------------------------------
.../OrphanConfigurationRecordPurger.java | 351 +++++++++++++++++++
1 file changed, 351 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/557fe9c1/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/OrphanConfigurationRecordPurger.java
----------------------------------------------------------------------
diff --git a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/OrphanConfigurationRecordPurger.java b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/OrphanConfigurationRecordPurger.java
new file mode 100644
index 0000000..78774ab
--- /dev/null
+++ b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/OrphanConfigurationRecordPurger.java
@@ -0,0 +1,351 @@
+/*
+ *
+ * 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
+ *
+ * http://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.qpid.server.store.berkeleydb;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import com.sleepycat.bind.tuple.IntegerBinding;
+import com.sleepycat.bind.tuple.TupleBase;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.Cursor;
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+import com.sleepycat.je.Transaction;
+import com.sleepycat.je.TransactionConfig;
+import com.sleepycat.je.rep.ReplicatedEnvironment;
+import com.sleepycat.je.rep.ReplicationConfig;
+
+/**
+ * Standalone tool to remove one or more configuration records from a BDB store.
+ * Intended for exceptional use only.
+ *
+ * If targeting a BDB HA store, then it is important to establish which node was
+ * most recently master and perform the update there.
+ */
+public class OrphanConfigurationRecordPurger
+{
+ private static final String USAGE_STRING =
+ "usage: " + (String.format("java %s\n"
+ + " -dryRun # Dry run mode\n"
+ + " -storePath <dir> # Store path\n"
+ + " [-ha # HA mode\n"
+ + " -nodeName <nodename> # HA node name\n"
+ + " -nodeHost <nodehost> # HA node host\n"
+ + " -groupName <groupName>] # HA group name\n"
+ + " -targetUuid <uuid1> # UUID to delete\n"
+ + " [-targetUuid <uuid2>...] # UUID to delete\n",
+ OrphanConfigurationRecordPurger.class.getName()));
+
+ private static final String VERSION_DB_NAME = "DB_VERSION";
+
+ private static final String CONFIGURED_OBJECTS_DB_NAME = "CONFIGURED_OBJECTS";
+ private static final String CONFIGURED_OBJECT_HIERARCHY_DB_NAME = "CONFIGURED_OBJECT_HIERARCHY";
+
+ private static final Set<Integer> ALLOWED_VERSIONS = new HashSet<>(Arrays.asList(8, 9));
+ private static final DatabaseConfig READ_ONLY_DB_CONFIG = DatabaseConfig.DEFAULT.setAllowCreate(false).setReadOnly(true).setTransactional(true);
+ private static final DatabaseConfig READ_WRITE_DB_CONFIG = READ_ONLY_DB_CONFIG.setReadOnly(false);
+
+ private String _storePath;
+ private Set<UUID> _uuids = new HashSet<>();
+ private boolean _dryRun;
+ private boolean _ha;
+ private String _nodeName;
+ private String _nodeHost;
+ private String _groupName;
+
+ public static void main(String[] argv) throws Exception
+ {
+ final OrphanConfigurationRecordPurger purger = new OrphanConfigurationRecordPurger();
+ purger.parseArgs(argv);
+ purger.purge();
+ }
+
+ private void purge() throws Exception
+ {
+ EnvironmentConfig config = EnvironmentConfig.DEFAULT;
+ config.setAllowCreate(false);
+ config.setTransactional(true);
+
+ try (Environment env = createEnvironment(config))
+ {
+ final int version = getVersion(env, READ_ONLY_DB_CONFIG);
+ if (!ALLOWED_VERSIONS.contains(version))
+ {
+ throw new IllegalStateException(String.format("Store has unexpected version. Found %d expected %s",
+ version,
+ ALLOWED_VERSIONS));
+ }
+
+ final Transaction tx = env.beginTransaction(null,
+ TransactionConfig.DEFAULT.setReadOnly(_dryRun));
+ boolean success = false;
+ int configChanges = 0;
+ try
+ {
+ for (final UUID uuid : _uuids)
+ {
+ configChanges += purgeOrphans(env, tx, uuid);
+ }
+
+ success = true;
+ }
+ finally
+ {
+ if (!_dryRun && success && configChanges > 0)
+ {
+ tx.commit();
+ System.out.format("%d config records(s) and associated hierarchy records purged.", configChanges);
+ }
+ else
+ {
+ System.out.println("No config or config hierarchy records purged.");
+ tx.abort();
+ }
+ }
+ }
+ }
+
+ private Environment createEnvironment(final EnvironmentConfig config) throws Exception
+ {
+ final Environment env;
+ if (_ha)
+ {
+ final ReplicationConfig repConfig = (ReplicationConfig) ReplicationConfig.DEFAULT
+ .setNodeHostPort(_nodeHost)
+ .setGroupName(_groupName)
+ .setNodeName(_nodeName)
+ .setDesignatedPrimary(true)
+ .setElectableGroupSizeOverride(1);
+
+ env = new ReplicatedEnvironment(new File(_storePath), repConfig, config);
+ }
+ else
+ {
+ env = new Environment(new File(_storePath), config);
+ }
+ return env;
+ }
+
+ private int getVersion(final Environment env, final DatabaseConfig dbConfig)
+ {
+ try (Database versionDb = env.openDatabase(null, VERSION_DB_NAME, dbConfig);
+ Cursor cursor = versionDb.openCursor(null, null))
+ {
+
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+
+ int version = 0;
+
+ while (cursor.getNext(key, value, null) == OperationStatus.SUCCESS)
+ {
+ int ver = IntegerBinding.entryToInt(key);
+ if (ver > version)
+ {
+ version = ver;
+ }
+ }
+
+ return version;
+ }
+ }
+
+ private int purgeOrphans(Environment env, final Transaction tx, UUID uuid) throws Exception
+ {
+ try(Database configDb = env.openDatabase(tx, CONFIGURED_OBJECTS_DB_NAME, READ_WRITE_DB_CONFIG))
+ {
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+
+ TupleOutput output = new TupleOutput();
+ output.writeLong(uuid.getMostSignificantBits());
+ output.writeLong(uuid.getLeastSignificantBits());
+ TupleBase.outputToEntry(output, key);
+
+ OperationStatus status =
+ _dryRun ? configDb.get(tx, key, value, LockMode.DEFAULT) : configDb.delete(tx, key);
+
+ if (status == OperationStatus.SUCCESS)
+ {
+ System.out.format("Config record for UUID %s found\n", uuid);
+
+ try (Database hierarchyDb = env.openDatabase(null, CONFIGURED_OBJECT_HIERARCHY_DB_NAME,
+ READ_WRITE_DB_CONFIG);
+ Cursor hierarchyCursor = hierarchyDb.openCursor(tx, null))
+ {
+
+ DatabaseEntry hkey = new DatabaseEntry();
+ DatabaseEntry hvalue = new DatabaseEntry();
+
+ int count = 0;
+ while (hierarchyCursor.getNext(hkey, hvalue, LockMode.DEFAULT) == OperationStatus.SUCCESS)
+ {
+ TupleInput dis = new TupleInput(hkey.getData());
+ final long mostSigBits = dis.readLong();
+ final long leastSigBits = dis.readLong();
+ final UUID recId = new UUID(mostSigBits, leastSigBits);
+ if (recId.equals(uuid))
+ {
+ if (!_dryRun)
+ {
+ hierarchyCursor.delete();
+ }
+ count++;
+ }
+ }
+
+ System.out.format("%d config hierarchy record(s) found\n", count);
+ return 1;
+ }
+ }
+ else
+ {
+ System.out.format("Config object record for UUID %s NOT found\n", uuid);
+ return 0;
+ }
+ }
+ }
+
+ private void parseArgs(final String[] argv)
+ {
+ final int argCount = argv.length;
+
+ if (argCount == 0)
+ {
+ printUsage(null);
+ }
+
+ int argc = 0;
+ while (argc < argCount)
+ {
+ String thisArg = argv[argc++];
+ switch (thisArg)
+ {
+ case "-storePath":
+ if (argc < argCount)
+ {
+ _storePath = argv[argc++];
+ }
+ else
+ {
+ printUsage("-storePath requires an argument");
+ }
+ break;
+ case "-targetUuid":
+ if (argc < argCount)
+ {
+ String uuid = argv[argc++];
+ _uuids.add(UUID.fromString(uuid));
+ }
+ else
+ {
+ printUsage("-targetUuid requires an argument");
+ }
+ break;
+ case "-ha":
+ _ha = true;
+ break;
+ case "-nodeName":
+ if (argc < argCount)
+ {
+ _nodeName = argv[argc++];
+ }
+ else
+ {
+ printUsage("-nodeName requires an argument");
+ }
+ break;
+ case "-nodeHost":
+ if (argc < argCount)
+ {
+ _nodeHost = argv[argc++];
+ }
+ else
+ {
+ printUsage("-nodeHost requires an argument");
+ }
+ break;
+ case "-groupName":
+ if (argc < argCount)
+ {
+ _groupName = argv[argc++];
+ }
+ else
+ {
+ printUsage("-groupName requires an argument");
+ }
+ break;
+ case "-dryRun":
+ _dryRun = true;
+ break;
+ default:
+ printUsage(thisArg + " is not a valid argument");
+ break;
+ }
+ }
+
+ if (_storePath == null)
+ {
+ printUsage("-storePath is a required argument");
+ }
+ if (_uuids.isEmpty())
+ {
+ printUsage("-targetUuid is a required argument");
+ }
+ if (_ha)
+ {
+ if (_nodeName == null)
+ {
+ printUsage("-nodeName is a required argument when in ha mode");
+ }
+ if (_nodeHost == null)
+ {
+ printUsage("-nodeHost is a required argument when in ha mode");
+ }
+ if (_groupName == null)
+ {
+ printUsage("-groupName is a required argument when in ha mode");
+ }
+ }
+ }
+
+ private void printUsage(String msg)
+ {
+ if (msg != null)
+ {
+ System.err.println(msg);
+ }
+
+ System.err.println(USAGE_STRING);
+ System.exit(-1);
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org