mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-11-10 14:44:04 +08:00
Add limit.Service.Check, a bucket check that uses nothing but reports the wait
This commit is contained in:
1 parent
e04b01af6d
commit
b916f216fc
2 files changed
+43
No files matched your search
@@ -187,6 +187,29 @@ func (s *Service) Tokens(key string, rule Rule) int64 {
|
||||
return int64(math.Max(0, math.Floor(b.tokens)))
|
||||
}
|
||||
|
||||
// Check decides like Allow whether the key's bucket for the rule has one unit now, without using
|
||||
// it. Like Tokens, a rule without a bucket admits nothing. It is for "may I try" checks that charge
|
||||
// only on failure, and still need to tell the client how long to wait.
|
||||
func (s *Service) Check(key string, rule Rule) Decision {
|
||||
if rule.Burst <= 0 {
|
||||
return Decision{}
|
||||
}
|
||||
now := s.now()
|
||||
bs := s.buckets[shardIndex(key)]
|
||||
bs.mu.Lock()
|
||||
defer bs.mu.Unlock()
|
||||
e, ok := bs.entries[key]
|
||||
if !ok {
|
||||
return Decision{OK: true} // A fresh bucket is full
|
||||
}
|
||||
b := &e.buckets[rule.Kind]
|
||||
b.refill(now, &rule)
|
||||
if b.tokens < 1 {
|
||||
return Decision{RetryAfter: b.wait(1, &rule)}
|
||||
}
|
||||
return Decision{OK: true}
|
||||
}
|
||||
|
||||
// Refund hands n units of the kind back to the key, e.g. for bytes that were reserved but not
|
||||
// sent. The bucket cannot exceed its burst, and the daily count cannot go below zero.
|
||||
func (s *Service) Refund(key string, kind Kind, n int64) {
|
||||
|
||||
@@ -271,6 +271,26 @@ func TestService_Tokens_DoesNotConsume(t *testing.T) {
|
||||
require.Equal(t, int64(0), s.Tokens("k", Rule{Kind: KindAuth})) // No bucket, no tokens
|
||||
}
|
||||
|
||||
func TestService_Check(t *testing.T) {
|
||||
// Check answers like Allow would for one unit, including the wait, but uses nothing
|
||||
s, clock := newTestServiceWithClock(t)
|
||||
rule := Rule{
|
||||
Kind: KindAuth,
|
||||
Burst: 2,
|
||||
Refill: time.Minute,
|
||||
}
|
||||
require.Equal(t, Decision{OK: true}, s.Check("k", rule)) // A fresh bucket is full
|
||||
require.True(t, s.Allow("k", rule, 2).OK)
|
||||
require.Equal(t, Decision{RetryAfter: time.Minute}, s.Check("k", rule))
|
||||
clock.advance(20 * time.Second)
|
||||
require.Equal(t, Decision{RetryAfter: 40 * time.Second}, s.Check("k", rule))
|
||||
require.Equal(t, int64(0), s.Tokens("k", rule)) // Checking did not take anything
|
||||
clock.advance(40 * time.Second)
|
||||
require.Equal(t, Decision{OK: true}, s.Check("k", rule))
|
||||
require.Equal(t, int64(1), s.Tokens("k", rule))
|
||||
require.Equal(t, Decision{}, s.Check("k", Rule{Kind: KindAuth})) // No bucket admits nothing, as Tokens says 0
|
||||
}
|
||||
|
||||
func TestService_Prune(t *testing.T) {
|
||||
s, clock := newTestServiceWithClock(t)
|
||||
rule := Rule{
|
||||
|
||||
Reference in new issue
Block a user