summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2020-04-07 12:07:09 +0200
committerantirez <antirez@gmail.com>2020-04-07 12:07:54 +0200
commitf69876280c630d49574fa094808d3a4791c5039b (patch)
tree66cb40c3a7729a5ef801ee3e2dd39c26cbbbce20
parent9a9109431b31a46da9c45fc1fd8c91b4f7bb9dc6 (diff)
downloadredis-faster-info.tar.gz
Speedup INFO by counting client memory incrementally.faster-info
Related to #5145. Design note: clients may change type when they turn into replicas or are moved into the Pub/Sub category and so forth. Moreover the recomputation of the bytes used is problematic for obvious reasons: it changes continuously, so as a conservative way to avoid accumulating errors, each client remembers the contribution it gave to the sum, and removes it when it is freed or before updating it with the new memory usage.
-rw-r--r--src/networking.c7
-rw-r--r--src/object.c33
-rw-r--r--src/server.c25
-rw-r--r--src/server.h13
4 files changed, 52 insertions, 26 deletions
diff --git a/src/networking.c b/src/networking.c
index 85c640e34..654fda517 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -157,6 +157,8 @@ client *createClient(connection *conn) {
c->client_list_node = NULL;
c->client_tracking_redirection = 0;
c->client_tracking_prefixes = NULL;
+ c->client_cron_last_memory_usage = 0;
+ c->client_cron_last_memory_type = CLIENT_TYPE_NORMAL;
c->auth_callback = NULL;
c->auth_callback_privdata = NULL;
c->auth_module = NULL;
@@ -1160,6 +1162,11 @@ void freeClient(client *c) {
listDelNode(server.clients_to_close,ln);
}
+ /* Remove the contribution that this client gave to our
+ * incrementally computed memory usage. */
+ server.stat_clients_type_memory[c->client_cron_last_memory_type] -=
+ c->client_cron_last_memory_usage;
+
/* Release other dynamically allocated client structure fields,
* and finally release the client structure itself. */
if (c->name) decrRefCount(c->name);
diff --git a/src/object.c b/src/object.c
index 11e335afc..52d5b11f5 100644
--- a/src/object.c
+++ b/src/object.c
@@ -974,30 +974,15 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
mh->repl_backlog = mem;
mem_total += mem;
- mem = 0;
- if (listLength(server.clients)) {
- listIter li;
- listNode *ln;
- size_t mem_normal = 0, mem_slaves = 0;
-
- listRewind(server.clients,&li);
- while((ln = listNext(&li))) {
- size_t mem_curr = 0;
- client *c = listNodeValue(ln);
- int type = getClientType(c);
- mem_curr += getClientOutputBufferMemoryUsage(c);
- mem_curr += sdsAllocSize(c->querybuf);
- mem_curr += sizeof(client);
- if (type == CLIENT_TYPE_SLAVE)
- mem_slaves += mem_curr;
- else
- mem_normal += mem_curr;
- }
- mh->clients_slaves = mem_slaves;
- mh->clients_normal = mem_normal;
- mem = mem_slaves + mem_normal;
- }
- mem_total+=mem;
+ /* Computing the memory used by the clients would be O(N) if done
+ * here online. We use our values computed incrementally by
+ * clientsCronTrackClientsMemUsage(). */
+ mh->clients_slaves = server.stat_clients_type_memory[CLIENT_TYPE_SLAVE];
+ mh->clients_normal = server.stat_clients_type_memory[CLIENT_TYPE_MASTER]+
+ server.stat_clients_type_memory[CLIENT_TYPE_PUBSUB]+
+ server.stat_clients_type_memory[CLIENT_TYPE_NORMAL];
+ mem_total += mh->clients_slaves;
+ mem_total += mh->clients_normal;
mem = 0;
if (server.aof_state != AOF_OFF) {
diff --git a/src/server.c b/src/server.c
index 56feb09a3..996e0f5d2 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1593,6 +1593,28 @@ int clientsCronTrackExpansiveClients(client *c) {
return 0; /* This function never terminates the client. */
}
+/* Iterating all the clients in getMemoryOverheadData() is too slow and
+ * in turn would make the INFO command too slow. So we perform this
+ * computation incrementally and track the (not instantaneous but updated
+ * to the second) total memory used by clients using clinetsCron() in
+ * a more incremental way (depending on server.hz). */
+int clientsCronTrackClientsMemUsage(client *c) {
+ size_t mem = 0;
+ int type = getClientType(c);
+ mem += getClientOutputBufferMemoryUsage(c);
+ mem += sdsAllocSize(c->querybuf);
+ mem += sizeof(client);
+ /* Now that we have the memory used by the client, remove the old
+ * value from the old categoty, and add it back. */
+ server.stat_clients_type_memory[c->client_cron_last_memory_type] -=
+ c->client_cron_last_memory_usage;
+ server.stat_clients_type_memory[type] += mem;
+ /* Remember what we added and where, to remove it next time. */
+ c->client_cron_last_memory_usage = mem;
+ c->client_cron_last_memory_type = type;
+ return 0;
+}
+
/* Return the max samples in the memory usage of clients tracked by
* the function clientsCronTrackExpansiveClients(). */
void getExpansiveClientsInfo(size_t *in_usage, size_t *out_usage) {
@@ -1653,6 +1675,7 @@ void clientsCron(void) {
if (clientsCronHandleTimeout(c,now)) continue;
if (clientsCronResizeQueryBuffer(c)) continue;
if (clientsCronTrackExpansiveClients(c)) continue;
+ if (clientsCronTrackClientsMemUsage(c)) continue;
}
}
@@ -2792,6 +2815,8 @@ void initServer(void) {
server.stat_rdb_cow_bytes = 0;
server.stat_aof_cow_bytes = 0;
server.stat_module_cow_bytes = 0;
+ for (int j = 0; j < CLIENT_TYPE_COUNT; j++)
+ server.stat_clients_type_memory[j] = 0;
server.cron_malloc_stats.zmalloc_used = 0;
server.cron_malloc_stats.process_rss = 0;
server.cron_malloc_stats.allocator_allocated = 0;
diff --git a/src/server.h b/src/server.h
index 9b77f55ac..cf4c285f8 100644
--- a/src/server.h
+++ b/src/server.h
@@ -274,6 +274,7 @@ typedef long long ustime_t; /* microsecond time type. */
#define CLIENT_TYPE_SLAVE 1 /* Slaves. */
#define CLIENT_TYPE_PUBSUB 2 /* Clients subscribed to PubSub channels. */
#define CLIENT_TYPE_MASTER 3 /* Master. */
+#define CLIENT_TYPE_COUNT 4 /* Total number of client types. */
#define CLIENT_TYPE_OBUF_COUNT 3 /* Number of clients to expose to output
buffer configuration. Just the first
three: normal, slave, pubsub. */
@@ -820,10 +821,10 @@ typedef struct client {
* when the authenticated user
* changes. */
void *auth_callback_privdata; /* Private data that is passed when the auth
- * changed callback is executed. Opaque for
+ * changed callback is executed. Opaque for
* Redis Core. */
void *auth_module; /* The module that owns the callback, which is used
- * to disconnect the client if the module is
+ * to disconnect the client if the module is
* unloaded for cleanup. Opaque for Redis Core.*/
/* If this client is in tracking mode and this field is non zero,
@@ -833,6 +834,13 @@ typedef struct client {
rax *client_tracking_prefixes; /* A dictionary of prefixes we are already
subscribed to in BCAST mode, in the
context of client side caching. */
+ /* In clientsCronTrackClientsMemUsage() we track the memory usage of
+ * each client and add it to the sum of all the clients of a given type,
+ * however we need to remember what was the old contribution of each
+ * client, and in which categoty the client was, in order to remove it
+ * before adding it the new value. */
+ uint64_t client_cron_last_memory_usage;
+ int client_cron_last_memory_type;
/* Response buffer */
int bufpos;
char buf[PROTO_REPLY_CHUNK_BYTES];
@@ -1129,6 +1137,7 @@ struct redisServer {
size_t stat_rdb_cow_bytes; /* Copy on write bytes during RDB saving. */
size_t stat_aof_cow_bytes; /* Copy on write bytes during AOF rewrite. */
size_t stat_module_cow_bytes; /* Copy on write bytes during module fork. */
+ uint64_t stat_clients_type_memory[CLIENT_TYPE_COUNT];/* Mem usage by type */
long long stat_unexpected_error_replies; /* Number of unexpected (aof-loading, replica to master, etc.) error replies */
/* The following two are used to track instantaneous metrics, like
* number of operations per second, network traffic. */