Delete key doesn't dirty client who watched stale key#10256
Delete key doesn't dirty client who watched stale key#10256oranagra merged 8 commits intoredis:unstablefrom
Conversation
This is a revamp of redis#9234. When WATCH is called, a flag is stored if the key is already expired at the time of watch. The expired key is not deleted, only checked. When a key is "touched", if it is deleted and it was already expired when a client watched it, the client is not marked as dirty. The LRU field of the key name's robj (the argument of WATCH) is used for the flag.
|
@zuiderkwast thank you for revamping that stale PR. |
@oranagra The problem: WATCH an already expired key, then active expire or DEL marks the key as dirty, causing EXEC to fail, although there was no actual change in the logical existence of the key since WATCH. The old PR attempted to delete expired keys on WATCH (expireIfNeeded), making WATCH a command with side effects that need replication. This PR is based on your idea of storing a bit somewhere, flagging that the keys was already expired when WATCH was called. Then, if the key is deleted or active-expired after WATCH, this doesn't mark the key as dirty if the flag is set. Problem solved without a need for replicating side-effects of WATCH. We don't care if the key is actually removed from the db dict or not. |
|
Oops... I have already implemented it in my laptop but forgot to push it, it's similar with your codes. I just pushed it and you can compare with the original PR. BTW, the implements both this PR and mine are incomplete, the |
@soloestoy OK, it seems to have diverged from unstable. Do you want to continue your work or shall we focus on this PR? We can avoid doing parallel work...
Good point!
|
…/pointer Use db pointer in watchedKey again (reverts the change to db index, because when swapping db with tempDb the db is not in the server.db array so index is not enough). Add test cases for FLUSDB and SWAPDB with watched stale key.
ba5acb9 to
9956401
Compare
oranagra
left a comment
There was a problem hiding this comment.
thanks.
i think the explicit code is easier to reason with.
|
@soloestoy please have a look before i merge this. |
|
@oranagra Please don't merge yet. I have found some errors for swapdb in a not-yet-committed test case "SWAPDB does not touch non-existing key replaced with stale key". It turns out the database dicts are swapped before touchAllKeysInDb are called. In the flushdb command, the dicts are emptied after touchAllKeysInDb is called. |
|
More test cases for SWAPDB found more errors that are now fixed:
@oranagra It's unfortunate that touchAllWatchedKeysInDb is called after swapping the dicts when it's called before flushing dics in FLUSHDB. I'll try to change this so touchAllWatchedKeysInDb is called before swapping the dics and see if it breaks other things. |
…KeysInDb before swapping dicts
|
this somehow break the test (timing issue in valgrind and FreeBsd): When WATCH is called on a key that's already logically expired, avoid discarding the transaction when the keys is actually deleted. new edit: |
The test will fail on slow machines (valgrind or FreeBsd). Because in redis#10256 when WATCH is called on a key that's already logically expired, we will add an `expired` flag, and we will skip it in `isWatchedKeyExpired` check. Apparently we need to increase the expiration time so that the key can not expire logically then the WATCH is called. Also added retries to make sure it doesn't fail. I suppose 100ms is enough in valgrind, tested locally, no need to retry.
The test will fail on slow machines (valgrind or FreeBsd). Because in #10256 when WATCH is called on a key that's already logically expired, we will add an `expired` flag, and we will skip it in `isWatchedKeyExpired` check. Apparently we need to increase the expiration time so that the key can not expire logically then the WATCH is called. Also added retries to make sure it doesn't fail. I suppose 100ms is enough in valgrind, tested locally, no need to retry.
When WATCH is called on a key that's already logically expired, avoid discarding the transaction when the keys is actually deleted.
This is a revamp of #9234.
When WATCH is called, a flag is stored if the key is already expired
at the time of watch. The expired key is not deleted, only checked.
When a key is "touched", if it is deleted and it was already expired
when a client watched it, the client is not marked as dirty.