Skip to content

Commit 87bd6fb

Browse files
Update adaptive advert limiter stats to include limit hit count and last limit hit age
1 parent 55ccab0 commit 87bd6fb

4 files changed

Lines changed: 39 additions & 14 deletions

File tree

docs/cli_commands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore
144144

145145
---
146146

147-
### Advert Limiter Stats - Limit, Remaining, Denied, Load Average and Last Limit Reached Time (Repeater Only)
147+
### Advert Limiter Stats - Limit, Remaining, Denied, Load Average, Limit Hit Count and Last Limit Hit Age (Repeater Only)
148148
**Usage:** `stats-advert-ratelimit`
149149

150150
**Serial Only:** No

examples/simple_repeater/MyMesh.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,9 +1258,9 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
12581258
}
12591259
} else if (memcmp(command, "stats-advert-ratelimit", 22) == 0 && (command[22] == 0 || command[22] == ' ')) {
12601260
AdaptiveRateLimiterStats stats = advert_limiter.stats(rtc_clock.getCurrentTime());
1261-
sprintf(reply, "{\"limit\":%u,\"remaining\":%u,\"denied\":%u,\"load_avg\":%u,\"last_limit_reached_at\":%lu}",
1261+
sprintf(reply, "{\"limit\":%u,\"remaining\":%u,\"denied\":%u,\"load_avg\":%u,\"limit_reached\":%u,\"last_limit_reached_ago\":%lu}",
12621262
(unsigned)stats.limit, (unsigned)stats.remaining, (unsigned)stats.denied,
1263-
(unsigned)stats.load_avg, (unsigned long)stats.last_limit_reached_at);
1263+
(unsigned)stats.load_avg, (unsigned)stats.limit_reached, (unsigned long)stats.last_limit_reached_ago);
12641264
} else{
12651265
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
12661266
}

examples/simple_repeater/RateLimiter.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ struct AdaptiveRateLimiterStats {
3232
uint8_t remaining;
3333
uint8_t denied;
3434
uint8_t load_avg;
35-
uint32_t last_limit_reached_at;
35+
uint16_t limit_reached;
36+
uint32_t last_limit_reached_ago;
3637
};
3738

3839
class AdaptiveRateLimiter {
@@ -54,6 +55,7 @@ class AdaptiveRateLimiter {
5455
uint8_t _burst_multiplier;
5556
uint8_t _min_limit;
5657
uint8_t _denied;
58+
uint16_t _limit_reached;
5759
uint32_t _last_limit_reached_at;
5860

5961
static uint8_t clampU8(uint16_t v) { return v > 255 ? 255 : (uint8_t)v; }
@@ -99,7 +101,7 @@ class AdaptiveRateLimiter {
99101
public:
100102
AdaptiveRateLimiter(uint16_t window_secs, uint8_t burst_multiplier, uint8_t min_limit)
101103
: _window_start(0), _window_secs(window_secs), _window_count(0), _window_limit(min_limit), _load_avg(min_limit),
102-
_burst_multiplier(burst_multiplier), _min_limit(min_limit), _denied(0), _last_limit_reached_at(0) {}
104+
_burst_multiplier(burst_multiplier), _min_limit(min_limit), _denied(0), _limit_reached(0), _last_limit_reached_at(0) {}
103105

104106
bool allow(uint32_t now) {
105107
advanceWindow(now);
@@ -111,20 +113,24 @@ class AdaptiveRateLimiter {
111113

112114
_window_count++;
113115

114-
if (_window_count >= _window_limit)
116+
if (_window_count >= _window_limit) {
117+
if (_limit_reached < 65535) _limit_reached++;
115118
_last_limit_reached_at = now;
119+
}
116120

117121
return true;
118122
}
119123

120124
void clearStats() {
121125
_denied = 0;
126+
_limit_reached = 0;
122127
_last_limit_reached_at = 0;
123128
}
124129

125130
AdaptiveRateLimiterStats stats(uint32_t now) {
126131
advanceWindow(now);
127132
uint8_t remaining = (_window_count < _window_limit) ? (_window_limit - _window_count) : 0;
128-
return { _window_limit, remaining, _denied, _load_avg, _last_limit_reached_at };
133+
uint32_t last_limit_reached_ago = (_last_limit_reached_at == 0) ? 0 : (now - _last_limit_reached_at);
134+
return { _window_limit, remaining, _denied, _load_avg, _limit_reached, last_limit_reached_ago };
129135
}
130136
};

test/test_simple_repeater/test_adaptive_rate_limiter.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ TEST(AdaptiveRateLimiter, StatsInitialState) {
100100
EXPECT_EQ(5, stats.remaining);
101101
EXPECT_EQ(0, stats.denied);
102102
EXPECT_EQ(5, stats.load_avg);
103-
EXPECT_EQ(0, stats.last_limit_reached_at);
103+
EXPECT_EQ(0, stats.limit_reached);
104+
EXPECT_EQ(0, stats.last_limit_reached_ago);
104105
}
105106

106107
TEST(AdaptiveRateLimiter, StatsRemainingDecreasesOnAllow) {
@@ -114,7 +115,7 @@ TEST(AdaptiveRateLimiter, StatsRemainingDecreasesOnAllow) {
114115
EXPECT_EQ(0, stats.denied);
115116
}
116117

117-
TEST(AdaptiveRateLimiter, StatsRecordsDeniedAndLastLimitReachedAt) {
118+
TEST(AdaptiveRateLimiter, StatsRecordsDeniedAndLastLimitReachedAge) {
118119
AdaptiveRateLimiter limiter(10, 3, 2);
119120

120121
EXPECT_TRUE(limiter.allow(1));
@@ -126,10 +127,11 @@ TEST(AdaptiveRateLimiter, StatsRecordsDeniedAndLastLimitReachedAt) {
126127

127128
EXPECT_EQ(0, stats.remaining);
128129
EXPECT_EQ(2, stats.denied);
129-
EXPECT_EQ(1, stats.last_limit_reached_at);
130+
EXPECT_EQ(1, stats.limit_reached);
131+
EXPECT_EQ(2, stats.last_limit_reached_ago);
130132
}
131133

132-
TEST(AdaptiveRateLimiter, StatsDeniedResetsOnWindowRollover) {
134+
TEST(AdaptiveRateLimiter, StatsDeniedResetsOnWindowRolloverAndKeepsLimitReachedAge) {
133135
AdaptiveRateLimiter limiter(10, 3, 2);
134136

135137
EXPECT_TRUE(limiter.allow(1));
@@ -139,7 +141,22 @@ TEST(AdaptiveRateLimiter, StatsDeniedResetsOnWindowRollover) {
139141
const AdaptiveRateLimiterStats stats = limiter.stats(10);
140142

141143
EXPECT_EQ(0, stats.denied);
142-
EXPECT_EQ(1, stats.last_limit_reached_at);
144+
EXPECT_EQ(1, stats.limit_reached);
145+
EXPECT_EQ(9, stats.last_limit_reached_ago);
146+
}
147+
148+
TEST(AdaptiveRateLimiter, StatsLimitReachedCountsEachWindowHit) {
149+
AdaptiveRateLimiter limiter(10, 1, 1);
150+
151+
EXPECT_TRUE(limiter.allow(1));
152+
EXPECT_FALSE(limiter.allow(1));
153+
154+
EXPECT_TRUE(limiter.allow(10));
155+
EXPECT_FALSE(limiter.allow(10));
156+
157+
const AdaptiveRateLimiterStats stats = limiter.stats(10);
158+
159+
EXPECT_EQ(2, stats.limit_reached);
143160
}
144161

145162
TEST(AdaptiveRateLimiter, StatsDeniedSaturatesAt255) {
@@ -166,7 +183,8 @@ TEST(AdaptiveRateLimiter, ClearStatsOnlyClearsReportingCounters) {
166183

167184
EXPECT_EQ(0, beforeClear.remaining);
168185
EXPECT_EQ(1, beforeClear.denied);
169-
EXPECT_EQ(1, beforeClear.last_limit_reached_at);
186+
EXPECT_EQ(1, beforeClear.limit_reached);
187+
EXPECT_EQ(1, beforeClear.last_limit_reached_ago);
170188

171189
limiter.clearStats();
172190
const AdaptiveRateLimiterStats afterClear = limiter.stats(2);
@@ -175,7 +193,8 @@ TEST(AdaptiveRateLimiter, ClearStatsOnlyClearsReportingCounters) {
175193
EXPECT_EQ(0, afterClear.remaining);
176194
EXPECT_EQ(0, afterClear.denied);
177195
EXPECT_EQ(2, afterClear.load_avg);
178-
EXPECT_EQ(0, afterClear.last_limit_reached_at);
196+
EXPECT_EQ(0, afterClear.limit_reached);
197+
EXPECT_EQ(0, afterClear.last_limit_reached_ago);
179198
EXPECT_FALSE(limiter.allow(2));
180199
}
181200

0 commit comments

Comments
 (0)