summaryrefslogtreecommitdiff
path: root/src/call_reply.c
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2022-02-13 18:37:32 +0200
committerGitHub <noreply@github.com>2022-02-13 18:37:32 +0200
commitb099889a3a6dccf5243135f0611b8cdb11cab7b8 (patch)
tree6dbf613779a6d8ec2aa0d6c17f9f1aec72e4fabf /src/call_reply.c
parent1193e96d0275470e11ae6b6b6d9ffedecf99958b (diff)
downloadredis-b099889a3a6dccf5243135f0611b8cdb11cab7b8.tar.gz
Fix and improve module error reply statistics (#10278)
This PR handles several aspects 1. Calls to RM_ReplyWithError from thread safe contexts don't violate thread safety. 2. Errors returning from RM_Call to the module aren't counted in the statistics (they might be handled silently by the module) 3. When a module propagates a reply it got from RM_Call to it's client, then the error statistics are counted. This is done by: 1. When appending an error reply to the output buffer, we avoid updating the global error statistics, instead we cache that error in a deferred list in the client struct. 2. When creating a RedisModuleCallReply object, the deferred error list is moved from the client into that object. 3. when a module calls RM_ReplyWithCallReply we copy the deferred replies to the dest client (if that's a real client, then that's when the error statistics are updated to the server) Note about RM_ReplyWithCallReply: if the original reply had an array with errors, and the module replied with just a portion of the original reply, and not the entire reply, the errors are currently not propagated and the errors stats will not get propagated. Fix #10180
Diffstat (limited to 'src/call_reply.c')
-rw-r--r--src/call_reply.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/src/call_reply.c b/src/call_reply.c
index 7aa79d089..3694db55e 100644
--- a/src/call_reply.c
+++ b/src/call_reply.c
@@ -60,7 +60,7 @@ struct CallReply {
double d; /* Reply value for double reply. */
struct CallReply *array; /* Array of sub-reply elements. used for set, array, map, and attribute */
} val;
-
+ list *deferred_error_list; /* list of errors in sds form or NULL */
struct CallReply *attribute; /* attribute reply, NULL if not exists */
};
@@ -237,6 +237,8 @@ void freeCallReply(CallReply *rep) {
freeCallReplyInternal(rep);
}
sdsfree(rep->original_proto);
+ if (rep->deferred_error_list)
+ listRelease(rep->deferred_error_list);
zfree(rep);
}
@@ -488,6 +490,11 @@ int callReplyIsResp3(CallReply *rep) {
return rep->flags & REPLY_FLAG_RESP3;
}
+/* Returns a list of errors in sds form, or NULL. */
+list *callReplyDeferredErrorList(CallReply *rep) {
+ return rep->deferred_error_list;
+}
+
/* Create a new CallReply struct from the reply blob.
*
* The function will own the reply blob, so it must not be used or freed by
@@ -496,6 +503,9 @@ int callReplyIsResp3(CallReply *rep) {
* The reply blob will be freed when the returned CallReply struct is later
* freed using freeCallReply().
*
+ * The deferred_error_list is an optional list of errors that are present
+ * in the reply blob, if given, this function will take ownership on it.
+ *
* The private_data is optional and can later be accessed using
* callReplyGetPrivateData().
*
@@ -504,7 +514,7 @@ int callReplyIsResp3(CallReply *rep) {
* DESIGNED TO HANDLE USER INPUT and using it to parse invalid replies is
* unsafe.
*/
-CallReply *callReplyCreate(sds reply, void *private_data) {
+CallReply *callReplyCreate(sds reply, list *deferred_error_list, void *private_data) {
CallReply *res = zmalloc(sizeof(*res));
res->flags = REPLY_FLAG_ROOT;
res->original_proto = reply;
@@ -512,5 +522,6 @@ CallReply *callReplyCreate(sds reply, void *private_data) {
res->proto_len = sdslen(reply);
res->private_data = private_data;
res->attribute = NULL;
+ res->deferred_error_list = deferred_error_list;
return res;
}