1.7. Parallelism#
QuasarDB is massively parallelised, and has both server-side parallelism and client-side parallelism. This document describes both.
Note
Partitions (thread pools) are distinct from shards (time-based data units). See Shards for information about data organization.
1.7.1. Server-side parallelism#
The main configuration option to tune for server-side parallelism is local.network.parallelism. This determines the total number of
server-side threads that are made available to serve client requests. By default, the server determines an appropriate value based on the
number of available CPU cores.
If you wish to have more control, under the hood, the total number of threads are divided over partitions. Each partition is a thread pool
that shares a single storage access mutex: only one thread within a partition can write to the storage layer at a time, but multiple threads
can read simultaneously. This architecture enables controlled parallelism for write operations while maximizing read throughput. As such,
if you have a server with 8 partitions, 8 different storage writes can happen in parallel (one per partition).
Client connections are pinned to partitions: each connection from a client is assigned to a specific partition and all requests from that connection are processed by threads within that partition. This ensures consistent request handling while maintaining parallelism across different client connections.
In practice, most of the time of write requests is spent parsing, sorting and compressing the data, which can all happen in parallel. As such,
we generally recommend using identical values for partitions and threads_per_partition as a good balanced setup: e.g. if you wish to
allocate 64 total threads, we would recommend 8 partitions and 8 threads per partition. This is exactly the mechanism that local.network.parallelism
uses to distribute the threads over all the partitions.
1.7.2. Client-side parallelism#
By default, all QuasarDB client-side operations are asynchronous and allocates multiple connections (8 by default). This means that a single client-side thread multiplexes multiple requests over multiple connections.
If your workload is particularly client-side heavy (for example, when compression is enabled and you’re pushing a lot of data, or when you’re pulling a lot of data from the server that needs to be merged), you can increase the number of client-side threads. In Python, you can speed up your client-side processing using multi-threading as follows:
import quasardb
uri = "qdb://127.0.0.1:2836"
quasardb.Cluster(uri, client_max_parallelism=8)
The code above enables 8 client-side threads.
This would still be limited by the number of connections that the client establishes with the server, and should be tuned accordingly as
well. To increase the number of connections per address (qdbd node) the client is allowed to establish, tune the set_connection_per_address_soft_limit
client-side option. This is a limit per handle, not per connection: e.g. if you have client_max_parallelism=8 and connection_per_address_soft_limit=16,
it means that each thread is able to perform approximately 2 concurrent operations.
Additionally, there’s the max_batch_load configuration option: that tunes the number of low-level operations that are allocated into
a single batch unit. Queries are processed in parallel in batches of shards. For example, if you push a single write operation that writes
into 768 different shards, and you have a max_batch_load of 32, it means that this will create 24 low-level batched operations that can
be parallelised across your available partitions and connections.
In Python, we can set the parallelism to 8, the number of connections per qdbd node to 16 and the max batch load to 32 using the following code:
with quasardb.Cluster(uri, client_max_parallelism=8) as conn:
conn.options().set_client_max_batch_load(16)
conn.options().set_connection_per_address_soft_limit(32)
# Do something with <conn>