Change a Setting without Redeploying
The shop raises the order limit for a holiday sale and wants it back to normal on Monday. Nobody should build and deploy for that. A component’s configuration can be replaced while the application runs: over HTTP, in a page the platform serves, or from an operation of your own.
§Change a value
Values are held by a component the platform brings, configuration.values, exposed through the gateway. Creating a configuration for store.orders, whose limit Add Configuration declares, is one request:
POST /configuration/values/store.orders/ HTTP/1.1
authorization: Token ...
content-type: application/yaml
configuration:
limit: 20000 The answer carries the id of the object created and the epoch it belongs to:
id: 7d8e…
epoch: 3f2a… Every running replica of store.orders receives the new values and uses them from then on; a
replica that starts later gets them too.
Three rules:
- What you send is the whole configuration. Nothing fills in a value you leave out, neither
the manifest’s
defaultsnor the previous object. Send every value the component needs. - It must fit the component’s schema. A value that does not, or an unknown component, is answered
422. - A secret is sent as its
$NAMEreference, exactly as in the Context; a plain string in its place is refused. The value behind the name does not change this way: it is whatever was deployed withtoa conceal.
§Try it locally
The gateway and the values service are part of sys, one of the two processes npm start keeps under PM2. Send the request above to http://localhost:8000, as an identity that has the role system:configuration:create (Roles), then create an order above the old limit and see it accepted.
§Read what a component runs with
GET /configuration/values/store.orders/ HTTP/1.1
authorization: Token ... configuration:
limit: 20000
schema:
type: object
properties:
limit: { type: integer, minimum: 0 }
required: [limit]
epoch: 3f2a…
created: 1767225600000
revision: null | Field | |
|---|---|
configuration | The values. Secrets appear as their references. |
schema | The schema from the manifest. |
epoch | Identifies the schema; see After a schema change. |
created | When the object was created; 0 for deployed values that nobody replaced. |
revision | Identifies the deployed values when they are what is served; null for a created object. |
A component without configuration is answered 404.
GET /configuration/values/ lists every component that declares configuration, ordered by
component name, each with the fields above and its component id.
§Go back to the deployed values
DELETE /configuration/values/store.orders/ HTTP/1.1
authorization: Token ... answers 204. The component runs with the values it was deployed with again, and follows them
from now on: a later deployment that changes them in the Context changes what the component runs
with.
An unknown component is answered 422.
§Who may do it
| Method | Path | Role |
|---|---|---|
GET | /configuration/values/ | system:configuration:get |
GET | /configuration/values/:component/ | system:configuration:get |
POST | /configuration/values/:component/ | system:configuration:create |
DELETE | /configuration/values/:component/ | system:configuration:create |
POST and DELETE record the identity that made the request as the originator of the change.
Granting roles is covered in Roles.
§Use the page
The values service serves a page that lists the configured components, creates configurations
and resets them. It is published at /.configuration/ on the hosts the Context declares for
ingress, and listens on port 8003:
# context.toa.yaml
ingress:
hosts:
- api.example.com https://api.example.com/.configuration/ The host must be one the gateway serves, because the page reads the API above on its own origin.
Reading needs the system:configuration:get role; creating and resetting need system:configuration:create. A component running on a created configuration is marked in the
list, and reset is offered only for those.
The page is always published; the annotation has no switch for it.
§Change it from an operation
configuration.values is an ordinary component, so an operation of yours can call it. A back-office operation added to store.orders that adjusts the
limit:
# components/store.orders/manifest.toa.yaml
operations:
limit:
input:
type: object
properties:
limit: { type: integer, minimum: 0 }
operator: { type: string }
required: [limit, operator]
errors: [UNKNOWN_COMPONENT, INVALID_CONFIGURATION] // components/store.orders/operations/limit.ts
import type { Context, LimitInput } from '../types/index.d.ts'
export async function effect (input: LimitInput, context: Context) {
const reply = await context.remote.configuration.values.create({
input: {
component: 'store.orders',
configuration: { limit: input.limit },
originator: { id: input.operator }
}
})
if (reply instanceof Error) return reply
} $ npm run restart
$ npx toa call store.orders.limit "{ input: { limit: 20000, operator: 'me' } }" Its operations:
| Operation | Input | Reply |
|---|---|---|
get | { component, epoch? } | { configuration, schema, epoch, created, revision }, or null when there is none |
fetch | [{ component, epoch }] | [{ component, epoch, configuration, created, revision }] |
list | — | [{ component, epoch, schema, configuration, created, revision }] |
create | { component, configuration, originator } | the stored object |
reset | { component, originator } | the stored object, holding the deployed values and their revision |
create answers the errors UNKNOWN_COMPONENT and INVALID_CONFIGURATION; reset answers UNKNOWN_COMPONENT. originator is an object with the id of whoever makes the change.
get without epoch reads the deployed one. Asked for an epoch the deployment does not know, it
answers without a schema.
§Keep a record of changes
Creating and resetting both publish the event configuration.values.created, with the stored
object as its payload. The object of a reset carries a revision; a created one does not. Receive it like any other event, for an audit trail for example, in a
component of your own whose record operation writes the trail:
# components/audit/manifest.toa.yaml
receivers:
configuration.values.created: record Nothing is edited in place: each change is a new object recording who made it, and earlier objects stay.
§When you deploy again
- A created configuration outlives deployments. While a component has one, changing its values in the Context has no visible effect. Reset the component to make it follow the Context again.
- A component on its deployed values follows every deployment, including one that changes only a value in the Context: such a deployment replaces the component’s processes, as a change to its code does.
During a rolling deployment a component may log a warning, on the first occurrence and every fifth after:
Configuration of another revision refused with the component, its epoch, and the expected and received revisions. It is waiting for
the values service of its own release and stops by itself when the rollout completes. A component
that keeps logging it has a values service that was not deployed with it.
A component started outside a deployment, by toa compose for example, takes whatever it is
served.
§After a schema change
Every created configuration belongs to the schema it was checked against. The schema’s identity
is its epoch; changing anything in configuration.schema makes a new one.
After a deployment that changes the schema:
- the component starts from its deployed values for the new schema;
- configurations created for the old schema are not applied, and are not migrated.
So a change made while running has to be made again after a schema change if it is still wanted. Put the values you want to keep into the Context before deploying.
§Change values safely
Replicas receive a change one after another, not at one instant, and work that spans several components can run partly with the old values and partly with the new. So:
- make each new set of values compatible with operations already in progress;
- when a change needs new code, deploy code that works with both the old and the new values, then change the values.