Serve Scenarios
This page explains nagi serve behavior through specific scenarios. Each scenario shows the timeline of when Evaluate and Sync are executed.
All scenarios assume autoSync: true.
Scenario 1: Linear Dependency Chain
graph LR
A["A (no interval)"] --> B["B (no interval)"] --> C["C (no interval)"]
A has 2 evaluations (initial + re-evaluate) and 1 Sync. B and C each have 1 evaluation (re-evaluate only) and 1 Sync. Convergence proceeds from upstream to downstream.
sequenceDiagram
participant A
participant B
participant C
Note over A: evaluate → Drifted
activate A
Note over A: sync
Note over A: re-evaluate → Ready
deactivate A
A->>B: upstream Ready → directly trigger sync
activate B
Note over B: sync
Note over B: re-evaluate → Ready
deactivate B
B->>C: upstream Ready → directly trigger sync
activate C
Note over C: sync
Note over C: re-evaluate → Ready
deactivate C
Scenario 2: Multiple Upstreams Become Ready in Quick Succession
graph LR
A["A (has interval)"] --> X["X (no interval)"]
B["B (has interval)"] --> X
C["C (has interval)"] --> X
An example where A, B, and C transition to Ready in close succession. X has 2 Syncs and 2 evaluations (re-evaluate after Sync only). B's propagation arrives while X is mid-Sync, so the running Sync subsumes B's propagation. C's propagation is accepted after X's Sync completes, and a second Sync is executed. This execution is necessary to reflect C's data changes into X.
sequenceDiagram
participant A
participant B
participant C
participant X
Note over A: Transition to Ready
A->>X: upstream Ready → directly trigger sync
activate X
Note over X: sync start
Note over B: Transition to Ready
B->>X: upstream Ready
Note over X: (mid-sync → running sync subsumes)
Note over X: sync complete
Note over X: re-evaluate → Ready
deactivate X
Note over C: Transition to Ready
C->>X: upstream Ready → directly trigger sync
activate X
Note over X: sync
Note over X: re-evaluate → Ready
deactivate X
Scenario 3: Upstreams Become Ready with Large Intervals
The same graph as Scenario 2, but with upstream Ready transitions occurring at well-spaced intervals. X has 3 Syncs and 3 evaluations (re-evaluate after Sync only). Each is a legitimate execution to reflect each upstream's data changes into X.
sequenceDiagram
participant A
participant B
participant C
participant X
Note over A: Transition to Ready
A->>X: upstream Ready → directly trigger sync
activate X
Note over X: sync
Note over X: re-evaluate → Ready
deactivate X
Note over A,X: Time passes
Note over B: Transition to Ready
B->>X: upstream Ready → directly trigger sync
activate X
Note over X: sync
Note over X: re-evaluate → Ready
deactivate X
Note over A,X: Time passes
Note over C: Transition to Ready
C->>X: upstream Ready → directly trigger sync
activate X
Note over X: sync
Note over X: re-evaluate → Ready
deactivate X
Scenario 4: Fan-out
graph LR
A["A (has interval)"] --> B["B (no interval)"]
A --> C["C (no interval)"]
A --> D["D (no interval)"]
When A transitions to Ready, Sync for B, C, and D is directly triggered. Each Asset has 1 Sync and 1 evaluation (re-evaluate only). Since B, C, and D have no dependencies on each other, their Syncs run in parallel.
sequenceDiagram
participant A
participant B
participant C
participant D
Note over A: Transition to Ready
par
A->>B: upstream Ready → directly trigger sync
activate B
Note over B: sync
Note over B: re-evaluate → Ready
deactivate B
and
A->>C: upstream Ready → directly trigger sync
activate C
Note over C: sync
Note over C: re-evaluate → Ready
deactivate C
and
A->>D: upstream Ready → directly trigger sync
activate D
Note over D: sync
Note over D: re-evaluate → Ready
deactivate D
end
Scenario 5: Diamond Dependency
graph LR
A["A (has interval)"] --> B["B (no interval)"] --> X["X (no interval)"]
A --> C["C (no interval)"] --> X
A combination of fan-out and fan-in. When A transitions to Ready, Sync for B and C is directly triggered. When B and C transition to Ready, they each directly trigger X's Sync. X has 1 Sync and 1 evaluation (re-evaluate only). If C becomes Ready while X is mid-Sync, the running Sync subsumes C's propagation.
sequenceDiagram
participant A
participant B
participant C
participant X
Note over A: Transition to Ready
par
A->>B: upstream Ready → directly trigger sync
activate B
Note over B: sync
Note over B: re-evaluate → Ready
deactivate B
and
A->>C: upstream Ready → directly trigger sync
activate C
Note over C: sync
end
B->>X: upstream Ready → directly trigger sync
activate X
Note over X: sync start
Note over C: re-evaluate → Ready
deactivate C
C->>X: upstream Ready
Note over X: (mid-sync → running sync subsumes)
Note over X: sync complete
Note over X: re-evaluate → Ready
deactivate X
Even if Syncs for B and C complete at nearly the same time, the running Sync subsumes subsequent propagations, so duplicate execution does not occur.
Scenario 6: Interval with Upstream Propagation
graph LR
A["A (has interval)"] --> B["B (has interval)"]
B operates via both polling-based Evaluate and direct Sync from upstream state changes. In this example, B has 1 Sync (directly triggered by upstream Ready) and 4 evaluations (3 from interval + 1 re-evaluate after Sync).
sequenceDiagram
participant A
participant B
participant Timer as B's interval timer
Timer->>B: interval elapsed
Note over B: evaluate → Ready
Timer->>B: interval elapsed
Note over B: evaluate → Ready
Note over A: Transition to Ready
A->>B: upstream Ready → directly trigger sync
activate B
Note over B: sync
Note over B: re-evaluate → Ready
deactivate B
Timer->>B: interval elapsed
Note over B: evaluate → Ready
Interval-based evaluate operates independently of upstream state changes. While upstream Drifted-to-Ready transitions trigger Sync directly (skipping Evaluate), periodic Evaluate via interval continues to run.
Scenario 7: Upstream Drifted Blocks Downstream Operations
graph LR
A["A (has interval)"] --> B["B (has interval)"] --> C["C (no interval)"]
While upstream A is Drifted, downstream B and C wait for all operations. B has an interval, but Evaluate is not run because the upstream is Drifted. Once A's Sync completes and it becomes Ready, upstream Ready is sent downstream.
sequenceDiagram
participant A
participant B
participant Timer as B's interval timer
participant C
Note over A: evaluate → Drifted
activate A
Timer->>B: interval elapsed
Note over B: Waiting (A is Drifted)
Timer->>B: interval elapsed
Note over B: Waiting (A is Drifted)
Note over A: sync
Note over A: re-evaluate → Ready
deactivate A
A->>B: upstream Ready → directly trigger sync
activate B
Note over B: sync
Note over B: re-evaluate → Ready
deactivate B
B->>C: upstream Ready → directly trigger sync
activate C
Note over C: sync
Note over C: re-evaluate → Ready
deactivate C