KeyedExecutor
The execution resource every dispatcher of one me.ahoo.wow.runtime.WowRuntime shares (design X7): one set of workers threads, sized by CPU cores rather than by the number of aggregate types or dispatchers.
A dispatcher keeps one mailbox per aggregate ID. A mailbox runs its messages one at a time, in arrival order, on the worker it was given when it was created (a running worker with nothing queued, else a parked one); different mailboxes run in parallel. Each worker has its own FIFO queue, so a mailbox that has run throughput messages goes behind the other mailboxes of its worker. A handler that waits (I/O, a retry backoff) holds no worker and delays only its own mailbox. Each receiver (a dispatcher has one per bounded context) accepts at most maxInFlight messages it has not finished, and requests more from its transport only as they finish, so a slow dispatcher backpressures its source instead of buffering.
A suspend or Flow message function called by a dispatcher resumes on these workers (coroutineDispatcher) instead of Dispatchers.Default; its mailbox does not start the next message until it returns, so per-aggregate serialization holds across suspension points.
The runtime owns its executor and disposes it once every component has stopped.
Parameters
the number of worker threads; defaults to the available processors.
the most messages one receiver, i.e. one (dispatcher, bounded context), holds unfinished (running or queued in a mailbox).
the worker thread name prefix.
the most messages of one aggregate a worker runs in one turn (while they complete synchronously) before it moves on to other aggregates, so one hot aggregate cannot starve the others.
the longest a worker that ran out of messages spins before it parks, at most MAX_SPIN; the default, Duration.ZERO, parks at once (no spin). Worth setting only for in-memory, high-rate dispatch whose messages keep arriving within microseconds of each other; with a real store the gain is within noise. A worker spins only when its last wait was shorter than this; after a longer wait it parks at once. A spinning worker takes the next message without the wake-up a park costs (an OS call by the sender and the scheduling delay of the woken thread). CPU cost: while messages keep arriving at intervals shorter than the spin, a worker can stay busy on one core the whole time; after each short interval, the next wait can cost up to the spin in CPU even when no message follows. The worst case is workers cores. In a container with a CPU quota (cgroup limit), set it to Duration.ZERO.
Constructors
Properties
Functions
Stops the workers at once (the runtime's force stop, like disposing a Reactor scheduler): queued mailboxes are discarded on the calling thread — their messages are rejected and stay unacknowledged for redelivery — and no mailbox starts another message. A handler already running finishes its current step. Idempotent.