summaryrefslogtreecommitdiff
path: root/src/server.c
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2023-05-15 13:08:15 +0300
committerGitHub <noreply@github.com>2023-05-15 13:08:15 +0300
commita51eb05b1895babb17c37c36b963e2bcbd5496d5 (patch)
tree7be24b09e0a5621a03e9f9ffe9ef27fcb44d8345 /src/server.c
parente26a769d9627ebecb8607375580970a740348956 (diff)
parent986dbf716e0cb904c80bb444635cea3242859cc1 (diff)
downloadredis-7.2.tar.gz
Release Redis 7.2 RC27.2-rc27.2
Diffstat (limited to 'src/server.c')
-rw-r--r--src/server.c310
1 files changed, 175 insertions, 135 deletions
diff --git a/src/server.c b/src/server.c
index 4e77d2f6d..9389e707f 100644
--- a/src/server.c
+++ b/src/server.c
@@ -36,6 +36,7 @@
#include "atomicvar.h"
#include "mt19937-64.h"
#include "functions.h"
+#include "hdr_histogram.h"
#include "syscheck.h"
#include <time.h>
@@ -655,11 +656,11 @@ const char *strChildType(int type) {
/* Return true if there are active children processes doing RDB saving,
* AOF rewriting, or some side process spawned by a loaded module. */
-int hasActiveChildProcess() {
+int hasActiveChildProcess(void) {
return server.child_pid != -1;
}
-void resetChildState() {
+void resetChildState(void) {
server.child_type = CHILD_TYPE_NONE;
server.child_pid = -1;
server.stat_current_cow_peak = 0;
@@ -681,7 +682,7 @@ int isMutuallyExclusiveChildType(int type) {
}
/* Returns true when we're inside a long command that yielded to the event loop. */
-int isInsideYieldingLongCommand() {
+int isInsideYieldingLongCommand(void) {
return scriptIsTimedout() || server.busy_module_yield_flags;
}
@@ -693,22 +694,24 @@ int allPersistenceDisabled(void) {
/* ======================= Cron: called every 100 ms ======================== */
-/* Add a sample to the operations per second array of samples. */
-void trackInstantaneousMetric(int metric, long long current_reading) {
- long long now = mstime();
- long long t = now - server.inst_metric[metric].last_sample_time;
- long long ops = current_reading -
- server.inst_metric[metric].last_sample_count;
- long long ops_sec;
-
- ops_sec = t > 0 ? (ops*1000/t) : 0;
-
- server.inst_metric[metric].samples[server.inst_metric[metric].idx] =
- ops_sec;
- server.inst_metric[metric].idx++;
- server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;
- server.inst_metric[metric].last_sample_time = now;
- server.inst_metric[metric].last_sample_count = current_reading;
+/* Add a sample to the instantaneous metric. This function computes the quotient
+ * of the increment of value and base, which is useful to record operation count
+ * per second, or the average time consumption of an operation.
+ *
+ * current_value - The dividend
+ * current_base - The divisor
+ * */
+void trackInstantaneousMetric(int metric, long long current_value, long long current_base, long long factor) {
+ if (server.inst_metric[metric].last_sample_base > 0) {
+ long long base = current_base - server.inst_metric[metric].last_sample_base;
+ long long value = current_value - server.inst_metric[metric].last_sample_value;
+ long long avg = base > 0 ? (value * factor / base) : 0;
+ server.inst_metric[metric].samples[server.inst_metric[metric].idx] = avg;
+ server.inst_metric[metric].idx++;
+ server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;
+ }
+ server.inst_metric[metric].last_sample_base = current_base;
+ server.inst_metric[metric].last_sample_value = current_value;
}
/* Return the mean of all the samples. */
@@ -744,7 +747,7 @@ int clientsCronResizeQueryBuffer(client *c) {
* sure not to resize to less than the bulk length. */
size_t resize = sdslen(c->querybuf);
if (resize < c->querybuf_peak) resize = c->querybuf_peak;
- if (c->bulklen != -1 && resize < (size_t)c->bulklen) resize = c->bulklen;
+ if (c->bulklen != -1 && resize < (size_t)c->bulklen + 2) resize = c->bulklen + 2;
c->querybuf = sdsResize(c->querybuf, resize, 1);
}
}
@@ -754,8 +757,7 @@ int clientsCronResizeQueryBuffer(client *c) {
c->querybuf_peak = sdslen(c->querybuf);
/* We reset to either the current used, or currently processed bulk size,
* which ever is bigger. */
- if (c->bulklen != -1 && (size_t)c->bulklen > c->querybuf_peak)
- c->querybuf_peak = c->bulklen;
+ if (c->bulklen != -1 && (size_t)c->bulklen + 2 > c->querybuf_peak) c->querybuf_peak = c->bulklen + 2;
return 0;
}
@@ -1015,12 +1017,11 @@ void clientsCron(void) {
client *c;
listNode *head;
- /* Rotate the list, take the current head, process.
- * This way if the client must be removed from the list it's the
- * first element and we don't incur into O(N) computation. */
- listRotateTailToHead(server.clients);
+ /* Take the current head, process, and then rotate the head to tail.
+ * This way we can fairly iterate all clients step by step. */
head = listFirst(server.clients);
c = listNodeValue(head);
+ listRotateHeadToTail(server.clients);
/* The following functions do different service checks on the client.
* The protocol is that they return non-zero if the client was
* terminated. */
@@ -1149,7 +1150,7 @@ void enterExecutionUnit(int update_cached_time, long long us) {
}
}
-void exitExecutionUnit() {
+void exitExecutionUnit(void) {
--server.execution_nesting;
}
@@ -1205,7 +1206,7 @@ void checkChildrenDone(void) {
}
/* Called from serverCron and cronUpdateMemoryStats to update cached memory metrics. */
-void cronUpdateMemoryStats() {
+void cronUpdateMemoryStats(void) {
/* Record the max memory used since the server was started. */
if (zmalloc_used_memory() > server.stat_peak_memory)
server.stat_peak_memory = zmalloc_used_memory();
@@ -1286,6 +1287,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* for debug purposes: skip actual cron work if pause_cron is on */
if (server.pause_cron) return 1000/server.hz;
+ monotime cron_start = getMonotonicUs();
+
run_with_period(100) {
long long stat_net_input_bytes, stat_net_output_bytes;
long long stat_net_repl_input_bytes, stat_net_repl_output_bytes;
@@ -1293,16 +1296,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
atomicGet(server.stat_net_output_bytes, stat_net_output_bytes);
atomicGet(server.stat_net_repl_input_bytes, stat_net_repl_input_bytes);
atomicGet(server.stat_net_repl_output_bytes, stat_net_repl_output_bytes);
-
- trackInstantaneousMetric(STATS_METRIC_COMMAND,server.stat_numcommands);
- trackInstantaneousMetric(STATS_METRIC_NET_INPUT,
- stat_net_input_bytes + stat_net_repl_input_bytes);
- trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT,
- stat_net_output_bytes + stat_net_repl_output_bytes);
- trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION,
- stat_net_repl_input_bytes);
- trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION,
- stat_net_repl_output_bytes);
+ monotime current_time = getMonotonicUs();
+ long long factor = 1000000; // us
+ trackInstantaneousMetric(STATS_METRIC_COMMAND, server.stat_numcommands, current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_INPUT, stat_net_input_bytes + stat_net_repl_input_bytes,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT, stat_net_output_bytes + stat_net_repl_output_bytes,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION, stat_net_repl_input_bytes, current_time,
+ factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION, stat_net_repl_output_bytes,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_EL_CYCLE, server.duration_stats[EL_DURATION_TYPE_EL].cnt,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_EL_DURATION, server.duration_stats[EL_DURATION_TYPE_EL].sum,
+ server.duration_stats[EL_DURATION_TYPE_EL].cnt, 1);
}
/* We have just LRU_BITS bits per object for LRU information.
@@ -1515,18 +1523,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
&ei);
server.cronloops++;
+
+ server.el_cron_duration = getMonotonicUs() - cron_start;
+
return 1000/server.hz;
}
-void blockingOperationStarts() {
+void blockingOperationStarts(void) {
if(!server.blocking_op_nesting++){
updateCachedTime(0);
server.blocked_last_cron = server.mstime;
}
}
-void blockingOperationEnds() {
+void blockingOperationEnds(void) {
if(!(--server.blocking_op_nesting)){
server.blocked_last_cron = 0;
}
@@ -1537,7 +1548,7 @@ void blockingOperationEnds() {
* It attempts to do its duties at a similar rate as the configured server.hz,
* and updates cronloops variable so that similarly to serverCron, the
* run_with_period can be used. */
-void whileBlockedCron() {
+void whileBlockedCron(void) {
/* Here we may want to perform some cron jobs (normally done server.hz times
* per second). */
@@ -1650,6 +1661,11 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* If any connection type(typical TLS) still has pending unread data don't sleep at all. */
aeSetDontWait(server.el, connTypeHasPendingData());
+ /* Record cron time in beforeSleep, which is the sum of active-expire, active-defrag and all other
+ * tasks done by cron and beforeSleep, but excluding read, write and AOF, that are counted by other
+ * sets of metrics. */
+ monotime cron_start_time_before_aof = getMonotonicUs();
+
/* Call the Redis Cluster before sleep function. Note that this function
* may change the state of Redis Cluster (from ok to fail or vice versa),
* so it's a good idea to call it before serving the unblocked clients
@@ -1658,7 +1674,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Run a fast expire cycle (the called function will return
* ASAP if a fast cycle is not needed). */
- if (server.active_expire_enabled && server.masterhost == NULL)
+ if (server.active_expire_enabled && iAmMaster())
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);
/* Unblock all the clients blocked for synchronous replication
@@ -1716,12 +1732,20 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
* since the unblocked clients may write data. */
handleClientsBlockedOnKeys();
+ /* Record time consumption of AOF writing. */
+ monotime aof_start_time = getMonotonicUs();
+ /* Record cron time in beforeSleep. This does not include the time consumed by AOF writing and IO writing below. */
+ monotime duration_before_aof = aof_start_time - cron_start_time_before_aof;
+
/* Write the AOF buffer on disk,
* must be done before handleClientsWithPendingWritesUsingThreads,
* in case of appendfsync=always. */
if (server.aof_state == AOF_ON || server.aof_state == AOF_WAIT_REWRITE)
flushAppendOnlyFile(0);
+ /* Record time consumption of AOF writing. */
+ durationAddSample(EL_DURATION_TYPE_AOF, getMonotonicUs() - aof_start_time);
+
/* Update the fsynced replica offset.
* If an initial rewrite is in progress then not all data is guaranteed to have actually been
* persisted to disk yet, so we cannot update the field. We will wait for the rewrite to complete. */
@@ -1734,6 +1758,9 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Handle writes with pending output buffers. */
handleClientsWithPendingWritesUsingThreads();
+ /* Record cron time in beforeSleep. This does not include the time consumed by AOF writing and IO writing above. */
+ monotime cron_start_time_after_write = getMonotonicUs();
+
/* Close clients that need to be closed asynchronous */
freeClientsInAsyncFreeQueue();
@@ -1745,6 +1772,25 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Disconnect some clients if they are consuming too much memory. */
evictClients();
+ /* Record cron time in beforeSleep. */
+ monotime duration_after_write = getMonotonicUs() - cron_start_time_after_write;
+
+ /* Record eventloop latency. */
+ if (server.el_start > 0) {
+ monotime el_duration = getMonotonicUs() - server.el_start;
+ durationAddSample(EL_DURATION_TYPE_EL, el_duration);
+ }
+ server.el_cron_duration += duration_before_aof + duration_after_write;
+ durationAddSample(EL_DURATION_TYPE_CRON, server.el_cron_duration);
+ server.el_cron_duration = 0;
+ /* Record max command count per cycle. */
+ if (server.stat_numcommands > server.el_cmd_cnt_start) {
+ long long el_command_cnt = server.stat_numcommands - server.el_cmd_cnt_start;
+ if (el_command_cnt > server.el_cmd_cnt_max) {
+ server.el_cmd_cnt_max = el_command_cnt;
+ }
+ }
+
/* 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. */
@@ -1775,6 +1821,10 @@ void afterSleep(struct aeEventLoop *eventLoop) {
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("module-acquire-GIL",latency);
}
+ /* Set the eventloop start time. */
+ server.el_start = getMonotonicUs();
+ /* Set the eventloop command count at start. */
+ server.el_cmd_cnt_start = server.stat_numcommands;
}
/* Update the time cache. */
@@ -1955,7 +2005,7 @@ void createSharedObjects(void) {
shared.maxstring = sdsnew("maxstring");
}
-void initServerClientMemUsageBuckets() {
+void initServerClientMemUsageBuckets(void) {
if (server.client_mem_usage_buckets)
return;
server.client_mem_usage_buckets = zmalloc(sizeof(clientMemUsageBucket)*CLIENT_MEM_USAGE_BUCKETS);
@@ -1965,7 +2015,7 @@ void initServerClientMemUsageBuckets() {
}
}
-void freeServerClientMemUsageBuckets() {
+void freeServerClientMemUsageBuckets(void) {
if (!server.client_mem_usage_buckets)
return;
for (int j = 0; j < CLIENT_MEM_USAGE_BUCKETS; j++)
@@ -2018,6 +2068,7 @@ void initServerConfig(void) {
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
server.aof_last_incr_size = 0;
+ server.aof_last_incr_fsync_offset = 0;
server.active_defrag_running = 0;
server.notify_keyspace_events = 0;
server.blocked_clients = 0;
@@ -2494,8 +2545,8 @@ void resetServerStats(void) {
atomicSet(server.stat_total_writes_processed, 0);
for (j = 0; j < STATS_METRIC_COUNT; j++) {
server.inst_metric[j].idx = 0;
- server.inst_metric[j].last_sample_time = mstime();
- server.inst_metric[j].last_sample_count = 0;
+ server.inst_metric[j].last_sample_base = 0;
+ server.inst_metric[j].last_sample_value = 0;
memset(server.inst_metric[j].samples,0,
sizeof(server.inst_metric[j].samples));
}
@@ -2512,6 +2563,8 @@ void resetServerStats(void) {
server.aof_delayed_fsync = 0;
server.stat_reply_buffer_shrinks = 0;
server.stat_reply_buffer_expands = 0;
+ memset(server.duration_stats, 0, sizeof(durationStats) * EL_DURATION_TYPE_NUM);
+ server.el_cmd_cnt_max = 0;
lazyfreeResetStats();
}
@@ -2717,7 +2770,7 @@ void initServer(void) {
initServerClientMemUsageBuckets();
}
-void initListeners() {
+void initListeners(void) {
/* Setup listeners from server config for TCP/TLS/Unix */
int conn_index;
connListener *listener;
@@ -2794,7 +2847,7 @@ void initListeners() {
* Specifically, creation of threads due to a race bug in ld.so, in which
* Thread Local Storage initialization collides with dlopen call.
* see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */
-void InitServerLast() {
+void InitServerLast(void) {
bioInit();
initThreadedIO();
set_jemalloc_bg_thread(server.jemalloc_bg_thread);
@@ -2942,21 +2995,6 @@ void setImplicitACLCategories(struct redisCommand *c) {
c->acl_categories |= ACL_CATEGORY_SLOW;
}
-/* Recursively populate the args structure (setting num_args to the number of
- * subargs) and return the number of args. */
-int populateArgsStructure(struct redisCommandArg *args) {
- if (!args)
- return 0;
- int count = 0;
- while (args->name) {
- serverAssert(count < INT_MAX);
- args->num_args = populateArgsStructure(args->subargs);
- count++;
- args++;
- }
- return count;
-}
-
/* Recursively populate the command structure.
*
* On success, the function return C_OK. Otherwise C_ERR is returned and we won't
@@ -2974,28 +3012,10 @@ int populateCommandStructure(struct redisCommand *c) {
* set of flags. */
setImplicitACLCategories(c);
- /* Redis commands don't need more args than STATIC_KEY_SPECS_NUM (Number of keys
- * specs can be greater than STATIC_KEY_SPECS_NUM only for module commands) */
- c->key_specs = c->key_specs_static;
- c->key_specs_max = STATIC_KEY_SPECS_NUM;
-
/* We start with an unallocated histogram and only allocate memory when a command
* has been issued for the first time */
c->latency_histogram = NULL;
- for (int i = 0; i < STATIC_KEY_SPECS_NUM; i++) {
- if (c->key_specs[i].begin_search_type == KSPEC_BS_INVALID)
- break;
- c->key_specs_num++;
- }
-
- /* Count things so we don't have to use deferred reply in COMMAND reply. */
- while (c->history && c->history[c->num_history].since)
- c->num_history++;
- while (c->tips && c->tips[c->num_tips])
- c->num_tips++;
- c->num_args = populateArgsStructure(c->args);
-
/* Handle the legacy range spec and the "movablekeys" flag (must be done after populating all key specs). */
populateCommandLegacyRangeSpec(c);
@@ -3155,12 +3175,13 @@ struct redisCommand *lookupCommandBySdsLogic(dict *commands, sds s) {
sds *strings = sdssplitlen(s,sdslen(s),"|",1,&argc);
if (strings == NULL)
return NULL;
- if (argc > 2) {
+ if (argc < 1 || argc > 2) {
/* Currently we support just one level of subcommands */
sdsfreesplitres(strings,argc);
return NULL;
}
+ serverAssert(argc > 0); /* Avoid warning `-Wmaybe-uninitialized` in lookupCommandLogic() */
robj objects[argc];
robj *argv[argc];
for (j = 0; j < argc; j++) {
@@ -3336,7 +3357,7 @@ void updateCommandLatencyHistogram(struct hdr_histogram **latency_histogram, int
/* Handle the alsoPropagate() API to handle commands that want to propagate
* multiple separated commands. Note that alsoPropagate() is not affected
* by CLIENT_PREVENT_PROP flag. */
-static void propagatePendingCommands() {
+static void propagatePendingCommands(void) {
if (server.also_propagate.numops == 0)
return;
@@ -3392,7 +3413,7 @@ static void propagatePendingCommands() {
* currently with respect to replication and post jobs, but in the future there might
* be other considerations. So we basically want the `postUnitOperations` to trigger
* after the entire chain finished. */
-void postExecutionUnitOperations() {
+void postExecutionUnitOperations(void) {
if (server.execution_nesting)
return;
@@ -3563,6 +3584,8 @@ void call(client *c, int flags) {
char *latency_event = (real_cmd->flags & CMD_FAST) ?
"fast-command" : "command";
latencyAddSampleIfNeeded(latency_event,duration/1000);
+ if (server.execution_nesting == 0)
+ durationAddSample(EL_DURATION_TYPE_CMD, duration);
}
/* Log the command into the Slow log if needed.
@@ -3595,6 +3618,12 @@ void call(client *c, int flags) {
updateCommandLatencyHistogram(&(real_cmd->latency_histogram), c->duration*1000);
}
+ /* The duration needs to be reset after each call except for a blocked command,
+ * which is expected to record and reset the duration after unblocking. */
+ if (!(c->flags & CLIENT_BLOCKED)) {
+ c->duration = 0;
+ }
+
/* Propagate the command into the AOF and replication link.
* We never propagate EXEC explicitly, it will be implicitly
* propagated if needed (see propagatePendingCommands).
@@ -4356,6 +4385,8 @@ int finishShutdown(void) {
serverLog(LL_WARNING, "Writing initial AOF. Exit anyway.");
} else {
serverLog(LL_WARNING, "Writing initial AOF, can't exit.");
+ if (server.supervised_mode == SUPERVISED_SYSTEMD)
+ redisCommunicateSystemd("STATUS=Writing initial AOF, can't exit.\n");
goto error;
}
}
@@ -4853,28 +4884,6 @@ void addReplyCommandSubCommands(client *c, struct redisCommand *cmd, void (*repl
dictReleaseIterator(di);
}
-/* Must match redisCommandGroup */
-const char *COMMAND_GROUP_STR[] = {
- "generic",
- "string",
- "list",
- "set",
- "sorted-set",
- "hash",
- "pubsub",
- "transactions",
- "connection",
- "server",
- "scripting",
- "hyperloglog",
- "cluster",
- "sentinel",
- "geo",
- "stream",
- "bitmap",
- "module"
-};
-
/* Output the representation of a Redis command. Used by the COMMAND command and COMMAND INFO. */
void addReplyCommandInfo(client *c, struct redisCommand *cmd) {
if (!cmd) {
@@ -4933,7 +4942,7 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
/* Always have the group, for module commands the group is always "module". */
addReplyBulkCString(c, "group");
- addReplyBulkCString(c, COMMAND_GROUP_STR[cmd->group]);
+ addReplyBulkCString(c, commandGroupStr(cmd->group));
if (cmd->complexity) {
addReplyBulkCString(c, "complexity");
@@ -5933,7 +5942,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
"io_threaded_reads_processed:%lld\r\n"
"io_threaded_writes_processed:%lld\r\n"
"reply_buffer_shrinks:%lld\r\n"
- "reply_buffer_expands:%lld\r\n",
+ "reply_buffer_expands:%lld\r\n"
+ "eventloop_cycles:%llu\r\n"
+ "eventloop_duration_sum:%llu\r\n"
+ "eventloop_duration_cmd_sum:%llu\r\n"
+ "instantaneous_eventloop_cycles_per_sec:%llu\r\n"
+ "instantaneous_eventloop_duration_usec:%llu\r\n",
server.stat_numconnections,
server.stat_numcommands,
getInstantaneousMetric(STATS_METRIC_COMMAND),
@@ -5983,7 +5997,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
server.stat_io_reads_processed,
server.stat_io_writes_processed,
server.stat_reply_buffer_shrinks,
- server.stat_reply_buffer_expands);
+ server.stat_reply_buffer_expands,
+ server.duration_stats[EL_DURATION_TYPE_EL].cnt,
+ server.duration_stats[EL_DURATION_TYPE_EL].sum,
+ server.duration_stats[EL_DURATION_TYPE_CMD].sum,
+ getInstantaneousMetric(STATS_METRIC_EL_CYCLE),
+ getInstantaneousMetric(STATS_METRIC_EL_DURATION));
info = genRedisInfoStringACLStats(info);
}
@@ -6233,6 +6252,21 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
0, /* not a crash report */
sections);
}
+
+ if (dictFind(section_dict, "debug") != NULL) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Debug\r\n"
+ "eventloop_duration_aof_sum:%llu\r\n"
+ "eventloop_duration_cron_sum:%llu\r\n"
+ "eventloop_duration_max:%llu\r\n"
+ "eventloop_cmd_per_cycle_max:%lld\r\n",
+ server.duration_stats[EL_DURATION_TYPE_AOF].sum,
+ server.duration_stats[EL_DURATION_TYPE_CRON].sum,
+ server.duration_stats[EL_DURATION_TYPE_EL].max,
+ server.el_cmd_cnt_max);
+ }
+
return info;
}
@@ -6551,7 +6585,7 @@ void setupChildSignalHandlers(void) {
* of the parent process, e.g. fd(socket or flock) etc.
* should close the resources not used by the child process, so that if the
* parent restarts it can bind/lock despite the child possibly still running. */
-void closeChildUnusedResourceAfterFork() {
+void closeChildUnusedResourceAfterFork(void) {
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 */
@@ -6746,6 +6780,7 @@ void loadDataFromDisk(void) {
serverLog(LL_NOTICE, "DB loaded from append only file: %.3f seconds", (float)(ustime()-start)/1000000);
} else {
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
+ int rsi_is_valid = 0;
errno = 0; /* Prevent a stale value from affecting error checking */
int rdb_flags = RDBFLAGS_NONE;
if (iAmMaster()) {
@@ -6767,6 +6802,7 @@ void loadDataFromDisk(void) {
* information in function rdbPopulateSaveInfo. */
rsi.repl_stream_db != -1)
{
+ rsi_is_valid = 1;
if (!iAmMaster()) {
memcpy(server.replid,rsi.repl_id,sizeof(server.replid));
server.master_repl_offset = rsi.repl_offset;
@@ -6800,7 +6836,7 @@ void loadDataFromDisk(void) {
* if RDB doesn't have replication info or there is no rdb, it is not
* possible to support partial resynchronization, to avoid extra memory
* of replication backlog, we drop it. */
- if (server.master_repl_offset == 0 && server.repl_backlog)
+ if (!rsi_is_valid && server.repl_backlog)
freeReplicationBacklog();
}
}
@@ -7224,6 +7260,34 @@ int main(int argc, char **argv) {
sdsfree(options);
}
if (server.sentinel_mode) sentinelCheckConfigFile();
+
+ /* Do system checks */
+#ifdef __linux__
+ linuxMemoryWarnings();
+ sds err_msg = NULL;
+ if (checkXenClocksource(&err_msg) < 0) {
+ serverLog(LL_WARNING, "WARNING %s", err_msg);
+ sdsfree(err_msg);
+ }
+#if defined (__arm64__)
+ int ret;
+ if ((ret = checkLinuxMadvFreeForkBug(&err_msg)) <= 0) {
+ if (ret < 0) {
+ serverLog(LL_WARNING, "WARNING %s", err_msg);
+ sdsfree(err_msg);
+ } else
+ serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
+ "Your system could be affected, please report this error.");
+ 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__ */
+
+ /* Daemonize if needed */
server.supervised = redisIsSupervised(server.supervised_mode);
int background = server.daemonize && !server.supervised;
if (background) daemonize();
@@ -7265,30 +7329,6 @@ int main(int argc, char **argv) {
if (!server.sentinel_mode) {
/* Things not needed when running in Sentinel mode. */
serverLog(LL_NOTICE,"Server initialized");
- #ifdef __linux__
- linuxMemoryWarnings();
- sds err_msg = NULL;
- if (checkXenClocksource(&err_msg) < 0) {
- serverLog(LL_WARNING, "WARNING %s", err_msg);
- sdsfree(err_msg);
- }
- #if defined (__arm64__)
- int ret;
- if ((ret = checkLinuxMadvFreeForkBug(&err_msg)) <= 0) {
- if (ret < 0) {
- serverLog(LL_WARNING, "WARNING %s", err_msg);
- sdsfree(err_msg);
- } else
- serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
- "Your system could be affected, please report this error.");
- 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__ */
aofLoadManifestFromDisk();
loadDataFromDisk();
aofOpenIfNeededOnServerStart();