-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CRITICAL BUG] Messages are "rolled back" with the main transaction (simple TransactionScope) #134
Comments
I guess it may be the issue inside Hangfire.SqlServer itself. Console just uses existing Storage API, but how transactions are made is up to storage implementation. Particularly, Hangfire.SqlServer uses Probably @odinserj can comment on why it does so, and whether it is correct. If this is the expected behavior, I guess your easiest option would be making an extension method that wraps logging into another TransactionScope with |
In some cases, when you are finishing your job you want the work to be committed along with the state change, so using the usual The issue although is that Hangfire.Console, since it's (or at least I use it as) a logging "library" to a given job, it should have a different behavior, and thus should create its own: |
Solution: For those having the same issues, here's a solution, based on: https://github.com/AnderssonPeter/Hangfire.Console.Extensions/blob/9ef89d8743459e77c5a139d7241765d79ece8602/Hangfire.Console.Extensions/HangfireLogger.cs With a little bit of abstraction: using Microsoft.Extensions.Logging;
public interface IJobProgress
{
public IJobProgressBar CreateProgressBar(string? name = null, double value = 0);
public void WriteProgressLine(LogLevel level, string message, params object[] p);
}
public interface IJobProgressBar
{
/// <summary>
/// Expecting values in the range of 0..1!
/// </summary>
/// <param name="value"></param>
void SetValue(double value);
} And a very similar implementation - but with a new transaction scope with suppress placed around the logging: using Hangfire.Console;
using Hangfire.Console.Extensions;
using Hangfire.Console.Progress;
using Hangfire.Server;
using Microsoft.Extensions.Logging;
namespace -- include your common interfaces --
public class HangfireSyncProgressBarWrapper : IJobProgressBar
{
protected readonly IProgressBar progressBar;
public HangfireSyncProgressBarWrapper(IProgressBar progressBar)
{
this.progressBar = progressBar;
}
public void SetValue(double value)
{
using (var ts = TransactionScopeBuilder.New(System.Transactions.TransactionScopeOption.Suppress))
{
progressBar.SetValue(100.0 * value);
ts.Complete();
}
}
}
public class HangfireSyncProgress : IJobProgress
{
public HangfireSyncProgress(PerformingContext performingContext, IProgressBarFactory progressBarFactory)
{
this.PerformingContext = performingContext;
this.ProgressBarFactory = progressBarFactory;
}
protected PerformContext PerformingContext { get; }
protected IProgressBarFactory ProgressBarFactory { get; }
public IJobProgressBar CreateProgressBar(string? name = null, double value = 0)
{
IProgressBar progressBar;
using (var ts = TransactionScopeBuilder.New(System.Transactions.TransactionScopeOption.Suppress))
{
progressBar = ProgressBarFactory.Create(name: name, value: value);
ts.Complete();
}
var wrapper = new HangfireSyncProgressBarWrapper(progressBar);
return wrapper;
}
public void WriteProgressLine(LogLevel level, string message, params object[] p)
{
using (var ts = TransactionScopeBuilder.New(System.Transactions.TransactionScopeOption.Suppress))
{
PerformingContext.WriteLine(GetConsoleColor(level), message, p);
ts.Complete();
}
}
/// <summary>
/// https://github.com/AnderssonPeter/Hangfire.Console.Extensions/blob/9ef89d8743459e77c5a139d7241765d79ece8602/Hangfire.Console.Extensions/HangfireLogger.cs
/// </summary>
/// <param name="logLevel"></param>
/// <returns></returns>
protected ConsoleTextColor GetConsoleColor(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.Critical:
return ConsoleTextColor.Red;
case LogLevel.Error:
return ConsoleTextColor.Yellow;
case LogLevel.Warning:
return ConsoleTextColor.DarkYellow;
case LogLevel.Information:
return ConsoleTextColor.White;
case LogLevel.Debug:
return ConsoleTextColor.DarkGray;
case LogLevel.Trace:
return ConsoleTextColor.Gray;
default:
throw new ArgumentOutOfRangeException(nameof(logLevel));
}
}
} The |
Yeah, this is basically what I meant. But you should add a suppress stope to I hate the idea of introducing a conditional dependency to System.Transactions and a bunch of checks for Windows/non-Windows environments, especially in case it would be confirmed to be an issue on SqlServer storage's side. I can't think of a case when Hangfire storage operations may require to be enlisted into some outer transaction scope, and see |
Thanx. Code in the comment above is updated. For me (due to those wrappers) it's easier to do the patch around those 2 operations. The reason for Hangfire to be enlisted in the operation is simple:
|
Never noticed before, but testing is simple.
We just need a job, 1 transaction scope that commits, and 1 that rolls back.
Issues:
Expected behavior:
Using:
This is the code inside the Job:
This is the console output - every log message inside the transaction are not showing up until commit and are disappearing when rolling back.
The text was updated successfully, but these errors were encountered: