forked from vmware/concord-bft
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_skvbc_fast_path.py
144 lines (109 loc) · 5.43 KB
/
test_skvbc_fast_path.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Concord
#
# Copyright (c) 2019 VMware, Inc. All Rights Reserved.
#
# This product is licensed to you under the Apache 2.0 license (the "License").
# You may not use this product except in compliance with the Apache 2.0 License.
#
# This product may include a number of subcomponents with separate copyright
# notices and license terms. Your use of these subcomponents is subject to the
# terms and conditions of the subcomponent's license, as noted in the LICENSE
# file.
import os.path
import random
import unittest
from util.bft import with_trio, with_bft_network, KEY_FILE_PREFIX
from util.skvbc_history_tracker import verify_linearizability
def start_replica_cmd(builddir, replica_id):
"""
Return a command that starts an skvbc replica when passed to
subprocess.Popen.
Note each arguments is an element in a list.
"""
statusTimerMilli = "500"
path = os.path.join(builddir, "tests", "simpleKVBC", "TesterReplica", "skvbc_replica")
return [path,
"-k", KEY_FILE_PREFIX,
"-i", str(replica_id),
"-s", statusTimerMilli,
"-p" if os.environ.get('BUILD_ROCKSDB_STORAGE', "").lower()
in set(["true", "on"])
else "",
"-t", os.environ.get('STORAGE_TYPE')]
class SkvbcFastPathTest(unittest.TestCase):
__test__ = False # so that PyTest ignores this test scenario
def setUp(self):
# Whenever a replica goes down, all messages initially go via the slow path.
# However, when an "evaluation period" elapses (set at 64 sequence numbers),
# the system should return to the fast path.
self.evaluation_period_seq_num = 64
@with_trio
@with_bft_network(start_replica_cmd)
@verify_linearizability
async def test_fast_path_only(self, bft_network, tracker):
"""
This test aims to check that the fast commit path is prevalent
in the normal, synchronous case (no failed replicas, no network partitioning).
First we write a series of K/V entries and tracked them using the tracker from the decorator.
Then we check that, in the process, we have stayed on the fast path.
Finally the decorator verifies the KV execution.
"""
bft_network.start_all_replicas()
write_weight = .50
numops = 100
nb_slow_path = await bft_network.num_of_slow_path()
await tracker.run_concurrent_ops(num_ops=numops, write_weight=write_weight)
await bft_network.wait_for_fast_path_to_be_prevalent(nb_slow_paths_so_far=nb_slow_path)
@with_trio
@with_bft_network(start_replica_cmd)
@verify_linearizability
async def test_fast_to_slow_path_transition(self, bft_network, tracker):
"""
This test aims to check the correct transition from fast to slow commit path.
First we write a series of K/V entries and track them using the tracker from the decorator, making sure
we stay on the fast path.
Once the first series of K/V writes have been processed, we bring down
one of the replicas, which should trigger a transition to the slow path.
We send a new series of K/V writes and make sure they
have been processed using the slow commit path.
Finally the decorator verifies the KV execution.
"""
bft_network.start_all_replicas()
write_weight = 0.5
numops = 100
_, fast_path_writes = await tracker.run_concurrent_ops(
num_ops=numops, write_weight=write_weight)
await bft_network.wait_for_fast_path_to_be_prevalent()
unstable_replicas = bft_network.all_replicas(without={0})
bft_network.stop_replica(
replica_id=random.choice(unstable_replicas))
await tracker.run_concurrent_ops(num_ops=numops, write_weight=write_weight)
await bft_network.wait_for_slow_path_to_be_prevalent(as_of_seq_num=fast_path_writes+1)
@with_trio
@with_bft_network(start_replica_cmd,
num_clients=4,
selected_configs=lambda n, f, c: c >= 1)
@verify_linearizability
async def test_fast_path_resilience_to_crashes(self, bft_network, tracker):
"""
In this test we check the fast path's resilience when up to "c" nodes fail.
As a first step, we bring down no more than c replicas,
triggering initially the slow path.
Then we write a series of K/V entries and track them using the tracker from the decorator, making sure
the fast path is eventually restored and becomes prevalent.
Finally the decorator verifies the KV execution.
"""
bft_network.start_all_replicas()
unstable_replicas = bft_network.all_replicas(without={0})
for _ in range(bft_network.config.c):
replica_to_stop = random.choice(unstable_replicas)
bft_network.stop_replica(replica_to_stop)
write_weight = 0.5
# make sure we first downgrade to the slow path...
_, slow_path_writes = await tracker.run_concurrent_ops(
num_ops=self.evaluation_period_seq_num-1, write_weight=1)
await bft_network.wait_for_slow_path_to_be_prevalent()
# ...but eventually (after the evaluation period), the fast path is restored!
await tracker.run_concurrent_ops(num_ops=self.evaluation_period_seq_num*2/write_weight, write_weight=write_weight)
await bft_network.wait_for_fast_path_to_be_prevalent(
nb_slow_paths_so_far=slow_path_writes)