diff options
author | antirez <antirez@gmail.com> | 2017-04-27 17:04:07 +0200 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2017-04-27 17:08:37 +0200 |
commit | 469d6e2b37e2913ecb673f910cdb7dbd3af18a67 (patch) | |
tree | 5cc000471af07e4e41549fda93bdfad0d70afd25 /src | |
parent | c861e1e1eee2c29c1df36759d471f6758048318f (diff) | |
download | redis-469d6e2b37e2913ecb673f910cdb7dbd3af18a67.tar.gz |
PSYNC2: fix master cleanup when caching it.
The master client cleanup was incomplete: resetClient() was missing and
the output buffer of the client was not reset, so pending commands
related to the previous connection could be still sent.
The first problem caused the client argument vector to be, at times,
half populated, so that when the correct replication stream arrived the
protcol got mixed to the arugments creating invalid commands that nobody
called.
Thanks to @yangsiran for also investigating this problem, after
already providing important design / implementation hints for the
original PSYNC2 issues (see referenced Github issue).
Note that this commit adds a new function to the list library of Redis
in order to be able to reset a list without destroying it.
Related to issue #3899.
Diffstat (limited to 'src')
-rw-r--r-- | src/adlist.c | 16 | ||||
-rw-r--r-- | src/adlist.h | 1 | ||||
-rw-r--r-- | src/replication.c | 10 |
3 files changed, 20 insertions, 7 deletions
diff --git a/src/adlist.c b/src/adlist.c index f171d3ecc..2fb61a6f1 100644 --- a/src/adlist.c +++ b/src/adlist.c @@ -52,10 +52,8 @@ list *listCreate(void) return list; } -/* Free the whole list. - * - * This function can't fail. */ -void listRelease(list *list) +/* Remove all the elements from the list without destroying the list itself. */ +void listEmpty(list *list) { unsigned long len; listNode *current, *next; @@ -68,6 +66,16 @@ void listRelease(list *list) zfree(current); current = next; } + list->head = list->tail = NULL; + list->len = 0; +} + +/* Free the whole list. + * + * This function can't fail. */ +void listRelease(list *list) +{ + listEmpty(list); zfree(list); } diff --git a/src/adlist.h b/src/adlist.h index be322552f..e457a979e 100644 --- a/src/adlist.h +++ b/src/adlist.h @@ -72,6 +72,7 @@ typedef struct list { /* Prototypes */ list *listCreate(void); void listRelease(list *list); +void listEmpty(list *list); list *listAddNodeHead(list *list, void *value); list *listAddNodeTail(list *list, void *value); list *listInsertNode(list *list, listNode *old_node, void *value, int after); diff --git a/src/replication.c b/src/replication.c index 1828eb8bf..6be5d2631 100644 --- a/src/replication.c +++ b/src/replication.c @@ -2119,13 +2119,17 @@ void replicationCacheMaster(client *c) { /* Unlink the client from the server structures. */ unlinkClient(c); - /* Fix the master specific fields: we want to discard to non processed - * query buffers and non processed offsets, including pending - * transactions. */ + /* Reset the master client so that's ready to accept new commands: + * we want to discard te non processed query buffers and non processed + * offsets, including pending transactions, already populated arguments, + * pending outputs to the master. */ sdsclear(server.master->querybuf); sdsclear(server.master->pending_querybuf); server.master->read_reploff = server.master->reploff; if (c->flags & CLIENT_MULTI) discardTransaction(c); + listEmpty(c->reply); + c->bufpos = 0; + resetClient(c); /* Save the master. Server.master will be set to null later by * replicationHandleMasterDisconnection(). */ |