Repository: redis/redis

Problem statement:
[CRASH]  networking.c:1124 'ln != NULL' is not true
**Crash report**

Paste the complete crash log between the quotes below. Please include a few lines from the log preceding the crash report to provide some context.
13528:M 21 Dec 2020 05:55:27.516 . --- Processing packet of type 1, 2568 bytes
13528:M 21 Dec 2020 05:55:27.516 . pong packet received: 0x7f67c4660800
13528:M 21 Dec 2020 05:55:27.516 . GOSSIP 1ae0679481fd4597e4aab8e6e2404151f0af0161 172.16.223.196:7001@17001 master
13528:M 21 Dec 2020 05:55:27.516 . GOSSIP a9945aeffc76d4c248b35cc389b59a0f03db5dc3 172.16.223.196:7002@17002 slave
13528:M 21 Dec 2020 05:55:27.516 . GOSSIP ca35d3a0213fefd07de52a37ff8d06bae56203ca 172.16.202.21:7002@17002 slave
13528:M 21 Dec 2020 05:55:27.516 . --- Processing packet of type 1, 2568 bytes
13528:M 21 Dec 2020 05:55:27.516 . pong packet received: 0x7f67c465fe00
13528:M 21 Dec 2020 05:55:27.516 . GOSSIP 2a4a953d192373f768b4b1e27d7250d2ac7882ee 172.16.194.129:7002@17002 slave
13528:M 21 Dec 2020 05:55:27.516 . GOSSIP 6e3ee383fb04e163d8d52cd52deb439e8487caf8 172.16.223.196:7003@17003 slave
13528:M 21 Dec 2020 05:55:27.516 . GOSSIP a3de4187354a7c42efa488b5dbc8839dbd6c9a3c 172.16.202.21:7001@17001 master
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38370
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38376
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38382
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38388
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38394
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38400
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38406
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38412
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38418
13528:M 21 Dec 2020 05:55:27.621 - Accepted 172.31.48.10:38424
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38430
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38436
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38442
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38448
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38454
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38460
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38466
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38472
13528:M 21 Dec 2020 05:55:27.622 - Accepted 172.31.48.10:38478
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38514
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38520
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38526
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38532
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38538
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38544
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38550
13528:M 21 Dec 2020 05:55:27.672 - Accepted 172.31.48.10:38556
13528:M 21 Dec 2020 05:55:27.673 - Accepted 172.31.48.10:38562
13528:M 21 Dec 2020 05:55:27.917 . Pinging node ca35d3a0213fefd07de52a37ff8d06bae56203ca
13528:M 21 Dec 2020 05:55:27.923 . --- Processing packet of type 1, 2568 bytes
13528:M 21 Dec 2020 05:55:27.923 . pong packet received: 0x7f67c465f400
13528:M 21 Dec 2020 05:55:27.923 . GOSSIP a3de4187354a7c42efa488b5dbc8839dbd6c9a3c 172.16.202.21:7001@17001 master
13528:M 21 Dec 2020 05:55:27.923 . GOSSIP a9945aeffc76d4c248b35cc389b59a0f03db5dc3 172.16.223.196:7002@17002 slave
13528:M 21 Dec 2020 05:55:27.923 . GOSSIP 6e3ee383fb04e163d8d52cd52deb439e8487caf8 172.16.223.196:7003@17003 slave
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.209 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.210 - Client closed connection
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38612
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38618
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38624
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38630
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38636
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38642
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38648
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38654
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38672
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38678
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38660
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38684
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38666
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38690
13528:M 21 Dec 2020 05:55:28.211 - Accepted 172.31.48.10:38696
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38702
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38708
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38714
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38720
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38726
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38732
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38738
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38744
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38750
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38756
13528:M 21 Dec 2020 05:55:28.261 - Accepted 172.31.48.10:38762
13528:M 21 Dec 2020 05:55:28.588 . --- Processing packet of type 0, 2568 bytes
13528:M 21 Dec 2020 05:55:28.588 . Ping packet received: (nil)
13528:M 21 Dec 2020 05:55:28.588 . ping packet received: (nil)
13528:M 21 Dec 2020 05:55:28.588 . GOSSIP ca35d3a0213fefd07de52a37ff8d06bae56203ca 172.16.202.21:7002@17002 slave
13528:M 21 Dec 2020 05:55:28.589 . GOSSIP 1ae0679481fd4597e4aab8e6e2404151f0af0161 172.16.223.196:7001@17001 master
13528:M 21 Dec 2020 05:55:28.589 . GOSSIP 29a98707188455528595a10719d4169d16d234df 172.16.194.129:7001@17001 master
13528:M 21 Dec 2020 05:55:28.630 . --- Processing packet of type 0, 2568 bytes
13528:M 21 Dec 2020 05:55:28.630 . Ping packet received: (nil)
13528:M 21 Dec 2020 05:55:28.630 . ping packet received: (nil)
13528:M 21 Dec 2020 05:55:28.630 . GOSSIP 1ae0679481fd4597e4aab8e6e2404151f0af0161 172.16.223.196:7001@17001 master
13528:M 21 Dec 2020 05:55:28.630 . GOSSIP a3de4187354a7c42efa488b5dbc8839dbd6c9a3c 172.16.202.21:7001@17001 master
13528:M 21 Dec 2020 05:55:28.630 . GOSSIP 29a98707188455528595a10719d4169d16d234df 172.16.194.129:7001@17001 master
13528:M 21 Dec 2020 05:55:28.658 . --- Processing packet of type 0, 2568 bytes
13528:M 21 Dec 2020 05:55:28.658 . Ping packet received: (nil)
13528:M 21 Dec 2020 05:55:28.658 . ping packet received: (nil)
13528:M 21 Dec 2020 05:55:28.658 . GOSSIP 2a4a953d192373f768b4b1e27d7250d2ac7882ee 172.16.194.129:7002@17002 slave
13528:M 21 Dec 2020 05:55:28.658 . GOSSIP ca35d3a0213fefd07de52a37ff8d06bae56203ca 172.16.202.21:7002@17002 slave
13528:M 21 Dec 2020 05:55:28.658 . GOSSIP a9945aeffc76d4c248b35cc389b59a0f03db5dc3 172.16.223.196:7002@17002 slave
13528:M 21 Dec 2020 05:55:28.746 - Client closed connection
13528:M 21 Dec 2020 05:55:28.746 - Client closed connection
13528:M 21 Dec 2020 05:55:28.746 - Client closed connection
13528:M 21 Dec 2020 05:55:28.746 - Client closed connection
13528:M 21 Dec 2020 05:55:28.746 - Client closed connection
13528:M 21 Dec 2020 05:55:28.746 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.747 - Client closed connection
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38782
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38788
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38800
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38794
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38806
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38812
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38818
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38824
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38830
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38836
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38842
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38848
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38854
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38860
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38866
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38872
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38878
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38884
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38890
13528:M 21 Dec 2020 05:55:28.748 - Accepted 172.31.48.10:38896
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38902
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38908
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38914
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38920
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38926
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38932
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38938
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38944
13528:M 21 Dec 2020 05:55:28.798 - Accepted 172.31.48.10:38950
13528:M 21 Dec 2020 05:55:28.799 - Accepted 172.31.48.10:38956
13528:M 21 Dec 2020 05:55:28.799 - Accepted 172.31.48.10:38962
13528:M 21 Dec 2020 05:55:28.799 - Protocol error (expected $ but got something else) from client: id=361 addr=172.31.48.10:38770 fd=30 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.799 - Protocol error (expected $ but got something else) from client: id=362 addr=172.31.48.10:38776 fd=31 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.799 - Protocol error (expected $ but got something else) from client: id=369 addr=172.31.48.10:38818 fd=38 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.799 - Protocol error (expected $ but got something else) from client: id=365 addr=172.31.48.10:38800 fd=34 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.799 - Protocol error (expected $ but got something else) from client: id=366 addr=172.31.48.10:38794 fd=35 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=367 addr=172.31.48.10:38806 fd=36 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=368 addr=172.31.48.10:38812 fd=37 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=363 addr=172.31.48.10:38782 fd=32 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=372 addr=172.31.48.10:38836 fd=41 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=371 addr=172.31.48.10:38830 fd=40 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=364 addr=172.31.48.10:38788 fd=33 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=370 addr=172.31.48.10:38824 fd=39 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=391 addr=172.31.48.10:38950 fd=60 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=390 addr=172.31.48.10:38944 fd=59 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=387 addr=172.31.48.10:38926 fd=56 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=389 addr=172.31.48.10:38938 fd=58 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=380 addr=172.31.48.10:38884 fd=49 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=393 addr=172.31.48.10:38962 fd=62 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=382 addr=172.31.48.10:38896 fd=51 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=386 addr=172.31.48.10:38920 fd=55 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.800 - Protocol error (expected $ but got something else) from client: id=385 addr=172.31.48.10:38914 fd=54 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.801 - Protocol error (expected $ but got something else) from client: id=375 addr=172.31.48.10:38854 fd=44 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.801 - Protocol error (expected $ but got something else) from client: id=373 addr=172.31.48.10:38842 fd=42 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.801 - Protocol error (expected $ but got something else) from client: id=376 addr=172.31.48.10:38860 fd=45 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.801 - Protocol error (expected $ but got something else) from client: id=377 addr=172.31.48.10:38866 fd=46 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=69 qbuf-free=32699 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default. Query buffer during protocol error: '63n.VXK..'
13528:M 21 Dec 2020 05:55:28.801 - Client closed connection
13528:M 21 Dec 2020 05:55:28.801 - Client closed connection
13528:M 21 Dec 2020 05:55:28.801 - Client closed connection
13528:M 21 Dec 2020 05:55:28.801 - Client closed connection


=== REDIS BUG REPORT START: Cut & paste starting from here ===
13528:M 21 Dec 2020 05:55:28.801 # === ASSERTION FAILED ===
13528:M 21 Dec 2020 05:55:28.801 # ==> networking.c:1124 'ln != NULL' is not true
13528:M 21 Dec 2020 05:55:28.801 # (forcing SIGSEGV to print the bug report.)
13528:M 21 Dec 2020 05:55:28.801 # Redis 6.0.9 crashed by signal: 11, si_code: 1
13528:M 21 Dec 2020 05:55:28.801 # Crashed running the instruction at: 0x478125
13528:M 21 Dec 2020 05:55:28.802 # Accessing address: 0xffffffffffffffff
13528:M 21 Dec 2020 05:55:28.802 # Failed assertion: ln != NULL (networking.c:1124)
------ STACK TRACE ------
EIP:
bin/redis-server 0.0.0.0:7001 [cluster](_serverAssert+0x65)[0x478125]

Backtrace:
bin/redis-server 0.0.0.0:7001 [cluster](logStackTrace+0x2e)[0x47a9de]
bin/redis-server 0.0.0.0:7001 [cluster](sigsegvHandler+0xb8)[0x47b0b8]
/lib64/libpthread.so.0(+0xf630)[0x7f67c4f45630]
bin/redis-server 0.0.0.0:7001 [cluster](_serverAssert+0x65)[0x478125]
bin/redis-server 0.0.0.0:7001 [cluster](unlinkClient+0x224)[0x444c44]
bin/redis-server 0.0.0.0:7001 [cluster](freeClient+0x101)[0x444f21]
bin/redis-server 0.0.0.0:7001 [cluster](freeClientsInAsyncFreeQueue+0x55)[0x4455c5]
bin/redis-server 0.0.0.0:7001 [cluster](beforeSleep+0xc7)[0x434c67]
bin/redis-server 0.0.0.0:7001 [cluster](aeProcessEvents+0x405)[0x4318a5]
bin/redis-server 0.0.0.0:7001 [cluster](aeMain+0x1d)[0x431abd]
bin/redis-server 0.0.0.0:7001 [cluster](main+0x4d7)[0x42e4d7]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f67c4b8a555]
bin/redis-server 0.0.0.0:7001 [cluster][0x42e77a]

------ INFO OUTPUT ------
# Server
redis_version:6.0.9
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:8abd1a4bfb8621e4
redis_mode:cluster
os:Linux 3.10.0-1062.12.1.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:9.3.1
process_id:13528
run_id:095207ee622c92dde4873f6f7287552e034689ad
tcp_port:7001
uptime_in_seconds:229
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:14694608
executable:/usr/local/redis/bin/redis-server
config_file:/u01/redis/conf/redis_7001.conf
io_threads_active:1

# Clients
connected_clients:10
client_recent_max_input_buffer:8
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0

# Memory
used_memory:17603768
used_memory_human:16.79M
used_memory_rss:49446912
used_memory_rss_human:47.16M
used_memory_peak:19816736
used_memory_peak_human:18.90M
used_memory_peak_perc:88.83%
used_memory_overhead:6459064
used_memory_startup:3100040
used_memory_dataset:11144704
used_memory_dataset_perc:76.84%
allocator_allocated:19630672
allocator_active:21721088
allocator_resident:28258304
total_system_memory:16260386816
total_system_memory_human:15.14G
used_memory_lua:35840
used_memory_lua_human:35.00K
used_memory_scripts:216
used_memory_scripts_human:216B
number_of_cached_scripts:1
mem_not_counted_for_evict:2046
mem_replication_backlog:1048576
mem_clients_slaves:20512
mem_clients_normal:143544
mem_aof_buffer:2560
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0

# Persistence
loading:0
rdb_changes_since_last_save:229693
rdb_bgsave_in_progress:0
rdb_last_save_time:1608529901
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:8896512
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
module_fork_in_progress:0
module_fork_last_cow_size:0
aof_current_size:84640586
aof_base_size:75045018
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0

# Stats
total_connections_received:390
total_commands_processed:473195
instantaneous_ops_per_sec:56490
total_net_input_bytes:13664977
total_net_output_bytes:19485920
instantaneous_input_kbps:2326.73
instantaneous_output_kbps:1575.72
rejected_connections:0
sync_full:1
sync_partial_ok:0
sync_partial_err:1
expired_keys:3
expired_stale_perc:0.00
expired_time_cap_reached_count:0
expire_cycle_cpu_milliseconds:7
evicted_keys:0
keyspace_hits:31793
keyspace_misses:275
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:760
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
tracking_total_keys:0
tracking_total_items:0
tracking_total_prefixes:0
unexpected_error_replies:0
total_reads_processed:363916
total_writes_processed:405152
io_threaded_reads_processed:96793
io_threaded_writes_processed:140861

# Replication
role:master
connected_slaves:1
slave0:ip=172.16.223.196,port=7002,state=online,offset=9587207,lag=0
master_replid:7da68b8721ff04a4ea5581207526b704d1bc7978
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:9589484
master_repl_offset:9589484
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:8540909
repl_backlog_histlen:1048576

# CPU
used_cpu_sys:4.654237
used_cpu_user:12.970479
used_cpu_sys_children:0.005984
used_cpu_user_children:0.064829

# Modules

# Commandstats
cmdstat_exists:calls=33,usec=49,usec_per_call=1.48
cmdstat_hscan:calls=46,usec=292,usec_per_call=6.35
cmdstat_psync:calls=1,usec=953,usec_per_call=953.00
cmdstat_incr:calls=36896,usec=29090,usec_per_call=0.79
cmdstat_lpop:calls=37376,usec=57597,usec_per_call=1.54
cmdstat_del:calls=58,usec=216,usec_per_call=3.72
cmdstat_auth:calls=390,usec=1237,usec_per_call=3.17
cmdstat_rpop:calls=37728,usec=44021,usec_per_call=1.17
cmdstat_set:calls=38250,usec=36520,usec_per_call=0.95
cmdstat_sadd:calls=34135,usec=24426,usec_per_call=0.72
cmdstat_client:calls=15,usec=22,usec_per_call=1.47
cmdstat_info:calls=219,usec=8951,usec_per_call=40.87
cmdstat_replconf:calls=233,usec=283,usec_per_call=1.21
cmdstat_eval:calls=58,usec=1494,usec_per_call=25.76
cmdstat_get:calls=31989,usec=24716,usec_per_call=0.77
cmdstat_config:calls=2,usec=26,usec_per_call=13.00
cmdstat_hset:calls=33,usec=40,usec_per_call=1.21
cmdstat_ping:calls=67185,usec=20614,usec_per_call=0.31
cmdstat_setex:calls=23,usec=165,usec_per_call=7.17
cmdstat_rpush:calls=41716,usec=41186,usec_per_call=0.99
cmdstat_lpush:calls=37612,usec=50521,usec_per_call=1.34
cmdstat_command:calls=3,usec=1948,usec_per_call=649.33
cmdstat_cluster:calls=16,usec=3445,usec_per_call=215.31
# Cluster
cluster_enabled:1

# Keyspace
db0:keys=25368,expires=25192,avg_ttl=4517562974

------ CLIENT LIST OUTPUT ------
id=27 addr=172.16.65.238:52984 fd=28 name= age=102 idle=28 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20544 events=r cmd=hscan user=default
id=19 addr=172.16.92.249:35922 fd=23 name= age=174 idle=10 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20496 events=r cmd=info user=default
id=20 addr=172.16.67.24:50888 fd=25 name= age=173 idle=2 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20496 events=r cmd=info user=default
id=28 addr=172.16.34.186:43286 fd=29 name= age=102 idle=72 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=get user=default
id=25 addr=172.16.88.250:37496 fd=26 name= age=103 idle=71 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=get user=default
id=26 addr=172.16.60.159:34578 fd=27 name= age=102 idle=72 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20504 events=r cmd=get user=default
id=9 addr=172.16.80.114:53616 fd=24 name= age=205 idle=9 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=20496 events=r cmd=info user=default
id=6 addr=172.16.223.196:19730 fd=22 name= age=227 idle=0 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=61464 events=r cmd=replconf user=default
id=365 addr=172.31.48.10:38800 fd=34 name= age=0 idle=0 flags=cA db=0 sub=0 psub=0 multi=-1 qbuf=9 qbuf-free=32759 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default
id=368 addr=172.31.48.10:38812 fd=37 name= age=0 idle=0 flags=cA db=0 sub=0 psub=0 multi=-1 qbuf=9 qbuf-free=32759 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default
id=387 addr=172.31.48.10:38926 fd=56 name= age=0 idle=0 flags=cA db=0 sub=0 psub=0 multi=-1 qbuf=9 qbuf-free=32759 argv-mem=36 obl=44 oll=0 omem=0 tot-mem=61508 events=r cmd=hset user=default

------ REGISTERS ------
13528:M 21 Dec 2020 05:55:28.803 #
RAX:0000000000000000 RBX:0000000000000464
RCX:0000000000bc02c0 RDX:00007f67c4f2f7b8
RDI:00007f67c4f2f760 RSI:0000000000000000
RBP:000000000054bd6f RSP:00007ffde4276ef0
R8 :0000000000000001 R9 :00007f67c5a76f80
R10:7562206568742074 R11:0000000000000206
R12:00000000005545a2 R13:0000000000000002
R14:0000000000000001 R15:0000000000bb1ba0
RIP:0000000000478125 EFL:0000000000010202
CSGSFS:0000000000000033
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276eff) -> 00007ffd00000000
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276efe) -> 00007f67b8d6d4d0
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276efd) -> 00000000004455c5
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276efc) -> 0000000000000001
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276efb) -> 00007f67b8d6eec8
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276efa) -> 000000000000000a
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef9) -> 0000000000444f21
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef8) -> 00007f67b9111b80
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef7) -> 00007ffde4276ff0
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef6) -> 0000000000000002
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef5) -> 8601000000000000
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef5) -> 8601000000000000
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef4) -> 0000000000bb1ba0
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef3) -> 0000000000444c44
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef2) -> 0000000000000001
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef1) -> 00007f67b9111b80
13528:M 21 Dec 2020 05:55:28.803 # (00007ffde4276ef0) -> 00007ffde4276ff0

------ MODULES INFO OUTPUT ------

------ FAST MEMORY TEST ------
13528:M 21 Dec 2020 05:55:28.804 # Bio thread for job type #0 terminated
13528:M 21 Dec 2020 05:55:28.804 # Bio thread for job type #1 terminated
13528:M 21 Dec 2020 05:55:28.804 # Bio thread for job type #2 terminated
13528:M 21 Dec 2020 05:55:28.804 # IO thread(tid:140083505121024) terminated
13528:M 21 Dec 2020 05:55:28.804 # IO thread(tid:140083496728320) terminated
13528:M 21 Dec 2020 05:55:28.804 # IO thread(tid:140083488335616) terminated
*** Preparing to test memory region 5b8000 (2277376 bytes)
*** Preparing to test memory region bb1000 (135168 bytes)
*** Preparing to test memory region 7f679c000000 (135168 bytes)
*** Preparing to test memory region 7f67a4000000 (135168 bytes)
*** Preparing to test memory region 7f67ac000000 (135168 bytes)
*** Preparing to test memory region 7f67b1dff000 (8388608 bytes)
*** Preparing to test memory region 7f67b2600000 (8388608 bytes)
*** Preparing to test memory region 7f67b2e00000 (19009536 bytes)
*** Preparing to test memory region 7f67b8200000 (8388608 bytes)
*** Preparing to test memory region 7f67b8a00000 (2097152 bytes)
*** Preparing to test memory region 7f67b8c79000 (13107200 bytes)
*** Preparing to test memory region 7f67b98fa000 (8388608 bytes)
*** Preparing to test memory region 7f67ba0fb000 (8388608 bytes)
*** Preparing to test memory region 7f67ba8fc000 (8388608 bytes)
*** Preparing to test memory region 7f67bb0fd000 (8388608 bytes)
*** Preparing to test memory region 7f67bb8fe000 (8388608 bytes)
*** Preparing to test memory region 7f67bd100000 (9437184 bytes)
*** Preparing to test memory region 7f67bda3d000 (2621440 bytes)
*** Preparing to test memory region 7f67c4200000 (8388608 bytes)
*** Preparing to test memory region 7f67c4f31000 (20480 bytes)
*** Preparing to test memory region 7f67c514e000 (16384 bytes)
*** Preparing to test memory region 7f67c5a75000 (24576 bytes)
*** Preparing to test memory region 7f67c5a7f000 (4096 bytes)
** Preparing to test memory region 7f67c4f31000 (20480 bytes)
*** Preparing to test memory region 7f67c514e000 (16384 bytes)
*** Preparing to test memory region 7f67c5a75000 (24576 bytes)
*** Preparing to test memory region 7f67c5a7f000 (4096 bytes)
*** Preparing to test memory region 7f67c5a80000 (4096 bytes)
*** Preparing to test memory region 7f67c5a83000 (4096 bytes)
.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O
Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.

------ DUMPING CODE AROUND EIP ------
Symbol: _serverAssert (base: 0x4780c0)
Module: bin/redis-server 0.0.0.0:7001 [cluster] (base 0x400000)
$ xxd -r -p /tmp/dump.hex /tmp/dump.bin
$ objdump --adjust-vma=0x4780c0 -D -b binary -m i386:x86-64 /tmp/dump.bin
------
13528:M 21 Dec 2020 05:55:29.528 # dump of function (hexdump of 229 bytes):
8b059666140041544989fc554889f55389d385c07462bec8945500bf0300000031c0e8e9cffbff4d89e089d94889eabee1945500bf0300000031c0e8d0cffbffbe907c550031c0bf030000004c89253566140048892d36661400891d38661400e8abcffbffc60425ffffffff785b5d415cc3660f1f440000e8d3faffffeb97904154554889fd534881eca00000008b150866140085d20f84f4000000bec07c5500bf0300000031c031dbe861cffbff488b95b8000000befc94550031c0bf03000000e849cffbff488b7d08ba200000004889e6e838ce0400be11955500bf030000004889c2
```
```

**Aditional information**

1. OS distribution and version
Linux ip-172-16-194-129.ap-south-1.compute.internal 3.10.0-1062.12.1.el7.x86_64 #1 SMP Tue Feb 4 23:02:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
CentOS Linux release 7.7.1908 (Core)
2. Steps to reproduce (if any)
3.redis-version 6.0.9
redis-cluster 3master 4slave
step:
(1): this is  redis new version,we want test the cluster ops and tps on the aws ec2 instance。we used 3 machines (each 4C 16G)
but  once we execute " redis-benchmark -q -h *.*.*.*  -p 7001 -c 50 -n 10000 --cluster" command they will crash like above logs.
(2): we use same config to execute on different machines (not aws ec2),they would be ok,There will be no such problems。



Hints:

Patch:
diff --git a/00-RELEASENOTES b/00-RELEASENOTES
index af2f5d6cade..bff270e77ee 100644
--- a/00-RELEASENOTES
+++ b/00-RELEASENOTES
@@ -11,6 +11,78 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
 SECURITY: There are security fixes in the release.
 --------------------------------------------------------------------------------
 
+================================================================================
+Redis 6.0.10     Released Tue Jan 12 16:20:20 IST 2021
+================================================================================
+
+Upgrade urgency MODERATE: several bugs with moderate impact are fixed,
+Here is a comprehensive list of changes in this release compared to 6.0.9.
+
+Command behavior changes:
+* SWAPDB invalidates WATCHed keys (#8239)
+* SORT command behaves differently when used on a writable replica (#8283)
+* EXISTS should not alter LRU (#8016)
+  In Redis 5.0 and 6.0 it would have touched the LRU/LFU of the key.
+* OBJECT should not reveal logically expired keys (#8016)
+  Will now behave the same TYPE or any other non-DEBUG command.
+* GEORADIUS[BYMEMBER] can fail with -OOM if Redis is over the memory limit (#8107)
+
+Other behavior changes:
+* Sentinel: Fix missing updates to the config file after SENTINEL SET command (#8229)
+* CONFIG REWRITE is atomic and safer, but requires write access to the config file's folder (#7824, #8051)
+  This change was already present in 6.0.9, but was missing from the release notes.
+
+Bug fixes with compatibility implications (bugs introduced in Redis 6.0):
+* Fix RDB CRC64 checksum on big-endian systems (#8270)
+  If you're using big-endian please consider the compatibility implications with
+  RESTORE, replication and persistence.
+* Fix wrong order of key/value in Lua's map response (#8266)
+  If your scripts use redis.setresp() or return a map (new in Redis 6.0), please
+  consider the implications.
+
+Bug fixes:
+* Fix an issue where a forked process deletes the parent's pidfile (#8231)
+* Fix crashes when enabling io-threads-do-reads (#8230)
+* Fix a crash in redis-cli after executing cluster backup (#8267)
+* Handle output buffer limits for module blocked clients (#8141)
+  Could result in a module sending reply to a blocked client to go beyond the limit.
+* Fix setproctitle related crashes. (#8150, #8088)
+  Caused various crashes on startup, mainly on Apple M1 chips or under instrumentation.
+* Backup/restore cluster mode keys to slots map for repl-diskless-load=swapdb (#8108)
+  In cluster mode with repl-diskless-load, when loading failed, slot map wouldn't
+  have been restored.
+* Fix oom-score-adj-values range, and bug when used in config file (#8046)
+  Enabling setting this in the config file in a line after enabling it, would
+  have been buggy.
+* Reset average ttl when empty databases (#8106)
+  Just causing misleading metric in INFO
+* Disable rehash when Redis has child process (#8007)
+  This could have caused excessive CoW during BGSAVE, replication or AOFRW.
+* Further improved ACL algorithm for picking categories (#7966)
+  Output of ACL GETUSER is now more similar to the one provided by ACL SETUSER.
+* Fix bug with module GIL being released prematurely (#8061)
+  Could in theory (and rarely) cause multi-threaded modules to corrupt memory.
+* Reduce effect of client tracking causing feedback loop in key eviction (#8100)
+* Fix cluster access to unaligned memory (SIGBUS on old ARM) (#7958)
+* Fix saving of strings larger than 2GB into RDB files (#8306)
+
+Additional improvements:
+* Avoid wasteful transient memory allocation in certain cases (#8286, #5954)
+
+Platform / toolchain support related improvements:
+* Fix crash log registers output on ARM. (#8020)
+* Add a check for an ARM64 Linux kernel bug (#8224)
+  Due to the potential severity of this issue, Redis will print log warning on startup.
+* Raspberry build fix. (#8095)
+
+New configuration options:
+* oom-score-adj-values config can now take absolute values (besides relative ones) (#8046)
+
+Module related fixes:
+* Moved RMAPI_FUNC_SUPPORTED so that it's usable (#8037)
+* Improve timer accuracy (#7987)
+* Allow '\0' inside of result of RM_CreateStringPrintf (#6260)
+
 ================================================================================
 Redis 6.0.9     Released Mon Oct 26 10:37:47 IST 2020
 ================================================================================
diff --git a/redis.conf b/redis.conf
index ab0a30cbd0e..97f81809dcd 100644
--- a/redis.conf
+++ b/redis.conf
@@ -1049,21 +1049,26 @@ lazyfree-lazy-user-del no
 # for all its processes, depending on their role. The default scores will
 # attempt to have background child processes killed before all others, and
 # replicas killed before masters.
-
+#
+# Redis supports three options:
+#
+# no:       Don't make changes to oom-score-adj (default).
+# yes:      Alias to "relative" see below.
+# absolute: Values in oom-score-adj-values are written as is to the kernel.
+# relative: Values are used relative to the initial value of oom_score_adj when
+#           the server starts and are then clamped to a range of -1000 to 1000.
+#           Because typically the initial value is 0, they will often match the
+#           absolute values.
 oom-score-adj no
 
 # When oom-score-adj is used, this directive controls the specific values used
-# for master, replica and background child processes. Values range -1000 to
-# 1000 (higher means more likely to be killed).
+# for master, replica and background child processes. Values range -2000 to
+# 2000 (higher means more likely to be killed).
 #
 # Unprivileged processes (not root, and without CAP_SYS_RESOURCE capabilities)
 # can freely increase their value, but not decrease it below its initial
-# settings.
-#
-# Values are used relative to the initial value of oom_score_adj when the server
-# starts. Because typically the initial value is 0, they will often match the
-# absolute values.
-
+# settings. This means that setting oom-score-adj to "relative" and setting the
+# oom-score-adj-values to positive values will always succeed.
 oom-score-adj-values 0 200 800
 
 ############################## APPEND ONLY MODE ###############################
@@ -1863,3 +1868,10 @@ jemalloc-bg-thread yes
 #
 # Set bgsave child process to cpu affinity 1,10,11
 # bgsave_cpulist 1,10-11
+
+# In some cases redis will emit warnings and even refuse to start if it detects
+# that the system is in bad state, it is possible to suppress these warnings
+# by setting the following config which takes a space delimited list of warnings
+# to suppress
+#
+# ignore-warnings ARM64-COW-BUG
diff --git a/src/acl.c b/src/acl.c
index e0fd3f72891..3119ff82f1b 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -475,34 +475,68 @@ sds ACLDescribeUserCommandRules(user *u) {
         ACLSetUser(fakeuser,"-@all",-1);
     }
 
-    /* Try to add or subtract each category one after the other. Often a
-     * single category will not perfectly match the set of commands into
-     * it, so at the end we do a final pass adding/removing the single commands
-     * needed to make the bitmap exactly match. A temp user is maintained to
-     * keep track of categories already applied. */
+    /* Attempt to find a good approximation for categories and commands
+     * based on the current bits used, by looping over the category list
+     * and applying the best fit each time. Often a set of categories will not 
+     * perfectly match the set of commands into it, so at the end we do a 
+     * final pass adding/removing the single commands needed to make the bitmap
+     * exactly match. A temp user is maintained to keep track of categories 
+     * already applied. */
     user tu = {0};
     user *tempuser = &tu;
+    
+    /* Keep track of the categories that have been applied, to prevent
+     * applying them twice.  */
+    char applied[sizeof(ACLCommandCategories)/sizeof(ACLCommandCategories[0])];
+    memset(applied, 0, sizeof(applied));
+
     memcpy(tempuser->allowed_commands,
         u->allowed_commands, 
         sizeof(u->allowed_commands));
-
-    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
-        unsigned long on, off;
-        ACLCountCategoryBitsForUser(tempuser,&on,&off,ACLCommandCategories[j].name);
-        if ((additive && on > off) || (!additive && off > on)) {
-            sds op = sdsnewlen(additive ? "+@" : "-@", 2);
-            op = sdscat(op,ACLCommandCategories[j].name);
-            ACLSetUser(fakeuser,op,-1);
-
-            sds invop = sdsnewlen(additive ? "-@" : "+@", 2);
-            invop = sdscat(invop,ACLCommandCategories[j].name);
-            ACLSetUser(tempuser,invop,-1);
-
-            rules = sdscatsds(rules,op);
-            rules = sdscatlen(rules," ",1);
-            sdsfree(op);
-            sdsfree(invop);
+    while (1) {
+        int best = -1;
+        unsigned long mindiff = INT_MAX, maxsame = 0;
+        for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
+            if (applied[j]) continue;
+
+            unsigned long on, off, diff, same;
+            ACLCountCategoryBitsForUser(tempuser,&on,&off,ACLCommandCategories[j].name);
+            /* Check if the current category is the best this loop:
+             * * It has more commands in common with the user than commands
+             *   that are different.
+             * AND EITHER
+             * * It has the fewest number of differences
+             *    than the best match we have found so far. 
+             * * OR it matches the fewest number of differences
+             *   that we've seen but it has more in common. */
+            diff = additive ? off : on;
+            same = additive ? on : off;
+            if (same > diff && 
+                ((diff < mindiff) || (diff == mindiff && same > maxsame)))
+            {
+                best = j;
+                mindiff = diff;
+                maxsame = same;
+            }
         }
+
+        /* We didn't find a match */
+        if (best == -1) break;
+
+        sds op = sdsnewlen(additive ? "+@" : "-@", 2);
+        op = sdscat(op,ACLCommandCategories[best].name);
+        ACLSetUser(fakeuser,op,-1);
+
+        sds invop = sdsnewlen(additive ? "-@" : "+@", 2);
+        invop = sdscat(invop,ACLCommandCategories[best].name);
+        ACLSetUser(tempuser,invop,-1);
+
+        rules = sdscatsds(rules,op);
+        rules = sdscatlen(rules," ",1);
+        sdsfree(op);
+        sdsfree(invop);
+
+        applied[best] = 1;
     }
 
     /* Fix the final ACLs with single commands differences. */
diff --git a/src/aof.c b/src/aof.c
index 6e2ef5ca069..9eb6c093f2d 100644
--- a/src/aof.c
+++ b/src/aof.c
@@ -1652,6 +1652,7 @@ int rewriteAppendOnlyFileBackground(void) {
         server.aof_rewrite_scheduled = 0;
         server.aof_rewrite_time_start = time(NULL);
         server.aof_child_pid = childpid;
+        updateDictResizePolicy();
         /* We set appendseldb to -1 in order to force the next call to the
          * feedAppendOnlyFile() to issue a SELECT command, so the differences
          * accumulated by the parent into server.aof_rewrite_buf will start
diff --git a/src/bio.c b/src/bio.c
index ff11087998e..5e5f6afd911 100644
--- a/src/bio.c
+++ b/src/bio.c
@@ -203,7 +203,7 @@ void *bioProcessBackgroundJobs(void *arg) {
             /* What we free changes depending on what arguments are set:
              * arg1 -> free the object at pointer.
              * arg2 & arg3 -> free two dictionaries (a Redis DB).
-             * only arg3 -> free the skiplist. */
+             * only arg3 -> free the radix tree. */
             if (job->arg1)
                 lazyfreeFreeObjectFromBioThread(job->arg1);
             else if (job->arg2 && job->arg3)
diff --git a/src/cluster.c b/src/cluster.c
index 7d690e863a6..2d4d417e355 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -77,6 +77,9 @@ uint64_t clusterGetMaxEpoch(void);
 int clusterBumpConfigEpochWithoutConsensus(void);
 void moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len);
 
+#define RCVBUF_INIT_LEN 1024
+#define RCVBUF_MAX_PREALLOC (1<<20) /* 1MB */
+
 /* -----------------------------------------------------------------------------
  * Initialization
  * -------------------------------------------------------------------------- */
@@ -613,7 +616,8 @@ clusterLink *createClusterLink(clusterNode *node) {
     clusterLink *link = zmalloc(sizeof(*link));
     link->ctime = mstime();
     link->sndbuf = sdsempty();
-    link->rcvbuf = sdsempty();
+    link->rcvbuf = zmalloc(link->rcvbuf_alloc = RCVBUF_INIT_LEN);
+    link->rcvbuf_len = 0;
     link->node = node;
     link->conn = NULL;
     return link;
@@ -628,7 +632,7 @@ void freeClusterLink(clusterLink *link) {
         link->conn = NULL;
     }
     sdsfree(link->sndbuf);
-    sdsfree(link->rcvbuf);
+    zfree(link->rcvbuf);
     if (link->node)
         link->node->link = NULL;
     zfree(link);
@@ -1728,7 +1732,7 @@ int clusterProcessPacket(clusterLink *link) {
 
     /* Perform sanity checks */
     if (totlen < 16) return 1; /* At least signature, version, totlen, count. */
-    if (totlen > sdslen(link->rcvbuf)) return 1;
+    if (totlen > link->rcvbuf_len) return 1;
 
     if (ntohs(hdr->ver) != CLUSTER_PROTO_VER) {
         /* Can't handle messages of different versions. */
@@ -2282,7 +2286,7 @@ void clusterReadHandler(connection *conn) {
     unsigned int readlen, rcvbuflen;
 
     while(1) { /* Read as long as there is data to read. */
-        rcvbuflen = sdslen(link->rcvbuf);
+        rcvbuflen = link->rcvbuf_len;
         if (rcvbuflen < 8) {
             /* First, obtain the first 8 bytes to get the full message
              * length. */
@@ -2318,7 +2322,15 @@ void clusterReadHandler(connection *conn) {
             return;
         } else {
             /* Read data and recast the pointer to the new buffer. */
-            link->rcvbuf = sdscatlen(link->rcvbuf,buf,nread);
+            size_t unused = link->rcvbuf_alloc - link->rcvbuf_len;
+            if ((size_t)nread > unused) {
+                size_t required = link->rcvbuf_len + nread;
+                /* If less than 1mb, grow to twice the needed size, if larger grow by 1mb. */
+                link->rcvbuf_alloc = required < RCVBUF_MAX_PREALLOC ? required * 2: required + RCVBUF_MAX_PREALLOC;
+                link->rcvbuf = zrealloc(link->rcvbuf, link->rcvbuf_alloc);
+            }
+            memcpy(link->rcvbuf + link->rcvbuf_len, buf, nread);
+            link->rcvbuf_len += nread;
             hdr = (clusterMsg*) link->rcvbuf;
             rcvbuflen += nread;
         }
@@ -2326,8 +2338,11 @@ void clusterReadHandler(connection *conn) {
         /* Total length obtained? Process this packet. */
         if (rcvbuflen >= 8 && rcvbuflen == ntohl(hdr->totlen)) {
             if (clusterProcessPacket(link)) {
-                sdsfree(link->rcvbuf);
-                link->rcvbuf = sdsempty();
+                if (link->rcvbuf_alloc > RCVBUF_INIT_LEN) {
+                    zfree(link->rcvbuf);
+                    link->rcvbuf = zmalloc(link->rcvbuf_alloc = RCVBUF_INIT_LEN);
+                }
+                link->rcvbuf_len = 0;
             } else {
                 return; /* Link no longer valid. */
             }
diff --git a/src/cluster.h b/src/cluster.h
index 48a1117645a..84e08f607a6 100644
--- a/src/cluster.h
+++ b/src/cluster.h
@@ -38,7 +38,9 @@ typedef struct clusterLink {
     mstime_t ctime;             /* Link creation time */
     connection *conn;           /* Connection to remote node */
     sds sndbuf;                 /* Packet send buffer */
-    sds rcvbuf;                 /* Packet reception buffer */
+    char *rcvbuf;               /* Packet reception buffer */
+    size_t rcvbuf_len;          /* Used size of rcvbuf */
+    size_t rcvbuf_alloc;        /* Used size of rcvbuf */
     struct clusterNode *node;   /* Node related to this link if any, or NULL */
 } clusterLink;
 
diff --git a/src/config.c b/src/config.c
index 4a956d69809..a85f1750afe 100644
--- a/src/config.c
+++ b/src/config.c
@@ -104,6 +104,15 @@ configEnum tls_auth_clients_enum[] = {
     {"optional", TLS_CLIENT_AUTH_OPTIONAL},
     {NULL, 0}
 };
+
+configEnum oom_score_adj_enum[] = {
+    {"no", OOM_SCORE_ADJ_NO},
+    {"yes", OOM_SCORE_RELATIVE},
+    {"relative", OOM_SCORE_RELATIVE},
+    {"absolute", OOM_SCORE_ADJ_ABSOLUTE},
+    {NULL, 0}
+};
+
 /* Output buffer limits presets. */
 clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
     {0, 0, 0}, /* normal */
@@ -293,7 +302,7 @@ void queueLoadModule(sds path, sds *argv, int argc) {
  * server.oom_score_adj_values if valid.
  */
 
-static int updateOOMScoreAdjValues(sds *args, char **err) {
+static int updateOOMScoreAdjValues(sds *args, char **err, int apply) {
     int i;
     int values[CONFIG_OOM_COUNT];
 
@@ -301,8 +310,8 @@ static int updateOOMScoreAdjValues(sds *args, char **err) {
         char *eptr;
         long long val = strtoll(args[i], &eptr, 10);
 
-        if (*eptr != '\0' || val < -1000 || val > 1000) {
-            if (err) *err = "Invalid oom-score-adj-values, elements must be between -1000 and 1000.";
+        if (*eptr != '\0' || val < -2000 || val > 2000) {
+            if (err) *err = "Invalid oom-score-adj-values, elements must be between -2000 and 2000.";
             return C_ERR;
         }
 
@@ -326,6 +335,10 @@ static int updateOOMScoreAdjValues(sds *args, char **err) {
         old_values[i] = server.oom_score_adj_values[i];
         server.oom_score_adj_values[i] = values[i];
     }
+    
+    /* When parsing the config file, we want to apply only when all is done. */
+    if (!apply)
+        return C_OK;
 
     /* Update */
     if (setOOMScoreAdj(-1) == C_ERR) {
@@ -550,7 +563,7 @@ void loadServerConfigFromString(char *config) {
             server.client_obuf_limits[class].soft_limit_bytes = soft;
             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
         } else if (!strcasecmp(argv[0],"oom-score-adj-values") && argc == 1 + CONFIG_OOM_COUNT) {
-            if (updateOOMScoreAdjValues(&argv[1], &err) == C_ERR) goto loaderr;
+            if (updateOOMScoreAdjValues(&argv[1], &err, 0) == C_ERR) goto loaderr;
         } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
             int flags = keyspaceEventsStringToFlags(argv[1]);
 
@@ -810,7 +823,7 @@ void configSetCommand(client *c) {
         int success = 1;
 
         sds *v = sdssplitlen(o->ptr, sdslen(o->ptr), " ", 1, &vlen);
-        if (vlen != CONFIG_OOM_COUNT || updateOOMScoreAdjValues(v, &errstr) == C_ERR)
+        if (vlen != CONFIG_OOM_COUNT || updateOOMScoreAdjValues(v, &errstr, 1) == C_ERR)
             success = 0;
 
         sdsfreesplitres(v, vlen);
@@ -2288,7 +2301,6 @@ standardConfig configs[] = {
     createBoolConfig("cluster-enabled", NULL, IMMUTABLE_CONFIG, server.cluster_enabled, 0, NULL, NULL),
     createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG, server.aof_enabled, 0, NULL, updateAppendonly),
     createBoolConfig("cluster-allow-reads-when-down", NULL, MODIFIABLE_CONFIG, server.cluster_allow_reads_when_down, 0, NULL, NULL),
-    createBoolConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, server.oom_score_adj, 0, NULL, updateOOMScoreAdj),
 
     /* String Configs */
     createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, "", NULL, NULL),
@@ -2305,6 +2317,7 @@ standardConfig configs[] = {
     createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bio_cpulist, NULL, NULL, NULL),
     createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.aof_rewrite_cpulist, NULL, NULL, NULL),
     createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),
+    createStringConfig("ignore-warnings", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.ignore_warnings, "ARM64-COW-BUG", NULL, NULL),
 
     /* Enum Configs */
     createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),
@@ -2313,6 +2326,7 @@ standardConfig configs[] = {
     createEnumConfig("loglevel", NULL, MODIFIABLE_CONFIG, loglevel_enum, server.verbosity, LL_NOTICE, NULL, NULL),
     createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),
     createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),
+    createEnumConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, oom_score_adj_enum, server.oom_score_adj, OOM_SCORE_ADJ_NO, NULL, updateOOMScoreAdj),
 
     /* Integer configs */
     createIntConfig("databases", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, server.dbnum, 16, INTEGER_CONFIG, NULL, NULL),
diff --git a/src/crcspeed.c b/src/crcspeed.c
index d2d97a8c795..81a80ce8e54 100644
--- a/src/crcspeed.c
+++ b/src/crcspeed.c
@@ -35,7 +35,8 @@ void crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {
 
     /* generate CRCs for all single byte sequences */
     for (int n = 0; n < 256; n++) {
-        table[0][n] = crcfn(0, &n, 1);
+        unsigned char v = n;
+        table[0][n] = crcfn(0, &v, 1);
     }
 
     /* generate nested CRC table for future slice-by-8 lookup */
diff --git a/src/db.c b/src/db.c
index eb87cebc557..c0a08ebb7a0 100644
--- a/src/db.c
+++ b/src/db.c
@@ -34,6 +34,13 @@
 #include <signal.h>
 #include <ctype.h>
 
+/* Database backup. */
+struct dbBackup {
+    redisDb *dbarray;
+    rax *slots_to_keys;
+    uint64_t slots_keys_count[CLUSTER_SLOTS];
+};
+
 /*-----------------------------------------------------------------------------
  * C-level DB API
  *----------------------------------------------------------------------------*/
@@ -102,11 +109,8 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
         /* Key expired. If we are in the context of a master, expireIfNeeded()
          * returns 0 only when the key does not exist at all, so it's safe
          * to return NULL ASAP. */
-        if (server.masterhost == NULL) {
-            server.stat_keyspace_misses++;
-            notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
-            return NULL;
-        }
+        if (server.masterhost == NULL)
+            goto keymiss;
 
         /* However if we are in the context of a slave, expireIfNeeded() will
          * not really try to expire the key, it only returns information
@@ -125,19 +129,21 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
             server.current_client->cmd &&
             server.current_client->cmd->flags & CMD_READONLY)
         {
-            server.stat_keyspace_misses++;
-            notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
-            return NULL;
+            goto keymiss;
         }
     }
     val = lookupKey(db,key,flags);
-    if (val == NULL) {
+    if (val == NULL)
+        goto keymiss;
+    server.stat_keyspace_hits++;
+    return val;
+
+keymiss:
+    if (!(flags & LOOKUP_NONOTIFY)) {
         server.stat_keyspace_misses++;
         notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
     }
-    else
-        server.stat_keyspace_hits++;
-    return val;
+    return NULL;
 }
 
 /* Like lookupKeyReadWithFlags(), but does not use any flag, which is the
@@ -360,6 +366,41 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
     return o;
 }
 
+/* Remove all keys from the database(s) structure. The dbarray argument
+ * may not be the server main DBs (could be a backup).
+ *
+ * The dbnum can be -1 if all the DBs should be emptied, or the specified
+ * DB index if we want to empty only a single database.
+ * The function returns the number of keys removed from the database(s). */
+long long emptyDbStructure(redisDb *dbarray, int dbnum, int async,
+                           void(callback)(void*))
+{
+    long long removed = 0;
+    int startdb, enddb;
+
+    if (dbnum == -1) {
+        startdb = 0;
+        enddb = server.dbnum-1;
+    } else {
+        startdb = enddb = dbnum;
+    }
+
+    for (int j = startdb; j <= enddb; j++) {
+        removed += dictSize(dbarray[j].dict);
+        if (async) {
+            emptyDbAsync(&dbarray[j]);
+        } else {
+            dictEmpty(dbarray[j].dict,callback);
+            dictEmpty(dbarray[j].expires,callback);
+        }
+        /* Because all keys of database are removed, reset average ttl. */
+        dbarray[j].avg_ttl = 0;
+        dbarray[j].expires_cursor = 0;
+    }
+
+    return removed;
+}
+
 /* Remove all keys from all the databases in a Redis server.
  * If callback is given the function is called from time to time to
  * signal that work is in progress.
@@ -368,18 +409,14 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
  * DB number if we want to flush only a single Redis database number.
  *
  * Flags are be EMPTYDB_NO_FLAGS if no special flags are specified or
- * 1. EMPTYDB_ASYNC if we want the memory to be freed in a different thread.
- * 2. EMPTYDB_BACKUP if we want to empty the backup dictionaries created by
- *    disklessLoadMakeBackups. In that case we only free memory and avoid
- *    firing module events.
+ * EMPTYDB_ASYNC if we want the memory to be freed in a different thread
  * and the function to return ASAP.
  *
  * On success the function returns the number of keys removed from the
  * database(s). Otherwise -1 is returned in the specific case the
  * DB number is out of range, and errno is set to EINVAL. */
-long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*)) {
+long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
     int async = (flags & EMPTYDB_ASYNC);
-    int backup = (flags & EMPTYDB_BACKUP); /* Just free the memory, nothing else */
     RedisModuleFlushInfoV1 fi = {REDISMODULE_FLUSHINFO_VERSION,!async,dbnum};
     long long removed = 0;
 
@@ -388,60 +425,107 @@ long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(
         return -1;
     }
 
-    /* Pre-flush actions */
-    if (!backup) {
-        /* Fire the flushdb modules event. */
-        moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
-                              REDISMODULE_SUBEVENT_FLUSHDB_START,
-                              &fi);
+    /* Fire the flushdb modules event. */
+    moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
+                          REDISMODULE_SUBEVENT_FLUSHDB_START,
+                          &fi);
 
-        /* Make sure the WATCHed keys are affected by the FLUSH* commands.
-         * Note that we need to call the function while the keys are still
-         * there. */
-        signalFlushedDb(dbnum);
-    }
+    /* Make sure the WATCHed keys are affected by the FLUSH* commands.
+     * Note that we need to call the function while the keys are still
+     * there. */
+    signalFlushedDb(dbnum);
 
-    int startdb, enddb;
-    if (dbnum == -1) {
-        startdb = 0;
-        enddb = server.dbnum-1;
-    } else {
-        startdb = enddb = dbnum;
+    /* Empty redis database structure. */
+    removed = emptyDbStructure(server.db, dbnum, async, callback);
+
+    /* Flush slots to keys map if enable cluster, we can flush entire
+     * slots to keys map whatever dbnum because only support one DB
+     * in cluster mode. */
+    if (server.cluster_enabled) slotToKeyFlush(async);
+
+    if (dbnum == -1) flushSlaveKeysWithExpireList();
+
+    /* Also fire the end event. Note that this event will fire almost
+     * immediately after the start event if the flush is asynchronous. */
+    moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
+                          REDISMODULE_SUBEVENT_FLUSHDB_END,
+                          &fi);
+
+    return removed;
+}
+
+/* Store a backup of the database for later use, and put an empty one
+ * instead of it. */
+dbBackup *backupDb(void) {
+    dbBackup *backup = zmalloc(sizeof(dbBackup));
+
+    /* Backup main DBs. */
+    backup->dbarray = zmalloc(sizeof(redisDb)*server.dbnum);
+    for (int i=0; i<server.dbnum; i++) {
+        backup->dbarray[i] = server.db[i];
+        server.db[i].dict = dictCreate(&dbDictType,NULL);
+        server.db[i].expires = dictCreate(&keyptrDictType,NULL);
     }
 
-    for (int j = startdb; j <= enddb; j++) {
-        removed += dictSize(dbarray[j].dict);
-        if (async) {
-            emptyDbAsync(&dbarray[j]);
-        } else {
-            dictEmpty(dbarray[j].dict,callback);
-            dictEmpty(dbarray[j].expires,callback);
-        }
+    /* Backup cluster slots to keys map if enable cluster. */
+    if (server.cluster_enabled) {
+        backup->slots_to_keys = server.cluster->slots_to_keys;
+        memcpy(backup->slots_keys_count, server.cluster->slots_keys_count,
+            sizeof(server.cluster->slots_keys_count));
+        server.cluster->slots_to_keys = raxNew();
+        memset(server.cluster->slots_keys_count, 0,
+            sizeof(server.cluster->slots_keys_count));
     }
 
-    /* Post-flush actions */
-    if (!backup) {
-        if (server.cluster_enabled) {
-            if (async) {
-                slotToKeyFlushAsync();
-            } else {
-                slotToKeyFlush();
-            }
-        }
-        if (dbnum == -1) flushSlaveKeysWithExpireList();
+    return backup;
+}
+
+/* Discard a previously created backup, this can be slow (similar to FLUSHALL)
+ * Arguments are similar to the ones of emptyDb, see EMPTYDB_ flags. */
+void discardDbBackup(dbBackup *buckup, int flags, void(callback)(void*)) {
+    int async = (flags & EMPTYDB_ASYNC);
 
-        /* Also fire the end event. Note that this event will fire almost
-         * immediately after the start event if the flush is asynchronous. */
-        moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
-                              REDISMODULE_SUBEVENT_FLUSHDB_END,
-                              &fi);
+    /* Release main DBs backup . */
+    emptyDbStructure(buckup->dbarray, -1, async, callback);
+    for (int i=0; i<server.dbnum; i++) {
+        dictRelease(buckup->dbarray[i].dict);
+        dictRelease(buckup->dbarray[i].expires);
     }
 
-    return removed;
-}
+    /* Release slots to keys map backup if enable cluster. */
+    if (server.cluster_enabled) freeSlotsToKeysMap(buckup->slots_to_keys, async);
+
+    /* Release buckup. */
+    zfree(buckup->dbarray);
+    zfree(buckup);
+}
+
+/* Restore the previously created backup (discarding what currently resides
+ * in the db).
+ * This function should be called after the current contents of the database
+ * was emptied with a previous call to emptyDb (possibly using the async mode). */
+void restoreDbBackup(dbBackup *buckup) {
+    /* Restore main DBs. */
+    for (int i=0; i<server.dbnum; i++) {
+        serverAssert(dictSize(server.db[i].dict) == 0);
+        serverAssert(dictSize(server.db[i].expires) == 0);
+        dictRelease(server.db[i].dict);
+        dictRelease(server.db[i].expires);
+        server.db[i] = buckup->dbarray[i];
+    }
 
-long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
-    return emptyDbGeneric(server.db, dbnum, flags, callback);
+    /* Restore slots to keys map backup if enable cluster. */
+    if (server.cluster_enabled) {
+        serverAssert(server.cluster->slots_to_keys->numele == 0);
+        raxFree(server.cluster->slots_to_keys);
+        server.cluster->slots_to_keys = buckup->slots_to_keys;
+        memcpy(server.cluster->slots_keys_count, buckup->slots_keys_count,
+                sizeof(server.cluster->slots_keys_count));
+    }
+
+    /* Release buckup. */
+    zfree(buckup->dbarray);
+    zfree(buckup);
 }
 
 int selectDb(client *c, int id) {
@@ -477,7 +561,18 @@ void signalModifiedKey(client *c, redisDb *db, robj *key) {
 }
 
 void signalFlushedDb(int dbid) {
-    touchWatchedKeysOnFlush(dbid);
+    int startdb, enddb;
+    if (dbid == -1) {
+        startdb = 0;
+        enddb = server.dbnum-1;
+    } else {
+        startdb = enddb = dbid;
+    }
+
+    for (int j = startdb; j <= enddb; j++) {
+        touchAllWatchedKeysInDb(&server.db[j], NULL);
+    }
+
     trackingInvalidateKeysOnFlush(dbid);
 }
 
@@ -592,7 +687,7 @@ void existsCommand(client *c) {
     int j;
 
     for (j = 1; j < c->argc; j++) {
-        if (lookupKeyRead(c->db,c->argv[j])) count++;
+        if (lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH)) count++;
     }
     addReplyLongLong(c,count);
 }
@@ -1133,9 +1228,14 @@ int dbSwapDatabases(long id1, long id2) {
      * However normally we only do this check for efficiency reasons
      * in dbAdd() when a list is created. So here we need to rescan
      * the list of clients blocked on lists and signal lists as ready
-     * if needed. */
+     * if needed.
+     *
+     * Also the swapdb should make transaction fail if there is any
+     * client watching keys */
     scanDatabaseForReadyLists(db1);
+    touchAllWatchedKeysInDb(db1, db2);
     scanDatabaseForReadyLists(db2);
+    touchAllWatchedKeysInDb(db2, db1);
     return C_OK;
 }
 
@@ -1703,11 +1803,25 @@ void slotToKeyDel(sds key) {
     slotToKeyUpdateKey(key,0);
 }
 
-void slotToKeyFlush(void) {
-    raxFree(server.cluster->slots_to_keys);
+/* Release the radix tree mapping Redis Cluster keys to slots. If 'async'
+ * is true, we release it asynchronously. */
+void freeSlotsToKeysMap(rax *rt, int async) {
+    if (async) {
+        freeSlotsToKeysMapAsync(rt);
+    } else {
+        raxFree(rt);
+    }
+}
+
+/* Empty the slots-keys map of Redis CLuster by creating a new empty one and
+ * freeing the old one. */
+void slotToKeyFlush(int async) {
+    rax *old = server.cluster->slots_to_keys;
+
     server.cluster->slots_to_keys = raxNew();
     memset(server.cluster->slots_keys_count,0,
            sizeof(server.cluster->slots_keys_count));
+    freeSlotsToKeysMap(old, async);
 }
 
 /* Pupulate the specified array of objects with keys in the specified slot.
diff --git a/src/debug.c b/src/debug.c
index 7112ff53510..a85ffe210bb 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -631,7 +631,11 @@ NULL
         for (int j = 2; j < c->argc; j++) {
             unsigned char digest[20];
             memset(digest,0,20); /* Start with a clean result */
-            robj *o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);
+
+            /* We don't use lookupKey because a debug command should
+             * work on logically expired keys */
+            dictEntry *de;
+            robj *o = ((de = dictFind(c->db->dict,c->argv[j]->ptr)) == NULL) ? NULL : dictGetVal(de);
             if (o) xorObjectDigest(c->db,c->argv[j],digest,o);
 
             sds d = sdsempty();
@@ -942,7 +946,7 @@ static void *getMcontextEip(ucontext_t *uc) {
     #endif
 #elif defined(__linux__)
     /* Linux */
-    #if defined(__i386__) || defined(__ILP32__)
+    #if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__))
     return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
     #elif defined(__X86_64__) || defined(__x86_64__)
     return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
@@ -1109,7 +1113,7 @@ void logRegisters(ucontext_t *uc) {
 /* Linux */
 #elif defined(__linux__)
     /* Linux x86 */
-    #if defined(__i386__) || defined(__ILP32__)
+    #if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__))
     serverLog(LL_WARNING,
     "\n"
     "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
@@ -1197,7 +1201,7 @@ void logRegisters(ucontext_t *uc) {
 	      "R10:%016lx R9 :%016lx\nR8 :%016lx R7 :%016lx\n"
 	      "R6 :%016lx R5 :%016lx\nR4 :%016lx R3 :%016lx\n"
 	      "R2 :%016lx R1 :%016lx\nR0 :%016lx EC :%016lx\n"
-	      "fp: %016lx ip:%016lx\n",
+	      "fp: %016lx ip:%016lx\n"
 	      "pc:%016lx sp:%016lx\ncpsr:%016lx fault_address:%016lx\n",
 	      (unsigned long) uc->uc_mcontext.arm_r10,
 	      (unsigned long) uc->uc_mcontext.arm_r9,
@@ -1489,7 +1493,7 @@ void logCurrentClient(void) {
     }
     /* Check if the first argument, usually a key, is found inside the
      * selected DB, and if so print info about the associated object. */
-    if (cc->argc >= 1) {
+    if (cc->argc > 1) {
         robj *val, *key;
         dictEntry *de;
 
@@ -1720,7 +1724,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
 );
 
     /* free(messages); Don't call free() with possibly corrupted memory. */
-    if (server.daemonize && server.supervised == 0) unlink(server.pidfile);
+    if (server.daemonize && server.supervised == 0 && server.pidfile) unlink(server.pidfile);
 
     /* Make sure we exit with the right signal at the end. So for instance
      * the core will be dumped if enabled. */
diff --git a/src/evict.c b/src/evict.c
index 5d398c6c958..e17bc8ea511 100644
--- a/src/evict.c
+++ b/src/evict.c
@@ -562,6 +562,8 @@ int freeMemoryIfNeeded(void) {
              * we are freeing removing the key, but we can't account for
              * that otherwise we would never exit the loop.
              *
+             * Same for CSC invalidation messages generated by signalModifiedKey.
+             *
              * AOF and Output buffer memory will be freed eventually so
              * we only care about memory used by the key space. */
             delta = (long long) zmalloc_used_memory();
@@ -570,12 +572,12 @@ int freeMemoryIfNeeded(void) {
                 dbAsyncDelete(db,keyobj);
             else
                 dbSyncDelete(db,keyobj);
-            signalModifiedKey(NULL,db,keyobj);
             latencyEndMonitor(eviction_latency);
             latencyAddSampleIfNeeded("eviction-del",eviction_latency);
             delta -= (long long) zmalloc_used_memory();
             mem_freed += delta;
             server.stat_evictedkeys++;
+            signalModifiedKey(NULL,db,keyobj);
             notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted",
                 keyobj, db->id);
             decrRefCount(keyobj);
diff --git a/src/lazyfree.c b/src/lazyfree.c
index 821dc50dff3..31197df2e74 100644
--- a/src/lazyfree.c
+++ b/src/lazyfree.c
@@ -136,16 +136,10 @@ void emptyDbAsync(redisDb *db) {
     bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,oldht2);
 }
 
-/* Empty the slots-keys map of Redis CLuster by creating a new empty one
- * and scheduling the old for lazy freeing. */
-void slotToKeyFlushAsync(void) {
-    rax *old = server.cluster->slots_to_keys;
-
-    server.cluster->slots_to_keys = raxNew();
-    memset(server.cluster->slots_keys_count,0,
-           sizeof(server.cluster->slots_keys_count));
-    atomicIncr(lazyfree_objects,old->numele);
-    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,old);
+/* Release the radix tree mapping Redis Cluster keys to slots asynchronously. */
+void freeSlotsToKeysMapAsync(rax *rt) {
+    atomicIncr(lazyfree_objects,rt->numele);
+    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,rt);
 }
 
 /* Release objects from the lazyfree thread. It's just decrRefCount()
@@ -157,9 +151,7 @@ void lazyfreeFreeObjectFromBioThread(robj *o) {
 
 /* Release a database from the lazyfree thread. The 'db' pointer is the
  * database which was substituted with a fresh one in the main thread
- * when the database was logically deleted. 'sl' is a skiplist used by
- * Redis Cluster in order to take the hash slots -> keys mapping. This
- * may be NULL if Redis Cluster is disabled. */
+ * when the database was logically deleted. */
 void lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2) {
     size_t numkeys = dictSize(ht1);
     dictRelease(ht1);
@@ -167,7 +159,7 @@ void lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2) {
     atomicDecr(lazyfree_objects,numkeys);
 }
 
-/* Release the skiplist mapping Redis Cluster keys to slots in the
+/* Release the radix tree mapping Redis Cluster keys to slots in the
  * lazyfree thread. */
 void lazyfreeFreeSlotsMapFromBioThread(rax *rt) {
     size_t len = rt->numele;
diff --git a/src/module.c b/src/module.c
index a253309edea..afea0063e2f 100644
--- a/src/module.c
+++ b/src/module.c
@@ -5441,7 +5441,13 @@ int moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *client
             raxRemove(Timers,(unsigned char*)ri.key,ri.key_len,NULL);
             zfree(timer);
         } else {
-            next_period = (expiretime-now)/1000; /* Scale to milliseconds. */
+            /* We call ustime() again instead of using the cached 'now' so that
+             * 'next_period' isn't affected by the time it took to execute
+             * previous calls to 'callback.
+             * We need to cast 'expiretime' so that the compiler will not treat
+             * the difference as unsigned (Causing next_period to be huge) in
+             * case expiretime < ustime() */
+            next_period = ((long long)expiretime-ustime())/1000; /* Scale to milliseconds. */
             break;
         }
     }
@@ -5454,7 +5460,16 @@ int moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *client
 
 /* Create a new timer that will fire after `period` milliseconds, and will call
  * the specified function using `data` as argument. The returned timer ID can be
- * used to get information from the timer or to stop it before it fires. */
+ * used to get information from the timer or to stop it before it fires.
+ * Note that for the common use case of a repeating timer (Re-registration
+ * of the timer inside the RedisModuleTimerProc callback) it matters when
+ * this API is called:
+ * If it is called at the beginning of 'callback' it means
+ * the event will triggered every 'period'.
+ * If it is called at the end of 'callback' it means
+ * there will 'period' milliseconds gaps between events.
+ * (If the time it takes to execute 'callback' is negligible the two
+ * statements above mean the same) */
 RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) {
     RedisModuleTimer *timer = zmalloc(sizeof(*timer));
     timer->module = ctx->module;
@@ -5476,7 +5491,8 @@ RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisMod
 
     /* We need to install the main event loop timer if it's not already
      * installed, or we may need to refresh its period if we just installed
-     * a timer that will expire sooner than any other else. */
+     * a timer that will expire sooner than any other else (i.e. the timer
+     * we just installed is the first timer in the Timers rax). */
     if (aeTimer != -1) {
         raxIterator ri;
         raxStart(&ri,Timers);
@@ -6988,6 +7004,7 @@ int RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) {
         server.module_child_pid = childpid;
         moduleForkInfo.done_handler = cb;
         moduleForkInfo.done_handler_user_data = user_data;
+        updateDictResizePolicy();
         serverLog(LL_VERBOSE, "Module fork started pid: %d ", childpid);
     }
     return childpid;
diff --git a/src/multi.c b/src/multi.c
index 3ce6d60ec27..33462a3fb18 100644
--- a/src/multi.c
+++ b/src/multi.c
@@ -340,31 +340,36 @@ void touchWatchedKey(redisDb *db, robj *key) {
     }
 }
 
-/* On FLUSHDB or FLUSHALL all the watched keys that are present before the
- * flush but will be deleted as effect of the flushing operation should
- * be touched. "dbid" is the DB that's getting the flush. -1 if it is
- * a FLUSHALL operation (all the DBs flushed). */
-void touchWatchedKeysOnFlush(int dbid) {
-    listIter li1, li2;
+/* Set CLIENT_DIRTY_CAS to all clients of DB when DB is dirty.
+ * It may happen in the following situations:
+ * FLUSHDB, FLUSHALL, SWAPDB
+ *
+ * replaced_with: for SWAPDB, the WATCH should be invalidated if
+ * the key exists in either of them, and skipped only if it
+ * doesn't exist in both. */
+void touchAllWatchedKeysInDb(redisDb *emptied, redisDb *replaced_with) {
+    listIter li;
     listNode *ln;
-
-    /* For every client, check all the waited keys */
-    listRewind(server.clients,&li1);
-    while((ln = listNext(&li1))) {
-        client *c = listNodeValue(ln);
-        listRewind(c->watched_keys,&li2);
-        while((ln = listNext(&li2))) {
-            watchedKey *wk = listNodeValue(ln);
-
-            /* For every watched key matching the specified DB, if the
-             * key exists, mark the client as dirty, as the key will be
-             * removed. */
-            if (dbid == -1 || wk->db->id == dbid) {
-                if (dictFind(wk->db->dict, wk->key->ptr) != NULL)
-                    c->flags |= CLIENT_DIRTY_CAS;
+    dictEntry *de;
+
+    if (dictSize(emptied->watched_keys) == 0) return;
+
+    dictIterator *di = dictGetSafeIterator(emptied->watched_keys);
+    while((de = dictNext(di)) != NULL) {
+        robj *key = dictGetKey(de);
+        list *clients = dictGetVal(de);
+        if (!clients) continue;
+        listRewind(clients,&li);
+        while((ln = listNext(&li))) {
+            client *c = listNodeValue(ln);
+            if (dictFind(emptied->dict, key->ptr)) {
+                c->flags |= CLIENT_DIRTY_CAS;
+            } else if (replaced_with && dictFind(replaced_with->dict, key->ptr)) {
+                c->flags |= CLIENT_DIRTY_CAS;
             }
         }
     }
+    dictReleaseIterator(di);
 }
 
 void watchCommand(client *c) {
diff --git a/src/networking.c b/src/networking.c
index 54de8ac54f3..dd2ca1aae96 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -249,8 +249,14 @@ int prepareClientToWrite(client *c) {
     if (!c->conn) return C_ERR; /* Fake client for AOF loading. */
 
     /* Schedule the client to write the output buffers to the socket, unless
-     * it should already be setup to do so (it has already pending data). */
-    if (!clientHasPendingReplies(c)) clientInstallWriteHandler(c);
+     * it should already be setup to do so (it has already pending data).
+     *
+     * If CLIENT_PENDING_READ is set, we're in an IO thread and should
+     * not install a write handler. Instead, it will be done by
+     * handleClientsWithPendingReadsUsingThreads() upon return.
+     */
+    if (!clientHasPendingReplies(c) && !(c->flags & CLIENT_PENDING_READ))
+            clientInstallWriteHandler(c);
 
     /* Authorize the caller to queue in the output buffer of this client. */
     return C_OK;
@@ -260,6 +266,9 @@ int prepareClientToWrite(client *c) {
  * Low level functions to add more data to output buffers.
  * -------------------------------------------------------------------------- */
 
+/* Attempts to add the reply to the static buffer in the client struct.
+ * Returns C_ERR if the buffer is full, or the reply list is not empty,
+ * in which case the reply must be added to the reply list. */
 int _addReplyToBuffer(client *c, const char *s, size_t len) {
     size_t available = sizeof(c->buf)-c->bufpos;
 
@@ -277,6 +286,8 @@ int _addReplyToBuffer(client *c, const char *s, size_t len) {
     return C_OK;
 }
 
+/* Adds the reply to the reply linked list.
+ * Note: some edits to this function need to be relayed to AddReplyFromClient. */
 void _addReplyProtoToList(client *c, const char *s, size_t len) {
     if (c->flags & CLIENT_CLOSE_AFTER_REPLY) return;
 
@@ -837,14 +848,40 @@ void addReplySubcommandSyntaxError(client *c) {
 /* Append 'src' client output buffers into 'dst' client output buffers. 
  * This function clears the output buffers of 'src' */
 void AddReplyFromClient(client *dst, client *src) {
-    if (prepareClientToWrite(dst) != C_OK)
+    /* If the source client contains a partial response due to client output
+     * buffer limits, propagate that to the dest rather than copy a partial
+     * reply. We don't wanna run the risk of copying partial response in case
+     * for some reason the output limits don't reach the same decision (maybe
+     * they changed) */
+    if (src->flags & CLIENT_CLOSE_ASAP) {
+        sds client = catClientInfoString(sdsempty(),dst);
+        freeClientAsync(dst);
+        serverLog(LL_WARNING,"Client %s scheduled to be closed ASAP for overcoming of output buffer limits.", client);
+        sdsfree(client);
         return;
+    }
+
+    /* First add the static buffer (either into the static buffer or reply list) */
     addReplyProto(dst,src->buf, src->bufpos);
+
+    /* We need to check with prepareClientToWrite again (after addReplyProto)
+     * since addReplyProto may have changed something (like CLIENT_CLOSE_ASAP) */
+    if (prepareClientToWrite(dst) != C_OK)
+        return;
+
+    /* We're bypassing _addReplyProtoToList, so we need to add the pre/post
+     * checks in it. */
+    if (dst->flags & CLIENT_CLOSE_AFTER_REPLY) return;
+
+    /* Concatenate the reply list into the dest */
     if (listLength(src->reply))
         listJoin(dst->reply,src->reply);
     dst->reply_bytes += src->reply_bytes;
     src->reply_bytes = 0;
     src->bufpos = 0;
+
+    /* Check output buffer limits */
+    asyncCloseClientOnOutputBufferLimitReached(dst);
 }
 
 /* Copy 'src' client output buffers into 'dst' client output buffers.
@@ -1597,6 +1634,7 @@ int processInlineBuffer(client *c) {
      * However the is an exception: masters may send us just a newline
      * to keep the connection active. */
     if (querylen != 0 && c->flags & CLIENT_MASTER) {
+        sdsfreesplitres(argv,argc);
         serverLog(LL_WARNING,"WARNING: Receiving inline protocol from master, master stream corruption? Closing the master connection and discarding the cached master.");
         setProtocolError("Master using the inline protocol. Desync?",c);
         return C_ERR;
@@ -1762,7 +1800,7 @@ int processMultibulkBuffer(client *c) {
                     c->qb_pos = 0;
                     /* Hint the sds library about the amount of bytes this string is
                      * going to contain. */
-                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2);
+                    c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-sdslen(c->querybuf));
                 }
             }
             c->bulklen = ll;
@@ -3309,6 +3347,12 @@ int handleClientsWithPendingReadsUsingThreads(void) {
             }
         }
         processInputBuffer(c);
+
+        /* We may have pending replies if a thread readQueryFromClient() produced
+         * replies and did not install a write handler (it can't).
+         */
+        if (!(c->flags & CLIENT_PENDING_WRITE) && clientHasPendingReplies(c))
+            clientInstallWriteHandler(c);
     }
 
     /* Update processed count on server */
diff --git a/src/object.c b/src/object.c
index 92eebb556fb..17a193b694a 100644
--- a/src/object.c
+++ b/src/object.c
@@ -1211,10 +1211,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
 /* This is a helper function for the OBJECT command. We need to lookup keys
  * without any modification of LRU or other parameters. */
 robj *objectCommandLookup(client *c, robj *key) {
-    dictEntry *de;
-
-    if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
-    return (robj*) dictGetVal(de);
+    return lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH|LOOKUP_NONOTIFY);
 }
 
 robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
diff --git a/src/rdb.c b/src/rdb.c
index ee9418c6b4e..3819918818c 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -84,7 +84,7 @@ void rdbReportError(int corruption_error, int linenum, char *reason, ...) {
     exit(1);
 }
 
-static int rdbWriteRaw(rio *rdb, void *p, size_t len) {
+static ssize_t rdbWriteRaw(rio *rdb, void *p, size_t len) {
     if (rdb && rioWrite(rdb,p,len) == 0)
         return -1;
     return len;
@@ -1412,6 +1412,7 @@ int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
         server.rdb_save_time_start = time(NULL);
         server.rdb_child_pid = childpid;
         server.rdb_child_type = RDB_CHILD_TYPE_DISK;
+        updateDictResizePolicy();
         return C_OK;
     }
     return C_OK; /* unreached */
@@ -2590,6 +2591,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
             server.rdb_save_time_start = time(NULL);
             server.rdb_child_pid = childpid;
             server.rdb_child_type = RDB_CHILD_TYPE_SOCKET;
+            updateDictResizePolicy();
             close(server.rdb_pipe_write); /* close write in parent so that it can detect the close on the child. */
             if (aeCreateFileEvent(server.el, server.rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {
                 serverPanic("Unrecoverable error creating server.rdb_pipe_read file event.");
diff --git a/src/redis-cli.c b/src/redis-cli.c
index 45a1228e60b..5456d3cb944 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -7073,7 +7073,9 @@ static void getRDB(clusterManagerNode *node) {
     } else {
         fprintf(stderr,"Transfer finished with success.\n");
     }
-    redisFree(s); /* Close the file descriptor ASAP as fsync() may take time. */
+    redisFree(s); /* Close the connection ASAP as fsync() may take time. */
+    if (node)
+        node->context = NULL;
     fsync(fd);
     close(fd);
     fprintf(stderr,"Transfer finished with success.\n");
diff --git a/src/redismodule.h b/src/redismodule.h
index 5da7d8658f0..2e8bc70397a 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -1018,13 +1018,13 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
 
 #define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1)))
 
+#define RMAPI_FUNC_SUPPORTED(func) (func != NULL)
+
 #else
 
 /* Things only defined for the modules core, not exported to modules
  * including this file. */
 #define RedisModuleString robj
 
-#define RMAPI_FUNC_SUPPORTED(func) (func != NULL)
-
 #endif /* REDISMODULE_CORE */
 #endif /* REDISMODULE_H */
diff --git a/src/replication.c b/src/replication.c
index 5f1868e8441..5ce9f02088f 100644
--- a/src/replication.c
+++ b/src/replication.c
@@ -1434,16 +1434,10 @@ static int useDisklessLoad() {
 }
 
 /* Helper function for readSyncBulkPayload() to make backups of the current
- * DBs before socket-loading the new ones. The backups may be restored later
- * or freed by disklessLoadRestoreBackups(). */
-redisDb *disklessLoadMakeBackups(void) {
-    redisDb *backups = zmalloc(sizeof(redisDb)*server.dbnum);
-    for (int i=0; i<server.dbnum; i++) {
-        backups[i] = server.db[i];
-        server.db[i].dict = dictCreate(&dbDictType,NULL);
-        server.db[i].expires = dictCreate(&keyptrDictType,NULL);
-    }
-    return backups;
+ * databases before socket-loading the new ones. The backups may be restored
+ * by disklessLoadRestoreBackup or freed by disklessLoadDiscardBackup later. */
+dbBackup *disklessLoadMakeBackup(void) {
+    return backupDb();
 }
 
 /* Helper function for readSyncBulkPayload(): when replica-side diskless
@@ -1451,30 +1445,15 @@ redisDb *disklessLoadMakeBackups(void) {
  * before loading the new ones from the socket.
  *
  * If the socket loading went wrong, we want to restore the old backups
- * into the server databases. This function does just that in the case
- * the 'restore' argument (the number of DBs to replace) is non-zero.
- *
- * When instead the loading succeeded we want just to free our old backups,
- * in that case the function will do just that when 'restore' is 0. */
-void disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags)
-{
-    if (restore) {
-        /* Restore. */
-        emptyDbGeneric(server.db,-1,empty_db_flags,replicationEmptyDbCallback);
-        for (int i=0; i<server.dbnum; i++) {
-            dictRelease(server.db[i].dict);
-            dictRelease(server.db[i].expires);
-            server.db[i] = backup[i];
-        }
-    } else {
-        /* Delete (Pass EMPTYDB_BACKUP in order to avoid firing module events) . */
-        emptyDbGeneric(backup,-1,empty_db_flags|EMPTYDB_BACKUP,replicationEmptyDbCallback);
-        for (int i=0; i<server.dbnum; i++) {
-            dictRelease(backup[i].dict);
-            dictRelease(backup[i].expires);
-        }
-    }
-    zfree(backup);
+ * into the server databases. */
+void disklessLoadRestoreBackup(dbBackup *buckup) {
+    restoreDbBackup(buckup);
+}
+
+/* Helper function for readSyncBulkPayload() to discard our old backups
+ * when the loading succeeded. */
+void disklessLoadDiscardBackup(dbBackup *buckup, int flag) {
+    discardDbBackup(buckup, flag, replicationEmptyDbCallback);
 }
 
 /* Asynchronously read the SYNC payload we receive from a master */
@@ -1483,7 +1462,7 @@ void readSyncBulkPayload(connection *conn) {
     char buf[PROTO_IOBUF_LEN];
     ssize_t nread, readlen, nwritten;
     int use_diskless_load = useDisklessLoad();
-    redisDb *diskless_load_backup = NULL;
+    dbBackup *diskless_load_backup = NULL;
     int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :
                                                         EMPTYDB_NO_FLAGS;
     off_t left;
@@ -1664,11 +1643,11 @@ void readSyncBulkPayload(connection *conn) {
         server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB)
     {
         /* Create a backup of server.db[] and initialize to empty
-         * dictionaries */
-        diskless_load_backup = disklessLoadMakeBackups();
+         * dictionaries. */
+        diskless_load_backup = disklessLoadMakeBackup();
     }
     /* We call to emptyDb even in case of REPL_DISKLESS_LOAD_SWAPDB
-     * (Where disklessLoadMakeBackups left server.db empty) because we
+     * (Where disklessLoadMakeBackup left server.db empty) because we
      * want to execute all the auxiliary logic of emptyDb (Namely,
      * fire module events) */
     emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
@@ -1698,14 +1677,14 @@ void readSyncBulkPayload(connection *conn) {
                 "from socket");
             cancelReplicationHandshake();
             rioFreeConn(&rdb, NULL);
+
+            /* Remove the half-loaded data in case we started with
+             * an empty replica. */
+            emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
+
             if (server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {
                 /* Restore the backed up databases. */
-                disklessLoadRestoreBackups(diskless_load_backup,1,
-                                           empty_db_flags);
-            } else {
-                /* Remove the half-loaded data in case we started with
-                 * an empty replica. */
-                emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
+                disklessLoadRestoreBackup(diskless_load_backup);
             }
 
             /* Note that there's no point in restarting the AOF on SYNC
@@ -1720,7 +1699,7 @@ void readSyncBulkPayload(connection *conn) {
             /* Delete the backup databases we created before starting to load
              * the new RDB. Now the RDB was loaded with success so the old
              * data is useless. */
-            disklessLoadRestoreBackups(diskless_load_backup,0,empty_db_flags);
+            disklessLoadDiscardBackup(diskless_load_backup, empty_db_flags);
         }
 
         /* Verify the end mark is correct. */
diff --git a/src/scripting.c b/src/scripting.c
index 6beb6cdbf1f..a75871ad358 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -410,9 +410,9 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
             lua_pushnil(lua); /* Use nil to start iteration. */
             while (lua_next(lua,-2)) {
                 /* Stack now: table, key, value */
-                luaReplyToRedisReply(c, lua); /* Return value. */
-                lua_pushvalue(lua,-1);        /* Dup key before consuming. */
+                lua_pushvalue(lua,-2);        /* Dup key before consuming. */
                 luaReplyToRedisReply(c, lua); /* Return key. */
+                luaReplyToRedisReply(c, lua); /* Return value. */
                 /* Stack now: table, key. */
                 maplen++;
             }
diff --git a/src/sds.c b/src/sds.c
index a723a42c33a..dc664ca9bc4 100644
--- a/src/sds.c
+++ b/src/sds.c
@@ -523,6 +523,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
     va_list cpy;
     char staticbuf[1024], *buf = staticbuf, *t;
     size_t buflen = strlen(fmt)*2;
+    int bufstrlen;
 
     /* We try to start using a static buffer for speed.
      * If not possible we revert to heap allocation. */
@@ -533,16 +534,19 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
         buflen = sizeof(staticbuf);
     }
 
-    /* Try with buffers two times bigger every time we fail to
+    /* Alloc enough space for buffer and \0 after failing to
      * fit the string in the current buffer size. */
     while(1) {
-        buf[buflen-2] = '\0';
         va_copy(cpy,ap);
-        vsnprintf(buf, buflen, fmt, cpy);
+        bufstrlen = vsnprintf(buf, buflen, fmt, cpy);
         va_end(cpy);
-        if (buf[buflen-2] != '\0') {
+        if (bufstrlen < 0) {
             if (buf != staticbuf) s_free(buf);
-            buflen *= 2;
+            return NULL;
+        }
+        if (((size_t)bufstrlen) >= buflen) {
+            if (buf != staticbuf) s_free(buf);
+            buflen = ((size_t)bufstrlen) + 1;
             buf = s_malloc(buflen);
             if (buf == NULL) return NULL;
             continue;
@@ -551,7 +555,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
     }
 
     /* Finally concat the obtained string to the SDS string and return it. */
-    t = sdscat(s, buf);
+    t = sdscatlen(s, buf, bufstrlen);
     if (buf != staticbuf) s_free(buf);
     return t;
 }
@@ -606,7 +610,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
     /* To avoid continuous reallocations, let's start with a buffer that
      * can hold at least two times the format string itself. It's not the
      * best heuristic but seems to work in practice. */
-    s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);
+    s = sdsMakeRoomFor(s, strlen(fmt)*2);
     va_start(ap,fmt);
     f = fmt;    /* Next format specifier byte to process. */
     i = initlen; /* Position of the next byte to write to dest str. */
@@ -1159,6 +1163,22 @@ int sdsTest(void) {
         test_cond("sdscatprintf() seems working in the base case",
             sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
 
+        sdsfree(x);
+        x = sdscatprintf(sdsempty(),"a%cb",0);
+        test_cond("sdscatprintf() seems working with \\0 inside of result",
+            sdslen(x) == 3 && memcmp(x,"a\0""b\0",4) == 0)
+
+        {
+            sdsfree(x);
+            char etalon[1024*1024];
+            for (size_t i = 0; i < sizeof(etalon); i++) {
+                etalon[i] = '0';
+            }
+            x = sdscatprintf(sdsempty(),"%0*d",(int)sizeof(etalon),0);
+            test_cond("sdscatprintf() can print 1MB",
+                sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 0)
+        }
+
         sdsfree(x);
         x = sdsnew("--");
         x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
diff --git a/src/sentinel.c b/src/sentinel.c
index 1bd82453ff1..969771d26ed 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -3509,14 +3509,13 @@ void sentinelSetCommand(client *c) {
                     "Reconfiguration of scripts path is denied for "
                     "security reasons. Check the deny-scripts-reconfig "
                     "configuration directive in your Sentinel configuration");
-                return;
+                goto seterr;
             }
 
             if (strlen(value) && access(value,X_OK) == -1) {
                 addReplyError(c,
                     "Notification script seems non existing or non executable");
-                if (changes) sentinelFlushConfig();
-                return;
+                goto seterr;
             }
             sdsfree(ri->notification_script);
             ri->notification_script = strlen(value) ? sdsnew(value) : NULL;
@@ -3529,15 +3528,14 @@ void sentinelSetCommand(client *c) {
                     "Reconfiguration of scripts path is denied for "
                     "security reasons. Check the deny-scripts-reconfig "
                     "configuration directive in your Sentinel configuration");
-                return;
+                goto seterr;
             }
 
             if (strlen(value) && access(value,X_OK) == -1) {
                 addReplyError(c,
                     "Client reconfiguration script seems non existing or "
                     "non executable");
-                if (changes) sentinelFlushConfig();
-                return;
+                goto seterr;
             }
             sdsfree(ri->client_reconfig_script);
             ri->client_reconfig_script = strlen(value) ? sdsnew(value) : NULL;
@@ -3587,8 +3585,7 @@ void sentinelSetCommand(client *c) {
         } else {
             addReplyErrorFormat(c,"Unknown option or number of arguments for "
                                   "SENTINEL SET '%s'", option);
-            if (changes) sentinelFlushConfig();
-            return;
+            goto seterr;
         }
 
         /* Log the event. */
@@ -3614,9 +3611,11 @@ void sentinelSetCommand(client *c) {
     return;
 
 badfmt: /* Bad format errors */
-    if (changes) sentinelFlushConfig();
     addReplyErrorFormat(c,"Invalid argument '%s' for SENTINEL SET '%s'",
         (char*)c->argv[badarg]->ptr,option);
+seterr:
+    if (changes) sentinelFlushConfig();
+    return;
 }
 
 /* Our fake PUBLISH command: it is actually useful only to receive hello messages
diff --git a/src/server.c b/src/server.c
index 831349a76d6..ece71834ee1 100644
--- a/src/server.c
+++ b/src/server.c
@@ -56,6 +56,10 @@
 #include <locale.h>
 #include <sys/socket.h>
 
+#ifdef __linux__
+#include <sys/mman.h>
+#endif
+
 /* Our shared "common" objects */
 
 struct sharedObjectsStruct shared;
@@ -882,7 +886,7 @@ struct redisCommand redisCommandTable[] = {
 
     /* GEORADIUS has store options that may write. */
     {"georadius",georadiusCommand,-6,
-     "write @geo",
+     "write use-memory @geo",
      0,georadiusGetKeys,1,1,1,0,0,0},
 
     {"georadius_ro",georadiusroCommand,-6,
@@ -890,7 +894,7 @@ struct redisCommand redisCommandTable[] = {
      0,georadiusGetKeys,1,1,1,0,0,0},
 
     {"georadiusbymember",georadiusbymemberCommand,-5,
-     "write @geo",
+     "write use-memory @geo",
      0,georadiusGetKeys,1,1,1,0,0,0},
 
     {"georadiusbymember_ro",georadiusbymemberroCommand,-5,
@@ -2019,6 +2023,9 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
             }
         }
     }
+    /* Just for the sake of defensive programming, to avoid forgeting to
+     * call this function when need. */
+    updateDictResizePolicy();
 
 
     /* AOF postponed flush: Try at every cron cycle if the slow fsync
@@ -2196,15 +2203,17 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
     /* Close clients that need to be closed asynchronous */
     freeClientsInAsyncFreeQueue();
 
+    /* Try to process blocked clients every once in while. Example: A module
+     * calls RM_SignalKeyAsReady from within a timer callback (So we don't
+     * visit processCommand() at all). */
+    handleClientsBlockedOnKeys();
+
     /* Before we are going to sleep, let the threads access the dataset by
      * releasing the GIL. Redis main thread will not touch anything at this
      * time. */
     if (moduleCount()) moduleReleaseGIL();
 
-    /* Try to process blocked clients every once in while. Example: A module
-     * calls RM_SignalKeyAsReady from within a timer callback (So we don't
-     * visit processCommand() at all). */
-    handleClientsBlockedOnKeys();
+    /* Do NOT add anything below moduleReleaseGIL !!! */
 }
 
 /* This function is called immediately after the event loop multiplexing
@@ -2213,6 +2222,9 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
 void afterSleep(struct aeEventLoop *eventLoop) {
     UNUSED(eventLoop);
 
+    /* Do NOT add anything above moduleAcquireGIL !!! */
+
+    /* Aquire the modules GIL so that their threads won't touch anything. */
     if (!ProcessingEventsWhileBlocked) {
         if (moduleCount()) moduleAcquireGIL();
     }
@@ -2553,7 +2565,7 @@ static void readOOMScoreAdj(void) {
  */
 int setOOMScoreAdj(int process_class) {
 
-    if (!server.oom_score_adj) return C_OK;
+    if (server.oom_score_adj == OOM_SCORE_ADJ_NO) return C_OK;
     if (process_class == -1)
         process_class = (server.masterhost ? CONFIG_OOM_REPLICA : CONFIG_OOM_MASTER);
 
@@ -2564,7 +2576,9 @@ int setOOMScoreAdj(int process_class) {
     int val;
     char buf[64];
 
-    val = server.oom_score_adj_base + server.oom_score_adj_values[process_class];
+    val = server.oom_score_adj_values[process_class];
+    if (server.oom_score_adj == OOM_SCORE_RELATIVE)
+        val += server.oom_score_adj_base;
     if (val > 1000) val = 1000;
     if (val < -1000) val = -1000;
 
@@ -4745,6 +4759,21 @@ void monitorCommand(client *c) {
 
 /* =================================== Main! ================================ */
 
+int checkIgnoreWarning(const char *warning) {
+    int argc, j;
+    sds *argv = sdssplitargs(server.ignore_warnings, &argc);
+    if (argv == NULL)
+        return 0;
+
+    for (j = 0; j < argc; j++) {
+        char *flag = argv[j];
+        if (!strcasecmp(flag, warning))
+            break;
+    }
+    sdsfreesplitres(argv,argc);
+    return j < argc;
+}
+
 #ifdef __linux__
 int linuxOvercommitMemoryValue(void) {
     FILE *fp = fopen("/proc/sys/vm/overcommit_memory","r");
@@ -4768,6 +4797,113 @@ void linuxMemoryWarnings(void) {
         serverLog(LL_WARNING,"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').");
     }
 }
+
+#ifdef __arm64__
+
+/* Get size in kilobytes of the Shared_Dirty pages of the calling process for the
+ * memory map corresponding to the provided address, or -1 on error. */
+static int smapsGetSharedDirty(unsigned long addr) {
+    int ret, in_mapping = 0, val = -1;
+    unsigned long from, to;
+    char buf[64];
+    FILE *f;
+
+    f = fopen("/proc/self/smaps", "r");
+    serverAssert(f);
+
+    while (1) {
+        if (!fgets(buf, sizeof(buf), f))
+            break;
+
+        ret = sscanf(buf, "%lx-%lx", &from, &to);
+        if (ret == 2)
+            in_mapping = from <= addr && addr < to;
+
+        if (in_mapping && !memcmp(buf, "Shared_Dirty:", 13)) {
+            ret = sscanf(buf, "%*s %d", &val);
+            serverAssert(ret == 1);
+            break;
+        }
+    }
+
+    fclose(f);
+    return val;
+}
+
+/* Older arm64 Linux kernels have a bug that could lead to data corruption
+ * during background save in certain scenarios. This function checks if the
+ * kernel is affected.
+ * The bug was fixed in commit ff1712f953e27f0b0718762ec17d0adb15c9fd0b
+ * titled: "arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect()"
+ * Return 1 if the kernel seems to be affected, and 0 otherwise. */
+int linuxMadvFreeForkBugCheck(void) {
+    int ret, pipefd[2];
+    pid_t pid;
+    char *p, *q, bug_found = 0;
+    const long map_size = 3 * 4096;
+
+    /* Create a memory map that's in our full control (not one used by the allocator). */
+    p = mmap(NULL, map_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+    serverAssert(p != MAP_FAILED);
+
+    q = p + 4096;
+
+    /* Split the memory map in 3 pages by setting their protection as RO|RW|RO to prevent
+     * Linux from merging this memory map with adjacent VMAs. */
+    ret = mprotect(q, 4096, PROT_READ | PROT_WRITE);
+    serverAssert(!ret);
+
+    /* Write to the page once to make it resident */
+    *(volatile char*)q = 0;
+
+    /* Tell the kernel that this page is free to be reclaimed. */
+#ifndef MADV_FREE
+#define MADV_FREE 8
+#endif
+    ret = madvise(q, 4096, MADV_FREE);
+    serverAssert(!ret);
+
+    /* Write to the page after being marked for freeing, this is supposed to take
+     * ownership of that page again. */
+    *(volatile char*)q = 0;
+
+    /* Create a pipe for the child to return the info to the parent. */
+    ret = pipe(pipefd);
+    serverAssert(!ret);
+
+    /* Fork the process. */
+    pid = fork();
+    serverAssert(pid >= 0);
+    if (!pid) {
+        /* Child: check if the page is marked as dirty, expecing 4 (kB).
+         * A value of 0 means the kernel is affected by the bug. */
+        if (!smapsGetSharedDirty((unsigned long)q))
+            bug_found = 1;
+
+        ret = write(pipefd[1], &bug_found, 1);
+        serverAssert(ret == 1);
+
+        exit(0);
+    } else {
+        /* Read the result from the child. */
+        ret = read(pipefd[0], &bug_found, 1);
+        serverAssert(ret == 1);
+
+        /* Reap the child pid. */
+        serverAssert(waitpid(pid, NULL, 0) == pid);
+    }
+
+    /* Cleanup */
+    ret = close(pipefd[0]);
+    serverAssert(!ret);
+    ret = close(pipefd[1]);
+    serverAssert(!ret);
+    ret = munmap(p, map_size);
+    serverAssert(!ret);
+
+    return bug_found;
+}
+#endif /* __arm64__ */
 #endif /* __linux__ */
 
 void createPidFile(void) {
@@ -4949,6 +5085,11 @@ void closeClildUnusedResourceAfterFork() {
     closeListeningSockets(0);
     if (server.cluster_enabled && server.cluster_config_file_lock_fd != -1)
         close(server.cluster_config_file_lock_fd);  /* don't care if this fails */
+
+    /* Clear server.pidfile, this is the parent pidfile which should not
+     * be touched (or deleted) by the child (on exit / crash) */
+    zfree(server.pidfile);
+    server.pidfile = NULL;
 }
 
 /* purpose is one of CHILD_TYPE_ types */
@@ -4969,7 +5110,6 @@ int redisFork(int purpose) {
         if (childpid == -1) {
             return -1;
         }
-        updateDictResizePolicy();
     }
     return childpid;
 }
@@ -5304,7 +5444,16 @@ int main(int argc, char **argv) {
         serverLog(LL_WARNING,"Server initialized");
     #ifdef __linux__
         linuxMemoryWarnings();
-    #endif
+    #if defined (__arm64__)
+        if (linuxMadvFreeForkBugCheck()) {
+            serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. Please upgrade to the latest stable kernel.");
+            if (!checkIgnoreWarning("ARM64-COW-BUG")) {
+                serverLog(LL_WARNING,"Redis will now exit to prevent data corruption. Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
+                exit(1);
+            }
+        }
+    #endif /* __arm64__ */
+    #endif /* __linux__ */
         moduleLoadFromQueue();
         ACLLoadUsersAtStartup();
         InitServerLast();
diff --git a/src/server.h b/src/server.h
index ae5a704fdac..96963c3b5bc 100644
--- a/src/server.h
+++ b/src/server.h
@@ -378,6 +378,11 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
 #define SET_OP_DIFF 1
 #define SET_OP_INTER 2
 
+/* oom-score-adj defines */
+#define OOM_SCORE_ADJ_NO 0
+#define OOM_SCORE_RELATIVE 1
+#define OOM_SCORE_ADJ_ABSOLUTE 2
+
 /* Redis maxmemory strategies. Instead of using just incremental number
  * for this defines, we use a set of flags so that testing for certain
  * properties common to multiple policies is faster. */
@@ -669,6 +674,11 @@ typedef struct redisDb {
     list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
 } redisDb;
 
+/* Declare database backup that include redis main DBs and slots to keys map.
+ * Definition is in db.c. We can't define it here since we define CLUSTER_SLOTS
+ * in cluster.h. */
+typedef struct dbBackup dbBackup;
+
 /* Client MULTI/EXEC state */
 typedef struct multiCmd {
     robj **argv;
@@ -1080,6 +1090,7 @@ struct redisServer {
     int sentinel_mode;          /* True if this instance is a Sentinel. */
     size_t initial_memory_usage; /* Bytes used after initialization. */
     int always_show_logo;       /* Show logo even for non-stdout logging. */
+    char *ignore_warnings;      /* Config: warnings that should be ignored. */
     /* Modules */
     dict *moduleapi;            /* Exported core APIs dictionary for modules. */
     dict *sharedapi;            /* Like moduleapi but containing the APIs that
@@ -1773,7 +1784,7 @@ void initClientMultiState(client *c);
 void freeClientMultiState(client *c);
 void queueMultiCommand(client *c);
 void touchWatchedKey(redisDb *db, robj *key);
-void touchWatchedKeysOnFlush(int dbid);
+void touchAllWatchedKeysInDb(redisDb *emptied, redisDb *replaced_with);
 void discardTransaction(client *c);
 void flagTransaction(client *c);
 void execCommandAbort(client *c, sds error);
@@ -2134,6 +2145,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
                        long long lru_clock, int lru_multiplier);
 #define LOOKUP_NONE 0
 #define LOOKUP_NOTOUCH (1<<0)
+#define LOOKUP_NONOTIFY (1<<1)
 void dbAdd(redisDb *db, robj *key, robj *val);
 int dbAddRDBLoad(redisDb *db, sds key, robj *val);
 void dbOverwrite(redisDb *db, robj *key, robj *val);
@@ -2147,11 +2159,14 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);
 
 #define EMPTYDB_NO_FLAGS 0      /* No flags. */
 #define EMPTYDB_ASYNC (1<<0)    /* Reclaim memory in another thread. */
-#define EMPTYDB_BACKUP (1<<2)   /* DB array is a backup for REPL_DISKLESS_LOAD_SWAPDB. */
 long long emptyDb(int dbnum, int flags, void(callback)(void*));
-long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*));
+long long emptyDbStructure(redisDb *dbarray, int dbnum, int async, void(callback)(void*));
 void flushAllDataAndResetRDB(int flags);
 long long dbTotalServerKeyCount();
+dbBackup *backupDb(void);
+void restoreDbBackup(dbBackup *buckup);
+void discardDbBackup(dbBackup *buckup, int flags, void(callback)(void*));
+
 
 int selectDb(client *c, int id);
 void signalModifiedKey(client *c, redisDb *db, robj *key);
@@ -2164,12 +2179,14 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor);
 int parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor);
 void slotToKeyAdd(sds key);
 void slotToKeyDel(sds key);
-void slotToKeyFlush(void);
 int dbAsyncDelete(redisDb *db, robj *key);
 void emptyDbAsync(redisDb *db);
-void slotToKeyFlushAsync(void);
+void slotToKeyFlush(int async);
 size_t lazyfreeGetPendingObjectsCount(void);
-void freeObjAsync(robj *o);
+void freeObjAsync(robj *obj);
+void freeSlotsToKeysMapAsync(rax *rt);
+void freeSlotsToKeysMap(rax *rt, int async);
+
 
 /* API to get key arguments from commands */
 int *getKeysPrepareResult(getKeysResult *result, int numkeys);
diff --git a/src/setproctitle.c b/src/setproctitle.c
index 5f91d7bfef0..1c91570eba9 100644
--- a/src/setproctitle.c
+++ b/src/setproctitle.c
@@ -50,6 +50,10 @@
 #if !HAVE_SETPROCTITLE
 #if (defined __linux || defined __APPLE__)
 
+#ifdef __GLIBC__
+#define HAVE_CLEARENV
+#endif
+
 extern char **environ;
 
 static struct {
@@ -80,11 +84,9 @@ static inline size_t spt_min(size_t a, size_t b) {
  * For discussion on the portability of the various methods, see
  * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
  */
-static int spt_clearenv(void) {
-#if __GLIBC__
-	clearenv();
-
-	return 0;
+int spt_clearenv(void) {
+#ifdef HAVE_CLEARENV
+	return clearenv();
 #else
 	extern char **environ;
 	static char **tmp;
@@ -100,34 +102,62 @@ static int spt_clearenv(void) {
 } /* spt_clearenv() */
 
 
-static int spt_copyenv(char *oldenv[]) {
+static int spt_copyenv(int envc, char *oldenv[]) {
 	extern char **environ;
+	char **envcopy = NULL;
 	char *eq;
 	int i, error;
+	int envsize;
 
 	if (environ != oldenv)
 		return 0;
 
-	if ((error = spt_clearenv()))
-		goto error;
+	/* Copy environ into envcopy before clearing it. Shallow copy is
+	 * enough as clearenv() only clears the environ array.
+	 */
+	envsize = (envc + 1) * sizeof(char *);
+	envcopy = malloc(envsize);
+	if (!envcopy)
+		return ENOMEM;
+	memcpy(envcopy, oldenv, envsize);
+
+	/* Note that the state after clearenv() failure is undefined, but we'll
+	 * just assume an error means it was left unchanged.
+	 */
+	if ((error = spt_clearenv())) {
+		environ = oldenv;
+		free(envcopy);
+		return error;
+	}
 
-	for (i = 0; oldenv[i]; i++) {
-		if (!(eq = strchr(oldenv[i], '=')))
+	/* Set environ from envcopy */
+	for (i = 0; envcopy[i]; i++) {
+		if (!(eq = strchr(envcopy[i], '=')))
 			continue;
 
 		*eq = '\0';
-		error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0;
+		error = (0 != setenv(envcopy[i], eq + 1, 1))? errno : 0;
 		*eq = '=';
 
-		if (error)
-			goto error;
+		/* On error, do our best to restore state */
+		if (error) {
+#ifdef HAVE_CLEARENV
+			/* We don't assume it is safe to free environ, so we
+			 * may leak it. As clearenv() was shallow using envcopy
+			 * here is safe.
+			 */
+			environ = envcopy;
+#else
+			free(envcopy);
+			free(environ);  /* Safe to free, we have just alloc'd it */
+			environ = oldenv;
+#endif
+			return error;
+		}
 	}
 
+	free(envcopy);
 	return 0;
-error:
-	environ = oldenv;
-
-	return error;
 } /* spt_copyenv() */
 
 
@@ -148,32 +178,57 @@ static int spt_copyargs(int argc, char *argv[]) {
 	return 0;
 } /* spt_copyargs() */
 
-
+/* Initialize and populate SPT to allow a future setproctitle()
+ * call.
+ *
+ * As setproctitle() basically needs to overwrite argv[0], we're
+ * trying to determine what is the largest contiguous block
+ * starting at argv[0] we can use for this purpose.
+ *
+ * As this range will overwrite some or all of the argv and environ
+ * strings, a deep copy of these two arrays is performed.
+ */
 void spt_init(int argc, char *argv[]) {
         char **envp = environ;
 	char *base, *end, *nul, *tmp;
-	int i, error;
+	int i, error, envc;
 
 	if (!(base = argv[0]))
 		return;
 
+	/* We start with end pointing at the end of argv[0] */
 	nul = &base[strlen(base)];
 	end = nul + 1;
 
+	/* Attempt to extend end as far as we can, while making sure
+	 * that the range between base and end is only allocated to
+	 * argv, or anything that immediately follows argv (presumably
+	 * envp).
+	 */
 	for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
 		if (!argv[i] || argv[i] < end)
 			continue;
 
-		end = argv[i] + strlen(argv[i]) + 1;
+		if (end >= argv[i] && end <= argv[i] + strlen(argv[i]))
+			end = argv[i] + strlen(argv[i]) + 1;
 	}
 
+	/* In case the envp array was not an immediate extension to argv,
+	 * scan it explicitly.
+	 */
 	for (i = 0; envp[i]; i++) {
 		if (envp[i] < end)
 			continue;
 
-		end = envp[i] + strlen(envp[i]) + 1;
+		if (end >= envp[i] && end <= envp[i] + strlen(envp[i]))
+			end = envp[i] + strlen(envp[i]) + 1;
 	}
+	envc = i;
 
+	/* We're going to deep copy argv[], but argv[0] will still point to
+	 * the old memory for the purpose of updating the title so we need
+	 * to keep the original value elsewhere.
+	 */
 	if (!(SPT.arg0 = strdup(argv[0])))
 		goto syerr;
 
@@ -194,8 +249,8 @@ void spt_init(int argc, char *argv[]) {
 	setprogname(tmp);
 #endif
 
-
-	if ((error = spt_copyenv(envp)))
+    /* Now make a full deep copy of the environment and argv[] */
+	if ((error = spt_copyenv(envc, envp)))
 		goto error;
 
 	if ((error = spt_copyargs(argc, argv)))
@@ -263,3 +318,14 @@ void setproctitle(const char *fmt, ...) {
 
 #endif /* __linux || __APPLE__ */
 #endif /* !HAVE_SETPROCTITLE */
+
+#ifdef SETPROCTITLE_TEST_MAIN
+int main(int argc, char *argv[]) {
+	spt_init(argc, argv);
+
+	printf("SPT.arg0: [%p] '%s'\n", SPT.arg0, SPT.arg0);
+	printf("SPT.base: [%p] '%s'\n", SPT.base, SPT.base);
+	printf("SPT.end: [%p] (%d bytes after base)'\n", SPT.end, (int) (SPT.end - SPT.base));
+	return 0;
+}
+#endif
diff --git a/src/sort.c b/src/sort.c
index aeef53e6a85..7fc5c634e07 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -270,7 +270,7 @@ void sortCommand(client *c) {
     }
 
     /* Lookup the key to sort. It must be of the right types */
-    if (storekey)
+    if (!storekey)
         sortval = lookupKeyRead(c->db,c->argv[1]);
     else
         sortval = lookupKeyWrite(c->db,c->argv[1]);
diff --git a/src/version.h b/src/version.h
index dbbd87338a6..f38cc273c43 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,2 +1,2 @@
-#define REDIS_VERSION "6.0.9"
-#define REDIS_VERSION_NUM 0x00060009
+#define REDIS_VERSION "6.0.10"
+#define REDIS_VERSION_NUM 0x0006000a


Test patch:
diff --git a/tests/cluster/cluster.tcl b/tests/cluster/cluster.tcl
index f9a0e180d20..ae0743c51cb 100644
--- a/tests/cluster/cluster.tcl
+++ b/tests/cluster/cluster.tcl
@@ -57,6 +57,11 @@ proc CI {n field} {
     get_info_field [R $n cluster info] $field
 }
 
+# Return the value of the specified INFO field.
+proc s {n field} {
+    get_info_field [R $n info] $field
+}
+
 # Assuming nodes are reest, this function performs slots allocation.
 # Only the first 'n' nodes are used.
 proc cluster_allocate_slots {n} {
diff --git a/tests/cluster/tests/16-transactions-on-replica.tcl b/tests/cluster/tests/16-transactions-on-replica.tcl
index 41083f421a4..baed15fbc91 100644
--- a/tests/cluster/tests/16-transactions-on-replica.tcl
+++ b/tests/cluster/tests/16-transactions-on-replica.tcl
@@ -15,6 +15,7 @@ set replica [Rn 1]
 
 test "Cant read from replica without READONLY" {
     $primary SET a 1
+    wait_for_ofs_sync $primary $replica
     catch {$replica GET a} err
     assert {[string range $err 0 4] eq {MOVED}}
 }
@@ -28,6 +29,7 @@ test "Can preform HSET primary and HGET from replica" {
     $primary HSET h a 1
     $primary HSET h b 2
     $primary HSET h c 3
+    wait_for_ofs_sync $primary $replica
     assert {[$replica HGET h a] eq {1}}
     assert {[$replica HGET h b] eq {2}}
     assert {[$replica HGET h c] eq {3}}
diff --git a/tests/cluster/tests/17-diskless-load-swapdb.tcl b/tests/cluster/tests/17-diskless-load-swapdb.tcl
new file mode 100644
index 00000000000..612818cb7ca
--- /dev/null
+++ b/tests/cluster/tests/17-diskless-load-swapdb.tcl
@@ -0,0 +1,79 @@
+# Check replica can restore database buckup correctly if fail to diskless load.
+
+source "../tests/includes/init-tests.tcl"
+
+test "Create a primary with a replica" {
+    create_cluster 1 1
+}
+
+test "Cluster should start ok" {
+    assert_cluster_state ok
+}
+
+test "Cluster is writable" {
+    cluster_write_test 0
+}
+
+test "Right to restore backups when fail to diskless load " {
+    set master [Rn 0]
+    set replica [Rn 1]
+    set master_id 0
+    set replica_id 1
+
+    $replica READONLY
+    $replica config set repl-diskless-load swapdb
+    $replica config set appendonly no
+    $replica config set save ""
+    $replica config rewrite
+    $master config set repl-backlog-size 1024
+    $master config set repl-diskless-sync yes
+    $master config set repl-diskless-sync-delay 0
+    $master config set rdb-key-save-delay 10000
+    $master config set rdbcompression no
+    $master config set appendonly no
+    $master config set save ""
+
+    # Write a key that belongs to slot 0
+    set slot0_key "06S"
+    $master set $slot0_key 1
+    after 100
+    assert_equal {1} [$replica get $slot0_key]
+    assert_equal $slot0_key [$replica CLUSTER GETKEYSINSLOT 0 1]
+
+    # Save an RDB and kill the replica
+    $replica save
+    kill_instance redis $replica_id
+
+    # Delete the key from master
+    $master del $slot0_key
+
+    # Replica must full sync with master when start because replication
+    # backlog size is very small, and dumping rdb will cost several seconds.
+    set num 10000
+    set value [string repeat A 1024]
+    set rd [redis_deferring_client redis $master_id]
+    for {set j 0} {$j < $num} {incr j} {
+        $rd set $j $value
+    }
+    for {set j 0} {$j < $num} {incr j} {
+        $rd read
+    }
+
+    # Start the replica again
+    restart_instance redis $replica_id
+    $replica READONLY
+
+    # Start full sync, wait till after db is flushed (backed up)
+    wait_for_condition 500 10 {
+        [s $replica_id loading] eq 1
+    } else {
+        fail "Fail to full sync"
+    }
+
+    # Kill master, abort full sync
+    kill_instance redis $master_id
+
+    # Replica keys and keys to slots map still both are right
+    assert_equal {1} [$replica get $slot0_key]
+    assert_equal $slot0_key [$replica CLUSTER GETKEYSINSLOT 0 1]
+}
diff --git a/tests/modules/blockedclient.c b/tests/modules/blockedclient.c
index 558e06502ea..1c485a44a4d 100644
--- a/tests/modules/blockedclient.c
+++ b/tests/modules/blockedclient.c
@@ -79,6 +79,105 @@ int acquire_gil(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
     return REDISMODULE_OK;
 }
 
+typedef struct {
+    RedisModuleString **argv;
+    int argc;
+    RedisModuleBlockedClient *bc;
+} bg_call_data;
+
+void *bg_call_worker(void *arg) {
+    bg_call_data *bg = arg;
+
+    // Get Redis module context
+    RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bg->bc);
+
+    // Acquire GIL
+    RedisModule_ThreadSafeContextLock(ctx);
+
+    // Call the command
+    const char* cmd = RedisModule_StringPtrLen(bg->argv[1], NULL);
+    RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", bg->argv + 2, bg->argc - 2);
+
+    // Release GIL
+    RedisModule_ThreadSafeContextUnlock(ctx);
+
+    // Reply to client
+    if (!rep) {
+        RedisModule_ReplyWithError(ctx, "NULL reply returned");
+    } else {
+        RedisModule_ReplyWithCallReply(ctx, rep);
+        RedisModule_FreeCallReply(rep);
+    }
+
+    // Unblock client
+    RedisModule_UnblockClient(bg->bc, NULL);
+
+    /* Free the arguments */
+    for (int i=0; i<bg->argc; i++)
+        RedisModule_FreeString(ctx, bg->argv[i]);
+    RedisModule_Free(bg->argv);
+    RedisModule_Free(bg);
+
+    // Free the Redis module context
+    RedisModule_FreeThreadSafeContext(ctx);
+
+    return NULL;
+}
+
+int do_bg_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    UNUSED(argv);
+    UNUSED(argc);
+
+    /* Make sure we're not trying to block a client when we shouldn't */
+    int flags = RedisModule_GetContextFlags(ctx);
+    int allFlags = RedisModule_GetContextFlagsAll();
+    if ((allFlags & REDISMODULE_CTX_FLAGS_MULTI) &&
+        (flags & REDISMODULE_CTX_FLAGS_MULTI)) {
+        RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not supported inside multi");
+        return REDISMODULE_OK;
+    }
+
+    /* Make a copy of the arguments and pass them to the thread. */
+    bg_call_data *bg = RedisModule_Alloc(sizeof(bg_call_data));
+    bg->argv = RedisModule_Alloc(sizeof(RedisModuleString*)*argc);
+    bg->argc = argc;
+    for (int i=0; i<argc; i++)
+        bg->argv[i] = RedisModule_HoldString(ctx, argv[i]);
+
+    /* Block the client */
+    bg->bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0);
+
+    /* Start a thread to handle the request */
+    pthread_t tid;
+    int res = pthread_create(&tid, NULL, bg_call_worker, bg);
+    assert(res == 0);
+
+    return REDISMODULE_OK;
+}
+
+int do_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
+    UNUSED(argv);
+    UNUSED(argc);
+
+    if(argc < 2){
+        return RedisModule_WrongArity(ctx);
+    }
+
+    const char* cmd = RedisModule_StringPtrLen(argv[1], NULL);
+
+    RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", argv + 2, argc - 2);
+    if(!rep){
+        RedisModule_ReplyWithError(ctx, "NULL reply returned");
+    }else{
+        RedisModule_ReplyWithCallReply(ctx, rep);
+        RedisModule_FreeCallReply(rep);
+    }
+
+    return REDISMODULE_OK;
+}
+
+
 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
     REDISMODULE_NOT_USED(argv);
     REDISMODULE_NOT_USED(argc);
@@ -89,5 +188,11 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
     if (RedisModule_CreateCommand(ctx, "acquire_gil", acquire_gil, "", 0, 0, 0) == REDISMODULE_ERR)
         return REDISMODULE_ERR;
 
+    if (RedisModule_CreateCommand(ctx, "do_rm_call", do_rm_call, "", 0, 0, 0) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx, "do_bg_rm_call", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
     return REDISMODULE_OK;
 }
diff --git a/tests/modules/fork.c b/tests/modules/fork.c
index 0443d9ef0c1..50889eef9c4 100644
--- a/tests/modules/fork.c
+++ b/tests/modules/fork.c
@@ -28,6 +28,12 @@ int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
         RedisModule_WrongArity(ctx);
         return REDISMODULE_OK;
     }
+
+    if(!RMAPI_FUNC_SUPPORTED(RedisModule_Fork)){
+        RedisModule_ReplyWithError(ctx, "Fork api is not supported in the current redis version");
+        return REDISMODULE_OK;
+    }
+
     RedisModule_StringToLongLong(argv[1], &code_to_exit_with);
     exitted_with_code = -1;
     child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef);
diff --git a/tests/support/redis.tcl b/tests/support/redis.tcl
index a90ac7f2999..3098a99f804 100644
--- a/tests/support/redis.tcl
+++ b/tests/support/redis.tcl
@@ -210,18 +210,44 @@ proc ::redis::redis_multi_bulk_read {id fd} {
     return $l
 }
 
+proc ::redis::redis_read_map {id fd} {
+    set count [redis_read_line $fd]
+    if {$count == -1} return {}
+    set d {}
+    set err {}
+    for {set i 0} {$i < $count} {incr i} {
+        if {[catch {
+            set k [redis_read_reply $id $fd] ; # key
+            set v [redis_read_reply $id $fd] ; # value
+            dict set d $k $v
+        } e] && $err eq {}} {
+            set err $e
+        }
+    }
+    if {$err ne {}} {return -code error $err}
+    return $d
+}
+
 proc ::redis::redis_read_line fd {
     string trim [gets $fd]
 }
 
+proc ::redis::redis_read_null fd {
+    gets $fd
+    return {}
+}
+
 proc ::redis::redis_read_reply {id fd} {
     set type [read $fd 1]
     switch -exact -- $type {
+        _ {redis_read_null $fd}
         : -
         + {redis_read_line $fd}
         - {return -code error [redis_read_line $fd]}
         $ {redis_bulk_read $fd}
+        > -
         * {redis_multi_bulk_read $id $fd}
+        % {redis_read_map $id $fd}
         default {
             if {$type eq {}} {
                 set ::redis::fd($id) {}
diff --git a/tests/unit/acl.tcl b/tests/unit/acl.tcl
index 12f59e74936..d35a012f4eb 100644
--- a/tests/unit/acl.tcl
+++ b/tests/unit/acl.tcl
@@ -138,15 +138,21 @@ start_server {tags {"acl"}} {
     # A regression test make sure that as long as there is a simple
     # category defining the commands, that it will be used as is.
     test {ACL GETUSER provides reasonable results} {
-        # Test for future commands where allowed
-        r ACL setuser additive reset +@all -@write
-        set cmdstr [dict get [r ACL getuser additive] commands]
-        assert_match {+@all -@write} $cmdstr
-
-        # Test for future commands are disallowed
-        r ACL setuser subtractive reset -@all +@read
-        set cmdstr [dict get [r ACL getuser subtractive] commands]
-        assert_match {-@all +@read} $cmdstr
+        set categories [r ACL CAT]
+
+        # Test that adding each single category will
+        # result in just that category with both +@all and -@all
+        foreach category $categories {
+            # Test for future commands where allowed
+            r ACL setuser additive reset +@all "-@$category"
+            set cmdstr [dict get [r ACL getuser additive] commands]
+            assert_equal "+@all -@$category" $cmdstr
+
+            # Test for future commands where disallowed
+            r ACL setuser restrictive reset -@all "+@$category"
+            set cmdstr [dict get [r ACL getuser restrictive] commands]
+            assert_equal "-@all +@$category" $cmdstr
+        }
     }
 
     test {ACL #5998 regression: memory leaks adding / removing subcommands} {
diff --git a/tests/unit/introspection-2.tcl b/tests/unit/introspection-2.tcl
index 55f6569568c..5496a1fb99f 100644
--- a/tests/unit/introspection-2.tcl
+++ b/tests/unit/introspection-2.tcl
@@ -5,11 +5,12 @@ proc cmdstat {cmd} {
 }
 
 start_server {tags {"introspection"}} {
-    test {TTL and TYPYE do not alter the last access time of a key} {
+    test {TTL, TYPE and EXISTS do not alter the last access time of a key} {
         r set foo bar
         after 3000
         r ttl foo
         r type foo
+        r exists foo
         assert {[r object idletime foo] >= 2}
     }
 
diff --git a/tests/unit/maxmemory.tcl b/tests/unit/maxmemory.tcl
index 0f64ddc18f6..3e7f381949b 100644
--- a/tests/unit/maxmemory.tcl
+++ b/tests/unit/maxmemory.tcl
@@ -241,3 +241,65 @@ test_slave_buffers {slave buffer are counted correctly} 1000000 10 0 1
 # test again with fewer (and bigger) commands without pipeline, but with eviction
 test_slave_buffers "replica buffer don't induce eviction" 100000 100 1 0
 
+start_server {tags {"maxmemory"}} {
+    test {client tracking don't cause eviction feedback loop} {
+        r config set maxmemory 0
+        r config set maxmemory-policy allkeys-lru
+
+        # 10 clients listening on tracking messages
+        set clients {}
+        for {set j 0} {$j < 10} {incr j} {
+            lappend clients [redis_deferring_client]
+        }
+        foreach rd $clients {
+            $rd HELLO 3
+            $rd read ; # Consume the HELLO reply
+            $rd CLIENT TRACKING on
+            $rd read ; # Consume the CLIENT reply
+        }
+
+        # populate 300 keys, with long key name and short value
+        for {set j 0} {$j < 300} {incr j} {
+            set key $j[string repeat x 1000]
+            r set $key x
+
+            # for each key, enable caching for this key
+            foreach rd $clients {
+                $rd get $key
+                $rd read
+            }
+        }
+
+        # we need to wait one second for the client querybuf excess memory to be
+        # trimmed by cron, otherwise the INFO used_memory and CONFIG maxmemory
+        # below (on slow machines) won't be "atomic" and won't trigger eviction.
+        after 1100
+
+        # set the memory limit which will cause a few keys to be evicted
+        # we need to make sure to evict keynames of a total size of more than
+        # 16kb since the (PROTO_REPLY_CHUNK_BYTES), only after that the
+        # invalidation messages have a chance to trigger further eviction.
+        set used [s used_memory]
+        set limit [expr {$used - 40000}]
+        r config set maxmemory $limit
+
+        # make sure some eviction happened
+        set evicted [s evicted_keys]
+        if {$::verbose} { puts "evicted: $evicted" }
+
+        # make sure we didn't drain the database
+        assert_range [r dbsize] 200 300
+
+        assert_range $evicted 10 50
+        foreach rd $clients {
+            $rd read ;# make sure we have some invalidation message waiting
+            $rd close
+        }
+
+        # eviction continues (known problem described in #8069)
+        # for now this test only make sures the eviction loop itself doesn't
+        # have feedback loop
+        set evicted [s evicted_keys]
+        if {$::verbose} { puts "evicted: $evicted" }
+    }
+}
diff --git a/tests/unit/moduleapi/blockedclient.tcl b/tests/unit/moduleapi/blockedclient.tcl
index 5a541d138bf..77d94c7bf56 100644
--- a/tests/unit/moduleapi/blockedclient.tcl
+++ b/tests/unit/moduleapi/blockedclient.tcl
@@ -14,4 +14,17 @@ start_server {tags {"modules"}} {
     	r acquire_gil
     	assert_equal {{Blocked client is not supported inside multi}} [r exec]
     }
+
+    test {blocked client reaches client output buffer limit} {
+        r hset hash big [string repeat x 50000]
+        r hset hash bada [string repeat x 50000]
+        r hset hash boom [string repeat x 50000]
+        r config set client-output-buffer-limit {normal 100000 0 0}
+        r client setname myclient
+        catch {r do_bg_rm_call hgetall hash} e
+        assert_match "*I/O error*" $e
+        reconnect
+        set clients [r client list]
+        assert_no_match "*name=myclient*" $clients
+    }
 }
diff --git a/tests/unit/multi.tcl b/tests/unit/multi.tcl
index e45a2d8f4dd..4b5c5acd82e 100644
--- a/tests/unit/multi.tcl
+++ b/tests/unit/multi.tcl
@@ -196,6 +196,29 @@ start_server {tags {"multi"}} {
         r exec
     } {PONG}
 
+    test {SWAPDB is able to touch the watched keys that exist} {
+        r flushall
+        r select 0
+        r set x 30
+        r watch x ;# make sure x (set to 30) doesn't change (SWAPDB will "delete" it)
+        r swapdb 0 1
+        r multi
+        r ping
+        r exec
+    } {}
+
+    test {SWAPDB is able to touch the watched keys that do not exist} {
+        r flushall
+        r select 1
+        r set x 30
+        r select 0
+        r watch x ;# make sure the key x (currently missing) doesn't change (SWAPDB will create it)
+        r swapdb 0 1
+        r multi
+        r ping
+        r exec
+    } {}
+
     test {WATCH is able to remember the DB a key belongs to} {
         r select 5
         r set x 30
diff --git a/tests/unit/other.tcl b/tests/unit/other.tcl
index 20a32e795f9..3ca70a77a7a 100644
--- a/tests/unit/other.tcl
+++ b/tests/unit/other.tcl
@@ -250,3 +250,23 @@ start_server {tags {"other"}} {
         r save
     } {OK}
 }
+
+start_server {tags {"other"}} {
+    test {Don't rehash if redis has child proecess} {
+        r config set save ""
+        r config set rdb-key-save-delay 1000000
+
+        populate 4096 "" 1
+        r bgsave
+        r mset k1 v1 k2 v2
+        # Hash table should not rehash
+        assert_no_match "*table size: 8192*" [r debug HTSTATS 9]
+        exec kill -9 [get_child_pid 0]
+        after 200
+
+        # Hash table should rehash since there is no child process,
+        # size is power of two and over 4098, so it is 16384
+        r set k3 v3
+        assert_match "*table size: 16384*" [r debug HTSTATS 9]
+    }
+}
diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl
index 6bcba4c3f9a..e3cd2f87fbb 100644
--- a/tests/unit/scripting.tcl
+++ b/tests/unit/scripting.tcl
@@ -533,6 +533,30 @@ start_server {tags {"scripting"}} {
         } e
         set e
     } {*wrong number*}
+
+    test {Script with RESP3 map} {
+        set expected_dict [dict create field value]
+        set expected_list [list field value]
+
+        # Sanity test for RESP3 without scripts
+        r HELLO 3
+        r hset hash field value
+        set res [r hgetall hash]
+        assert_equal $res $expected_dict
+
+        # Test RESP3 client with script in both RESP2 and RESP3 modes
+        set res [r eval {redis.setresp(3); return redis.call('hgetall', KEYS[1])} 1 hash]
+        assert_equal $res $expected_dict
+        set res [r eval {redis.setresp(2); return redis.call('hgetall', KEYS[1])} 1 hash]
+        assert_equal $res $expected_list
+
+        # Test RESP2 client with script in both RESP2 and RESP3 modes
+        r HELLO 2
+        set res [r eval {redis.setresp(3); return redis.call('hgetall', KEYS[1])} 1 hash]
+        assert_equal $res $expected_list
+        set res [r eval {redis.setresp(2); return redis.call('hgetall', KEYS[1])} 1 hash]
+        assert_equal $res $expected_list
+    }
 }
 
 # Start a new server since the last test in this stanza will kill the