-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kv/concurrency: never regress timestamp in lockTable on acquisition
Fixes #46290. Fixes #43735. Fixes #41425. This change adjusts the lockTable to be more lenient to the timestamp of new lock acquisitions. Specifically, it lifts the restriction that all calls to AcquireLock use monotonically increasing timestamps. Instead, it properly handles apparent timestamp regressions by ratcheting lock timestamps instead of replacing them directly. This matches the corresponding behavior in MVCC: https://github.com/cockroachdb/cockroach/blob/92107b551bbafe54fddb496442c590cb6feb5d65/pkg/storage/mvcc.go#L1631 This leniency is needed for sequences of events like the following: - txn A acquires lock at epoch 1, ts 10 - txn B pushes txn A to ts 20 - txn B updates lock to ts 20 - txn A restarts at ts 15 without noticing that it has been pushes - txn A re-acquires lock at epoch 2, ts 15 - we hit the lock timestamp regression assertion We see this frequently in CDC roachtests because the rangefeed processor performs high-priority timestamp pushes on long-running transactions. Outside of CDC, this is rare in our system. Release note (bug fix): CDC no longer combines with long running transactions to trigger an assertion. Release justification: fixes a high-priority bug in existing functionality. The bug could crash a server if the right sequence of events occurred. This was typically rare, but was much more common when CDC was in use.
- Loading branch information
1 parent
69b878a
commit fcd74cd
Showing
5 changed files
with
343 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
240 changes: 240 additions & 0 deletions
240
pkg/kv/kvserver/concurrency/testdata/concurrency_manager/update
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
# ------------------------------------------------------------- | ||
# A transaction writes an intent. The intent is pushed to a | ||
# higher timestamp by a second transaction. The transaction then | ||
# returns to re-acquire the intent at a new sequence number but | ||
# still at the original timestamp. This is permitted but the | ||
# lock's timestamp should not regress. | ||
# | ||
# Setup: txn1 acquire lock k | ||
# txn2 reads k and waits | ||
# txn2 pushes txn1 | ||
# | ||
# Test: txn2 succeeds in pushing txn1's ts forward | ||
# txn2 proceeds | ||
# txn1 re-acquires lock k at new seq num, lower ts | ||
# ------------------------------------------------------------- | ||
|
||
new-txn name=txn1 ts=10,1 epoch=0 | ||
---- | ||
|
||
new-txn name=txn2 ts=12,1 epoch=0 | ||
---- | ||
|
||
new-request name=req1 txn=txn1 ts=10,1 | ||
put key=k value=v | ||
---- | ||
|
||
new-request name=req2 txn=txn2 ts=12,1 | ||
get key=k | ||
---- | ||
|
||
sequence req=req1 | ||
---- | ||
[1] sequence req1: sequencing request | ||
[1] sequence req1: acquiring latches | ||
[1] sequence req1: scanning lock table for conflicting locks | ||
[1] sequence req1: sequencing complete, returned guard | ||
|
||
on-lock-acquired req=req1 key=k | ||
---- | ||
[-] acquire lock: txn 00000001 @ k | ||
|
||
finish req=req1 | ||
---- | ||
[-] finish req1: finishing request | ||
|
||
sequence req=req2 | ||
---- | ||
[2] sequence req2: sequencing request | ||
[2] sequence req2: acquiring latches | ||
[2] sequence req2: scanning lock table for conflicting locks | ||
[2] sequence req2: waiting in lock wait-queues | ||
[2] sequence req2: pushing timestamp of txn 00000001 above 0.000000012,1 | ||
[2] sequence req2: blocked on select in concurrency_test.(*cluster).PushTransaction | ||
|
||
debug-lock-table | ||
---- | ||
global: num=1 | ||
lock: "k" | ||
holder: txn: 00000001-0000-0000-0000-000000000000, ts: 0.000000010,1, info: unrepl epoch: 0, seqs: [0] | ||
waiting readers: | ||
req: 2, txn: 00000002-0000-0000-0000-000000000000 | ||
distinguished req: 2 | ||
local: num=0 | ||
|
||
# -------------------------------- | ||
# Setup complete, test starts here | ||
# -------------------------------- | ||
|
||
on-txn-updated txn=txn1 status=pending ts=12,2 | ||
---- | ||
[-] update txn: increasing timestamp of txn1 | ||
[2] sequence req2: resolving intent "k" for txn 00000001 with PENDING status | ||
[2] sequence req2: acquiring latches | ||
[2] sequence req2: scanning lock table for conflicting locks | ||
[2] sequence req2: sequencing complete, returned guard | ||
|
||
finish req=req2 | ||
---- | ||
[-] finish req2: finishing request | ||
|
||
debug-lock-table | ||
---- | ||
global: num=1 | ||
lock: "k" | ||
holder: txn: 00000001-0000-0000-0000-000000000000, ts: 0.000000012,2, info: unrepl epoch: 0, seqs: [0] | ||
local: num=0 | ||
|
||
# Issue another write to the same key for txn1 at its initial | ||
# timestamp. The timestamp in the lock table does not regress. | ||
|
||
new-request name=req3 txn=txn1 ts=10,1 | ||
put key=k value=v2 seq=1 | ||
---- | ||
|
||
sequence req=req3 | ||
---- | ||
[3] sequence req3: sequencing request | ||
[3] sequence req3: acquiring latches | ||
[3] sequence req3: scanning lock table for conflicting locks | ||
[3] sequence req3: sequencing complete, returned guard | ||
|
||
on-lock-acquired req=req3 key=k seq=1 | ||
---- | ||
[-] acquire lock: txn 00000001 @ k | ||
|
||
finish req=req3 | ||
---- | ||
[-] finish req3: finishing request | ||
|
||
debug-lock-table | ||
---- | ||
global: num=1 | ||
lock: "k" | ||
holder: txn: 00000001-0000-0000-0000-000000000000, ts: 0.000000012,2, info: unrepl epoch: 0, seqs: [0, 1] | ||
local: num=0 | ||
|
||
reset namespace | ||
---- | ||
|
||
# ------------------------------------------------------------- | ||
# A transaction writes an intent. The intent is pushed to a | ||
# higher timestamp by a second transaction. The transaction then | ||
# returns to re-acquire the intent at a new epoch but still at | ||
# the original timestamp. This is permitted but the lock's | ||
# timestamp should not regress. | ||
# | ||
# Setup: txn1 acquire lock k | ||
# txn2 reads k and waits | ||
# | ||
# Test: txn2 pushes txn1's timestamp forward | ||
# txn2 proceeds | ||
# txn1 re-acquires lock k at new epoch, lower ts | ||
# ------------------------------------------------------------- | ||
|
||
new-txn name=txn1 ts=10,1 epoch=0 | ||
---- | ||
|
||
new-txn name=txn2 ts=12,1 epoch=0 | ||
---- | ||
|
||
new-request name=req1 txn=txn1 ts=10,1 | ||
put key=k value=v | ||
---- | ||
|
||
new-request name=req2 txn=txn2 ts=12,1 | ||
get key=k | ||
---- | ||
|
||
sequence req=req1 | ||
---- | ||
[1] sequence req1: sequencing request | ||
[1] sequence req1: acquiring latches | ||
[1] sequence req1: scanning lock table for conflicting locks | ||
[1] sequence req1: sequencing complete, returned guard | ||
|
||
on-lock-acquired req=req1 key=k | ||
---- | ||
[-] acquire lock: txn 00000001 @ k | ||
|
||
finish req=req1 | ||
---- | ||
[-] finish req1: finishing request | ||
|
||
sequence req=req2 | ||
---- | ||
[2] sequence req2: sequencing request | ||
[2] sequence req2: acquiring latches | ||
[2] sequence req2: scanning lock table for conflicting locks | ||
[2] sequence req2: waiting in lock wait-queues | ||
[2] sequence req2: pushing timestamp of txn 00000001 above 0.000000012,1 | ||
[2] sequence req2: blocked on select in concurrency_test.(*cluster).PushTransaction | ||
|
||
debug-lock-table | ||
---- | ||
global: num=1 | ||
lock: "k" | ||
holder: txn: 00000001-0000-0000-0000-000000000000, ts: 0.000000010,1, info: unrepl epoch: 0, seqs: [0] | ||
waiting readers: | ||
req: 5, txn: 00000002-0000-0000-0000-000000000000 | ||
distinguished req: 5 | ||
local: num=0 | ||
|
||
# -------------------------------- | ||
# Setup complete, test starts here | ||
# -------------------------------- | ||
|
||
on-txn-updated txn=txn1 status=pending ts=12,2 | ||
---- | ||
[-] update txn: increasing timestamp of txn1 | ||
[2] sequence req2: resolving intent "k" for txn 00000001 with PENDING status | ||
[2] sequence req2: acquiring latches | ||
[2] sequence req2: scanning lock table for conflicting locks | ||
[2] sequence req2: sequencing complete, returned guard | ||
|
||
finish req=req2 | ||
---- | ||
[-] finish req2: finishing request | ||
|
||
debug-lock-table | ||
---- | ||
global: num=1 | ||
lock: "k" | ||
holder: txn: 00000001-0000-0000-0000-000000000000, ts: 0.000000012,2, info: unrepl epoch: 0, seqs: [0] | ||
local: num=0 | ||
|
||
# The txn restarts at a new timestamp, but below the pushed | ||
# timestamp. It re-issues the same write at the new epoch. The | ||
# timestamp in the lock table does not regress. | ||
|
||
new-txn name=txn1 ts=11,1 epoch=1 | ||
---- | ||
|
||
new-request name=req3 txn=txn1 ts=11,1 | ||
put key=k value=v2 | ||
---- | ||
|
||
sequence req=req3 | ||
---- | ||
[3] sequence req3: sequencing request | ||
[3] sequence req3: acquiring latches | ||
[3] sequence req3: scanning lock table for conflicting locks | ||
[3] sequence req3: sequencing complete, returned guard | ||
|
||
on-lock-acquired req=req3 key=k | ||
---- | ||
[-] acquire lock: txn 00000001 @ k | ||
|
||
finish req=req3 | ||
---- | ||
[-] finish req3: finishing request | ||
|
||
debug-lock-table | ||
---- | ||
global: num=1 | ||
lock: "k" | ||
holder: txn: 00000001-0000-0000-0000-000000000000, ts: 0.000000012,2, info: unrepl epoch: 1, seqs: [0] | ||
local: num=0 | ||
|
||
reset namespace | ||
---- |
Oops, something went wrong.