Skip to content

Commit

Permalink
Merge pull request #15 from microsoft/fix/bot-service-timeout-issues
Browse files Browse the repository at this point in the history
Improve how the bot service reports its starting status to windows
  • Loading branch information
dcherubini authored Aug 18, 2021
2 parents 3cfb8e1 + 859497d commit f698795
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 42 deletions.
38 changes: 0 additions & 38 deletions src/BotService/HostService.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.ServiceProcess;
using Application.Common.Config;
using Application.Interfaces.Common;
using BotService.Infrastructure.WindowsService;
using Infrastructure.Core.CosmosDbData.Interfaces;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -18,14 +19,20 @@ public static void RunAsCustomService(this IWebHost host)
ServiceBase.Run(webHostService);
}

public static void SetupAndRegisterBotService(this IWebHost host)
public static void SetupDatabase(this IWebHost host)
{
var logger = host.Services.GetService<ILogger<IWebHost>>();

logger.LogInformation("Setting up database schema");
var cosmosDbSetup = host.Services.GetService<ICosmosDbSetup>();
cosmosDbSetup.SetupDatabaseAsync().Wait();
}

public static void RegisterBotService(this IWebHost host)
{
var logger = host.Services.GetService<ILogger<IWebHost>>();

logger.LogInformation("Registering bot service");
var appConfiguration = host.Services.GetService<IAppConfiguration>();
var bot = host.Services.GetService<IBot>();

Expand All @@ -37,7 +44,6 @@ public static void UnregisterBotService(this IWebHost host)
var logger = host.Services.GetService<ILogger<IWebHost>>();

logger.LogInformation("Unregistering bot service");

var appConfiguration = host.Services.GetService<IAppConfiguration>();
var bot = host.Services.GetService<IBot>();

Expand Down
91 changes: 91 additions & 0 deletions src/BotService/Infrastructure/WindowsService/HostService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
using BotService.Infrastructure.Extensions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace BotService.Infrastructure.WindowsService
{
public class HostService : WebHostService
{
private readonly IWebHost _webHost;
private readonly ILogger _logger;

public HostService(IWebHost host)
: base(host)
{
_webHost = host;
_logger = host.Services.GetRequiredService<ILogger<HostService>>();
}

protected override void OnStarting(string[] args)
{
_logger.LogInformation("Starting the bot service");

// At this point, all dependencies have been registered and the configuration was retrieved
SetServiceAsStartPending(2);

_webHost.SetupDatabase();
SetServiceAsStartPending(3);

base.OnStarting(args);
}

protected override void OnStarted()
{
// At this point, the ASP.NET host should be running and receiving requests
SetServiceAsStartPending(4);

_webHost.RegisterBotService();
SetServiceAsRunning();

_logger.LogInformation("The bot service completed the start up process.");

base.OnStarted();
}

protected override void OnStopping()
{
_logger.LogInformation("Stopping the bot service.");
_webHost.UnregisterBotService();

base.OnStopping();
}

protected override void OnStopped()
{
_logger.LogInformation("The bot service was stopped.");

base.OnStopped();
}

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool SetServiceStatus(System.IntPtr handle, ref WindowsServiceStatus serviceStatus);

private void SetServiceAsStartPending(int? progress = null)
{
var serviceStatus = default(WindowsServiceStatus);
serviceStatus.dwCurrentState = WindowsServiceState.SERVICE_START_PENDING;
serviceStatus.dwWaitHint = 100000; // 100 seconds

if (progress.HasValue)
{
// The number itself doesn't have any meaning, but it needs to be increased in each call to this method
// to inform Windows that the service is making progress and didn't hang-up.
serviceStatus.dwCheckPoint = progress.Value;
}

SetServiceStatus(ServiceHandle, ref serviceStatus);
}

private void SetServiceAsRunning()
{
var serviceStatus = default(WindowsServiceStatus);
serviceStatus.dwCurrentState = WindowsServiceState.SERVICE_RUNNING;
SetServiceStatus(ServiceHandle, ref serviceStatus);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace BotService.Infrastructure.Extensions
{
public enum WindowsServiceState
{
SERVICE_STOPPED = 0x00000001,
SERVICE_START_PENDING = 0x00000002,
SERVICE_STOP_PENDING = 0x00000003,
SERVICE_RUNNING = 0x00000004,
SERVICE_CONTINUE_PENDING = 0x00000005,
SERVICE_PAUSE_PENDING = 0x00000006,
SERVICE_PAUSED = 0x00000007,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System.Runtime.InteropServices;

#pragma warning disable SA1307 // We cannot change the name of the struct fields to match our naming conventions
namespace BotService.Infrastructure.Extensions
{
[StructLayout(LayoutKind.Sequential)]
public struct WindowsServiceStatus
{
public int dwServiceType;
public WindowsServiceState dwCurrentState;
public int dwControlsAccepted;
public int dwWin32ExitCode;
public int dwServiceSpecificExitCode;
public int dwCheckPoint;
public int dwWaitHint;
}
}
#pragma warning restore SA1307
4 changes: 2 additions & 2 deletions src/BotService/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Infrastructure.Core.Services;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Serilog;
Expand Down Expand Up @@ -64,7 +63,8 @@ public static async Task Main(string[] args)
else
{
// When running as a console app we have time to run this part of the setup before starting the web host.
host.SetupAndRegisterBotService();
host.SetupDatabase();
host.RegisterBotService();
host.Run();
}
}
Expand Down

0 comments on commit f698795

Please sign in to comment.