Merge 2954eb0e29dd65688d2aec1ea1332d1ba2374cae into 26c6f1af9b29d525831c7fa9840ab3e47ed7b700
This commit is contained in:
commit
8fb48e9222
18
src/aof.c
18
src/aof.c
@ -1011,16 +1011,22 @@ int startAppendOnly(void) {
|
||||
* the first call is short, there is a end-of-space condition, so the next
|
||||
* is likely to fail. However apparently in modern systems this is no longer
|
||||
* true, and in general it looks just more resilient to retry the write. If
|
||||
* there is an actual error condition we'll get it at the next try. */
|
||||
ssize_t aofWrite(int fd, const char *buf, size_t len) {
|
||||
ssize_t nwritten = 0, totwritten = 0;
|
||||
* there is an actual error condition we'll get it at the next try.
|
||||
* We also check for aof-max-size limit here returning custom error on exceed. */
|
||||
ssize_t aofWrite(int fd, const char *buf, size_t len, off_t aof_current_size, unsigned long long aof_max_size) {
|
||||
ssize_t nwritten = 0, totwritten = 0, nonewritten = -1;
|
||||
|
||||
if (aof_max_size && (unsigned long long)aof_current_size >= aof_max_size) {
|
||||
errno = EFBIG;
|
||||
return nonewritten;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
nwritten = write(fd, buf, len);
|
||||
|
||||
if (nwritten < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return totwritten ? totwritten : -1;
|
||||
return totwritten ? totwritten : nonewritten;
|
||||
}
|
||||
|
||||
len -= nwritten;
|
||||
@ -1120,7 +1126,7 @@ void flushAppendOnlyFile(int force) {
|
||||
}
|
||||
|
||||
latencyStartMonitor(latency);
|
||||
nwritten = aofWrite(server.aof_fd, server.aof_buf, sdslen(server.aof_buf));
|
||||
nwritten = aofWrite(server.aof_fd, server.aof_buf, sdslen(server.aof_buf), server.aof_current_size, server.aof_max_size);
|
||||
latencyEndMonitor(latency);
|
||||
/* We want to capture different events for delayed writes:
|
||||
* when the delay happens with a pending fsync, or with a saving child
|
||||
@ -1152,7 +1158,7 @@ void flushAppendOnlyFile(int force) {
|
||||
/* Log the AOF write error and record the error code. */
|
||||
if (nwritten == -1) {
|
||||
if (can_log) {
|
||||
serverLog(LL_WARNING, "Error writing to the AOF file: %s", strerror(errno));
|
||||
serverLog(LL_WARNING, "Error writing to the AOF file: %s", getAofWriteErrStr(errno));
|
||||
}
|
||||
server.aof_last_write_errno = errno;
|
||||
} else {
|
||||
|
@ -3328,6 +3328,7 @@ standardConfig static_configs[] = {
|
||||
/* Unsigned Long Long configs */
|
||||
createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
|
||||
createULongLongConfig("cluster-link-sendbuf-limit", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.cluster_link_msg_queue_limit_bytes, 0, MEMORY_CONFIG, NULL, NULL),
|
||||
createULongLongConfig("aof-max-size", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.aof_max_size, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
|
||||
/* Size_t configs */
|
||||
createSizeTConfig("hash-max-listpack-entries", "hash-max-ziplist-entries", MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_listpack_entries, 512, INTEGER_CONFIG, NULL, NULL),
|
||||
|
@ -179,7 +179,7 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx,
|
||||
"Writable scripts are blocked. Use 'no-writes' flag for read only scripts. "
|
||||
"AOF error: %s",
|
||||
server.extended_redis_compat ? "Redis" : SERVER_TITLE,
|
||||
strerror(server.aof_last_write_errno));
|
||||
getAofWriteErrStr(server.aof_last_write_errno));
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
|
21
src/server.c
21
src/server.c
@ -4626,13 +4626,17 @@ int writeCommandsDeniedByDiskError(void) {
|
||||
return DISK_ERROR_TYPE_NONE;
|
||||
}
|
||||
|
||||
char *getAofWriteErrStr(int error_code) {
|
||||
return (errno == EFBIG) ? "Reached aof-max-size" : strerror(error_code);
|
||||
}
|
||||
|
||||
sds writeCommandsGetDiskErrorMessage(int error_code) {
|
||||
sds ret = NULL;
|
||||
if (error_code == DISK_ERROR_TYPE_RDB) {
|
||||
ret = sdsdup(shared.bgsaveerr->ptr);
|
||||
} else {
|
||||
ret = sdscatfmt(sdsempty(), "-MISCONF Errors writing to the AOF file: %s\r\n",
|
||||
strerror(server.aof_last_write_errno));
|
||||
getAofWriteErrStr(server.aof_last_write_errno));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -5837,10 +5841,17 @@ sds genValkeyInfoString(dict *section_dict, int all_sections, int everything) {
|
||||
"module_fork_last_cow_size:%zu\r\n", server.stat_module_cow_bytes));
|
||||
|
||||
if (server.aof_enabled) {
|
||||
char aof_current_size_hdsk[64];
|
||||
char aof_max_size_hdsk[64];
|
||||
bytesToHuman(aof_current_size_hdsk, sizeof(aof_current_size_hdsk), (unsigned long long)server.aof_current_size);
|
||||
bytesToHuman(aof_max_size_hdsk, sizeof(aof_max_size_hdsk), server.aof_max_size);
|
||||
info = sdscatprintf(
|
||||
info,
|
||||
FMTARGS(
|
||||
"aof_current_size:%lld\r\n", (long long)server.aof_current_size,
|
||||
"aof_current_size_human:%s\r\n", aof_current_size_hdsk,
|
||||
"aof_max_size:%lld\r\n", server.aof_max_size,
|
||||
"aof_max_size_human:%s\r\n", aof_max_size_hdsk,
|
||||
"aof_base_size:%lld\r\n", (long long)server.aof_rewrite_base_size,
|
||||
"aof_pending_rewrite:%d\r\n", server.aof_rewrite_scheduled,
|
||||
"aof_buffer_length:%zu\r\n", sdslen(server.aof_buf),
|
||||
@ -7150,6 +7161,14 @@ __attribute__((weak)) int main(int argc, char **argv) {
|
||||
server.maxmemory);
|
||||
}
|
||||
|
||||
/* Warning the user about suspicious aof-max-size setting. */
|
||||
if (server.aof_max_size > 0 && server.aof_max_size < 1024 * 1024) {
|
||||
serverLog(LL_WARNING,
|
||||
"WARNING: You specified a aof-max-size value that is less than 1MB (current value is %llu bytes). Are "
|
||||
"you sure this is what you really want?",
|
||||
server.aof_max_size);
|
||||
}
|
||||
|
||||
serverSetCpuAffinity(server.server_cpulist);
|
||||
setOOMScoreAdj(-1);
|
||||
|
||||
|
@ -1811,6 +1811,7 @@ struct valkeyServer {
|
||||
off_t aof_rewrite_min_size; /* the AOF file is at least N bytes. */
|
||||
off_t aof_rewrite_base_size; /* AOF size on latest startup or rewrite. */
|
||||
off_t aof_current_size; /* AOF current size (Including BASE + INCRs). */
|
||||
unsigned long long aof_max_size; /* Max number of disk bytes to use for AOF */
|
||||
off_t aof_last_incr_size; /* The size of the latest incr AOF. */
|
||||
off_t aof_last_incr_fsync_offset; /* AOF offset which is already requested to be synced to disk.
|
||||
* Compare with the aof_last_incr_size. */
|
||||
@ -2968,6 +2969,7 @@ int allPersistenceDisabled(void);
|
||||
#define DISK_ERROR_TYPE_RDB 2 /* Don't accept writes: RDB errors. */
|
||||
#define DISK_ERROR_TYPE_NONE 0 /* No problems, we can accept writes. */
|
||||
int writeCommandsDeniedByDiskError(void);
|
||||
char *getAofWriteErrStr(int);
|
||||
sds writeCommandsGetDiskErrorMessage(int);
|
||||
|
||||
/* RDB persistence */
|
||||
|
64
tests/unit/aof-max-size.tcl
Normal file
64
tests/unit/aof-max-size.tcl
Normal file
@ -0,0 +1,64 @@
|
||||
proc setup {{size 1}} {
|
||||
r set k v
|
||||
r config set aof-max-size $size
|
||||
r set k2 v2
|
||||
}
|
||||
|
||||
proc cleanup {} {
|
||||
r config set aof-max-size 0
|
||||
r flushall
|
||||
}
|
||||
|
||||
start_server {tags {"external:skip"}} {
|
||||
r config set auto-aof-rewrite-percentage 0 ; # disable auto-rewrite
|
||||
r config set appendonly yes ; # enable AOF
|
||||
|
||||
set master_host [srv 0 host]
|
||||
set master_port [srv 0 port]
|
||||
|
||||
test "Low aof-max-size stops writing AOF with EFBIG" {
|
||||
setup
|
||||
wait_for_log_messages 0 {"*Error writing to the AOF file: Reached aof-max-size*"} 0 100 10
|
||||
cleanup
|
||||
}
|
||||
|
||||
test "New write attempts when limited with aof-max-size fail and doesn't insrease AOF buffer anymore" {
|
||||
setup
|
||||
set info1 [r info]
|
||||
set buf1 [getInfoProperty $info1 mem_aof_buffer]
|
||||
set len1 [getInfoProperty $info1 aof_buffer_length]
|
||||
|
||||
catch {r set somelongerkey somelongrvalue} err
|
||||
assert {$err eq "MISCONF Errors writing to the AOF file: Reached aof-max-size"}
|
||||
assert_equal [r get somelongerkey] ""
|
||||
|
||||
set info2 [r info]
|
||||
set buf2 [getInfoProperty $info2 mem_aof_buffer]
|
||||
set len2 [getInfoProperty $info2 aof_buffer_length]
|
||||
assert_equal $buf1 $buf2
|
||||
assert_equal $len1 $len2
|
||||
cleanup
|
||||
}
|
||||
|
||||
test "Increasing aof-max-size fixes AOF write error" {
|
||||
setup
|
||||
set loglines [count_log_lines 0] ; # want to check new line, not from previous test
|
||||
r config set aof-max-size 1000
|
||||
wait_for_log_messages 0 {"*AOF write error looks solved. The server can write again.*"} $loglines 100 10
|
||||
|
||||
assert_equal [r set k3 v3] "OK"
|
||||
assert_equal [r get k3] "v3"
|
||||
cleanup
|
||||
}
|
||||
|
||||
test "Meeting aof-max-size does not prevent AOF rewrite" {
|
||||
setup 200
|
||||
set loglines [count_log_lines 0] ; # want to check new line, not from previous test
|
||||
|
||||
waitForBgrewriteaof r
|
||||
r bgrewriteaof
|
||||
wait_for_log_messages 0 {"*Background AOF rewrite finished successfully*"} $loglines 100 10
|
||||
wait_for_log_messages 0 {"*AOF write error looks solved. The server can write again.*"} $loglines 100 10
|
||||
cleanup
|
||||
}
|
||||
}
|
@ -1662,6 +1662,9 @@ aof-use-rdb-preamble yes
|
||||
# the AOF format in a way that may not be compatible with existing AOF parsers.
|
||||
aof-timestamp-enabled no
|
||||
|
||||
# Maximum size for AOF files on disk in bytes. Ignored, if set to 0.
|
||||
aof-max-size 0
|
||||
|
||||
################################ SHUTDOWN #####################################
|
||||
|
||||
# Maximum time to wait for replicas when shutting down, in seconds.
|
||||
|
Loading…
x
Reference in New Issue
Block a user