Repository: tikv/client-go
Pull request #1719 (2025-07-22 02:49:43)

replica selector: do not send replica-read to leader
close #1718

When the client select replica with `ReplicaReadMixed`, it should be aware of the leader target and set `Context.ReplicaRead` to false for this case.

Test with fixed rate sysbench read only, the QPS keeps the same, this PR lower the read index and TiKV CPU utilization a lot.

```
sysbench --config-file="config-3" oltp_read_only --tables=1 --table-size=100000000 --range-size=1000 --rate=3000 run
```

<img width="1603" height="880" alt="Screenshot_20250722_150521" src="https://github.com/user-attachments/assets/a04751c0-3968-4c40-96d7-c8135f2252c0" />

<img width="1604" height="878" alt="image" src="https://github.com/user-attachments/assets/c74ebde8-a3bc-4e96-a984-07874bfca3b0" />

Think one-third read requests are sent to the leader, this optimization should reduce read index operations by one-third. Fortunately, we already have a [follower read cache](https://github.com/tikv/rfcs/blob/master/text/0113-follower-read-cache.md) that works on follower replicas to skip some read index operations, that means there are much more read-index operations happen on leader. The final result is that the number of read index operations is halved.

<img width="1598" height="873" alt="image" src="https://github.com/user-attachments/assets/1c280906-5779-49d8-b0e1-43ab6c327a4d" />



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 [glorv](https://github.com/glorv) for approval. For more information see [the Code Review Process](https://book.prow.tidb.net/#/workflows/pr).
**Please ensure that each of them provides their approval before proceeding.**

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":["glorv"]} -->
ti-chi-bot[bot]: [LGTM Timeline notifier]
---
Timeline:

- `2025-07-22 07:41:24.210445505 +0000 UTC m=+64040.947871715`: :ballot_box_with_check: agreed by [zyguan](https://github.com/zyguan).
- `2025-07-22 08:59:44.657239752 +0000 UTC m=+68741.394665963`: :ballot_box_with_check: agreed by [cfzjywxk](https://github.com/cfzjywxk).

Patch:
diff --git a/internal/locate/replica_selector.go b/internal/locate/replica_selector.go
index dc1fbaa..a29f924 100644
--- a/internal/locate/replica_selector.go
+++ b/internal/locate/replica_selector.go
@@ -199,9 +199,9 @@ func (s *replicaSelector) nextForReplicaReadMixed(req *tikvrpc.Request) {
 				req.ReplicaRead = true
 			}
 		} else {
-			// always use replica.
+			// use replica read only if the target is not leader.
 			req.StaleRead = false
-			req.ReplicaRead = s.isReadOnlyReq
+			req.ReplicaRead = s.isReadOnlyReq && s.target.peer.Id != s.region.GetLeaderPeerID()
 		}
 		// Monitor the flows destination if selector is under `ReplicaReadPreferLeader` mode.
 		if s.option.preferLeader {


Test patch:
diff --git a/integration_tests/snapshot_test.go b/integration_tests/snapshot_test.go
index b35c728..240ee6d 100644
--- a/integration_tests/snapshot_test.go
+++ b/integration_tests/snapshot_test.go
@@ -49,6 +49,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/suite"
 	"github.com/tikv/client-go/v2/config"
+	"github.com/tikv/client-go/v2/config/retry"
 	tikverr "github.com/tikv/client-go/v2/error"
 	"github.com/tikv/client-go/v2/kv"
 	"github.com/tikv/client-go/v2/oracle"
@@ -435,8 +436,27 @@ func (s *testSnapshotSuite) TestReplicaReadAdjuster() {
 		cfg.EnableAsyncBatchGet = originAsyncEnable
 		config.StoreGlobalConfig(cfg)
 	}()
-	_, err := s.store.SplitRegions(context.Background(), [][]byte{[]byte("y1")}, false, nil)
+	regionIDs, err := s.store.SplitRegions(context.Background(), [][]byte{[]byte("y1")}, false, nil)
 	s.Nil(err)
+	for _, regionID := range regionIDs {
+		var loc *tikv.KeyLocation
+		s.Eventually(func() bool {
+			loc, err = s.store.GetRegionCache().LocateRegionByID(retry.NewNoopBackoff(context.Background()), regionID)
+			return err == nil
+		}, 5*time.Second, time.Millisecond)
+		region := s.store.GetRegionCache().GetCachedRegionWithRLock(loc.Region)
+		s.NotNil(region)
+		s.Equal(region.GetLeaderStoreID(), uint64(1))
+	}
+	stores := s.store.GetRegionCache().GetAllStores()
+	var leaderStoreAddr string
+	for _, store := range stores {
+		if store.StoreID() == 1 {
+			leaderStoreAddr = store.GetAddr()
+			break
+		}
+	}
+	s.NotEqual(leaderStoreAddr, "")
 	for _, async := range []bool{true, false} {
 		cfg := config.GetGlobalConfig()
 		cfg.EnableAsyncBatchGet = async
@@ -447,8 +467,9 @@ func (s *testSnapshotSuite) TestReplicaReadAdjuster() {
 			// check the replica read type
 			fn := func(next interceptor.RPCInterceptorFunc) interceptor.RPCInterceptorFunc {
 				return func(target string, req *tikvrpc.Request) (*tikvrpc.Response, error) {
-					// when the request is fallback to leader read, the ReplicaRead should be false to avoid read-index in store.
-					s.Equal(hit, req.ReplicaRead)
+					// When the request falls back to leader read or when the target replica is the leader,
+					// ReplicaRead should be set to false to avoid read-index operations on the leader.
+					s.Equal(hit && target != leaderStoreAddr, req.ReplicaRead)
 					if hit {
 						s.Equal(kv.ReplicaReadMixed, req.ReplicaReadType)
 					} else {
diff --git a/internal/locate/replica_selector_test.go b/internal/locate/replica_selector_test.go
index 553bda4..87d8acd 100644
--- a/internal/locate/replica_selector_test.go
+++ b/internal/locate/replica_selector_test.go
@@ -539,7 +539,7 @@ func testReplicaReadAccessPathByCase(s *testReplicaSelectorSuite) {
 		accessErr: []RegionErrorType{ServerIsBusyErr, ServerIsBusyErr, DeadLineExceededErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}"},
 			respErr:         "",
@@ -648,7 +648,7 @@ func testReplicaReadAccessPathByCase(s *testReplicaSelectorSuite) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}"},
 			respErr:         "",
 			respRegionError: nil,
@@ -690,7 +690,7 @@ func testReplicaReadAccessPathByCase(s *testReplicaSelectorSuite) {
 			accessPath: []string{
 				"{addr: store1, replica-read: true, stale-read: false}",
 				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: false}",
 			},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
@@ -708,7 +708,7 @@ func testReplicaReadAccessPathByCase(s *testReplicaSelectorSuite) {
 		accessErr: []RegionErrorType{ServerIsBusyErr, ServerIsBusyErr, ServerIsBusyErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: false}",
 				"{addr: store1, replica-read: true, stale-read: false}",
 				"{addr: store2, replica-read: true, stale-read: false}",
 			},
@@ -730,7 +730,7 @@ func testReplicaReadAccessPathByCase(s *testReplicaSelectorSuite) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store3, replica-read: true, stale-read: false}",
+				"{addr: store3, replica-read: false, stale-read: false}",
 				"{addr: store1, replica-read: true, stale-read: false}",
 			},
 			respErr:         "",
@@ -775,7 +775,7 @@ func testReplicaReadAccessPathByCase2(s *testReplicaSelectorSuite) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}"},
 			respErr:         "region 0 is not prepared for the flashback",
 			respRegionError: nil,
@@ -818,7 +818,7 @@ func testReplicaReadAccessPathByCase2(s *testReplicaSelectorSuite) {
 		accessErr: []RegionErrorType{ServerIsBusyErr, ServerIsBusyErr, DeadLineExceededErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}"},
 			respErr:         "",
@@ -968,7 +968,7 @@ func testReplicaReadAccessPathByCase2(s *testReplicaSelectorSuite) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}"},
 			respErr:         "",
 			respRegionError: nil,
@@ -990,7 +990,7 @@ func testReplicaReadAccessPathByCase2(s *testReplicaSelectorSuite) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store2, replica-read: true, stale-read: false}",
+				"{addr: store2, replica-read: false, stale-read: false}",
 				"{addr: store1, replica-read: true, stale-read: false}"},
 			respErr:         "",
 			respRegionError: nil,
@@ -1072,7 +1072,7 @@ func testReplicaReadAccessPathByBasicCase(s *testReplicaSelectorSuite) {
 						backoff = []string{"maxTsNotSynced+1"}
 					}
 					accessPath := []string{
-						"{addr: store1, replica-read: true, stale-read: false}",
+						"{addr: store1, replica-read: false, stale-read: false}",
 						"{addr: store2, replica-read: true, stale-read: false}",
 					}
 					switch readType {
@@ -1133,7 +1133,7 @@ func testReplicaReadAccessPathByBasicCase(s *testReplicaSelectorSuite) {
 					regionIsValid := false
 					respErr := ""
 					respRegionError := tp.GenRegionError()
-					accessPath := []string{"{addr: store1, replica-read: true, stale-read: false}"}
+					accessPath := []string{"{addr: store1, replica-read: false, stale-read: false}"}
 					switch tp {
 					case RecoveryInProgressErr:
 						backoff = []string{"regionRecoveryInProgress+1"}
@@ -1558,7 +1558,7 @@ func testReplicaReadAccessPathByFollowerCase(s *testReplicaSelectorSuite) {
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -1577,7 +1577,7 @@ func testReplicaReadAccessPathByFollowerCase(s *testReplicaSelectorSuite) {
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 			},
 			respErr:         "",
 			respRegionError: fakeEpochNotMatch,
@@ -1596,7 +1596,7 @@ func testReplicaReadAccessPathByFollowerCase(s *testReplicaSelectorSuite) {
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -1616,7 +1616,7 @@ func testReplicaReadAccessPathByFollowerCase(s *testReplicaSelectorSuite) {
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -1657,7 +1657,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: nil,
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 				},
 				respErr:         "",
 				respRegionError: nil,
@@ -1674,7 +1674,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: []RegionErrorType{ServerIsBusyWithEstimatedWaitMsErr, StaleCommandErr, ServerIsBusyErr},
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 					"{addr: store2, replica-read: true, stale-read: false}",
 					"{addr: store3, replica-read: true, stale-read: false}",
 				},
@@ -1693,7 +1693,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: []RegionErrorType{ServerIsBusyErr, RegionNotFoundErr},
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 					"{addr: store2, replica-read: true, stale-read: false}",
 				},
 				respErr:         "",
@@ -1711,7 +1711,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: []RegionErrorType{DeadLineExceededErr, ServerIsBusyErr},
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 					"{addr: store2, replica-read: true, stale-read: false}",
 					"{addr: store3, replica-read: true, stale-read: false}",
 				},
@@ -1731,7 +1731,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: []RegionErrorType{DeadLineExceededErr, ServerIsBusyErr},
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 					"{addr: store2, replica-read: true, stale-read: false}",
 					"{addr: store3, replica-read: true, stale-read: false}",
 				},
@@ -1753,7 +1753,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store2, replica-read: true, stale-read: false}", // try match label first.
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}",
 			},
 			respErr:         "",
@@ -1772,7 +1772,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 		accessErr: []RegionErrorType{DeadLineExceededErr, ServerIsBusyErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store3, replica-read: true, stale-read: false}", // try leader first.
+				"{addr: store3, replica-read: false, stale-read: false}", // try leader first.
 				"{addr: store1, replica-read: true, stale-read: false}",
 				"{addr: store2, replica-read: true, stale-read: false}",
 			},
@@ -1792,8 +1792,8 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 		accessErr: []RegionErrorType{DeadLineExceededErr, ServerIsBusyErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store2, replica-read: true, stale-read: false}", // try match label first, since match label has higher priority.
-				"{addr: store3, replica-read: true, stale-read: false}", // try leader.
+				"{addr: store2, replica-read: true, stale-read: false}",  // try match label first, since match label has higher priority.
+				"{addr: store3, replica-read: false, stale-read: false}", // try leader.
 				"{addr: store1, replica-read: true, stale-read: false}",
 			},
 			respErr:         "",
@@ -1815,7 +1815,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: []RegionErrorType{ServerIsBusyErr},
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}", // store1 will be marked already slow.
+					"{addr: store1, replica-read: false, stale-read: false}", // store1 will be marked already slow.
 					"{addr: store2, replica-read: true, stale-read: false}",
 				},
 				respErr:         "",
@@ -1837,7 +1837,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 				accessPath: []string{
 					"{addr: store2, replica-read: true, stale-read: false}", // won't try leader in store1, since it is slow.
 					"{addr: store3, replica-read: true, stale-read: false}",
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 				},
 				respErr:         "",
 				respRegionError: fakeEpochNotMatch,
@@ -1858,8 +1858,8 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			accessErr: []RegionErrorType{ServerIsBusyErr, ServerIsBusyErr},
 			expect: &accessPathResult{
 				accessPath: []string{
-					"{addr: store1, replica-read: true, stale-read: false}", // store1 will be marked already slow.
-					"{addr: store2, replica-read: true, stale-read: false}", // store2 will be marked already slow.
+					"{addr: store1, replica-read: false, stale-read: false}", // store1 will be marked already slow.
+					"{addr: store2, replica-read: true, stale-read: false}",  // store2 will be marked already slow.
 					"{addr: store3, replica-read: true, stale-read: false}",
 				},
 				respErr:         "",
@@ -1880,7 +1880,7 @@ func testReplicaReadAccessPathByMixedAndPreferLeaderCase(s *testReplicaSelectorS
 			expect: &accessPathResult{
 				accessPath: []string{
 					"{addr: store3, replica-read: true, stale-read: false}", // won't try leader in store1, since it is slow, ditto for store2.
-					"{addr: store1, replica-read: true, stale-read: false}",
+					"{addr: store1, replica-read: false, stale-read: false}",
 					// won't retry store2, since it is slow, and it is not leader replica.
 				},
 				respErr:         "",
@@ -2412,7 +2412,7 @@ func TestReplicaReadAccessPathByFlashbackInProgressCase(t *testing.T) {
 		accessErr: []RegionErrorType{FlashbackInProgressErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 			},
 			respErr:         "region 0 is in flashback progress, FlashbackStartTS is 0",
 			respRegionError: nil,
@@ -2432,7 +2432,7 @@ func TestReplicaReadAccessPathByFlashbackInProgressCase(t *testing.T) {
 		accessErr: []RegionErrorType{DeadLineExceededErr, FlashbackInProgressErr, FlashbackInProgressErr},
 		expect: &accessPathResult{
 			accessPath: []string{
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 				"{addr: store2, replica-read: true, stale-read: false}",
 				"{addr: store3, replica-read: true, stale-read: false}",
 			},
@@ -2633,7 +2633,7 @@ func TestReplicaReadAccessPathByLearnerCase(t *testing.T) {
 		expect: &accessPathResult{
 			accessPath: []string{
 				"{addr: store4, replica-read: true, stale-read: false}",
-				"{addr: store1, replica-read: true, stale-read: false}",
+				"{addr: store1, replica-read: false, stale-read: false}",
 			},
 			respErr:         "",
 			respRegionError: nil,
@@ -2700,7 +2700,7 @@ func TestReplicaReadAvoidSlowStore(t *testing.T) {
 				accessErr:       []RegionErrorType{},
 				expect: &accessPathResult{
 					accessPath: []string{
-						fmt.Sprintf("{addr: store%v, replica-read: %v, stale-read: %v}", expectedFirstStore, !staleRead, staleRead),
+						fmt.Sprintf("{addr: store%v, replica-read: %v, stale-read: %v}", expectedFirstStore, !staleRead && expectedFirstStore != 3, staleRead),
 					},
 					respErr:         "",
 					respRegionError: nil,
@@ -2723,7 +2723,7 @@ func TestReplicaReadAvoidSlowStore(t *testing.T) {
 			} else {
 				if withLabel {
 					// Prefer retrying leader.
-					expectedSecondPath = "{addr: store3, replica-read: true, stale-read: false}"
+					expectedSecondPath = "{addr: store3, replica-read: false, stale-read: false}"
 				} else {
 					// Retry any another replica.
 					expectedSecondPath = "{addr: store2, replica-read: true, stale-read: false}"
@@ -2763,13 +2763,8 @@ func TestReplicaReadAvoidSlowStore(t *testing.T) {
 					expectedSecondPath = "{addr: store2, replica-read: true, stale-read: false}"
 				}
 			} else {
-				if staleRead {
-					// Retry in leader read mode
-					expectedSecondPath = "{addr: store3, replica-read: false, stale-read: false}"
-				} else {
-					// Retry with the same mode, which is replica-read mode.
-					expectedSecondPath = "{addr: store3, replica-read: true, stale-read: false}"
-				}
+				// Retry in leader read mode will not use replica-read.
+				expectedSecondPath = "{addr: store3, replica-read: false, stale-read: false}"
 			}
 
 			ca = replicaSelectorAccessPathCase{
@@ -2782,7 +2777,7 @@ func TestReplicaReadAvoidSlowStore(t *testing.T) {
 				accessErr:       []RegionErrorType{ServerIsBusyErr},
 				expect: &accessPathResult{
 					accessPath: []string{
-						fmt.Sprintf("{addr: store%v, replica-read: %v, stale-read: %v}", expectedFirstStore, !staleRead, staleRead),
+						fmt.Sprintf("{addr: store%v, replica-read: %v, stale-read: %v}", expectedFirstStore, !staleRead && expectedFirstStore != 3, staleRead),
 						expectedSecondPath,
 					},
 					respErr:         "",