Why a C# lock fails across 3 Kubernetes pods: PostgreSQL advisory locks and distributed locking

C# Async & Multithreading November 13, 2026 csharp

lock, SemaphoreSlim and ConcurrentDictionary protect only within a single process.

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ======================================================================
// Q: The lock works on my machine but not in production with 3 pods. Why?
//
// lock, SemaphoreSlim and ConcurrentDictionary protect only within a single
// process. With 3 pods there are 3 independent locks. The fix depends on the
// problem:
// - Data in the database: atomic UPDATE, unique constraint, optimistic
//   concurrency (Q60-Q61), FOR UPDATE SKIP LOCKED for job queues.
// - "Only one instance runs the job": PostgreSQL advisory lock, a distributed
//   lock in Redis / Blob lease, or a Kubernetes CronJob.
// - Duplicate messages: idempotency key / inbox (Q31).
// ======================================================================

// Only one pod runs the nightly price import. The lock lives as long as the connection is open.
await using var conn = await dataSource.OpenConnectionAsync(ct);
await using var tryLock = new NpgsqlCommand("SELECT pg_try_advisory_lock(42)", conn);
if (!(bool)(await tryLock.ExecuteScalarAsync(ct))!) return;      // another pod is already running it
try { await importer.RunAsync(ct); }
finally
{
    await using var unlock = new NpgsqlCommand("SELECT pg_advisory_unlock(42)", conn);
    await unlock.ExecuteNonQueryAsync(CancellationToken.None);
}