Package-level declarations

Types

Link copied to clipboard

How the access attributes are stamped over the histories: by the built-in subscription wow.access, in stamps of a bounded number of streams, remembering the last attributes of a bounded number of aggregates; and by the access walk of each aggregate type, a page of aggregates at a time, at a bounded rate and a bounded share of the time.

Link copied to clipboard

An aggregate type's ClickHouse settings: wow.aggregates.[<context>.<aggregate>].clickhouse.*.

Link copied to clipboard

Whether every commit of the type is kept (enabled: auto, the Sink's default, keeps it; true or false decide it), and how long (ttl, forever by default).

Link copied to clipboard

An aggregate type's settings. tenant fixes the tenant of every aggregate of the type: its commands and state reads are in that tenant whoever the caller is (me.ahoo.wow.spi.tenant.TenantPolicy). idGenerator names the me.ahoo.wow.spi.id.AggregateIdGenerator bean that gives the type's new ids, or a generator an me.ahoo.wow.spi.id.AggregateIdGeneratorSource bean finds by that name (CosId's, with wow-cosid); the type's ids come from the wowAggregateIdGenerator bean otherwise. A type the node does not serve, or a name nothing gives a generator of, is refused at startup.

Link copied to clipboard

An aggregate type's events queries: unpinned, whether one that names no aggregate runs, over its events store's and wow.query.events.unpinned; store, the store they read (one of wow.stores), its log store when not set: an Elasticsearch store whose event search Sink writes the type's streams, say.

Link copied to clipboard

An aggregate type's queries (wow.aggregates.[<context>.<aggregate>].query.*).

Link copied to clipboard

An aggregate type's snapshot queries: store, the snapshot store they read, one of the type's snapshot.stores, its primary when not set: an Elasticsearch store that searches the states, say, while the commands and the state reads stay on the primary.

Link copied to clipboard

One aggregate type's snapshot stores: stores lists the stores of wow.stores its snapshots are written to, over wow.snapshot.stores; primary is the one its write side and state reads read, and its snapshot queries unless its query.snapshot.store names another, the first when not set.

Link copied to clipboard

One aggregate type's store of its log or of its snapshots: one of wow.stores.

Link copied to clipboard
class BackendsLifecycle(backends: HostBackends) : SmartLifecycle

Creates the schemas of the backends the node opened, where they are missing, before anything uses them.

Link copied to clipboard

How a subscription answers backpressure: a downstream that is up but asks to be called later (wow.command.busy, wow.wait.timeout, wow.query.busy, a 429 with Retry-After). The entry is retried, then its aggregate parks; it never pauses the partition. Meanwhile each backpressure halves the handlings the partition runs at once, and each success raises them again.

Link copied to clipboard

One calling service: the commands it may call, names or patterns (AddItem, shop.cart.*, wow.lifecycle.*): without them, the domain commands alone; a system command (wow.*: the lifecycle, the snapshot rebuild, the schedules) only when an entry naming wow. lists it; an entry naming a domain command narrows the domain commands to those listed. The reads narrow the models it may read on the internal port's read routes, by context.aggregate.model (ordering.order.snapshot, .events, .state) or a dataset's context.dataset, patterns with *: without them, every model the node serves.

Link copied to clipboard
class CheckedMap(val name: String, val entry: String, val type: Class<*>)

A map of beans whose keys are checked to the value's properties: the map's name, how the reference writes one of its entries (entry, <name> or [<context>.<aggregate>]), and the type of its values.

Link copied to clipboard

How the nodes of a service share the partitions of each context's log, and forward commands to their owners.

Link copied to clipboard
class ConfigurationKeys(known: List<ConfigurationPropertyName>, owned: List<ConfigurationPropertyName> = emptyList(), storeOptions: Collection<ProviderOption<*>> = emptyList())

The wow.* keys of the environment that no property names: Spring binds what it knows and ignores the rest, so wow.cluster.forward-timout would be a silent no-op. A key is known when a property of the configuration metadata on the class path (METADATA, the host's and any module's) is the key or holds it (a map or list property, such as wow.aggregates), or when it is under the prefix of another @ConfigurationProperties bean of the application that no metadata describes (its owned prefixes), whose keys this check cannot tell. Names compare as Spring's relaxed binding does (laneCapacity is lane-capacity); only property sources that list their names are read.

Link copied to clipboard
data class ContextSelection(val include: Set<String> = emptySet(), val exclude: Set<String> = emptySet())

The bounded contexts a node serves: wow.contexts.include and wow.contexts.exclude (ContextsProperties) over the domain modules on the class path, their binding indexes (ModelProvider).

Link copied to clipboard

The bounded contexts the node serves among the domain modules on the class path (ContextSelection), all of them by default. A name that is no context on the class path is refused at startup.

Link copied to clipboard
@AutoConfiguration(before = [WowAutoConfiguration::class])
@ConditionalOnClass(name = ["me.ahoo.wow.cosec.CoSecCallerResolver", "me.ahoo.cosec.webflux.ServerWebExchanges"])
@EnableConfigurationProperties(value = [WowProperties::class])
class CoSecAutoConfiguration

Wires CoSec into the host when the application has wow-cosec and CoSec's WebFlux module: the host wires its optional integrations as it wires Kafka, and wow-cosec keeps the Spring-free part, the resolver, the authorizers and the route map. It reads wow.cosec.* once, through WowProperties.cosec, the keys' one reader and one default.

Link copied to clipboard

CoSec's settings (wow-cosec), which the host's CoSec configuration reads from here alone, their one reader and their one default: they have metadata, and a key of wow.cosec that none of these is refuses the startup (ConfigurationKeys) instead of being ignored, so a mistyped Authorizer.enabled cannot leave the authorizer that allows all in place.

Link copied to clipboard
@AutoConfiguration(after = [WowAutoConfiguration::class], beforeName = ["me.ahoo.cosec.spring.boot.starter.pipeline.CoSecPipelineAutoConfiguration"])
@ConditionalOnClass(name = ["me.ahoo.wow.cosec.CoSecCallerResolver", "me.ahoo.cosec.webflux.ServerWebExchanges"])
@ConditionalOnProperty(value = ["cosec.enabled"], havingValue = "true", matchIfMissing = true)
@ConditionalOnProperty(value = ["cosec.pipeline.mode"], havingValue = "enforce", matchIfMissing = true)
class CoSecRouteMapAutoConfiguration

The Manifest's command and service routes as the route map CoSec's pipeline reads (CoSecActions.routes), where CoSec decides the requests itself (cosec.pipeline.mode enforce, its default): a command is <context>:<command> on <aggregate>/<id>, a service route <context>:<route> on route/<route>, the names CoSecCommandAuthorizer and CoSecServiceRouteAuthorizer decide them by. It comes after the host's configuration, which defines the Manifest, and before CoSec's pipeline, whose route map file it takes the place of; an application that maps routes of its own declares its RouteMap. Experimental as wow-cosec is.

Link copied to clipboard
@AutoConfiguration(before = [WowAutoConfiguration::class])
@ConditionalOnClass(name = ["com.zaxxer.hikari.HikariDataSource"])
@ConditionalOnMissingClass(value = ["org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration"])
@ConditionalOnProperty(prefix = "spring.datasource", name = ["url"])
class DataSourceAutoConfiguration

The application's database connection pool, from its properties alone: when spring.datasource.url is set, HikariCP is on the class path, the application has no DataSource bean and no Spring Boot JDBC (spring-boot-jdbc, whose own auto-configuration then makes it), the host makes a HikariDataSource of spring.datasource.url, .username, .password and .driver-class-name, with HikariCP's settings under spring.datasource.hikari.* as Boot reads them; closed with the context. Its pool opens at the first connection. The backend then follows the DataSource bean as it follows the application's (wow.backend); without the URL the host connects to no database.

Link copied to clipboard

A resolver that answers through another one, decorated, narrowing or completing its callers (a service's reserved scopes, its sign-in): the request's credentials are still decorated's, so what the host declares of them, the OpenAPI security schemes (WowOpenApiSecurity.of), is decorated's too.

Link copied to clipboard
class DevelopmentCallerResolver(defaultTenant: String = Caller.DEFAULT_TENANT) : CallerResolver

The host's callers in a development profile (a profile of wow.dev-profiles), when the application has no CallerResolver and CoSec is off: the caller the request's identity headers (DevelopmentHeaders) name, so a service runs and is tried out at every caller level without an identity provider. Any client sets these headers, so the host wires it only when a development profile is active: outside one, it never does.

Link copied to clipboard
class DevelopmentMode(val reason: String?, val profiles: List<String> = emptyList())

Whether the node runs for development: a profile of wow.dev-profiles (dev, local, test, demo) is active, or its internal port binds a loopback address alone. It is the one exemption the host's safeguards share, so a laptop starts as it is and a deployment cannot: wow.backend=auto falls back to memory, the public port serves without a CallerResolver bean, and, in a development profile alone (profiles), takes its callers from request headers. reason says why, null outside development.

Link copied to clipboard
class DnsPeerDiscovery(lookup: SrvLookup = JndiSrvLookup(), namespaceFile: Path = SERVICE_ACCOUNT_NAMESPACE) : PeerDiscovery

wow.peer.discovery=dns, the default: a peer service's nodes are the targets of the DNS SRV records name-template names after it, _internal._tcp.{service}-cluster.{namespace}.svc by default, each http://<target>:<port>. Kubernetes publishes one such record per pod behind a headless Service, whose targets resolve to the pods' IPs, the addresses their nodes advertise. A peer's {namespace} is its namespaces.<service>, else the node's own: namespace, else that of the pod's service account, from namespaceFile.

Link copied to clipboard
class EventQueryBackend(store: (AggregateType) -> String? = { null }, prepare: (AggregateType) -> Unit = {}, indexes: (AggregateType, QueryIndexPlan) -> QueryIndexReport? = null, manages: (AggregateType) -> Boolean = { true }, val bindings: QueryBindings<AggregateType>)

The events query backend of the node: the bindings of each aggregate type's events backend and storage facts, none for a type no backend serves (its events are then not queried), and the store its queries run on, null for the application's own backend. For a store whose events queries manage indexes (me.ahoo.wow.query.store.EventQueries), how it prepares a type's log and manages the indexes of its access attributes at startup. It is not itself QueryBindings, so that it is never taken for the application's snapshot bindings bean.

Link copied to clipboard

How the node executes commands.

Link copied to clipboard
class GracefulShutdownSettled @JvmOverloads constructor(timeout: Duration = DEFAULT_TIMEOUT, threads: () -> Collection<Thread> = { Thread.getAllStackTraces().keys }) : SmartLifecycle

Waits, between the public server's graceful shutdown and its stop, for the thread of that shutdown to end, at most timeout: a workaround of a race in Spring Boot 4.1.1 (spring-boot-reactor-netty, and 3.5.x before it), to remove once the host builds on a Spring Boot that fixes it (below).

Link copied to clipboard
@AutoConfiguration(after = [WowAutoConfiguration::class])
@ConditionalOnClass(name = ["org.springframework.boot.health.contributor.HealthIndicator"])
@ConditionalOnBooleanProperty(name = ["wow.health.enabled"], matchIfMissing = true)
class HealthAutoConfiguration

The node's health in Spring Boot Actuator, when the application has Actuator's health support (spring-boot-starter-actuator), unless wow.health.enabled=false (HealthProperties):

Link copied to clipboard

The node's command executors.

Link copied to clipboard

Whether the node is stuck, so a restart helps; a storage outage never fails it.

Link copied to clipboard

The node's share of each lease group's partitions.

Link copied to clipboard

The node's health (me.ahoo.wow.runtime.health.NodeHealth): with Spring Boot Actuator on the class path, the contributor wow (every component), and wowReadiness and wowLiveness, which the host adds to Actuator's readiness and liveness groups (/actuator/health/readiness, /actuator/health/liveness) unless the application configures their members itself. The thresholds are conservative: a probe that fails too soon takes nodes out of service, or restarts them, for what a retry would have absorbed.

Link copied to clipboard

The node's event logs, as its own calls and a probe of their heads see them.

Link copied to clipboard

The node's subscriptions, never a reason to take it out of service.

Link copied to clipboard

The topics of other contexts the node reads.

Link copied to clipboard
class HostBackends @InternalWowApi constructor(val type: String?, shared: List<() -> Unit> = emptyList(), val stores: Map<String, Store> = emptyMap(), val layout: StoreLayout? = null, val local: Boolean = type == StoreTypes.MEMORY, open: (context: String, schemas: MutableList<() -> Unit>) -> ContextBackends) : AutoCloseable

The backends of the node: the BackendFactory the me.ahoo.wow.runtime.node.ServiceNode opens each context's stores from, remembering what it opened, so that the state reads find a context's stores and the backend lifecycle (WowLifecycles.BACKENDS_PHASE) creates their schemas before the node starts.

Link copied to clipboard

The node's dataset bindings: the application's, or a dataset backend's (provided); null when no backend serves datasets. Closed with the context, they close a dataset backend's bindings that hold resources of their own (a client it opened), never the application's.

Link copied to clipboard
class HostQueryNode(nodes: ObjectProvider<ServiceNode>) : SmartInitializingSingleton

The node whose write pressure the stores' query bounds adapt to (HostQueryBulkheads.pressureOf), taken once the context has built every singleton. The bounds cannot take the node as a parameter: the node is built with the gate its state rebuilds replay under (me.ahoo.wow.runtime.state.ReplayGate), which is built over them.

Link copied to clipboard

The HTTP edge: its routes and its error answers.

Link copied to clipboard
@AutoConfiguration
@EnableConfigurationProperties(value = [WowProperties::class])
class InMemoryTopicsAutoConfiguration(environment: Environment)

The node's topics in its JVM, for trying a service out without Kafka (DX gap G27), when wow.transport selects them (TransportSelection.MEMORY: memory, or auto in development without a configured transport): one InMemoryTopics of wow.log.partitions partitions, each keeping its last wow.memory.topic-retention messages, unless the application has one of its own, as wow-kafka's beans give them on Kafka:

Link copied to clipboard
Link copied to clipboard
class InternalServerLifecycle(server: InternalPort, bound: (port: Int) -> Unit = {}) : SmartLifecycle

Serves the internal port from before the node starts until after it stops; once the port is bound, tells bound the port it is bound at, so a node whose port the system chose learns its address (ServiceNode.bind).

Link copied to clipboard
class JndiSrvLookup(resolvConf: Path = RESOLV_CONF, query: (name: String) -> List<SrvRecord> = Companion::records) : SrvLookup

The SRV lookup of the JDK's DNS provider (JNDI), with the search domains of the node's resolver: a name without a trailing dot is tried under each search domain of resolvConf and as given, the first that has records answering, as given first when it has at least the resolver's ndots dots (options ndots:n, 1 by default; Kubernetes gives its pods 5); a name with one is absolute. resolvConf is read once; it is absent on a developer's machine, where a name is tried as given alone.

Link copied to clipboard
Link copied to clipboard

The in-memory provider's settings, wow.memory.*, as each provider has its group (wow.kafka.*, wow.mongo.*).

Link copied to clipboard
@AutoConfiguration(before = [WowAutoConfiguration::class])
@ConditionalOnClass(name = ["me.ahoo.wow.mongo.MongoStoreProvider"])
@ConditionalOnMissingClass(value = ["org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration"])
@ConditionalOnProperty(value = ["spring.mongodb.uri"])
class MongoClientAutoConfiguration

The application's MongoDB client, from its properties alone: when wow-mongo is on the class path, spring.mongodb.uri is set, the application has no MongoClient bean and no Spring Boot MongoDB (spring-boot-mongodb, whose own auto-configuration then makes it), the host makes a MongoClient of the URI; closed with the context. The MongoDB stores then open over it, in the database their database option names.

Link copied to clipboard
@ConditionalOnClass(name = ["me.ahoo.wow.mongo.MongoStoreProvider"])
@Import(value = [BootMongoPoolGauge::class])
class MongoPoolAutoConfiguration

The pool gauge of the MongoDB stores (MongoPoolGauge), when the application has wow-mongo: added to the MongoClient the host makes (MongoClientAutoConfiguration) or Spring Boot makes (through its settings customizer), so the stores bound their queries by the client's pool and read their write pressure from it. An application that builds its own client adds the gauge itself (MongoPoolGauge.applyTo).

Link copied to clipboard
data class NodeIdentity(val service: String, val node: String, val address: String?, val host: String, val bindHost: String)

Who this node is: the service it belongs to, its node id and the address of its internal port, null while the port is not bound yet when the system chooses it (wow.internal.port=0): the node then learns it as host and the bound port (ServiceNode.bind).

Link copied to clipboard
Link copied to clipboard

The node's one sampler (NodeSampler), every wow.node.sample-interval, its parked entries counted up to wow.node.max-parked: the node's health, its gauges and the operator's read of its subscriptions all read its samples, so the ledgers and the logs' heads are read once per interval, whoever reports them.

Link copied to clipboard

The standby of node as the internal port's operator routes see it (wow.node.standby).

Link copied to clipboard
@AutoConfiguration(after = [WowAutoConfiguration::class])
@ConditionalOnClass(name = ["me.ahoo.wow.observability.OtelObserver"])
@ConditionalOnProperty(prefix = "wow.observability", name = ["enabled"], matchIfMissing = true)
@EnableConfigurationProperties(value = [ObservabilityProperties::class])
class ObservabilityAutoConfiguration

Tracing and metrics (wow-observability), active when the application has wow-observability on its classpath, unless wow.observability.enabled=false (ObservabilityProperties).

Link copied to clipboard
@ConfigurationProperties(prefix = "wow.observability")
class ObservabilityProperties

Observability's configuration, wow.observability.*, read when wow-observability is on the classpath (ObservabilityAutoConfiguration).

Link copied to clipboard

The operator's side of the parked entries: the webhook told when an entry parks, and the failure ledger published to the ops context. Their routes are the edge's, wow.http.ops.enabled.

Link copied to clipboard

The automatic retries of parked entries over minutes and hours; per subscription, wow.subscriptions.[<context>.<name>].parked.* overrides them.

Link copied to clipboard

One subscription's retry spec of its parked entries, each setting not given being wow.subscription.parked's.

Link copied to clipboard

How the node calls its peers over HTTP, whichever peer: one HTTP client, these settings.

Link copied to clipboard

How a problem a retry may get past is retried (me.ahoo.wow.client.RetryPolicy): a 429, a command's problem that keeps the request id, or a connection that could not be made.

Link copied to clipboard

One peer context: its internal port's base url (service mode), http://host:port of an address in its internal network, never its public port, which refuses calls; its public routes' base publicUrl (user mode); the mode it is called in, over wow.peer.mode; where its contract comes from (jar, or manifest for a peer called by its Manifest alone); and its timeout, over wow.peer.timeout.

Link copied to clipboard

The Sinks the node's providers opened, each its subscriptions' factory; closed with the context.

Link copied to clipboard

Publishing the failure ledger to the ops context, so one console sees the parked entries of every service: each entry goes to the parked aggregate of the ops context through its command topic, which needs a topic transport.

Link copied to clipboard
@AutoConfiguration(after = [WowAutoConfiguration::class])
@ConditionalOnClass(name = ["me.ahoo.wow.query.QueryModule"])
@ConditionalOnProperty(prefix = "wow.query", name = ["enabled"], matchIfMissing = true)
@EnableConfigurationProperties(value = [QueryProperties::class])
class QueryAutoConfiguration

The query plane (–), active when wow-query is on the classpath (wow-http brings it) and wow.query.enabled is not off. Over the node's Manifest it builds:

Link copied to clipboard

The application's beans a QueryEngine is built of: each it may give, or not.

Link copied to clipboard

The events queries of a store or an aggregate type: unpinned, whether one that names no aggregate runs, over the store's and wow.query.events.unpinned.

Link copied to clipboard
class QueryIndexesLifecycle(manifest: Manifest, types: List<AggregateType>, backend: SnapshotQueryBackend, catalog: QuerySchemaCatalog, events: EventQueryBackend = EventQueryBackend.NONE, eventTypes: List<AggregateType> = emptyList(), ddlKeyOf: (store: String?) -> String = { DDL }) : SmartLifecycle

Manages the indexes of every aggregate type in types at startup, after the backends' schemas exist (BackendsLifecycle) and before the node starts: each aggregate's storage is prepared, its query model compiled from catalog, and its Manifest's x-wow-index planned and managed by backend as the ddl of the store it is on says (wow.stores.<store>.ddl, else wow.ddl). A key the model cannot index fails the start, and so does an index that validate finds missing or invalid: a query it would serve scans the whole collection.

Link copied to clipboard
@ConfigurationProperties(prefix = "wow.query")
class QueryProperties

The query plane's configuration, wow.query.*, read by QueryAutoConfiguration: the query routes and gateways, the budgets they run under, who may query what, and how many queries each store runs at once.

Link copied to clipboard
class QueryStreamsLifecycle(streams: QueryStreams) : SmartLifecycle

Ends every open query stream of the public port with end {"reason":"node-stopping","resumable":true}, and refuses new ones, as the node starts to stop: its clients resume them on another node.

Link copied to clipboard

How a failing read is retried, never giving up: the wait grows from initial-backoff by multiplier at each failure, up to max-backoff; past error-after attempts each failure is logged as an error.

Link copied to clipboard

A retry policy: the wait grows from initial-backoff by multiplier at each failure, up to max-backoff.

Link copied to clipboard

One subscription's in-process retry budget: each setting given replaces its handler's @Retry one, each one not given is the handler's, else wow.subscription.retry's. timeout bounds one attempt: one that runs longer is cancelled and counts as failed.

Link copied to clipboard
class RoutesScope : SmartLifecycle

The scope the HTTP routes, public and internal, produce their streamed answers in, apart from the requests: cancelled once the node and the internal port have stopped, so a stream still open then ends instead of outliving the node.

Link copied to clipboard

An @OnSchedule job's settings, the only place it is configured: its rule, the cron, the time zone and the misfire policy, each unset to keep the annotation's (a tenant's own rule of a TENANT job comes before it); enabled false disables a SYSTEM or a SHARDED job's instances at startup; and the retry and parked retries of the subscriptions that run it, over its handler's @Retry and wow.subscription.retry, and over wow.subscription.parked.

Link copied to clipboard
class ServiceNodeLifecycle(node: ServiceNode, events: ApplicationEventPublisher, standby: Boolean = false, availability: () -> ApplicationAvailability?) : SmartLifecycle

Starts the node once its backends and internal port are up, and stops it once the public server has stopped: a stop reports the application not ready first, when nothing did (a host without a public server), then runs ServiceNode.stop apart from the thread that closes the context, which waits for it at most the phase's timeout.

Link copied to clipboard
fun interface ServiceNodeListener

Hears each step of ServiceNode.stop once it is done, in order: a bean of the application's.

Link copied to clipboard

The bounds of each context's shared states, the states its consumers share after each commit.

Link copied to clipboard

Requires a signed-in caller: decorated's caller without a subject, the anonymous one, is no caller, so the edge answers the request 401 on every route, as CoSec's resolver has it answered. For a service whose every route needs a user, in development too, where the host's callers (DevelopmentCallerResolver) serve a request without Wow-User as the anonymous caller.

Link copied to clipboard
@AutoConfiguration(after = [TransportAutoConfiguration::class])
@EnableConfigurationProperties(value = [WowProperties::class])
class SinkAutoConfiguration

The Sinks of the streams the service publishes: those the me.ahoo.wow.spi.sink providers on the classpath or among the application's beans find configured (PortProviders), the ClickHouse Sink by wow.clickhouse.sink.enabled=true alone, over the application's ClickHouse client when it has one, else one of wow.clickhouse.endpoint. Each is one subscription per published stream, named <processor>.<aggregate>, which the node takes with its subscription factories (ProvidedSinks).

Link copied to clipboard

Where the snapshots are, and how the snapshot maintenance runs. Snapshots may be written to several stores; a store added to them is backfilled from the log: switch the primary to it once it caught up, then drop the old one.

Link copied to clipboard
class SnapshotQueryBackend(val bindings: QueryBindings<AggregateType>?, prepare: (AggregateType) -> Unit = {}, indexes: (AggregateType, QueryIndexPlan) -> QueryIndexReport? = null, serves: (AggregateType) -> Boolean = { bindings != null }, store: (AggregateType) -> String? = { null })

The snapshot query backend of the node: the bindings of each aggregate type's backend and storage facts (null when no backend serves the node's snapshots), and, for a backend the host manages, how it prepares an aggregate's storage and manages its x-wow-index indexes and those of its access attributes at startup.

Link copied to clipboard

How an in-place rebuild of the snapshots rewrites them (wow.snapshot.rebuild.*).

Link copied to clipboard
@AutoConfiguration(after = [WowAutoConfiguration::class], beforeName = ["org.springdoc.core.configuration.SpringDocConfiguration"])
@ConditionalOnClass(name = ["org.springdoc.core.customizers.OpenApiCustomizer", "io.swagger.v3.core.util.Json31"])
@ConditionalOnBean(value = [ServiceNode::class])
@ConditionalOnProperty(prefix = "wow.http.openapi.springdoc", name = ["enabled"], matchIfMissing = true)
class SpringdocAutoConfiguration

The springdoc bridge (gap): with springdoc on the classpath, the OpenAPI document of the public routes, the one GET /wow/openapi answers (wowOpenApi), joins springdoc's, so that /v3/api-docs and Swagger UI list the application's own controllers and the Wow routes together.

Link copied to clipboard

The Wow document as plain data, read into swagger-core's model for each springdoc document.

Link copied to clipboard

A DNS SRV lookup: the records of name, as a resolver finds them.

Link copied to clipboard
data class SrvRecord(val priority: Int, val weight: Int, val port: Int, val target: String)

One SRV record: its priority, weight, port and target, the host name of a node.

Link copied to clipboard

The cache of aggregate states each context keeps between commands.

Link copied to clipboard

How one store's query bound adapts to its write pressure (wow.stores.<name>.query.adaptive.*): floor and ceiling over wow.query.adaptive.*.

Link copied to clipboard

A named store instance (wow.stores.<name>): its type, memory, postgres, mongo or a community backend's (the type of its store provider), how it manages the framework's schema (ddl) and how its queries run (query). Every other key of the store is a setting its provider declares, at the same level (wow.stores.<name>.data-source, wow.stores.<name>.client): the host reserves RESERVED alone, and a key no provider of the store's type declares fails the start, naming the ones it does. Two stores of one type may serve different databases; two log stores may not share one.

Link copied to clipboard

The store providers of the node, by type: the memory provider built into the host, those on the classpath (ServiceLoader: the built-in PostgreSQL and MongoDB ones, a community backend's), and the application's StoreProvider beans, each taking the place of the one of its type before it. A store's queries are its own facet (me.ahoo.wow.query.store.StoreQueries).

Link copied to clipboard

How one store's queries run (wow.stores.<name>.query.*): maxConcurrent, how many run at once, over wow.query.max-concurrent (beyond it a query is wow.query.busy, 429; 0 for no bound); maxWait and maxQueued, how long and how many queries beyond it wait for a permit, over wow.query.max-wait and wow.query.max-queued; events, its events queries; adaptive, how its bound adapts to its write pressure, over wow.query.adaptive.*.

Link copied to clipboard

How the node's subscriptions run, retry their failures and park what keeps failing.

Link copied to clipboard

One subscription's settings. start is where it starts in a partition it has no checkpoint in yet: beginning, end, or an ISO-8601 instant (2026-10-01T00:00:00Z), the first entry committed at or after it; unset, its kind's default (a projection from the beginning, a reactor or a saga from the end). A subscription that has a checkpoint goes on from it. parked is how its parked entries are retried, over wow.subscription.parked. retry is its in-process retry budget before it parks, over its handler's @Retry and wow.subscription.retry. A name the node has no subscription of is refused at startup, and so is the subscription of an @OnSchedule job, whose settings are its job's, wow.schedules.<job>.*.

Link copied to clipboard

When a subscription pauses its partition on a systemic failure: at once on a failure classified systemic, or when enough different aggregates failed within a window with no handling succeeding since. While paused, one attempt at a time probes until a handling succeeds. A handler's failures are classified by its @Retry(recoverable, unrecoverable), then the FailureClassifier beans, then the modules' on the classpath, then the runtime's own.

Link copied to clipboard

The tenants of the callers.

Link copied to clipboard

The node's firing loop of the timers, which sends the scheduled commands when they are due.

Link copied to clipboard

The node's topic transport: the one a TransportProvider gives, found on the classpath or among the application's beans (PortProviders), configured by its keys (wow.kafka.* for Wow's Kafka transport). The host builds nothing of a broker's own: it takes the transport's ports.

Link copied to clipboard

What needs a topic transport in a node without one (TransportSelection.NONE), in words: its aggregate types routed through Kafka (wow.aggregates.[..].routing=topic), its failure ledger published to the ops context (wow.ops.publish.enabled), and the command intake (a transport's command-intake.enabled, intake). A sourced subscription or a saga's commands to another context need it too, which the node itself refuses when it is built.

Link copied to clipboard

The node's topic transport, as wow.transport selects it (derived, the key an override):

Link copied to clipboard

How long and how far the commands' waits go.

Link copied to clipboard

The webhook of the parked entries, told when an entry parks or spends its retries.

Link copied to clipboard
@AutoConfiguration(afterName = ["org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration", "org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration", "org.springframework.boot.validation.autoconfigure.ValidationAutoConfiguration"])
@EnableConfigurationProperties(value = [WowProperties::class])
class WowAutoConfiguration

The Wow host (.3; –): by what the classpath and the application's beans offer, it assembles one ServiceNode and serves it on two ports.

Link copied to clipboard
class WowModels(val providers: List<ModelProvider>, val models: List<AggregateModel<*>>)

The aggregate models the node serves and the providers they come from (the binding index), with their factories' parameters from the application's beans.

Link copied to clipboard

How a caller of the public routes presents its identity, as the springdoc document declares it: the document's security schemes, by name, and its global security, one requirement naming them all, since a caller presents them together, so that Swagger UI's "Try it out" sends the identity.

Link copied to clipboard
@ConfigurationProperties(prefix = "wow")
class WowProperties

The host's configuration, the wow.* namespace: the node, its internal port, its stores and logs, the cluster, the commands' waits and execution, the subscriptions, the timers and schedules, the HTTP edge and the calls to other contexts. The defaults are the runtime's own. The query plane, Kafka, ClickHouse and OpenTelemetry have their own classes (QueryProperties, KafkaProperties, ClickHouseProperties, ObservabilityProperties).