Avisos de limites de taxa e violações de quota tendem a ocorrer repetidamente em curtos períodos de tempo. Ao incorporar a lógica de aviso e o estado de cooldown dentro da própria entidade, você pode evitar o gerenciamento global externo, garantindo que os avisos sejam emitidos apenas em intervalos apropriados.
Código
interface WarnableEntity { /** Timestamp do último aviso (milissegundos) */ warnedAt: number | null /** Período de cooldown do aviso (milissegundos) */ warnCooldown: number}
/** * Classe base para entidades com avisos baseados em cooldown */class CooldownWarner<T extends WarnableEntity> { constructor(protected entity: T) {}
/** * Determina se um aviso deve ser emitido */ protected shouldWarn(): boolean { const now = Date.now() if (this.entity.warnedAt === null) { return true } const elapsed = now - this.entity.warnedAt return elapsed >= this.entity.warnCooldown }
/** * Emite um aviso e atualiza warnedAt */ protected warnBy(message: string): void { if (!this.shouldWarn()) { return } console.warn(message) this.entity.warnedAt = Date.now() }}
/** * Exemplo de implementação: entidade de limite de taxa */interface RateLimitEntity extends WarnableEntity { limit: number current: number}
class RateLimiter extends CooldownWarner<RateLimitEntity> { constructor(limit: number, cooldown: number = 60_000) { super({ limit, current: 0, warnedAt: null, warnCooldown: cooldown }) }
/** * Registra uma solicitação e avisa se necessário */ record(): void { this.entity.current++
if (this.entity.current >= this.entity.limit) { this.warnBy( `Rate limit reached: ${this.entity.current}/${this.entity.limit}` ) } }
/** * Reseta o contador */ reset(): void { this.entity.current = 0 }}Uso
// Limite de 100 solicitações/min, avisar no máximo uma vez a cada 60 segundosconst limiter = new RateLimiter(100, 60_000)
// Registrar solicitaçõesfor (let i = 0; i < 150; i++) { limiter.record() // Avisa na 100ª solicitação, suprimido por 60s após isso}
// Após 1 minuto, exceder novamente acionará outro avisosetTimeout(() => { limiter.record() // Aviso é exibido novamente}, 61_000)Como Funciona
- A entidade possui os campos
warnedAtewarnCooldown shouldWarn()compara o tempo atual com o timestamp do último aviso- Retorna
truese o período de cooldown passou warnBy()verificashouldWarn()e emite um aviso apenas se a condição for atendida- Após o aviso, atualiza
warnedAtpara iniciar o cooldown
Como a lógica de aviso e o estado estão encapsulados dentro da entidade, não há necessidade de maps ou sets globais.
Benefícios
- Localidade: O estado do aviso é autocontido na entidade, sem necessidade de gerenciamento externo
- Reutilizabilidade: Qualquer entidade pode implementar
WarnableEntitypara usar este padrão - Testável: Injete tempo para testar sem mocks
- Escalável: Cada entidade mantém estado independente, amigável ao processamento concorrente
Ressalvas
A dependência direta de Date.now() significa que você precisará estender o design para injetar tempo para testes. Além disso, se as entidades forem persistidas, garanta a restauração adequada de warnedAt e a lógica de cooldown.
Aplicações
- Notificações de aviso para limites de taxa de API
- Alertas de violação de quota em sistemas de cobrança
- Notificações de saldo baixo
- Supressão de alertas de monitoramento ruidosos
- Prevenção de rajadas em logs
hsb.horse