Skip to content

Commit 9805e4c

Browse files
committed
asim: account for follower replica load
Previously, asim only accounted for load on the leaseholder, ignoring non-leaseholder replicas. This commit updates it to consider all replicas for RangeUsageInfo and store capacity aggregation. RangeUsageInfo handles leaseholder checks and clears request CPU and QPS stats for non-leaseholder replicas. Epic: none Release note: none
1 parent fa46204 commit 9805e4c

File tree

6 files changed

+161
-133
lines changed

6 files changed

+161
-133
lines changed

pkg/kv/kvserver/asim/state/impl.go

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,18 @@ func (s *state) capacity(storeID StoreID) roachpb.StoreCapacity {
279279

280280
for _, repl := range s.Replicas(storeID) {
281281
rangeID := repl.Range()
282-
replicaID := repl.ReplicaID()
283282
rng, _ := s.Range(rangeID)
284-
if rng.Leaseholder() == replicaID {
285-
// TODO(kvoli): We currently only consider load on the leaseholder
286-
// replica for a range. The other replicas have an estimate that is
287-
// calculated within the allocation algorithm. Adapt this to
288-
// support follower reads, when added to the workload generator.
289-
usage := s.RangeUsageInfo(rng.RangeID(), storeID)
290-
capacity.QueriesPerSecond += usage.QueriesPerSecond
291-
capacity.WritesPerSecond += usage.WritesPerSecond
292-
capacity.LogicalBytes += usage.LogicalBytes
293-
capacity.CPUPerSecond += usage.RequestCPUNanosPerSecond + usage.RaftCPUNanosPerSecond
283+
usage := s.RangeUsageInfo(rng.RangeID(), storeID)
284+
// RangeUsageInfo should return valid usage depending on leaseholder. No
285+
// special handling needed here.
286+
capacity.QueriesPerSecond += usage.QueriesPerSecond
287+
capacity.WritesPerSecond += usage.WritesPerSecond
288+
capacity.LogicalBytes += usage.LogicalBytes
289+
capacity.CPUPerSecond += usage.RequestCPUNanosPerSecond + usage.RaftCPUNanosPerSecond
290+
capacity.RangeCount++
291+
if leaseholder, _ := s.LeaseholderStore(rangeID); leaseholder.StoreID() == storeID {
294292
capacity.LeaseCount++
295293
}
296-
capacity.RangeCount++
297294
}
298295

299296
// TODO(kvoli): parameterize the logical to actual used storage bytes. At the
@@ -1025,30 +1022,36 @@ func (s *state) applyLoad(rng *rng, le workload.LoadEvent) {
10251022
s.loadsplits[store.StoreID()].Record(s.clock.Now(), rng.rangeID, le)
10261023
}
10271024

1028-
// ReplicaLoad returns the usage information for the Range with ID
1029-
// RangeID on the store with ID StoreID.
1025+
// RangeUsageInfo returns the usage information for the Range with ID RangeID on
1026+
// the store with ID StoreID. If the given store has a replica for the range,
1027+
// this returns the write-bytes-per-second, raft cpu, written keys and logical
1028+
// bytes. If the given store is the leaseholder for the range, then the request
1029+
// cpu and qps is also returned. If the given store does not have a replica for
1030+
// the range, this function panics.
10301031
func (s *state) RangeUsageInfo(rangeID RangeID, storeID StoreID) allocator.RangeUsageInfo {
1031-
// NB: we only return the actual replica load, if the range leaseholder is
1032-
// currently on the store given. Otherwise, return an empty, zero counter
1033-
// value.
1034-
// TODO(wenyihu6): Track non-leaseholder replica load as well.
1035-
store, ok := s.LeaseholderStore(rangeID)
1032+
r, ok := s.Range(rangeID)
10361033
if !ok {
10371034
panic(fmt.Sprintf("no leaseholder store found for range %d", storeID))
10381035
}
10391036

1040-
r, _ := s.Range(rangeID)
1041-
// TODO(kvoli): The requested storeID is not the leaseholder. Non
1042-
// leaseholder load tracking is not currently supported but is checked by
1043-
// other components such as hot ranges. In this case, ignore it but we
1044-
// should also track non leaseholder load. See load.go for more. Return an
1045-
// empty initialized load counter here.
1046-
if store.StoreID() != storeID {
1047-
return allocator.RangeUsageInfo{LogicalBytes: r.Size()}
1037+
if _, ok = r.Replica(storeID); !ok {
1038+
panic(fmt.Sprintf("no replica found for range %v on store %v [replicas=%v]",
1039+
rangeID, storeID, r.Replicas()))
10481040
}
10491041

10501042
usage := s.load[rangeID].Load()
10511043
usage.LogicalBytes = r.Size()
1044+
leaseholderStore, ok := s.LeaseholderStore(rangeID)
1045+
if !ok {
1046+
panic(fmt.Sprintf("no leaseholder store found for range %v", rangeID))
1047+
}
1048+
1049+
if leaseholderStore.StoreID() != storeID {
1050+
// See the method comment, we don't include the request cpu or qps for
1051+
// non-leaseholder replicas.
1052+
usage.RequestCPUNanosPerSecond = 0
1053+
usage.QueriesPerSecond = 0
1054+
}
10521055
return usage
10531056
}
10541057

pkg/kv/kvserver/asim/state/state_test.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,23 +287,30 @@ func TestWorkloadApply(t *testing.T) {
287287
require.Equal(t, expectedLoad, sc3)
288288
}
289289

290-
// TestReplicaLoadQPS asserts that the rated replica load accounting maintains
291-
// the average per second corresponding to the tick clock.
292-
func TestReplicaLoadQPS(t *testing.T) {
290+
// TestReplicaLoadRangeUsageInfo asserts that the rated replica load accounting
291+
// maintains the average per second corresponding to the tick clock.
292+
func TestReplicaLoadRangeUsageInfo(t *testing.T) {
293293
settings := config.DefaultSimulationSettings()
294294
s := NewState(settings)
295295
start := settings.StartTime
296296

297297
n1 := s.AddNode()
298+
n2 := s.AddNode()
299+
n3 := s.AddNode()
298300
k1 := Key(100)
299301
qps := 1000
300302
s1, _ := s.AddStore(n1.NodeID())
303+
s2, _ := s.AddStore(n2.NodeID())
304+
s3, _ := s.AddStore(n3.NodeID())
301305
_, r1, _ := s.SplitRange(k1)
302306
s.AddReplica(r1.RangeID(), s1.StoreID(), roachpb.VOTER_FULL)
307+
s.AddReplica(r1.RangeID(), s2.StoreID(), roachpb.VOTER_FULL)
308+
s.AddReplica(r1.RangeID(), s3.StoreID(), roachpb.VOTER_FULL)
303309

304310
applyLoadToStats := func(key int64, count int) {
305311
for i := 0; i < count; i++ {
306-
s.ApplyLoad(workload.LoadBatch{workload.LoadEvent{Key: key, Writes: 1}})
312+
s.ApplyLoad(workload.LoadBatch{workload.LoadEvent{
313+
Key: key, Writes: 1, WriteSize: 1, RequestCPU: 1, RaftCPU: 1}})
307314
}
308315
}
309316

@@ -317,6 +324,24 @@ func TestReplicaLoadQPS(t *testing.T) {
317324
// Assert that the rated avg comes out to rate of queries applied per
318325
// second.
319326
require.Equal(t, float64(qps), s.RangeUsageInfo(r1.RangeID(), s1.StoreID()).QueriesPerSecond)
327+
// The non-leaseholder replicas should have no QPS.
328+
require.Equal(t, float64(0), s.RangeUsageInfo(r1.RangeID(), s2.StoreID()).QueriesPerSecond)
329+
require.Equal(t, float64(0), s.RangeUsageInfo(r1.RangeID(), s2.StoreID()).QueriesPerSecond)
330+
// Similarly, only the leaseholder should have request CPU recorded.
331+
require.Equal(t, float64(qps),
332+
s.RangeUsageInfo(r1.RangeID(), s1.StoreID()).RequestCPUNanosPerSecond)
333+
require.Equal(t, float64(0),
334+
s.RangeUsageInfo(r1.RangeID(), s2.StoreID()).RequestCPUNanosPerSecond)
335+
require.Equal(t, float64(0),
336+
s.RangeUsageInfo(r1.RangeID(), s3.StoreID()).RequestCPUNanosPerSecond)
337+
// All the replicas should have identical write bytes/s, which is equal to
338+
// the QPS. The replicas should also have identical raft CPU usage.
339+
require.Equal(t, float64(qps),
340+
s.RangeUsageInfo(r1.RangeID(), s1.StoreID()).RaftCPUNanosPerSecond)
341+
require.Equal(t, float64(qps),
342+
s.RangeUsageInfo(r1.RangeID(), s2.StoreID()).RaftCPUNanosPerSecond)
343+
require.Equal(t, float64(qps),
344+
s.RangeUsageInfo(r1.RangeID(), s3.StoreID()).RaftCPUNanosPerSecond)
320345
}
321346

322347
// TestKeyTranslation asserts that key encoding between roachpb keys and

pkg/kv/kvserver/asim/tests/testdata/non_rand/example_fulldisk

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,46 +19,46 @@ OK
1919
# store 5. This is shown below as it sheds replicas.
2020
plot stat=replicas
2121
----
22-
342╮╭─╮╭╭╭─╮╭
23-
331 ╭╮ ╭╭─╮╮╭╮╭╭╮╭╭───╮─╭──╮╮ ╭╮──╭╭───╮─╮╭──╮╯╭╯╭╰╮╭╯│╰
24-
319 ╭╭╮╭╮─╭─╭──╯╭│╭╭─────╯╯╰─│╭╯╮╭│╰─╭╮╭───╯╭─╯│╭─╯╭─╰─╯╰╯─╰╯╭╰╰
25-
308 ┤ ╭───╭──────╯╯╰╰╯╰─╯╰╯ ╰╯ ╰╯ ╰╯╰──╯╰╯ ╰╰╯╯ ╰╯ ╰╯ ╰
26-
296 ┼───────────────╮───╯╯╯╰╯
27-
285 ╰──╮
28-
274 │╭─
29-
262 ╰╯ │╭
30-
251 ╰╯╰╮ ╭
31-
239 │╭╯│ ╭─
32-
228 ╰╯ ╰╮╭╮ ╭╮ ╭─╮ │ │ ╭╮ ╭
33-
217 ╰╯╰───╯│╭╯ │ ╭╮ ╭╮╭╯ ╰─╯╰╮╭╯│ ╭╮
34-
205 ╰╯ ╰╮╭─╯╰─╯╰╯ ╰╯ ╰╮ ╭╮ ││
35-
194 ││ ╰─╯╰─╯╰╮╭╮ ╭╮ ╭╮ ╭╮
36-
182 ╰╯╰╮│╰╮╭╯│ │╰
37-
171 ╰╯ ╰╯ ╰─
22+
364╭╭╮╭─╮╮╭╭╮─╭───╮╭─╮╮╭─╭╮───╭╮─╭╭─╮─╭────╮╭╭───────╮──╭─╮─╭──────────────
23+
344╭───╯╭─────╯╰─╯───╰──────╯╰───╯╰──╯╯╰─╯╰╰──╰─╯╯╰────╯╰──╯╯╰─╯╯ ╰───╯ ╰───
24+
324╭─╯╭───╯
25+
304 ┼─╮───
26+
283 ┤ ╰╮
27+
263
28+
243
29+
223
30+
203
31+
183
32+
163
33+
143
34+
122╰╮
35+
102╰────────╮╭──╮╭─╮╭──╮ ╭──╮ ╭──╮ ╭╮
36+
82 ╰╯ ╰╯ ╰╯ ╰─╯ ╰─╰─╯╰──────────────╮╭───╮╭───╮╭────╮ ╭──╮
37+
62 ╰╯ ╰╯ ╰╯ ╰─╯ ╰─
3838
replicas
3939
initial store values: [s1=300, s2=300, s3=300, s4=300, s5=300] (stddev=0.00, mean=300.00, sum=1500)
40-
last store values: [s1=342, s2=341, s3=326, s4=328, s5=178] (stddev=62.84, mean=303.00, sum=1515)
40+
last store values: [s1=363, s2=352, s3=360, s4=362, s5=66] (stddev=117.36, mean=300.60, sum=1503)
4141

4242
# Plot the % of disk storage capacity used. We should see s5 hovering right
4343
# around 92.5-95% (the storage capacity threshold value).
4444
plot stat=disk_fraction_used
4545
----
46-
1.10 ┤ ╭╮ ╭╮ ╭╮ ╭
47-
1.04 ╭╮ ╭╮ │╰╮ ╭╮ ││ ╭╮ ╭╮ ││ ╭╮ ╭╮ ╭─╮ │╰
48-
0.97 ╭───╮╭─╮ ╭╮│╰╮╭╯╰╮╭╮╭╮ ╭╮ │ ╰╮╭╮││ ╭╮ │╰╮╭╯╰╮╭╯╰╮ ╭╮ │╰╮││ │╰╮│ │╭╯
49-
0.91 ╭──────╯ ╰╯ ╰─╯╰╯ ╰╯ ╰╯╰╯╰─╯│╭╯ ││╰╯╰─╯│╭╯ ╰╯ ╰╯ ╰─╯╰─╯ ╰╯╰─╯ ╰╯ ╰╯
50-
0.84 ┼────╯ ╰╯ ╰╯ ╰╯
51-
0.78 ┤
52-
0.71 ┤
53-
0.65 ┤
54-
0.59 ┤
55-
0.52 ┤
56-
0.46 ┤
57-
0.39 ┤
58-
0.33 ┤
59-
0.26 ╭╮ ╭╮╮╭─╭╭────╮╭───────────────
60-
0.20 ╭╮╭╭────────────────────────────╯╰──╯╰────╯─╯╰─╰╯──
61-
0.14 ┼──────────────────╯ ╰╯╰
46+
2.51 ┼─
47+
2.37╰╮
48+
2.23
49+
2.09╰╮
50+
1.95 ┤ ╰╮
51+
1.81 ┤ │
52+
1.67 ┤ ╰╮
53+
1.53 ┤ ╰╮
54+
1.39 ┤ ╰╮
55+
1.25 ┤ │ ╭╮ ╭╮ ╭─╮
56+
1.11 ┤ ╰╮ ╭╮ ╭╮ ╭──╮ │╰╮ ╭╮ ╭╮ ╭─╮ ╭──╮ ╭╮ │╰─╮ │ ╰╮ ╭─╮
57+
0.97 ┤ ╰╮╭───╮╭─╮╭──╮│╰╮╭╯╰─╮│ ╰╮│ ╰─╮│╰──╯╰╮│ ╰─╯ ╰─╮╭╯╰─╮│ ╰╮╭╯ ╰─╮│ ╰──
58+
0.83 ┤ ╰╯ ╰╯ ╰╯ ╰╯ ╰╯ ╰╯ ╰╯ ╰╯ ╰╯ ╰╯╭─╭╰╯───╰╯─────╰╯────
59+
0.69╭╭─╭─╮╭───────────────────────╯╯╰─╯ ╰╯
60+
0.55╭──╭────────────────────────╯ ╰
61+
0.41 ┼────────╯
6262
disk_fraction_used
63-
initial store values: [s1=0.14, s2=0.14, s3=0.14, s4=0.14, s5=0.83] (stddev=0.28, mean=0.28, sum=1)
64-
last store values: [s1=0.28, s2=0.26, s3=0.25, s4=0.25, s5=1.00] (stddev=0.30, mean=0.41, sum=2)
63+
initial store values: [s1=0.41, s2=0.41, s3=0.41, s4=0.41, s5=2.50] (stddev=0.84, mean=0.83, sum=4)
64+
last store values: [s1=0.87, s2=0.85, s3=0.87, s4=0.87, s5=0.97] (stddev=0.04, mean=0.89, sum=4)

pkg/kv/kvserver/asim/tests/testdata/non_rand/example_multi_store

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ OK
2323

2424
plot stat=leases
2525
----
26-
14.00 ┼
27-
13.07 ┤
28-
12.13 ┤ ╰╮
29-
11.20 ┤
30-
10.27 ┤ ╰╮
31-
9.33 ┤
26+
14.00 ┼╮
27+
13.07 ┤╰╮
28+
12.13 ┤
29+
11.20 ┤ │
30+
10.27 ┤ ╰╮
31+
9.33 ┤ ╰╮
3232
8.40 ┤ │
33-
7.47 ┤
34-
6.53 ┤
35-
5.60 ┤ ╰╮
36-
4.67 ┤
37-
3.73 ┤ ╰╮
38-
2.80 ┤ ╭────────────╭───────────────╮
39-
1.87 ┤ ╭────────────╮──────────────╮│───────────────╮
40-
0.93 ┤ ╭─╯────────────╭──────────────────────────────────────────────────────────────
41-
0.00 ┼────────────────╯───────────────────────────────
33+
7.47 ┤ ╰╮
34+
6.53 ┤ ╰╮
35+
5.60 ┤
36+
4.67 ┤ ╰──────────╮
37+
3.73 ┤
38+
2.80 ┤
39+
1.87 ┤ ╭─────────────────────────╮
40+
0.93 ┤╭╭──────────────╭──────────────────────────────────────────────────────────────
41+
0.00 ┼────────────────╯───────────────╯
4242
leases
4343
initial store values: [s1=14, s2=0, s3=0, s4=0, s5=0, s6=0, s7=0, s8=0, s9=0, s10=0, s11=0, s12=0, s13=0, s14=0] (stddev=3.61, mean=1.00, sum=14)
4444
last store values: [s1=1, s2=1, s3=1, s4=1, s5=1, s6=1, s7=1, s8=1, s9=1, s10=1, s11=1, s12=1, s13=1, s14=1] (stddev=0.00, mean=1.00, sum=14)

0 commit comments

Comments
 (0)