V Platform Features

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 defaults nor 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 $NAME reference, 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 with toa 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
configurationThe values. Secrets appear as their references.
schemaThe schema from the manifest.
epochIdentifies the schema; see After a schema change.
createdWhen the object was created; 0 for deployed values that nobody replaced.
revisionIdentifies 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

MethodPathRole
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:

OperationInputReply
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.
Search
Search the documentation