Example #1
0
 // Two primary constructors
 public ReplicaMethodComputed(ComputedOptions options, ComputeMethodInput input, IReplicaComputed <T> source)
     : this(source.Replica, options, input, source.Version)
 {
     ((IComputedImpl)this).AddUsed((IComputedImpl)source);
     // ReSharper disable once VirtualMemberCallInConstructor
     TrySetOutput(source.Output);
     if (!source.IsConsistent())
     {
         Invalidate();
     }
 }
Example #2
0
    // Protected methods

    protected static void SetReturnValue(ComputeMethodInput input, Result <TOut> output)
    {
        if (input.Method.ReturnsValueTask)
        {
            input.Invocation.ReturnValue =
                // ReSharper disable once HeapView.BoxingAllocation
                output.IsValue(out var v)
                    ? ValueTaskExt.FromResult(v)
                    : ValueTaskExt.FromException <TOut>(output.Error !);
        }
        else
        {
            input.Invocation.ReturnValue =
                output.IsValue(out var v)
                    ? Task.FromResult(v)
                    : Task.FromException <TOut>(output.Error !);
        }
    }
Example #3
0
    public override async Task <T> InvokeAndStrip(
        ComputeMethodInput input, IComputed?usedBy, ComputeContext?context,
        CancellationToken cancellationToken = default)
    {
        context ??= ComputeContext.Current;
        ResultBox <T>?output;

        // Read-Lock-RetryRead-Compute-Store pattern

        var computed = GetExisting(input);

        if (computed != null)
        {
            output = await computed.TryUseExisting(context, usedBy, cancellationToken)
                     .ConfigureAwait(false);

            if (output != null)
            {
                return(output.Value);
            }
        }

        using var @lock = await Locks.Lock(input, cancellationToken).ConfigureAwait(false);

        computed = GetExisting(input);
        if (computed != null)
        {
            output = await computed.TryUseExisting(context, usedBy, cancellationToken)
                     .ConfigureAwait(false);

            if (output != null)
            {
                return(output.Value);
            }
        }

        computed = (IAsyncComputed <T>) await Compute(input, computed, cancellationToken)
                   .ConfigureAwait(false);

        var rOutput = computed.Output; // RenewTimeouts isn't called yet, so it's ok

        computed.UseNew(context, usedBy);
        return(rOutput !.Value);
    }
Example #4
0
    protected override async ValueTask <IComputed <T> > Compute(
        ComputeMethodInput input, IComputed <T>?existing,
        CancellationToken cancellationToken)
    {
        var version  = VersionGenerator.NextVersion(existing?.Version ?? default);
        var computed = CreateComputed(input, version);

        try {
            using var _ = Computed.ChangeCurrent(computed);
            var result = input.InvokeOriginalFunction(cancellationToken);
            if (input.Method.ReturnsValueTask)
            {
                var output = await((ValueTask <T>)result).ConfigureAwait(false);
                computed.TrySetOutput(output);
            }
            else
            {
                var output = await((Task <T>)result).ConfigureAwait(false);
                computed.TrySetOutput(output);
            }
        }
        catch (OperationCanceledException e) {
            computed.TrySetOutput(Result.Error <T>(e));
            computed.Invalidate();
            throw;
        }
        catch (Exception e) {
            if (e is AggregateException ae)
            {
                e = ae.GetFirstInnerException();
            }
            computed.TrySetOutput(Result.Error <T>(e));
            // Weird case: if the output is already set, all we can
            // is to ignore the exception we've just caught;
            // throwing it further will probably make it just worse,
            // since the the caller have to take this scenario into acc.
        }
        return(computed);
    }
Example #5
0
 protected override IComputed <T> CreateComputed(ComputeMethodInput input, LTag tag)
 => new Computed <T>(Options, input, tag);
Example #6
0
 public ReplicaMethodComputed(ComputedOptions options, ComputeMethodInput input, Exception error, LTag version)
     : this(null, options, input, new Result <T>(default !, error), version, false)
Example #7
0
 protected Computed(ComputedOptions options, ComputeMethodInput input, Result <T> output, LTag version, bool isConsistent = true)
     : base(options, input, output, version, isConsistent)
 {
 }
Example #8
0
 public Computed(ComputedOptions options, ComputeMethodInput input, LTag version)
     : base(options, input, version)
 {
 }
Example #9
0
    protected override async ValueTask <IComputed <T> > Compute(
        ComputeMethodInput input, IComputed <T>?existing,
        CancellationToken cancellationToken)
    {
        var                       method = input.Method;
        IReplica <T>              replica;
        IReplicaComputed <T>      replicaComputed;
        ReplicaMethodComputed <T> result;

        // 1. Trying to update the Replica first
        if (existing is IReplicaMethodComputed <T> rsc && rsc.Replica != null)
        {
            try {
                replica         = rsc.Replica;
                replicaComputed = (IReplicaComputed <T>)
                                  await replica.Computed.Update(cancellationToken).ConfigureAwait(false);

                result = new (method.Options, input, replicaComputed);
                ComputeContext.Current.TryCapture(result);
                return(result);
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                DebugLog?.LogError(e, "ComputeAsync: error on Replica update");
            }
        }

        // 2. Replica update failed, let's refresh it
        Result <T>           output;
        PublicationStateInfo?psi;

        using (var psiCapture = new PublicationStateInfoCapture()) {
            try {
                var rpcResult = input.InvokeOriginalFunction(cancellationToken);
                if (method.ReturnsValueTask)
                {
                    var task = (ValueTask <T>)rpcResult;
                    output = Result.Value(await task.ConfigureAwait(false));
                }
                else
                {
                    var task = (Task <T>)rpcResult;
                    output = Result.Value(await task.ConfigureAwait(false));
                }
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                DebugLog?.LogError(e, "ComputeAsync: error on update");
                if (e is AggregateException ae)
                {
                    e = ae.GetFirstInnerException();
                }
                output = Result.Error <T>(e);
            }
            psi = psiCapture.Captured;
        }

        if (psi == null)
        {
            output = new Result <T>(default !, Errors.NoPublicationStateInfo());
Example #10
0
 protected abstract IComputed <T> CreateComputed(ComputeMethodInput input, LTag tag);
Example #11
0
    protected new IAsyncComputed <T>?GetExisting(ComputeMethodInput input)
    {
        var computed = ComputedRegistry.Instance.Get(input);

        return(computed as IAsyncComputed <T>);
    }