private String createSequential(String path, byte[] data, List<ACL> acl, CreateMode createMode)
      throws KeeperException, InterruptedException {
    RetryCounter retryCounter = retryCounterFactory.create();
    boolean first = true;
    String newPath = path + this.identifier;
    while (true) {
      try {
        if (!first) {
          // Check if we succeeded on a previous attempt
          String previousResult = findPreviousSequentialNode(newPath);
          if (previousResult != null) {
            return previousResult;
          }
        }
        first = false;
        return zk.create(newPath, data, acl, createMode);
      } catch (KeeperException e) {
        switch (e.code()) {
          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "create");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
    }
  }
  private String createNonSequential(String path, byte[] data, List<ACL> acl, CreateMode createMode)
      throws KeeperException, InterruptedException {
    RetryCounter retryCounter = retryCounterFactory.create();
    boolean isRetry = false; // False for first attempt, true for all retries.
    while (true) {
      try {
        return zk.create(path, data, acl, createMode);
      } catch (KeeperException e) {
        switch (e.code()) {
          case NODEEXISTS:
            if (isRetry) {
              // If the connection was lost, there is still a possibility that
              // we have successfully created the node at our previous attempt,
              // so we read the node and compare.
              byte[] currentData = zk.getData(path, false, null);
              if (currentData != null && Bytes.compareTo(currentData, data) == 0) {
                // We successfully created a non-sequential node
                return path;
              }
              LOG.error(
                  "Node "
                      + path
                      + " already exists with "
                      + Bytes.toStringBinary(currentData)
                      + ", could not write "
                      + Bytes.toStringBinary(data));
              throw e;
            }
            LOG.info("Node " + path + " already exists and this is not a " + "retry");
            throw e;

          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "create");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
      isRetry = true;
    }
  }
  /**
   * exists is an idempotent operation. Retry before throwing exception
   *
   * @return A Stat instance
   */
  public Stat exists(String path, boolean watch) throws KeeperException, InterruptedException {
    RetryCounter retryCounter = retryCounterFactory.create();
    while (true) {
      try {
        return zk.exists(path, watch);
      } catch (KeeperException e) {
        switch (e.code()) {
          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "exists");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
    }
  }
 /**
  * setData is NOT an idempotent operation. Retry may cause BadVersion Exception Adding an
  * identifier field into the data to check whether badversion is caused by the result of previous
  * correctly setData
  *
  * @return Stat instance
  */
 public Stat setData(String path, byte[] data, int version)
     throws KeeperException, InterruptedException {
   RetryCounter retryCounter = retryCounterFactory.create();
   byte[] newData = appendMetaData(data);
   boolean isRetry = false;
   while (true) {
     try {
       return zk.setData(path, newData, version);
     } catch (KeeperException e) {
       switch (e.code()) {
         case CONNECTIONLOSS:
         case SESSIONEXPIRED:
         case OPERATIONTIMEOUT:
           retryOrThrow(retryCounter, e, "setData");
           break;
         case BADVERSION:
           if (isRetry) {
             // try to verify whether the previous setData success or not
             try {
               Stat stat = new Stat();
               byte[] revData = zk.getData(path, false, stat);
               if (Bytes.compareTo(revData, newData) == 0) {
                 // the bad version is caused by previous successful setData
                 return stat;
               }
             } catch (KeeperException keeperException) {
               // the ZK is not reliable at this moment. just throwing exception
               throw keeperException;
             }
           }
           // throw other exceptions and verified bad version exceptions
         default:
           throw e;
       }
     }
     retryCounter.sleepUntilNextRetry();
     retryCounter.useRetry();
     isRetry = true;
   }
 }
  /** Run multiple operations in a transactional manner. Retry before throwing exception */
  public List<OpResult> multi(Iterable<Op> ops) throws KeeperException, InterruptedException {
    RetryCounter retryCounter = retryCounterFactory.create();
    Iterable<Op> multiOps = prepareZKMulti(ops);
    while (true) {
      try {
        return zk.multi(multiOps);
      } catch (KeeperException e) {
        switch (e.code()) {
          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "multi");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
    }
  }
  /**
   * getChildren is an idempotent operation. Retry before throwing exception
   *
   * @return List of children znodes
   */
  public List<String> getChildren(String path, Watcher watcher)
      throws KeeperException, InterruptedException {
    RetryCounter retryCounter = retryCounterFactory.create();
    while (true) {
      try {
        return zk.getChildren(path, watcher);
      } catch (KeeperException e) {
        switch (e.code()) {
          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "getChildren");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
    }
  }
  /**
   * getData is an idemnpotent operation. Retry before throwing exception
   *
   * @return Data
   */
  public byte[] getData(String path, boolean watch, Stat stat)
      throws KeeperException, InterruptedException {
    RetryCounter retryCounter = retryCounterFactory.create();
    while (true) {
      try {
        byte[] revData = zk.getData(path, watch, stat);
        return this.removeMetaData(revData);
      } catch (KeeperException e) {
        switch (e.code()) {
          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "getData");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
    }
  }
  /**
   * delete is an idempotent operation. Retry before throwing exception. This function will not
   * throw NoNodeException if the path does not exist.
   */
  public void delete(String path, int version) throws InterruptedException, KeeperException {
    RetryCounter retryCounter = retryCounterFactory.create();
    boolean isRetry = false; // False for first attempt, true for all retries.
    while (true) {
      try {
        zk.delete(path, version);
        return;
      } catch (KeeperException e) {
        switch (e.code()) {
          case NONODE:
            if (isRetry) {
              LOG.info(
                  "Node "
                      + path
                      + " already deleted. Assuming that a "
                      + "previous attempt succeeded.");
              return;
            }
            LOG.warn("Node " + path + " already deleted, and this is not a " + "retry");
            throw e;

          case CONNECTIONLOSS:
          case SESSIONEXPIRED:
          case OPERATIONTIMEOUT:
            retryOrThrow(retryCounter, e, "delete");
            break;

          default:
            throw e;
        }
      }
      retryCounter.sleepUntilNextRetry();
      retryCounter.useRetry();
      isRetry = true;
    }
  }