Skip to content

Commit 34da2e9

Browse files
authored
Merge pull request #9 from BinaryIgor/benchmark-improvements
Benchmark improvements
2 parents a96ef54 + 41b821d commit 34da2e9

6 files changed

Lines changed: 45 additions & 40 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
Events over SQL.
44

5-
Simple, Reliable, Fast.
5+
Simple. Reliable. Fast.
66

77
Able to publish and consume thousands of events per second on a single Postgres instance.
88

9-
With sharding, it can easily support tens of thousands events per second for virtually endless scalability.
9+
With sharding, it can easily support tens and hundreds of thousands events per second for virtually endless scalability.
1010

11-
For scalability details, see [benchmarks](/benchmarks/README.md).
11+
For proofs, see [benchmarks](/benchmarks/README.md).
1212

1313
## How it works
1414

TODO.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
* compact topics - unique key
55
* join, aka streams
66
* more elaborate definitions change support - especially around partitions growth & shrinkage
7-
* JavaDocs
7+
* JavaDocs
8+
* sharding - routing topics and consumers to a subset of shards based on some tag/pattern/function (public vs private topics use-case)

benchmarks/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Some background and details:
1717
Defined in the `prepare_infra.py` script; sometimes resources are limited by `docker run` command, but essentially:
1818
* *benchmarks-app (consumer)* runs on 2 GB and 2 CPUs (AMD) machine
1919
* each *events-db* runs on 8 GB and 4 CPUs (AMD) machine
20-
* each *benchmarks-runner* runs alongside *events-db*, but is throttled to 2 GB memory and 2 CPUs
20+
* each *benchmarks-runner* runs alongside *events-db*
2121
* there is a basic firewall and virtual private network (vpc) setup (`prepare_infra.py`), so that nobody is bothering us during benchmarks
2222

2323
## Requirements
@@ -52,6 +52,14 @@ We right now have four machines connected to each other by the vpc.
5252
To each we have access, using ssh public key authentication, as the `eventsql` user.
5353
Infrastructure is now ready, let's prepare apps.
5454

55+
### EventSQL
56+
57+
Before building apps, make sure that EventSQL is available on your machine, since it's their dependency. Just run:
58+
```
59+
mvn clean install
60+
```
61+
From the root repo directory.
62+
5563
### Apps
5664

5765
Let's build `events-db` (from scripts dir again):

benchmarks/app/src/main/java/com/binaryigor/eventsql/benchmarks/EventsConsumer.java

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import java.time.Duration;
1414
import java.util.*;
15-
import java.util.concurrent.atomic.AtomicBoolean;
1615
import java.util.stream.Collectors;
1716

1817
@Component
@@ -23,7 +22,6 @@ public class EventsConsumer {
2322
private final EventSQL eventSQL;
2423
private final ObjectMapper objectMapper;
2524
private final EventsProperties eventsProperties;
26-
private final AtomicBoolean accountCreatedBatchHandler = new AtomicBoolean(true);
2725
private final Map<String, Counter> accountHandledCountersByConsumer;
2826

2927
public EventsConsumer(EventSQL eventSQL,
@@ -41,10 +39,6 @@ public EventsConsumer(EventSQL eventSQL,
4139
.register(meterRegistry)));
4240
}
4341

44-
public void batchAccountCreatedHandler(boolean batch) {
45-
accountCreatedBatchHandler.set(batch);
46-
}
47-
4842
@PostConstruct
4943
void start() {
5044
eventSQL.registry()
@@ -56,13 +50,7 @@ void start() {
5650
eventSQL.registry().registerConsumer(c);
5751
eventSQL.consumers()
5852
.startBatchConsumer(c.topic(), c.name(),
59-
events -> {
60-
if (accountCreatedBatchHandler.get()) {
61-
handleAccountCreatedEvents(c.name(), events);
62-
} else {
63-
events.forEach(e -> handleAccountCreatedEvent(c.name(), e));
64-
}
65-
},
53+
events -> handleAccountCreatedEvents(c.name(), events),
6654
EventSQLConsumers.ConsumptionConfig.of(10, 100));
6755
});
6856
}
@@ -72,20 +60,6 @@ void stop() {
7260
eventSQL.consumers().stop(Duration.ofSeconds(5));
7361
}
7462

75-
private void handleAccountCreatedEvent(String consumer, Event event) {
76-
try {
77-
var accountCreated = objectMapper.readValue(event.value(), AccountCreated.class);
78-
handleDelay();
79-
accountHandledCountersByConsumer.get(consumer)
80-
.increment();
81-
} catch (IllegalStateException e) {
82-
throw new EventSQLConsumptionException(e, event);
83-
} catch (Exception e) {
84-
logger.error("Problem while handling AccountCreated event:", e);
85-
throw new RuntimeException(e);
86-
}
87-
}
88-
8963
private void handleAccountCreatedEvents(String consumer, List<Event> events) {
9064
try {
9165
var accountsCreated = new ArrayList<AccountCreated>(events.size());

benchmarks/runner/src/main/java/com/binaryigor/eventsql/benchmarks/EventSQLBenchmarksRunner.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
import java.time.Duration;
1313
import java.time.Instant;
1414
import java.time.LocalDateTime;
15+
import java.time.temporal.ChronoUnit;
1516
import java.util.*;
1617
import java.util.concurrent.Executors;
1718
import java.util.concurrent.Future;
19+
import java.util.stream.Stream;
1820

1921
public class EventSQLBenchmarksRunner {
2022

@@ -26,6 +28,7 @@ public class EventSQLBenchmarksRunner {
2628
static final EventSQLDialect SQL_DIALECT;
2729
static final int RUNNER_INSTANCES = envIntValueOrDefault("RUNNER_INSTANCES", 1);
2830
static final int EVENTS_TO_PUBLISH = envIntValueOrDefault("EVENTS_TO_PUBLISH", 60_000);
31+
static final int EVENTS_BATCH_SIZE = envIntValueOrDefault("EVENTS_BATCH_SIZE", 1);
2932
static final int EVENTS_RATE = envIntValueOrDefault("EVENTS_RATE", 1_000);
3033
static final String TEST_TOPIC = envValueOrDefault("TEST_TOPIC", "account_created");
3134
static final String TEST_CONSUMER = envValueOrDefault("TEST_CONSUMER", "benchmarks-consumer");
@@ -182,15 +185,27 @@ static <T> T executeQuery(DataSource source, String query, ResultSetMapper<T> re
182185
static void publishEvents(EventSQLPublisher publisher) throws Exception {
183186
var futures = new LinkedList<Future<?>>();
184187

188+
String eventsWerePublishedMessage;
189+
if (EVENTS_BATCH_SIZE > 1) {
190+
eventsWerePublishedMessage = "events were published - in batches of " + EVENTS_BATCH_SIZE;
191+
} else {
192+
eventsWerePublishedMessage = "events were published";
193+
}
194+
195+
var publishRate = EVENTS_RATE / EVENTS_BATCH_SIZE;
196+
var batchesToPublish = EVENTS_TO_PUBLISH / EVENTS_BATCH_SIZE;
185197
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
186-
for (var i = 0; i < EVENTS_TO_PUBLISH; i++) {
187-
var result = executor.submit(() -> publishRandomEvent(publisher));
198+
for (var i = 0; i < batchesToPublish; i++) {
199+
var result = executor.submit(() -> publishRandomEventOrEventsBatch(publisher));
188200
futures.add(result);
189201

190202
var publications = i + 1;
191-
if (futures.size() >= EVENTS_RATE && publications < EVENTS_TO_PUBLISH) {
192-
System.out.printf("%s, %d/%d events were published, waiting 1s before next publications...%n",
193-
LocalDateTime.now(), publications, EVENTS_TO_PUBLISH);
203+
if (futures.size() >= publishRate && publications < batchesToPublish) {
204+
System.out.printf("%s, %d/%d %s, waiting 1s before next publications...%n",
205+
LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS),
206+
publications * EVENTS_BATCH_SIZE,
207+
EVENTS_TO_PUBLISH,
208+
eventsWerePublishedMessage);
194209
Thread.sleep(1000);
195210
awaitForFutures(futures);
196211
futures.clear();
@@ -204,11 +219,18 @@ static void publishEvents(EventSQLPublisher publisher) throws Exception {
204219
}
205220
}
206221

207-
static void publishRandomEvent(EventSQLPublisher publisher) {
222+
static void publishRandomEventOrEventsBatch(EventSQLPublisher publisher) {
208223
try {
209224
// make publication more evenly distributed in time
210225
Thread.sleep(RANDOM.nextInt(1000));
211-
publisher.publish(nextEvent());
226+
if (EVENTS_BATCH_SIZE > 1) {
227+
var batch = Stream.generate(EventSQLBenchmarksRunner::nextEvent)
228+
.limit(EVENTS_BATCH_SIZE)
229+
.toList();
230+
publisher.publishAll(batch);
231+
} else {
232+
publisher.publish(nextEvent());
233+
}
212234
} catch (Exception e) {
213235
e.printStackTrace();
214236
}

src/main/java/com/binaryigor/eventsql/EventSQL.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
public class EventSQL {
2727

28-
public static final int DEFAULT_FLUSH_PUBLISH_BUFFER_SIZE = 500;
28+
public static final int DEFAULT_FLUSH_PUBLISH_BUFFER_SIZE = 1000;
2929
// delay is applied only if there is no more records to flush; see DefaultEventSQLPublisher.startFlushPublishBufferThread()
3030
public static final Duration DEFAULT_FLUSH_PUBLISH_BUFFER_DELAY = Duration.ofMillis(250);
3131
private final EventSQLRegistry registry;

0 commit comments

Comments
 (0)