tryRecordAttempt method
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;
}