• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1952806084

28 Jul 2025 02:43PM UTC coverage: 65.55% (+0.004%) from 65.546%
1952806084

Pull #831

gitlab-ci

alfrunes
fix(pkg): invalid sign when computing delay for rate limits

Affects deviceauth and useradm rate limiting.

Changelog: None
Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #831: fix(pkg): invalid sign when computing delay for rate limits

0 of 11 new or added lines in 1 file covered. (0.0%)

3 existing lines in 2 files now uncovered.

32389 of 49411 relevant lines covered (65.55%)

1.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/backend/pkg/redis/ratelimit.go
1
package redis
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "time"
8

9
        "github.com/redis/go-redis/v9"
10

11
        "github.com/mendersoftware/mender-server/pkg/rate"
12
)
13

14
func NewFixedWindowRateLimiter(
15
        client Client,
16
        keyPrefix string,
17
        interval time.Duration,
18
        quota int64,
19
) *FixedWindowRateLimiter {
×
20
        return &FixedWindowRateLimiter{
×
21
                client:    client,
×
22
                nowFunc:   time.Now,
×
23
                keyPrefix: keyPrefix,
×
24
                interval:  interval,
×
25
                quota:     quota,
×
26
        }
×
27
}
×
28

29
var (
30
        _ rate.Limiter      = &FixedWindowRateLimiter{}
31
        _ rate.EventLimiter = &FixedWindowRateLimiter{}
32
)
33

34
// FixedWindowRateLimiter implements a version of the algorithm described
35
// at https://redis.io/glossary/rate-limiting/
36
type FixedWindowRateLimiter struct {
37
        client    Client
38
        nowFunc   func() time.Time
39
        keyPrefix string
40

41
        interval time.Duration
42
        quota    int64
43
}
44

45
// simpleReservation is a straight forward implementation of the
46
// rate.Reservation interface.
47
type simpleReservation struct {
48
        // tokens count the number of tokens (events) remaining after
49
        // reservation has been made.
50
        tokens int64
51
        // delay is the time the client would need to wait for a token (event)
52
        // to become available. If 0 or negative, the reservation is accepted
53
        // that is, (*simpleReservation).OK() == true.
54
        delay time.Duration
55
}
56

NEW
57
func (r simpleReservation) OK() bool {
×
NEW
58
        return r.tokens >= 0
×
UNCOV
59
}
×
60

NEW
61
func (r simpleReservation) Delay() time.Duration {
×
62
        return r.delay
×
63
}
×
64

NEW
65
func (r simpleReservation) Tokens() int64 {
×
66
        return r.tokens
×
67
}
×
68

69
func epoch(t time.Time, interval time.Duration) int64 {
×
70
        return t.UnixMilli() / interval.Milliseconds()
×
71
}
×
72

73
func fixedWindowKey(prefix, eventID string, epoch int64) string {
×
74
        if prefix == "" {
×
75
                prefix = "ratelimit"
×
76
        }
×
77
        if eventID == "" {
×
78
                return fmt.Sprintf("%s:e:%d:c", prefix, epoch)
×
79
        } else {
×
80
                return fmt.Sprintf("%s:%s:e:%d:c", prefix, eventID, epoch)
×
81
        }
×
82
}
83

84
func (rl *FixedWindowRateLimiter) ReserveEvent(
85
        ctx context.Context,
86
        eventID string,
87
) (rate.Reservation, error) {
×
88
        now := rl.nowFunc()
×
89
        epoch := epoch(now, rl.interval)
×
90
        key := fixedWindowKey(rl.keyPrefix, eventID, epoch)
×
91
        count := int64(1)
×
92

×
93
        err := rl.client.SetArgs(ctx, key, count, redis.SetArgs{
×
94
                TTL:  rl.interval,
×
95
                Mode: `NX`,
×
96
        }).Err()
×
97
        if errors.Is(err, redis.Nil) {
×
98
                count, err = rl.client.Incr(ctx, key).Result()
×
99
        }
×
100
        if err != nil {
×
101
                return nil, fmt.Errorf("redis: error computing rate limit: %w", err)
×
102
        }
×
NEW
103
        res := simpleReservation{
×
NEW
104
                tokens: rl.quota - count,
×
105
        }
×
NEW
106
        if res.tokens < 0 {
×
NEW
107
                nextEpoch := time.UnixMilli((epoch + 1) * rl.interval.Milliseconds())
×
NEW
108
                res.delay = nextEpoch.Sub(now)
×
NEW
109
        }
×
NEW
110
        return res, nil
×
111
}
112

113
func (rl *FixedWindowRateLimiter) Reserve(ctx context.Context) (rate.Reservation, error) {
×
114
        return rl.ReserveEvent(ctx, "")
×
115
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc