<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>odinserj</title>
 <link href="http://odinserj.net/atom.xml" rel="self"/>
 <link href="http://odinserj.net/"/>
 <updated>2018-10-26T07:41:40+00:00</updated>
 <id>http://odinserj.net</id>
 <author>
   <name>odinserj</name>
   <email></email>
 </author>

 
 <entry>
   <title>Hangfire Blog Moved</title>
   <link href="http://odinserj.net/2015/04/14/hangfire-blog-moved/"/>
   <updated>2015-04-14T00:00:00+00:00</updated>
   <id>http://odinserj.net/2015/04/14/hangfire-blog-moved</id>
   <content type="html">&lt;p&gt;Hi there! I’m writing just to inform you that Hangfire Blog moved to the official site (long time ago, sorry for the late notification):&lt;/p&gt;

&lt;h3&gt;&lt;a href=&quot;https://www.hangfire.io/blog&quot;&gt;https://www.hangfire.io/blog&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;I’m not’ ready to blog about anything else yet, and it would be very strange to write only about Hangfire in a personal blog. New product blog contains all the required links, has the same header as other parts of official site and thus is more useful.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Hangfire 1.3.0 Released</title>
   <link href="http://odinserj.net/2014/12/10/hangfire-1.3.0-released/"/>
   <updated>2014-12-10T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/12/10/hangfire-1.3.0-released</id>
   <content type="html">&lt;h2 id=&quot;logging-that-just-works&quot;&gt;Logging that just works&lt;/h2&gt;

&lt;p&gt;Logging was a very difficult thing to set-up in previous versions of Hangfire. Even if we omit &lt;a href=&quot;http://discuss.hangfire.io/t/move-away-from-common-logging-library/378/7&quot;&gt;tricky versioning&lt;/a&gt; issues, you had to install a separate logging adapter for your current logging library, with guessing the right one first (i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;Common.Logging.NLog&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Common.Logging.NLog20&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now, the logging just works. If your project already uses &lt;a href=&quot;https://code.google.com/p/elmah/&quot;&gt;Elmah&lt;/a&gt;, &lt;a href=&quot;http://nlog-project.org/&quot;&gt;NLog&lt;/a&gt;, &lt;a href=&quot;https://logging.apache.org/log4net/&quot;&gt;Log4Net&lt;/a&gt;, &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ff647183.aspx&quot;&gt;EntLib Logging&lt;/a&gt;, &lt;a href=&quot;http://serilog.net/&quot;&gt;Serilog&lt;/a&gt; or &lt;a href=&quot;http://www.gibraltarsoftware.com/Loupe&quot;&gt;Loupe&lt;/a&gt; you are &lt;em&gt;not required to do anything&lt;/em&gt;. Thanks to awesome &lt;a href=&quot;https://github.com/damianh/LibLog&quot;&gt;LibLog&lt;/a&gt; library by Damian Hickey (&lt;a href=&quot;https://twitter.com/randompunter&quot;&gt;@randompunter&lt;/a&gt; on Twitter), installed logging library will be used automatically through the reflection.&lt;/p&gt;

&lt;p&gt;If your application uses other logging library or a custom one, just implement two simple interfaces:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ILog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LogLevel&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;messageFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ILogProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ILog&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And yes, Hangfire is no more have &lt;code class=&quot;highlighter-rouge&quot;&gt;Common.Logging&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Common.Logging.Core&lt;/code&gt; dependencies.&lt;/p&gt;

&lt;h2 id=&quot;debugging-with-symbolsource&quot;&gt;Debugging with Symbolsource&lt;/h2&gt;

&lt;p&gt;SymbolSource is a service which exposes PDB symbols so you can step through Hangfire code while debugging your application. There are a few things to configure before we can begin, see &lt;a href=&quot;http://www.symbolsource.org/&quot;&gt;symbolsource.org&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Within Visual Studio:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Go to &lt;strong&gt;Tools → Options → Debugger → General&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Uncheck “Enable Just My Code (Managed only)”&lt;/li&gt;
  &lt;li&gt;Uncheck “Enable .NET Framework source stepping”&lt;/li&gt;
  &lt;li&gt;Check “Enable source server support”&lt;/li&gt;
  &lt;li&gt;Uncheck “Require source files to exactly match the original version”&lt;/li&gt;
  &lt;li&gt;Go to &lt;strong&gt;Tools → Options → Debugger → Symbols&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select a folder for the local symbol/source cache&lt;/li&gt;
  &lt;li&gt;Add symbol servers under “Symbol file (.pdb) locations”: &lt;a href=&quot;http://srv.symbolsource.org/pdb/Public&quot;&gt;http://srv.symbolsource.org/pdb/Public&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To speed up startup of the debug session, you can specify only the Hangfire dlls to be loaded. Go to &lt;strong&gt;Tools →  Options → Debugger → Symbols&lt;/strong&gt;, select “Only specified modules” and enter Hangfire.*.dll&lt;/p&gt;

&lt;p&gt;Now you can step into Hangfire code while debugging.&lt;/p&gt;

&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Generation of NuGet symbol packages with &lt;code class=&quot;highlighter-rouge&quot;&gt;*.pdb&lt;/code&gt; and source files.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Allow to customize serialization settings of Json.NET (by &lt;a href=&quot;https://github.com/fpellet&quot;&gt;@fpellet&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Ability to configure &lt;code class=&quot;highlighter-rouge&quot;&gt;ServerTimeout&lt;/code&gt; option.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Breaking&lt;/strong&gt; – Use &lt;code class=&quot;highlighter-rouge&quot;&gt;LibLog&lt;/code&gt; package instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;Common.Logging&lt;/code&gt; for logging.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire.SqlServer&lt;/code&gt; is now merged with &lt;code class=&quot;highlighter-rouge&quot;&gt;Dapper&lt;/code&gt; package.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;Dapper&lt;/code&gt; package updated to 1.38.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Use ILMerge instead of ILRepack.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Update &lt;code class=&quot;highlighter-rouge&quot;&gt;Microsoft.Owin.Host.SystemWeb&lt;/code&gt; to the latest version.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – Dashboard not crash if a scheduling task is cancelled (by &lt;a href=&quot;https://github.com/fpellet&quot;&gt;@fpellet&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Other&lt;/strong&gt; – Use psake instead of MSBuild for project build automation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://hangfire.io&quot;&gt;Hangfire Official Site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/HangfireIO/Hangfire&quot;&gt;Hangfire GitHub Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages?q=hangfire&quot;&gt;Hangfire Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing Hangfire Pro</title>
   <link href="http://odinserj.net/2014/11/15/hangfire-pro/"/>
   <updated>2014-11-15T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/11/15/hangfire-pro</id>
   <content type="html">&lt;p&gt;As an ASP.NET developer, I always wanted to have something simple to handle scenarios where you need to build a long-running async task and show a loader to a user:&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;/img/ajax-loader.gif&quot; alt=&quot;Loader&quot; style=&quot;width:54px;display:inline;margin-bottom:0;&quot; /&gt;
    &lt;br /&gt;
    Loading…  
    &lt;br /&gt;
    &lt;em&gt;Nothing is being loaded here, don't let hypnotize yourself!&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;But every time I faced Windows Services, message queues and other difficult-to-understand problems and hard-to-maintain solutions, my head exploded into a billion pieces. At the same time I looked at Rails’ &lt;a href=&quot;http://sidekiq.org&quot;&gt;Sidekiq&lt;/a&gt; and wished to have a simple .NET alternative for background job processing. That is why I began to develop &lt;a href=&quot;http://hangfire.io&quot;&gt;Hangfire&lt;/a&gt; (yep, just for the loader).&lt;/p&gt;

&lt;p&gt;Slightly more than a year passed from the &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/tree/d58a619ebc487ef28bef8c6e7f4df8e1d51ee8c5&quot;&gt;first commit&lt;/a&gt; and &lt;a href=&quot;https://www.nuget.org/packages/HangFire/0.1.0&quot;&gt;first version&lt;/a&gt; of Hangfire. This was my first open-source project that is being used more than in one organization, and it was an amazing experience for me to grow a project from scratch. I’m glad to see more and more people coming to the project and giving positive ratings and unvaluable feedback.&lt;/p&gt;

&lt;p&gt;I want to keep Hangfire project as free as possible, but eliminate the most dangerous risk – the absence of time. There are many things to be done, including problems, new features, documentation, and I want to do them in a reasonable time.&lt;/p&gt;

&lt;p&gt;Today, I’m introducing a new stage of Hangfire development – &lt;a href=&quot;http://hangfire.io/pro/&quot;&gt;Hangfire Pro&lt;/a&gt;. It is a set of paid libraries that extend Hangfire functionality by providing features to improve performance and simplifying maintenance of background job processing in larger applications.&lt;/p&gt;

&lt;h2 id=&quot;the-destiny-of-hangfireredis&quot;&gt;The Destiny of Hangfire.Redis&lt;/h2&gt;

&lt;h3 id=&quot;bad-news&quot;&gt;Bad News&lt;/h3&gt;

&lt;p&gt;First, I’ll start with bad news – Hangfire.Redis package is moved to Hangfire Pro. This means that it became available only for Pro users and will be removed from public NuGet Gallery soon.&lt;/p&gt;

&lt;p&gt;I understand that this step breaks your expectations, and I’m sorry for this. The source code will be available at &lt;a href=&quot;https://github.com/HangfireIO/Hangfire.Redis&quot;&gt;Hangfire.Redis&lt;/a&gt; repository, but there will be no updates and official support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other existing parts of Hangfire will not be moved to the Pro version ever&lt;/strong&gt;. Moreover, I’ll try to do the vice versa, by open-sourcing paid extensions in the future, when the project will be more stable.&lt;/p&gt;

&lt;h3 id=&quot;good-news&quot;&gt;Good News&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/HangfireIO/Hangfire/issues/122&quot;&gt;Most&lt;/a&gt; &lt;a href=&quot;https://github.com/HangfireIO/Hangfire/issues/195&quot;&gt;wanted&lt;/a&gt; features were implemented in the next version of Hangfire.Redis package. It now includes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Compatibility with ServiceStack 4.0&lt;/strong&gt; libraries. It still uses ServiceStack v3 libraries, but &lt;code class=&quot;highlighter-rouge&quot;&gt;ILMerge /internalize&lt;/code&gt; utility is being used to merge them into Hangfire.Redis assembly. So, you can use either Service v3 or v4 in your projects.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Configurable prefix for keys&lt;/strong&gt;. You can use now the same database for different environments by setting corresponding prefix for keys in the &lt;code class=&quot;highlighter-rouge&quot;&gt;RedisStorageOptions.Prefix&lt;/code&gt; property.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hangfire-pro-today&quot;&gt;Hangfire Pro Today&lt;/h2&gt;

&lt;p&gt;Hangfire Pro already includes previously mentioned new version of &lt;strong&gt;Hangfire Redis&lt;/strong&gt; package and a completely new &lt;strong&gt;Hangfire.PerformanceCounters&lt;/strong&gt; package. The latter pushes internal metrics to Windows Performance Counters to proactively monitor issues with background job processing:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://hangfire.io/img/perfmon.png&quot; alt=&quot;Performance Monitor with Hangfire counters&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hangfire Pro is available through the subscriptions you can buy at the &lt;a href=&quot;http://hangfire.io/subscriptions/&quot;&gt;official site&lt;/a&gt;. After purchasing a subscription, you will be able to do the following things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Access to Hangfire Pro package &lt;strong&gt;binaries&lt;/strong&gt; and &lt;strong&gt;sources&lt;/strong&gt; on the private GitHub repository.&lt;/li&gt;
  &lt;li&gt;Access to &lt;strong&gt;priority and private support&lt;/strong&gt; by dedicated e-mail (public forum discussions will be always free).&lt;/li&gt;
  &lt;li&gt;Use Hangfire under &lt;strong&gt;EULA&lt;/strong&gt; for organizations that don’t want to use open-source licenses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hangfire-pro-roadmap&quot;&gt;Hangfire Pro Roadmap&lt;/h2&gt;

&lt;p&gt;As I already said, Hangfire Pro will solve performance/usability/monitoring issues for larger web applications. Further work will be made in the following directions.&lt;/p&gt;

&lt;h3 id=&quot;batch-jobs&quot;&gt;Batch Jobs&lt;/h3&gt;

&lt;p&gt;Batch jobs will help to build more complex workflows with Hangfire. They will give you the power of &lt;strong&gt;parallel processing&lt;/strong&gt; and &lt;strong&gt;continuations&lt;/strong&gt;, you can look at this code snippet:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BatchJob&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Messy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Output&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;With&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContinueWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Predictable continuation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Three first tasks will be executed in parallel, end the continuation will be invoked only after successful run of the first tasks.&lt;/p&gt;

&lt;h3 id=&quot;async-methods-support&quot;&gt;Async Methods Support&lt;/h3&gt;

&lt;p&gt;As with &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ee728598(v=vs.100).aspx&quot;&gt;async controllers&lt;/a&gt; in ASP.NET MVC, this feature enables you to improve overall throughput keeping the lowest number of workers in the pool. Instead of waiting for an outstanding I/O operation, they will be able to process other background jobs as well.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HighlightAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snippetId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snippet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Snippets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SingleOrDefaultAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snippetId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RemoteService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HighlightAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snippet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SaveChangesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-are-you-waiting-for&quot;&gt;What are you waiting for?&lt;/h2&gt;

&lt;p&gt;Smart people say that content like this should contain a call to action. I have no enough experience to break the rules, so here it is:&lt;/p&gt;

&lt;div style=&quot;text-align: center;&quot;&gt;
    &lt;a class=&quot;btn btn-lg btn-success&quot; href=&quot;http://hangfire.io/subscriptions/&quot;&gt;Get a Subscription&lt;/a&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Hangfire 1.0 Released</title>
   <link href="http://odinserj.net/2014/06/30/hangfire-1.0-released/"/>
   <updated>2014-06-30T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/06/30/hangfire-1.0-released</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://hangfire.io&quot;&gt;Hangfire&lt;/a&gt; has finally reached the &lt;code class=&quot;highlighter-rouge&quot;&gt;1.0&lt;/code&gt; milestone! This means that public API is frozen and considered to be stable. Starting from now, the &lt;a href=&quot;http://semver.org&quot;&gt;SemVer 2.0&lt;/a&gt; specification will be used for versioning every package (but there may be exceptions, follow the README for each package).&lt;/p&gt;

&lt;p&gt;I want to thank everyone who helped me to reach this milestone, without your participation I would not be able to finish the work. Special thanks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/devmondo&quot;&gt;@devmondo&lt;/a&gt; – for endless optimism that helped me a lot in difficult times.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dennyferra&quot;&gt;@dennyferra&lt;/a&gt; – for awesome &lt;a href=&quot;https://www.nuget.org/packages/Hangfire.SqlServer.RabbitMQ/&quot;&gt;Hangfire.RabbitMQ&lt;/a&gt; extension.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;release-notes&quot;&gt;Release Notes&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – OWIN implementation for Hangfire Dashboard.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Custom authorization filters support for dashboard.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Unified bootstrapper to start Hangfire in web applications.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Descriptive names for background jobs in Dashboard.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – RabbitMQ support for SQL Server storage.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Hangfire now requires .NET Framework 4.5.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Namespaces, assemblies and packages now start with &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire&lt;/code&gt; (not &lt;code class=&quot;highlighter-rouge&quot;&gt;HangFire&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire.Core&lt;/code&gt; now contains the dashboard. There is no more &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire.Web&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Common.Logging dependency package updated to 2.2.0.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Default dashboard url is now &lt;code class=&quot;highlighter-rouge&quot;&gt;/hangfire&lt;/code&gt; (without &lt;code class=&quot;highlighter-rouge&quot;&gt;.axd&lt;/code&gt; suffix).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Removed&lt;/strong&gt; – Removed &lt;code class=&quot;highlighter-rouge&quot;&gt;Enqueue&lt;/code&gt; method overloads with queue parameter.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Removed&lt;/strong&gt; – Removed &lt;code class=&quot;highlighter-rouge&quot;&gt;AspNetBackgroundJobServer&lt;/code&gt; class.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – Russian language in Dashboard replaced with English.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – Recurring jobs reported to be executed 44 years ago.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;upgrading-from-0x&quot;&gt;Upgrading from 0.x&lt;/h3&gt;

&lt;p&gt;If you have only &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire&lt;/code&gt; package installed, package update is very simple. Just type in your Package Manager Console window:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PM&amp;gt; Update-Package Hangfire
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For custom installations, first update each package, and then remove the &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire.Web&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PM&amp;gt; Update-Package Hangfire.Core
PM&amp;gt; Uninstall-Package Hangfire.Web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;breaking-changes&quot;&gt;Breaking Changes&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb398202.aspx&quot;&gt;Target your project&lt;/a&gt; to .NET Framework 4.5 or later.&lt;/li&gt;
  &lt;li&gt;Replace all occurrences of &lt;code class=&quot;highlighter-rouge&quot;&gt;HangFire&lt;/code&gt; (case-sensitive) to &lt;code class=&quot;highlighter-rouge&quot;&gt;Hangfire&lt;/code&gt; (lowercased &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; letter) in your projects.&lt;/li&gt;
  &lt;li&gt;Use the &lt;a href=&quot;http://docs.hangfire.io/en/latest/users-guide/getting-started/owin-bootstrapper.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UseHangfire&lt;/code&gt;&lt;/a&gt; extension method for OWIN app builder instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;AspNetBackgroundJobServer&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Replace all links to Dashboard from &lt;code class=&quot;highlighter-rouge&quot;&gt;/hangfire.axd&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;/hangfire&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Change your authorization rules as &lt;a href=&quot;http://docs.hangfire.io/en/latest/users-guide/deployment-to-production/configuring-authorization.html&quot;&gt;described here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/HangfireIO/Hangfire/releases/tag/v1.0&quot;&gt;Release notes on GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://hangfire.io&quot;&gt;Hangfire Official Site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/HangfireIO/Hangfire&quot;&gt;Hangfire GitHub Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages?q=hangfire&quot;&gt;Hangfire Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>HangFire 0.9 Released</title>
   <link href="http://odinserj.net/2014/06/07/hangfire-0.9-released/"/>
   <updated>2014-06-07T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/06/07/hangfire-0.9-released</id>
   <content type="html">&lt;h2 id=&quot;recurring-jobs&quot;&gt;Recurring jobs&lt;/h2&gt;

&lt;p&gt;This version brings incredibly easy method of scheduling and running recurring jobs inside ASP.NET applications. You need to call only a single line of code to perform this task:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Easy!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cron&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Daily&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;Cron&lt;/code&gt; class provides different easy to use methods and overloads to set up recurring jobs on a minutely, hourly, weekly, monthly and yearly basis. It is also possible to use more complex &lt;a href=&quot;http://en.wikipedia.org/wiki/Cron#CRON_expression&quot;&gt;CRON expressions&lt;/a&gt;, since the &lt;a href=&quot;https://code.google.com/p/ncrontab/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;NCrontab&lt;/code&gt;&lt;/a&gt; library is being used to perform scheduling logic.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;RecurringJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddOrUpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Powerful!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;&quot;0 12 * */2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;HangFire will check the schedule each minute and enqueue recurring jobs as regular background jobs, so you receive all power of HangFire, including the full transparency, free of charge.&lt;/p&gt;

&lt;p&gt;HangFire Monitor was also updated and allows you to see and manage your recurring jobs:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/recurring.png&quot; alt=&quot;Recurring jobs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, HangFire now supports all kinds of background jobs: fire-and-forget, delayed and recurring, and let you process them with ease!&lt;/p&gt;

&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Out-of-the-box support for running recurring jobs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;SqlServerStorage&lt;/code&gt; now also accepts connection string names.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;IBackgroundJobClient&lt;/code&gt; does not implement the &lt;code class=&quot;highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt; interface anymore.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;IMonitoringApi&lt;/code&gt; does not implement the &lt;code class=&quot;highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt; interface anymore.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Improve &lt;code class=&quot;highlighter-rouge&quot;&gt;DateTime&lt;/code&gt; resolution for job arguments by including milliseconds.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; – Removed deprecated &lt;code class=&quot;highlighter-rouge&quot;&gt;RetryAttribute&lt;/code&gt; class. Please, use &lt;code class=&quot;highlighter-rouge&quot;&gt;AutomaticRetryAttribute&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyNotFoundException&lt;/code&gt; when accessing job cancellation token with Redis.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – Theoretical race condition that makes HangFire Server invisible from Monitor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;/h2&gt;

&lt;p&gt;Version 0.9 brings some breaking changes. Please, do the following steps to perform the upgrade.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Replace the &lt;code class=&quot;highlighter-rouge&quot;&gt;RetryAttribute&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;AutomaticRetryAttribute&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Remove the calls to &lt;code class=&quot;highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; method of &lt;code class=&quot;highlighter-rouge&quot;&gt;IBackgroundJobClient&lt;/code&gt; interface (or &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJobClient&lt;/code&gt; class).&lt;/li&gt;
  &lt;li&gt;Remove the invocations of &lt;code class=&quot;highlighter-rouge&quot;&gt;IMonitoringApi.Dispose&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://hangfire.io&quot;&gt;HangFire Official Site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/odinserj/HangFire&quot;&gt;HangFire GitHub Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages?q=hangfire&quot;&gt;HangFire Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>HangFire 0.8.3 Released</title>
   <link href="http://odinserj.net/2014/05/23/hangfire-0.8.3-released/"/>
   <updated>2014-05-23T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/05/23/hangfire-0.8.3-released</id>
   <content type="html">&lt;h2 id=&quot;cancellation-tokens&quot;&gt;Cancellation tokens&lt;/h2&gt;

&lt;p&gt;This release comes with great feature for long-running jobs: cancellation tokens. They are like &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt; class, but for background jobs.&lt;/p&gt;

&lt;p&gt;Previously, you was not able to stop a running job by using &lt;code class=&quot;highlighter-rouge&quot;&gt;Requeue&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Delete&lt;/code&gt; methods of the &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJob&lt;/code&gt; class, or by clicking a button in HangFire Monitor. Its state was changed, but the job is still running. And running… And running… Nobody waits for it, why it is not stopped yet?&lt;/p&gt;

&lt;p&gt;Job cancellation tokens provide &lt;code class=&quot;highlighter-rouge&quot;&gt;ThrowIfCancellationRequested&lt;/code&gt; method that throws &lt;code class=&quot;highlighter-rouge&quot;&gt;OperationCanceledException&lt;/code&gt; if a job was canceled due to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Shutdown request&lt;/strong&gt; – executed on &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJobServer.Stop&lt;/code&gt; method invocation. Performed automatically when ASP.NET application is shutting down.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;State transition&lt;/strong&gt; – the state of the job has been changed, and it is not in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Processing&lt;/code&gt; state now. &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJob.Delete&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJob.Requeue&lt;/code&gt; as well as all Monitor interface buttons lead to the job cancellation (in case it is running).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use cancellation tokens, you need to add just one parameter of type &lt;code class=&quot;highlighter-rouge&quot;&gt;IJobCancellationToken&lt;/code&gt; to a target method:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Cancelable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;IJobCancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Loop breaker&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThrowIfCancellationRequested&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Performing step {0} of {1}...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;iterationCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;            
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OperationCanceledException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Cancellation requested, exiting...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, create a background job:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Cancelable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JobCancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To be able to test target methods, or to add the support of cancellation token to your old jobs, you can use the &lt;code class=&quot;highlighter-rouge&quot;&gt;JobCancellationToken&lt;/code&gt; class:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;JobCancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Cancelable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;hanging-jobs&quot;&gt;Hanging jobs&lt;/h2&gt;

&lt;p&gt;This release solved another problem. On some job queues (Redis, SQL Server, but not MSMQ) it is not possible to apply transaction just to fetch a job from a queue, hide it from another workers, and delete it on successful processing or place it back to a queue on failure or process termination.&lt;/p&gt;

&lt;p&gt;To mimic this behavior, HangFire uses atomic get/set commands: &lt;code class=&quot;highlighter-rouge&quot;&gt;update&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;output&lt;/code&gt; clause in SQL Server and &lt;code class=&quot;highlighter-rouge&quot;&gt;BRPOPLPUSH&lt;/code&gt; in Redis. That is why other workers don’t see just fetched job. But in case of process termination, all jobs will remain in invisible state. To fight with this, there is a component on a server, who seeks invisible jobs and decides what to do with them.&lt;/p&gt;

&lt;p&gt;Unfortunately, there is no way to determine whether a job was aborted by a process termination, or it is still working. To separate these cases, the component checks the fetching time of a job and compares it with current time. If the result is greater than InvisibilityTimeout, then we should trait the job as hanged, and return it to its queue. InvisibilityTimeout defaults to 30 minutes to be sure that the job processing was aborted.&lt;/p&gt;

&lt;p&gt;When ASP.NET issues shutdown request and gives background jobs 30 seconds to die, some of them may be aborted by &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAbortException&lt;/code&gt;. In previous releases this lead to the fact that background job may stay in Invisible state on regular shutdown, and this sometimes increase the latency of job processing.&lt;/p&gt;

&lt;p&gt;In this release HangFire make an attempt to place aborted job back to the start of its queue on this exception, and it will be started again immediately after application restart on success, and after InvisibilityTimeout on failure.&lt;/p&gt;

&lt;p&gt;When the feature of re-queueing jobs on &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAbortException&lt;/code&gt; plays together with job cancellation tokens, it means that you greatly decrease the latency of background job processing, because the probability of using &lt;code class=&quot;highlighter-rouge&quot;&gt;InvisibilityTimeout&lt;/code&gt; is greatly decreased.&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;Use job cancellation tokens where possible to ensure that your jobs are shutting down gracefully, and you greatly decrease the probability of high latencies in your background job processing.&lt;/p&gt;

&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Cancellation token for job methods that throws on server shutdown and job aborts.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; – Place interrupted job back to its queue if possible.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – Can not delete jobs when method or class was removed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – NullReferenceException in Monitor.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fixed&lt;/strong&gt; – SqlException when changing state of a job with absent target method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://hangfire.io&quot;&gt;HangFire Official Site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/odinserj/HangFire&quot;&gt;HangFire GitHub Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages?q=hangfire&quot;&gt;HangFire Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>HangFire 0.8.2 Released</title>
   <link href="http://odinserj.net/2014/05/21/hangfire-0.8.2-released/"/>
   <updated>2014-05-21T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/05/21/hangfire-0.8.2-released</id>
   <content type="html">&lt;h2 id=&quot;batch-operations&quot;&gt;Batch operations&lt;/h2&gt;

&lt;p&gt;Got tired to aim and click the &lt;em&gt;Retry&lt;/em&gt; button on each failed job? It is much easier now, look at this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/batch.gif&quot; alt=&quot;Batch operations&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Never knew that animated GIF optmimzation is so boring, especially for the first time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You are also able to select all jobs in one click. Many thanks to GitHub Issues – they gave me an idea of how to make an ideal implementation of multiple items selection.&lt;/p&gt;

&lt;h2 id=&quot;additional-metrics&quot;&gt;Additional metrics&lt;/h2&gt;

&lt;p&gt;When you write the code, it is important to have an instrument to measure the performance time. ASP.NET has different diagnostic tools for this task – &lt;a href=&quot;http://getglimpse.com&quot;&gt;Glimpse&lt;/a&gt;, &lt;a href=&quot;http://miniprofiler.com&quot;&gt;MiniProfiler&lt;/a&gt; and other useful ones. But they are aimed to provide information about requests only, and almost useless for background jobs – they are being executed outside of a request.&lt;/p&gt;

&lt;p&gt;I’ve implemented simple diagnostic feature (and not the replacement to full-stack performance profilers, such as &lt;a href=&quot;http://www.jetbrains.com/profiler/&quot;&gt;dotTrace&lt;/a&gt;) for HangFire Monitor, and now you are able to see the following timings in HangFire Monitor:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Duration&lt;/strong&gt; – job method + all filters performance time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt; – delay between the job creation and method invocation. This metric shows you background job invocation overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/metrics.png&quot; alt=&quot;Additional metrics&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, you can also watch the delays between state transitions. All timings have minimum resolution in 1 second. This resolution caused by using unix timestamps in HangFire. We need to change the things, but a bit later.&lt;/p&gt;

&lt;p&gt;These metrics are added to the &lt;code class=&quot;highlighter-rouge&quot;&gt;SucceededState&lt;/code&gt;, so they are also available to state filters.&lt;/p&gt;

&lt;h2 id=&quot;disableconcurrentexecution-filter&quot;&gt;DisableConcurrentExecution filter&lt;/h2&gt;

&lt;p&gt;This filter places a distributed lock in the beginning of a method performance and releases it after it was completed using the &lt;code class=&quot;highlighter-rouge&quot;&gt;IServerFilter&lt;/code&gt; interface. The type and method name are being used as a locking resource. Each storage uses its own distributed lock implementation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SQL Server uses &lt;code class=&quot;highlighter-rouge&quot;&gt;sp_getapplock&lt;/code&gt; stored procedure;&lt;/li&gt;
  &lt;li&gt;Redis implementation uses the technique described in its &lt;a href=&quot;http://redis.io/commands/setnx&quot;&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;[DisableConcurrentExecution(timeoutInSeconds: 10 * 60)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Operations performed inside a distributed lock&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The filter is very useful in situations when you are accessing/changing shared resources that does not provide built-in locking routines. Otherwise you should make argumentative decision between using locking system provided by the resource itself, and a custom distributed locking like this filter.&lt;/p&gt;

&lt;p&gt;For example, in SQL Server it is better to consider using different isolation levels and table hints first.&lt;/p&gt;

&lt;h2 id=&quot;changes&quot;&gt;Changes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - Batch operations on jobs for HangFire Monitor.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - Retry and delete buttons for almost every page of HangFire Monitor.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - Duration and latency metrics for succeeded jobs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - Display state transition latencies on job details page.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - DisableConcurrentExecution filter.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Misc&lt;/strong&gt; - Tables in HangFire Monitor received some love.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://hangfire.io&quot;&gt;HangFire Official Site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/odinserj/HangFire&quot;&gt;HangFire GitHub Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages?q=hangfire&quot;&gt;HangFire Packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>HangFire 0.8.1 released</title>
   <link href="http://odinserj.net/2014/05/17/hangfire-0.8.1-released/"/>
   <updated>2014-05-17T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/05/17/hangfire-0.8.1-released</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Note.&lt;/strong&gt; &lt;em&gt;The following information may be outdated after 1.0 release. Please see the &lt;a href=&quot;http://docs.hangfire.io&quot;&gt;official documentation&lt;/a&gt; first.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;release-notes&quot;&gt;Release notes&lt;/h2&gt;

&lt;p&gt;This release contains a bunch of new features, that complete the background job workflow (Deleted state), make it without additional latencies (MSMQ support) and inform you about failures in-time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - MSMQ queues support for SQL Server job storage.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - “Deleted” state for jobs, when we don’t want to process them anymore.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - “Requeue” and “Delete” buttons on a job page in HF Monitor.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - Logging job failures: warning - there is a retry, error - no attempts left.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Added&lt;/strong&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJob.Requeue&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJob.Delete&lt;/code&gt; methods.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; - Set &lt;code class=&quot;highlighter-rouge&quot;&gt;InvisibleTimeout&lt;/code&gt; back from 5 to 30 minutes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Changed&lt;/strong&gt; - &lt;code class=&quot;highlighter-rouge&quot;&gt;RetryAttribute&lt;/code&gt; is deprecated. Use &lt;code class=&quot;highlighter-rouge&quot;&gt;AutomaticRetryAttribute&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As always, the new version of &lt;a href=&quot;http://hangfire.io&quot;&gt;HangFire&lt;/a&gt; can be installed via NuGet Gallery. Here is the &lt;a href=&quot;https://www.nuget.org/packages?q=hangfire&quot;&gt;package list&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;deleting-jobs&quot;&gt;Deleting jobs&lt;/h2&gt;

&lt;p&gt;There is a new state – &lt;code class=&quot;highlighter-rouge&quot;&gt;DeletedState&lt;/code&gt;, and some methods to perform the deletion – &lt;code class=&quot;highlighter-rouge&quot;&gt;BackgroundJob.Delete&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;IBackgroundJobClient.Delete&lt;/code&gt;. When you are using these methods, a job is not being actually deleted, there is only state transition. Jobs in the deleted state expire after some delay (as succeeded jobs).&lt;/p&gt;

&lt;p&gt;The operation does not provides guarantee that the job will not be performed. If you deleting a job that is performing right now, it will be performed anyway, despite of calls to delete methods.&lt;/p&gt;

&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;

&lt;p&gt;If you want to delete job despite its current state, do the following:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jobId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IBackgroundJobClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BackgroundJobClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to be able to handle only deletion from exact state, for example, only from FailedState (because it may change after you click and before it will be deleted), you can specify it:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jobId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FailedState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StateName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// or&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IBackgroundJobClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BackgroundJobClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jobId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ScheduledState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StateName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;manual-deletion&quot;&gt;Manual deletion&lt;/h3&gt;

&lt;p&gt;Job details page now contains &lt;em&gt;Requeue&lt;/em&gt; and &lt;em&gt;Delete&lt;/em&gt; buttons (other pages also have these buttons). So, you can requeue and delete your jobs at any time:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/job-deletion.png&quot; alt=&quot;Job deletion&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;msmq-support-for-sql-server-storage&quot;&gt;MSMQ support for SQL Server storage&lt;/h2&gt;

&lt;p&gt;SQL Server job storage implementation does not require you to learn and install additional technologies, such as Redis, for projects to use HangFire. However, it uses polling to get new jobs that &lt;em&gt;increases latency&lt;/em&gt; between the creation and invocation process (see also &lt;a href=&quot;https://github.com/odinserj/HangFire/issues/52&quot;&gt;this feature request&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The MSMQ implementation, that was introduced in HangFire 0.8.1, replaces only the way HangFire enqueues and dequeues jobs. It uses transactional queues to delete jobs only upon successful completion, that allows to process jobs reliably inside ASP.NET applications. MSMQ queues contain &lt;strong&gt;only job identifiers&lt;/strong&gt;, other information is still &lt;strong&gt;persisted in the SQL Server database&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;advantages-of-using-msmq&quot;&gt;Advantages of using MSMQ&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No additional latency&lt;/strong&gt;. It uses blocking calls to fetch jobs – they will be processed as soon as possible.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Immediate re-queueing of terminated jobs&lt;/strong&gt;. If the processing was terminated in the middle, it will be started again immediately after application restart. SQL Server implementation uses InvisibleTimeout to distinguish between long-running and aborted jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;

&lt;p&gt;MSMQ support for SQL Server job storage implementation, like other HangFire extensions, is a NuGet package. So, you can install it using NuGet Package Manager Console window:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PM&amp;gt; Install-Package HangFire.SqlServer.MSMQ
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;usage-1&quot;&gt;Usage&lt;/h3&gt;

&lt;p&gt;To use MSMQ queues, you should do the following steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create them manually on each host. Don’t forget to grant appropriate permissions.&lt;/li&gt;
  &lt;li&gt;Register all MSMQ queues in current &lt;code class=&quot;highlighter-rouge&quot;&gt;SqlServerStorage&lt;/code&gt; instance.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;connection string&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseMsmqQueues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;.\hangfire-{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;critical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// or storage.UseMsmqQueues(@&quot;.\hangfire-{0}&quot;) if you are using only &quot;default&quot; queue.&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;JobStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To see the full list of supported paths and queues, check the &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/e9d4k4ze.aspx&quot;&gt;MSDN article&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;limitations&quot;&gt;Limitations&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Only transactional MSMQ queues supported for reability reasons inside ASP.NET.&lt;/li&gt;
  &lt;li&gt;You can not use both SQL Server Job Queue and MSMQ Job Queue implementations in the same server (see below). This limitation relates to HangFire.Server only. You can still enqueue jobs to whatever queues and watch them both in HangFire.Monitor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following case will &lt;strong&gt;not work&lt;/strong&gt;: the &lt;code class=&quot;highlighter-rouge&quot;&gt;critical&lt;/code&gt; queue uses MSMQ, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;default&lt;/code&gt; queue uses SQL Server to store job queue. In this case job fetcher can not make the right decision.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;connection string&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseMsmqQueues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;.\hangfire-{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;critical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;JobStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackgroundJobServerOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Queues&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;critical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AspNetBackgroundJobServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;transition-to-msmq-queues&quot;&gt;Transition to MSMQ queues&lt;/h3&gt;

&lt;p&gt;If you have a fresh installation, just use the &lt;code class=&quot;highlighter-rouge&quot;&gt;UseMsmqQueues&lt;/code&gt; method. Otherwise, your system may contain unprocessed jobs in SQL Server. Since one HangFire.Server instance can not process job from different queues, you should deploy two instances of HangFire.Server, one listens only MSMQ queues, another – only SQL Server queues. When the latter finish its work (you can see this from HangFire.Monitor – your SQL Server queues will be removed), you can remove it safely.&lt;/p&gt;

&lt;p&gt;If you are using default queue only, do this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* This server will process only SQL Server table queues, i.e. old jobs */&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldStorage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;connection string&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackgroundJobServerOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ServerName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;OldQueueServer&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Pass this to differentiate this server from the next one&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldQueueServer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AspNetBackgroundJobServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;oldQueueServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* This server will process only MSMQ queues, i.e. new jobs */&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Assign the storage globally, for client, server and monitor.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;JobStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;connection string&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseMsmqQueues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;.\hangfire-{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AspNetBackgroundJobServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you use multiple queues, do this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* This server will process only SQL Server table queues, i.e. old jobs */&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldStorage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;connection string&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackgroundJobServerOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Queues&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;critical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Include this line only if you have multiple queues&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ServerName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;OldQueueServer&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Pass this to differentiate this server from the next one&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldQueueServer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AspNetBackgroundJobServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;oldQueueServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* This server will process only MSMQ queues, i.e. new jobs */&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Assign the storage globally, for client, server and monitor.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;JobStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SqlServerStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;connection string&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseMsmqQueues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;.\hangfire-{0}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackgroundJobServerOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Queues&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;critical&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AspNetBackgroundJobServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Are your methods ready to run in background?</title>
   <link href="http://odinserj.net/2014/05/10/are-your-methods-ready-to-run-in-background/"/>
   <updated>2014-05-10T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/05/10/are-your-methods-ready-to-run-in-background</id>
   <content type="html">&lt;p&gt;HangFire takes regular classes and regular methods to perform them in the background, because it is simple to use them:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hi!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This snippet says that the &lt;code class=&quot;highlighter-rouge&quot;&gt;Console.WriteLine&lt;/code&gt; method will be &lt;em&gt;called&lt;/em&gt; in background. But notice that the name of the method is &lt;code class=&quot;highlighter-rouge&quot;&gt;Enqueue&lt;/code&gt;, and not &lt;code class=&quot;highlighter-rouge&quot;&gt;Call&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke&lt;/code&gt; and so on.&lt;/p&gt;

&lt;p&gt;The name of the method was chosen to highlight that invocation of a given method is only being &lt;em&gt;queued&lt;/em&gt; in the current execution context and returns the control to a calling thread immediately after enqueueing. It will be invoked in a &lt;em&gt;different&lt;/em&gt; execution context.&lt;/p&gt;

&lt;p&gt;What does this mean? Several things, that may break your usual expectations about method invocation process. You should be aware of them.&lt;/p&gt;

&lt;h2 id=&quot;differences-between-local-and-background-method-invocation&quot;&gt;Differences between local and background method invocation&lt;/h2&gt;

&lt;h3 id=&quot;method-invocation-is-being-serialized&quot;&gt;Method invocation is being serialized&lt;/h3&gt;

&lt;p&gt;Before creating a background job, the information about the given method (its type, method name and parameter types) are serialized to strings. MethodInfo serialization process is absolutely invisible to a user, unlike arguments serialization.&lt;/p&gt;

&lt;p&gt;Arguments are also serialized to string, but arguments serialization process uses the &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeConverter&lt;/code&gt; class. All standard classes like numbers, strings, dates and so on already have the corresponding &lt;code class=&quot;highlighter-rouge&quot;&gt;TypeConverter&lt;/code&gt; implementation, but if you want to pass an instance of a custom class as an argument, you should write the custom converter first.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Does not work until you implement the custom TypeConverter.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckArticle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Furthermore, serialized arguments can take more space, and it often is more efficient to pass database identifiers or file names instead of their contents.&lt;/p&gt;

&lt;h3 id=&quot;execution-context-is-being-changed&quot;&gt;Execution context is being changed&lt;/h3&gt;

&lt;p&gt;In the simplest case, such as using &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadPool.QueueUserWorkItem&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Task.Factory.StartNew&lt;/code&gt; methods, only thread is being changed. But in HangFire, you can use different process, or different server to process background jobs.&lt;/p&gt;

&lt;p&gt;So, the &lt;em&gt;execution context&lt;/em&gt; term includes not only thread context, request context and so on, but also static data, including local locks, local filesystem, etc.&lt;/p&gt;

&lt;p&gt;That is why if you are querying data inside a background job that corresponds to the execution context where the job was enqueued, it may fail. If you need to pass the current state to a job, use arguments or shared storage.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Does not work, use distributed locks.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 

    &lt;span class=&quot;c1&quot;&gt;// Does not work either, pass data as an argument.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsLocal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Processing&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;delayed-invocation&quot;&gt;Delayed invocation&lt;/h3&gt;

&lt;p&gt;Background job methods are not invoked immediately. It is placed on a queue and waits until any worker pick it up. This leads to an undefined start time and end time.&lt;/p&gt;

&lt;h4 id=&quot;undefined-start-up-time&quot;&gt;Undefined start-up time&lt;/h4&gt;

&lt;p&gt;Your method can be invoked tomorrow, after two weeks or six months (always true for scheduled jobs, but works with “fail-deploy-retry” practice as well). If it is true even for regular method calls, that application data can be changed or arguments can become stale during the method invocation, especially in a highly concurrent web applications, the probability of these situations in background job processing is very high.&lt;/p&gt;

&lt;p&gt;Always double-check the data that you pass as arguments and think about its changing nature. Here are some examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You want to publish an article tomorrow, think, what do you need to do: publish its current state, or publish the article itself. In the most cases you’d choose to publish the article itself, changed or unchanged. That is why in this case you need to pass an &lt;em&gt;article identifier&lt;/em&gt; as an argument.&lt;/li&gt;
  &lt;li&gt;You want to check comments for spam and it is possible to change them. You are creating a new background job on each edit attempt. In this case, you need to check &lt;em&gt;exactly the given text&lt;/em&gt; for each edit, so pass the whole text as an argument. And after the check is completed, you can compare this text with the current one. If it was changed, then don’t do anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;undefined-end-time&quot;&gt;Undefined end time&lt;/h4&gt;

&lt;p&gt;We are thinking about the end time, when we want to tell our users about the job was completed. If you do something inside the request processing synchronously, you can rely on that fact that this information will be available for a user immediately. That is why you can redirect her to a just created article page.&lt;/p&gt;

&lt;p&gt;But when, for example, you are creating the same article in a background job, you can redirect user to a non-existing page yet, because you can not guarantee that the enqueued job will be processed in time.&lt;/p&gt;

&lt;p&gt;Of course, you always can tune your system to perform background jobs as soon as possible, but you can not eliminate the delay between enqueue time and a real invocation completely.&lt;/p&gt;

&lt;p&gt;So you need to think how to show yet unprocessed entities (with loaders, progress bars and so on), and how to report the job completion. For a latter task, you can always use polling, server push (for example, using &lt;a href=&quot;http://signalr.net&quot;&gt;SignalR&lt;/a&gt;), or force users to reload the page manually.&lt;/p&gt;

&lt;h3 id=&quot;delayed-failure&quot;&gt;Delayed failure&lt;/h3&gt;

&lt;p&gt;If you pass user input as an argument of your job, you should validate them first. Otherwise the job will always fail, retry, fail again, retry again and so on. It will never be completed without manual intervention.&lt;/p&gt;

&lt;p&gt;You can disable automatic retry on failure, but there is another problem – since the immediate invocation is not guaranteed (see the section &lt;em&gt;Delayed invocation&lt;/em&gt;), your user may leave the site and she will not be able to correct the data.&lt;/p&gt;

&lt;p&gt;So, validate your arguments early, and instead of doing this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentNullException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Processing&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ValidationError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Pseudo-code&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BackgroundJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckComment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// But you can leave the guard condition here.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Processing&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;method-can-be-called-multiple-times&quot;&gt;Method can be called multiple times&lt;/h3&gt;

&lt;p&gt;Your method can be retried manually (through the Monitor interface) or automatically on failure (i.e on unexpected exception that is unhandler by the method itself).&lt;/p&gt;

&lt;p&gt;So, be prepared for this situation. Try to make all your background job methods &lt;a href=&quot;http://en.wikipedia.org/wiki/Idempotence&quot;&gt;idempotent&lt;/a&gt;. Be prepared, in very rare cases, that your background job can fire multiple times.&lt;/p&gt;

&lt;p&gt;You can always disable the automatic retry feature by applying the &lt;code class=&quot;highlighter-rouge&quot;&gt;[Retry(0)]&lt;/code&gt; filter to the exact method or globally. But to successfully fight with ASP.NET unexpected application domain unload in the middle of a job processing, HangFire retries them automatically despite of the given attribute. But don’t worry too much, these cases happen very rarely.&lt;/p&gt;

&lt;p&gt;But as a general rule remember, that your job will be performed &lt;strong&gt;at least once&lt;/strong&gt;. You can test your job for idempotence by calling it multiple times and compare the result:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TestIdempotence&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;// Arrange&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

   &lt;span class=&quot;c1&quot;&gt;// Act&lt;/span&gt;
   &lt;span class=&quot;nf&quot;&gt;YourMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
   &lt;span class=&quot;nf&quot;&gt;YourMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

   &lt;span class=&quot;c1&quot;&gt;// Assert&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;// For example, check that records were not duplicated.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;method-may-become-unavailable&quot;&gt;Method may become unavailable&lt;/h3&gt;

&lt;p&gt;Your application can be redeployed with a different code base after a background job was enqueued. That is why you should change the signature of your background job methods carefully.&lt;/p&gt;

&lt;p&gt;You can safely change parameter names, but the following things will lead to a broken job (however you are able to fix it and re-deploy the application):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Addition and removal of method parameters.&lt;/li&gt;
  &lt;li&gt;Parameter type changes.&lt;/li&gt;
  &lt;li&gt;Parameter reordering.&lt;/li&gt;
  &lt;li&gt;Method name changes.&lt;/li&gt;
  &lt;li&gt;Method’s type name changes.&lt;/li&gt;
  &lt;li&gt;Method removal.&lt;/li&gt;
  &lt;li&gt;Method’s type removal.&lt;/li&gt;
  &lt;li&gt;Type’s namespace changes.&lt;/li&gt;
  &lt;li&gt;Type’s assembly changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course you can do all the above things, if there are no jobs in a storage. But instead of doing this, add a new method without touching the old one and call the new method from the old one until all old jobs become processed:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OldMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;c1&quot;&gt;// Redirect&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;NewMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultValueForArg3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NewMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Real processing&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;There are a lot of differences between local and background method invocation, but you likely know the most of them, because they relate to asynchronous and concurrent programming as well.&lt;/p&gt;

&lt;p&gt;If you have any questions, ask them in the comments form below the post or start a new topic on http://discuss.hangfire.io if you want to share code snippets.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>HangFire 0.8 Released</title>
   <link href="http://odinserj.net/2014/05/02/hangfire-0.8-released/"/>
   <updated>2014-05-02T00:00:00+00:00</updated>
   <id>http://odinserj.net/2014/05/02/hangfire-0.8-released</id>
   <content type="html">&lt;p&gt;Today I’ve pushed the &lt;a href=&quot;https://github.com/odinserj/HangFire/releases/tag/v0.8&quot;&gt;next version&lt;/a&gt; of &lt;a href=&quot;http://hangfire.io&quot;&gt;HangFire&lt;/a&gt; project that allows to process background jobs inside ASP.NET applications in a reliable way.&lt;/p&gt;

&lt;p&gt;Although this release contains only one new feature that is visible for all users (security improvements), it brings HangFire closer than ever to the version 1.0. But let’s see the new changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note.&lt;/strong&gt; &lt;em&gt;The conceptual part of the following information is correct, but implementation details changed since version 1.0. Please, see the &lt;a href=&quot;http://docs.hangfire.io&quot;&gt;documentation&lt;/a&gt; instead of relying this information.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;remote-requests-are-denied-by-default&quot;&gt;Remote requests are denied by default&lt;/h2&gt;

&lt;p&gt;HangFire has integrated monitoring system that shows you information about background jobs and acts as an ASP.NET’s &lt;code class=&quot;highlighter-rouge&quot;&gt;IHttpHandler&lt;/code&gt;. Registration is performed by automatically changing the &lt;code class=&quot;highlighter-rouge&quot;&gt;web.config&lt;/code&gt; file during the installation of the &lt;a href=&quot;https://www.nuget.org/packages/HangFire.Web/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HangFire.Web&lt;/code&gt;&lt;/a&gt; package (it is a dependency of &lt;a href=&quot;https://www.nuget.org/packages/HangFire/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HangFire&lt;/code&gt;&lt;/a&gt; bootstrap package) by NuGet Package Manager.&lt;/p&gt;

&lt;p&gt;This HTTP handler displays the internals of your application. Furthermore it offers to interact with this internal state: background jobs can be retried, possibly removed, scheduled earlier than necessary and so on. What if it become available for malicious users? It is hard to guess the actual consequences for a wide range of applications, but it is simple to understand that this behavior is highly undesirable.&lt;/p&gt;

&lt;p&gt;The automatics greatly simplifies the installation process, but performs it on the quiet, making it simple to overlook that Monitor handler is ever exist or requires additional set-up. So, it is required to provide strong rules that work by default.&lt;/p&gt;

&lt;p&gt;To solve this problem, we need to authorize users. ASP.NET applications have authorization feature by default, that is based on &lt;a href=&quot;http://msdn.microsoft.com/en-US/library/eeyk640h.aspx&quot;&gt;user names&lt;/a&gt; or their &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/5k850zwb.aspx&quot;&gt;roles&lt;/a&gt;. But it is impossible to guess what user names and roles uses your ASP.NET application (if your application uses them at all). So, we need more simple solution.&lt;/p&gt;

&lt;p&gt;The simplest solution is to block all remote requests by default using the &lt;code class=&quot;highlighter-rouge&quot;&gt;Request.IsLocal&lt;/code&gt; property. Local requests are accepted as usually, and this decision does not bring problems for application developers. Another libraries, such as &lt;a href=&quot;https://code.google.com/p/elmah/&quot;&gt;Elmah&lt;/a&gt; or &lt;a href=&quot;http://getglimpse.com&quot;&gt;Glimpse&lt;/a&gt; are also using this authorization policy by default.&lt;/p&gt;

&lt;p&gt;The policy can be configured from the &lt;code class=&quot;highlighter-rouge&quot;&gt;appSettings&lt;/code&gt; section of &lt;code class=&quot;highlighter-rouge&quot;&gt;web.config&lt;/code&gt; file – I don’t want to introduce separate configuration section for a single setting.&lt;/p&gt;

&lt;p&gt;Disable remote access to HangFire Monitor (by default):&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;appSettings&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- You can also remove this line to keep the default settings --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hangfire:EnableRemoteMonitorAccess&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/appSettings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Enable remote access to HangFire Monitor:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;appSettings&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hangfire:EnableRemoteMonitorAccess&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  ...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/appSettings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;granting-or-denying-access-via-aspnet-authorization&quot;&gt;Granting or denying access via ASP.NET authorization&lt;/h2&gt;

&lt;p&gt;Remote Monitor access policy is good. But once your application is deployed to production environment, you need to be able to access Monitor, otherwise it is useless.&lt;/p&gt;

&lt;p&gt;Elmah library author and contributors conducted a deep investigation on a topic and proposed &lt;a href=&quot;https://code.google.com/p/elmah/wiki/SecuringErrorLogPages&quot;&gt;the solution&lt;/a&gt; to provide use ASP.NET authorization for such registered handlers:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The handler registrations need to be moved under the location tag. Having them outside does not secure access sufficiently.&lt;/li&gt;
  &lt;li&gt;The location element’s path attribute carries the same value as the path attribute of the handler registrations. Unfortunately, it has to be repeated so if you change one, the others must be updated to match.&lt;/li&gt;
  &lt;li&gt;The authorization element is where the authorization rules go and where you will want to selectively grant access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, starting from this version, HangFire Monitor HTTP handler is being registered under the &lt;code class=&quot;highlighter-rouge&quot;&gt;location&lt;/code&gt; tag:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;location&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hangfire.axd&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;inheritInChildApplications=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.web&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;authorization&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;deny&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;users=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/authorization&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.web&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.webServer&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;handlers&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HangFire&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hangfire.axd&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;verb=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HangFire.Web.HangFirePageFactory, HangFire.Web&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/handlers&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.webServer&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/location&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The configuration example above denies access to all users, but that is a good starting point. You will probably want to add rules that allow access to only specific users and/or roles. For example, you might have a role for administrators and developers called admin and dev, respectively. To allow users that are members of either role, you could configure the authorization section above as follows:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;authorization&amp;gt;&lt;/span&gt;  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;allow&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;admin&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;allow&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dev&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;deny&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;users=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;  
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/authorization&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But to enable these rules, you need to set-up &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/vstudio/eeyk640h.aspx&quot;&gt;ASP.NET Authentication&lt;/a&gt; using, for example, the new and shining &lt;a href=&quot;http://www.asp.net/identity&quot;&gt;ASP.NET Identity&lt;/a&gt; project.&lt;/p&gt;

&lt;h2 id=&quot;moving-closer-to-10&quot;&gt;Moving closer to 1.0&lt;/h2&gt;

&lt;p&gt;This version contains 300+ new unit tests, their total number is 550, and the test coverage of &lt;code class=&quot;highlighter-rouge&quot;&gt;HangFire.Core.dll&lt;/code&gt; is 99%. Although this does not say that HangFire is 100% bug-free, this is an indicator of the fact that the most code is tested and it is fully unit testable. New features can be added much faster. To do this, I’ve completely rewritten the Server subsystem that resulted in a more clean application model.&lt;/p&gt;

&lt;p&gt;Full unit testability was one of the goals I wanted to achieve, and the fact that it requires a lot of breaking changes stopped me even to think about production-ready version. I want to use &lt;a href=&quot;http://semver.org&quot;&gt;semantic versioning&lt;/a&gt; and do not want to have HangFire 28.0 in a few months.&lt;/p&gt;

&lt;p&gt;Now I’ve started to think about production readiness, but I need a lot of feedback to give any stability guarantees.&lt;/p&gt;

&lt;p&gt;Recently I’ve launched a new forum for HangFire to replace unnoying (for me) Google Groups – http://discuss.hangfire.io. It is modern, clean and easy to use, based on &lt;a href=&quot;http://www.discourse.org&quot;&gt;Discourse&lt;/a&gt;. So, have ideas or problems? Let’s discuss!&lt;/p&gt;
</content>
 </entry>
 

</feed>
