Repository: tikv/client-go
Pull request #1509 (2024-11-22 19:00:29)

retry as stale read on replica
Issue #17422
We have seen that retrying stale read as follower read caused raft channel clogging which increases the blast radius and impact other regions. 
This would be the flow with this change
1. Try on replica in same AZ
2. Retry in leader if original request is not on leader and leader is marked serverIsBusy and DeadlineExceeded
3. Retry on replica as stale read

I expect requests to be succeeded in leader read if the leader is not slow. We should retry it as follower read if leader already slow. 

Unit Test
1. I injected the error between TiDB-->TikV and confirmed that reads are retried as stale reads. 

Test results

<img width="1728" alt="Screenshot 2024-11-22 at 10 36 54 AM" src="https://github.com/user-attachments/assets/c3441269-fa11-4044-b01f-70f0de41848f">
<img width="1703" alt="Screenshot 2024-11-22 at 10 34 02 AM" src="https://github.com/user-attachments/assets/32f6ac66-84a1-4443-bd13-d2af36e62f52">


ti-chi-bot[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign [5kbpers](https://github.com/5kbpers) for approval. For more information see [the Code Review Process](https://book.prow.tidb.net/#/workflows/pr).

The full list of commands accepted by this bot can be found [here](https://prow.tidb.net/command-help?repo=tikv%2Fclient-go).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/tikv/client-go/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["5kbpers"]} -->
ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code
- [b0706f4](https://github.com/tikv/client-go/commits/b0706f4533ebfeb21d08e63c9fa1027940b7926f) restructure the code

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code
- [b0706f4](https://github.com/tikv/client-go/commits/b0706f4533ebfeb21d08e63c9fa1027940b7926f) restructure the code
- [04d39eb](https://github.com/tikv/client-go/commits/04d39eb2b513f3b71bfc550cd2b622487818f31b) lint fix

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code
- [b0706f4](https://github.com/tikv/client-go/commits/b0706f4533ebfeb21d08e63c9fa1027940b7926f) restructure the code
- [04d39eb](https://github.com/tikv/client-go/commits/04d39eb2b513f3b71bfc550cd2b622487818f31b) lint fix
- [f68d5df](https://github.com/tikv/client-go/commits/f68d5df73fd44acd16011de3bbe2654b18b2068e) Update replica_selector.go

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code
- [b0706f4](https://github.com/tikv/client-go/commits/b0706f4533ebfeb21d08e63c9fa1027940b7926f) restructure the code
- [04d39eb](https://github.com/tikv/client-go/commits/04d39eb2b513f3b71bfc550cd2b622487818f31b) lint fix
- [f68d5df](https://github.com/tikv/client-go/commits/f68d5df73fd44acd16011de3bbe2654b18b2068e) Update replica_selector.go
- [c79a8e9](https://github.com/tikv/client-go/commits/c79a8e92282bf8817bc53dfaff59d98b7dd34090) format
- [4b109e7](https://github.com/tikv/client-go/commits/4b109e779c26a0e11f46fa2e010fcf9eb6a48027) fixed tests

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code
- [b0706f4](https://github.com/tikv/client-go/commits/b0706f4533ebfeb21d08e63c9fa1027940b7926f) restructure the code
- [04d39eb](https://github.com/tikv/client-go/commits/04d39eb2b513f3b71bfc550cd2b622487818f31b) lint fix
- [f68d5df](https://github.com/tikv/client-go/commits/f68d5df73fd44acd16011de3bbe2654b18b2068e) Update replica_selector.go
- [c79a8e9](https://github.com/tikv/client-go/commits/c79a8e92282bf8817bc53dfaff59d98b7dd34090) format
- [4b109e7](https://github.com/tikv/client-go/commits/4b109e779c26a0e11f46fa2e010fcf9eb6a48027) fixed tests
- [7431c01](https://github.com/tikv/client-go/commits/7431c015e38d0bc8032e958de0a1b52ddcf4b966) fixed tests

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>

ti-chi-bot[bot]: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/tikv/client-go/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [03dea11](https://github.com/tikv/client-go/commits/03dea1186261c5b5e638ca243bfe8c58b1a869c9) don't overwhelm the leader if it is busy or deadline exceeded
- [f41796d](https://github.com/tikv/client-go/commits/f41796dc17e9a65fa37b412e4668427669f66c64) add timeout
- [0b10c38](https://github.com/tikv/client-go/commits/0b10c38be87ed0bb2494168e4b521bb0fba427ab) build failure
- [2d91cf4](https://github.com/tikv/client-go/commits/2d91cf4fc1aec4a2184bf86c6140e9a68053d0aa) fixed a bug. sending replica read instead of stale read
- [8e09c68](https://github.com/tikv/client-go/commits/8e09c6869f2c36c42d261fe2e5207350071c5857) restructure the code
- [b0706f4](https://github.com/tikv/client-go/commits/b0706f4533ebfeb21d08e63c9fa1027940b7926f) restructure the code
- [04d39eb](https://github.com/tikv/client-go/commits/04d39eb2b513f3b71bfc550cd2b622487818f31b) lint fix
- [f68d5df](https://github.com/tikv/client-go/commits/f68d5df73fd44acd16011de3bbe2654b18b2068e) Update replica_selector.go
- [c79a8e9](https://github.com/tikv/client-go/commits/c79a8e92282bf8817bc53dfaff59d98b7dd34090) format
- [4b109e7](https://github.com/tikv/client-go/commits/4b109e779c26a0e11f46fa2e010fcf9eb6a48027) fixed tests
- [7431c01](https://github.com/tikv/client-go/commits/7431c015e38d0bc8032e958de0a1b52ddcf4b966) fixed tests
- [c5dfe0f](https://github.com/tikv/client-go/commits/c5dfe0fe007edb179a9f367a40223503766599c6) fixed tests

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>


Patch:
diff --git a/internal/locate/replica_selector.go b/internal/locate/replica_selector.go
index c906454..901592c 100644
--- a/internal/locate/replica_selector.go
+++ b/internal/locate/replica_selector.go
@@ -182,17 +182,21 @@ func (s *replicaSelector) nextForReplicaReadMixed(req *tikvrpc.Request) {
 	}
 	s.target = strategy.next(s)
 	if s.target != nil {
-		if s.isStaleRead && s.attempts == 1 {
-			// stale-read request first access.
-			if !s.target.store.IsLabelsMatch(s.option.labels) && s.target.peer.Id != s.region.GetLeaderPeerID() {
-				// If the target replica's labels is not match and not leader, use replica read.
-				// This is for compatible with old version.
-				req.StaleRead = false
-				req.ReplicaRead = true
-			} else {
-				// use stale read.
+		if s.isStaleRead {
+			isStaleRead := true
+			if s.attempts != 1 || (!s.target.store.IsLabelsMatch(s.option.labels) && s.target.peer.Id != s.region.GetLeaderPeerID()) {
+				// retry or target replica's labels does not match and not leader
+				if strategy.canSendReplicaRead(s) {
+					// use replica read.
+					isStaleRead = false
+				}
+			}
+			if isStaleRead {
 				req.StaleRead = true
 				req.ReplicaRead = false
+			} else {
+				req.StaleRead = false
+				req.ReplicaRead = true
 			}
 		} else {
 			// always use replica.
@@ -300,6 +304,16 @@ func (s *ReplicaSelectMixedStrategy) next(selector *replicaSelector) *replica {
 	return nil
 }
 
+func (s *ReplicaSelectMixedStrategy) canSendReplicaRead(selector *replicaSelector) bool {
+	replicas := selector.replicas
+	replica := replicas[s.leaderIdx]
+	if replica.hasFlag(deadlineErrUsingConfTimeoutFlag) || replica.hasFlag(serverIsBusyFlag) {
+		// don't overwhelm the leader if it is busy
+		return false
+	}
+	return true
+}
+
 func hasDeadlineExceededError(replicas []*replica) bool {
 	for _, replica := range replicas {
 		if replica.hasFlag(deadlineErrUsingConfTimeoutFlag) {
@@ -529,6 +543,9 @@ func (s *replicaSelector) onServerIsBusy(
 	backoffErr := errors.Errorf("server is busy, ctx: %v", ctx)
 	if s.canFastRetry() {
 		s.addPendingBackoff(store, retry.BoTiKVServerBusy, backoffErr)
+		if s.target != nil {
+			s.target.addFlag(serverIsBusyFlag)
+		}
 		return true, nil
 	}
 	err = bo.Backoff(retry.BoTiKVServerBusy, backoffErr)
diff --git a/txnkv/transaction/txn.go b/txnkv/transaction/txn.go
index 4eaa841..aeebceb 100644
--- a/txnkv/transaction/txn.go
+++ b/txnkv/transaction/txn.go
@@ -1704,6 +1704,11 @@ func (txn *KVTxn) StartTS() uint64 {
 	return txn.startTS
 }
 
+// CommitTS returns the commit timestamp of the already committed transaction, or zero if it's not committed yet.
+func (txn *KVTxn) CommitTS() uint64 {
+	return txn.commitTS
+}
+
 // Valid returns if the transaction is valid.
 // A transaction become invalid after commit or rollback.
 func (txn *KVTxn) Valid() bool {


Test patch:
diff --git a/integration_tests/1pc_test.go b/integration_tests/1pc_test.go
index de4db18..c69bbdb 100644
--- a/integration_tests/1pc_test.go
+++ b/integration_tests/1pc_test.go
@@ -112,12 +112,12 @@ func (s *testOnePCSuite) Test1PC() {
 	s.Equal(txn.GetCommitter().GetOnePCCommitTS(), txn.GetCommitter().GetCommitTS())
 	s.Greater(txn.GetCommitter().GetOnePCCommitTS(), txn.StartTS())
 	// Check keys are committed with the same version
-	s.mustGetFromSnapshot(txn.GetCommitTS(), k3, v3)
-	s.mustGetFromSnapshot(txn.GetCommitTS(), k4, v4)
-	s.mustGetFromSnapshot(txn.GetCommitTS(), k5, v5)
-	s.mustGetNoneFromSnapshot(txn.GetCommitTS()-1, k3)
-	s.mustGetNoneFromSnapshot(txn.GetCommitTS()-1, k4)
-	s.mustGetNoneFromSnapshot(txn.GetCommitTS()-1, k5)
+	s.mustGetFromSnapshot(txn.CommitTS(), k3, v3)
+	s.mustGetFromSnapshot(txn.CommitTS(), k4, v4)
+	s.mustGetFromSnapshot(txn.CommitTS(), k5, v5)
+	s.mustGetNoneFromSnapshot(txn.CommitTS()-1, k3)
+	s.mustGetNoneFromSnapshot(txn.CommitTS()-1, k4)
+	s.mustGetNoneFromSnapshot(txn.CommitTS()-1, k5)
 
 	// Overwriting in MVCC
 	v5New := []byte("v5new")
@@ -129,8 +129,8 @@ func (s *testOnePCSuite) Test1PC() {
 	s.True(txn.GetCommitter().IsOnePC())
 	s.Equal(txn.GetCommitter().GetOnePCCommitTS(), txn.GetCommitter().GetCommitTS())
 	s.Greater(txn.GetCommitter().GetOnePCCommitTS(), txn.StartTS())
-	s.mustGetFromSnapshot(txn.GetCommitTS(), k5, v5New)
-	s.mustGetFromSnapshot(txn.GetCommitTS()-1, k5, v5)
+	s.mustGetFromSnapshot(txn.CommitTS(), k5, v5New)
+	s.mustGetFromSnapshot(txn.CommitTS()-1, k5, v5)
 
 	// Check all keys
 	keys := [][]byte{k1, k2, k3, k4, k5}
@@ -175,8 +175,8 @@ func (s *testOnePCSuite) Test1PCIsolation() {
 	s.mustGetFromTxn(txn2, k, v1)
 	s.Nil(txn2.Rollback())
 
-	s.mustGetFromSnapshot(txn.GetCommitTS(), k, v2)
-	s.mustGetFromSnapshot(txn.GetCommitTS()-1, k, v1)
+	s.mustGetFromSnapshot(txn.CommitTS(), k, v2)
+	s.mustGetFromSnapshot(txn.CommitTS()-1, k, v1)
 }
 
 func (s *testOnePCSuite) Test1PCDisallowMultiRegion() {
diff --git a/integration_tests/2pc_test.go b/integration_tests/2pc_test.go
index 08de42e..fec8cfb 100644
--- a/integration_tests/2pc_test.go
+++ b/integration_tests/2pc_test.go
@@ -1076,7 +1076,7 @@ func (s *testCommitterSuite) TestPessimisticLockAllowLockWithConflict() {
 			txn.StartAggressiveLocking()
 
 			s.Nil(txn0.Commit(context.Background()))
-			s.Greater(txn0.GetCommitTS(), txn.StartTS())
+			s.Greater(txn0.CommitTS(), txn.StartTS())
 
 			lockCtx := &kv.LockCtx{ForUpdateTS: txn.StartTS(), WaitStartTime: time.Now()}
 			if checkExistence {
@@ -1087,9 +1087,9 @@ func (s *testCommitterSuite) TestPessimisticLockAllowLockWithConflict() {
 			}
 			s.Nil(txn.LockKeys(context.Background(), lockCtx, key))
 
-			s.Equal(txn0.GetCommitTS(), lockCtx.MaxLockedWithConflictTS)
+			s.Equal(txn0.CommitTS(), lockCtx.MaxLockedWithConflictTS)
 			v := lockCtx.Values[string(key)]
-			s.Equal(txn0.GetCommitTS(), v.LockedWithConflictTS)
+			s.Equal(txn0.CommitTS(), v.LockedWithConflictTS)
 			s.True(v.Exists)
 			s.Equal(value, v.Value)
 
@@ -1269,12 +1269,12 @@ func (s *testCommitterSuite) TestAggressiveLockingInsert() {
 	s.IsType(errors.Cause(err), &tikverr.ErrWriteConflict{})
 	lockCtx = &kv.LockCtx{ForUpdateTS: txn.StartTS(), WaitStartTime: time.Now()}
 	s.NoError(insertPessimisticLock(lockCtx, "k8"))
-	s.Equal(txn2.GetCommitTS(), lockCtx.MaxLockedWithConflictTS)
-	s.Equal(txn2.GetCommitTS(), lockCtx.Values["k8"].LockedWithConflictTS)
+	s.Equal(txn2.CommitTS(), lockCtx.MaxLockedWithConflictTS)
+	s.Equal(txn2.CommitTS(), lockCtx.Values["k8"].LockedWithConflictTS)
 	// Update forUpdateTS to simulate a pessimistic retry.
 	newForUpdateTS, err := s.store.CurrentTimestamp(oracle.GlobalTxnScope)
 	s.Nil(err)
-	s.GreaterOrEqual(newForUpdateTS, txn2.GetCommitTS())
+	s.GreaterOrEqual(newForUpdateTS, txn2.CommitTS())
 	lockCtx = &kv.LockCtx{ForUpdateTS: newForUpdateTS, WaitStartTime: time.Now()}
 	mustAlreadyExist(insertPessimisticLock(lockCtx, "k7"))
 	s.NoError(insertPessimisticLock(lockCtx, "k8"))
@@ -1291,7 +1291,7 @@ func (s *testCommitterSuite) TestAggressiveLockingLockOnlyIfExists() {
 	txn0 := s.begin()
 	s.NoError(txn0.Set([]byte("k1"), []byte("v1")))
 	s.NoError(txn0.Commit(context.Background()))
-	txn0CommitTS := txn0.GetCommitTS()
+	txn0CommitTS := txn0.CommitTS()
 
 	txn.StartAggressiveLocking()
 	lockCtx := &kv.LockCtx{ForUpdateTS: txn.StartTS(), WaitStartTime: time.Now(), ReturnValues: true, LockOnlyIfExists: true}
@@ -1312,7 +1312,7 @@ func (s *testCommitterSuite) TestAggressiveLockingLockOnlyIfExists() {
 	txn0 = s.begin()
 	s.NoError(txn0.Delete([]byte("k1")))
 	s.NoError(txn0.Commit(context.Background()))
-	txn0CommitTS = txn0.GetCommitTS()
+	txn0CommitTS = txn0.CommitTS()
 
 	txn.StartAggressiveLocking()
 	lockCtx = &kv.LockCtx{ForUpdateTS: txn.StartTS(), WaitStartTime: time.Now(), ReturnValues: true, LockOnlyIfExists: true}
@@ -1461,13 +1461,13 @@ func (s *testCommitterSuite) TestAggressiveLockingLoadValueOptionChanges() {
 		s.NoError(txn.LockKeys(context.Background(), lockCtx, []byte("k2")))
 
 		if firstAttemptLockedWithConflict {
-			s.Equal(txn2.GetCommitTS(), lockCtx.MaxLockedWithConflictTS)
-			s.Equal(txn2.GetCommitTS(), lockCtx.Values["k1"].LockedWithConflictTS)
-			s.Equal(txn2.GetCommitTS(), lockCtx.Values["k2"].LockedWithConflictTS)
+			s.Equal(txn2.CommitTS(), lockCtx.MaxLockedWithConflictTS)
+			s.Equal(txn2.CommitTS(), lockCtx.Values["k1"].LockedWithConflictTS)
+			s.Equal(txn2.CommitTS(), lockCtx.Values["k2"].LockedWithConflictTS)
 		}
 
 		if firstAttemptLockedWithConflict {
-			forUpdateTS = txn2.GetCommitTS() + 1
+			forUpdateTS = txn2.CommitTS() + 1
 		} else {
 			forUpdateTS++
 		}
diff --git a/integration_tests/assertion_test.go b/integration_tests/assertion_test.go
index 73f901b..f5699d5 100644
--- a/integration_tests/assertion_test.go
+++ b/integration_tests/assertion_test.go
@@ -81,7 +81,7 @@ func (s *testAssertionSuite) testAssertionImpl(keyPrefix string, pessimistic boo
 	err = prepareTxn.Commit(context.Background())
 	s.Nil(err)
 	prepareStartTS := prepareTxn.GetCommitter().GetStartTS()
-	prepareCommitTS := prepareTxn.GetCommitTS()
+	prepareCommitTS := prepareTxn.CommitTS()
 
 	// A helper to perform a complete transaction. When multiple keys are passed in, assertion will be set on only
 	// the last key.
diff --git a/integration_tests/async_commit_test.go b/integration_tests/async_commit_test.go
index 5410984..4c478f4 100644
--- a/integration_tests/async_commit_test.go
+++ b/integration_tests/async_commit_test.go
@@ -112,7 +112,7 @@ func (s *testAsyncCommitCommon) putKV(key, value []byte, enableAsyncCommit bool)
 	s.Nil(err)
 	err = txn.Commit(context.Background())
 	s.Nil(err)
-	return txn.StartTS(), txn.GetCommitTS()
+	return txn.StartTS(), txn.CommitTS()
 }
 
 func (s *testAsyncCommitCommon) mustGetFromTxn(txn transaction.TxnProbe, key, expectedValue []byte) {
@@ -440,8 +440,8 @@ func (s *testAsyncCommitSuite) TestAsyncCommitLinearizability() {
 	s.Nil(err)
 	err = t1.Commit(ctx)
 	s.Nil(err)
-	commitTS1 := t1.GetCommitTS()
-	commitTS2 := t2.GetCommitTS()
+	commitTS1 := t1.CommitTS()
+	commitTS2 := t2.CommitTS()
 	s.Less(commitTS2, commitTS1)
 }
 
diff --git a/integration_tests/isolation_test.go b/integration_tests/isolation_test.go
index 254fb45..f8fcadf 100644
--- a/integration_tests/isolation_test.go
+++ b/integration_tests/isolation_test.go
@@ -96,7 +96,7 @@ func (s *testIsolationSuite) SetWithRetry(k, v []byte) writeRecord {
 		if err == nil {
 			return writeRecord{
 				startTS:  txn.StartTS(),
-				commitTS: txn.GetCommitTS(),
+				commitTS: txn.CommitTS(),
 			}
 		}
 	}
diff --git a/integration_tests/lock_test.go b/integration_tests/lock_test.go
index 65d0426..0019027 100644
--- a/integration_tests/lock_test.go
+++ b/integration_tests/lock_test.go
@@ -142,7 +142,7 @@ func (s *testLockSuite) putKV(key, value []byte) (uint64, uint64) {
 	s.Nil(err)
 	err = txn.Commit(context.Background())
 	s.Nil(err)
-	return txn.StartTS(), txn.GetCommitTS()
+	return txn.StartTS(), txn.CommitTS()
 }
 
 func (s *testLockSuite) prepareAlphabetLocks() {
@@ -1311,7 +1311,7 @@ func (s *testLockWithTiKVSuite) TestPrewriteCheckForUpdateTS() {
 			info := simulatedTxn.GetAggressiveLockingKeysInfo()
 			s.Equal(1, len(info))
 			s.Equal(k1, info[0].Key())
-			s.Equal(txn2.GetCommitTS(), info[0].ActualLockForUpdateTS())
+			s.Equal(txn2.CommitTS(), info[0].ActualLockForUpdateTS())
 
 			simulatedTxn.DoneAggressiveLocking(context.Background())
 			defer func() {
@@ -1324,12 +1324,12 @@ func (s *testLockWithTiKVSuite) TestPrewriteCheckForUpdateTS() {
 		s.Error(err)
 		s.Regexp("[pP]essimistic ?[lL]ock ?[nN]ot ?[fF]ound", err.Error())
 
-		snapshot := s.store.GetSnapshot(txn2.GetCommitTS())
+		snapshot := s.store.GetSnapshot(txn2.CommitTS())
 		v, err := snapshot.Get(context.Background(), k1)
 		s.NoError(err)
 		s.Equal(v2, v)
 
-		snapshot = s.store.GetSnapshot(txn2.GetCommitTS() - 1)
+		snapshot = s.store.GetSnapshot(txn2.CommitTS() - 1)
 		_, err = snapshot.Get(context.Background(), k1)
 		s.Equal(tikverr.ErrNotExist, err)
 
@@ -1369,7 +1369,7 @@ func (s *testLockWithTiKVSuite) TestPrewriteCheckForUpdateTS() {
 
 		s.NoError(txn.Commit(ctx))
 
-		snapshot = s.store.GetSnapshot(txn.GetCommitTS())
+		snapshot = s.store.GetSnapshot(txn.CommitTS())
 		v, err = snapshot.Get(context.Background(), k2)
 		s.NoError(err)
 		s.Equal(v1, v)
@@ -1377,7 +1377,7 @@ func (s *testLockWithTiKVSuite) TestPrewriteCheckForUpdateTS() {
 		s.NoError(err)
 		s.Equal(v1, v)
 
-		snapshot = s.store.GetSnapshot(txn.GetCommitTS() - 1)
+		snapshot = s.store.GetSnapshot(txn.CommitTS() - 1)
 		v, err = snapshot.Get(context.Background(), k2)
 		s.NoError(err)
 		s.Equal(v2, v)
diff --git a/internal/locate/region_request_state_test.go b/internal/locate/region_request_state_test.go
index 022fb13..e903cb4 100644
--- a/internal/locate/region_request_state_test.go
+++ b/internal/locate/region_request_state_test.go
@@ -367,7 +367,7 @@ func TestRegionCacheStaleRead(t *testing.T) {
 			leaderRegionValid:       true,
 			leaderAsyncReload:       util.Some(false),
 			leaderSuccessReplica:    []string{"z2", "z3"},
-			leaderSuccessReadType:   SuccessFollowerRead,
+			leaderSuccessReadType:   SuccessStaleRead,
 			followerRegionValid:     true,
 			followerAsyncReload:     util.Some(false),
 			followerSuccessReplica:  []string{"z2"},
@@ -392,11 +392,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
 			leaderRegionValid:       true,
 			leaderAsyncReload:       util.Some(false),
 			leaderSuccessReplica:    []string{"z3"},
-			leaderSuccessReadType:   SuccessFollowerRead,
+			leaderSuccessReadType:   SuccessStaleRead,
 			followerRegionValid:     true,
 			followerAsyncReload:     util.Some(false),
 			followerSuccessReplica:  []string{"z3"},
-			followerSuccessReadType: SuccessFollowerRead,
+			followerSuccessReadType: SuccessStaleRead,
 		},
 		{
 			do:                      leaderServerIsBusy,
@@ -405,11 +405,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
 			leaderRegionValid:       true,
 			leaderAsyncReload:       util.Some(false),
 			leaderSuccessReplica:    []string{"z2", "z3"},
-			leaderSuccessReadType:   SuccessFollowerRead,
+			leaderSuccessReadType:   SuccessStaleRead,
 			followerRegionValid:     true,
 			followerAsyncReload:     util.Some(false),
 			followerSuccessReplica:  []string{"z2", "z3"},
-			followerSuccessReadType: SuccessFollowerRead,
+			followerSuccessReadType: SuccessStaleRead,
 		},
 		{
 			do:                      leaderServerIsBusy,
@@ -418,11 +418,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
 			leaderRegionValid:       true,
 			leaderAsyncReload:       util.Some(false),
 			leaderSuccessReplica:    []string{"z3"},
-			leaderSuccessReadType:   SuccessFollowerRead,
+			leaderSuccessReadType:   SuccessStaleRead,
 			followerRegionValid:     true,
 			followerAsyncReload:     util.Some(false),
 			followerSuccessReplica:  []string{"z3"},
-			followerSuccessReadType: SuccessFollowerRead,
+			followerSuccessReadType: SuccessStaleRead,
 		},
 		{
 			do:                      leaderDown,
diff --git a/internal/locate/replica_selector_test.go b/internal/locate/replica_selector_test.go
index e7af35f..d928b0a 100644
--- a/internal/locate/replica_selector_test.go
+++ b/internal/locate/replica_selector_test.go
@@ -362,7 +362,7 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}",
-				"{addr: store2, replica-read: true, stale-read: false}"},
+				"{addr: store2, replica-read: false, stale-read: true}"},
 			respErr:         "",
 			respRegionError: nil,
 			backoffCnt:      0,
@@ -404,7 +404,7 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}"}, // store2 has DeadLineExceededErr, so don't retry store2 even it is new leader.
+				"{addr: store3, replica-read: false, stale-read: true}"}, // store2 has DeadLineExceededErr, so don't retry store2 even it is new leader.
 			respErr:         "",
 			respRegionError: nil,
 			backoffCnt:      0,
@@ -504,8 +504,8 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store1, replica-read: false, stale-read: true}",
-				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}"},
+				"{addr: store2, replica-read: false, stale-read: true}",
+				"{addr: store3, replica-read: false, stale-read: true}"},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
 			backoffCnt:      1,
@@ -548,7 +548,7 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store3, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}",
-				"{addr: store2, replica-read: true, stale-read: false}"},
+				"{addr: store2, replica-read: false, stale-read: true}"},
 			respErr:         "",
 			respRegionError: nil,
 			backoffCnt:      0,
@@ -568,8 +568,8 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}", // try leader with leader read.
-				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store2, replica-read: false, stale-read: true}",
+				"{addr: store3, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
@@ -590,8 +590,8 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store1, replica-read: false, stale-read: true}",
 				"{addr: store2, replica-read: false, stale-read: false}", // try leader with leader read.
-				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: true}",
+				"{addr: store1, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -611,8 +611,8 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store1, replica-read: false, stale-read: true}",
 				"{addr: store2, replica-read: false, stale-read: false}", // try leader with leader read.
-				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: true}",
+				"{addr: store1, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
@@ -654,8 +654,8 @@ func TestReplicaReadAccessPathByCase(t *testing.T) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store1, replica-read: false, stale-read: true}",
-				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store2, replica-read: false, stale-read: true}",
+				"{addr: store3, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -920,7 +920,7 @@ func TestReplicaReadAccessPathByCase2(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}"},
+				"{addr: store3, replica-read: false, stale-read: true}"},
 			respErr:         "",
 			respRegionError: nil,
 			backoffCnt:      0,
@@ -1052,9 +1052,16 @@ func TestReplicaReadAccessPathByBasicCase(t *testing.T) {
 							backoff = []string{}
 						}
 						if staleRead {
-							accessPath = []string{
-								"{addr: store1, replica-read: false, stale-read: true}",
-								"{addr: store2, replica-read: true, stale-read: false}",
+							if tp == ServerIsBusyErr || tp == ServerIsBusyWithEstimatedWaitMsErr {
+								accessPath = []string{
+									"{addr: store1, replica-read: false, stale-read: true}",
+									"{addr: store2, replica-read: false, stale-read: true}",
+								}
+							} else {
+								accessPath = []string{
+									"{addr: store1, replica-read: false, stale-read: true}",
+									"{addr: store2, replica-read: true, stale-read: false}",
+								}
 							}
 						}
 					default:
@@ -1918,8 +1925,8 @@ func TestReplicaReadAccessPathByStaleReadCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}", // try leader with leader read.
-				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store2, replica-read: false, stale-read: true}",
+				"{addr: store3, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
@@ -1940,8 +1947,8 @@ func TestReplicaReadAccessPathByStaleReadCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store1, replica-read: false, stale-read: true}",
 				"{addr: store2, replica-read: false, stale-read: false}", // try leader with leader read.
-				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: true}",
+				"{addr: store1, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
@@ -2050,8 +2057,8 @@ func TestReplicaReadAccessPathByStaleReadCase(t *testing.T) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store1, replica-read: false, stale-read: true}",
-				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store2, replica-read: false, stale-read: true}",
+				"{addr: store3, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -2073,7 +2080,7 @@ func TestReplicaReadAccessPathByStaleReadCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -2095,7 +2102,7 @@ func TestReplicaReadAccessPathByStaleReadCase(t *testing.T) {
 			accessPath: []string{
 				"{addr: store2, replica-read: false, stale-read: true}",
 				"{addr: store1, replica-read: false, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: true}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -2666,7 +2673,11 @@ func TestReplicaReadAvoidSlowStore(t *testing.T) {
 			if expectedFirstStore == 3 {
 				// Retry on store 2 which is a follower.
 				// Stale-read mode falls back to replica-read mode.
-				expectedSecondPath = "{addr: store2, replica-read: true, stale-read: false}"
+				if staleRead {
+					expectedSecondPath = "{addr: store2, replica-read: false, stale-read: true}"
+				} else {
+					expectedSecondPath = "{addr: store2, replica-read: true, stale-read: false}"
+				}
 			} else {
 				if staleRead {
 					// Retry in leader read mode
diff --git a/txnkv/transaction/test_probe.go b/txnkv/transaction/test_probe.go
index b782a3a..7e0a582 100644
--- a/txnkv/transaction/test_probe.go
+++ b/txnkv/transaction/test_probe.go
@@ -38,11 +38,6 @@ func (txn TxnProbe) SetStartTS(ts uint64) {
 	txn.startTS = ts
 }
 
-// GetCommitTS returns the commit ts.
-func (txn TxnProbe) GetCommitTS() uint64 {
-	return txn.commitTS
-}
-
 // GetUnionStore returns transaction's embedded unionstore.
 func (txn TxnProbe) GetUnionStore() *unionstore.KVUnionStore {
 	return txn.us