Repository: redis/redis

Problem statement:
[BUG] Empty "save" option is not storred with "config rewrite"
**Describe the bug**

If `save` option is set to empty string (`""`), ie if snapshotting is disabled, then `config rewrite` command doesn't save `save` option at all.

Therefore, after restart redis have non-empty save option that matches sample in default redis.conf .

**To reproduce**

Run redis-server with `save` option set to `""` either in config or with runtime command `config set save ""`.
Evaluate `config rewrite` command.
Rewritten config doesn't contain `save ""` line.
After restart, redis has non-empty save option.

```
# redis-cli -p 6380 config get save
1) "save"
2) "3600 1 300 100 60 10000"
```

**Expected behavior**

Config contains `save ""` option.


Hints:
Like https://github.com/redis/redis/issues/8572.
If there is no save, we should emit `save ""`.
@huangzhw It's not exactly the same issue, #8572 also involves `include` which is I believe a bigger issue that is not limited to `save` itself.
@yossigo Yes. https://github.com/redis/redis/issues/8572 has more to do. But I think the author just want this.
Will we fix this first?

Patch:
diff --git a/src/config.c b/src/config.c
index fbf3ca1ff6d..b7381c67002 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1381,14 +1381,19 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) {
         return;
     }
 
-    /* Note that if there are no save parameters at all, all the current
-     * config line with "save" will be detected as orphaned and deleted,
-     * resulting into no RDB persistence as expected. */
-    for (j = 0; j < server.saveparamslen; j++) {
-        line = sdscatprintf(sdsempty(),"save %ld %d",
-            (long) server.saveparams[j].seconds, server.saveparams[j].changes);
-        rewriteConfigRewriteLine(state,"save",line,1);
+    /* Rewrite save parameters, or an empty 'save ""' line to avoid the
+     * defaults from being used.
+     */
+    if (!server.saveparamslen) {
+        rewriteConfigRewriteLine(state,"save",sdsnew("save \"\""),0);
+    } else {
+        for (j = 0; j < server.saveparamslen; j++) {
+            line = sdscatprintf(sdsempty(),"save %ld %d",
+                (long) server.saveparams[j].seconds, server.saveparams[j].changes);
+            rewriteConfigRewriteLine(state,"save",line,1);
+        }
     }
+
     /* Mark "save" as processed in case server.saveparamslen is zero. */
     rewriteConfigMarkAsProcessed(state,"save");
 }


Test patch:
diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl
index 698ed778962..bf4addbe5ff 100644
--- a/tests/unit/introspection.tcl
+++ b/tests/unit/introspection.tcl
@@ -180,6 +180,18 @@ start_server {tags {"introspection"}} {
         }
     }
 
+    test {CONFIG REWRITE handles save properly} {
+        r config set save "3600 1 300 100 60 10000"
+        r config rewrite
+        restart_server 0 true false
+        assert_equal [r config get save] {save {3600 1 300 100 60 10000}}
+
+        r config set save ""
+        r config rewrite
+        restart_server 0 true false
+        assert_equal [r config get save] {save {}}
+    }
+
     # Config file at this point is at a wierd state, and includes all
     # known keywords. Might be a good idea to avoid adding tests here.
 }