Ejemplo n.º 1
0
        public async Task CommitAsync(
            ScheduledActions actions,
            ITransitionCarrier transitionCarrier,
            TransitionCommitOptions options,
            CancellationToken ct)
        {
            ITransitionContext context = _transitionScope.CurrentMonitor.Context;
            SerializedMethodContinuationState continuationState = null;

            if (actions.SaveStateIntent != null)
            {
                var intent           = actions.SaveStateIntent;
                var serviceRef       = _serviceResolver.Resolve(context.Service);
                var methodRef        = _methodResolver.Resolve(serviceRef.Definition, context.Method);
                var behaviorSettings = _communicationSettingsProvider.GetMethodSettings(methodRef.Definition);

                IMethodStateStorage stateStorage = null;

                if (intent.RoutineState != null && intent.RoutineResult == null)
                {
                    var roamState = behaviorSettings.RoamingState;

                    if (!roamState)
                    {
                        stateStorage = _methodStateStorageProvider.GetStorage(context.Service, context.Method, returnNullIfNotFound: true);
                        if (stateStorage == null)
                        {
                            // Fallback: try to roam the state if possible
                            roamState = true;
                        }
                    }

                    if (roamState)
                    {
                        // Aggregate methods (WhenAll, WhenAny) must have a concurrency control
                        // using a persistence mechanism to aggregate multiple results.
                        roamState = methodRef.Definition.FindProperty("aggregate")?.Value != (object)true;
                    }

                    if (!roamState && stateStorage == null)
                    {
                        throw new InvalidOperationException($"Invoking method '{methodRef.Id}' on '{serviceRef.Id}' requires persistence for aggregating result.");
                    }

                    if (roamState)
                    {
                        continuationState = EncodeContinuationState(intent, transitionCarrier, context);
                    }
                    else
                    {
                        try
                        {
                            var executionState = GetMethodExecutionState(
                                actions.SaveStateIntent, transitionCarrier, context);

                            var etag = await stateStorage.WriteStateAsync(
                                actions.SaveStateIntent.Service,
                                actions.SaveStateIntent.Method,
                                executionState);

                            // NOTE: assume that ContinuationDescriptor instances refer to this instance of
                            // PersistedMethodId and the ETag gets automatically propagated to the invoke intents.
                            actions.SaveStateIntent.Method.ETag = etag;
                        }
                        catch (ETagMismatchException ex)
                        {
                            // The record in the storage has changed since the beginning of the transition.
                            // There must have been a concurrent transition.
                            // Discard current results and try again.
                            throw new ConcurrentTransitionException(ex);
                        }
                    }
                }
                else if (intent.RoutineResult != null)
                {
                    var expectsSyncReply = (transitionCarrier as TransitionCarrier)?.Message.CommunicatorTraits.HasFlag(CommunicationTraits.SyncReplies) == true;

                    // TODO: make this behavior optional if a continuation is present (no polling expected).
                    // TODO: for the event sourcing style, the result must be written by the receiver of the response.
                    var writeResult = !expectsSyncReply;

                    if (writeResult && context.Caller?.Event != null)
                    {
                        writeResult = false;
                    }

                    if (writeResult && stateStorage == null)
                    {
                        stateStorage = _methodStateStorageProvider.GetStorage(context.Service, context.Method, returnNullIfNotFound: true);
                    }

                    if (stateStorage == null)
                    {
                        // Fallback (A): if the method has a continuation, assume that no polling is expected,
                        // so there is no need to write the result into a persisted storage.
                        // This does not cover 'fire and forget' scenarios.
                        // Fallback (B): This method must be an event handler - no need
                        // to write result because nothing should poll for the result.
                        if (expectsSyncReply ||
                            transitionCarrier.GetContinuationsAsync(default).Result?.Count > 0 ||
Ejemplo n.º 2
0
 public InMemoryPersistenceMethod(IDefaultSerializerProvider defaultSerializerProvider)
 {
     _singleStorage = new InMemoryMethodStateStorage(defaultSerializerProvider.DefaultSerializer);
 }