public Task <RpcCallResult <Unit> > NotifyAsync(OpenBond.WorkerNotificationArgs message, IList <long> semiStableHashes) { var grpcMessage = message.ToGrpc(); return(m_connectionManager.CallAsync( (callOptions) => m_client.NotifyAsync(grpcMessage, options: callOptions), DistributionHelpers.GetNotifyDescription(message, semiStableHashes))); }
public async Task <RpcCallResult <Unit> > ExecutePipsAsync(OpenBond.PipBuildRequest message, IList <long> semiStableHashes) { var internalBondMessage = message.ToInternalBond(); var result = await m_proxyManager.Call <PipBuildRequest, Void>( internalBondMessage, functionName : "ExecutePips", description : DistributionHelpers.GetExecuteDescription(semiStableHashes)); return(result.ToUnit()); }
/// <summary> /// Notifies the worker of pip completions /// </summary> public async Task <RpcCallResult <Unit> > NotifyAsync(OpenBond.WorkerNotificationArgs message, IList <long> semiStableHashes) { var internalBondMessage = message.ToInternalBond(); var result = await m_proxyManager.Call <WorkerNotificationArgs, Void>( internalBondMessage, functionName : "Notify", description : DistributionHelpers.GetNotifyDescription(message, semiStableHashes)); return(result.ToUnit()); }
public Task <RpcCallResult <Unit> > NotifyAsync(OpenBond.WorkerNotificationArgs message, IList <long> semiStableHashes, CancellationToken cancellationToken = default) { Contract.Assert(m_initialized); var grpcMessage = message.ToGrpc(); return(m_connectionManager.CallAsync( async(callOptions) => await m_client.NotifyAsync(grpcMessage, options: callOptions), DistributionHelpers.GetNotifyDescription(message, semiStableHashes), cancellationToken: cancellationToken)); }
private async Task <RpcCallResult <T> > CallCore <T>( Func <TrackedConnection, BondCallTracker, Task <T> > callAsync, CancellationToken cancellationToken, string functionName, BondCallTracker callTracker, bool allowInactive = false, Func <T, bool> shouldRetry = null, uint maxTryCount = 0) { Contract.Requires(functionName != null); callTracker = callTracker ?? CreateLoggingCallTracker(functionName); TimeSpan waitForConnectionDuration = default(TimeSpan); Failure lastFailure = null; m_outstandingCalls.AddOrUpdate(functionName, 1, (k, i) => i + 1); // For heartbeat only try once if (maxTryCount == 0) { maxTryCount = DefaultMaxRetryCount; } for (uint retryCount = 0; retryCount < maxTryCount; retryCount++) { callTracker.TryCount = retryCount; if (retryCount != 0) { // For retries, log a call start with the call tracker's updated retry count callTracker.OnStateChanged(BondCallState.Started); // Yield after first iteration to ensure // we don't overflow the stack with async continuations await Task.Yield(); } TrackedConnection connection = null; try { var startWaitForConnection = m_stopwatch.Elapsed; callTracker.OnStateChanged(BondCallState.WaitingForConnection); // Wait for a connection to become active via the a successful heartbeat using (var connectionScope = await WaitForConnectionAsync(callTracker, allowInactive, cancellationToken)) { // Log wait for connection success var iterationWaitForConnectionDuration = GetElapsed(startWaitForConnection); waitForConnectionDuration += iterationWaitForConnectionDuration; callTracker.OnStateChanged(BondCallState.CompletedWaitForConnection); // connection is not returned in the case that the proxy is shutting down or timed out // other case is that this is a failed heartbeat call. In which case, just continue. if (connectionScope.Connection == null) { if (m_isShuttingDown || m_exceededInactivityTimeout) { // Log the failure lastFailure = new RecoverableExceptionFailure(new BuildXLException(m_isShuttingDown ? "Bond RPC Call failure: Proxy is shutting down" : "Bond RPC Call failure: Proxy timed out")); callTracker.LogMessage("Could not retrieve connection. Failure={0}", lastFailure.DescribeIncludingInnerFailures()); callTracker.OnStateChanged(BondCallState.Failed); return(new RpcCallResult <T>(RpcCallResultState.Failed, retryCount + 1, callTracker.TotalDuration, waitForConnectionDuration, lastFailure)); } continue; } connection = connectionScope.Connection; // Make the actual call var result = await callAsync(connection, callTracker); // Check if call should be retried if (shouldRetry != null && shouldRetry(result)) { continue; } // Log the call completion callTracker.OnStateChanged(BondCallState.Succeeded); m_proxyLogger.LogSuccessfulCall(m_loggingContext, functionName, retryCount); connectionScope.MarkSucceeded(); m_services.Counters.AddToCounter(DistributionCounter.SendPipBuildRequestCallDurationMs, (long)callTracker.TotalDuration.TotalMilliseconds); return(new RpcCallResult <T>(result, retryCount + 1, callTracker.TotalDuration, waitForConnectionDuration)); } } catch (OperationCanceledException) { callTracker.OnStateChanged(BondCallState.Canceled); return(new RpcCallResult <T>(RpcCallResultState.Cancelled, retryCount + 1, callTracker.TotalDuration, waitForConnectionDuration)); } catch (Exception ex) { // If shutting down just return the failed result if (ex is ObjectDisposedException && m_isShuttingDown) { lastFailure = new RecoverableExceptionFailure(new BuildXLException("Bond RPC Call failure: Proxy is shutting down", ex)); callTracker.LogMessage("{0}", lastFailure.DescribeIncludingInnerFailures()); callTracker.OnStateChanged(BondCallState.Failed); return(new RpcCallResult <T>(RpcCallResultState.Failed, retryCount + 1, callTracker.TotalDuration, waitForConnectionDuration)); } if (DistributionServices.IsBuildIdMismatchException(ex)) { m_proxyLogger.LogCallException(m_loggingContext, functionName, retryCount, ex); // If a message with different build is received, it means that the sender has participated in a different distributed build. // Then, we need to lose the connection with the sender. OnConnectionTimeOut?.Invoke(this, EventArgs.Empty); return(new RpcCallResult <T>(RpcCallResultState.Failed, retryCount + 1, callTracker.TotalDuration, waitForConnectionDuration)); } // If not a transient exception, log and throw if (!DistributionHelpers.IsTransientBondException(ex, m_services.Counters) && !m_services.IsChecksumMismatchException(ex)) { m_proxyLogger.LogCallException(m_loggingContext, functionName, retryCount, ex); throw; } // Otherwise, the exception is transient, so log exception and try again lastFailure = new RecoverableExceptionFailure(new BuildXLException("Failed Bond RPC call", ex)); callTracker.LogMessage("{0}", lastFailure.DescribeIncludingInnerFailures()); // Deactivate connection so subsequent calls on the proxy will wait for heartbeat before trying to make call. DeactivateConnection(connection); m_services.Counters.AddToCounter(DistributionCounter.FailedSendPipBuildRequestCallDurationMs, (long)callTracker.TotalDuration.TotalMilliseconds); m_services.Counters.IncrementCounter(DistributionCounter.FailedSendPipBuildRequestCount); m_proxyLogger.LogFailedCall(m_loggingContext, functionName, retryCount, lastFailure); } finally { m_outstandingCalls.AddOrUpdate(functionName, 0, (k, i) => i - 1); } } // Exceeded retry count. callTracker.LogMessage("Call failed and exhausted allowed retries. LastFailure={0}", lastFailure?.DescribeIncludingInnerFailures() ?? string.Empty); callTracker.OnStateChanged(BondCallState.Failed); return(new RpcCallResult <T>(RpcCallResultState.Failed, DefaultMaxRetryCount, callTracker.TotalDuration, waitForConnectionDuration, lastFailure)); }