Skip to content

Ban snapshot-creating commands and other admin commands from transactions#10015

Merged
oranagra merged 8 commits intoredis:unstablefrom
guybe7:no_multi_flag
Jan 4, 2022
Merged

Ban snapshot-creating commands and other admin commands from transactions#10015
oranagra merged 8 commits intoredis:unstablefrom
guybe7:no_multi_flag

Conversation

@guybe7
Copy link
Collaborator

@guybe7 guybe7 commented Dec 27, 2021

Creating fork (or even a foreground SAVE) during a transaction breaks the atomicity of the transaction.
In addition to that, it could mess up the propagated transaction to the AOF file.

This change blocks SAVE, PSYNC, SYNC and SHUTDOWN from being executed inside MULTI-EXEC.
It does that by adding a command flag, so that modules can flag their commands with that flag too.

Besides it changes BGSAVE, BGREWRITEAOF, and CONFIG SET appendonly, to turn the
scheduled flag instead of forking righ taway.

Other changes:

  • expose protected, no-async-loading, and no_multi flags in COMMAND command
  • add a test to validate propagation of FLUSHALL inside a transaction.
  • add a test to validate how CONFIG SET that errors reacts in a transaction

Others:
1. Add some missing command flags to COMMAND's output
* no need to check CMD_NO_MULTI inside EXEC
* revert accidental deletion of no_mandatory_keys
* use AOF for the "MULTI with FLUSHALL" instead of replication
@oranagra
Copy link
Member

@redis/core-team please approve, and state if you think this should be mentioned in the release notes as a breaking change.

@oranagra oranagra added the approval-needed Waiting for core team approval to be merged label Dec 27, 2021
@itamarhaber
Copy link
Member

Change makes sense to me and should be noted as breaking despite not being documented. Should it be documented now (i.e. commands that can't be run from multi) somewhere (e.g. https://redis.io/topics/transactions)?

Copy link
Collaborator

@yossigo yossigo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, and I do think this should be listed in the release notes as a breaking change.

@oranagra oranagra added breaking-change This change can potentially break existing application release-notes indication that this issue needs to be mentioned in the release notes state:major-decision Requires core team consensus labels Dec 27, 2021
@soloestoy
Copy link
Contributor

I believe we should ban config set appendonly too.

And some other commands I discussed in #5666.

@soloestoy
Copy link
Contributor

About bgsave bgrewriteaof and config set appendonly yes, how about that we just set server.rdb_bgsave_scheduled or server.aof_rewrite_scheduled to 1 in transaction, then we can trigger it in serverCron(), just like what BLPOP does.

@oranagra
Copy link
Member

I thought it's a valid way out of the problem, i was about to suggest it for config set appendonly (since i didn't want to add a new config flag), but now that i think of it, and remember some discussion with Salvatore, i think it's wrong.

The contract with the user is that when append only is enabled (during runtime in this case), as soon as the config command replies with OK, the data is guaranteed to be persisted. this is why redis will refuse to shutdown in the initial AOFRW is in progress.
So this is not a normal rewrite, in which we can use the schedule flag, it's a special state, and i don't like to complicate things for this problem and add another state where there is a scheduled initial rewrite.

instead, i think we can just modify updateAppendonly in config.c to return 0 (error) if we're in a transaction.

Regarding BGSAVE and BGAOFREWRITE, it may be slightly misleading for the user.
if someone was bold enough to do

multi
set x y
bgsave
set x z
exec

then i think their intention might have been to actually break the transaction and start the fork (snapshot) half way.
or maybe for some reason they meant that he wants to guarantee it will fork before the next client (not at serverCron or beforeSleep).
So my point is, that i think i'd rather just say it's an invalid use and block it.
but maybe i'm not aware of how many users may be relying on this.

So to sum up, i think we can change this PR and add the new flag to SYNC, PSYNC, SLAVEOF, REPLICAOF, SHUTDOWN.
and modify updateAppendonly in config.c to return 0 (an error) when we're in a transaction.

@oranagra oranagra changed the title Ban snapshot-creating commands from transactions Ban snapshot-creating commands and other admin commands from transactions Dec 28, 2021
@soloestoy
Copy link
Contributor

soloestoy commented Dec 28, 2021

I just wanna try our best to make it compatible with user's old behavior, because I'm not sure if some scripts are doing like that, but I'm OK if we all think a breaking change is better.

@oranagra
Copy link
Member

i just found this test, which is apparently working, so although i think it's an invalid case, it's currently solve, so we can keep it this way.

    test {MULTI / EXEC with REPLICAOF} {
        # This test verifies that if we demote a master to replica inside a transaction, the
        # entire transaction is not propagated to the already-connected replica
        set repl [attach_to_replication_stream]
        r set foo bar
        r multi
        r set foo2 bar
        r replicaof localhost 9999
        r set foo3 bar
        r exec
        catch {r set foo4 bar} e
        assert_match {READONLY*} $e
        assert_replication_stream $repl {
            {select *}
            {set foo bar}
        }
        r replicaof no one
    } {OK} {needs:repl cluster:skip}

@soloestoy
Copy link
Contributor

i just found this test, which is apparently working, so although i think it's an invalid case, it's currently solve, so we can keep it this way.

    test {MULTI / EXEC with REPLICAOF} {
        # This test verifies that if we demote a master to replica inside a transaction, the
        # entire transaction is not propagated to the already-connected replica
        set repl [attach_to_replication_stream]
        r set foo bar
        r multi
        r set foo2 bar
        r replicaof localhost 9999
        r set foo3 bar
        r exec
        catch {r set foo4 bar} e
        assert_match {READONLY*} $e
        assert_replication_stream $repl {
            {select *}
            {set foo bar}
        }
        r replicaof no one
    } {OK} {needs:repl cluster:skip}

It was introduced in #9890 , and I think it's an invalid case we should remove it, WDYT @guybe7 ?

@oranagra
Copy link
Member

oranagra commented Dec 28, 2021

ohh, i have a short memory.. seems like i asked for that test.
but still there was an attempt to handle it in the past (inside multi.c, so that can be an argument to keep it).
i'm leaning towards blocking it now, but i wanna check if there are not too many other cases in the test suite that rely on it.

@guybe7
Copy link
Collaborator Author

guybe7 commented Dec 28, 2021

Let's block it

@oranagra
Copy link
Member

The contract with the user is that when append only is enabled (during runtime in this case), as soon as the config command replies with OK, the data is guaranteed to be persisted. this is why redis will refuse to shutdown in the initial AOFRW is in progress.
So this is not a normal rewrite, in which we can use the schedule flag, it's a special state, and i don't like to complicate things for this problem and add another state where there is a scheduled initial rewrite.

i realize i was wrong.
setting the appendonly config already has a case where it responds with ok, and just set the server.aof_rewrite_scheduled flag

@guybe7
Copy link
Collaborator Author

guybe7 commented Dec 30, 2021

@oranagra @soloestoy so we'll just make sure to add "transaction" to the list of cases in which CONFIG SET avoids starting a fork.

sound good?

@oranagra
Copy link
Member

we discussed this in a core-team meeting.
our conclusions were:

  • save, sync, psync, shutdown - just reject in a transaction
  • bgsave, bgrewrite - delay the fork (set the scheduled flag)
  • replicaof, slaveof - keep them working in a transaction (we see some possible use cases for sending anther command just before or after demoting / promoting)
  • config set appendonly - delay the fork (set the scheduled flag)

* don't block BGSAVE and BGREWRITEAOF
* block PSYNC, SYNC, and SHUTDOWN
* BGSAVE and BGREWRITEAOF will set the schedule flag instead of forking
* CONFIG SET appendonly willl set the schedule flag instead of forking
* add tests for all of the above

unrealted
* add test for config that fails inside MULTI
* add test for FLUSHDB inside MULTI
@oranagra
Copy link
Member

@redis/core-team i made the changes we discussed, please take a quick look at the top comment and approve.

Copy link
Contributor

@madolson madolson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes reflect our discussions, so LGTM.

@oranagra oranagra merged commit ac84b1c into redis:unstable Jan 4, 2022
enjoy-binbin added a commit to enjoy-binbin/redis that referenced this pull request Jan 13, 2022
The dbs doesn't have any keys, `rdb-key-save-delay`
config has no effect that cause the rewrite to complete.

It was introduced in redis#10015.
oranagra pushed a commit that referenced this pull request Jan 13, 2022
The dbs doesn't have any keys, `rdb-key-save-delay`
config has no effect that cause the rewrite to complete.

It was introduced in #10015.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval-needed Waiting for core team approval to be merged breaking-change This change can potentially break existing application release-notes indication that this issue needs to be mentioned in the release notes state:major-decision Requires core team consensus

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants