Problem
RedisUsageStorage.resetBillingCycle이 특정 meterId 없이 tenant 전체 billing cycle을 reset할 때 Redis KEYS를 사용합니다. 사용량 key가 많아진 운영 Redis에서 blocking scan이 발생할 수 있고, Upstash/managed Redis 환경에서는 비용과 지연이 커질 수 있습니다.
Evidence
packages/metering-core/src/libs/UsageStorage.ts:61-67는 meterId 없는 tenant-wide billing cycle reset을 공개합니다.
packages/metering-core/src/libs/RedisUsageStorage.ts:373-394는 meterId가 없을 때 usage:${tenantId}:*:${periodKey} 패턴을 만들고 Lua script 안에서 redis.call('KEYS', ARGV[1])를 호출합니다.
- 같은 저장소에는 rate limit Redis KEYS 사용을 별도 이슈로 다루는 precedent가 있어 운영 리스크가 동일합니다.
Desired Outcome
Tenant-wide reset은 Redis blocking KEYS 없이 동작해야 하며, 대량 key 환경에서도 예측 가능한 비용/지연을 가져야 합니다.
Suggested Approach
- tenant/period별 index set을 유지하고 reset 시 index를 순회합니다.
- 또는 cursor 기반
SCAN/batched delete를 지원하는 저장소 구현으로 바꿉니다.
- meterId 없는 reset의 운영 제한과 idempotency를 문서화합니다.
Acceptance Criteria
resetBillingCycle(tenantId, periodKey) 경로에서 KEYS를 사용하지 않습니다.
- 대량 key reset이 batch/cursor 또는 index 기반으로 동작합니다.
- Redis mock 또는 integration-style 테스트가 tenant-wide reset을 검증합니다.
Validation
pnpm test --filter=@croco/metering-core
- RedisUsageStorage tenant-wide reset regression test
Problem
RedisUsageStorage.resetBillingCycle이 특정 meterId 없이 tenant 전체 billing cycle을 reset할 때 RedisKEYS를 사용합니다. 사용량 key가 많아진 운영 Redis에서 blocking scan이 발생할 수 있고, Upstash/managed Redis 환경에서는 비용과 지연이 커질 수 있습니다.Evidence
packages/metering-core/src/libs/UsageStorage.ts:61-67는meterId없는 tenant-wide billing cycle reset을 공개합니다.packages/metering-core/src/libs/RedisUsageStorage.ts:373-394는meterId가 없을 때usage:${tenantId}:*:${periodKey}패턴을 만들고 Lua script 안에서redis.call('KEYS', ARGV[1])를 호출합니다.Desired Outcome
Tenant-wide reset은 Redis blocking
KEYS없이 동작해야 하며, 대량 key 환경에서도 예측 가능한 비용/지연을 가져야 합니다.Suggested Approach
SCAN/batched delete를 지원하는 저장소 구현으로 바꿉니다.Acceptance Criteria
resetBillingCycle(tenantId, periodKey)경로에서KEYS를 사용하지 않습니다.Validation
pnpm test --filter=@croco/metering-core