Configuration Keys
Every key of the wow.* configuration a Spring Boot host reads, 233 in all, generated from the host's configuration metadata (spring-configuration-metadata.json). Spring Boot binds them relaxed: max-concurrent, maxConcurrent and MAX_CONCURRENT are one key. A wow.* key that no property names is reported when the node starts. A key below a map takes the map's key in brackets when it holds a dot: wow.aggregates.[shop.cart].tenant.
wow
| Key | Type | Default | Description |
|---|---|---|---|
wow.aggregates | Map<String,Aggregate> | Per aggregate type, by <context>.<aggregate> (bracketed in a property name: wow.aggregates.[shop.cart]). | |
wow.backend | String | auto | The backend of a node that names no wow.stores: auto (the default) takes PostgreSQL or MongoDB by the application's DataSource or MongoClient, and refuses to start without either unless a profile of wow.internal.dev-profiles is active or the internal port binds the loopback interface alone; memory keeps the data in memory on purpose, for one node whose data does not outlive it. |
wow.ddl | StoreDdl | auto | How the stores manage the framework's tables, collections and indexes: auto creates what is missing (the default), validate creates nothing and refuses a store whose schema is missing or in another format, none creates and checks nothing; wow.stores.<name>.ddl overrides it for one store. |
wow.schedules | Map<String,Schedule> | Per @OnSchedule job, by its name: wow.schedules.<job>.*. | |
wow.service | String | The service's name; spring.application.name when not set. | |
wow.standby | Boolean | false | Whether the node starts on standby: through the dual-run window of a migration's cut-over it serves reads of what migrate-data imports, refuses every command (wow.node.standby, 503), and handles no subscription, retries no parked entry and runs no job, until its operator resumes it on the internal port (POST /wow/node/resume with the cluster token, naming who resumes it). A subscription that starts at the end of its stream starts where the stream ends at the resume. |
wow.stores | Map<String,Store> | The named store instances, by name: wow.stores.<name>.type and its settings. | |
wow.subscriptions | Map<String,SubscriptionSettings> | Per subscription, by its name (bracketed in a property name: wow.subscriptions.[CartSaga.onOrderCreated]). |
wow.calls
The synchronous calls of the node's sagas to the command gateways of other contexts: each context's base URL (contexts, wow.calls.contexts.<context>.url: the target's internal port, http://host:port of an address in its internal network, never its public port, which refuses calls), the service token the node presents as Authorization: Bearer (token; an application's me.ahoo.wow.client.ServiceCredential bean, such as one that renews a CoSec token, replaces it), how long one call may take (timeout) and connect (connectTimeout). A saga that calls a context without a URL fails, then parks.
| Key | Type | Default | Description |
|---|---|---|---|
wow.calls.connect-timeout | Duration | PT2S | |
wow.calls.contexts | Map<String,CallTarget> | ||
wow.calls.timeout | Duration | PT10S | |
wow.calls.token | String |
wow.clickhouse
ClickHouse's configuration, wow.clickhouse.*, read when wow-clickhouse is on the classpath (ClickHouseAutoConfiguration): - wow.clickhouse.endpoint: the server's HTTP endpoint, such as http://localhost:8123; ClickHouse is wired only when it is set. username (default) and password (empty) sign in; database (default) holds the tables. - wow.clickhouse.sink.enabled: whether the Manifest's published streams are written to ClickHouse; on by default. - wow.clickhouse.sink.ddl: auto (create what is missing, then validate; the default), validate or none. - wow.clickhouse.sink.batch-size: the most records one insert carries (1000). - wow.clickhouse.sink.deduplication-window: how many recent inserts each table remembers to drop a retried one (1000), the non_replicated_deduplication_window of the tables auto creates. - wow.clickhouse.sink.aggregates.<context>.<aggregate>.history.enabled: whether every commit is kept (on by default); off keeps only the current state. ...history.ttl: how long the history is kept, forever when unset. - wow.clickhouse.query.enabled: whether the datasets are served from ClickHouse; on by default. - wow.clickhouse.query.metadata-ttl: how long a dataset table's columns and engine are reused before system.columns is read again (1m). - wow.clickhouse.query.settings.<name>: server settings every dataset query runs under, such as max_memory_usage; the execution time comes from each query's budget (wow.query.http.timeout), not from here.
| Key | Type | Default | Description |
|---|---|---|---|
wow.clickhouse.database | String | default | |
wow.clickhouse.endpoint | String | ||
wow.clickhouse.password | String | "" | |
wow.clickhouse.username | String | default |
wow.clickhouse.query
| Key | Type | Default | Description |
|---|---|---|---|
wow.clickhouse.query.enabled | Boolean | true | |
wow.clickhouse.query.metadata-ttl | Duration | PT1M | |
wow.clickhouse.query.settings | Map<String,String> |
wow.clickhouse.sink
| Key | Type | Default | Description |
|---|---|---|---|
wow.clickhouse.sink.aggregates | Map<String,Map<String,Aggregate>> | By context, then by aggregate name. | |
wow.clickhouse.sink.batch-size | Integer | 1000 | |
wow.clickhouse.sink.ddl | String | auto | |
wow.clickhouse.sink.deduplication-window | Integer | 1000 | |
wow.clickhouse.sink.enabled | Boolean | true |
wow.clients
The typed clients of the peer contexts over HTTP, wow-client's: a peer context listed in contexts is reached over HTTP by the service's me.ahoo.wow.api.client.CommandClient and me.ahoo.wow.api.read.QueryApi beans, in its ClientTarget.mode (or mode); any other context as without these clients. Where each is: its public routes' base ClientTarget.url (user mode) and its internal port's ClientTarget.internalUrl (service mode; wow.calls.contexts.<context>.url when not set, which the sagas' calls use too), or what an application's me.ahoo.wow.client.EndpointResolver bean resolves. Each request is answered within timeout (a command's longer wait aside) after connecting within connectTimeout, and retried as retry says; a user-mode request forwards the forwardedHeaders of the public request it serves. The service mode presents the me.ahoo.wow.client.ServiceCredential bean (wow.calls.token).
| Key | Type | Default | Description |
|---|---|---|---|
wow.clients.connect-timeout | Duration | PT2S | |
wow.clients.contexts | Map<String,ClientTarget> | ||
wow.clients.forwarded-headers | List<String> | Authorization | |
wow.clients.mode | Mode | service | |
wow.clients.timeout | Duration | PT30S |
wow.clients.retry
How a problem a retry may get past is retried (me.ahoo.wow.client.RetryPolicy).
| Key | Type | Default | Description |
|---|---|---|---|
wow.clients.retry.backoff | Duration | PT0.2S | |
wow.clients.retry.max-attempts | Integer | 3 | |
wow.clients.retry.max-retry-after | Duration | PT5S |
wow.cluster
| Key | Type | Default | Description |
|---|---|---|---|
wow.cluster.drain-timeout | Duration | PT10S | |
wow.cluster.forward-timeout | Duration | How long a forward waits for the owner's reply, capped by the command's own deadline: wow.wait.default-timeout when not set, so a node whose waits are longer forwards as long. A forward the owner received and did not answer by then is reported unknown (wow.command.unknown), not run here, so a retry with the same request id is safe. | |
wow.cluster.heartbeat-interval | Duration | PT3S | |
wow.cluster.lease-parallelism | Integer | 16 | |
wow.cluster.lease-ttl | Duration | PT10S | |
wow.cluster.max-in-flight-per-peer | Integer | 1024 | |
wow.cluster.member-ttl | Duration | PT10S | |
wow.cluster.reconcile-interval | Duration | PT1S | |
wow.cluster.reconcile-timeout | Duration | PT1M | How long one reconcile may run before the node takes itself as stuck and leaves the members until it ends. |
wow.cluster.renew-interval | Duration | PT3S | |
wow.cluster.route-refresh-interval | Duration | PT1S | |
wow.cluster.router | Routing | Where commands run; local on the memory backend and partitioned on the others when not set. |
wow.contexts
The bounded contexts the node serves: every context of the domain modules on the class path by default; only those include names when set; less those exclude names. A name that is no context on the class path is refused at startup.
| Key | Type | Default | Description |
|---|---|---|---|
wow.contexts.exclude | Set<String> | ||
wow.contexts.include | Set<String> |
wow.cosec
The keys wow-cosec reads (wow.cosec.*), bound here so they are checked and documented as the others are.
| Key | Type | Default | Description |
|---|---|---|---|
wow.cosec.trust-injected-app | Boolean | false | Whether a service in CoSec's inject mode, behind a CoSec gateway (cosec.inject.enabled=true with cosec.authorization.enabled=false), records the application of the requests the gateway forwards, their CoSec-App-Id, as the caller's app and the request fact app_id: a trust decision, off by default; in any other mode it fails the startup. |
wow.cosec.authorizer
| Key | Type | Default | Description |
|---|---|---|---|
wow.cosec.authorizer.enabled | Boolean | false | Whether CoSec authorizes every command as its caller (CoSecCommandAuthorizer), not only the public routes by path; the services' calls are authorized by CoSec whatever this says. |
wow.cosec.cross-scope
The callers that may act across scopes: by CoSec role or policy.
| Key | Type | Default | Description |
|---|---|---|---|
wow.cosec.cross-scope.policies | Set<String> | ||
wow.cosec.cross-scope.roles | Set<String> |
wow.cosec.service
The service identities, whose synchronous calls the internal port takes.
| Key | Type | Default | Description |
|---|---|---|---|
wow.cosec.service.policies | Set<String> | ||
wow.cosec.service.roles | Set<String> |
wow.cosec.tenant-level
The tenant-level callers, who read and write every aggregate of their tenant.
| Key | Type | Default | Description |
|---|---|---|---|
wow.cosec.tenant-level.policies | Set<String> | ||
wow.cosec.tenant-level.roles | Set<String> |
wow.cosid
The keys wow-cosid reads (wow.cosid.*), bound here as cosec is.
| Key | Type | Default | Description |
|---|---|---|---|
wow.cosid.global-generator | String | The CosId generator of the node's global ids (command ids, wait ids); CosId's own cosid when not set. |
wow.executor
| Key | Type | Default | Description |
|---|---|---|---|
wow.executor.lane-capacity | Integer | 1024 | The commands one aggregate may have waiting in the executor. |
wow.executor.max-in-flight | Integer | 4096 | The commands the node may have in flight, all contexts together: beyond it a command is refused as wow.command.busy (429, with Retry-After) instead of queued. |
wow.health
The node's health and its probes: wow.health.*.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.enabled | Boolean | true | Whether the host contributes the node's health to Actuator and its probe groups. |
wow.health.sample-interval | Duration | PT10S | How often the node samples what a probe must not wait for (the logs' heads, the subscriptions' lag and parked entries) and probes its stores. |
wow.health.executor
The node's executors.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.executor.saturation | Double | 0.9 | The share of wow.executor.max-in-flight in flight at which the executor is degraded. |
wow.health.liveness
Whether the node is stuck, so a restart helps; a storage outage never fails it.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.liveness.stall-timeout | Duration | PT3M | How long the node's dispatcher may not tick, or commands in flight not finish while every log answers, before the node is not live. |
wow.health.ownership
The node's share of each lease group's partitions.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.ownership.handover-grace | Duration | PT1M | How long the owned partitions may differ from the node's share (a hand-over) before the group is degraded, and down (the node not ready) when it owns none of a share that is not empty. |
wow.health.stores
The node's event logs, as its own calls and a probe of their heads see them.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.stores.down-after | Duration | PT15S | How long a log must keep failing, with no success since, before it is down and the node not ready: a failed call or probe, or a probe that timed out (after this long, at least a second); a probe still under way is no failure. 0s is down at the first failure. |
wow.health.stores.window | Duration | PT1M | A log with a failure this recent is degraded. |
wow.health.subscriptions
The node's subscriptions, never a reason to take it out of service.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.subscriptions.lag-degraded-at | Long | 10000 | The entries a subscription may be behind the log's head before it is degraded. |
wow.health.subscriptions.parked-degraded-at | Integer | 100 | The parked entries of a subscription, counted up to this, at which it is degraded (as when they grow). |
wow.health.topics
The topics of other contexts the node reads.
| Key | Type | Default | Description |
|---|---|---|---|
wow.health.topics.required | Boolean | true | Whether a topic the node is still joining makes it not ready; degraded only when false. |
wow.http
| Key | Type | Default | Description |
|---|---|---|---|
wow.http.client-facts | Boolean | true | Whether the public port records each command's client address and User-Agent as the request facts remote_ip and user_agent (09 §17 G-10, as 9.x did), for audit. They are personal data, and written into the event stream, which is never deleted: once recorded, they cannot be erased. On by default; off records neither. |
wow.http.errors | Map<String,Error> | The HTTP status of an error code, by code (bracketed in a property name: wow.http.errors.[a.b].status). | |
wow.http.global-errors | Boolean | true | Answers what fails on the application's own routes as the framework routes' problem+json: on by default; off leaves those errors to Spring's error handling. |
wow.http.request-facts | Set<String> | The request headers copied into a command's headers, beside the standard request facts the edge records: app_id, the application the caller resolver authenticated, and on the public port user_agent and remote_ip (09 §17 G-10); none of the three can be listed. remote_ip is the server's remote address: behind a proxy, set server.forward-headers-strategy to have the server take it from the proxy's forwarded headers. | |
wow.http.services | Map<String,Service> | The service identities the internal port trusts by their static tokens, by service name: a call whose Authorization: Bearer token is one of them is that service's, which alone may send Wow-Call, and only to the internal port. For a deployment without CoSec; with CoSec, wow.cosec.service.roles names its service principals. |
wow.http.manifest
| Key | Type | Default | Description |
|---|---|---|---|
wow.http.manifest.enabled | Boolean | true | Serves GET /wow/manifest. |
wow.http.openapi
| Key | Type | Default | Description |
|---|---|---|---|
wow.http.openapi.enabled | Boolean | true | Serves GET /wow/openapi, the OpenAPI 3.1 document of the routes. |
wow.http.openapi.springdoc
The springdoc bridge, when the application has springdoc.
| Key | Type | Default | Description |
|---|---|---|---|
wow.http.openapi.springdoc.enabled | Boolean | true | Merges the Wow routes into springdoc's documents. |
wow.http.openapi.springdoc.group | String | wow | The springdoc group of the Wow routes alone: /v3/api-docs/<group>. |
wow.internal
| Key | Type | Default | Description |
|---|---|---|---|
wow.internal.connect-timeout | Duration | PT1S | How long a peer has to accept a connection of the cluster channel or the signal sender. |
wow.internal.dev-profiles | Set<String> | dev, local, test, demo | The development profiles, dev, local, test and demo: in them the internal port may run without token, and wow.backend=auto falls back to memory when no durable backend is available. |
wow.internal.host | String | The address the internal server binds, 0.0.0.0 for every interface; when not set, the host of the member address the node advertises (wow.node.address, else this host's address). | |
wow.internal.port | Integer | 9090 | The port of the internal server: the cluster channel and the wait signals; 0 for one the system chooses, which the node advertises once the server is bound. |
wow.internal.signal-queue-capacity | Integer | 1024 | The signals queued per origin node before they are dropped. |
wow.internal.token | List<String> | The service's cluster tokens, at least 32 characters each: the secret its nodes share, which alone opens the cluster channel (/wow/internal/forward) and the wait signals of the internal port. A node sends the first and takes any, so a token is rotated by adding the new one second, then moving it first on every node, then removing the old one; a comma-separated value (WOW_INTERNAL_TOKEN) is a list. A service token of wow.http.services opens the call routes, not these, and this token no call route. A node without one starts only in a profile of devProfiles, or with its internal port bound to the loopback interface alone, and warns: its cluster routes then take every request. |
wow.kafka
The Kafka transport's configuration, wow.kafka.*, read when wow-kafka is on the classpath (KafkaAutoConfiguration): - wow.kafka.bootstrap-servers: the brokers; the transport, the Kafka Sink and the Kafka-sourced subscriptions are wired only when it is set. - wow.kafka.properties.*: passed to every producer and consumer as they are (security.protocol, client.id, linger.ms,...), except the settings the transport's guarantees rest on (me.ahoo.wow.kafka.KafkaOptions). - wow.kafka.topic-prefix: put before every topic name, such as staging. for staging.wow.shop.order.state; none by default, so the topics are the Manifest's. The sinks write under it and the subscriptions of other contexts' streams read under it. - wow.kafka.poll-timeout: the longest a consumer's poll waits (200 ms by default); a read that is cancelled, and a partition that joins or leaves a topic's shared consumer, take effect within it. - wow.kafka.offset-gap: what a read of another context's topic does when its partition no longer holds the offset it is to read from (the retention deleted it, or the topic was created again): fail (the default) fails the read, so the subscription stays where it is, logging errors; to recover, set earliest and restart the node. earliest reads on from the earliest offset retained; the skipped range is lost to that subscription. Either way the gap is logged as an error and counted (wow.kafka.offset.gaps). - wow.kafka.command-intake: whether each context the node hosts executes the commands other contexts send to its command topic, wow.<context>.commands; off by default. The topic must exist with as many partitions as the context's log, and the node checks it at start. Sending commands to other contexts and reading their receipts need no switch. A context with an aggregate type routed through Kafka takes them whatever this says. - wow.kafka.topics.ddl: how the node manages the topics its contexts own at its start, see Topics: validate (the default) fails the start when one is missing or a command topic does not have exactly wow.log.partitions partitions; auto creates the missing ones first; none checks only the command topics the node reads, whose partitions must still be the log's. - wow.kafka.topics.replication-factor (the broker's default), wow.kafka.topics.min-insync-replicas (the broker's default) and wow.kafka.topics.configs.* (retention.ms,...): what auto creates a topic with. A command accepted on a command topic is durable only when both are at least 2: at start the node warns when they are below that, as configured or as the command topics of its contexts are, unless a profile of Topics.devProfiles is active (dev, local, test, demo). - wow.kafka.topics.partition-refresh (30s): how often the node looks for partitions added to the topics it reads (other contexts' streams, its receipts), and its consumers' metadata.max.age.ms; the new partitions are read within about twice this.
| Key | Type | Default | Description |
|---|---|---|---|
wow.kafka.bootstrap-servers | String | ||
wow.kafka.command-intake | Boolean | false | |
wow.kafka.offset-gap | OffsetGapPolicy | fail | |
wow.kafka.poll-timeout | Duration | PT0.2S | |
wow.kafka.properties | Map<String,String> | ||
wow.kafka.topic-prefix | String | "" |
wow.kafka.topics
The topics the node's contexts own: their DDL, and how auto creates them.
| Key | Type | Default | Description |
|---|---|---|---|
wow.kafka.topics.configs | Map<String,String> | ||
wow.kafka.topics.ddl | TopicDdl | validate | |
wow.kafka.topics.dev-profiles | Set<String> | dev, local, test, demo | The profiles that run without durable topics, a single broker: no warning about it. |
wow.kafka.topics.min-insync-replicas | Integer | ||
wow.kafka.topics.partition-refresh | Duration | PT30S | |
wow.kafka.topics.replication-factor | Short |
wow.log
| Key | Type | Default | Description |
|---|---|---|---|
wow.log.max-poll-interval | Duration | PT1S | The longest a tail waits between polls that find nothing: an idle tail backs off up to it. |
wow.log.partitions | Integer | 64 | The partitions of each context's log: fixed once the log holds entries, a power of two that divides 4096, refused at start otherwise. |
wow.log.poll-interval | Duration | PT0.05S | How often a tail polls while it finds entries. |
wow.log.store | String | The store of every aggregate's log that wow.aggregates.[<context>.<aggregate>].log.store does not name: one of wow.stores, the only one when not set. |
wow.log.append-queue
The partition append queue; each setting not given is the backend's preset (AppendQueueOptions).
| Key | Type | Default | Description |
|---|---|---|---|
wow.log.append-queue.enabled | Boolean | ||
wow.log.append-queue.max-batch | Integer |
wow.log.handoff
The hand-over of the node's own commits to its tails in memory: a tail waits its gap wait for a missing sequence and maxPollInterval without a commit before it reads the store.
| Key | Type | Default | Description |
|---|---|---|---|
wow.log.handoff.capacity | Integer | 1024 | |
wow.log.handoff.enabled | Boolean | true | |
wow.log.handoff.gap-wait | Duration | PT1S | The longest a tail waits for a missing sequence that an append of the node still in flight may hold; once every such append returned without it, the tail reads the store at once. |
wow.mongo
| Key | Type | Default | Description |
|---|---|---|---|
wow.mongo.database | String | wow | The database of the log and the stores on the MongoDB backend. |
wow.mongo.transaction-timeout | Duration | PT5S | How long an append's transaction, its retries included, may take on a store that sets none in its options.transaction-timeout: below the default wait timeout, so a command appending during an outage is answered before its wait ends. |
wow.node
| Key | Type | Default | Description |
|---|---|---|---|
wow.node.address | String | The host:port of this node's internal port, as the other nodes reach it. | |
wow.node.id | String | This node's id among the nodes of the service; a new one each start when not set. |
wow.opentelemetry
Observability's configuration, wow.opentelemetry.*, read when wow-opentelemetry is on the classpath (ObservabilityAutoConfiguration): - wow.opentelemetry.enabled: whether the node is traced and measured; on by default. - wow.opentelemetry.sample-interval: how often the gauges that are read, not heard (parked entries, the subscriptions' positions and a rebuild's progress), are sampled (15s).
| Key | Type | Default | Description |
|---|---|---|---|
wow.opentelemetry.enabled | Boolean | true | |
wow.opentelemetry.sample-interval | Duration | PT15S |
wow.ops
The operator's side of the parked entries: enabled serves their routes under /wow/ops/parked on the public port, off by default; webhook tells a URL when an entry parks or spends its retries.
| Key | Type | Default | Description |
|---|---|---|---|
wow.ops.enabled | Boolean | false |
wow.ops.publish
Publishing the failure ledger to the ops context: when enabled, each entry goes to the parked aggregate of context (ops) through its command topic, at once when it parks and on a scan every interval (30s); the node needs the Kafka transport.
| Key | Type | Default | Description |
|---|---|---|---|
wow.ops.publish.context | String | ops | |
wow.ops.publish.enabled | Boolean | false | |
wow.ops.publish.interval | Duration | PT30S |
wow.ops.webhook
The webhook of the parked entries, posted to url when set: in Wow's JSON (generic) or as a WeCom robot's Markdown message (wecom) (format), for the events listed (parked, exhausted; both by default), within timeout, with headers added to each request.
| Key | Type | Default | Description |
|---|---|---|---|
wow.ops.webhook.events | Set<Kind> | parked, exhausted | |
wow.ops.webhook.format | Format | generic | |
wow.ops.webhook.headers | Map<String,String> | ||
wow.ops.webhook.timeout | Duration | PT5S | |
wow.ops.webhook.url | java.net.URI |
wow.query
The query plane's configuration, wow.query.*, read by QueryAutoConfiguration: - wow.query.enabled (true): whether the query routes and gateways are wired at all. - wow.query.http.*: the HTTP budget (QueryBudget) every query route runs under: max-list-size (1000), max-page-size (100), max-page-window (10000), max-filter-nodes (128), max-filter-values (1000), allow-expensive-operators (true), max-residual-groups (10000), count-limit, allow-exact-count (false), timeout (30s), unindexed-sort-expensive (false); and default-list-size (100), the limit of a list that names none. A limit of 0 is off. - wow.query.in-process.*: the budget of in-process queries, the typed me.ahoo.wow.api.read.QueryApi's, of trusted code too: the same keys and defaults as wow.query.http.*, its default-list-size the limit of an in-process list that names none. - wow.query.in-process-timeout (30s): the timeout of an in-process query that has no budget, trusted code's that dropped it (QueryApi.trusted().unbounded()). - wow.query.require-explicit-entry, wow.query.require-authenticated-scope (both false): the entry gates. - wow.query.cross-tenant.enabled: whether a caller that may act across scopes may query across tenants on the query routes, Wow-Scope: tenant=* or tenant=("t1" "t2"); 403 wow.auth.forbidden while off. A type whose tenant is fixed is still read in that tenant alone, and every such query's audit is logged at INFO (me.ahoo.wow.query.CrossTenantAudit). The commands and the state reads never cross tenants. - wow.query.abac.*: enabled (false), require-principal-tags (false), match-missing-tag-key (true), attribute-prefix (abac.): the caller's abac.<key> attributes matched against the snapshots' tags. - wow.query.sensitivity.display-comparable (true): whether filters and sorts may compare a DISPLAY value. - wow.query.index.ddl: how the snapshot backend manages the indexes of each aggregate's x-wow-index at startup. - wow.query.count-cache.*: enabled (true), max-entries (10000), ttl (30s): the cache of the snapshot counts, valid while the type's snapshot write checkpoint holds and for at most ttl (another node's write can make a cached count lag the rows by up to ttl); paged-totals serves the totals of paged queries from it too, which by default are counted with their page so that a total agrees with its rows; events-enabled caches the events' counts the same way, though the log runs ahead of that checkpoint, so a cached events count may lag the streams for up to ttl. - wow.query.dataset.config-directory: the working directory whose wow/dataset/ holds dataset files, {context}.{dataset}.json; each takes precedence over the classpath's META-INF/wow/dataset/ file of the same dataset. The datasets of both are served when a dataset backend is wired (ClickHouse's, say). - wow.query.memory.enabled: whether the snapshots on a store in memory are queried, from the in-memory reference query backend, and the events of a log in memory, scanned in memory: for local runs, demos and tests, not for production. - wow.query.events.*: the events model, served from each aggregate's log store, or the store its wow.aggregates.[<context>.<aggregate>].query.events.store names: mongo-read-preference (none, the primary), the MongoDB read preference of its queries (secondaryPreferred, say), so a free filter scans a secondary rather than the primary the log appends to; wow.stores.<name>.query.read-preference takes its place for one store, and sets that of its snapshot queries too. unpinned: whether an events query that names no aggregateId runs, over which wow.stores.<name>.query.events.unpinned and wow.aggregates.[<context>.<aggregate>].query.events.unpinned decide for one store or aggregate type; refused, it is wow.query.invalid (STORAGE_UNSUPPORTED on aggregateId), and the model's descriptor lists the constraint AGGREGATE_REQUIRED. - wow.query.dataset.max-concurrent: how many dataset queries the node runs at once, under one bulkhead, datasets, which keeps the analytics queries from taking a warehouse the Sink writes to; 0 sets none. A query beyond it waits for wow.query.dataset.max-wait (wow.query.max-wait when unset) in a queue of wow.query.dataset.max-queued (wow.query.max-queued, else 4 per permit), then is wow.query.busy. The bound is fixed, not adaptive: the warehouse's writes (the ClickHouse Sink's) give the node no write pressure to adapt to. - wow.query.max-concurrent: how many snapshot and events queries each store runs at once, whichever entry asks, beyond which a query is wow.query.busy (429, with Retry-After); wow.stores.<name>.query.max-concurrent takes its place for one store; 0 for no bound. A query beyond the bound waits in a FIFO queue at most wow.query.max-wait (200ms) for a permit, and is refused when the wait runs out or when wow.query.max-queued (4 per permit) queries wait already; wow.stores.<name>.query.max-wait and .max-queued take their place for one store. - wow.query.adaptive.*: the bound of a store whose max-concurrent neither wow.query.max-concurrent nor wow.stores.<name>.query.max-concurrent fixes adapts to the store's write pressure, evaluated every interval (1s): periods (2) periods in a row in which its appends' p90 latency is above latency-factor (1.5) times its baseline and at least min-latency (5ms), or in which connection-waits (1) callers wait for a connection of its pool, halve it down to floor (1); a calm period in which a query waited or was refused raises it by one up to ceiling (none: the provider's, PostgreSQL half a pool the appends share, all of a query-data-source's). enabled (true) turns it off; wow.stores.<name>.query.adaptive.enabled, .floor and .ceiling take their place for one store. - wow.query.state.max-replay: the most commits a state point read replays from the aggregate's snapshot, or from version 1 (GET …/state?version= or ?at=, …/state/trace, the latest state before the first snapshot); a state further away is 400 wow.query.invalid (REPLAY_TOO_LONG). min-version-wait: how long a read of ?minVersion= (or QueryApi state of a minimum version) waits for the aggregate to reach it, its snapshot caught up with its log meanwhile; past it the read is 409 wow.query.min-version-not-reached with the current version. - wow.query.stream.*: a list streamed as server-sent events reads keyset chunks of chunk-size (500) records, a query permit per chunk, each record's id its position; after max-hold (30s; 0 for no bound) it ends with end {"reason":"hold-expired","resumable":true} and the client resumes it with Last-Event-ID. Neither http.max-list-size nor http.default-list-size bounds such a stream. A list whose sort cannot order a cursor streams without positions, under those bounds, and every end it sends (the hold time's included) says "resumable":false.
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.enabled | Boolean | true | |
wow.query.in-process-timeout | Duration | PT30S | |
wow.query.max-concurrent | Integer | ||
wow.query.max-queued | Integer | ||
wow.query.max-wait | Duration | PT0.2S | |
wow.query.require-authenticated-scope | Boolean | false | |
wow.query.require-explicit-entry | Boolean | false |
wow.query.abac
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.abac.attribute-prefix | String | abac. | |
wow.query.abac.enabled | Boolean | false | |
wow.query.abac.match-missing-tag-key | Boolean | true | |
wow.query.abac.require-principal-tags | Boolean | false |
wow.query.adaptive
wow.query.adaptive.*: how a store's query bound adapts to its write pressure.
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.adaptive.ceiling | Integer | ||
wow.query.adaptive.connection-waits | Integer | 1 | |
wow.query.adaptive.enabled | Boolean | true | |
wow.query.adaptive.floor | Integer | 1 | |
wow.query.adaptive.interval | Duration | PT1S | |
wow.query.adaptive.latency-factor | Double | 1.5 | |
wow.query.adaptive.min-latency | Duration | PT0.005S | |
wow.query.adaptive.periods | Integer | 2 |
wow.query.count-cache
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.count-cache.enabled | Boolean | true | |
wow.query.count-cache.events-enabled | Boolean | false | |
wow.query.count-cache.max-entries | Integer | 10000 | |
wow.query.count-cache.paged-totals | Boolean | false | |
wow.query.count-cache.ttl | Duration | PT30S |
wow.query.cross-tenant
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.cross-tenant.enabled | Boolean | false |
wow.query.dataset
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.dataset.config-directory | String | config | |
wow.query.dataset.max-concurrent | Integer | 8 | |
wow.query.dataset.max-queued | Integer | ||
wow.query.dataset.max-wait | Duration |
wow.query.events
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.events.mongo-read-preference | String | ||
wow.query.events.unpinned | UnpinnedEvents | allow |
wow.query.http
The budget of the query routes, wow.query.http.*.
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.http.allow-exact-count | Boolean | false | |
wow.query.http.allow-expensive-operators | Boolean | true | |
wow.query.http.count-limit | Integer | 10000 | |
wow.query.http.default-list-size | Integer | 100 | |
wow.query.http.max-filter-nodes | Integer | 128 | |
wow.query.http.max-filter-values | Integer | 1000 | |
wow.query.http.max-list-size | Integer | 1000 | |
wow.query.http.max-page-size | Integer | 100 | |
wow.query.http.max-page-window | Long | 10000 | |
wow.query.http.max-residual-groups | Integer | 10000 | |
wow.query.http.timeout | Duration | PT30S | |
wow.query.http.unindexed-sort-expensive | Boolean | false |
wow.query.in-process
The budget of the in-process me.ahoo.wow.api.read.QueryApi, wow.query.in-process.*: the HTTP defaults.
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.in-process.allow-exact-count | Boolean | false | |
wow.query.in-process.allow-expensive-operators | Boolean | true | |
wow.query.in-process.count-limit | Integer | 10000 | |
wow.query.in-process.default-list-size | Integer | 100 | |
wow.query.in-process.max-filter-nodes | Integer | 128 | |
wow.query.in-process.max-filter-values | Integer | 1000 | |
wow.query.in-process.max-list-size | Integer | 1000 | |
wow.query.in-process.max-page-size | Integer | 100 | |
wow.query.in-process.max-page-window | Long | 10000 | |
wow.query.in-process.max-residual-groups | Integer | 10000 | |
wow.query.in-process.timeout | Duration | PT30S | |
wow.query.in-process.unindexed-sort-expensive | Boolean | false |
wow.query.index
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.index.ddl | QueryIndexDdl | auto |
wow.query.memory
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.memory.enabled | Boolean | false |
wow.query.sensitivity
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.sensitivity.display-comparable | Boolean | true |
wow.query.state
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.state.max-replay | Integer | 10000 | |
wow.query.state.min-version-wait | Duration | PT2S |
wow.query.stream
wow.query.stream.*: how a streamed list reads its chunks and how long it runs.
| Key | Type | Default | Description |
|---|---|---|---|
wow.query.stream.chunk-size | Integer | 500 | |
wow.query.stream.max-hold | Duration | PT30S |
wow.snapshot
Where the snapshots are by default: wow.snapshot.store.
| Key | Type | Default | Description |
|---|---|---|---|
wow.snapshot.batch | Integer | 100 | |
wow.snapshot.interval | Duration | PT5S | |
wow.snapshot.pages | Integer | 10 | |
wow.snapshot.primary | String | ||
wow.snapshot.store | String | ||
wow.snapshot.targets | List<String> | ||
wow.snapshot.write-batch | Integer | 256 | How many snapshots of a partition the snapshot subscriptions write together at most. |
wow.state-cache
| Key | Type | Default | Description |
|---|---|---|---|
wow.state-cache.capacity | Integer | The aggregate states kept per context between commands; 0 keeps none. Not set (the default), each context's cache is sized from the heap (work package B.O-4): a tenth of the maximum heap (-Xmx), split between the node's contexts, over an estimated 1 KB per state plus 12 bytes per recent request (about 4 KB with the default 256), at least 4096 and at most 131072 states. A miss costs a snapshot and a tail read before the command is decided, so size it to the aggregates written often: set it when states are much larger than 1 KB (a capacity times the average state size should stay well within the heap), or when a context has more hot aggregates than the default holds. The wow.state-cache counter (hit, miss, evicted) shows the fit. | |
wow.state-cache.recent-requests | Integer | 256 | The requests each cached aggregate keeps of those it committed last, derived from the log, so that most commands skip looking their request id up; 0 looks every request id up. |
wow.state-views
The bounds of each context's state views: wow.state-views.*.
| Key | Type | Default | Description |
|---|---|---|---|
wow.state-views.capacity | Integer | 4096 | |
wow.state-views.max-age | Duration | PT1M | |
wow.state-views.max-bytes | Long | 67108864 |
wow.subscription
| Key | Type | Default | Description |
|---|---|---|---|
wow.subscription.checkpoint-interval | Duration | PT1S | |
wow.subscription.max-in-flight | Integer | 1024 | |
wow.subscription.stop-timeout | Duration | PT10S |
wow.subscription.backpressure
How a handler whose downstream pushes back is retried, and its partition throttled.
| Key | Type | Default | Description |
|---|---|---|---|
wow.subscription.backpressure.initial-backoff | Duration | PT0.1S | |
wow.subscription.backpressure.jitter | Double | 0.5 | |
wow.subscription.backpressure.max-attempts | Integer | 20 | |
wow.subscription.backpressure.max-backoff | Duration | PT10S | |
wow.subscription.backpressure.min-concurrency | Integer | 1 |
wow.subscription.parked
How the parked entries are retried over the long horizon.
| Key | Type | Default | Description |
|---|---|---|---|
wow.subscription.parked.execution-timeout | Duration | PT2M | |
wow.subscription.parked.max-retries | Integer | 10 | |
wow.subscription.parked.min-backoff | Duration | PT3M | |
wow.subscription.parked.scan-interval | Duration | PT10S |
wow.subscription.read-retry
How a failing read of the log, or write of a store, is retried: until the node stops, the backoff growing for max-attempts (5) attempts, after which each failure is logged as an error and retried at max-backoff.
| Key | Type | Default | Description |
|---|---|---|---|
wow.subscription.read-retry.initial-backoff | Duration | PT0.1S | |
wow.subscription.read-retry.max-attempts | Integer | 5 | |
wow.subscription.read-retry.max-backoff | Duration | PT5S | |
wow.subscription.read-retry.multiplier | Double | 2 | |
wow.subscription.read-retry.timeout | Duration |
wow.subscription.retry
How a failing handler is retried before its entry is parked.
| Key | Type | Default | Description |
|---|---|---|---|
wow.subscription.retry.initial-backoff | Duration | PT0.1S | |
wow.subscription.retry.max-attempts | Integer | 5 | |
wow.subscription.retry.max-backoff | Duration | PT5S | |
wow.subscription.retry.multiplier | Double | 2 | |
wow.subscription.retry.timeout | Duration |
wow.subscription.systemic
When a partition pauses on a systemic failure.
| Key | Type | Default | Description |
|---|---|---|---|
wow.subscription.systemic.failing-aggregates | Integer | 3 | |
wow.subscription.systemic.initial-backoff | Duration | PT1S | |
wow.subscription.systemic.max-backoff | Duration | PT30S | |
wow.subscription.systemic.window | Duration | PT10S |
wow.tenant
| Key | Type | Default | Description |
|---|---|---|---|
wow.tenant.default | String | (0) | The tenant of a caller that has none, such as the anonymous one. |
wow.timers
The node's firing loop of the timers: how often it reads, with one query per timer store over every partition the node owns, the timers due within lookahead (pollInterval); how many attempts a technical failure to send one gets before it is parked under wow.timers (maxAttempts); when set, how late a timer may be sent before it expires instead (expiresAfter); how many commands a partition sends at once (parallelism) and, when set, at most a second (maxFireRate), the rate a backlog of overdue timers drains at; how long the claim on a timer being sent holds before another owner may send it (claimTimeout); how long a scheduled command waits in its target for its due time on the target node's clock before it fails instead, to be sent again later (maxEarlyWait).
| Key | Type | Default | Description |
|---|---|---|---|
wow.timers.claim-timeout | Duration | PT30S | |
wow.timers.expires-after | Duration | ||
wow.timers.lookahead | Duration | PT30S | |
wow.timers.max-attempts | Integer | 10 | |
wow.timers.max-early-wait | Duration | PT5S | |
wow.timers.max-fire-rate | Double | ||
wow.timers.parallelism | Integer | 64 | |
wow.timers.poll-interval | Duration | PT1S |
wow.topics
The node's topics without Kafka (DX gap G27), see Topics.
| Key | Type | Default | Description |
|---|---|---|---|
wow.topics.in-memory | Boolean | false | Whether the node's topics are in its JVM instead of Kafka. |
wow.wait
| Key | Type | Default | Description |
|---|---|---|---|
wow.wait.default-timeout | Duration | PT10S | How long a command whose request names no timeout waits (Wow-Wait-Timeout), whichever edge it came through: at most maxTimeout. |
wow.wait.max-hops | Integer | 16 | The most hops of a wait plan, at most WaitOptions.MAX_HOPS_CAP; each saga a chain follows adds two. |
wow.wait.max-registered | Integer | 16384 | The most waits the node holds at once: beyond it a command whose plan waits past ACCEPTED is not run and is answered wow.command.busy (429, with Retry-After), FAILED at ACCEPTED. |
wow.wait.max-timeout | Duration | PT1M | The longest wait; a longer timeout is capped to it, and a node stops within it. |
wow.wait.unknown-contexts | UnknownContexts | allow | Whether a wait plan may name a context the node knows no Manifest of: allow, checked where the chain arrives, or reject. |