diff --git a/CustomDictionary.xml b/CustomDictionary.xml
index 3a731bef80..de5e246bbf 100644
--- a/CustomDictionary.xml
+++ b/CustomDictionary.xml
@@ -73,6 +73,7 @@
ScopeId
FunctionMetadata
Debounce
+ ScriptHost
@@ -92,6 +93,7 @@
SingletonAttribute
ScopeId
FunctionMetadata
+ ScriptHost
diff --git a/src/WebJobs.Script.WebHost/App_Start/AutofacBootstrap.cs b/src/WebJobs.Script.WebHost/App_Start/AutofacBootstrap.cs
index 32dd9b0ab2..f904fd916c 100644
--- a/src/WebJobs.Script.WebHost/App_Start/AutofacBootstrap.cs
+++ b/src/WebJobs.Script.WebHost/App_Start/AutofacBootstrap.cs
@@ -60,7 +60,7 @@ internal static void Initialize(ContainerBuilder builder)
WebHookReceiverManager webHookRecieverManager = new WebHookReceiverManager(secretManager);
builder.RegisterInstance(webHookRecieverManager);
- Task.Run(() => scriptHostManager.RunAndBlock(CancellationToken.None));
+ HostingEnvironment.QueueBackgroundWorkItem((ct) => scriptHostManager.RunAndBlock(ct));
}
}
}
\ No newline at end of file
diff --git a/src/WebJobs.Script/Host/ScriptHost.cs b/src/WebJobs.Script/Host/ScriptHost.cs
index c88cffaf4e..f4af3996c5 100644
--- a/src/WebJobs.Script/Host/ScriptHost.cs
+++ b/src/WebJobs.Script/Host/ScriptHost.cs
@@ -24,7 +24,6 @@ public class ScriptHost : JobHost
private const string HostAssemblyName = "ScriptHost";
private const string HostConfigFileName = "host.json";
internal const string FunctionConfigFileName = "function.json";
- private readonly TraceWriter _traceWriter;
private readonly AutoResetEvent _restartEvent = new AutoResetEvent(false);
private Action _restart;
private FileSystemWatcher _fileWatcher;
@@ -38,12 +37,12 @@ protected ScriptHost(ScriptHostConfiguration scriptConfig)
if (scriptConfig.FileLoggingEnabled)
{
string hostLogFilePath = Path.Combine(scriptConfig.RootLogPath, "Host");
- _traceWriter = new FileTraceWriter(hostLogFilePath, TraceLevel.Verbose);
- scriptConfig.HostConfig.Tracing.Tracers.Add(_traceWriter);
+ TraceWriter = new FileTraceWriter(hostLogFilePath, TraceLevel.Verbose);
+ scriptConfig.HostConfig.Tracing.Tracers.Add(TraceWriter);
}
else
{
- _traceWriter = NullTraceWriter.Instance;
+ TraceWriter = NullTraceWriter.Instance;
}
if (scriptConfig.TraceWriter != null)
@@ -75,8 +74,8 @@ protected ScriptHost(ScriptHostConfiguration scriptConfig)
// restart after ALL the operations are complete and there is a quiet period.
_restart = (e) =>
{
- _traceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath));
- _traceWriter.Verbose("Host configuration has changed. Signaling restart.");
+ TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "File change of type '{0}' detected for '{1}'", e.ChangeType, e.FullPath));
+ TraceWriter.Verbose("Host configuration has changed. Signaling restart.");
// signal host restart
_restartEvent.Set();
@@ -87,6 +86,8 @@ protected ScriptHost(ScriptHostConfiguration scriptConfig)
_directoryCountSnapshot = Directory.EnumerateDirectories(ScriptConfig.RootScriptPath).Count();
}
+ public TraceWriter TraceWriter { get; private set; }
+
public ScriptHostConfiguration ScriptConfig { get; private set; }
public Collection Functions { get; private set; }
@@ -134,7 +135,7 @@ protected virtual void Initialize()
File.WriteAllText(hostConfigFilePath, "{}");
}
- _traceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath));
+ TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath));
string json = File.ReadAllText(hostConfigFilePath);
JObject hostConfig = JObject.Parse(json);
ApplyConfiguration(hostConfig, ScriptConfig);
@@ -143,7 +144,7 @@ protected virtual void Initialize()
Collection functions = ReadFunctions(ScriptConfig, descriptionProviders);
string defaultNamespace = "Host";
string typeName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", defaultNamespace, "Functions");
- _traceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Generating {0} job function(s)", functions.Count));
+ TraceWriter.Verbose(string.Format(CultureInfo.InvariantCulture, "Generating {0} job function(s)", functions.Count));
Type type = FunctionGenerator.Generate(HostAssemblyName, typeName, functions);
List types = new List();
types.Add(type);
@@ -168,14 +169,13 @@ public static ScriptHost Create(ScriptHostConfiguration scriptConfig = null)
}
ScriptHost scriptHost = new ScriptHost(scriptConfig);
-
try
{
scriptHost.Initialize();
}
- catch (Exception e)
+ catch (Exception ex)
{
- scriptHost._traceWriter.Error("Script Host initialization failed", e);
+ scriptHost.TraceWriter.Error("ScriptHost initialization failed", ex);
throw;
}
diff --git a/src/WebJobs.Script/Host/ScriptHostManager.cs b/src/WebJobs.Script/Host/ScriptHostManager.cs
index 1897f48ac6..430f4dee97 100644
--- a/src/WebJobs.Script/Host/ScriptHostManager.cs
+++ b/src/WebJobs.Script/Host/ScriptHostManager.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Azure.WebJobs.Host;
namespace Microsoft.Azure.WebJobs.Script
{
@@ -27,6 +28,7 @@ public class ScriptHostManager : IDisposable
private bool _disposed;
private bool _stopped;
private AutoResetEvent _stopEvent = new AutoResetEvent(false);
+ private TraceWriter _traceWriter;
public ScriptHostManager(ScriptHostConfiguration config)
{
@@ -53,44 +55,60 @@ public ScriptHost Instance
// host level configuration files change
do
{
- IsRunning = false;
-
- // Create a new host config, but keep the host id from existing one
- _config.HostConfig = new JobHostConfiguration
+ try
{
- HostId = _config.HostConfig.HostId
- };
- ScriptHost newInstance = ScriptHost.Create(_config);
-
- // TODO: consider using StartAsync here to speed up
- // restarts.
- newInstance.Start();
- lock (_liveInstances)
+ IsRunning = false;
+
+ // Create a new host config, but keep the host id from existing one
+ _config.HostConfig = new JobHostConfiguration
+ {
+ HostId = _config.HostConfig.HostId
+ };
+ ScriptHost newInstance = ScriptHost.Create(_config);
+ _traceWriter = newInstance.TraceWriter;
+
+ newInstance.Start();
+ lock (_liveInstances)
+ {
+ _liveInstances.Add(newInstance);
+ }
+ _currentInstance = newInstance;
+ OnHostStarted();
+
+ // only after ALL initialization is complete do we set this flag
+ IsRunning = true;
+
+ // Wait for a restart signal. This event will automatically reset.
+ // While we're restarting, it is possible for another restart to be
+ // signaled. That is fine - the restart will be processed immediately
+ // once we get to this line again. The important thing is that these
+ // restarts are only happening on a single thread.
+ WaitHandle.WaitAny(new WaitHandle[] {
+ newInstance.RestartEvent,
+ _stopEvent
+ });
+
+ // Orphan the current host instance. We're stopping it, so it won't listen for any new functions
+ // it will finish any currently executing functions and then clean itself up.
+ // Spin around and create a new host instance.
+ Task tIgnore = Orphan(newInstance);
+ }
+ catch (Exception ex)
{
- _liveInstances.Add(newInstance);
+ // We need to keep the host running, so we catch and log any errors
+ // then restart the host
+ if (_traceWriter != null)
+ {
+ _traceWriter.Error("A ScriptHost error occurred", ex);
+ }
+
+ // Wait for a short period of time before restarting to
+ // avoid cases where a host level config error might cause
+ // a rapid restart cycle
+ Task.Delay(5000).Wait();
}
- _currentInstance = newInstance;
- OnHostStarted();
-
- // only after ALL initialization is complete do we set this flag
- IsRunning = true;
-
- // Wait for a restart signal. This event will automatically reset.
- // While we're restarting, it is possible for another restart to be
- // signaled. That is fine - the restart will be processed immediately
- // once we get to this line again. The important thing is that these
- // restarts are only happening on a single thread.
- WaitHandle.WaitAny(new WaitHandle[] {
- newInstance.RestartEvent,
- _stopEvent
- });
-
- // Orphan the current host instance. We're stopping it, so it won't listen for any new functions
- // it will finish any currently executing functions and then clean itself up.
- // Spin around and create a new host instance.
- Task tIgnore = Orphan(newInstance);
}
- while (!_stopped);
+ while (!_stopped && !cancellationToken.IsCancellationRequested);
}
// Let the existing host instance finish currently executing functions.