> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Hangfire Servisi

> PostgreSQL destekli arka plan iş kuyruğu — MediatR command/event köprüsü, BasicAuth dashboard ve health check.

Hangfire servisi `src/DiyanetCleanArchitecture.Infrastructure.Jobs.Hangfire` projesindedir. Arka plan ve gecikmeli işleri **PostgreSQL** storage üzerinde çalıştırır ve MediatR ile köprülenmiş bir command/event-bus sunar.

## Storage — PostgreSQL

Hangfire job verileri ayrı bir `hangfire` şemasında tutulur. Bağlantı `ConnectionStrings:HangfireConnection`'dan gelir:

```csharp theme={null}
var storageOptions = new PostgreSqlStorageOptions
{
    SchemaName              = configuration.GetSection("Services:Hangfire:Schema").Value ?? "hangfire",
    QueuePollInterval       = TimeSpan.FromMilliseconds(200),
    InvisibilityTimeout     = TimeSpan.FromMinutes(5),
    DistributedLockTimeout  = TimeSpan.FromMinutes(5),
    PrepareSchemaIfNecessary= true,
    StartupConnectionMaxRetries = 2,
    AllowDegradedModeWithoutStorage = false,   // storage yoksa uygulama başlamaz
};

services.AddHangfire((provider, conf) =>
    conf.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
        .UseSimpleAssemblyNameTypeSerializer()
        .UseRecommendedSerializerSettings()
        .UsePostgreSqlStorage(opts =>
            opts.UseNpgsqlConnection(configuration.GetConnectionString("HangfireConnection")),
            storageOptions));
```

```json theme={null}
{
  "ConnectionStrings": {
    "HangfireConnection": "Host=localhost;Port=5432;Database=DiyanetCleanArchitecture;Username=postgres;Password=..."
  }
}
```

<Note>
  `QueuePollInterval` 200 ms'dir — Hangfire.PostgreSql 50 ms altına izin vermez; CPU sızıntısını önlemek için bilinçli olarak biraz yüksek tutulmuştur.
</Note>

## Sunucular ve kuyruklar

Her Hangfire server `IHostedService` olarak kaydedilir. `Services:Hangfire:Servers[]` listesindeki her giriş bir server üretir. Standart kurulumda iki server vardır — **event-bus** ve **command-bus** — her biri kendi kuyruğu ve DLX (dead-letter) kuyruğu ile:

```json theme={null}
{
  "Services": {
    "Hangfire": {
      "Path": "/jobs",
      "DashboardTitle": "DiyanetCleanArchitecture Hangfire Dashboard - Dev",
      "Username": "admin",
      "Password": "admin",
      "Schema": "hangfire",
      "Servers": [
        { "Name": "event-bus",   "WorkerCount": 5, "Queues": [ "event-queue",   "event-queue-dlx" ] },
        { "Name": "command-bus", "WorkerCount": 5, "Queues": [ "command-queue", "command-queue-dlx" ] }
      ]
    }
  }
}
```

```csharp theme={null}
foreach (var server in servers)
{
    services.AddHangfireServer(opt =>
    {
        opt.ServerName = server.Name;
        opt.WorkerCount = server.WorkerCount;
        opt.CancellationCheckInterval = TimeSpan.FromSeconds(10);
        opt.Queues = server.Queues;
    });
}
```

## MediatR köprüsü

`BuildingBlocks.Hangfire.MediatR.Extensions` paketi, Hangfire job'larını MediatR command/event'lerine bağlar. DI'da şu servisler kayıt edilir:

```csharp theme={null}
services.AddScoped<ICommandBus, CommandBus>();
services.AddScoped<ICommandExecuter, CommandExecuter>();
services.AddScoped<IEventBus, EventBus>();
services.AddScoped<IEventDispatcher, EventDispatcher>();
services.AddSingleton<ITimeZoneResolver>(new TimeZoneConverterResolver());
```

* **`ICommandBus`** — bir command'i command kuyruğuna enqueue eder; worker `ICommandExecuter` üzerinden MediatR `Send` çağırır.
* **`IEventBus`** — bir event'i event kuyruğuna enqueue eder; `IEventDispatcher` MediatR `Publish` çağırır.

İşlenmeyen işler ilgili DLX kuyruğuna (`*-dlx`) düşer.

## Dashboard — `/jobs` + BasicAuth

Dashboard `Services:Hangfire:Path` (default `/jobs`) altında host edilir ve `BasicAuthAuthorizationFilter` ile korunur (kullanıcı adı/şifre config'ten):

```csharp theme={null}
public static IApplicationBuilder UseHangfireDashboard(this IApplicationBuilder app, IConfiguration configuration)
{
    var authUser = new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
    {
        RequireSsl = false, SslRedirect = false, LoginCaseSensitive = true,
        Users = new[] { new BasicAuthAuthorizationUser
        {
            Login = configuration.GetSection("Services:Hangfire:Username").Value,
            PasswordClear = configuration.GetSection("Services:Hangfire:Password").Value
        }}
    });

    app.UseHangfireDashboard(configuration.GetSection("Services:Hangfire:Path").Value, new DashboardOptions
    {
        DashboardTitle = configuration.GetSection("Services:Hangfire:DashboardTitle").Value,
        IsReadOnlyFunc = ctx => false,
        Authorization = new[] { authUser }
    });

    return app;
}
```

<Warning>
  `RequireSsl` ve `SslRedirect` `false` ayarlıdır — dashboard'a HTTP üzerinden erişilebildiği varsayılır (ters proxy TLS sonlandırması yapar). Public erişimde mutlaka güçlü BasicAuth kimlik bilgileri kullanın.
</Warning>

## Health check

Hangfire server'lar ve storage erişimi `[Ready]` tag'li bir health check ile izlenir. Worker yoksa durum `Degraded` olur (API request/response çalışmaya devam etmeli):

```csharp theme={null}
services.AddHealthChecks()
    .AddHangfire(opts => opts.MinimumAvailableServers = 1,
        name: "hangfire",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { HealthCheckTags.Ready, HealthCheckTags.Jobs });
```

## Kullanım — RecurringJob / BackgroundJob

```csharp theme={null}
// Tek seferlik (fire-and-forget)
BackgroundJob.Enqueue<IMyJob>(j => j.RunAsync(payload));

// Gecikmeli
BackgroundJob.Schedule<IMyJob>(j => j.RunAsync(payload), TimeSpan.FromMinutes(10));

// Periyodik (cron)
RecurringJob.AddOrUpdate<IMyJob>("daily-cleanup", j => j.CleanupAsync(), Cron.Daily);
```

MediatR köprüsü üzerinden command/event enqueue:

```csharp theme={null}
await _commandBus.SendAsync(new MyCommand(...));   // command-queue
await _eventBus.PublishAsync(new MyEvent(...));     // event-queue
```

<CardGroup cols={2}>
  <Card title="Events" icon="bolt" href="/events/overview">
    Command/event bus akışı ve MassTransit Outbox ile ilişkisi.
  </Card>

  <Card title="Operasyon" icon="gear" href="/operations/overview">
    Dashboard erişimi, izleme ve dağıtım notları.
  </Card>
</CardGroup>
