redict/deps
Oran Agra d4e7ffb38c
Improve active defrag in jemalloc 5.2 (#9778)
Background:
Following the upgrade to jemalloc 5.2, there was a test that used to be flaky and
started failing consistently (on 32bit), so we disabled it ​(see #9645).

This is a test that i introduced in #7289 when i attempted to solve a rare stagnation
problem, and it later turned out i failed to solve it, ans what's more i added a test that
caused it to be not so rare, and as i mentioned, now in jemalloc 5.2 it became consistent on 32bit.

Stagnation can happen when all the slabs of the bin are equally utilized, so the decision
to move an allocation from a relatively empty slab to a relatively full one, will never
happen, and in that test all the slabs are at 50% utilization, so the defragger could just
keep scanning the keyspace and not move anything.

What this PR changes:
* First, finally in jemalloc 5.2 we have the count of non-full slabs, so when we compare
  the utilization of the current slab, we can compare it to the average utilization of the non-full
  slabs in our bin, instead of the total average of our bin. this takes the full slabs out of the game,
  since they're not candidates for migration (neither source nor target).
* Secondly, We add some 12% (100/8) to the decision to defrag an allocation, this is the part
  that aims to avoid stagnation, and it's especially important since the above mentioned change
  can get us closer to stagnation.
* Thirdly, since jemalloc 5.2 adds sharded bins, we take into account all shards (something
  that's missing from the original PR that merged it), this isn't expected to make any difference
  since anyway there should be just one shard.

How this was benchmarked.
What i did was run the memefficiency test unit with `--verbose` and compare the defragger hits
and misses the tests reported.
At first, when i took into consideration only the non-full slabs, it got a lot worse (i got into
stagnation, or just got a lot of misses and a lot of hits), but when i added the 10% i got back
to results that were slightly better than the ones of the jemalloc 5.1 branch. i.e. full defragmentation
was achieved with fewer hits (relocations), and fewer misses (keyspace scans).
2021-11-21 13:35:39 +02:00
..
hdr_histogram Extended redis-benchmark instant metrics and overall latency report (#7600) 2020-08-25 21:21:29 +03:00
hiredis hiredis: improve calloc() overflow fix. (#9630) 2021-10-12 20:16:29 +03:00
jemalloc Improve active defrag in jemalloc 5.2 (#9778) 2021-11-21 13:35:39 +02:00
linenoise Use fchmod to update command history file. (#9447) 2021-09-02 10:00:00 +03:00
lua Lua: Use all characters to calculate string hash (#9449) 2021-09-09 12:47:26 +03:00
Makefile Handle cross-compiling when configuring jemalloc. (#9659) 2021-10-24 10:15:32 +03:00
README.md Added jemalloc subtree upgrade instructions. 2021-10-12 12:55:35 +03:00

This directory contains all Redis dependencies, except for the libc that should be provided by the operating system.

  • Jemalloc is our memory allocator, used as replacement for libc malloc on Linux by default. It has good performances and excellent fragmentation behavior. This component is upgraded from time to time.
  • hiredis is the official C client library for Redis. It is used by redis-cli, redis-benchmark and Redis Sentinel. It is part of the Redis official ecosystem but is developed externally from the Redis repository, so we just upgrade it as needed.
  • linenoise is a readline replacement. It is developed by the same authors of Redis but is managed as a separated project and updated as needed.
  • lua is Lua 5.1 with minor changes for security and additional libraries.

How to upgrade the above dependencies

Jemalloc

Jemalloc is modified with changes that allow us to implement the Redis active defragmentation logic. However this feature of Redis is not mandatory and Redis is able to understand if the Jemalloc version it is compiled against supports such Redis-specific modifications. So in theory, if you are not interested in the active defragmentation, you can replace Jemalloc just following these steps:

  1. Remove the jemalloc directory.
  2. Substitute it with the new jemalloc source tree.
  3. Edit the Makefile located in the same directory as the README you are reading, and change the --with-version in the Jemalloc configure script options with the version you are using. This is required because otherwise Jemalloc configuration script is broken and will not work nested in another git repository.

However note that we change Jemalloc settings via the configure script of Jemalloc using the --with-lg-quantum option, setting it to the value of 3 instead of 4. This provides us with more size classes that better suit the Redis data structures, in order to gain memory efficiency.

If you want to upgrade Jemalloc while also providing support for active defragmentation, in addition to the above steps you need to perform the following additional steps:

  1. In Jemalloc tree, file include/jemalloc/jemalloc_macros.h.in, make sure to add #define JEMALLOC_FRAG_HINT.
  2. Implement the function je_get_defrag_hint() inside src/jemalloc.c. You can see how it is implemented in the current Jemalloc source tree shipped with Redis, and rewrite it according to the new Jemalloc internals, if they changed, otherwise you could just copy the old implementation if you are upgrading just to a similar version of Jemalloc.

Updating/upgrading jemalloc

The jemalloc directory is pulled as a subtee from the upstream jemalloc github repo. To update it you should run from the project root:

  1. git subtree pull --prefix deps/jemalloc https://github.com/jemalloc/jemalloc.git <version-tag> --squash
    This should hopefully merge the local changes into the new version.
  2. In case any conflicts arise (due to our changes) you'll need to resolve them and commit.
  3. Reconfigure jemalloc:
rm deps/jemalloc/VERSION deps/jemalloc/configure
cd deps/jemalloc
./autogen.sh --with-version=<version-tag>-0-g0
  1. Update jemalloc's version in deps/Makefile: search for "--with-version=<old-version-tag>-0-g0" and update it accordingly.
  2. Commit the changes (VERSION,configure,Makefile).

Hiredis

Hiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:

  1. Check with diff if hiredis API changed and what impact it could have in Redis.
  2. Make sure that the SDS library inside Hiredis and inside Redis are compatible.
  3. After the upgrade, run the Redis Sentinel test.
  4. Check manually that redis-cli and redis-benchmark behave as expected, since we have no tests for CLI utilities currently.

Linenoise

Linenoise is rarely upgraded as needed. The upgrade process is trivial since Redis uses a non modified version of linenoise, so to upgrade just do the following:

  1. Remove the linenoise directory.
  2. Substitute it with the new linenoise source tree.

Lua

We use Lua 5.1 and no upgrade is planned currently, since we don't want to break Lua scripts for new Lua features: in the context of Redis Lua scripts the capabilities of 5.1 are usually more than enough, the release is rock solid, and we definitely don't want to break old scripts.

So upgrading of Lua is up to the Redis project maintainers and should be a manual procedure performed by taking a diff between the different versions.

Currently we have at least the following differences between official Lua 5.1 and our version:

  1. Makefile is modified to allow a different compiler than GCC.
  2. We have the implementation source code, and directly link to the following external libraries: lua_cjson.o, lua_struct.o, lua_cmsgpack.o and lua_bit.o.
  3. There is a security fix in ldo.c, line 498: The check for LUA_SIGNATURE[0] is removed in order to avoid direct bytecode execution.