protected override async Task WakeUpAsync(CancellationToken cancellationToken)
        {
            var minCommitTime = (MaxKnownCommitTime - MaxCommitDuration).ToDateTime();

            // Fetching potentially new operations
            var operations = await DbOperationLog
                             .ListNewlyCommittedAsync(minCommitTime, cancellationToken)
                             .ConfigureAwait(false);

            // var secondsAgo = (Clock.Now.ToDateTime() - minCommitTime).TotalSeconds;
            // Log.LogInformation("({Ago:F2}s ago ... now): {OpCount} operations",
            //     secondsAgo, operations.Count);

            // Processing them
            foreach (var operation in operations)
            {
                OperationCompletionNotifier.NotifyCompleted(operation);
                var commitTime = operation.CommitTime.ToMoment();
                if (MaxKnownCommitTime < commitTime)
                {
                    // This update should happen even for locally executed operations,
                    // i.e. when NotifyCompleted(...) returns false!
                    MaxKnownCommitTime = commitTime;
                }
            }
        }
示例#2
0
    public async Task OnCommand(ICommand command, CommandContext context, CancellationToken cancellationToken)
    {
        var operationRequired =
            context.OuterContext == null && // Should be a top-level command
            !(command is IMetaCommand) && // No operations for meta commands
            !Computed.IsInvalidating();

        if (!operationRequired)
        {
            await context.InvokeRemainingHandlers(cancellationToken).ConfigureAwait(false);

            return;
        }

        var scope = Services.GetRequiredService <TransientOperationScope>();

        await using var _ = scope.ConfigureAwait(false);

        var operation = scope.Operation;

        operation.Command = command;
        context.Items.Set(scope);
        context.SetOperation(operation);

        try {
            await context.InvokeRemainingHandlers(cancellationToken).ConfigureAwait(false);

            await scope.Commit(cancellationToken).ConfigureAwait(false);
        }
        catch (OperationCanceledException) {
            throw;
        }
        catch (Exception e) {
            if (scope.IsUsed)
            {
                Log.LogError(e, "Operation failed: {Command}", command);
            }
            await scope.Rollback().ConfigureAwait(false);

            throw;
        }

        // Since this is the outermost scope handler, it's reasonable to
        // call OperationCompletionNotifier.NotifyCompleted from it
        var actualOperation = context.Items.GetOrDefault <IOperation>(operation);
        await OperationCompletionNotifier.NotifyCompleted(actualOperation).ConfigureAwait(false);
    }
示例#3
0
        protected override async Task WakeUp(CancellationToken cancellationToken)
        {
            var minCommitTime = (MaxKnownCommitTime - MaxCommitDuration).ToDateTime();

            // Fetching potentially new operations
            var operations = await DbOperationLog
                             .ListNewlyCommitted(minCommitTime, BatchSize, cancellationToken)
                             .ConfigureAwait(false);

            // Processing them
            var tasks = new Task[operations.Count];

            for (var i = 0; i < operations.Count; i++)
            {
                var operation = operations[i];
                var isLocal   = operation.AgentId == AgentInfo.Id.Value;
                // Local completions are invoked by TransientOperationScopeProvider
                // _inside_ the command processing pipeline. Trying to trigger them here
                // means a tiny chance of running them _outside_ of command processing
                // pipeline, which makes it possible to see command completing
                // prior to its invalidation logic completion.
                tasks[i] = isLocal
                    ? Task.CompletedTask // Skips local operation!
                    : OperationCompletionNotifier.NotifyCompleted(operation);
                var commitTime = operation.CommitTime.ToMoment();
                if (MaxKnownCommitTime < commitTime)
                {
                    MaxKnownCommitTime = commitTime;
                }
            }

            // Let's wait when all of these tasks complete, otherwise
            // we might end up creating too many tasks
            await Task.WhenAll(tasks).ConfigureAwait(false);

            LastCount = operations.Count;
        }
示例#4
0
        protected override async Task WakeAsync(CancellationToken cancellationToken)
        {
            var minCommitTime = (MaxKnownCommitTime - MaxCommitDuration).ToDateTime();

            // Fetching potentially new operations
            await using var dbContext = CreateDbContext();
            var operations = await DbOperationLog
                             .ListNewlyCommittedAsync(dbContext, minCommitTime, cancellationToken)
                             .ConfigureAwait(false);

            // Processing them
            foreach (var operation in operations)
            {
                if (!OperationCompletionNotifier.NotifyCompleted(operation))
                {
                    continue; // We saw this operation already
                }
                var commitTime = operation.CommitTime.ToMoment();
                if (MaxKnownCommitTime < commitTime)
                {
                    MaxKnownCommitTime = commitTime;
                }
            }
        }
        public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var operationRequired =
                context.OuterContext == null && // Should be top-level command
                !(command is IMetaCommand) && // No operations for "second-order" commands
                !Computed.IsInvalidating();

            if (!operationRequired)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            var tScope = typeof(DbOperationScope <TDbContext>);

            if (context.Items[tScope] != null) // Safety check
            {
                throw Stl.Internal.Errors.InternalError($"'{tScope}' scope is already provided. Duplicate handler?");
            }

            await using var scope = Services.GetRequiredService <DbOperationScope <TDbContext> >();
            var operation = scope.Operation;

            operation.Command = command;
            context.Items.Set(scope);

            var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel);

            try {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                await scope.CommitAsync(cancellationToken);
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                if (scope.IsUsed)
                {
                    Log.LogError(e, "Operation failed: {Command}", command);
                }
                try {
                    await scope.RollbackAsync();
                }
                catch {
                    // Intended
                }
                throw;
            }
            if (scope.IsUsed)
            {
                if (logEnabled)
                {
                    Log.Log(LogLevel, "Operation succeeded: {Command}", command);
                }
                var completion = Completion.New(operation);
                context.Items.Set(completion);
                OperationCompletionNotifier.NotifyCompleted(operation);
                try {
                    await context.Commander.CallAsync(completion, true, default).ConfigureAwait(false);
                }
                catch (Exception e) {
                    Log.LogError(e, "Local operation completion failed! Command: {Command}", command);
                    // No throw: the operation itself succeeded
                }
            }
        }
        public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var operationRequired =
                context.OuterContext == null && // Should be top-level command
                !(command is IMetaCommand) && // No operations for "second-order" commands
                !Computed.IsInvalidating();

            if (!operationRequired)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            await using var scope = Services.GetRequiredService <LocalOperationScope>();
            var operation = scope.Operation;

            operation.Command = command;
            context.Items.Set(scope);
            context.SetOperation(operation);

            var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel);

            try {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                await scope.CommitAsync(cancellationToken);
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                if (scope.IsUsed)
                {
                    Log.LogError(e, "Operation failed: {Command}", command);
                }
                await scope.RollbackAsync();

                throw;
            }

            // Since this is the outermost scope handler, it's reasonable to run the
            // ICompletion command right from it for any other scope too.
            var completion = context.Items.TryGet <ICompletion>();

            if (completion == null)   // Also means scope.IsUsed == true
            {
                if (logEnabled)
                {
                    Log.Log(LogLevel, "Operation succeeded: {Command}", command);
                }
                completion = Completion.New(operation);
                OperationCompletionNotifier.NotifyCompleted(operation);
                try {
                    await context.Commander.CallAsync(completion, true, default).ConfigureAwait(false);
                }
                catch (Exception e) {
                    Log.LogError(e, "Local operation completion failed! Command: {Command}", command);
                    // No throw: the operation itself succeeded
                }
            }
        }
示例#7
0
        public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var skip = context.OuterContext != null || // Should be top-level command
                       command is IInvalidateCommand || // Second handler here will take care of it
                       Computed.IsInvalidating();

            if (skip)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            var tScope = typeof(IDbOperationScope <TDbContext>);

            if (context.Items[tScope] != null) // Safety check
            {
                throw Stl.Internal.Errors.InternalError($"'{tScope}' scope is already provided. Duplicate handler?");
            }

            var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel);

            await using var scope = Services.GetRequiredService <IDbOperationScope <TDbContext> >();
            scope.Command         = command;
            context.Items.Set(scope);
            if (logEnabled)
            {
                Log.Log(LogLevel, "+ Operation started: {0}", command);
            }

            IOperation?operation = null;

            try {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                // Building IOperation.Items from CommandContext.Items
                foreach (var(key, value) in context.Items.Items)
                {
                    if (value is IOperationItem)
                    {
                        scope.Items = scope.Items.Set(key, value);
                    }
                }
                operation = await scope.CommitAsync(cancellationToken);

                if (logEnabled)
                {
                    Log.Log(LogLevel, "- Operation succeeded: {0}", command);
                }
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                Log.LogError(e, "! Operation failed: {0}", command);
                try {
                    await scope.RollbackAsync();
                }
                catch {
                    // Intended
                }
                throw;
            }
            if (operation != null)
            {
                if (InvalidationInfoProvider?.RequiresInvalidation(command) ?? false)
                {
                    context.Items.Set(InvalidateCommand.New(command, operation));
                }
                OperationCompletionNotifier?.NotifyCompleted(operation);
            }
        }