Skip to content

Commit 5fe12d2

Browse files
authored
Merge pull request #122 from joaomlneto/refactor-schedulers
cleanup scheduler logic
2 parents 7dbe09d + c10b988 commit 5fe12d2

File tree

9 files changed

+214
-269
lines changed

9 files changed

+214
-269
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ To build and run the benchmarking suite, run the following command:
9797
./gradlew bootRun
9898
```
9999

100+
### Configuring
101+
102+
To configure the simulator, you can modify the [
103+
`application.properties` file](simulator/src/main/resources/application.yml) in the `simulator` module.
104+
105+
It has two main subsections:
106+
107+
- `scheduler`: The scheduler configuration: which scheduler to use, and its parameters such as the probability of
108+
dropping messages.
109+
- `scenario`: The scenario configuration: which scenario to run, and its parameters such as conditions to stop the
110+
simulation.
111+
100112
## Web Interface
101113

102114
The web UI is a simple React application (using NextJS/TypeScript) that allows you to interact with the simulator. It is

simulator/src/main/java/byzzbench/simulator/config/ByzzBenchConfig.java

+28-3
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,40 @@ public static final class TerminationConfig {
7474
*/
7575
@Data
7676
public final class SchedulerConfig {
77+
/**
78+
* The ID of the scheduler to use.
79+
*/
7780
private String id;
7881
/**
79-
* Maximum number of messages to drop during scenario execution
82+
* Maximum number of messages to drop for a given scenario.
8083
*/
8184
private int maxDropMessages = 0;
8285
/**
83-
* Maximum number of faults to inect during scenario execution
86+
* Maximum number of messages to mutate-and-deliver for a given scenario.
87+
*/
88+
private int maxMutateMessages = 0;
89+
/**
90+
* Weighted probability of triggering a timeout
91+
*/
92+
private int deliverTimeoutWeight = 1;
93+
/**
94+
* Weighted probability of delivering a message
95+
*/
96+
private int deliverMessageWeight = 99;
97+
/**
98+
* Weighted probability of delivering a request from a client
99+
*/
100+
private int deliverClientRequestWeight = 99;
101+
/**
102+
* Weighted probability of dropping a message.
103+
* The default is 0 (no messages dropped as a scheduler decision).
104+
*/
105+
private int dropMessageWeight = 0;
106+
/**
107+
* Weighted probability of mutating and delivering a message.
108+
* The default is 0 (no messages are mutated as a scheduler decision).
84109
*/
85-
private int maxFaults;
110+
private int mutateMessageWeight = 0;
86111

87112
private Map<String, String> params;
88113

simulator/src/main/java/byzzbench/simulator/scheduler/BaseScheduler.java

+64-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package byzzbench.simulator.scheduler;
22

33
import byzzbench.simulator.Scenario;
4+
import byzzbench.simulator.config.ByzzBenchConfig;
45
import byzzbench.simulator.service.MessageMutatorService;
56
import byzzbench.simulator.transport.ClientRequestEvent;
67
import byzzbench.simulator.transport.Event;
@@ -12,34 +13,40 @@
1213
import lombok.Getter;
1314
import lombok.RequiredArgsConstructor;
1415

16+
import java.util.HashMap;
1517
import java.util.List;
18+
import java.util.Map;
1619

1720
/**
1821
* Abstract base class for a scheduler.
1922
*/
2023
@RequiredArgsConstructor
2124
public abstract class BaseScheduler implements Scheduler {
22-
@NonNull
25+
/**
26+
* The remaining number of drop messages for each scenario.
27+
* If the number of remaining drop messages is 0, the scheduler will not drop messages.
28+
*/
29+
protected final Map<Scenario, Integer> remainingDropMessages = new HashMap<>();
30+
/**
31+
* The remaining number of mutate messages for each scenario.
32+
* If the number of remaining mutate messages is 0, the scheduler will not mutate messages.
33+
*/
34+
protected final Map<Scenario, Integer> remainingMutateMessages = new HashMap<>();
35+
/**
36+
* ByzzBench configuration
37+
*/
2338
@Getter
24-
private final String id;
39+
private final ByzzBenchConfig config;
2540
@NonNull
2641
@Getter(AccessLevel.PROTECTED)
2742
private final transient MessageMutatorService messageMutatorService;
28-
@Getter
29-
protected boolean dropMessages = true;
30-
3143

3244
/**
3345
* Loads the parameters for the scheduler from a JSON object.
3446
*
3547
* @param parameters The JSON object containing the parameters for the scheduler.
3648
*/
3749
public final void loadParameters(JsonNode parameters) {
38-
// check if drop messages
39-
if (parameters != null && parameters.has("dropMessages")) {
40-
this.dropMessages = parameters.get("dropMessages").asBoolean();
41-
}
42-
4350
this.loadSchedulerParameters(parameters);
4451
}
4552

@@ -93,4 +100,51 @@ public List<ClientRequestEvent> getQueuedClientRequestEvents(Scenario scenario)
93100
* @param parameters The JSON object containing the parameters for the scheduler.
94101
*/
95102
protected abstract void loadSchedulerParameters(JsonNode parameters);
103+
104+
/**
105+
* Returns the weight of delivering a message
106+
*
107+
* @return The weight of delivering a message
108+
*/
109+
public int deliverMessageWeight() {
110+
return config.getScheduler().getDeliverMessageWeight();
111+
}
112+
113+
/**
114+
* Returns the weight of triggering a timeout
115+
*
116+
* @return The weight of triggering a timeout
117+
*/
118+
public int deliverTimeoutWeight() {
119+
return config.getScheduler().getDeliverTimeoutWeight();
120+
}
121+
122+
/**
123+
* Returns the weight of delivering a request from a client
124+
*
125+
* @return The weight of delivering a request from a client
126+
*/
127+
public int deliverClientRequestWeight() {
128+
return config.getScheduler().getDeliverClientRequestWeight();
129+
}
130+
131+
/**
132+
* Returns the weight of delivering a request from a client
133+
*
134+
* @return The weight of delivering a request from a client
135+
*/
136+
public int dropMessageWeight(Scenario scenario) {
137+
int remaining = remainingDropMessages.computeIfAbsent(scenario, s -> config.getScheduler().getMaxDropMessages());
138+
return remaining > 0 ? config.getScheduler().getDropMessageWeight() : 0;
139+
}
140+
141+
/**
142+
* Returns the weight of mutating and delivering a message
143+
*
144+
* @return The weight of mutating and delivering a message
145+
*/
146+
public int mutateMessageWeight(Scenario scenario) {
147+
int remaining = remainingMutateMessages.computeIfAbsent(scenario, s -> config.getScheduler().getMaxMutateMessages());
148+
return remaining > 0 ? config.getScheduler().getMutateMessageWeight() : 0;
149+
}
96150
}
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
package byzzbench.simulator.scheduler;
22

33
import byzzbench.simulator.Scenario;
4+
import byzzbench.simulator.config.ByzzBenchConfig;
45
import byzzbench.simulator.faults.Fault;
56
import byzzbench.simulator.faults.FaultContext;
67
import byzzbench.simulator.faults.FaultFactory;
78
import byzzbench.simulator.faults.factories.ByzzFuzzScenarioFaultFactory;
89
import byzzbench.simulator.service.MessageMutatorService;
9-
import byzzbench.simulator.transport.ClientRequestEvent;
10-
import byzzbench.simulator.transport.Event;
11-
import byzzbench.simulator.transport.MessageEvent;
12-
import byzzbench.simulator.transport.TimeoutEvent;
1310
import com.fasterxml.jackson.databind.JsonNode;
1411
import lombok.Getter;
1512
import lombok.extern.java.Log;
1613
import org.springframework.stereotype.Component;
1714

1815
import java.util.ArrayList;
1916
import java.util.List;
20-
import java.util.Optional;
2117
import java.util.Random;
2218

2319
/**
@@ -28,7 +24,7 @@
2824
@Component
2925
@Log
3026
@Getter
31-
public class ByzzFuzzScheduler extends BaseScheduler {
27+
public class ByzzFuzzScheduler extends RandomScheduler {
3228
/**
3329
* Small-scope mutations to be applied to protocol messages
3430
*/
@@ -40,22 +36,30 @@ public class ByzzFuzzScheduler extends BaseScheduler {
4036
/**
4137
* Number of protocol rounds with process faults
4238
*/
39+
@Getter
4340
private int numRoundsWithProcessFaults = 1;
4441
/**
4542
* Number of protocol rounds with network faults
4643
*/
44+
@Getter
4745
private int numRoundsWithNetworkFaults = 1;
4846
/**
4947
* Number of protocol rounds among which the faults will be injected
5048
*/
49+
@Getter
5150
private int numRoundsWithFaults = 3;
5251

53-
public ByzzFuzzScheduler(MessageMutatorService messageMutatorService) {
54-
super("ByzzFuzz", messageMutatorService);
52+
public ByzzFuzzScheduler(ByzzBenchConfig config, MessageMutatorService messageMutatorService) {
53+
super(config, messageMutatorService);
5554
}
5655

5756
@Override
58-
protected void loadSchedulerParameters(JsonNode parameters) {
57+
public String getId() {
58+
return "ByzzFuzz";
59+
}
60+
61+
@Override
62+
public void loadSchedulerParameters(JsonNode parameters) {
5963
System.out.println("Loading ByzzFuzz parameters:");
6064

6165
if (parameters != null)
@@ -76,100 +80,29 @@ protected void loadSchedulerParameters(JsonNode parameters) {
7680

7781
@Override
7882
public void initializeScenario(Scenario scenario) {
83+
super.initializeScenario(scenario);
84+
85+
// Generate round-aware small-scope mutations for the scenario
7986
FaultFactory faultFactory = new ByzzFuzzScenarioFaultFactory();
8087
FaultContext context = new FaultContext(scenario);
8188
List<Fault> faults = faultFactory.generateFaults(context);
8289
faults.forEach(fault -> scenario.getTransport().addFault(fault, true));
8390
}
8491

8592
@Override
86-
public synchronized Optional<EventDecision> scheduleNext(Scenario scenario) throws Exception {
87-
// Get a random event
88-
List<Event> queuedEvents = scenario.getTransport().getEventsInState(Event.Status.QUEUED);
89-
90-
// if there are no events, return empty
91-
if (queuedEvents.isEmpty()) {
92-
System.out.println("No events!");
93-
return Optional.empty();
94-
}
95-
96-
int eventCount = queuedEvents.size();
97-
int timeoutEventCount = (int) queuedEvents.stream().filter(TimeoutEvent.class::isInstance).count();
98-
int clientRequestEventCount = (int) queuedEvents.stream().filter(ClientRequestEvent.class::isInstance).count();
99-
int messageEventCount = eventCount - (timeoutEventCount + clientRequestEventCount);
100-
101-
double timeoutEventProb = (double) timeoutEventCount / eventCount;
102-
double clientRequestEventProb = (double) clientRequestEventCount / eventCount;
103-
double messageEventProb = (double) messageEventCount / eventCount;
104-
105-
assert timeoutEventProb + clientRequestEventProb + messageEventProb == 1.0;
106-
107-
double dieRoll = random.nextDouble();
108-
109-
// check if we should schedule a timeout
110-
if (dieRoll < timeoutEventProb) {
111-
// select a random event of type timeout
112-
List<Event> queuedTimeouts = queuedEvents.stream()
113-
.filter(TimeoutEvent.class::isInstance)
114-
.toList();
115-
116-
if (queuedTimeouts.isEmpty()) {
117-
return Optional.empty();
118-
}
119-
120-
Event timeout = queuedTimeouts.get(random.nextInt(queuedTimeouts.size()));
121-
scenario.getTransport().deliverEvent(timeout.getEventId());
122-
123-
EventDecision decision = new EventDecision(EventDecision.DecisionType.DELIVERED, timeout.getEventId());
124-
return Optional.of(decision);
125-
}
126-
127-
// check if we should target a message
128-
if (dieRoll < timeoutEventProb + messageEventProb) {
129-
// select a random event of type message
130-
List<Event> queuedMessages = queuedEvents.stream()
131-
.filter(MessageEvent.class::isInstance)
132-
.toList();
133-
134-
// if there are no messages, return an empty action
135-
if (queuedMessages.isEmpty()) {
136-
return Optional.empty();
137-
}
138-
139-
Event message = queuedMessages.get(random.nextInt(queuedMessages.size()));
140-
141-
// deliver the message, without changes
142-
scenario.getTransport().deliverEvent(message.getEventId());
143-
144-
EventDecision decision = new EventDecision(EventDecision.DecisionType.DELIVERED, message.getEventId());
145-
return Optional.of(decision);
146-
}
147-
148-
if (dieRoll < timeoutEventProb + messageEventProb + clientRequestEventProb) {
149-
List<Event> queuedClientRequests = queuedEvents.stream().filter(ClientRequestEvent.class::isInstance).toList();
150-
151-
if (queuedClientRequests.isEmpty()) {
152-
return Optional.empty();
153-
}
154-
155-
Event request = queuedClientRequests.get(random.nextInt(clientRequestEventCount));
156-
157-
scenario.getTransport().deliverEvent(request.getEventId());
158-
159-
EventDecision decision = new EventDecision(EventDecision.DecisionType.DELIVERED, request.getEventId());
160-
return Optional.of(decision);
161-
}
162-
163-
return Optional.empty();
93+
public int dropMessageWeight(Scenario scenario) {
94+
// ByzzFuzz does not drop messages as a scheduler decision
95+
return 0;
16496
}
16597

16698
@Override
167-
public void reset() {
168-
// nothing to do
99+
public int mutateMessageWeight(Scenario scenario) {
100+
// ByzzFuzz does not mutate messages as a scheduler decision
101+
return 0;
169102
}
170103

171104
@Override
172-
public void stopDropMessages() {
173-
// FIXME: refactor
105+
public void reset() {
106+
// nothing to do
174107
}
175108
}

simulator/src/main/java/byzzbench/simulator/scheduler/EventDecision.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55

66
@Data
77
public class EventDecision {
8-
@NonNull private final DecisionType decision;
9-
@NonNull private final long eventId;
8+
@NonNull
9+
private final DecisionType decision;
10+
@NonNull
11+
private final long eventId;
1012

1113
public enum DecisionType {
1214
DELIVERED,
1315
DROPPED,
14-
MUTATED
16+
MUTATED_AND_DELIVERED
1517
}
1618
}

0 commit comments

Comments
 (0)