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

mendersoftware / mender-server / 1897784243

30 Jun 2025 11:50AM UTC coverage: 65.64% (-0.09%) from 65.731%
1897784243

Pull #769

gitlab-ci

alfrunes
chore(deviceauth): Add missing config env key replacer

The replacer did not exist because there were no prior configuration
settings with a period or dash in the key name.

Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #769: MEN-7744: Rate limits for authenticated requests

181 of 332 new or added lines in 9 files covered. (54.52%)

1 existing line in 1 file now uncovered.

32539 of 49572 relevant lines covered (65.64%)

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,
NEW
19
) *FixedWindowRateLimiter {
×
NEW
20
        return &FixedWindowRateLimiter{
×
NEW
21
                client:    client,
×
NEW
22
                nowFunc:   time.Now,
×
NEW
23
                keyPrefix: keyPrefix,
×
NEW
24
                interval:  interval,
×
NEW
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
type simpleReservation struct {
46
        ok     bool
47
        tokens int64
48
        delay  time.Duration
49
}
50

51
func (r *simpleReservation) OK() bool {
×
52
        return r.ok
×
53
}
×
54

55
func (r *simpleReservation) Delay() time.Duration {
×
56
        return r.delay
×
57
}
×
58

NEW
59
func (r *simpleReservation) Tokens() int64 {
×
60
        return r.tokens
×
61
}
×
62

63
func epoch(t time.Time, interval time.Duration) int64 {
×
64
        return t.UnixMilli() / interval.Milliseconds()
×
65
}
×
66

67
func fixedWindowKey(prefix, eventID string, epoch int64) string {
×
68
        if prefix == "" {
×
69
                prefix = "ratelimit"
×
70
        }
×
NEW
71
        if eventID == "" {
×
NEW
72
                return fmt.Sprintf("%s:e:%d:c", prefix, epoch)
×
NEW
73
        } else {
×
NEW
74
                return fmt.Sprintf("%s:%s:e:%d:c", prefix, eventID, epoch)
×
NEW
75
        }
×
76
}
77

78
func (rl *FixedWindowRateLimiter) ReserveEvent(
79
        ctx context.Context,
80
        eventID string,
NEW
81
) (rate.Reservation, error) {
×
82
        now := rl.nowFunc()
×
83
        epoch := epoch(now, rl.interval)
×
84
        key := fixedWindowKey(rl.keyPrefix, eventID, epoch)
×
NEW
85
        count := int64(1)
×
86

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

NEW
112
func (rl *FixedWindowRateLimiter) Reserve(ctx context.Context) (rate.Reservation, error) {
×
NEW
113
        return rl.ReserveEvent(ctx, "")
×
UNCOV
114
}
×
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