mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-20 07:00:05 +01:00
core: add rate-limiting helper
It can be used to perform an action like logging a message with simple rate limiting.
This commit is contained in:
parent
5f85b55f7f
commit
ff0c4346fc
3 changed files with 133 additions and 0 deletions
|
|
@ -5568,3 +5568,59 @@ out_removed:
|
||||||
feature);
|
feature);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nm_rate_limit_check():
|
||||||
|
* @rate_limit: the NMRateLimit instance
|
||||||
|
* @window_sec: the time window in seconds, between 1 and 864000 (ten days)
|
||||||
|
* @burst: the number of max allowed event occurrences in the given time
|
||||||
|
* window
|
||||||
|
*
|
||||||
|
* The function rate limits an event. Call it multiple times with the
|
||||||
|
* same @window_sec, and @burst values.
|
||||||
|
*
|
||||||
|
* Returns: TRUE if the event is allowed, FALSE if it is rate-limited
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
nm_rate_limit_check(NMRateLimit *rate_limit, gint32 window_sec, gint32 burst)
|
||||||
|
{
|
||||||
|
gint64 now;
|
||||||
|
gint64 old_ts_msec;
|
||||||
|
gint64 window_msec;
|
||||||
|
gint64 capacity;
|
||||||
|
gint64 elapsed;
|
||||||
|
|
||||||
|
nm_assert(window_sec >= 1 && window_sec <= 864000);
|
||||||
|
nm_assert(burst >= 1);
|
||||||
|
|
||||||
|
/* This implements a simple token bucket algorithm. For each millisecond,
|
||||||
|
* refill "burst" tokens. Thus, during a full time window we
|
||||||
|
* refill (window_msec * burst) tokens. Each event consumes @window_msec
|
||||||
|
* tokens. */
|
||||||
|
|
||||||
|
window_msec = (gint64) window_sec * NM_UTILS_MSEC_PER_SEC;
|
||||||
|
capacity = window_msec * (gint64) burst;
|
||||||
|
old_ts_msec = rate_limit->ts_msec;
|
||||||
|
now = nm_utils_get_monotonic_timestamp_msec();
|
||||||
|
rate_limit->ts_msec = now;
|
||||||
|
|
||||||
|
elapsed = now - old_ts_msec;
|
||||||
|
if (old_ts_msec == 0 || elapsed > window_msec) {
|
||||||
|
/* On the first call, or in case a whole window passed, (re)start with
|
||||||
|
* a full budget */
|
||||||
|
rate_limit->tokens = capacity;
|
||||||
|
} else {
|
||||||
|
rate_limit->tokens += elapsed * (gint64) burst;
|
||||||
|
rate_limit->tokens = NM_MIN(rate_limit->tokens, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Consume the tokens */
|
||||||
|
if (rate_limit->tokens >= window_msec) {
|
||||||
|
rate_limit->tokens -= window_msec;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -494,4 +494,13 @@ gid_t nm_utils_get_nm_gid(void);
|
||||||
|
|
||||||
gboolean nm_utils_connection_supported(NMConnection *connection, GError **error);
|
gboolean nm_utils_connection_supported(NMConnection *connection, GError **error);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gint64 ts_msec;
|
||||||
|
gint64 tokens;
|
||||||
|
} NMRateLimit;
|
||||||
|
|
||||||
|
gboolean nm_rate_limit_check(NMRateLimit *rate_limit, gint32 window_sec, gint32 burst);
|
||||||
|
|
||||||
#endif /* __NM_CORE_UTILS_H__ */
|
#endif /* __NM_CORE_UTILS_H__ */
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,73 @@ test_shorten_hostname(void)
|
||||||
do_test_shorten_hostname(".name1", FALSE, NULL);
|
do_test_shorten_hostname(".name1", FALSE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
typedef struct {
|
||||||
|
NMRateLimit ratelimit;
|
||||||
|
GMainLoop *loop;
|
||||||
|
GSource *source1;
|
||||||
|
GSource *source2;
|
||||||
|
guint num;
|
||||||
|
} RateLimitData;
|
||||||
|
|
||||||
|
static int
|
||||||
|
rate_limit_window_expire_cb(gpointer user_data)
|
||||||
|
{
|
||||||
|
RateLimitData *data = user_data;
|
||||||
|
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
|
||||||
|
g_assert(!nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(!nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
|
||||||
|
g_main_loop_quit(data->loop);
|
||||||
|
|
||||||
|
nm_clear_g_source_inst(&data->source1);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
rate_limit_check_cb(gpointer user_data)
|
||||||
|
{
|
||||||
|
RateLimitData *data = user_data;
|
||||||
|
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
|
||||||
|
g_assert(!nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
g_assert(!nm_rate_limit_check(&data->ratelimit, 1, 5));
|
||||||
|
|
||||||
|
nm_clear_g_source_inst(&data->source2);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_rate_limit_check(void)
|
||||||
|
{
|
||||||
|
RateLimitData data;
|
||||||
|
|
||||||
|
data = (RateLimitData) {
|
||||||
|
.loop = g_main_loop_new(NULL, FALSE),
|
||||||
|
.ratelimit = {},
|
||||||
|
.num = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
data.source1 = nm_g_timeout_add_source(1100, rate_limit_window_expire_cb, &data);
|
||||||
|
data.source2 = nm_g_timeout_add_source(10, rate_limit_check_cb, &data);
|
||||||
|
|
||||||
|
g_main_loop_run(data.loop);
|
||||||
|
g_main_loop_unref(data.loop);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
NMTST_DEFINE();
|
NMTST_DEFINE();
|
||||||
|
|
@ -272,6 +339,7 @@ main(int argc, char **argv)
|
||||||
g_test_add_func("/utils/stable_privacy", test_stable_privacy);
|
g_test_add_func("/utils/stable_privacy", test_stable_privacy);
|
||||||
g_test_add_func("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth);
|
g_test_add_func("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth);
|
||||||
g_test_add_func("/utils/shorten-hostname", test_shorten_hostname);
|
g_test_add_func("/utils/shorten-hostname", test_shorten_hostname);
|
||||||
|
g_test_add_func("/utils/rate-limit-check", test_rate_limit_check);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue