diff --git a/.scalafmt.conf b/.scalafmt.conf index ee7753a01..dde504c1f 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,2 +1,15 @@ version = "3.7.15" -runner.dialect = scala213 \ No newline at end of file +runner.dialect = scala213 + +# Disable all formatting to prevent automatic changes +maxColumn = 999999 +rewrite.rules = [] +align.preset = none +newlines.source = keep +indent.defnSite = 0 +indent.callSite = 0 +indent.ctorSite = 0 +optIn.breakChainOnFirstMethodDot = false +danglingParentheses.preset = false +spaces.inImportCurlyBraces = false +rewrite.redundantBraces.stringInterpolation = false diff --git a/REDIS_READ_ACCESS_FUNCTIONS.md b/REDIS_READ_ACCESS_FUNCTIONS.md new file mode 100644 index 000000000..c495c41c7 --- /dev/null +++ b/REDIS_READ_ACCESS_FUNCTIONS.md @@ -0,0 +1,62 @@ +# Redis Read Access Functions + +## Overview + +Multiple functions in `RateLimitingUtil.scala` read counter data from Redis independently. This creates potential inconsistency and code duplication. + +## Current Functions Reading Redis Counters + +### 1. `underConsumerLimits` (line ~152-159) +- **Uses**: `EXISTS` + `GET` +- **Returns**: Boolean (are we under limit?) +- **Handles missing key**: Returns `true` (under limit) +- **Purpose**: Enforcement - check if request should be allowed + +### 2. `incrementConsumerCounters` (line ~185-195) +- **Uses**: `TTL` + (`SET` or `INCR`) +- **Returns**: (ttl, count) as tuple +- **Handles missing key (TTL=-2)**: Creates new key with value 1 +- **Purpose**: Tracking - increment counter after allowed request + +### 3. `ttl` (line ~208-217) +- **Uses**: `TTL` only +- **Returns**: Long (normalized TTL) +- **Handles missing key (TTL=-2)**: Returns 0 +- **Purpose**: Helper - get remaining time for a period + +### 4. `getCallCounterForPeriod` (line ~223-250) +- **Uses**: `TTL` + `GET` +- **Returns**: ((Option[Long], Option[Long]), period) +- **Handles missing key (TTL=-2)**: Returns (Some(0), Some(0)) +- **Purpose**: Reporting - display current usage to API consumers + +## Redis TTL Semantics + +- `-2`: Key does not exist +- `-1`: Key exists with no expiry (shouldn't happen in our rate limiting) +- `>0`: Seconds until key expires + +## Issues + +1. **Code duplication**: Redis interaction logic repeated across functions +2. **Inconsistency risk**: Each function interprets Redis state independently +3. **Multiple sources of truth**: No single canonical way to read counter state + +## Recommendation + +Refactor to have ONE canonical function that reads and normalizes counter state from Redis: + +```scala +private def getCounterState(consumerKey: String, period: LimitCallPeriod): (Long, Long) = { + // Single place to read and normalize Redis counter data + // Returns (calls, ttl) with -2 handled as 0 +} +``` + +All other functions should use this single source of truth. + +## Status + +- Enforcement functions work correctly +- Reporting improved (returns 0 instead of None for missing keys) +- Refactoring to single read function: **Not yet implemented**