HangfireIO / HangfireIO/Hangfire

Hangfire workers pick up same job one after another

Open
#2,525 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
10.1k
Forks
1.8k
Avg merge
1h 19m
Merged PRs (30d)
1

Description

MRE:

DB -> sqlite, `hangfire.db`

Registering in program.cs of an api project -> `builder.Services.AddPersonalHangfire(hangfireConnectionString);`

Service extension ->
```
public static IServiceCollection AddPersonalHangfire(this IServiceCollection services, string dbConnectionString)
{
var sqliteOptions = new SQLiteStorageOptions();
services.AddHangfire(configuration => configuration
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSQLiteStorage(dbConnectionString, sqliteOptions)
);
services.AddHangfireServer();

return services;
}
```

Hangfire job invoke -> `BackgroundJob.Enqueue(job => job.SendEmail(message));`

Enqueued job implementation ->
```
public class EmailJob
{
private readonly IEmailManager _emailManager;
public EmailJob(IEmailManager emailManager)
{
_emailManager = emailManager;
}
public Task SendEmail(Message message)
{
return _emailManager.SendEmailAsync(message);
}
}
```

Actual mail method ->
```
public async Task SendEmailAsync(Message message)
{
if (_emailOptions?.CurrentValue is null)
{
Log.Error("Cannot retrieve mailbox configuration.");
return false;
}

var isMessageSent = false;

using var client = new SmtpClient();
try
{
var messageMime = CreateMessage(message);

await client.ConnectAsync(_emailOptions.CurrentValue.Host, int.Parse(_emailOptions.CurrentValue.Port), SecureSocketOptions.SslOnConnect);
await client.AuthenticateAsync(_emailOptions.CurrentValue.Email, _emailOptions.CurrentValue.Password);
await client.SendAsync(messageMime);

isMessageSent = true;
}
catch (Exception ex)
{
Log.Error($"Email to recipent {message.To} could not be sent.", ex.Message);
isMessageSent = false;
}
finally
{
if (client.IsConnected)
{
await client.DisconnectAsync(true);
client.Dispose();
}
}

return isMessageSent;
}
```

What happens?
User gets sent 20 emails, after one method invocation. Shouldn't it work like queue? When one worker takes the job, rest can not see it? What should i do to make only one worker send the email?

Temporary solution (not satisfied):
Set worker count to 0 ->
```
services.AddHangfireServer(options =>
{
options.WorkerCount = 1;
});

```

Contributor guide

Open the contributing guide

Research direction

Start in Program.cs with AddPersonalHangfire and AddHangfireServer, then trace BackgroundJob.Enqueue to EmailJob.SendEmail. Inspect the worker configuration and job execution history to determine why one invocation results in 20 sends, including whether the job is enqueued or retried repeatedly. Done means the cause of the duplicate sends is identified and the expected single-send behavior is verified.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sqlite
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.