I am able to cause a reproducible out of memory exception to occur when enqueuing and processing background jobs with Hangfire.
The jobs are simple Console.WriteLine
invocations so I wouldn't expect heap memory to increase the way that it does.
Have I configured incorrectly or should I think about filing an issue?
Results (VMMap)
Using Redis as backing storage for Jobs:
- At start, total heap = 29,088K;
- after 5,000 jobs, 938,672K;
- 6,000 jobs, 1,056,004K;
- 7,000 jobs, 1,219,296K;
- 8,000 jobs, heap value is not present;
- within 100 more jobs, the
iisexpress.exe
instance crashes.
With SQL storage the limit is much higher ~= 15,000 jobs.
Setup
- empty ASP.NET project;
- install Owin packages for IIS hosting and Hangfire;
- startup class and controller.
Packages
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Hangfire.Core" version="1.6.6" targetFramework="net452" />
<package id="Hangfire.Pro" version="1.4.7" targetFramework="net452" />
<package id="Hangfire.Pro.PerformanceCounters" version="1.4.7" targetFramework="net452" />
<package id="Hangfire.Pro.Redis" version="2.0.2" targetFramework="net452" />
<package id="Hangfire.SqlServer" version="1.6.6" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.0" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="1.0.0" targetFramework="net452" developmentDependency="true" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
<package id="StackExchange.Redis" version="1.1.606" targetFramework="net452" />
</packages>
Controller
public class DefaultController : ApiController
{
static int _;
[HttpPost]
public void Post(int count = 1000)
{
for (var i = 0; i < count; ++i)
{
BackgroundJob.Enqueue(() => Console.WriteLine(_));
++_;
}
}
}
Startup
static class AppSettings
{
internal static bool HangfireUseRedis => true;
internal static int RedisDatabase => 0;
internal static string RedisConnection => "localhost:6379";
internal static string SqlConnection => "Data Source=(localdb)\\v11.0;Initial Catalog=Hangfire";
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
if (AppSettings.HangfireUseRedis)
{
var redisOptions = new RedisStorageOptions
{
Database = AppSettings.RedisDatabase,
Prefix = "Foobar:"
};
GlobalConfiguration.Configuration.UseRedisStorage(AppSettings.RedisConnection, redisOptions);
}
else
{
GlobalConfiguration.Configuration.UseSqlServerStorage(AppSettings.SqlConnection);
}
JobHelper.SetSerializerSettings(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
app.UseHangfireServer();
app.UseHangfireDashboard();
app.UseWebApi(config);
}
}