// 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(); } }
// 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 !); } }
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); }
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); }
protected override IComputed <T> CreateComputed(ComputeMethodInput input, LTag tag) => new Computed <T>(Options, input, tag);
public ReplicaMethodComputed(ComputedOptions options, ComputeMethodInput input, Exception error, LTag version) : this(null, options, input, new Result <T>(default !, error), version, false)
protected Computed(ComputedOptions options, ComputeMethodInput input, Result <T> output, LTag version, bool isConsistent = true) : base(options, input, output, version, isConsistent) { }
public Computed(ComputedOptions options, ComputeMethodInput input, LTag version) : base(options, input, version) { }
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());
protected abstract IComputed <T> CreateComputed(ComputeMethodInput input, LTag tag);
protected new IAsyncComputed <T>?GetExisting(ComputeMethodInput input) { var computed = ComputedRegistry.Instance.Get(input); return(computed as IAsyncComputed <T>); }