public static Dictionary <string, string> CreateMetadata(FunctionInstanceSnapshot snapshot) { if (snapshot == null) { throw new ArgumentNullException("snapshot"); } var metadata = new Dictionary <string, string>(); AddMetadataGuid(metadata, IdKey, snapshot.Id); AddMetadataString(metadata, DisplayTitleKey, snapshot.DisplayTitle); AddMetadataNullableDateTimeOffset(metadata, StartTimeKey, snapshot.StartTime); AddMetadataNullableDateTimeOffset(metadata, EndTimeKey, snapshot.EndTime); AddMetadataNullableBoolean(metadata, SucceededKey, snapshot.Succeeded); HeartbeatDescriptor heartbeat = snapshot.Heartbeat; if (heartbeat != null) { AddMetadataString(metadata, HeartbeatSharedContainerNameKey, heartbeat.SharedContainerName); AddMetadataString(metadata, HeartbeatSharedDirectoryNameKey, heartbeat.SharedDirectoryName); AddMetadataString(metadata, HeartbeatInstanceBlobNameKey, heartbeat.InstanceBlobName); AddMetadataInt32(metadata, HeartbeatExpirationInSecondsKey, heartbeat.ExpirationInSeconds); } return(metadata); }
private RecentInvocationEntry(Guid id, string displayTitle, DateTimeOffset?startTime, DateTimeOffset?endTime, bool?succeeded, HeartbeatDescriptor heartbeat) { _id = id; _displayTitle = displayTitle; _startTime = startTime; _endTime = endTime; _succeeded = succeeded; _heartbeat = heartbeat; }
private RecentInvocationEntry(Guid id, string displayTitle, DateTimeOffset? startTime, DateTimeOffset? endTime, bool? succeeded, HeartbeatDescriptor heartbeat) { _id = id; _displayTitle = displayTitle; _startTime = startTime; _endTime = endTime; _succeeded = succeeded; _heartbeat = heartbeat; }
private bool?HostInstanceHasHeartbeat(FunctionInstanceSnapshot snapshot) { HeartbeatDescriptor heartbeat = snapshot.Heartbeat; if (heartbeat == null) { return(null); } return(HostInstanceHasHeartbeat(heartbeat.SharedContainerName, heartbeat.SharedDirectoryName, heartbeat.InstanceBlobName, heartbeat.ExpirationInSeconds)); }
public static RecentInvocationEntry Create(IDictionary <string, string> metadata) { if (metadata == null) { throw new ArgumentNullException("metadata"); } Guid?id = GetMetadataNullableGuid(metadata, IdKey); if (!id.HasValue) { throw new InvalidOperationException("Invalid recent function instance ID"); } string displayTitle = GetMetadataString(metadata, DisplayTitleKey); DateTimeOffset?startTime = GetMetadataNullableDateTimeOffset(metadata, StartTimeKey); DateTimeOffset?endTime = GetMetadataNullableDateTimeOffset(metadata, EndTimeKey); bool? succeeded = GetMetadataNullableBoolean(metadata, SucceededKey); string heartbeatSharedContainerName = GetMetadataString(metadata, HeartbeatSharedContainerNameKey); string heartbeatSharedDirectoryName = GetMetadataString(metadata, HeartbeatSharedDirectoryNameKey); string heartbeatInstanceBlobName = GetMetadataString(metadata, HeartbeatInstanceBlobNameKey); int? heartbeatExpirationInSeconds = GetMetadataNullableInt32(metadata, HeartbeatExpirationInSecondsKey); HeartbeatDescriptor heartbeat; if (heartbeatSharedContainerName != null && heartbeatSharedDirectoryName != null && heartbeatInstanceBlobName != null && heartbeatExpirationInSeconds.HasValue) { heartbeat = new HeartbeatDescriptor { SharedContainerName = heartbeatSharedContainerName, SharedDirectoryName = heartbeatSharedDirectoryName, InstanceBlobName = heartbeatInstanceBlobName, ExpirationInSeconds = heartbeatExpirationInSeconds.Value }; } else { heartbeat = null; } var entry = new RecentInvocationEntry(id.Value, displayTitle, startTime, endTime, succeeded, heartbeat); entry.FunctionInstanceHeartbeatExpiry = GetMetadataNullableDateTime(metadata, FunctionInstanceHeartbeatExpiredTimeKey); return(entry); }
private static FunctionSnapshot CreateFunctionSnapshot(string queueName, HeartbeatDescriptor heartbeat, FunctionDescriptor function, DateTimeOffset hostVersion) { return(new FunctionSnapshot { HostVersion = hostVersion, Id = new FunctionIdentifier(queueName, function.Id).ToString(), QueueName = queueName, HeartbeatSharedContainerName = heartbeat != null ? heartbeat.SharedContainerName : null, HeartbeatSharedDirectoryName = heartbeat != null ? heartbeat.SharedDirectoryName : null, HeartbeatExpirationInSeconds = heartbeat != null ? (int?)heartbeat.ExpirationInSeconds : null, HostFunctionId = function.Id, FullName = function.FullName, ShortName = function.ShortName, Parameters = CreateParameterSnapshots(function.Parameters) }); }
private bool?HostInstanceHasHeartbeat(FunctionInstanceSnapshot snapshot) { if (snapshot.FunctionInstanceHeartbeatExpiry.HasValue) { var now = DateTime.UtcNow; return(snapshot.FunctionInstanceHeartbeatExpiry.Value > now); } HeartbeatDescriptor heartbeat = snapshot.Heartbeat; if (heartbeat == null) { return(null); } return(HostInstanceHasHeartbeat(heartbeat.SharedContainerName, heartbeat.SharedDirectoryName, heartbeat.InstanceBlobName, heartbeat.ExpirationInSeconds)); }
public static async Task<JobHostContext> CreateAndLogHostStartedAsync( JobHost host, IStorageAccountProvider storageAccountProvider, IQueueConfiguration queueConfiguration, ITypeLocator typeLocator, IJobActivator activator, INameResolver nameResolver, IConsoleProvider consoleProvider, JobHostConfiguration config, CancellationToken shutdownToken, CancellationToken cancellationToken, IHostIdProvider hostIdProvider = null, FunctionExecutor functionExecutor = null, IFunctionIndexProvider functionIndexProvider = null, IBindingProvider bindingProvider = null, IHostInstanceLoggerProvider hostInstanceLogerProvider = null, IFunctionInstanceLoggerProvider functionInstanceLoggerProvider = null, IFunctionOutputLoggerProvider functionOutputLoggerProvider = null, IBackgroundExceptionDispatcher backgroundExceptionDispatcher = null, SingletonManager singletonManager = null) { if (hostIdProvider == null) { hostIdProvider = new DynamicHostIdProvider(storageAccountProvider, () => functionIndexProvider); } IExtensionTypeLocator extensionTypeLocator = new ExtensionTypeLocator(typeLocator); if (backgroundExceptionDispatcher == null) { backgroundExceptionDispatcher = BackgroundExceptionDispatcher.Instance; } ContextAccessor<IMessageEnqueuedWatcher> messageEnqueuedWatcherAccessor = new ContextAccessor<IMessageEnqueuedWatcher>(); ContextAccessor<IBlobWrittenWatcher> blobWrittenWatcherAccessor = new ContextAccessor<IBlobWrittenWatcher>(); ISharedContextProvider sharedContextProvider = new SharedContextProvider(); // Create a wrapper TraceWriter that delegates to both the user // TraceWriter specified on Config (if present), as well as to Console TraceWriter trace = new ConsoleTraceWriter(config.Tracing, consoleProvider.Out); // Register system services with the service container config.AddService<INameResolver>(nameResolver); ExtensionConfigContext context = new ExtensionConfigContext { Config = config, Trace = trace, Host = host }; InvokeExtensionConfigProviders(context); IExtensionRegistry extensions = config.GetExtensions(); ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, backgroundExceptionDispatcher, messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, trace); if (bindingProvider == null) { bindingProvider = DefaultBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, extensions); } DefaultLoggerProvider loggerProvider = new DefaultLoggerProvider(storageAccountProvider, trace); if (singletonManager == null) { singletonManager = new SingletonManager(storageAccountProvider, backgroundExceptionDispatcher, config.Singleton, trace, config.NameResolver); } using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken)) { CancellationToken combinedCancellationToken = combinedCancellationSource.Token; await WriteSiteExtensionManifestAsync(combinedCancellationToken); IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken); IHostInstanceLogger hostInstanceLogger = null; if (hostInstanceLogerProvider != null) { hostInstanceLogger = await hostInstanceLogerProvider.GetAsync(combinedCancellationToken); } else { hostInstanceLogger = await((IHostInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken); } IFunctionInstanceLogger functionInstanceLogger = null; if (functionInstanceLoggerProvider != null) { functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken); } else { functionInstanceLogger = (IFunctionInstanceLogger)(await((IFunctionInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken)); } IFunctionOutputLogger functionOutputLogger = null; if (functionOutputLoggerProvider != null) { functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken); } else { functionOutputLogger = (IFunctionOutputLogger)(await((IFunctionOutputLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken)); } if (functionExecutor == null) { functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, backgroundExceptionDispatcher, trace); } if (functionIndexProvider == null) { functionIndexProvider = new FunctionIndexProvider(typeLocator, triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager); } IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken); IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace); IFunctionExecutor hostCallExecutor; IListener listener; HostOutputMessage hostOutputMessage; if (dashboardAccount != null) { string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken); string sharedQueueName = HostQueueNames.GetHostQueueName(hostId); IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient(); IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName); IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue, queueConfiguration, backgroundExceptionDispatcher, trace, functions, functionInstanceLogger, functionExecutor); Guid hostInstanceId = Guid.NewGuid(); string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N")); IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName); IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue, queueConfiguration, backgroundExceptionDispatcher, trace, functions, functionInstanceLogger, functionExecutor); HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor { SharedContainerName = HostContainerNames.Hosts, SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId, InstanceBlobName = hostInstanceId.ToString("N"), ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds }; IStorageBlockBlob blob = dashboardAccount.CreateBlobClient() .GetContainerReference(heartbeatDescriptor.SharedContainerName) .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName); IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob)); IEnumerable<MethodInfo> indexedMethods = functions.ReadAllMethods(); Assembly hostAssembly = GetHostAssembly(indexedMethods); string displayName = hostAssembly != null ? hostAssembly.GetName().Name : "Unknown"; hostOutputMessage = new DataOnlyHostOutputMessage { HostInstanceId = hostInstanceId, HostDisplayName = displayName, SharedQueueName = sharedQueueName, InstanceQueueName = instanceQueueName, Heartbeat = heartbeatDescriptor, WebJobRunIdentifier = WebJobRunIdentifier.Current }; hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken, functionExecutor); IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory, sharedQueueListenerFactory, instanceQueueListenerFactory); listener = CreateHostListener(hostListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken); // Publish this to Azure logging account so that a web dashboard can see it. await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken); } else { hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, functionExecutor); IListener factoryListener = new ListenerFactoryListener(functionsListenerFactory); IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener); listener = shutdownListener; hostOutputMessage = new DataOnlyHostOutputMessage(); } functionExecutor.HostOutputMessage = hostOutputMessage; IEnumerable<FunctionDescriptor> descriptors = functions.ReadAllDescriptors(); int descriptorsCount = descriptors.Count(); if (descriptorsCount == 0) { trace.Warning("No functions found. Try making job classes and methods public.", TraceSource.Indexing); } else { StringBuilder functionsTrace = new StringBuilder(); functionsTrace.AppendLine("Found the following functions:"); foreach (FunctionDescriptor descriptor in descriptors) { functionsTrace.AppendLine(descriptor.FullName); } trace.Info(functionsTrace.ToString(), TraceSource.Indexing); } return new JobHostContext(functions, hostCallExecutor, listener, trace); } }
public static async Task <JobHostContext> CreateAndLogHostStartedAsync( JobHost host, IStorageAccountProvider storageAccountProvider, IQueueConfiguration queueConfiguration, ITypeLocator typeLocator, IJobActivator activator, INameResolver nameResolver, IConsoleProvider consoleProvider, JobHostConfiguration config, CancellationToken shutdownToken, CancellationToken cancellationToken, IHostIdProvider hostIdProvider = null, FunctionExecutor functionExecutor = null, IFunctionIndexProvider functionIndexProvider = null, IBindingProvider bindingProvider = null, IHostInstanceLoggerProvider hostInstanceLogerProvider = null, IFunctionInstanceLoggerProvider functionInstanceLoggerProvider = null, IFunctionOutputLoggerProvider functionOutputLoggerProvider = null, IBackgroundExceptionDispatcher backgroundExceptionDispatcher = null, SingletonManager singletonManager = null) { if (hostIdProvider == null) { hostIdProvider = new DynamicHostIdProvider(storageAccountProvider, () => functionIndexProvider); } IExtensionTypeLocator extensionTypeLocator = new ExtensionTypeLocator(typeLocator); if (backgroundExceptionDispatcher == null) { backgroundExceptionDispatcher = BackgroundExceptionDispatcher.Instance; } ContextAccessor <IMessageEnqueuedWatcher> messageEnqueuedWatcherAccessor = new ContextAccessor <IMessageEnqueuedWatcher>(); ContextAccessor <IBlobWrittenWatcher> blobWrittenWatcherAccessor = new ContextAccessor <IBlobWrittenWatcher>(); ISharedContextProvider sharedContextProvider = new SharedContextProvider(); // Create a wrapper TraceWriter that delegates to both the user // TraceWriter specified on Config (if present), as well as to Console TraceWriter trace = new ConsoleTraceWriter(config.Tracing, consoleProvider.Out); // Register system services with the service container config.AddService <INameResolver>(nameResolver); ExtensionConfigContext context = new ExtensionConfigContext { Config = config, Trace = trace, Host = host }; InvokeExtensionConfigProviders(context); IExtensionRegistry extensions = config.GetExtensions(); ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, backgroundExceptionDispatcher, messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, trace); if (bindingProvider == null) { bindingProvider = DefaultBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, extensions); } DefaultLoggerProvider loggerProvider = new DefaultLoggerProvider(storageAccountProvider, trace); if (singletonManager == null) { singletonManager = new SingletonManager(storageAccountProvider, backgroundExceptionDispatcher, config.Singleton, trace, config.NameResolver); } using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken)) { CancellationToken combinedCancellationToken = combinedCancellationSource.Token; await WriteSiteExtensionManifestAsync(combinedCancellationToken); IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken); IHostInstanceLogger hostInstanceLogger = null; if (hostInstanceLogerProvider != null) { hostInstanceLogger = await hostInstanceLogerProvider.GetAsync(combinedCancellationToken); } else { hostInstanceLogger = await((IHostInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken); } IFunctionInstanceLogger functionInstanceLogger = null; if (functionInstanceLoggerProvider != null) { functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken); } else { functionInstanceLogger = (IFunctionInstanceLogger)(await((IFunctionInstanceLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken)); } IFunctionOutputLogger functionOutputLogger = null; if (functionOutputLoggerProvider != null) { functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken); } else { functionOutputLogger = (IFunctionOutputLogger)(await((IFunctionOutputLoggerProvider)loggerProvider).GetAsync(combinedCancellationToken)); } if (functionExecutor == null) { functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, backgroundExceptionDispatcher, trace, config.FunctionTimeout); } if (functionIndexProvider == null) { functionIndexProvider = new FunctionIndexProvider(typeLocator, triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager); } IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken); IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace); IFunctionExecutor hostCallExecutor; IListener listener; HostOutputMessage hostOutputMessage; if (dashboardAccount != null) { string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken); string sharedQueueName = HostQueueNames.GetHostQueueName(hostId); IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient(); IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName); IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue, queueConfiguration, backgroundExceptionDispatcher, trace, functions, functionInstanceLogger, functionExecutor); Guid hostInstanceId = Guid.NewGuid(); string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N")); IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName); IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue, queueConfiguration, backgroundExceptionDispatcher, trace, functions, functionInstanceLogger, functionExecutor); HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor { SharedContainerName = HostContainerNames.Hosts, SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId, InstanceBlobName = hostInstanceId.ToString("N"), ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds }; IStorageBlockBlob blob = dashboardAccount.CreateBlobClient() .GetContainerReference(heartbeatDescriptor.SharedContainerName) .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName); IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob)); IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods(); Assembly hostAssembly = GetHostAssembly(indexedMethods); string displayName = hostAssembly != null?hostAssembly.GetName().Name : "Unknown"; hostOutputMessage = new DataOnlyHostOutputMessage { HostInstanceId = hostInstanceId, HostDisplayName = displayName, SharedQueueName = sharedQueueName, InstanceQueueName = instanceQueueName, Heartbeat = heartbeatDescriptor, WebJobRunIdentifier = WebJobRunIdentifier.Current }; hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken, functionExecutor); IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory, sharedQueueListenerFactory, instanceQueueListenerFactory); listener = CreateHostListener(hostListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken); // Publish this to Azure logging account so that a web dashboard can see it. await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken); } else { hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, functionExecutor); IListener factoryListener = new ListenerFactoryListener(functionsListenerFactory); IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener); listener = shutdownListener; hostOutputMessage = new DataOnlyHostOutputMessage(); } functionExecutor.HostOutputMessage = hostOutputMessage; IEnumerable <FunctionDescriptor> descriptors = functions.ReadAllDescriptors(); int descriptorsCount = descriptors.Count(); if (descriptorsCount == 0) { trace.Warning(string.Format("No job functions found. Try making your job classes and methods public. {0}", Constants.ExtensionInitializationMessage), TraceSource.Indexing); } else { StringBuilder functionsTrace = new StringBuilder(); functionsTrace.AppendLine("Found the following functions:"); foreach (FunctionDescriptor descriptor in descriptors) { functionsTrace.AppendLine(descriptor.FullName); } trace.Info(functionsTrace.ToString(), TraceSource.Indexing); } return(new JobHostContext(functions, hostCallExecutor, listener, trace)); } }
private static FunctionSnapshot CreateFunctionSnapshot(string queueName, HeartbeatDescriptor heartbeat, FunctionDescriptor function, DateTimeOffset hostVersion) { return new FunctionSnapshot { HostVersion = hostVersion, Id = new FunctionIdentifier(queueName, function.Id).ToString(), QueueName = queueName, HeartbeatSharedContainerName = heartbeat != null ? heartbeat.SharedContainerName : null, HeartbeatSharedDirectoryName = heartbeat != null ? heartbeat.SharedDirectoryName : null, HeartbeatExpirationInSeconds = heartbeat != null ? (int?)heartbeat.ExpirationInSeconds : null, HostFunctionId = function.Id, FullName = function.FullName, ShortName = function.ShortName, Parameters = CreateParameterSnapshots(function.Parameters) }; }
// Do the full runtime intitialization. This includes static initialization. // This mainly means: // - indexing the functions // - spinning up the listeners (so connecting to the services) public static async Task <JobHostContext> CreateJobHostContextAsync( this JobHostConfiguration config, ServiceProviderWrapper services, // Results from first phase JobHost host, CancellationToken shutdownToken, CancellationToken cancellationToken) { FunctionExecutor functionExecutor = services.GetService <FunctionExecutor>(); IFunctionIndexProvider functionIndexProvider = services.GetService <IFunctionIndexProvider>(); ITriggerBindingProvider triggerBindingProvider = services.GetService <ITriggerBindingProvider>(); IBindingProvider bindingProvider = services.GetService <IBindingProvider>(); SingletonManager singletonManager = services.GetService <SingletonManager>(); IJobActivator activator = services.GetService <IJobActivator>(); IHostIdProvider hostIdProvider = services.GetService <IHostIdProvider>(); INameResolver nameResolver = services.GetService <INameResolver>(); IExtensionRegistry extensions = services.GetExtensions(); IStorageAccountProvider storageAccountProvider = services.GetService <IStorageAccountProvider>(); ILoggerFactory loggerFactory = services.GetService <ILoggerFactory>(); IFunctionResultAggregatorFactory aggregatorFactory = services.GetService <IFunctionResultAggregatorFactory>(); IAsyncCollector <FunctionInstanceLogEntry> functionEventCollector = null; // Create the aggregator if all the pieces are configured IAsyncCollector <FunctionInstanceLogEntry> aggregator = null; if (loggerFactory != null && aggregatorFactory != null && config.Aggregator.IsEnabled) { aggregator = aggregatorFactory.Create(config.Aggregator.BatchSize, config.Aggregator.FlushTimeout, loggerFactory); } IQueueConfiguration queueConfiguration = services.GetService <IQueueConfiguration>(); var blobsConfiguration = config.Blobs; TraceWriter trace = services.GetService <TraceWriter>(); IAsyncCollector <FunctionInstanceLogEntry> registeredFunctionEventCollector = services.GetService <IAsyncCollector <FunctionInstanceLogEntry> >(); if (registeredFunctionEventCollector != null && aggregator != null) { // If there are both an aggregator and a registered FunctionEventCollector, wrap them in a composite functionEventCollector = new CompositeFunctionEventCollector(new[] { registeredFunctionEventCollector, aggregator }); } else { // Otherwise, take whichever one is null (or use null if both are) functionEventCollector = aggregator ?? registeredFunctionEventCollector; } IWebJobsExceptionHandler exceptionHandler = services.GetService <IWebJobsExceptionHandler>(); if (exceptionHandler != null) { exceptionHandler.Initialize(host); } bool hasFastTableHook = services.GetService <IAsyncCollector <FunctionInstanceLogEntry> >() != null; bool noDashboardStorage = config.DashboardConnectionString == null; // Only testing will override these interfaces. IHostInstanceLoggerProvider hostInstanceLoggerProvider = services.GetService <IHostInstanceLoggerProvider>(); IFunctionInstanceLoggerProvider functionInstanceLoggerProvider = services.GetService <IFunctionInstanceLoggerProvider>(); IFunctionOutputLoggerProvider functionOutputLoggerProvider = services.GetService <IFunctionOutputLoggerProvider>(); if (hostInstanceLoggerProvider == null && functionInstanceLoggerProvider == null && functionOutputLoggerProvider == null) { if (hasFastTableHook && noDashboardStorage) { var loggerProvider = new FastTableLoggerProvider(trace); hostInstanceLoggerProvider = loggerProvider; functionInstanceLoggerProvider = loggerProvider; functionOutputLoggerProvider = loggerProvider; } else { var loggerProvider = new DefaultLoggerProvider(storageAccountProvider, trace); hostInstanceLoggerProvider = loggerProvider; functionInstanceLoggerProvider = loggerProvider; functionOutputLoggerProvider = loggerProvider; } } using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken)) { CancellationToken combinedCancellationToken = combinedCancellationSource.Token; await WriteSiteExtensionManifestAsync(combinedCancellationToken); IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken); IHostInstanceLogger hostInstanceLogger = await hostInstanceLoggerProvider.GetAsync(combinedCancellationToken); IFunctionInstanceLogger functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken); IFunctionOutputLogger functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken); if (functionExecutor == null) { var extensionRegistry = config.GetService <IExtensionRegistry>(); var globalFunctionFilters = extensionRegistry.GetFunctionFilters(); functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, exceptionHandler, trace, functionEventCollector, loggerFactory, globalFunctionFilters); services.AddService(functionExecutor); } if (functionIndexProvider == null) { functionIndexProvider = new FunctionIndexProvider( services.GetService <ITypeLocator>(), triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager, trace, loggerFactory); // Important to set this so that the func we passed to DynamicHostIdProvider can pick it up. services.AddService <IFunctionIndexProvider>(functionIndexProvider); } IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken); IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace, loggerFactory); IFunctionExecutor hostCallExecutor; IListener listener; HostOutputMessage hostOutputMessage; string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken); if (string.Compare(config.HostId, hostId, StringComparison.OrdinalIgnoreCase) != 0) { // if this isn't a static host ID, provide the HostId on the config // so it is accessible config.HostId = hostId; } if (dashboardAccount == null) { hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, functionExecutor); IListener factoryListener = new ListenerFactoryListener(functionsListenerFactory); IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener); listener = shutdownListener; hostOutputMessage = new DataOnlyHostOutputMessage(); } else { string sharedQueueName = HostQueueNames.GetHostQueueName(hostId); IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient(); IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName); IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue, queueConfiguration, exceptionHandler, trace, loggerFactory, functions, functionInstanceLogger, functionExecutor); Guid hostInstanceId = Guid.NewGuid(); string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N")); IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName); IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue, queueConfiguration, exceptionHandler, trace, loggerFactory, functions, functionInstanceLogger, functionExecutor); HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor { SharedContainerName = HostContainerNames.Hosts, SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId, InstanceBlobName = hostInstanceId.ToString("N"), ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds }; IStorageBlockBlob blob = dashboardAccount.CreateBlobClient() .GetContainerReference(heartbeatDescriptor.SharedContainerName) .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName); IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob)); IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods(); Assembly hostAssembly = GetHostAssembly(indexedMethods); string displayName = hostAssembly != null?AssemblyNameCache.GetName(hostAssembly).Name : "Unknown"; hostOutputMessage = new DataOnlyHostOutputMessage { HostInstanceId = hostInstanceId, HostDisplayName = displayName, SharedQueueName = sharedQueueName, InstanceQueueName = instanceQueueName, Heartbeat = heartbeatDescriptor, WebJobRunIdentifier = WebJobRunIdentifier.Current }; hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand, exceptionHandler, shutdownToken, functionExecutor); IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory, sharedQueueListenerFactory, instanceQueueListenerFactory); listener = CreateHostListener(hostListenerFactory, heartbeatCommand, exceptionHandler, shutdownToken); // Publish this to Azure logging account so that a web dashboard can see it. await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken); } functionExecutor.HostOutputMessage = hostOutputMessage; IEnumerable <FunctionDescriptor> descriptors = functions.ReadAllDescriptors(); int descriptorsCount = descriptors.Count(); ILogger startupLogger = loggerFactory?.CreateLogger(LogCategories.Startup); if (config.UsingDevelopmentSettings) { string msg = "Development settings applied"; trace.Verbose(msg); startupLogger?.LogDebug(msg); } if (descriptorsCount == 0) { string msg = string.Format("No job functions found. Try making your job classes and methods public. {0}", Constants.ExtensionInitializationMessage); trace.Warning(msg, Host.TraceSource.Indexing); startupLogger?.LogWarning(msg); } else { StringBuilder functionsTrace = new StringBuilder(); functionsTrace.AppendLine("Found the following functions:"); foreach (FunctionDescriptor descriptor in descriptors) { functionsTrace.AppendLine(descriptor.FullName); } string msg = functionsTrace.ToString(); trace.Info(msg, Host.TraceSource.Indexing); startupLogger?.LogInformation(msg); } return(new JobHostContext( functions, hostCallExecutor, listener, trace, functionEventCollector, loggerFactory)); } }
public void ProcessHostStarted(HostStartedMessage message) { if (message == null) { throw new ArgumentNullException("message"); } string hostId = message.SharedQueueName; DateTimeOffset hostVersion = message.EnqueuedOn; DateTime hostVersionUtc = hostVersion.UtcDateTime; IEnumerable <FunctionDescriptor> messageFunctions = message.Functions ?? Enumerable.Empty <FunctionDescriptor>(); HostSnapshot newSnapshot = new HostSnapshot { HostVersion = hostVersion, FunctionIds = CreateFunctionIds(messageFunctions) }; if (_hostIndexManager.UpdateOrCreateIfLatest(hostId, newSnapshot)) { IEnumerable <VersionedMetadata> existingFunctions = _functionIndexManager.List(hostId); IEnumerable <VersionedMetadata> removedFunctions = existingFunctions .Where((f) => !newSnapshot.FunctionIds.Any(i => f.Id == i)); foreach (VersionedMetadata removedFunction in removedFunctions) { // Remove all functions no longer in our list (unless they exist with a later host version than // ours). string fullId = new FunctionIdentifier(hostId, removedFunction.Id).ToString(); _functionIndexManager.DeleteIfLatest(fullId, hostVersion, removedFunction.ETag, removedFunction.Version); } HeartbeatDescriptor heartbeat = message.Heartbeat; IEnumerable <FunctionDescriptor> addedFunctions = messageFunctions .Where((d) => !existingFunctions.Any(f => d.Id == f.Id)); foreach (FunctionDescriptor addedFunction in addedFunctions) { // Create any functions just appearing in our list (or update existing ones if they're earlier than // ours). FunctionSnapshot snapshot = CreateFunctionSnapshot(hostId, heartbeat, addedFunction, hostVersion); _functionIndexManager.CreateOrUpdateIfLatest(snapshot); } // Update any functions appearing in both lists provided they're still earlier than ours (or create them // if they've since been deleted). var possiblyUpdatedFunctions = existingFunctions .Join(messageFunctions, (f) => f.Id, (d) => d.Id, (f, d) => new { Descriptor = d, HostVersion = f.Version, ETag = f.ETag }); foreach (var possiblyUpdatedFunction in possiblyUpdatedFunctions) { FunctionSnapshot snapshot = CreateFunctionSnapshot(hostId, heartbeat, possiblyUpdatedFunction.Descriptor, hostVersion); _functionIndexManager.UpdateOrCreateIfLatest(snapshot, possiblyUpdatedFunction.ETag, possiblyUpdatedFunction.HostVersion); } } // Delete any functions we may have added or updated that are no longer in the index. // The create and update calls above may have occured after another instance started processing a later // version of this host. If that instance had already read the existing function list before we added the // function, it would think the function had already been deleted. In the end, we can't leave a function // around unless it's still in the host index after we've added or updated it. HostSnapshot finalSnapshot = _hostIndexManager.Read(hostId); IEnumerable <FunctionDescriptor> functionsRemovedAfterThisHostVersion; if (finalSnapshot == null) { functionsRemovedAfterThisHostVersion = messageFunctions; } else if (finalSnapshot.HostVersion.UtcDateTime > hostVersionUtc) { // Note that we base the list of functions to delete on what's in the HostStartedMessage, not what's in // the addedFunctions and possibleUpdatedFunctions variables, as this instance could have been aborted // and resumed and could lose state like those local variables. functionsRemovedAfterThisHostVersion = messageFunctions.Where( f => !finalSnapshot.FunctionIds.Any((i) => f.Id == i)); } else { functionsRemovedAfterThisHostVersion = Enumerable.Empty <FunctionDescriptor>(); } foreach (FunctionDescriptor functionNoLongerInSnapshot in functionsRemovedAfterThisHostVersion) { string fullId = new FunctionIdentifier(hostId, functionNoLongerInSnapshot.Id).ToString(); _functionIndexManager.DeleteIfLatest(fullId, hostVersionUtc); } _functionIndexVersionManager.UpdateOrCreateIfLatest(newSnapshot.HostVersion); }
public bool Setup( IFunctionIndex functions, IListenerFactory functionsListenerFactory, out IFunctionExecutor hostCallExecutor, out IListener listener, out HostOutputMessage hostOutputMessage, string hostId, CancellationToken shutdownToken) { string sharedQueueName = HostQueueNames.GetHostQueueName(hostId); var sharedQueue = sharedQueueName; IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(_storageServices, sharedQueue, _exceptionHandler, _loggerFactory, functions, _functionInstanceLogger, _functionExecutor); Guid hostInstanceId = Guid.NewGuid(); string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N")); var instanceQueue = instanceQueueName; IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(_storageServices, instanceQueue, _exceptionHandler, _loggerFactory, functions, _functionInstanceLogger, _functionExecutor); HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor { SharedContainerName = HostContainerNames.Hosts, SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId, InstanceBlobName = hostInstanceId.ToString("N"), ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds }; var dashboardAccount = _storageAccountOptions.GetDashboardStorageAccount(); var blob = dashboardAccount.CreateCloudBlobClient() .GetContainerReference(heartbeatDescriptor.SharedContainerName) .GetBlockBlobReference(heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName); IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand(blob)); IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods(); Assembly hostAssembly = JobHostContextFactory.GetHostAssembly(indexedMethods); string displayName = hostAssembly != null?AssemblyNameCache.GetName(hostAssembly).Name : "Unknown"; hostOutputMessage = new JobHostContextFactory.DataOnlyHostOutputMessage { HostInstanceId = hostInstanceId, HostDisplayName = displayName, SharedQueueName = sharedQueueName, InstanceQueueName = instanceQueueName, Heartbeat = heartbeatDescriptor, WebJobRunIdentifier = WebJobRunIdentifier.Current }; hostCallExecutor = JobHostContextFactory.CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand, _exceptionHandler, shutdownToken, _functionExecutor); IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory, sharedQueueListenerFactory, instanceQueueListenerFactory); listener = JobHostContextFactory.CreateHostListener(hostListenerFactory, _sharedQueueHandler, heartbeatCommand, _exceptionHandler, shutdownToken); return(true); }
internal static async Task <JobHostContext> CreateAndLogHostStartedAsync( IStorageAccountProvider storageAccountProvider, IFunctionIndexProvider functionIndexProvider, IBindingProvider bindingProvider, IHostIdProvider hostIdProvider, IHostInstanceLoggerProvider hostInstanceLoggerProvider, IFunctionInstanceLoggerProvider functionInstanceLoggerProvider, IFunctionOutputLoggerProvider functionOutputLoggerProvider, IQueueConfiguration queueConfiguration, IBackgroundExceptionDispatcher backgroundExceptionDispatcher, IConsoleProvider consoleProvider, CancellationToken shutdownToken, CancellationToken cancellationToken) { using (CancellationTokenSource combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, shutdownToken)) { CancellationToken combinedCancellationToken = combinedCancellationSource.Token; await WriteSiteExtensionManifestAsync(combinedCancellationToken); IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync( combinedCancellationToken); IHostInstanceLogger hostInstanceLogger = await hostInstanceLoggerProvider.GetAsync( combinedCancellationToken); IFunctionInstanceLogger functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync( combinedCancellationToken); IFunctionOutputLogger functionOutputLogger = await functionOutputLoggerProvider.GetAsync( combinedCancellationToken); IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken); IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll()); FunctionExecutor executor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, backgroundExceptionDispatcher); TextWriter consoleOut = consoleProvider.Out; IFunctionExecutor hostCallExecutor; IListener listener; HostOutputMessage hostOutputMessage; if (dashboardAccount != null) { string hostId = await hostIdProvider.GetHostIdAsync(cancellationToken); string sharedQueueName = HostQueueNames.GetHostQueueName(hostId); IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient(); IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName); IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue, queueConfiguration, backgroundExceptionDispatcher, consoleOut, functions, functionInstanceLogger); Guid hostInstanceId = Guid.NewGuid(); string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N")); IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName); IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue, queueConfiguration, backgroundExceptionDispatcher, consoleOut, functions, functionInstanceLogger); HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor { SharedContainerName = HostContainerNames.Hosts, SharedDirectoryName = HostDirectoryNames.Heartbeats + "/" + hostId, InstanceBlobName = hostInstanceId.ToString("N"), ExpirationInSeconds = (int)HeartbeatIntervals.ExpirationInterval.TotalSeconds }; IRecurrentCommand heartbeatCommand = new UpdateHostHeartbeatCommand(new HeartbeatCommand( dashboardAccount, heartbeatDescriptor.SharedContainerName, heartbeatDescriptor.SharedDirectoryName + "/" + heartbeatDescriptor.InstanceBlobName)); IEnumerable <MethodInfo> indexedMethods = functions.ReadAllMethods(); Assembly hostAssembly = GetHostAssembly(indexedMethods); string displayName = hostAssembly != null?hostAssembly.GetName().Name : "Unknown"; hostOutputMessage = new DataOnlyHostOutputMessage { HostInstanceId = hostInstanceId, HostDisplayName = displayName, SharedQueueName = sharedQueueName, InstanceQueueName = instanceQueueName, Heartbeat = heartbeatDescriptor, WebJobRunIdentifier = WebJobRunIdentifier.Current }; hostCallExecutor = CreateHostCallExecutor(instanceQueueListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken, executor); IListenerFactory hostListenerFactory = new CompositeListenerFactory(functionsListenerFactory, sharedQueueListenerFactory, instanceQueueListenerFactory); listener = CreateHostListener(hostListenerFactory, heartbeatCommand, backgroundExceptionDispatcher, shutdownToken, executor); // Publish this to Azure logging account so that a web dashboard can see it. await LogHostStartedAsync(functions, hostOutputMessage, hostInstanceLogger, combinedCancellationToken); } else { hostCallExecutor = new ShutdownFunctionExecutor(shutdownToken, executor); IListener factoryListener = new ListenerFactoryListener(functionsListenerFactory, executor); IListener shutdownListener = new ShutdownListener(shutdownToken, factoryListener); listener = shutdownListener; hostOutputMessage = new DataOnlyHostOutputMessage(); } executor.HostOutputMessage = hostOutputMessage; IEnumerable <FunctionDescriptor> descriptors = functions.ReadAllDescriptors(); int descriptorsCount = descriptors.Count(); if (descriptorsCount == 0) { consoleOut.WriteLine( "No functions found. Try making job classes and methods public."); } else { consoleOut.WriteLine("Found the following functions:"); foreach (FunctionDescriptor descriptor in descriptors) { consoleOut.WriteLine(descriptor.FullName); } } return(new JobHostContext(functions, hostCallExecutor, listener, consoleOut)); } }
public static RecentInvocationEntry Create(IDictionary<string, string> metadata) { if (metadata == null) { throw new ArgumentNullException("metadata"); } Guid? id = GetMetadataNullableGuid(metadata, IdKey); if (!id.HasValue) { throw new InvalidOperationException("Invalid recent function instance ID"); } string displayTitle = GetMetadataString(metadata, DisplayTitleKey); DateTimeOffset? startTime = GetMetadataNullableDateTimeOffset(metadata, StartTimeKey); DateTimeOffset? endTime = GetMetadataNullableDateTimeOffset(metadata, EndTimeKey); bool? succeeded = GetMetadataNullableBoolean(metadata, SucceededKey); string heartbeatSharedContainerName = GetMetadataString(metadata, HeartbeatSharedContainerNameKey); string heartbeatSharedDirectoryName = GetMetadataString(metadata, HeartbeatSharedDirectoryNameKey); string heartbeatInstanceBlobName = GetMetadataString(metadata, HeartbeatInstanceBlobNameKey); int? heartbeatExpirationInSeconds = GetMetadataNullableInt32(metadata, HeartbeatExpirationInSecondsKey); HeartbeatDescriptor heartbeat; if (heartbeatSharedContainerName != null && heartbeatSharedDirectoryName != null && heartbeatInstanceBlobName != null && heartbeatExpirationInSeconds.HasValue) { heartbeat = new HeartbeatDescriptor { SharedContainerName = heartbeatSharedContainerName, SharedDirectoryName = heartbeatSharedDirectoryName, InstanceBlobName = heartbeatInstanceBlobName, ExpirationInSeconds = heartbeatExpirationInSeconds.Value }; } else { heartbeat = null; } return new RecentInvocationEntry(id.Value, displayTitle, startTime, endTime, succeeded, heartbeat); }