Principle:Vespa engine Vespa Configuration Application
| Metadata | |
|---|---|
| Sources | Vespa |
| Domains | Configuration, Distributed_Systems |
| Last Updated | 2026-02-09 12:00 GMT |
Overview
Configuration application is the typed deserialization of configuration data into a strongly-typed config object, providing type-safe access to config snapshots through the handle pattern.
Description
Once a configuration generation has been successfully fetched (via nextConfig() or nextGeneration()), the actual configuration data must be extracted and applied by the consuming process. This is the configuration application step, where raw configuration payloads are deserialized into strongly-typed C++ objects.
Vespa implements this through the ConfigHandle pattern. Each ConfigHandle<ConfigType> provides a getConfig() method that returns a std::unique_ptr<ConfigType> -- a fully typed configuration object generated from a .def file.
The handle pattern provides several important guarantees:
Type Safety: The template parameter ConfigType ensures that the deserialized configuration matches the expected schema at compile time. There is no runtime casting or type checking required by the caller.
Snapshot Semantics: Each call to getConfig() returns a new unique_ptr to a config object representing the state at the time of the last successful nextConfig()/nextGeneration() call. The returned object is an independent snapshot that remains valid even if the subscriber fetches a newer generation.
Precondition Enforcement: The handle throws a ConfigRuntimeException if getConfig() is called before the subscriber has been polled (i.e., before nextConfig() has been called). It also throws an InvalidConfigException if the config payload cannot be parsed into the expected type. This fail-fast behavior prevents processes from operating with invalid or uninitialized configuration.
Change Detection: The handle also provides an isChanged() method that indicates whether this particular config changed in the most recent generation. This allows processes to skip expensive reconfiguration when only unrelated configs have changed.
Usage
Use this principle after a successful call to nextConfig() or nextGeneration() to extract the actual configuration values. Configuration application is performed for each subscribed config type individually through its respective handle.
Common patterns include:
- Applying configuration at startup after the initial fetch
- Selectively reconfiguring components based on
isChanged()in a polling loop - Storing config snapshots for later comparison or auditing
Theoretical Basis
The handle pattern implements a typed accessor over an opaque data channel. The underlying ConfigSubscription holds raw configuration values (as ConfigValue objects containing serialized payload), while the ConfigHandle provides a typed lens:
- getConfig : ConfigSubscription -> ConfigType
This separation of concerns follows the Data Transfer Object pattern, where the transport layer deals with generic payloads and the application layer works with domain-specific types.
The unique_ptr return type enforces ownership transfer semantics: the caller owns the config object and is responsible for its lifetime. This prevents shared mutable state between the subscription system and the application logic.
The fail-fast exception semantics implement a design by contract approach:
- Precondition: The subscriber must have been polled at least once
- Postcondition: The returned object is a valid, fully populated config instance
- Invariant: The config handle always refers to the same subscription