Kungfuig behaviour (Kungfuig v1.0.1)
View SourceKungfuig
provides a pluggable drop-in support for live configurations with change notification capabilities.
Overview
Kungfuig (pronounced: [ˌkʌŋˈfig]) is a comprehensive configuration management system for Elixir applications that provides:
- Live configuration updates from multiple sources
- Callback notifications when configurations change
- Configuration validation
- Extensible backend system for custom configuration sources
- Supervisor-managed configuration processes
This behaviour defines the dynamic config provider. In most cases, you don't need to implement
this behaviour directly. Instead, implement one or more backends (see Kungfuig.Backend
)
and pass them, optionally parametrized, as workers:
to start_link/1
as shown below:
{:ok, pid} =
Kungfuig.start_link(
workers: [
{Kungfuig.Backends.EnvTransform,
interval: 100,
validator: Kungfuig.Validators.EnvTransform,
callback: {self(), {:info, :updated}},
callback: Kungfuig.Test.CallbackAlert}
]
)
By default, Kungfuig
starts two backends:
- Application environment backend (
Application.get_all_env/1
) - System environment backend (for environment variables)
Architecture
Kungfuig operates using a supervisor tree pattern:
- A supervisor (
Kungfuig.Supervisor
) manages the configuration process - A manager process (
Kungfuig.Manager
) supervises the backend workers - Backend workers (
Kungfuig.Backend
implementations) poll their respective configuration sources - A blender process (
Kungfuig.Blender
) combines configurations from all backends - Callbacks are triggered when configurations change
Custom Configuration Sources
If you need to get configuration updates from sources other than the default ones,
such as JSON/YAML files, Redis, databases, or any external service, you can create
your own backends by implementing the Kungfuig.Backend
behaviour.
See Kungfuig.Backend
for reference or check the
Usage section in the README for an example
using MySQL.
Validation
Since v0.3.0, Kungfuig supports validation through the Kungfuig.Validator
behaviour,
which by default uses NimbleOptions
for schema validation. Validators can be specified:
- Per backend (applying only to that backend's configuration)
- Globally (applying to the entire configuration)
Callbacks
When a configuration changes, Kungfuig notifies interested parties through callbacks.
Multiple callback mechanisms are supported, as detailed in the callback/0
type.
Named Instances
Since v0.4.0, Kungfuig supports multiple named instances, allowing different parts of your application to use separate configuration managers.
Examples
Basic usage:
# Start Kungfuig with default options
Kungfuig.start_link()
# Get all configuration
Kungfuig.config()
#⇒ %{env: %{kungfuig: []}, system: %{}}
# Get configuration from a specific backend
Kungfuig.config(:env)
#⇒ %{kungfuig: []}
# Update application environment and see the change
Application.put_env(:kungfuig, :foo, 42)
Kungfuig.config(:env)
#⇒ %{kungfuig: [foo: 42]}
Using callbacks:
# Using a process message as callback
Kungfuig.start_link(
workers: [
{Kungfuig.Backends.Env, callback: {self(), {:info, :config_updated}}}
]
)
# Using a module that implements Kungfuig.Callback
defmodule MyApp.ConfigHandler do
@behaviour Kungfuig.Callback
@impl true
def handle_config_update(config) do
IO.puts("Configuration updated: " <> inspect(config))
end
end
Kungfuig.start_link(
workers: [
{Kungfuig.Backends.Env, callback: MyApp.ConfigHandler}
]
)
Using named instances (since v0.4.0):
# Start a named Kungfuig instance
Kungfuig.start_link(name: MyApp.Config)
# Query the named instance
Kungfuig.config(:env, MyApp.Config)
Using immediate validation (since v0.4.2):
# Start with imminent validation for immediate config processing
Kungfuig.start_link(
workers: [
{Kungfuig.Backends.Env, imminent: true, validator: MyApp.ConfigValidator}
]
)
Summary
Types
The callback to be used for subscribing to configuration updates.
The config map to be updated and exposed through callbacks.
The options that can be passed to start_link/1
function.
Types
@type callback() :: module() | pid() | {module(), atom()} | (config() -> :ok) | {GenServer.name() | pid(), {:call | :cast | :info, atom()}}
The callback to be used for subscribing to configuration updates.
Kungfuig supports several callback mechanisms:
Module implementing
Kungfuig.Callback
behaviour:defmodule MyApp.ConfigHandler do @behaviour Kungfuig.Callback @impl true def handle_config_update(config) do # Handle configuration update :ok end end
Process identifier (PID): The process will receive a
{:kungfuig_update, config}
message# In a GenServer def handle_info({:kungfuig_update, config}, state) do # Handle configuration update {:noreply, state} end
Module-function tuple: A tuple
{module, function}
where the function accepts a single argument (the configuration)defmodule MyApp.ConfigHandler do def on_config_update(config) do # Handle configuration update end end # In your Kungfuig setup callback: {MyApp.ConfigHandler, :on_config_update}
Anonymous function: A function that takes one argument (the configuration)
callback: fn config -> # Handle configuration update :ok end
GenServer interaction tuple: A tuple specifying a GenServer and how to interact with it
# Send a cast to a GenServer callback: {MyGenServer, {:cast, :config_updated}} # Inside the GenServer def handle_cast({:config_updated, config}, state) do # Handle configuration update {:noreply, state} end
The config map to be updated and exposed through callbacks.
This is a map where the keys are atoms (typically representing backend names) and the values are the configuration data from those backends.
@type option() :: {:name, GenServer.name()} | {:callback, callback()} | {:interval, non_neg_integer()} | {:imminent, boolean()} | {:anonymous, boolean()} | {:start_options, GenServer.options()} | {:validator, module()} | {:workers, [module() | {module(), keyword()}]}
The options that can be passed to start_link/1
function.
Options include:
:name
- The name to register the Kungfuig instance under. Default isKungfuig
. @since v0.4.0:callback
- One or more callbacks to notify when configuration changes. Seecallback/0
.:interval
- How frequently to check for configuration updates, in milliseconds. Default is 1000 (1 second).:imminent
- Whentrue
, performs configuration validation immediately during initialization, rather than as a continuation after init returns. Default isfalse
.:anonymous
- Whentrue
, theGenServer
is not registered under a name. Default isfalse
.:start_options
- Options passed toGenServer.start_link/3
.:validator
- A module implementing theKungfuig.Validator
behaviour to validate the configuration. Default isKungfuig.Validators.Void
(no validation). @since v0.3.0:workers
- A list of backend specifications, each either a module name or a{module, opts}
tuple. It might also be a function of arity
Functions
@spec config(which :: atom() | [atom()], supervisor :: Supervisor.supervisor()) :: config()
Retrieves the current config for the key(s) specified
The function to start the Kungfuig
manager under a supervision tree with default options
The function to start the Kungfuig
manager under a supervision tree with custom options