See What Is Deployed and How Components Talk
A new developer joins and asks which components the shop has, which events they publish and who calls whom. The running application answers that itself: it collects its topology and serves it as a page and as an HTTP API. Every component takes part without a declaration. What the topology is for is in the model article Introspection.
§Look at it
With the gateway running, the topology is published in two forms on the application’s hosts:
- a page at
/.introspection/, which draws the graph; - an HTTP API under
/introspection/, which the page reads and which you can query.
Both need the system:introspection role (Roles).
Locally, the gateway and the service that collects the topology are part of sys, one of the
two processes npm start keeps under PM2. Add introspection to the Context to shorten the interval at which
observed calls are reported, and restart:
# context.toa.yaml
introspection:
interval@local: 5 $ npm run restart GET /introspection/nodes/ HTTP/1.1
host: localhost:8000
authorization: Token ...
accept: application/yaml - id: f5f5f72d2495c71128a41f810a199452
namespace: store
component: orders
version: 7bb04bba Call an operation that calls another. Where approve of store.orders debits the customer’s wallet, approve an
order, and within the interval GET /introspection/edges/ shows the call from store.orders.approve to billing.wallets.debit.
| Request | Answers |
|---|---|
GET /introspection/nodes/ | every node |
GET /introspection/nodes/:id/ | one node, with what it declares |
GET /introspection/edges/ | every observed edge; [] when nothing has been called |
GET /introspection/edges/:id/ | one edge |
§An application without the gateway
Introspection needs the gateway, which publishes its API. An application that does not use the gateway has to turn introspection off:
# context.toa.yaml
introspection: false §What it shows
Nodes are components, described from their manifests:
namespace: store
component: orders
entity:
storage: '@toa.io/storages.mongodb'
schema:
properties:
customer: { type: string }
total: { type: integer }
operations:
- endpoint: approve
type: transition
scope: entry
input: …
output: …
events:
- label: created
- label: approved
receivers:
- label: fulfilment.shipments.refused
source: fulfilment.shipments
event: refused
operation: cancel An operation’s description from the manifest is carried along, so write one:
# components/store.orders/manifest.toa.yaml
operations:
approve:
description: Debits the customer's wallet and marks the order approved. Event relations between components come from receivers and are there before any traffic.
Edges are calls observed while the application runs. No manifest lists calls, so an edge appears once the call has happened:
src:
namespace: store
component: orders
operation: approve
dst:
namespace: billing
component: wallets
operation: debit The source of an edge is one of:
src | The call was made by |
|---|---|
namespace, component, operation | an operation of a component |
namespace, component, event | the delivery of that event to a receiver |
service | a service, such as the gateway or the command line; unknown for a caller that did not identify itself |
A local call and a remote one are recorded the same way. A call is an edge whether the operation replied, returned an error or threw.
An edge is the pair it connects and nothing else. Inputs, replies and anything else the application passes between its components are not collected.
§Leave a component out
# components/billing.wallets/manifest.toa.yaml
introspection: false The component is then not described as a node, and calls to it produce no edges.
§The page
The page is published at /.introspection/ on the hosts the Context declares for ingress, and
the service behind it listens on port 8002:
# context.toa.yaml
ingress:
hosts:
- api.example.com https://api.example.com/.introspection/ The host has to be one the gateway serves too, because the page reads the API on its own origin.
The page itself is public, but shows nothing without an API session that has the system:introspection role.
To collect the topology without publishing the page:
# context.toa.yaml
introspection:
ui: false §The annotation
All keys are optional:
# context.toa.yaml
introspection:
interval: 300
threshold: 1024
ui: true
halt: false
resources:
cpu: [100m, 500m]
memory: [128Mi, 256Mi] | Key | Default | |
|---|---|---|
interval | 300 | Seconds between updates of the topology with newly observed calls. |
threshold | 1024 | The number of distinct observed interactions that triggers an update before the interval ends. |
ui | true | Publishes the page. |
halt | false | Accepts halt signals, which stop the whole deployment: true, or bounds for what a signal may ask. See Halt. |
resources | the Context’s resources | What the introspection service may take in a deployment: a pair of request and limit for cpu and for memory. resources: ~ deploys it without any. |
A new edge appears within interval seconds of the first call, not at once.
§What to use it for
- To learn or check the structure. The graph shows the application as it is, not as a diagram drawn some time ago says it is.
- To find a cycle before it closes. Event subscriptions that could form a circle are visible as declared relations, before a call chain is refused.
- To decide on compositions. Components with many edges between them are candidates for sharing one.
- To see what is unused. A declared relation with no observed edge, in an environment with real traffic, never fires.