Design and Implementation of Multi Part AOF in Redis 7.0

Design and Implementation of Multi Part AOF in Redis 7.0

As a very popular in-memory database, Redis achieves extremely high read and write performance by storing data in memory. However, once the process exits, all data in Redis will be lost.To solve this problem, Redis provides two persistence solutions, RDB and AOF, to save data from memory to disk, preventing data loss. This article will focus on the AOF persistence solution, discuss some of the issues it has, and explore the design and implementation details of Multi Part AOF (hereinafter referred to as MP-AOF) in Redis 7.0 (RC1 has been released).

1. AOF

AOF (Append Only File) persistence records each write command in an independent log file and replays the commands in the AOF file upon Redis startup to achieve data recovery.Since AOF records each write command to Redis in an appended manner, as the number of write commands processed by Redis increases, the AOF file will grow larger, and the time for command replay will also increase. To solve this problem, Redis introduces the AOF rewrite mechanism (hereinafter referred to as AOFRW). AOFRW removes redundant write commands from the AOF, rewrites them in an equivalent manner, and generates a new AOF file to reduce the size of the AOF file.

2. AOFRW

Figure 1 shows the implementation principle of AOFRW. When AOFRW is triggered, Redis first forks a child process to perform the background rewrite operation, which rewrites the data snapshot of Redis at the moment of forking into a temporary AOF file named temp-rewriteaof-bg-pid.aof.Since the rewrite operation is executed in the background by a child process, the main process can still respond to user commands normally during AOF rewriting. Therefore, to ensure that the child process can also obtain the incremental changes generated by the main process during the rewrite, the main process will not only write the executed write commands into aof_buf but also cache a copy in aof_rewrite_buf. In the later stage of the child process rewriting, the main process will send the accumulated data in aof_rewrite_buf to the child process using a pipe, and the child process will append this data to the temporary AOF file (for detailed principles, refer to [1]).When the main process receives a large write traffic, a lot of data may accumulate in aof_rewrite_buf, preventing the child process from consuming all the data in aof_rewrite_buf during the rewrite. At this time, the remaining data in aof_rewrite_buf will be processed by the main process when the rewrite ends.After the child process completes the rewrite operation and exits, the main process will handle subsequent tasks in backgroundRewriteDoneHandler. First, it will append the unconsumed data in aof_rewrite_buf to the temporary AOF file. Secondly, when everything is ready, Redis will atomically rename the temporary AOF file to server.aof_filename, at which point the original AOF file will be overwritten. Thus, the entire AOFRW process ends.Design and Implementation of Multi Part AOF in Redis 7.0Figure 1: AOFRW Implementation Principle

3. Issues with AOFRW

1. Memory Overhead

As shown in Figure 1, during AOFRW, the main process writes the data changes after forking into aof_rewrite_buf, and most of the contents in aof_rewrite_buf and aof_buf are duplicates, which leads to additional memory overhead.The memory size occupied by aof_rewrite_buf can be seen in the aof_rewrite_buffer_length field in Redis INFO. As shown below, under high write traffic, aof_rewrite_buffer_length occupies nearly the same memory space as aof_buffer_length, wasting almost double the memory.

aof_pending_rewrite:0
aof_buffer_length:35500
aof_rewrite_buffer_length:34000
aof_pending_bio_fsync:0

When the memory size occupied by aof_rewrite_buf exceeds a certain threshold, we will see the following information in the Redis logs. It can be seen that aof_rewrite_buf occupies 100MB of memory space, and 2135MB of data has been transmitted between the main process and the child process (the child process also has internal read buffer memory overhead when reading this data through the pipe).For an in-memory database like Redis, this is a significant overhead.

3351:M 25 Jan 2022 09:55:39.655 * Background append only file rewriting started by pid 68173351:M 25 Jan 2022 09:57:51.864 * AOF rewrite child asks to stop sending diffs.6817:C 25 Jan 2022 09:57:51.864 * Parent agreed to stop sending diffs. Finalizing AOF...6817:C 25 Jan 2022 09:57:51.864 * Concatenating 2135.60 MB of AOF diff received from parent.3351:M 25 Jan 2022 09:57:56.545 * Background AOF buffer size: 100 MB

The memory overhead caused by AOFRW may cause Redis memory to suddenly reach the maxmemory limit, affecting the normal writing of commands, and may even trigger the operating system to kill it with OOM Killer, leading to Redis being unavailable.

2. CPU Overhead

The CPU overhead mainly comes from three aspects, explained as follows:

  1. During AOFRW, the main process needs to spend CPU time writing data to aof_rewrite_buf and using the event loop to send data from aof_rewrite_buf to the child process:
/* Append data to the AOF rewrite buffer, allocating new blocks if needed. */
void aofRewriteBufferAppend(unsigned char *s, unsigned long len) {    // Other details omitted...
    /* Install a file event to send data to the rewrite child if there is     * not one already. */    if (!server.aof_stop_sending_diff &&        aeGetFileEvents(server.el,server.aof_pipe_write_data_to_child) == 0)    {        aeCreateFileEvent(server.el, server.aof_pipe_write_data_to_child,            AE_WRITABLE, aofChildWriteDiffData, NULL);    }
    // Other details omitted...
}
  1. In the later stage of the child process executing the rewrite operation, it will loop to read the incremental data sent from the main process in the pipe and append it to the temporary AOF file:
int rewriteAppendOnlyFile(char *filename) {     // Other details omitted...
    /* Read again a few times to get more data from the parent.     * We can't read forever (the server may receive data from clients     * faster than it is able to send data to the child), so we try to read     * some more data in a loop as soon as there is a good chance more data     * will come. If it looks like we are wasting time, we abort (this     * happens after 20 ms without new data). */    int nodata = 0;    mstime_t start = mstime();    while(mstime()-start < 1000 && nodata < 20) {        if (aeWait(server.aof_pipe_read_data_from_parent, AE_READABLE, 1) <= 0)        {            nodata++;            continue;        }        nodata = 0; /* Start counting from zero, we stop on N *contiguous*                       timeouts. */        aofReadDiffFromParent();    }
    // Other details omitted...
}
  1. After the child process completes the rewrite operation, the main process will perform cleanup work in backgroundRewriteDoneHandler. One of the tasks is to write the data that was not consumed in aof_rewrite_buf during the rewrite into the temporary AOF file. If there is a lot of leftover data in aof_rewrite_buf, this will also consume CPU time.
void backgroundRewriteDoneHandler(int exitcode, int bysignal) {    // Other details omitted...
    /* Flush the differences accumulated by the parent to the rewritten AOF. */    if (aofRewriteBufferWrite(newfd) == -1) {        serverLog(LL_WARNING,                "Error trying to flush the parent diff to the rewritten AOF: %s", strerror(errno));        close(newfd);        goto cleanup;     }
     // Other details omitted...
}

The CPU overhead caused by AOFRW may cause Redis to experience fluctuations in RT when executing commands, and even lead to client timeout issues.

3. Disk IO Overhead

As mentioned earlier, during AOFRW, the main process writes the executed write commands to aof_buf and also writes a copy to aof_rewrite_buf. The data in aof_buf will eventually be written to the currently used old AOF file, generating disk IO. At the same time, the data in aof_rewrite_buf will also be written to the newly rewritten AOF file, generating disk IO. Therefore, the same data will generate two disk IOs.

4. Code Complexity

Redis uses the six pipes shown below for data transmission and control interaction between the main process and the child process, making the entire AOFRW logic more complex and harder to understand.

 /* AOF pipes used to communicate between parent and child during rewrite. */ 
int aof_pipe_write_data_to_child; 
int aof_pipe_read_data_from_parent; 
int aof_pipe_write_ack_to_parent; 
int aof_pipe_read_ack_from_child; 
int aof_pipe_write_ack_to_child; 
int aof_pipe_read_ack_from_parent;

4. MP-AOF Implementation

1. Overview

As the name suggests, MP-AOF splits the original single AOF file into multiple AOF files. In MP-AOF, we divide AOF into three types:

  • BASE: Represents the base AOF, which is generally generated by the child process through rewriting, and there can be at most one of this type.
  • INCR: Represents the incremental AOF, which is generally created when AOFRW starts executing, and there can be multiple of this type.
  • HISTORY: Represents the historical AOF, which is derived from the changes in BASE and INCR AOF. Each time AOFRW successfully completes, the BASE and INCR AOF corresponding to this AOFRW will be marked as HISTORY, and HISTORY type AOFs will be automatically deleted by Redis.

To manage these AOF files, we introduce a manifest file to track and manage them. Additionally, to facilitate AOF backup and copying, we place all AOF files and the manifest file into a separate directory, the directory name is determined by the appenddirname configuration (a new configuration item in Redis 7.0).Design and Implementation of Multi Part AOF in Redis 7.0Figure 2: MP-AOF Rewrite PrincipleFigure 2 shows the approximate process of executing an AOFRW in MP-AOF. At the beginning, we will still fork a child process for the rewrite operation, and in the main process, we will simultaneously open a new INCR type AOF file. During the child process rewrite operation, all data changes will be written to this newly opened INCR AOF. The child process’s rewrite operation is completely independent and will not have any data and control interaction with the main process during the rewrite. The final rewrite operation will generate a BASE AOF. The newly generated BASE AOF and the newly opened INCR AOF represent all the data in Redis at that moment. When AOFRW ends, the main process will be responsible for updating the manifest file, adding the information of the newly generated BASE AOF and INCR AOF, and marking the previous BASE AOF and INCR AOF as HISTORY (these HISTORY AOFs will be asynchronously deleted by Redis). Once the manifest file is updated, the entire AOFRW process is marked as complete.As shown in Figure 2, we no longer need aof_rewrite_buf during AOFRW, thus eliminating the corresponding memory consumption. Meanwhile, there is no longer any data transfer and control interaction between the main process and the child process, thus eliminating the corresponding CPU overhead. Accordingly, the six pipes mentioned earlier and their corresponding code are also entirely removed, making the AOFRW logic clearer and simpler.

2. Key Implementations

Manifest

1) Representation in Memory

MP-AOF strongly relies on the manifest file, which is represented as the following structure in memory:

  • aofInfo: Represents the information of an AOF file, currently only includes the file name, file sequence number, and file type.
  • base_aof_info: Represents BASE AOF information, which is NULL if there is no BASE AOF.
  • incr_aof_list: Used to store the information of all INCR AOF files, all INCR AOFs will be arranged in the order they were opened.
  • history_aof_list: Used to store HISTORY AOF information, elements in history_aof_list are moved from base_aof_info and incr_aof_list.
typedef struct {    sds           file_name;  /* file name */    long long     file_seq;   /* file sequence */    aof_file_type file_type;  /* file type */} aofInfo;
typedef struct {    aofInfo     *base_aof_info;       /* BASE file information. NULL if there is no BASE file. */    list        *incr_aof_list;       /* INCR AOFs list. We may have multiple INCR AOF when rewrite fails. */    list        *history_aof_list;    /* HISTORY AOF list. When the AOFRW success, The aofInfo contained in                                         `base_aof_info` and `incr_aof_list` will be moved to this list. We                                         will delete these AOF files when AOFRW finish. */    long long   curr_base_file_seq;   /* The sequence number used by the current BASE file. */    long long   curr_incr_file_seq;   /* The sequence number used by the current INCR file. */    int         dirty;                /* 1 Indicates that the aofManifest in the memory is inconsistent with                                         disk, we need to persist it immediately. */} aofManifest;

To facilitate atomic modification and rollback operations, we use pointers to reference aofManifest in the redisServer structure.

struct redisServer {    // Other details omitted...
    aofManifest *aof_manifest;       /* Used to track AOFs. */
    // Other details omitted...
}

2) Representation on Disk

The manifest is essentially a text file containing multiple records, where each record corresponds to an AOF file’s information, displayed in a key/value pair format, making it easy for Redis to handle, read, and modify. Below is a possible content of a manifest file:

file appendonly.aof.1.base.rdb seq 1 type b
file appendonly.aof.1.incr.aof seq 1 type i
file appendonly.aof.2.incr.aof seq 2 type i

The manifest format itself needs to have a certain level of extensibility to support future functionalities. For example, it can easily support adding new key/values and annotations (similar to annotations in AOF), ensuring good forward compatibility.

file appendonly.aof.1.base.rdb seq 1 type b newkey newvalue
file appendonly.aof.1.incr.aof type i seq 1 # this is annotations
seq 2 type i
file appendonly.aof.2.incr.aof

File Naming Rules

Before MP-AOF, the AOF file name was set by the appendfilename parameter (default is appendonly.aof). In MP-AOF, we name multiple AOF files using the format basename.suffix. The appendfilename configuration content will serve as the basename part, while the suffix consists of three parts, formatted as seq.type.format, where:

  • seq is the file sequence number, starting from 1 and increasing monotonically, BASE and INCR have independent file sequence numbers.
  • type indicates the type of the AOF, representing whether this AOF file is BASE or INCR.
  • format indicates the encoding method of the internal AOF, as Redis supports RDB preamble mechanism, thus BASE AOF may be encoded in either RDB format or AOF format:
#define BASE_FILE_SUFFIX           ".base"
#define INCR_FILE_SUFFIX           ".incr"
#define RDB_FORMAT_SUFFIX          ".rdb"
#define AOF_FORMAT_SUFFIX          ".aof"
#define MANIFEST_NAME_SUFFIX       ".manifest"

Therefore, when using the default configuration of appendfilename, the possible naming of BASE, INCR, and manifest files is as follows:

appendonly.aof.1.base.rdb // RDB preamble enabled
appendonly.aof.1.base.aof // RDB preamble disabled
appendonly.aof.1.incr.aof
appendonly.aof.2.incr.aof

Compatibility Upgrade from Older Versions

Due to the strong reliance of MP-AOF on the manifest file, Redis will strictly follow the manifest’s instructions to load the corresponding AOF files during startup. However, when upgrading from an older version of Redis (prior to Redis 7.0), since there is no manifest file at that time, it is essential to support the capability of correctly and safely loading the old AOF.The identification capability is the primary step in this important process. Before actually loading the AOF file, we will check whether there is an AOF file named server.aof_filename in the Redis working directory. If it exists, it indicates that we may be upgrading from an older version of Redis. Next, we will continue to judge that when any of the following three conditions are met, we will consider this an upgrade startup:

  1. If the appenddirname directory does not exist.
  1. Or the appenddirname directory exists, but there is no corresponding manifest file in the directory.
  1. If the appenddirname directory exists and there is a manifest file in the directory, and the manifest file contains only BASE AOF related information, and the name of this BASE AOF is the same as server.aof_filename, and there is no file named server.aof_filename in the appenddirname directory.
/* Load the AOF files according the aofManifest pointed by am. */
int loadAppendOnlyFiles(aofManifest *am) {    // Other details omitted...
    /* If the 'server.aof_filename' file exists in dir, we may be starting     * from an old redis version. We will use enter upgrade mode in three situations.     *     * 1. If the 'server.aof_dirname' directory not exist     * 2. If the 'server.aof_dirname' directory exists but the manifest file is missing     * 3. If the 'server.aof_dirname' directory exists and the manifest file it contains     *    has only one base AOF record, and the file name of this base AOF is 'server.aof_filename',     *    and the 'server.aof_filename' file not exist in 'server.aof_dirname' directory     * */    if (fileExist(server.aof_filename)) {        if (!dirExists(server.aof_dirname) ||            (am->base_aof_info == NULL && listLength(am->incr_aof_list) == 0) ||            (am->base_aof_info != NULL && listLength(am->incr_aof_list) == 0 &&             !strcmp(am->base_aof_info->file_name, server.aof_filename) &&             !aofFileExist(server.aof_filename)))        {            aofUpgradePrepare(am);        }    }
    // Other details omitted...
}

Once identified as an upgrade startup, we will use the aofUpgradePrepare function to prepare for the upgrade.The upgrade preparation work mainly consists of three parts:

  1. Using server.aof_filename as the file name to construct a BASE AOF information.
  1. Persisting this BASE AOF information to the manifest file.
  1. Using rename to move the old AOF file to the appenddirname directory.
void aofUpgradePrepare(aofManifest *am) {    // Other details omitted...
    /* 1. Manually construct a BASE type aofInfo and add it to aofManifest. */    if (am->base_aof_info) aofInfoFree(am->base_aof_info);    aofInfo *ai = aofInfoCreate();    ai->file_name = sdsnew(server.aof_filename);    ai->file_seq = 1;    ai->file_type = AOF_FILE_TYPE_BASE;    am->base_aof_info = ai;    am->curr_base_file_seq = 1;    am->dirty = 1;
    /* 2. Persist the manifest file to AOF directory. */    if (persistAofManifest(am) != C_OK) {        exit(1);    }
    /* 3. Move the old AOF file to AOF directory. */    sds aof_filepath = makePath(server.aof_dirname, server.aof_filename);    if (rename(server.aof_filename, aof_filepath) == -1) {        sdsfree(aof_filepath);        exit(1);;    }
    // Other details omitted...
}

The upgrade preparation operation is Crash Safe, meaning that if any of the three steps above crash, we can correctly identify and retry the entire upgrade operation during the next startup.

Multi-File Loading and Progress Calculation

Redis records the loading progress when loading AOF and displays it through the loading_loaded_perc field in Redis INFO. In MP-AOF, the loadAppendOnlyFiles function will load AOF files based on the provided aofManifest. Before loading, we need to calculate the total size of all AOF files to be loaded in advance and pass it to the startLoading function, which will report the loading progress continuously in loadSingleAppendOnlyFile.Next, loadAppendOnlyFiles will sequentially load BASE AOF and INCR AOF based on aofManifest. After all AOF files are loaded, stopLoading will end the loading state.

int loadAppendOnlyFiles(aofManifest *am) {    // Other details omitted...
    /* Here we calculate the total size of all BASE and INCR files in     * advance, it will be set to `server.loading_total_bytes`. */    total_size = getBaseAndIncrAppendOnlyFilesSize(am);    startLoading(total_size, RDBFLAGS_AOF_PREAMBLE, 0);
    /* Load BASE AOF if needed. */    if (am->base_aof_info) {        aof_name = (char*)am->base_aof_info->file_name;        updateLoadingFileName(aof_name);        loadSingleAppendOnlyFile(aof_name);    }
    /* Load INCR AOFs if needed. */    if (listLength(am->incr_aof_list)) {        listNode *ln;        listIter li;
        listRewind(am->incr_aof_list, &li);        while ((ln = listNext(&li)) != NULL) {            aofInfo *ai = (aofInfo*)ln->value;            aof_name = (char*)ai->file_name;            updateLoadingFileName(aof_name);            loadSingleAppendOnlyFile(aof_name);        }
    }
    server.aof_current_size = total_size;    server.aof_rewrite_base_size = server.aof_current_size;    server.aof_fsync_offset = server.aof_current_size;
    stopLoading();
    // Other details omitted...
}

AOFRW Crash Safety

When the child process completes the rewrite operation, it creates a temporary AOF file named temp-rewriteaof-bg-pid.aof. At this point, this file is still invisible to Redis, as it has not yet been added to the manifest file. To ensure it can be recognized by Redis and correctly loaded during startup, we also need to perform a rename operation according to the naming rules mentioned earlier and include its information in the manifest file.The rename operation for the AOF file and the modification of the manifest file are two independent operations, but we must ensure the atomicity of these two operations to allow Redis to correctly load the corresponding AOF during startup. MP-AOF employs two designs to solve this problem:

  1. The name of the BASE AOF contains the file sequence number, ensuring that each newly created BASE AOF does not conflict with previous BASE AOFs.
  1. First perform the rename operation for the AOF, then modify the manifest file.

For illustration, let’s assume that before AOFRW starts, the manifest file content is as follows:

file appendonly.aof.1.base.rdb seq 1 type b
file appendonly.aof.1.incr.aof seq 1 type i

Then after AOFRW starts executing, the manifest file content will be as follows:

file appendonly.aof.1.base.rdb seq 1 type b
file appendonly.aof.1.incr.aof seq 1 type i
file appendonly.aof.2.incr.aof seq 2 type i

After the child process rewrite completes, in the main process, we will rename temp-rewriteaof-bg-pid.aof to appendonly.aof.2.base.rdb and add it to the manifest, while marking the previous BASE and INCR AOFs as HISTORY. At this point, the manifest file content will be as follows:

file appendonly.aof.2.base.rdb seq 2 type b
file appendonly.aof.1.base.rdb seq 1 type h
file appendonly.aof.1.incr.aof seq 1 type h
file appendonly.aof.2.incr.aof seq 2 type i

At this point, the result of this AOFRW is visible to Redis, and the HISTORY AOFs will be asynchronously cleaned by Redis.The backgroundRewriteDoneHandler function implements the above logic through seven steps:

  1. Before modifying the in-memory server.aof_manifest, first duplicate a temporary manifest structure, and subsequent modifications will be made to this temporary manifest. The benefit of this approach is that if any of the later steps fail, we can simply destroy the temporary manifest to roll back the entire operation, avoiding pollution of the global data structure server.aof_manifest.
  1. Obtain the new BASE AOF file name from the temporary manifest (denoted as new_base_filename) and mark the previous (if any) BASE AOF as HISTORY.
  1. Rename the temporary AOF file generated by the child process to new_base_filename.
  1. Mark all the previous INCR AOFs in the temporary manifest as HISTORY type.
  1. Persist the modifications corresponding to the temporary manifest to disk (persistAofManifest will ensure the atomicity of the manifest modification itself).
  1. If all the above steps succeed, we can safely let server.aof_manifest point to the temporary manifest structure (and free the previous manifest structure), thus making the entire modification visible to Redis.
  1. Clean up HISTORY type AOFs, this step is allowed to fail as it will not cause data consistency issues.
void backgroundRewriteDoneHandler(int exitcode, int bysignal) {    snprintf(tmpfile, 256, "temp-rewriteaof-bg-%d.aof",        (int)server.child_pid);
    /* 1. Dup a temporary aof_manifest for subsequent modifications. */    temp_am = aofManifestDup(server.aof_manifest);
    /* 2. Get a new BASE file name and mark the previous (if we have)     * as the HISTORY type. */    new_base_filename = getNewBaseFileNameAndMarkPreAsHistory(temp_am);
    /* 3. Rename the temporary aof file to 'new_base_filename'. */    if (rename(tmpfile, new_base_filename) == -1) {        aofManifestFree(temp_am);        goto cleanup;    }
    /* 4. Change the AOF file type in 'incr_aof_list' from AOF_FILE_TYPE_INCR     * to AOF_FILE_TYPE_HIST, and move them to the 'history_aof_list'. */    markRewrittenIncrAofAsHistory(temp_am);
    /* 5. Persist our modifications. */    if (persistAofManifest(temp_am) == C_ERR) {        bg_unlink(new_base_filename);        aofManifestFree(temp_am);        goto cleanup;    }
    /* 6. We can safely let `server.aof_manifest` point to 'temp_am' and free the previous one. */    aofManifestFreeAndUpdate(temp_am);
    /* 7. We don't care about the return value of `aofDelHistoryFiles`, because the history     * deletion failure will not cause any problems. */    aofDelHistoryFiles();
}

Support for AOF Truncate

In the event of a crash, the AOF file may experience incomplete writes, such as when a transaction has only written MULTI but has not yet written EXEC before Redis crashes. By default, Redis cannot load such incomplete AOFs, but Redis supports the AOF truncate feature (enabled through aof-load-truncated configuration). The principle is to track the last correct file offset of the AOF using server.aof_current_size, and then use the ftruncate function to delete all file content after that offset, ensuring the integrity of the AOF, even though some data may be lost.In MP-AOF, server.aof_current_size no longer represents the size of a single AOF file but rather the total size of all AOF files. Since only the last INCR AOF may experience incomplete writes, we introduce a separate field server.aof_last_incr_size to track the size of the last INCR AOF file. When the last INCR AOF experiences an incomplete write, we only need to delete the file content after server.aof_last_incr_size.

if (ftruncate(server.aof_fd, server.aof_last_incr_size) == -1) {      // Other details omitted... }

AOFRW Rate Limiting

Redis supports automatic execution of AOFRW when the AOF size exceeds a certain threshold. When a disk failure occurs or a code bug causes AOFRW to fail, Redis will repeatedly execute AOFRW until successful. Before MP-AOF, this did not seem like a significant issue (at most it consumes some CPU time and fork overhead). However, in MP-AOF, since each AOFRW opens an INCR AOF, and only upon successful AOFRW will the previous INCR and BASE be marked as HISTORY and deleted. Therefore, consecutive AOFRW failures will inevitably lead to the issue of multiple INCR AOFs coexisting. In extreme cases, if the AOFRW retry frequency is very high, we will see hundreds or thousands of INCR AOF files.To address this, we introduce an AOFRW rate limiting mechanism. If AOFRW fails consecutively three times, the next AOFRW will be forcibly delayed by 1 minute. If the next AOFRW still fails, it will be delayed by 2 minutes, and so on, delaying 4, 8, 16… with a maximum delay time of 1 hour.During the AOFRW rate limiting period, we can still use the bgrewriteaof command to immediately execute an AOFRW.

if (server.aof_state == AOF_ON &&    !hasActiveChildProcess() &&    server.aof_rewrite_perc &&    server.aof_current_size > server.aof_rewrite_min_size &&    !aofRewriteLimited()){    long long base = server.aof_rewrite_base_size ?        server.aof_rewrite_base_size : 1;    long long growth = (server.aof_current_size*100/base) - 100;    if (growth >= server.aof_rewrite_perc) {        rewriteAppendOnlyFileBackground();    }}

The introduction of the AOFRW rate limiting mechanism can also effectively avoid the CPU and fork overhead caused by high-frequency AOFRW retries. Many of the RT fluctuations in Redis are related to forking.

5. Conclusion

The introduction of MP-AOF successfully solves the adverse effects of the memory and CPU overhead caused by the previous AOFRW on Redis instances and even business access. At the same time, while addressing these issues, we also encountered many unforeseen challenges, primarily stemming from Redis’s large user base and diverse usage scenarios. Therefore, we must consider the problems users may encounter when using MP-AOF in various scenarios, such as compatibility, usability, and minimizing invasiveness to Redis code. These are all top priorities in the evolution of Redis community functions.Moreover, the introduction of MP-AOF also brings more imaginative space for Redis data persistence. For example, when aof-use-rdb-preamble is enabled, the BASE AOF is essentially an RDB file, so we do not need to perform a separate BGSAVE operation during full backups. We can directly back up the BASE AOF. MP-AOF supports the ability to turn off automatic cleaning of HISTORY AOFs, allowing those historical AOFs to be retained. Additionally, Redis now supports adding timestamp annotations in AOF, thus based on these, we can even implement a simple PITR capability (point-in-time recovery).The design prototype of MP-AOF comes from the binlog implementation of Tair for Redis enterprise edition [2], which is a core feature validated on Alibaba Cloud’s Tair service. On this core feature, Alibaba Cloud Tair has successfully built global multi-active, PITR, and other enterprise-level capabilities, meeting more business scenario needs for users. Today, we contribute this core capability to the Redis community, hoping that community users can also enjoy these enterprise-level features and better optimize and create their business code through these enterprise-level features. For more details on MP-AOF, please refer to the relevant PR (#9788), where more original designs and complete code can be found.[1]http://mysql.taobao.org/monthly/2018/12/06/[2]https://help.aliyun.com/document_detail/145956.html

Search and Recommendation Technology Practical Training Camp

Click to read the original text for details

Leave a Comment