diff --git a/src/Azos.Sky.Server/Fabric/Server/FiberMemory.cs b/src/Azos.Sky.Server/Fabric/Server/FiberMemory.cs index 805df68b4..5981f254a 100644 --- a/src/Azos.Sky.Server/Fabric/Server/FiberMemory.cs +++ b/src/Azos.Sky.Server/Fabric/Server/FiberMemory.cs @@ -162,10 +162,12 @@ public void Crash(Exception error) /// Creates s snapshot of data changes which can be commited back into /// using . /// This only succeeds if the is - /// otherwise Delta can not be obtained + /// otherwise Delta can not be obtained. + /// Performs validation and throws if slots are in invalid state /// - public FiberMemoryDelta MakeDeltaSnapshot(FiberStep? nextStep, FiberState currentState) + public FiberMemoryDelta MakeDeltaSnapshot(IApplication app, FiberStep? nextStep, FiberState currentState) { + app.NonNull(nameof(app)); (m_Status == MemoryStatus.LockedForCaller).IsTrue("Delta obtained for LockedForCaller memory"); var result = new FiberMemoryDelta @@ -188,7 +190,24 @@ public FiberMemoryDelta MakeDeltaSnapshot(FiberStep? nextStep, FiberState curren result.NextSliceInterval = nxt.NextSliceInterval; result.ExitCode = nxt.ExitCode; result.Result = nxt.Result; - result.Changes = currentState.SlotChanges.ToArray(); + + var changes = currentState.SlotChanges.ToArray(); + foreach(var change in changes) + { + app.InjectInto(change.Value); + + var error = change.Value.Validate(); + if (error != null) + { + throw new FabricStateValidationException( + "Validation error for state slot `{0}.{1}` validation failed: {2}".Args( + currentState.GetType().DisplayNameWithExpandedGenericArgs(), + change.Value.GetType().DisplayNameWithExpandedGenericArgs(), + error.ToMessageWithType()), error); + } + } + + result.Changes = changes; } return result; diff --git a/src/Azos.Sky.Server/Fabric/Server/FiberProcessorDaemon.cs b/src/Azos.Sky.Server/Fabric/Server/FiberProcessorDaemon.cs index e071a2602..ae77eea16 100644 --- a/src/Azos.Sky.Server/Fabric/Server/FiberProcessorDaemon.cs +++ b/src/Azos.Sky.Server/Fabric/Server/FiberProcessorDaemon.cs @@ -403,7 +403,7 @@ private async Task processFiberQuantumUnsafe(ShardMapping shard, Guid logRel) var (wasHandled, nextStep, fiberState) = await processFiberQuantumCore(memory).ConfigureAwait(false);//<===================== FIBER SLICE gets called if (!wasHandled) return;//get out asap - var delta = memory.MakeDeltaSnapshot(nextStep, fiberState); + var delta = memory.MakeDeltaSnapshot(App, nextStep, fiberState);//this can throw on invalid state var saveErrorCount = 0; while(true) diff --git a/src/Azos.Sky/Fabric/Exceptions.cs b/src/Azos.Sky/Fabric/Exceptions.cs index f183f4405..23c8c0007 100644 --- a/src/Azos.Sky/Fabric/Exceptions.cs +++ b/src/Azos.Sky/Fabric/Exceptions.cs @@ -48,6 +48,18 @@ public FabricProcessorException(string message, Exception inner) : base(message, protected FabricProcessorException(SerializationInfo info, StreamingContext context) : base(info, context) { } } + /// + /// Thrown to indicate state validation error + /// + [Serializable] + public class FabricStateValidationException : FabricProcessorException + { + public FabricStateValidationException() : base() { } + public FabricStateValidationException(string message) : base(message) { } + public FabricStateValidationException(string message, Exception inner) : base(message, inner) { } + protected FabricStateValidationException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } + /// /// Thrown to indicate errors when fibers are not derived from /// diff --git a/src/Azos.Sky/Fabric/FiberStep.cs b/src/Azos.Sky/Fabric/FiberStep.cs index fc844d4a8..5e954c992 100644 --- a/src/Azos.Sky/Fabric/FiberStep.cs +++ b/src/Azos.Sky/Fabric/FiberStep.cs @@ -21,6 +21,9 @@ public struct FiberStep { public const string CONVENTION_STEP_METHOD_NAME_PREFIX = "Step_"; + public static readonly Atom START = Atom.Encode("Start"); + + public static readonly FiberStep ZERO = new FiberStep(); private static readonly FiniteSetLookup<(Type t, Atom s), MethodInfo> STEP_MI_CACHE = diff --git a/src/testing/Azos.Tests.Unit/Fabric/MemoryDeltaFormatTests.cs b/src/testing/Azos.Tests.Unit/Fabric/MemoryDeltaFormatTests.cs index d585165d5..c2ced90ea 100644 --- a/src/testing/Azos.Tests.Unit/Fabric/MemoryDeltaFormatTests.cs +++ b/src/testing/Azos.Tests.Unit/Fabric/MemoryDeltaFormatTests.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; - +using Azos.Apps; using Azos.Data; using Azos.Scripting; using Azos.Serialization.Bix; @@ -73,7 +73,7 @@ public void Test01_Roundtrip_NextStep_NoResult() gotState.AccountNumber = 223322;//mutate state <==================== Aver.IsTrue(got.HasDelta(gotState)); - var delta = got.MakeDeltaSnapshot(FiberStep.ContinueImmediately(Atom.Encode("step2")), gotState); + var delta = got.MakeDeltaSnapshot(NOPApplication.Instance, FiberStep.ContinueImmediately(Atom.Encode("step2")), gotState); Aver.AreEqual("step2", delta.NextStep.Value); Aver.AreEqual(new TimeSpan(0), delta.NextSliceInterval); @@ -156,7 +156,7 @@ public void Test02_Roundtrip_FinalStep_WithResult() String1 = "Dallas" }; - var delta = got.MakeDeltaSnapshot(FiberStep.FinishWithResult(123, result), gotState); + var delta = got.MakeDeltaSnapshot(NOPApplication.Instance, FiberStep.FinishWithResult(123, result), gotState); Aver.IsTrue(delta.NextStep.IsZero); Aver.AreEqual(new TimeSpan(0), delta.NextSliceInterval); @@ -241,7 +241,7 @@ public void Test03_Roundtrip_FinalStep_NoResult() Aver.IsTrue(got.HasDelta(gotState)); - var delta = got.MakeDeltaSnapshot(FiberStep.Finish(321), gotState); + var delta = got.MakeDeltaSnapshot(NOPApplication.Instance, FiberStep.Finish(321), gotState); Aver.IsTrue(delta.NextStep.IsZero); Aver.AreEqual(new TimeSpan(0), delta.NextSliceInterval); @@ -323,7 +323,7 @@ public void Test04_Roundtrip_Crash() Aver.IsFalse(got.HasDelta(gotState)); got.Crash(new FabricException("Problem X"));//crash memory - var delta = got.MakeDeltaSnapshot(FiberStep.Finish(321), gotState); + var delta = got.MakeDeltaSnapshot(NOPApplication.Instance, FiberStep.Finish(321), gotState); Aver.IsTrue(delta.NextStep.IsZero); Aver.AreEqual(new TimeSpan(0), delta.NextSliceInterval);