public <Result> Result executeWithRetry(SqlRetryCallProperties properties, SqlRetryFunction<Result> function) throws TException {
    Objects.requireNonNull(function, "RetryFunction<Result> cannot be null!");
    Objects.requireNonNull(properties, "RetryCallProperties cannot be null!");

    if (threadLocal.isSet() && properties.getRetryPropagation().canJoinContext()) {
      /*
        If there is a context in the ThreadLocal and we are allowed to join it, we can skip establishing a nested retry-call.
      */
      LOG.info("Already established retry-call detected, current call will join it. The passed SqlRetryCallProperties " +
          "instance will be ignored, using the original one.");
      try {
        return function.execute();
      } catch (SQLException e) {
        //Since we are in a nested call, This will be caught in the below exception handler.
        throw new UncategorizedSQLException(null, null, e);
      }
    }

    if (!properties.getRetryPropagation().canCreateContext()) {
      throw new MetaException("The current RetryPropagation mode (" + properties.getRetryPropagation().name() +
          ") allows only to join an existing retry-call, but there is no retry-call upper in the callstack!");
    }

    if (StringUtils.isEmpty(properties.getCaller())) {
      properties.withCallerId(function.toString());
    }
    properties
        .setRetryCount(retryLimit)
        .setDeadlockCount(ALLOWED_REPEATED_DEADLOCKS);
    
    try {
      if (properties.isLockInternally()) {
        databaseProduct.lockInternal();
      }
      threadLocal.set(new Object());
      return executeWithRetryInternal(properties, function);
    } finally {
      threadLocal.unset();
      if (properties.isLockInternally()) {
        databaseProduct.unlockInternal();
      }
    }
  }
Executes the passed {@link SqlRetryFunction} and automatically retries the execution in case of failure. If there is
a retry-call upper in the call hierarchy, {@link SqlRetryCallProperties#withRetryPropagation } can be used to 
control wether to join it or create a nested retry-call.
@param properties {@link SqlRetryCallProperties} instance used to fine-tune/alter the execution-mechanism.
@param function The {@link SqlRetryFunction} to execute.
@return Returns with the result of the execution of the passed {@link SqlRetryFunction}.
@param <Result> Type of the result
@throws MetaException Thrown in case of execution error.
standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/retry/SqlRetryHandler.java