private void HandleReactiveComputationExecute(IAddressable target, InvokeMethodRequest request, Message message, IGrainMethodInvoker invoker, bool refresh = false) { // Fetch the method info for the intercepted call. var interceptedMethodInvoker = interceptedMethodInvokerCache.GetOrCreate( target.GetType(), request.InterfaceId, invoker); var methodInfo = interceptedMethodInvoker.GetMethodInfo(request.MethodId); Type arg_type = methodInfo.ReturnType.GenericTypeArguments[0]; Type class_type = typeof(InsideRuntimeClient); MethodInfo mi = class_type.GetMethod("StartQuery"); MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { arg_type }); logger.Info("{0} # Received Summary Initiation/Subscription for {1} from {2}", CurrentActivationAddress, request, message.SendingActivation); var activationKey = RcUtils.GetRawActivationKey(CurrentGrain); mi2.Invoke(this, new object[] { activationKey, target, request, invoker, message, refresh }); }
/// <summary> /// Intercepts the call to a subquery. /// This will either get the existing value in the cache if it exists or create the cache and ask the grain this computation belongs to to start the computation. /// </summary> /// <remarks> /// This is assumed to be running within a task that is executing a reactive computation, i.e. after testing <see cref="IRuntimeClient.InReactiveComputation"/> /// </remarks> /// <typeparam name="T"></typeparam> /// <param name="grain"></param> /// <param name="request"></param> /// <param name="options"></param> /// <returns></returns> public async Task <T> ReuseOrRetrieveRcResult <T>(GrainReference grain, InvokeMethodRequest request, InvokeMethodOptions options) { T Result; var DependingRcSummary = this.CurrentRc(); var activationKey = RcUtils.GetRawActivationKey(grain); var Key = MakeCacheMapKey(activationKey, request); //var Timeout = this.CurrentRc().GetTimeout(); var ncache = new RcCache <T>(this, Key, grain, request, options, Config.ReactiveComputationRefresh); RcEnumeratorAsync <T> EnumAsync; RcCache <T> cache; bool existed; var threadSafeRetrieval = false; // We need to retrieve the cache and add the enumerator under the lock of the cache // in order to prevent interleaving with removal of the cache from the CacheMap. do { cache = (RcCache <T>)GetOrAddCache(activationKey, request, ncache); // Get an enumerator from the sub-cache dedicated to this summary threadSafeRetrieval = cache.GetEnumeratorAsync(DependingRcSummary, out EnumAsync); existed = ncache != cache; } while (!threadSafeRetrieval); //logger.Info("{0} # Initiating sub-query for caching {1}", new object[] { this.InterfaceId + "[" + this.GetPrimaryKey() + "]", request }); //logger.Info("{0} # Got initial result for sub-query {1} = {2} for summary {3}", new object[] { this.InterfaceId + "[" + this.GetPrimaryKey() + "]", request, result, ParentQuery.GetFullKey() }); if (!existed) { Logger.Verbose("{0} # {2} created a new entry {1}", RuntimeClient.Current.CurrentActivationAddress.Grain, cache, DependingRcSummary); cache.StartTimer(); } else { Logger.Verbose("{0} # {2} retrieved an existing {1}", RuntimeClient.Current.CurrentActivationAddress.Grain, cache, DependingRcSummary); } // First time we execute this sub-summary for the currently running summary if (!DependingRcSummary.HasDependencyOn(Key)) { Logger.Verbose("{0} # {2} new dependency on {1}", RuntimeClient.Current.CurrentActivationAddress.Grain, cache, DependingRcSummary); // Add the cache as a dependency to the summary DependingRcSummary.AddCacheDependency(Key, cache); // If the cache didn't exist yet, send a message to the activation // of the sub-summary responsible for this cache to start it if (!existed) { grain.InitiateQuery(request, options); } // Wait for the first result to arrive try { Logger.Verbose("{0} # waiting for the first result for {1} ", RuntimeClient.Current.CurrentActivationAddress.Grain, cache); Result = await EnumAsync.NextResultAsync(); } catch (ComputationStopped e) { Logger.Warn(ErrorCode.ReactiveCaches_PullFailure, "Caught exception while waiting for first result of {0} : {1}", request, e); Result = await ReuseOrRetrieveRcResult <T>(grain, request, options); } var task = HandleDependencyUpdates(Key, DependingRcSummary, EnumAsync); } // The running summary already has a dependency on this sub-summary else { if (!existed) { throw new OrleansException("illegal state"); } Logger.Verbose("{0} # {2} existing dependency on {1}", RuntimeClient.Current.CurrentActivationAddress.Grain, cache, DependingRcSummary); // Flag the dependency as still valid DependingRcSummary.MarkDependencyAsAlive(Key); // If we already have a value in the cache for the sub-summary, just return it if (cache.HasValue()) { Logger.Verbose("{0} # {2} using existing result for {1}: res={3}, exc={4}", RuntimeClient.Current.CurrentActivationAddress.Grain, cache, DependingRcSummary, cache.Result, cache.ExceptionResult); if (cache.ExceptionResult != null) { throw cache.ExceptionResult; } else { Result = cache.Result; } } // Otherwise wait for the result to arrive using the enumerator else { // Wait for the first result to arrive try { Logger.Verbose("{0} # waiting for the result of {1} ", RuntimeClient.Current.CurrentActivationAddress.Grain, cache); Result = await EnumAsync.NextResultAsync(); } catch (ComputationStopped e) { Logger.Warn(ErrorCode.ReactiveCaches_PullFailure, "Caught exception while waiting for result of {0} : {1}", request, e); Result = await ReuseOrRetrieveRcResult <T>(grain, request, options); } } } Logger.Verbose("{0} # {2} returning result of {1}", RuntimeClient.Current.CurrentActivationAddress.Grain, cache, DependingRcSummary, Result); return(Result); //logger.Info("{0} # re-using cached result for sub-query {1} = {2} for summary {3}", new object[] { this.InterfaceId + "[" + this.GetPrimaryKey() + "]", request, cache.Result, ParentQuery.GetFullKey() }); }