Skip to content

Instantly share code, notes, and snippets.

@lmolkova
Last active January 3, 2024 20:54
Show Gist options
  • Save lmolkova/115e6e319037a06d50e09666560363fb to your computer and use it in GitHub Desktop.
Save lmolkova/115e6e319037a06d50e09666560363fb to your computer and use it in GitHub Desktop.
ServiceBus binder processor perf testing

Checking ServiceBusProcessorClient performance (via Spring binder)

Setup

Tested processor performance in the following setup:

image

Sender sends up to 4k messages per second in batches. Message size is 12 bytes.

Messages are received by consumer (via Spring Boot binder), which

  • forwards the payload of each message to another queue in the same namespace.
  • then completes the message.

Consumer uses two different connections for queue1 and queue2. We also have a receiver for the queue2 so it does not overflow and we simulate more load on SB instance.

We're going to monitor the consumer:

  • throughput: number of received (forwarded and completed) messages
  • CPU utilization %, normalized per core (i.e. 50% with 2 core is comparable to 100% on 1 core)
  • max memory usage

In all scenarios:

  • we have more messages sent than consumer is able to process.
  • ServiceBus namespace instance is not overloaded (CPU/memory remains under 80%).
  • sender, consumer and receiver run in different containers in k8s pod.

Code

@Autowired
private ServiceBusSenderClient senderClient;

@Bean
public Consumer<Message<String>> consume() {
    return message -> {
        Checkpointer checkpointer = (Checkpointer) message.getHeaders().get(CHECKPOINTER);
        ServiceBusReceivedMessageContext messageContext = (ServiceBusReceivedMessageContext) message.getHeaders().get("azure_service_bus_received_message_context");
        ServiceBusReceivedMessage msg = messageContext.getMessage();
        try {
            checkMessage(messageContext.getMessage());
            senderClient.sendMessage(new ServiceBusMessage(message.getPayload()));
            checkpoint(checkpointer.success(), msg).block();
        } catch (Exception ex) {
            checkpoint(checkpointer.failure(), msg).block();
            throw ex;
        }
    };
}

Tried other variations (no noticeable effect on perf):

  • using topic + 1 subscription
  • using producer binder

Configuration

spring.cloud.azure.servicebus.connection-string=${SERVICEBUS_CONNECTION_STRING}
spring.cloud.azure.servicebus.namespace=${SERVICEBUS_NAMESPACE}
spring.cloud.azure.servicebus.entity-type=queue
spring.cloud.azure.servicebus.producer.entity-name=${FORWARD_QUEUE_NAME}
spring.cloud.stream.bindings.consume-in-0.destination=${SERVICEBUS_QUEUE_NAME}
spring.cloud.stream.servicebus.bindings.supply-out-0.producer.entity-type=queue
spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.auto-complete=false
spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.entity-type=queue
spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.prefetch-count=0
spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.max-concurrent-calls=${PROCESSOR_CONCURRENCY}
spring.cloud.function.definition=consume;
spring.cloud.azure.retry.mode=exponential
spring.cloud.azure.retry.exponential.max-retries=5
spring.cloud.azure.retry.exponential.base-delay=PT2S
spring.cloud.azure.retry.exponential.max-delay=PT60S
spring.cloud.azure.resource-manager.enabled=false
resources:
  requests:
    memory: "2Gi" # parameter
    cpu: "2"      # parameter
  limits:
    memory: "2Gi" # parameter
    cpu: "2"      # parameter

Parameters

  • azure-messaging-servicebus version: 7.15.0-beta.4 or 7.15.0-beta.5
  • processor concurrency: from 8 to 256
  • reactor thread pool size (-Dreactor.schedulers.defaultBoundedElasticSize): default (20 threads), up to 260 threads
  • CPU requests/limits: 1 or 2
  • Memory requests/limits: 2Gi or 6Gi

Results

Concurrency

Tested with 7.15.0-beta.5, 2 cores, 2Gi.

Version Concurrency Threads Throughput, messages per second Max CPU per core % Max memory usage, MB
7.15.beta.5 8 20 ~120 ~12% ~450 MB
7.15.beta.5 96 100 ~1350 ~55% ~900 MB
7.15.beta.5 128 140 ~1650 ~57% ~900 MB
7.15.beta.5 256 260 ~2400 ~87% ~1200 MB

Throughtput grows with concurrency and resource utilization while resources are available.

image

Versions

2 cores, 6 Gi

Version Concurrency Threads Throughput, messages per second Max CPU per core % Max memory usage, MB
7.15.beta.4 256 260 ~1350 ~97% ~2000 MB
7.15.beta.5 256 260 ~2700 ~90% ~2200 MB

7.15.beta.5 version has 2x throughput improvement comparing to 7.15.beta.4.

image

2 cores, 2 Gi

Version Concurrency Threads Throughput, messages per second Max CPU per core % Max memory usage, MB
7.15.beta.4 256 260 ~480 ~95% ~1100 MB
7.15.beta.5 256 260 ~2400 ~87% ~1200 MB

With 2 Gi memory limit, beta.5 shows 5x improvement over beta.4. image

Threads

Tested with 2 cores, 6Gi memory limit

Version Concurrency Threads Throughput, messages per second Max CPU per core % Max memory usage, MB
7.15.beta.5 96 20 ~250 ~12% ~800 MB
7.15.beta.5 96 100 ~1350 ~56% ~1000 MB

Configuring reactor elastic thread pool size (reactor.schedulers.defaultBoundedElasticSize) to slightly exceed concurrency results in corresponding throughput increase and better resource utilization. Concurrency should be increased along with the thread pool size.

image

Cores

Tested with 2 Gi memory request/limit

Version Cores Concurrency Throughput, messages per second Max CPU per core % Max memory usage, MB
7.15.beta.5 1 128 ~1350 ~90% ~700 MB
7.15.beta.5 2 128 ~1800 ~60% ~900 MB

image

Memory limits

Tested with 2 cores request/limit

Version Memory Concurrency Throughput, messages per second Max CPU per core % Max memory usage, MB
7.15.beta.5 2Gi 256 ~2400 ~90% ~1000 MB
7.15.beta.5 6Gi 256 ~2700 ~90% ~2200 MB

Reducing memory to 2Gi has minor impact on throughput. image

RunId Pod Version Total received messages Test duration Concurrency Threads Throughput, mps Cores Mem limit, Gi Mem used, MB (memoryWorkingSetBytes) CPU normalized, %
s88372 happycase-java-servicebus-stress-test-29-gzsd4 7.15.0-beta.5 141876 1081.392 8 20 131.1975676 2 6 800  
s9d3b5 happycase-java-servicebus-stress-test-28-p4m5k 7.15.0-beta.5 1451840 1082.628 96 100 1341.033116 2 6 1020  
sb3f7d happycase-java-servicebus-stress-test-30-4nd79 7.15.0-beta.5 1765911 1082.687 128 140 1631.044799 2 6 1330  
s27e9e happycase-java-servicebus-stress-test-34-r4rgk 7.15.0-beta.5 2963176 1084.699 256 260 2731.795641 2 6 2160  
s1e549 happycase-java-servicebus-stress-test-61-j9lv2 7.15.0-beta.5 278826 1081.987 96 20 257.6981054 2 6 800  
sc604c happycase-java-servicebus-stress-test-58-j28h2 7.15.0-beta.4 3442272 2520 256 260 1365.980952 2 6 2000  
sb33e1 happycase-java-servicebus-stress-test-64-kz6bb 7.15.0-beta.5 1487875 1084.597 128 140 1371.822898 1 2 700  
s0aa17 happycase-java-servicebus-stress-test-65-xqwqh 7.15.0-beta.5 2627220 1083.146 256 260 2425.545587 2 2 1000  
sd7f68 happycase-java-servicebus-stress-test-66-n7tns 7.15.0-beta.5 1841540 1023.307 128 140 1799.596797 2 2 900  
                       
s03396 happycase820-java-servicebus-stress-test-70-79xf6 7.15.0-beta.5 131762 1081.166 8 20 121.8702771 2 2 450 12
sc052a happycase96100-java-servicebus-stress-test-70-wgvvj 7.15.0-beta.5 1464390 1082.713 96 100 1352.519089 2 2 900 55
sa45a9 happycase128140-java-servicebus-stress-test-70-9nq9x 7.15.0-beta.5 1796760 1083.007 128 140 1659.047448 2 2 900 57
s4ad8d happycase256260-java-servicebus-stress-test-70-w8p9g 7.15.0-beta.5 2605304 1084.138 256 260 2403.111043 2 2 1200 87
s2dec1 happycase256260-java-servicebus-stress-test-72-lf4jg 7.15.0-beta.4 530240 1098.044 256 260 482.8950388 2 2 1100 95
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment