Best practice for pull-data cache invalidation in a push-exec / pull-data node graph scheduler (C#)
01:32 21 Jul 2026

I'm building a Blueprint-style (Unreal Engine inspired) node graph scheduler in C#. It uses a hybrid model:

  • Exec pins — control flow is pushed from node to node.
  • Data pins — values are pulled lazily and cached per run.

For loop nodes (Repeat, ForEach), the pull-data cache for nodes inside the loop body must be invalidated between iterations, or the second iteration reuses stale values. My current approach makes the loop node call an explicit API on the execution context before each iteration:

public interface IExecutionContext
{
    T PullData(PinId pin);
    void InvalidateDataCache(string nodeId);
}

public sealed class RepeatNode : INode
{
    public void Execute(IExecutionContext ctx)
    {
        int count = ctx.PullData(CountPin);
        for (int i = 0; i < count; i++)
        {
            foreach (var bodyNodeId in BodySubgraphNodeIds)
                ctx.InvalidateDataCache(bodyNodeId);

            ctx.PushExec(BodyExecPin);
        }
    }
}

It works, but it feels ad-hoc; the loop node has to know the cache exists and walk its body subgraph, and pure/constant nodes get invalidated and re-evaluated unnecessarily.

Is this explicit invalidation approach the right core pattern for a Blueprint-style scheduler, or is there a better established alternative (iteration-scoped caches, pure vs impure node marking, generation counters, etc.)?

Repository: https://github.com/MizzleAa/VSMVVM

Scheduler engine: https://github.com/MizzleAa/VSMVVM/tree/main/src/VSMVVM.Core.Scheduler

best-practices c# design-patterns scheduling directed-acyclic-graphs