Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a safe overhead for RequestAdditionalTime()?

I have a Windows service that spawns a set of child activities on separate threads and that should only terminate when all those activities have successfully completed. I do not know in advance how long it might take to terminate an activity after a stop signal is received. During OnStop(), I wait in intervals for that stop signal and keep requesting additional time for as long as the system is willing to grant it.

Here is the basic structure:

class MyService : ServiceBase
{
    private CancellationTokenSource stopAllActivities;
    private CountdownEvent runningActivities;

    protected override void OnStart(string[] args)
    {
        // ... start a set of activities that signal runningActivities
        //       when they stop
        // ... initialize runningActivities to the number of activities
    }

    protected override void OnStop()
    {
        stopAllActivities.Cancel();

        while (!runningActivities.Wait(10000))
        {
            RequestAdditionalTime(15000); // NOTE: 5000 added for overhead
        }
    }
}

Just how much "overhead" should I be adding in the RequestAdditionalTime call? I'm concerned that the requests are cumulative, instead of based on the point in time when each RequestAdditionalTime call is made. If that's the case, adding overhead could result in the system eventually denying the request because it's too far out in the future. But if I don't add any overhead then my service could be terminated before it has a chance to request the next block of additional time.

like image 565
Lars Kemmann Avatar asked May 02 '15 15:05

Lars Kemmann


1 Answers

This post wasn't exactly encouraging:

The MSDN documentation doesn’t mention this but it appears that the value specified in RequestAdditionalTime is not actually ‘additional’ time. Instead, it replaces the value in ServicesPipeTimeout. Worse still, any value greater than two minutes (120000 milliseconds) is ignored, i.e. capped at two minutes.

I hope that's not the case, but I'm posting this as a worst-case answer.

UPDATE: The author of that post was kind enough to post a very detailed reply to my comment, which I've copied below.

Lars, the short answer is no.

What I would say is that I now realise that Windows Services ought to be designed to start and terminate processing quickly when requested to do so.

As developers, we tend to focus on the implementation of the processing and then package it up and deliver it as a Windows Service.

However, this really isn’t the correct approach to designing Windows Services. Services must be able to respond quickly to requests to start and stop not only when an administrator making the request from the services console but also when the operating system is requesting a start as part of its start up processing or a stop because it is shutting down,

Consider what happens when Windows is configured to shut down when a UPS signals that the power has failed. It’s not appropriate for the service to respond with “I need a few more minutes…”.

It’s possible to write services that react quickly to stop requests even when they implement long running processing tasks. Usually a long running process will consist of batch processing of data and the processing should check if a stop has been requested at the level of the smallest unit of work that ensures data consistency.

As an example, the first service where I found the stop timeout was a problem involved the processing of a notifications queue on a remote server. The processing retrieved a notification from the queue, calling a web service to retrieve data related to the subject of the notification, and then writing a data file for processing by another application.

I implemented the processing as a timer driven call to a single method. Once the method is called it doesn’t return until all the notifications in the queue have been processed. I realised this was a mistake for a Windows Service because occasionally there might be tens of thousands of notifications in the queue and processing might take several minutes.

The method is capable of processing 50 notifications per second. So, what I should have done was implement a check to see if a stop had been requested before processing each notification. This would have allowed the method to return when it has completed the processing of a notification but before it has started to process the next notification. This would have ensured that the service responds quickly to a stop request and any pending notifications remained queued for processing when the service is restarted.

like image 77
Lars Kemmann Avatar answered Nov 16 '22 00:11

Lars Kemmann