QueryBulkhead

class QueryBulkhead(val store: String, val maxConcurrent: Int, val maxWait: Duration = DEFAULT_MAX_WAIT, val maxQueued: Int = maxConcurrent * DEFAULT_QUEUE_FACTOR, val ceiling: Int = maxConcurrent)

How many queries one store runs at once: the queries of the snapshots and the events of every aggregate type on the store share it, whichever entry asks, so a burst of list queries cannot take every connection of a pool the store's appends draw from. A query beyond the limit waits briefly: in a FIFO queue of at most maxQueued queries, for at most maxWait, for a permit to free up; it fails with QueryBusyException (wow.query.busy, 429 with Retry-After) when the queue is full (at once) or the wait runs out, as a command beyond the node's in-flight limit is wow.command.busy. A maxWait of zero or a maxQueued of zero refuses at once. A caller cancelled while it waits leaves its queue slot and takes no permit.

A query holds its permit from the backend call to its end: a streamed list or aggregation until its last row is emitted, or until it fails or its collector cancels it. A paged query that counts its total takes one permit for its page and one for its count, one after the other.

The limit starts at maxConcurrent and may move between 1 and ceiling (limit it to another: an AdaptiveQueryLimit gives way to the store's writes). Lowering it cancels no query: the permits of the queries beyond it are kept back as those queries end. A bulkhead whose ceiling is its maxConcurrent keeps that limit.

Constructors

Link copied to clipboard
constructor(store: String, maxConcurrent: Int, maxWait: Duration = DEFAULT_MAX_WAIT, maxQueued: Int = maxConcurrent * DEFAULT_QUEUE_FACTOR, ceiling: Int = maxConcurrent)

Types

Link copied to clipboard

Properties

Link copied to clipboard

The highest the limit may be raised to.

Link copied to clipboard

How many queries the store runs at once now.

Link copied to clipboard

The limit the bulkhead starts with: the configured one, else the one its store's provider derives.

Link copied to clipboard

How many queries may wait at once; one more is refused at once.

Link copied to clipboard

How long a query beyond the limit waits for a permit before it is refused.

Link copied to clipboard
val queued: Int

The queries waiting for a permit now.

Link copied to clipboard

The queries running now.

Link copied to clipboard

The store the queries run on, wow.stores.<name>, for the logs.

Functions

Link copied to clipboard
suspend fun acquire()

Takes a permit: at once when one is free, else after waiting in the queue at most maxWait, or fails with QueryBusyException. The semaphore hands a released permit to the longest waiter (FIFO) and gives it back when that waiter was cancelled meanwhile. Each permit taken is given back with one release: a caller that reads in chunks may give it back between two and take it again, so a lowered limit applies to it from its next chunk.

Link copied to clipboard
fun limit(to: Int)

Sets the limit to to, from 1 to the ceiling. Lowering it lets every running query run on: the permits it takes back are those free now, then those of the queries that end.

Link copied to clipboard
fun release()

Gives a permit acquire took back, or keeps it back when the limit was lowered below the running queries.

Link copied to clipboard
suspend fun <T> run(block: suspend () -> T): T

Runs block under one permit, or fails with QueryBusyException when none is free.

fun <T> run(source: Flow<T>): Flow<T>

source, collected under one permit taken when it is collected, or failing with QueryBusyException.

Link copied to clipboard

How many queries had to wait or were refused since the last call: whether the limit held anyone back.

Link copied to clipboard
open override fun toString(): String