AuthRateLimitStore

actor AuthRateLimitStore

Attempt counts and lockouts for AuthRateLimiter, kept in memory. Swiftarr runs as a single instance (UserCache relies on this too), so there’s no need to share this state through Redis. A restart clears every count and lockout.

Being an actor, each call runs to completion before the next one starts, so concurrent requests each see the count left by the one before.

  • Declaration

    Swift

    private struct Bucket
  • Declaration

    Swift

    static let strikeMemory: TimeInterval
  • Declaration

    Swift

    static let sweepInterval: TimeInterval
  • Declaration

    Swift

    private var buckets: [String : Bucket]
  • Declaration

    Swift

    private var nextSweep: Date
  • now

    Declaration

    Swift

    private let now: @Sendable () -> Date
  • now is for tests that need to move the clock.

    Declaration

    Swift

    init(now: @escaping @Sendable () -> Date = { Date() })
  • Counts one attempt against key. Going over limit attempts within window seconds locks the bucket. Returns wait: 0 if the attempt may proceed, else the number of seconds the caller must wait. lockedAtCount is the attempt count if this attempt is the one that locked the bucket, nil otherwise (including attempts against an already locked bucket).

    Declaration

    Swift

    func reserve(
    	_ key: String,
    	limit: Int,
    	window: Int,
    	baseLockout: Int,
    	maxLockout: Int
    ) -> (wait: Int, lockedAtCount: Int?)
  • Takes back one attempt counted by reserve, so that successful attempts don’t count toward the limit.

    Declaration

    Swift

    func refund(_ key: String)
  • Clears a bucket’s attempt count, lockout, and lockout history.

    Declaration

    Swift

    func clear(_ key: String)
  • Clears every bucket that is currently locked out, including its lockout history. Buckets that are counting attempts but aren’t locked are left alone. Returns the number of lockouts cleared.

    Declaration

    Swift

    func clearLocked() -> Int
  • The attempts currently counted against key, for tests.

    Declaration

    Swift

    func count(_ key: String) -> Int
  • Drops buckets with nothing left in them, every few minutes, so that guesses at many different usernames don’t pile up.

    Declaration

    Swift

    private func sweep(at now: Date)