]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] rspamd_control: wire /memstat command and reply union
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 May 2026 14:27:42 +0000 (15:27 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 May 2026 14:27:42 +0000 (15:27 +0100)
Add RSPAMD_CONTROL_MEMORY_STAT to the enum, a fixed-size summary slot
in the cmd/reply unions (status, rss_kb, lua_kb, mempool_bytes,
jemalloc_allocated), the /memstat URL mapping, and the per-worker UCL
emission and totals aggregation in rspamd_control_write_reply().

The actual collector and the dispatch through default_cmd_handler are
introduced in the following commit; with this change in isolation the
command is reachable end-to-end but returns only zero summaries.

src/libserver/rspamd_control.c
src/libserver/rspamd_control.h

index cbd57ca74303726293e257878a699340dc83258b..ff56fc1bff208afa10ef34b57a68ba0870bc0a93 100644 (file)
@@ -84,6 +84,7 @@ static const struct rspamd_control_cmd_match {
        {.name = {.begin = "/fuzzystat", .len = sizeof("/fuzzystat") - 1}, .type = RSPAMD_CONTROL_FUZZY_STAT},
        {.name = {.begin = "/fuzzysync", .len = sizeof("/fuzzysync") - 1}, .type = RSPAMD_CONTROL_FUZZY_SYNC},
        {.name = {.begin = "/compositesstats", .len = sizeof("/compositesstats") - 1}, .type = RSPAMD_CONTROL_COMPOSITES_STATS},
+       {.name = {.begin = "/memstat", .len = sizeof("/memstat") - 1}, .type = RSPAMD_CONTROL_MEMORY_STAT},
 };
 
 static void rspamd_control_ignore_io_handler(int fd, short what, void *ud);
@@ -185,6 +186,9 @@ rspamd_control_write_reply(struct rspamd_control_session *session)
        unsigned int total_conns = 0;
        /* Composites stats aggregation */
        uint64_t total_checked_slow = 0, total_checked_fast = 0, total_matched = 0;
+       /* Memory stat aggregation */
+       uint64_t total_rss_kb = 0, total_lua_kb = 0, total_mempool_bytes = 0,
+                        total_jemalloc_allocated = 0;
 
        rep = ucl_object_typed_new(UCL_OBJECT);
        workers = ucl_object_typed_new(UCL_OBJECT);
@@ -294,6 +298,43 @@ rspamd_control_write_reply(struct rspamd_control_session *session)
                        total_checked_fast += elt->reply.reply.composites_stats.checked_fast;
                        total_matched += elt->reply.reply.composites_stats.matched;
                        break;
+               case RSPAMD_CONTROL_MEMORY_STAT:
+                       ucl_object_insert_key(cur,
+                                                                 ucl_object_fromint(elt->reply.reply.memory_stat.status),
+                                                                 "status", 0, false);
+                       ucl_object_insert_key(cur,
+                                                                 ucl_object_fromint(elt->reply.reply.memory_stat.rss_kb),
+                                                                 "rss_kb", 0, false);
+                       ucl_object_insert_key(cur,
+                                                                 ucl_object_fromint(elt->reply.reply.memory_stat.lua_kb),
+                                                                 "lua_kb", 0, false);
+                       ucl_object_insert_key(cur,
+                                                                 ucl_object_fromint(elt->reply.reply.memory_stat.mempool_bytes),
+                                                                 "mempool_bytes", 0, false);
+                       ucl_object_insert_key(cur,
+                                                                 ucl_object_fromint(elt->reply.reply.memory_stat.jemalloc_allocated),
+                                                                 "jemalloc_allocated", 0, false);
+
+                       if (elt->attached_fd != -1) {
+                               parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS);
+
+                               if (ucl_parser_add_fd(parser, elt->attached_fd)) {
+                                       ucl_object_insert_key(cur, ucl_parser_get_object(parser),
+                                                                                 "data", 0, false);
+                               }
+                               else {
+                                       ucl_object_insert_key(cur,
+                                                                                 ucl_object_fromstring(ucl_parser_get_error(parser)),
+                                                                                 "error", 0, false);
+                               }
+                               ucl_parser_free(parser);
+                       }
+
+                       total_rss_kb += elt->reply.reply.memory_stat.rss_kb;
+                       total_lua_kb += elt->reply.reply.memory_stat.lua_kb;
+                       total_mempool_bytes += elt->reply.reply.memory_stat.mempool_bytes;
+                       total_jemalloc_allocated += elt->reply.reply.memory_stat.jemalloc_allocated;
+                       break;
                default:
                        break;
                }
@@ -326,6 +367,18 @@ rspamd_control_write_reply(struct rspamd_control_session *session)
 
                ucl_object_insert_key(rep, cur, "total", 0, false);
        }
+       else if (session->cmd.type == RSPAMD_CONTROL_MEMORY_STAT) {
+               /* Total memory stats */
+               cur = ucl_object_typed_new(UCL_OBJECT);
+               ucl_object_insert_key(cur, ucl_object_fromint(total_rss_kb), "rss_kb", 0, false);
+               ucl_object_insert_key(cur, ucl_object_fromint(total_lua_kb), "lua_kb", 0, false);
+               ucl_object_insert_key(cur, ucl_object_fromint(total_mempool_bytes),
+                                                         "mempool_bytes", 0, false);
+               ucl_object_insert_key(cur, ucl_object_fromint(total_jemalloc_allocated),
+                                                         "jemalloc_allocated", 0, false);
+
+               ucl_object_insert_key(rep, cur, "total", 0, false);
+       }
 
        rspamd_control_send_ucl(session, rep);
        ucl_object_unref(rep);
index e55870d472f46b231f96d7b29856b1517e678725..39835d1c2157040cfc1688f408cfa51c625dc470 100644 (file)
@@ -41,6 +41,7 @@ enum rspamd_control_type {
        RSPAMD_CONTROL_COMPOSITES_STATS,
        RSPAMD_CONTROL_MULTIPATTERN_LOADED,
        RSPAMD_CONTROL_REGEXP_MAP_LOADED,
+       RSPAMD_CONTROL_MEMORY_STAT,
        RSPAMD_CONTROL_MAX
 };
 
@@ -128,6 +129,9 @@ struct rspamd_control_command {
                struct {
                        unsigned int unused;
                } composites_stats;
+               struct {
+                       unsigned int unused;
+               } memory_stat;
        } cmd;
 };
 
@@ -182,6 +186,13 @@ struct rspamd_control_reply {
                        double time_fast_mean;   /**< EMA mean time in fast path (ms) */
                        double time_fast_stddev; /**< EMA stddev time in fast path (ms) */
                } composites_stats;
+               struct {
+                       unsigned int status;
+                       uint64_t rss_kb;             /**< process resident set, KiB           */
+                       uint64_t lua_kb;             /**< Lua heap usage, KiB                 */
+                       uint64_t mempool_bytes;      /**< rspamd_mempool aggregate bytes      */
+                       uint64_t jemalloc_allocated; /**< jemalloc stats.allocated; 0 if N/A  */
+               } memory_stat;
        } reply;
 };