tryRecordAttempt method

  1. @override
Future<bool> tryRecordAttempt(
  1. Session session, {
  2. required String key,
  3. Map<String, String>? extraData,
})
override

Atomically admits and records an attempt if budget remains.

Returns true when recorded, or false when rejected. Rejected attempts are not recorded and invoke RateLimiterConfig.onRateLimitExceeded. Admitted attempts survive a rollback of the caller's transaction.

Implementation

@override
Future<bool> tryRecordAttempt(
  final Session session, {
  required final String key,
  final Map<String, String>? extraData,
}) async {
  // NOTE: The attempt counting runs in a separate transaction, so that it is
  // never rolled back with the parent transaction.
  final rateLimitExceeded = await session.db.transaction((
    final transaction,
  ) async {
    // Taken before the savepoint, so that rolling the savepoint back on a
    // rate limited attempt does not release it early.
    await _lockAttemptKey(session, key: key, transaction: transaction);

    final savePoint = await transaction.createSavepoint();
    await _recordAttempt(
      session,
      key: key,
      extraData: extraData,
      transaction: transaction,
    );

    final attemptCount = await countAttempts(
      session,
      key: key,
      transaction: transaction,
    );

    final isRateLimited = attemptCount > config.maxAttempts;

    if (isRateLimited) {
      await savePoint.rollback();
      return true;
    }

    await savePoint.release();
    return false;
  });

  if (rateLimitExceeded) {
    await config.onRateLimitExceeded?.call(session, key);
  }

  return !rateLimitExceeded;
}