private boolean canIgnoreExceptionForEmptyObject(
      IOException exceptionOnCreate, StorageResourceId resourceId, CreateObjectOptions options)
      throws IOException {
    // TODO(user): Maybe also add 409 and even 412 errors if they pop up in this use case.
    // 500 ISE and 503 Service Unavailable tend to be raised when spamming GCS with create requests:
    if (errorExtractor.rateLimited(exceptionOnCreate)
        || errorExtractor.isInternalServerError(exceptionOnCreate)) {
      // We know that this is an error that is most often associated with trying to create an empty
      // object from multiple workers at the same time. We perform the following assuming that we
      // will eventually succeed and find an existing object. This will add up to a user-defined
      // maximum delay that caller will wait to receive an exception in the case of an incorrect
      // assumption and this being a scenario other than the multiple workers racing situation.
      GoogleCloudStorageItemInfo existingInfo;
      BackOff backOff;
      int maxWaitMillis = storageOptions.getMaxWaitMillisForEmptyObjectCreation();
      if (maxWaitMillis > 0) {
        backOff = new ExponentialBackOff.Builder()
            .setMaxElapsedTimeMillis(maxWaitMillis)
            .setMaxIntervalMillis(500)
            .setInitialIntervalMillis(100)
            .setMultiplier(1.5)
            .setRandomizationFactor(0.15)
            .build();
      } else {
        backOff = BackOff.STOP_BACKOFF;
      }
      long nextSleep = 0L;
      do {
        if (nextSleep > 0) {
          try {
            sleeper.sleep(nextSleep);
          } catch (InterruptedException e) {
            // We caught an InterruptedException, we should set the interrupted bit on this thread.
            Thread.currentThread().interrupt();
            nextSleep = BackOff.STOP;
          }
        }
        existingInfo = getItemInfo(resourceId);
        nextSleep = nextSleep == BackOff.STOP ? BackOff.STOP : backOff.nextBackOffMillis();
      } while (!existingInfo.exists() && nextSleep != BackOff.STOP);

      // Compare existence, size, and metadata; for 429 errors creating an empty object,
      // we don't care about metaGeneration/contentGeneration as long as the metadata
      // matches, since we don't know for sure whether our low-level request succeeded
      // first or some other client succeeded first.
      if (existingInfo.exists() && existingInfo.getSize() == 0) {
        if (!options.getRequireMetadataMatchForEmptyObjects()) {
          return true;
        } else if (existingInfo.metadataEquals(options.getMetadata())) {
          return true;
        }
      }
    }
    return false;
  }
private boolean canIgnoreExceptionForEmptyObject ( IOException exceptionOnCreate , StorageResourceId resourceId , CreateObjectOptions options ) throws IOException { if ( errorExtractor . rateLimited ( exceptionOnCreate ) || errorExtractor . isInternalServerError ( exceptionOnCreate ) ) { GoogleCloudStorageItemInfo existingInfo ; BackOff backOff ; int maxWaitMillis = storageOptions . getMaxWaitMillisForEmptyObjectCreation ( ) ; if ( maxWaitMillis > 0 ) { backOff = new ExponentialBackOff . Builder ( ) . setMaxElapsedTimeMillis ( maxWaitMillis ) . setMaxIntervalMillis ( 500 ) . setInitialIntervalMillis ( 100 ) . setMultiplier ( 1.5 ) . setRandomizationFactor ( 0.15 ) . build ( ) ; } else { backOff = BackOff . STOP_BACKOFF ; } long nextSleep = 0L ; do { if ( nextSleep > 0 ) { try { sleeper . sleep ( nextSleep ) ; } catch ( InterruptedException e ) { Thread . currentThread ( ) . interrupt ( ) ; nextSleep = BackOff . STOP ; } } existingInfo = getItemInfo ( resourceId ) ; nextSleep = nextSleep == BackOff . STOP ? BackOff . STOP : backOff . nextBackOffMillis ( ) ; } while ( ! existingInfo . exists ( ) && nextSleep != BackOff . STOP ) ; if ( existingInfo . exists ( ) && existingInfo . getSize ( ) == 0 ) { if ( ! options . getRequireMetadataMatchForEmptyObjects ( ) ) { return true ; } else if ( existingInfo . metadataEquals ( options . getMetadata ( ) ) ) { return true ; } } } return false ; }
Helper to check whether an empty object already exists with the expected metadata specified
in {@code options}, to be used to determine whether it's safe to ignore an exception that
was thrown when trying to create the object, {@code exceptionOnCreate}.