docs: public VoltTable[] run(SystemProcedureExecutionContext ctx,
                             String catalogDiffCommands,
                             int expectedCatalogVersion,
                             long genId,
                             byte[] catalogBytes,
                             byte[] catalogHash,
                             byte[] deploymentBytes,
                             byte[] deploymentHash,
                             byte worksWithElastic,
                             String[] tablesThatMustBeEmpty,
                             String[] reasonsForEmptyTables,
                             byte requiresSnapshotIsolation,
                             byte requireCatalogDiffCmdsApplyToEE,
                             byte hasSchemaChange,
                             byte requiresNewExportGeneration,
                             byte hasSecurityUserChange)
                                     throws Exception
      {
          assert(tablesThatMustBeEmpty != null);
          ZooKeeper zk = VoltDB.instance().getHostMessenger().getZK();
          long start, duration = 0;
  
          if (worksWithElastic == 0 && VoltZK.zkNodeExists(zk, VoltZK.elasticOperationInProgress)) {
              throw new VoltAbortException("Can't do a catalog update while an elastic join is active. Please retry catalog update later.");
          }
          if (requiresSnapshotIsolation == 1 && VoltZK.hasHostsSnapshotting(zk)) {
              throw new VoltAbortException("Snapshot in progress. Please retry catalog update later.");
          }
          final CatalogContext context = VoltDB.instance().getCatalogContext();
          if (context.catalogVersion == expectedCatalogVersion) {
              if (context.checkMismatchedPreparedCatalog(catalogHash, deploymentHash)) {
                  throw new VoltAbortException("Concurrent catalog update detected, abort the current one");
              }
          } else {
              if (context.catalogVersion == (expectedCatalogVersion + 1) &&
                  Arrays.equals(context.getCatalogHash(), catalogHash) &&
                  Arrays.equals(context.getDeploymentHash(), deploymentHash)) {
                  log.info("Restarting catalog update");
              } else {
                  // impossible to happen since we only allow catalog update sequentially
                  String errMsg = "Invalid catalog update.  Catalog or deployment change was planned " +
                          "against one version of the cluster configuration but that version was " +
                          "no longer live when attempting to apply the change.  This is likely " +
                          "the result of multiple concurrent attempts to change the cluster " +
                          "configuration.  Please make such changes synchronously from a single " +
                          "connection to the cluster.";
                  log.warn(errMsg);
                  throw new VoltAbortException(errMsg);
              }
          }
  
          // log the start of UpdateCore
          log.info("New catalog update from: " + VoltDB.instance().getCatalogContext().getCatalogLogString());
          log.info("To: catalog hash: " + Encoder.hexEncode(catalogHash).substring(0, 10) +
                  ", deployment hash: " + Encoder.hexEncode(deploymentHash).substring(0, 10));
  
          start = System.nanoTime();
  
          try {
              performCatalogVerifyWork(
                      tablesThatMustBeEmpty,
                      reasonsForEmptyTables);
          }
          catch (VoltAbortException vae) {
              log.info("Catalog verification failed: " + vae.getMessage());
              throw vae;
          }
  
          try {
              CatalogUtil.updateCatalogToZK(zk, expectedCatalogVersion + 1, genId,
                      catalogBytes, catalogHash, deploymentBytes);
          } catch (KeeperException | InterruptedException e) {
              log.error("error writing catalog bytes on ZK during @UpdateCore");
              throw e;
          }
  
          performCatalogUpdateWork(
                  catalogDiffCommands,
                  expectedCatalogVersion,
                  requiresSnapshotIsolation,
                  requireCatalogDiffCmdsApplyToEE,
                  hasSchemaChange,
                  requiresNewExportGeneration,
                  genId,
                  hasSecurityUserChange);
  
          duration = System.nanoTime() - start;
  
          VoltDB.instance().getCatalogContext().m_lastUpdateCoreDuration = duration;
          log.info("Catalog update block time (milliseconds): " + duration * 1.0 / 1000 / 1000);
  
          // This is when the UpdateApplicationCatalog really ends in the blocking path
          log.info(String.format("Globally updating the current application catalog and deployment " +
                                 "(new hashes %s, %s).",
                                 Encoder.hexEncode(catalogHash).substring(0, 10),
                                 Encoder.hexEncode(deploymentHash).substring(0, 10)));
  
          VoltTable result = new VoltTable(VoltSystemProcedure.STATUS_SCHEMA);
          result.addRow(VoltSystemProcedure.STATUS_OK);
          return (new VoltTable[] {result});
      }