internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.WorkerInitResponse, out StatusResult status); try { _powerShellManager.InitializeRunspace(); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } return(response); }
internal void InvocationRequestHandler(StreamingMessage request) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; RpcHttp rpcHttp = new RpcHttp() { StatusCode = "200" }; TypedData typedData = new TypedData(); typedData.Http = rpcHttp; response.InvocationResponse.ReturnValue = typedData; _blockingCollectionQueue.Add(response); }
/// <summary> /// Checkout an idle PowerShellManager instance in a non-blocking asynchronous way. /// </summary> internal PowerShellManager CheckoutIdleWorker(StreamingMessage request, AzFunctionInfo functionInfo) { PowerShellManager psManager = null; string requestId = request.RequestId; string invocationId = request.InvocationRequest?.InvocationId; // If the pool has an idle one, just use it. if (!_pool.TryTake(out psManager)) { // The pool doesn't have an idle one. if (_poolSize < _upperBound) { int id = Interlocked.Increment(ref _poolSize); if (id <= _upperBound) { // If the pool hasn't reached its bounded capacity yet, then // we create a new item and return it. var logger = new RpcLogger(_msgStream); logger.SetContext(requestId, invocationId); psManager = new PowerShellManager(logger, id); RpcLogger.WriteSystemLog(string.Format(PowerShellWorkerStrings.LogNewPowerShellManagerCreated, id.ToString())); } } if (psManager == null) { // If the pool has reached its bounded capacity, then the thread // should be blocked until an idle one becomes available. psManager = _pool.Take(); } } // Finish the initialization if not yet. // This applies only to the very first PowerShellManager instance, whose initialization was deferred. psManager.Initialize(); // Register the function with the Runspace before returning the idle PowerShellManager. FunctionMetadata.RegisterFunctionMetadata(psManager.InstanceId, functionInfo); psManager.Logger.SetContext(requestId, invocationId); return(psManager); }
/// <summary> /// Method to process a InvocationRequest. /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread. /// </summary> internal StreamingMessage ProcessInvocationRequest(StreamingMessage request) { try { var stopwatch = new FunctionInvocationPerformanceStopwatch(); stopwatch.OnStart(); // Will block if installing dependencies is required _dependencyManager.WaitForDependenciesAvailability( () => { var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId); return(rpcLogger); }); stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.DependenciesAvailable); AzFunctionInfo functionInfo = FunctionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId); PowerShellManager psManager = _powershellPool.CheckoutIdleWorker(request, functionInfo); stopwatch.OnCheckpoint(FunctionInvocationPerformanceStopwatch.Checkpoint.RunspaceAvailable); // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker // thread, so multiple invocations can make progress at the same time, even though by time-sharing. Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager, stopwatch)); } catch (Exception e) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } return(null); }
internal StreamingMessage ProcessWorkerInitRequest(StreamingMessage request) { StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.WorkerInitResponse, out StatusResult status); // If the environment variable is set, spin up the custom named pipe server. // This is typically used for debugging. It will throw a friendly exception if the // pipe name is not a valid pipename. string pipeName = Environment.GetEnvironmentVariable("PSWorkerCustomPipeName"); if (!string.IsNullOrEmpty(pipeName)) { RpcLogger.WriteSystemLog(LogLevel.Trace, string.Format(PowerShellWorkerStrings.SpecifiedCustomPipeName, pipeName)); RemoteSessionNamedPipeServer.CreateCustomNamedPipeServer(pipeName); } return(response); }
public async Task WriteAsync(StreamingMessage message) { if (isDisposed) { return; } // Wait for the handle to be released because we can't have // more than one message being sent at the same time await _writeSemaphore.WaitAsync(); try { await _call.RequestStream.WriteAsync(message); } finally { _writeSemaphore.Release(); } }
public void SendWorkerInitRequest_PublishesOutboundEvent_V2Compatable() { _testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionsV2CompatibilityModeKey, "true"); StartStream startStream = new StartStream() { WorkerId = _workerId }; StreamingMessage startStreamMessage = new StreamingMessage() { StartStream = startStream }; RpcEvent rpcEvent = new RpcEvent(_workerId, startStreamMessage); _workerChannel.SendWorkerInitRequest(rpcEvent); _testFunctionRpcService.PublishWorkerInitResponseEvent(); var traces = _logger.GetLogMessages(); Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, _expectedLogMsg))); Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, "Worker and host running in V2 compatibility mode"))); }
public async Task <WorkerStatus> GetWorkerStatusAsync() { var workerStatus = new WorkerStatus(); if (!string.IsNullOrEmpty(_workerCapabilities.GetCapabilityState(RpcWorkerConstants.WorkerStatus))) { // get the worker's current status // this will include the OOP worker's channel latency in the request, which can be used upstream // to make scale decisions var message = new StreamingMessage { RequestId = Guid.NewGuid().ToString(), WorkerStatusRequest = new WorkerStatusRequest() }; var sw = Stopwatch.StartNew(); var tcs = new TaskCompletionSource <bool>(); if (_workerStatusRequests.TryAdd(message.RequestId, tcs)) { SendStreamingMessage(message); await tcs.Task; sw.Stop(); workerStatus.Latency = sw.Elapsed; _workerChannelLogger.LogDebug($"[HostMonitor] Worker status request took {sw.ElapsedMilliseconds}ms"); } } // get the process stats for the worker var workerProcessStats = _rpcWorkerProcess.GetStats(); workerStatus.ProcessStats = workerProcessStats; if (workerProcessStats.CpuLoadHistory.Any()) { string formattedLoadHistory = string.Join(",", workerProcessStats.CpuLoadHistory); int executingFunctionCount = FunctionInputBuffers.Sum(p => p.Value.Count); _workerChannelLogger.LogDebug($"[HostMonitor] Worker process stats: EffectiveCores={_environment.GetEffectiveCoresCount()}, ProcessId={_rpcWorkerProcess.Id}, ExecutingFunctions={executingFunctionCount}, CpuLoadHistory=({formattedLoadHistory}), AvgLoad={workerProcessStats.CpuLoadHistory.Average()}, MaxLoad={workerProcessStats.CpuLoadHistory.Max()}"); } return(workerStatus); }
/// <summary> /// Create an object of 'StreamingMessage' as a template, for further update. /// </summary> private StreamingMessage NewStreamingMessageTemplate(string requestId, StreamingMessage.ContentOneofCase msgType, out StatusResult status) { // Assume success. The state of the status object can be changed in the caller. status = new StatusResult() { Status = StatusResult.Types.Status.Success }; var response = new StreamingMessage() { RequestId = requestId }; switch (msgType) { case StreamingMessage.ContentOneofCase.WorkerInitResponse: response.WorkerInitResponse = new WorkerInitResponse() { Result = status }; break; case StreamingMessage.ContentOneofCase.FunctionLoadResponse: response.FunctionLoadResponse = new FunctionLoadResponse() { Result = status }; break; case StreamingMessage.ContentOneofCase.InvocationResponse: response.InvocationResponse = new InvocationResponse() { Result = status }; break; default: throw new InvalidOperationException("Unreachable code."); } return(response); }
private async Task ProcessRequestCoreAsync(StreamingMessage request) { StreamingMessage responseMessage = new StreamingMessage { RequestId = request.RequestId }; if (request.ContentCase == MsgType.InvocationRequest) { responseMessage.InvocationResponse = await InvocationRequestHandlerAsync(request.InvocationRequest, _application, _invocationFeaturesFactory, _serializer, _outputBindingsInfoProvider); } else if (request.ContentCase == MsgType.WorkerInitRequest) { responseMessage.WorkerInitResponse = WorkerInitRequestHandler(request.WorkerInitRequest); } else if (request.ContentCase == MsgType.FunctionLoadRequest) { responseMessage.FunctionLoadResponse = FunctionLoadRequestHandler(request.FunctionLoadRequest, _application, _methodInfoLocator); } else if (request.ContentCase == MsgType.FunctionEnvironmentReloadRequest) { // No-op for now, but return a response. responseMessage.FunctionEnvironmentReloadResponse = new FunctionEnvironmentReloadResponse { Result = new StatusResult { Status = StatusResult.Types.Status.Success } }; } else { // TODO: Trace failure here. return; } await _outputWriter.WriteAsync(responseMessage); }
public void Log(LogLevel logLevel, string message, Exception exception = null, bool isUserLog = false) { if (isUserLog) { // For user logs, we send them over Rpc with details about the invocation. var logMessage = new StreamingMessage { RequestId = _requestId, RpcLog = new RpcLog() { Exception = exception?.ToRpcException(), InvocationId = _invocationId ?? "N/A", Level = logLevel, Message = message } }; _msgStream.Write(logMessage); } else { // For system logs, we log to stdio with a prefix of LanguageWorkerConsoleLog. // These are picked up by the Functions Host _systemLogMsg.Append(SystemLogPrefix).AppendLine("System Log: {"); if (!string.IsNullOrEmpty(_requestId)) { _systemLogMsg.AppendLine($" Request-Id: {_requestId}"); } if (!string.IsNullOrEmpty(_invocationId)) { _systemLogMsg.AppendLine($" Invocation-Id: {_invocationId}"); } _systemLogMsg.AppendLine($" Log-Message: {message}").AppendLine("}"); Console.WriteLine(_systemLogMsg.ToString()); _systemLogMsg.Clear(); } }
public async Task <bool> RpcStream() { StartStream str = new StartStream() { WorkerId = _workerId }; StreamingMessage startStream = new StreamingMessage() { StartStream = str }; await _call.RequestStream.WriteAsync(startStream); var consumer = Task.Run(async() => { foreach (var rpcWriteMsg in _blockingCollectionQueue.GetConsumingEnumerable()) { await _call.RequestStream.WriteAsync(rpcWriteMsg); } }); await consumer; return(true); }
public void SharedMemoryDataTransferSetting_VerifyDisabledIfWorkerCapabilityAbsent() { // Enable shared memory data transfer in the environment _testEnvironment.SetEnvironmentVariable(RpcWorkerConstants.FunctionsWorkerSharedMemoryDataTransferEnabledSettingName, "1"); StartStream startStream = new StartStream() { WorkerId = _workerId }; StreamingMessage startStreamMessage = new StreamingMessage() { StartStream = startStream }; // Send worker init request and enable the capabilities GrpcEvent rpcEvent = new GrpcEvent(_workerId, startStreamMessage); _workerChannel.SendWorkerInitRequest(rpcEvent); _testFunctionRpcService.PublishWorkerInitResponseEvent(); Assert.False(_workerChannel.IsSharedMemoryDataTransferEnabled()); }
public void Log(LogLevel logLevel, string message, Exception exception = null, bool isUserLog = false) { if (isUserLog) { // For user logs, we send them over Rpc with details about the invocation. var logMessage = new StreamingMessage { RequestId = _requestId, RpcLog = new RpcLog() { Exception = exception?.ToRpcException(), InvocationId = _invocationId ?? "N/A", Level = logLevel, Message = message } }; _msgStream.Write(logMessage); } else { WriteSystemLog(message, _systemLogMsg, _requestId, _invocationId); } }
public void PublishWorkerInitResponseEvent(IDictionary <string, string> capabilities = null) { StatusResult statusResult = new StatusResult() { Status = StatusResult.Types.Status.Success }; WorkerInitResponse initResponse = new WorkerInitResponse() { Result = statusResult }; if (capabilities != null) { initResponse.Capabilities.Add(capabilities); } StreamingMessage responseMessage = new StreamingMessage() { WorkerInitResponse = initResponse }; _eventManager.Publish(new InboundGrpcEvent(_workerId, responseMessage)); }
/// <summary> /// Method to process a InvocationRequest. /// InvocationRequest should be processed in parallel eventually. /// </summary> internal StreamingMessage ProcessInvocationRequest(StreamingMessage request) { InvocationRequest invocationRequest = request.InvocationRequest; StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = invocationRequest.InvocationId; // Invoke powershell logic and return hashtable of out binding data try { // Load information about the function var functionInfo = _functionLoader.GetFunctionInfo(invocationRequest.FunctionId); _powerShellManager.RegisterFunctionMetadata(functionInfo); Hashtable results = functionInfo.Type == AzFunctionType.OrchestrationFunction ? InvokeOrchestrationFunction(functionInfo, invocationRequest) : InvokeSingleActivityFunction(functionInfo, invocationRequest); BindOutputFromResult(response.InvocationResponse, functionInfo, results); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } finally { _powerShellManager.UnregisterFunctionMetadata(); } return(response); }
/// <summary> /// Entry point of the language worker. /// </summary> public async static Task Main(string[] args) { RpcLogger.WriteSystemLog( LogLevel.Information, string.Format(PowerShellWorkerStrings.PowerShellWorkerVersion, typeof(Worker).Assembly.GetName().Version)); WorkerArguments arguments = null; Parser.Default.ParseArguments <WorkerArguments>(args) .WithParsed(ops => arguments = ops) .WithNotParsed(err => Environment.Exit(1)); // Create the very first Runspace so the debugger has the target to attach to. // This PowerShell instance is shared by the first PowerShellManager instance created in the pool, // and the dependency manager (used to download dependent modules if needed). var firstPowerShellInstance = Utils.NewPwshInstance(); LogPowerShellVersion(firstPowerShellInstance); WarmUpPowerShell(firstPowerShellInstance); var msgStream = new MessagingStream(arguments.Host, arguments.Port); var requestProcessor = new RequestProcessor(msgStream, firstPowerShellInstance); // Send StartStream message var startedMessage = new StreamingMessage() { RequestId = arguments.RequestId, StartStream = new StartStream() { WorkerId = arguments.WorkerId } }; msgStream.Write(startedMessage); await requestProcessor.ProcessRequestLoop(); }
/// <summary> /// Method to process a FunctionLoadRequest. /// FunctionLoadRequest should be processed sequentially. There is no point to process FunctionLoadRequest /// concurrently as a FunctionApp doesn't include a lot functions in general. Having this step sequential /// will make the Runspace-level initialization easier and more predictable. /// </summary> internal StreamingMessage ProcessFunctionLoadRequest(StreamingMessage request) { FunctionLoadRequest functionLoadRequest = request.FunctionLoadRequest; StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.FunctionLoadResponse, out StatusResult status); response.FunctionLoadResponse.FunctionId = functionLoadRequest.FunctionId; try { // Ideally, the initialization should happen when processing 'WorkerInitRequest', however, the 'WorkerInitRequest' // message doesn't provide the file path of the FunctionApp. That information is not available until the first // 'FunctionLoadRequest' comes in. Therefore, we run initialization here. if (!_isFunctionAppInitialized) { FunctionLoader.SetupWellKnownPaths(functionLoadRequest); _powerShellManager.PerformWorkerLevelInitialization(); _powerShellManager.PerformRunspaceLevelInitialization(); _isFunctionAppInitialized = true; } // Load the metadata of the function. _functionLoader.LoadFunction(functionLoadRequest); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } return(response); }
/// <summary> /// Entry point of the language worker. /// </summary> public async static Task Main(string[] args) { WorkerArguments arguments = null; Parser.Default.ParseArguments <WorkerArguments>(args) .WithParsed(ops => arguments = ops) .WithNotParsed(err => Environment.Exit(1)); var msgStream = new MessagingStream(arguments.Host, arguments.Port); var requestProcessor = new RequestProcessor(msgStream); // Send StartStream message var startedMessage = new StreamingMessage() { RequestId = arguments.RequestId, StartStream = new StartStream() { WorkerId = arguments.WorkerId } }; msgStream.Write(startedMessage); await requestProcessor.ProcessRequestLoop(); }
/// <summary> /// Processes the dependency download request /// </summary> /// <param name="msgStream">The protobuf messaging stream</param> /// <param name="request">The StreamingMessage request for function load</param> /// <param name="pwsh">The PowerShell instance used to download modules</param> internal void ProcessDependencyDownload(MessagingStream msgStream, StreamingMessage request, PowerShell pwsh) { if (request.FunctionLoadRequest.ManagedDependencyEnabled) { var rpcLogger = new RpcLogger(msgStream); rpcLogger.SetContext(request.RequestId, null); if (Dependencies.Count == 0) { // If there are no dependencies to install, log and return. rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.FunctionAppDoesNotHaveDependentModulesToInstall, isUserLog: true); return; } if (!_shouldUpdateFunctionAppDependencies) { // The function app already has the latest dependencies installed. rpcLogger.Log(LogLevel.Trace, PowerShellWorkerStrings.LatestFunctionAppDependenciesAlreadyInstalled, isUserLog: true); return; } //Start dependency download on a separate thread _dependencyDownloadTask = Task.Run(() => InstallFunctionAppDependencies(pwsh, rpcLogger)); } }
internal StreamingMessage ProcessWorkerTerminateRequest(StreamingMessage request) { return(null); }
private void Send(StreamingMessage msg) { _eventManager.Publish(new OutboundEvent(_workerId, msg)); }
/// <summary> /// Method to process a InvocationRequest. /// This method checks out a worker from the pool, and then starts the actual invocation in a threadpool thread. /// </summary> internal StreamingMessage ProcessInvocationRequest(StreamingMessage request) { AzFunctionInfo functionInfo = null; PowerShellManager psManager = null; try { if (_dependencyManager.DependencyDownloadTask != null && (_dependencyManager.DependencyDownloadTask.Status != TaskStatus.Canceled || _dependencyManager.DependencyDownloadTask.Status != TaskStatus.Faulted || _dependencyManager.DependencyDownloadTask.Status != TaskStatus.RanToCompletion)) { var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, request.InvocationRequest?.InvocationId); rpcLogger.Log(LogLevel.Information, PowerShellWorkerStrings.DependencyDownloadInProgress, isUserLog: true); _dependencyManager.WaitOnDependencyDownload(); } if (_dependencyManager.DependencyError != null) { StreamingMessage response = NewStreamingMessageTemplate(request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); status.Status = StatusResult.Types.Status.Failure; status.Exception = _dependencyManager.DependencyError.ToRpcException(); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; return(response); } functionInfo = _functionLoader.GetFunctionInfo(request.InvocationRequest.FunctionId); psManager = _powershellPool.CheckoutIdleWorker(request, functionInfo); if (_powershellPool.UpperBound == 1) { // When the concurrency upper bound is 1, we can handle only one invocation at a time anyways, // so it's better to just do it on the current thread to reduce the required synchronization. ProcessInvocationRequestImpl(request, functionInfo, psManager); } else { // When the concurrency upper bound is more than 1, we have to handle the invocation in a worker // thread, so multiple invocations can make progress at the same time, even though by time-sharing. Task.Run(() => ProcessInvocationRequestImpl(request, functionInfo, psManager)); } } catch (Exception e) { _powershellPool.ReclaimUsedWorker(psManager); StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.InvocationResponse, out StatusResult status); response.InvocationResponse.InvocationId = request.InvocationRequest.InvocationId; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } return(null); }
internal StreamingMessage ProcessFileChangeEventRequest(StreamingMessage request) { return(null); }
private static async Task HandleInvocationRequest(InvocationRequest invokeRequest) { var status = new StatusResult() { Status = StatusResult.Types.Status.Success }; var response = new StreamingMessage() { RequestId = s_requestId, InvocationResponse = new InvocationResponse() { InvocationId = invokeRequest.InvocationId, Result = status } }; var metadata = s_loadedFunctions[invokeRequest.FunctionId]; // Not exactly sure what to do with bindings yet, so only handles 'httpTrigger-in' + 'http-out' string outHttpName = null; foreach (var binding in metadata.Bindings) { if (binding.Value.Direction == BindingInfo.Types.Direction.In) { continue; } if (binding.Value.Type == "http") { outHttpName = binding.Key; break; } } if (outHttpName == null) { status.Status = StatusResult.Types.Status.Failure; status.Result = "PowerShell worker only handles http out binding for now."; } else { object argument = null; foreach (var input in invokeRequest.InputData) { if (input.Data != null && input.Data.Http != null) { argument = input.Data.Http.Query.GetValueOrDefault("name", "Azure Functions"); } } s_ps.AddCommand(metadata.ScriptFile); if (argument != null) { s_ps.AddArgument(argument); } TypedData retValue; try { var results = s_ps.Invoke <string>(); retValue = new TypedData() { String = String.Join(',', results) }; } finally { s_ps.Commands.Clear(); } // This is just mimic what nodejs worker does var paramBinding = new ParameterBinding() { Name = outHttpName, Data = new TypedData() { Http = new RpcHttp() { StatusCode = "200", Body = retValue } } }; // Not exactly sure which one to use for what scenario, so just set both. response.InvocationResponse.OutputData.Add(paramBinding); response.InvocationResponse.ReturnValue = retValue; } await s_call.RequestStream.WriteAsync(response); }
internal StreamingMessage ProcessInvocationCancelRequest(StreamingMessage request) { return(null); }
/// <summary> /// Method to process a FunctionLoadRequest. /// FunctionLoadRequest should be processed sequentially. There is no point to process FunctionLoadRequest /// concurrently as a FunctionApp doesn't include a lot functions in general. Having this step sequential /// will make the Runspace-level initialization easier and more predictable. /// </summary> internal StreamingMessage ProcessFunctionLoadRequest(StreamingMessage request) { FunctionLoadRequest functionLoadRequest = request.FunctionLoadRequest; StreamingMessage response = NewStreamingMessageTemplate( request.RequestId, StreamingMessage.ContentOneofCase.FunctionLoadResponse, out StatusResult status); response.FunctionLoadResponse.FunctionId = functionLoadRequest.FunctionId; // The worker may occasionally receive multiple function load requests with // the same FunctionId. In order to make function load request idempotent, // the worker should ignore the duplicates. if (FunctionLoader.IsLoaded(functionLoadRequest.FunctionId)) { // If FunctionLoader considers this function loaded, this means // the previous request was successful, so respond accordingly. return(response); } // When a functionLoadRequest comes in, we check to see if a dependency download has failed in a previous call // or if PowerShell could not be initialized. If this is the case, mark this as a failed request // and submit the exception to the Host (runtime). if (_initTerminatingError != null) { status.Status = StatusResult.Types.Status.Failure; status.Exception = _initTerminatingError.ToRpcException(); return(response); } // Ideally, the initialization should happen when processing 'WorkerInitRequest', however, the 'WorkerInitRequest' // message doesn't provide information about the FunctionApp. That information is not available until the first // 'FunctionLoadRequest' comes in. Therefore, we run initialization here. // Also, we receive a FunctionLoadRequest when a proxy is configured. Proxies don't have the Metadata.Directory set // which would cause initialization issues with the PSModulePath. Since they don't have that set, we skip over them. if (!_isFunctionAppInitialized && !functionLoadRequest.Metadata.IsProxy) { try { _isFunctionAppInitialized = true; var rpcLogger = new RpcLogger(_msgStream); rpcLogger.SetContext(request.RequestId, null); _dependencyManager = new DependencyManager(request.FunctionLoadRequest.Metadata.Directory); var managedDependenciesPath = _dependencyManager.Initialize(request, rpcLogger); // Setup the FunctionApp root path and module path. FunctionLoader.SetupWellKnownPaths(functionLoadRequest, managedDependenciesPath); // Create the very first Runspace so the debugger has the target to attach to. // This PowerShell instance is shared by the first PowerShellManager instance created in the pool, // and the dependency manager (used to download dependent modules if needed). var pwsh = Utils.NewPwshInstance(); _powershellPool.Initialize(pwsh); // Start the download asynchronously if needed. _dependencyManager.StartDependencyInstallationIfNeeded(request, pwsh, rpcLogger); } catch (Exception e) { // Failure that happens during this step is terminating and we will need to return a failure response to // all subsequent 'FunctionLoadRequest'. Cache the exception so we can reuse it in future calls. _initTerminatingError = e; status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); return(response); } } try { // Load the metadata of the function. FunctionLoader.LoadFunction(functionLoadRequest); } catch (Exception e) { status.Status = StatusResult.Types.Status.Failure; status.Exception = e.ToRpcException(); } return(response); }
/// <summary> /// Write the outgoing message. /// </summary> internal void Write(StreamingMessage message) => WriteImplAsync(message).ConfigureAwait(false);
public OutboundEvent(string workerId, StreamingMessage message) : base(workerId, message, MessageOrigin.Host) { }
public Task ProcessRequestAsync(StreamingMessage request) { // Dispatch and return. Task.Run(() => ProcessRequestCoreAsync(request)); return(Task.CompletedTask); }