feat(audit): M5.4 ParentExecutionId tag-cascade for alarm + nested calls (T4)
This commit is contained in:
@@ -571,7 +571,20 @@ public class AlarmActor : ReceiveActor
|
||||
/// Passes the firing alarm's level/priority/message so the script can
|
||||
/// branch on severity via the <c>Alarm</c> global.
|
||||
/// </summary>
|
||||
private void SpawnAlarmExecution(AlarmLevel level, int priority, string message)
|
||||
/// <param name="level">The firing alarm severity level.</param>
|
||||
/// <param name="priority">The firing alarm priority.</param>
|
||||
/// <param name="message">The firing alarm message.</param>
|
||||
/// <param name="parentExecutionId">
|
||||
/// Audit Log #23 (M5.4 — ParentExecutionId tag-cascade): the execution id of
|
||||
/// the context that fired this alarm, recorded as the on-trigger script run's
|
||||
/// <c>ParentExecutionId</c> so the alarm-triggered run chains under its firing
|
||||
/// context in the audit tree. The alarm subsystem currently has no Guid-typed
|
||||
/// firing id, so the only call sites pass <c>null</c> (the on-trigger run is a
|
||||
/// root). The parameter exists so a future firing-id can flow without
|
||||
/// touching the actor wiring.
|
||||
/// </param>
|
||||
private void SpawnAlarmExecution(
|
||||
AlarmLevel level, int priority, string message, Guid? parentExecutionId = null)
|
||||
{
|
||||
if (_onTriggerCompiledScript == null) return;
|
||||
|
||||
@@ -591,7 +604,9 @@ public class AlarmActor : ReceiveActor
|
||||
_options,
|
||||
_logger,
|
||||
// M2.5 (#9): per-script timeout from the on-trigger script (null = global).
|
||||
_onTriggerExecutionTimeoutSeconds));
|
||||
_onTriggerExecutionTimeoutSeconds,
|
||||
// Audit Log #23 (M5.4): the firing context's execution id (null today).
|
||||
parentExecutionId));
|
||||
|
||||
Context.ActorOf(props, executionId);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,14 @@ public class AlarmExecutionActor : ReceiveActor
|
||||
/// <param name="options">Site runtime configuration options, including the execution timeout.</param>
|
||||
/// <param name="logger">Logger for execution diagnostics.</param>
|
||||
/// <param name="executionTimeoutSeconds">M2.5 (#9): the on-trigger script's per-script execution timeout in seconds. Null or non-positive falls back to the global <see cref="SiteRuntimeOptions.ScriptExecutionTimeoutSeconds"/>.</param>
|
||||
/// <param name="parentExecutionId">
|
||||
/// Audit Log #23 (M5.4 — ParentExecutionId tag-cascade): the execution id of
|
||||
/// the context that fired this alarm, threaded into the on-trigger script's
|
||||
/// <see cref="ScriptRuntimeContext"/> as its <c>ParentExecutionId</c> so the
|
||||
/// alarm-triggered run chains under its firing context. Null today (no
|
||||
/// Guid-typed firing id exists yet) — the run is a root, but the plumbing
|
||||
/// is in place for a future firing id.
|
||||
/// </param>
|
||||
public AlarmExecutionActor(
|
||||
string alarmName,
|
||||
string instanceName,
|
||||
@@ -42,7 +50,9 @@ public class AlarmExecutionActor : ReceiveActor
|
||||
ILogger logger,
|
||||
// M2.5 (#9): per-script execution timeout override (seconds) for the
|
||||
// alarm on-trigger script. Null or non-positive falls back to the global.
|
||||
int? executionTimeoutSeconds = null)
|
||||
int? executionTimeoutSeconds = null,
|
||||
// Audit Log #23 (M5.4): the firing context's execution id (null today).
|
||||
Guid? parentExecutionId = null)
|
||||
{
|
||||
var self = Self;
|
||||
var parent = Context.Parent;
|
||||
@@ -51,7 +61,7 @@ public class AlarmExecutionActor : ReceiveActor
|
||||
alarmName, instanceName, level, priority, message,
|
||||
compiledScript, instanceActor,
|
||||
sharedScriptLibrary, options, self, parent, logger,
|
||||
executionTimeoutSeconds);
|
||||
executionTimeoutSeconds, parentExecutionId);
|
||||
}
|
||||
|
||||
private static void ExecuteAlarmScript(
|
||||
@@ -67,7 +77,8 @@ public class AlarmExecutionActor : ReceiveActor
|
||||
IActorRef self,
|
||||
IActorRef parent,
|
||||
ILogger logger,
|
||||
int? executionTimeoutSeconds)
|
||||
int? executionTimeoutSeconds,
|
||||
Guid? parentExecutionId)
|
||||
{
|
||||
// M2.5 (#9): per-script timeout overrides the global default. A null or
|
||||
// non-positive per-script value (≤ 0) falls back to the global.
|
||||
@@ -95,7 +106,14 @@ public class AlarmExecutionActor : ReceiveActor
|
||||
options.MaxScriptCallDepth,
|
||||
timeout,
|
||||
instanceName,
|
||||
logger);
|
||||
logger,
|
||||
// Audit Log #23 (M5.4 — ParentExecutionId tag-cascade): the
|
||||
// alarm on-trigger run mints its own fresh ExecutionId (the
|
||||
// ctor's `?? NewGuid()` fallback) and records the firing
|
||||
// context's id as its ParentExecutionId — null today, so the
|
||||
// run is a root, but the plumbing exists for a future
|
||||
// firing id.
|
||||
parentExecutionId: parentExecutionId);
|
||||
|
||||
var globals = new ScriptGlobals
|
||||
{
|
||||
|
||||
@@ -247,6 +247,59 @@ public class ScriptRuntimeContext
|
||||
_siteEventLogger = siteEventLogger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log #23 (M5.4): this run's own per-execution id. Exposed so a
|
||||
/// nested <c>Scripts.CallShared</c> can record it as the spawned shared
|
||||
/// script's <c>ParentExecutionId</c>, forming a true execution tree.
|
||||
/// </summary>
|
||||
internal Guid ExecutionId => _executionId;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log #23 (M5.4): the spawning execution's id for this run (null for
|
||||
/// a root run). Exposed for test assertions on the execution tree.
|
||||
/// </summary>
|
||||
internal Guid? ParentExecutionId => _parentExecutionId;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log #23 (M5.4 — ParentExecutionId tag-cascade): builds a child
|
||||
/// <see cref="ScriptRuntimeContext"/> for an inline <c>Scripts.CallShared</c>
|
||||
/// invocation. The shared script runs inline (no actor hop) but is modelled
|
||||
/// as its OWN execution node in the audit tree: it mints a fresh
|
||||
/// <see cref="_executionId"/> and records THIS run's <see cref="_executionId"/>
|
||||
/// as its <c>ParentExecutionId</c>, so <c>B → CallShared(C)</c> yields
|
||||
/// <c>C.ParentExecutionId == B.ExecutionId</c>. Every other dependency
|
||||
/// (actors, gateways, audit writer, site id, source node, call-depth) is
|
||||
/// carried over verbatim from this context.
|
||||
/// </summary>
|
||||
/// <param name="childCallDepth">The recursion depth of the shared-script call.</param>
|
||||
internal ScriptRuntimeContext CreateChildContextForSharedScript(int childCallDepth)
|
||||
{
|
||||
return new ScriptRuntimeContext(
|
||||
_instanceActor,
|
||||
_self,
|
||||
_sharedScriptLibrary,
|
||||
childCallDepth,
|
||||
_maxCallDepth,
|
||||
_askTimeout,
|
||||
_instanceName,
|
||||
_logger,
|
||||
_externalSystemClient,
|
||||
_databaseGateway,
|
||||
_storeAndForward,
|
||||
_siteCommunicationActor,
|
||||
_siteId,
|
||||
_sourceScript,
|
||||
_auditWriter,
|
||||
_operationTrackingStore,
|
||||
_cachedForwarder,
|
||||
// Fresh execution id for the shared-script run (omit so the ctor mints one)…
|
||||
executionId: null,
|
||||
// …parented to THIS run's execution id (the spawner).
|
||||
parentExecutionId: _executionId,
|
||||
sourceNode: _sourceNode,
|
||||
siteEventLogger: _siteEventLogger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M2.12 (#25): fire-and-forget emission of a <c>script</c> Error site event
|
||||
/// for a recursion-limit violation. Mirrors the call shape used by
|
||||
@@ -366,7 +419,14 @@ public class ScriptRuntimeContext
|
||||
scriptName,
|
||||
ScriptArgs.Normalize(parameters),
|
||||
nextDepth,
|
||||
correlationId);
|
||||
correlationId,
|
||||
// Audit Log #23 (M5.4 — ParentExecutionId tag-cascade): the child
|
||||
// script run is a NEW execution spawned BY this run. Its parent is
|
||||
// THIS run's own ExecutionId — NOT the inherited _parentExecutionId.
|
||||
// So A → CallScript(B) yields B.ParentExecutionId == A.ExecutionId,
|
||||
// building a true multi-level execution tree rather than flattening
|
||||
// every nested call under the original inbound spawner.
|
||||
ParentExecutionId: _executionId);
|
||||
|
||||
// Ask the Instance Actor, which routes to the appropriate Script Actor
|
||||
var result = await _instanceActor.Ask<ScriptCallResult>(request, _askTimeout);
|
||||
@@ -526,8 +586,14 @@ public class ScriptRuntimeContext
|
||||
throw new InvalidOperationException(msg);
|
||||
}
|
||||
|
||||
// Audit Log #23 (M5.4 — ParentExecutionId tag-cascade): the shared
|
||||
// script runs inline, but is modelled as its OWN execution node — a
|
||||
// child context mints a fresh ExecutionId parented to the caller's
|
||||
// ExecutionId, so its audit rows chain under the calling run.
|
||||
var childContext = _context.CreateChildContextForSharedScript(nextDepth);
|
||||
|
||||
return await _library.ExecuteAsync(
|
||||
scriptName, _context, ScriptArgs.Normalize(parameters), cancellationToken);
|
||||
scriptName, childContext, ScriptArgs.Normalize(parameters), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user