Skip to content

Dependencies

Dependencies define how components relate to each other and how failures propagate through your system. They specify the logic that determines when a component is UP based on the state of its inputs.

In YAML, dependencies are declared per target component under the top-level dependencies map. Allowed keys: do, and, or, n_of_k.


Dependency Types

Direct (do:)

A direct (causal) dependency: the target is UP if its parents are UP. Use this for straightforward structural links.

Usage

  • Works with any component type.
  • When the target is a normal node, use do: (don’t attach and/or/n_of_k to a normal target).

Example (direct deps)

root: web_app
components:
  web_app:  { type: logical }
  web_tier: { type: normal, mttf: 20000, mttr: 2 }
  api_tier: { type: normal, mttf: 25000, mttr: 2 }
  db_tier:  { type: normal, mttf: 30000, mttr: 1 }
dependencies:
  web_app:
    do: [web_tier, api_tier, db_tier]

AND (and:)

Target is UP only when all listed inputs are UP (serial/critical-path behavior). Model this on a logical node.

Use for

  • Serial systems where every element must work

Rules

  • The target must be a logical component

Example (AND gate)

root: app_core
components:
  app_core: { type: logical }
  lb:       { type: normal, mttf: 40000, mttr: 1 }
  db:       { type: normal, mttf: 30000, mttr: 2 }
dependencies:
  app_core:
    and: [lb, db]

OR (or:)

Target is UP when any listed input is UP (redundancy/alternate paths). Model this on a logical node.

Use for

  • Redundant services and instant failover
  • Alternative service paths

Rules

  • The target must be a logical component

Example (OR gate)

root: api_virtual
components:
  api_virtual: { type: logical }
  api_a:       { type: normal, mttf: 18000, mttr: 1 }
  api_b:       { type: normal, mttf: 18000, mttr: 1 }
dependencies:
  api_virtual:
    or: [api_a, api_b]

Use or: for instantaneous redundancy. If failover success depends on time to react, model that timing with a latch and wire it using do: (see patterns below).


N-of-K (n_of_k:)

Target is UP when at least n of k inputs are UP (quorum/partial redundancy). Model this on a logical node.

Use for

  • Quorum services, partial redundancy
  • Load sharing with a minimum capacity requirement

Rules

  • n must satisfy 1 ≤ n ≤ len(inputs)
  • The target must be a logical component

Example (quorum 2-of-3)

root: storage_quorum
components:
  storage_quorum: { type: logical }
  disk1:          { type: normal, mttf: 50000, mttr: 4 }
  disk2:          { type: normal, mttf: 50000, mttr: 4 }
  disk3:          { type: normal, mttf: 50000, mttr: 4 }
dependencies:
  storage_quorum:
    n_of_k:
      n: 2
      inputs: [disk1, disk2, disk3]

Dependency Structure (schema shape)

  • do: array of component names
  • and: array of component names
  • or: array of component names
  • n_of_k: object with n (integer) and inputs (array of names)

All are declared under dependencies:<target>.


Core Rules (validator)

Configuration rules

  • One dependency type per target. Don’t mix do, and, or, n_of_k on the same target. Use hierarchical logical nodes instead.
  • Targets exist. Every dependency name must reference a component defined under components.
  • Quorum bounds. For n_of_k, ensure n is between 1 and the number of inputs.
  • No cycles. Avoid circular dependencies.
  • Type discipline. Attach and/or/n_of_k to logical targets; use do for normal targets.

Modeling Patterns

Build complex logic by layering logical nodes so each node uses one dependency type.

Example (hybrid AND + OR)

root: web_app
components:
  # logical aggregators
  web_app:     { type: logical }
  app_core:    { type: logical }
  api_virtual: { type: logical }

  # normals
  lb:    { type: normal, mttf: 40000, mttr: 1 }
  db:    { type: normal, mttf: 30000, mttr: 2 }
  api_a: { type: normal, mttf: 18000, mttr: 1 }
  api_b: { type: normal, mttf: 18000, mttr: 1 }

dependencies:
  app_core:
    and: [lb, db]             # BOTH required

  api_virtual:
    or: [api_a, api_b]        # EITHER API works

  web_app:
    and: [app_core, api_virtual]  # combine tiers

Time-Constrained Failover (with latches)

Use latches to model success-within-deadline behavior; connect them with do:. The edge expresses coupling; the latch is activated on parent failure by the engine’s latch semantics.

Example (DB failover with deadline)

root: service
components:
  # logical aggregators
  service:       { type: logical }
  failover_path: { type: logical }
  db_virtual:    { type: logical }

  # primaries/standby + latch
  db_primary:  { type: normal, mttf: 24000, mttr: 1 }
  db_standby:  { type: normal, mttf: 24000, mttr: 1 }
  db_failover: { type: latch,  mttf: 0.5, max_delay: 1.0 }  # 30 min avg, 1 h deadline

dependencies:
  # Latch listens to primary’s failure (engine triggers on failure even though the edge is `do`)
  db_failover:
    do: [db_primary]

  # Failover path requires both: successful failover AND standby availability
  failover_path:
    and: [db_failover, db_standby]

  # Virtual DB is available if primary is up OR the failover path is up
  db_virtual:
    or: [db_primary, failover_path]

  # Service depends on the virtual DB (extend with app/web tiers as needed)
  service:
    do: [db_virtual]

Best Practices

Do - Model structure with logical nodes; keep normal nodes simple and causal. - Start from high-level dependencies, refine iteratively. - Use consistent, descriptive names for clarity in diagrams.

Don’t - Mix multiple dependency types on the same target (breaks validation). - Leave dangling references (every dependency name must exist under components).


Next Steps