Skip to content

Multi-Tenancy

GrydJobs integrates with Gryd.IO's multi-tenancy infrastructure to provide automatic tenant isolation across all job operations.

How It Works

  1. Job Enqueue — The current tenant ID is captured when a job is enqueued
  2. Job Execution — Tenant context is restored before the job executes
  3. Data Isolation — All queries filter by tenant via ITenantQueryContext
  4. Dashboard — Stats and listings respect tenant boundaries

Tenant Context Propagation

When a job is enqueued from a tenant-scoped request, the tenant ID is automatically:

  1. Stored in the JobDefinition.TenantId field
  2. Passed to the Hangfire job payload
  3. Restored via TenantContextAccessor when the job executes
csharp
// Tenant context is automatically captured from the current request
await scheduler.EnqueueAsync<SendEmailJob, SendEmailArgs>(
    new SendEmailArgs(email, subject, body));
// The job will execute with the same tenant context

Database Isolation

The JobsDbContext implements ITenantQueryContext, which automatically applies tenant filters to all EF Core queries:

csharp
// All repositories automatically filter by tenant
var jobs = await jobStore.GetAllAsync(parameters); // Only returns current tenant's jobs
var stats = await jobStore.GetDashboardStatsAsync(); // Stats scoped to tenant

API Endpoints

All API endpoints respect tenant context via the ITenantOptionalAware interface on MediatR commands/queries:

csharp
// Commands automatically get tenant ID from the pipeline behavior
public sealed record EnqueueJobCommand(...) : IRequest<Result<JobSummaryDto>>,
    ITenantOptionalAware
{
    public Guid TenantId { get; set; } // Injected by TenantBehavior
}

Cross-Tenant Operations

Some operations (like DLQ purge) can bypass tenant filtering when needed:

csharp
// PurgeDeadLetterQueueCommand implements IIgnoreTenantFilter
// This allows system-wide cleanup regardless of tenant
public sealed record PurgeDeadLetterQueueCommand(int? RetentionDays)
    : IRequest<Result>, IIgnoreTenantFilter;

Recurring Jobs

Recurring jobs can be tenant-scoped:

csharp
var schedule = JobSchedule.Create(
    "tenant-report",
    "MyApp.TenantReportJob",
    "0 8 * * 1", // Every Monday at 8:00 AM
    timeZoneId: null,
    queue: "default",
    tenantId: currentTenantId  // Scoped to this tenant
);

Released under the MIT License.