0

I recently deployed a site that runs in a shared hosting environment. The problem is that the site receives sporadic traffic: after 20 minutes I suppose the server shuts down the instance and so when the site loads the first request, it's often slow. So I decided to add a functionality that loads the webpage every few minutes. I call the code in the Global.asax with new CodeKeepAlive.KeepAliveManager().SetKeepAlive(); and this is the complete code:

public class KeepAliveManager
{
    Timer KeepAliveTimer;

    public void SetKeepAlive()
    {
        KeepAliveTimer = new Timer(DoKeepAliveRequest, null, new Random().Next(200000, 900000), Timeout.Infinite);
    }

    public void DoKeepAliveRequest(object state)
    {
        string TheUrl = "https://www.the_website_url.com";

        HttpWebRequest TheRequest = (HttpWebRequest)WebRequest.Create(TheUrl);
        HttpWebResponse TheResponse = (HttpWebResponse)TheRequest.GetResponse();

        KeepAliveTimer.Change(new Random().Next(200000, 900000), Timeout.Infinite);
    }
}

For some reason, since I added this functionality, the site locks once in a while; after 30 seconds of load time, the server says the page can't be loaded. I also have an error logging functionality that triggers on Application_Error but there are no logs.

Is there anything wrong with my code?

frenchie
  • 51,731
  • 109
  • 304
  • 510
  • Checkout [Do C# Timers elapse on a separate thread?](https://stackoverflow.com/questions/1435876/do-c-sharp-timers-elapse-on-a-separate-thread) and see what kind of timer are you using. The site may be locking since you're doing an HTTP call every n time. Make sure the timer callback is running on a separate thread. Anyway, you are doing an ugly hack and not addressing the right issue which is the server shutting down the instance as you claim. – jegtugado Mar 15 '21 at 12:31
  • Is it possible for you to use separate application pool in IIS for the site and configure it to not shutdown? – Alexander Mar 15 '21 at 16:12
  • What are you AppPool settngs? Is the StartMode AlwaysRunning? – George Vovos Mar 16 '21 at 16:01
  • 1
    Why not simply disable or extend the apppool 20 minute shutdown? Or make a windows service that acts as a keep-alive tool. – VDWWD Mar 16 '21 at 21:02
  • Check if the shared hosting environment has "Always On" settings. It doesn't make sense to use code inside the app being terminated to keep itself alive. When the site is brought down, usually the app pool process is terminated, and your keep alive code will have nowhere to execute, unless you are running the code like others say in a separate process. – weichch Mar 16 '21 at 23:45
  • 1
    And, if there is one issue your keep alive code has, it might be the response is not closed (`TheResponse.Close()`), which can lead to connection starvation. And no new connections can be established to the server after all. – weichch Mar 17 '21 at 00:24
  • 1
    Look at Idle timeout in IIS – LukaszBalazy Mar 18 '21 at 08:03
  • I don't control the server, it's shared hosting – frenchie Mar 18 '21 at 08:06
  • @frenchie the issue is that on IIS you don't have a reliable additional thread, There are some things that mitigate the issue such as Hangfire, but even that has it's limitations. – johnny 5 Mar 19 '21 at 13:25

2 Answers2

1

The issue is in asp.net there is no reliable way to run background tasks with out the risk off IIS killing your thread. Once a request is complete IIS does not have reason to keep any of the threads you spawn up alive.

There are a few ways to do this, such as using some simple service on the free tier of amazon or google cloud to act as your heart beat.

But assuming you just want to use your shared hosting

You can use something like HangFire which specializes in this but they have their limitations see the docs for getting started:

GlobalConfiguration.Configuration
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage("Database=Hangfire.Sample; Integrated Security=True;", new SqlServerStorageOptions
    {
        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
        QueuePollInterval = TimeSpan.Zero,
        UseRecommendedIsolationLevel = true,
        UsePageLocksOnDequeue = true,
        DisableGlobalLocks = true
    })
    .UseBatches()
    .UsePerformanceCounters();

//Queue the job
BackgroundJob.Enqueue(() => Console.WriteLine("Hello, world!"));

//run it
using (var server = new BackgroundJobServer())
{
    Console.ReadLine();
}

NOTE: Hang Fire it self has its limitations IIS will shut down any long running threads e.g things that take longer that 90 - or 180 seconds (I forget the limit) So make sure you queue your heart beat task on each request that comes in. If you want to make sure you don't fire too many you can add a header to the request and verify it's on the request.

See this answer on the new .Net-Core Background Tasks which is applicable because it relates to IIS

johnny 5
  • 19,893
  • 50
  • 121
  • 195
  • There are a few other dirty hacks you can pull. Such as making a controller method forcefully long pull, by issuing a request to yourself and using a [semiphore](https://stackoverflow.com/a/4462174/1938988) to wait until another thread request that resource and the return. This way you're sure another heartbeat has hit before letting iis know the request is complete – johnny 5 Mar 21 '21 at 13:43
-1

Add a Test program in PhantomJs, to randomly or periodically take screenshots of the url and deploy it on the same server . IT will do that work for u