def METHOD_NAME(key_name,
                 max_hold_seconds=DEFAULT_MAX_HOLD_SECONDS,
                 retries=None,
                 by_zone=True):
  """Acquire a lock for the given key name. Returns the expiration time if
  succeeded, otherwise None. The lock holder is responsible for making sure it
  doesn't assume the lock is still held after the expiration time."""
  logs.log('Acquiring lock for %s.' % key_name)
  failed_acquires = 0
  total_wait = 0
  wait_exponent = 1
  if by_zone:
    key_name_with_zone = _get_key_name_with_lock_zone(key_name)
    if key_name_with_zone is None:
      logs.log_error('Failed to get zone while trying to lock %s.' % key_name)
      return None
    key_name = key_name_with_zone
  bot_name = environment.get_value('BOT_NAME')
  expiration_delta = datetime.timedelta(seconds=max_hold_seconds)
  while total_wait < LOCK_CHECK_TIMEOUT:
    try:
      lock_entity = ndb.transaction(
          lambda: _try_acquire_lock(key_name,
                                    expiration_time=datetime.datetime.utcnow() +
                                    expiration_delta, holder=bot_name),
          retries=TRANSACTION_RETRIES)
      if lock_entity.holder == bot_name:
        logs.log('Got the lock.')
        return lock_entity.expiration_time
    except exceptions.Error:
      pass
    failed_acquires += 1
    if retries and retries >= failed_acquires:
      logs.log('Failed to acquire lock, exceeded max retries.')
      return None
    logs.log('Failed to acquire lock, waiting...')
    # Exponential backoff.
    max_sleep = (1 << wait_exponent) * LOCK_CHECK_SLEEP_MULTIPLIER
    sleep_time = random.uniform(1.0, max_sleep)
    time.sleep(sleep_time)
    total_wait += sleep_time
    wait_exponent = min(wait_exponent + 1, MAX_WAIT_EXPONENT)
  logs.log('Timeout exceeded while trying to acquire lock, bailing.')
  return None