Clearing Pinned Data
Pinned data persists indefinitely unless explicitly cleared. Three mechanisms exist for clearing pinned data: explicit API clear (from executor code or admin), TTL-based expiry, and cascade delete when a node is removed from the workflow.
Mechanism 1 — Explicit Clear from Executor
An executor can clear its own pinned data by calling ctx.ExecutionMemory.ClearPinnedDataAsync. This deletes the row from Process_NodePinnedData.
// ResetableNode.cs — clears pinned data when reset input is received
public override async Task<NodeExecutionResult> ExecuteAsync(
NodeExecutionContext ctx,
CancellationToken ct)
{
var shouldReset = ctx.ExecutionMemory.GetNodeOutput<bool>("reset-trigger-node");
if (shouldReset)
{
await ctx.ExecutionMemory.ClearPinnedDataAsync(ctx.NodeId, ct);
ctx.Observability.Logger.LogInformation("Pinned data cleared — starting fresh");
return NodeExecutionResult.Success(output: null);
}
var previous = await ctx.ExecutionMemory.GetPinnedDataAsync<AccumulatedData>(ctx.NodeId, ct);
// ... continue with previous data
}
IExecutionMemory — Clear Methods
// IExecutionMemory.cs (clear methods)
public interface IExecutionMemory
{
// Delete pinned data for a specific node
Task ClearPinnedDataAsync(string nodeId, CancellationToken ct = default);
// Delete ALL pinned data for this process (use with care)
Task ClearAllPinnedDataAsync(CancellationToken ct = default);
}
Mechanism 2 — TTL-Based Expiry
Pinned data can be saved with a time-to-live by using the SaveAsync overload that accepts an expiresAt parameter. The background cleanup job removes expired rows.
// PinnedDataService.SaveAsync with TTL (called by executor result handling in BaseNodeExecutor)
// The TTL overload of NodeExecutionResult.Success is used to set expiry:
// Executor returns pinned data with 24-hour expiry
return NodeExecutionResult.Success(
output : result,
pinnedData : new CachedApiResult { Data = result, CachedAt = DateTime.UtcNow },
pinnedDataTtl : TimeSpan.FromHours(24) // Optional TTL argument
);
TTL Expiry Behaviour
| Scenario | Behaviour |
|---|---|
| ExpiresAt in future | GetPinnedDataAsync returns the data normally |
| ExpiresAt in past | GetPinnedDataAsync returns null (as if no data exists) |
| Background cleanup job | Runs every 24h (configurable) — deletes rows where ExpiresAt < NOW() |
| New write to expired node | Upsert updates the row and sets new ExpiresAt — row is not deleted first |
Mechanism 3 — Cascade Delete on Node Removal
When a node is removed from the workflow definition and the workflow is saved, the WorkflowDefinitionService computes the diff and calls PinnedDataService.ClearForRemovedNodesAsync. This prevents orphaned pinned data accumulating for nodes that no longer exist in the workflow.
// WorkflowDefinitionService.cs (simplified)
public async Task SaveDefinitionAsync(int processId, SerializedWorkflow definition, CancellationToken ct)
{
var previous = await _definitionRepo.GetAsync(processId, ct);
var removedNodeIds = GetRemovedNodeIds(previous, definition);
// Persist the new definition
await _definitionRepo.SaveAsync(processId, definition, ct);
// Cascade: clear pinned data for nodes no longer in the workflow
if (removedNodeIds.Any())
{
await _pinnedDataService.ClearForNodesAsync(processId, removedNodeIds, ct);
}
}
Clearing via Admin API
Tenant administrators can clear pinned data from outside the execution engine via the management API.
// Management API — clear pinned data for a specific node
DELETE /api/processes/{processId}/nodes/{nodeId}/pinned-data
// Clear all pinned data for a process
DELETE /api/processes/{processId}/pinned-data
ClearPinnedDataAsync only removes the database row. The current execution continues normally. If the node writes new pinned data in the same run (by returning non-null pinnedData), a new row is inserted immediately after.