Skip to content

Archive

Concurrency

172 articles
Software Engineering 21 Sep 2026 5 min read

Version Columns Turn Lost Updates into Detectable Conflicts

Version Columns Turn Lost Updates into Detectable Conflicts A read-modify-write flow can overwrite another committed change even when every individual database statement succeeds. Two clients read the same row, compute different replacements, then write in sequence. Without a condition tying each write to the state it read, the later write can silently erase the earlier one. A version column makes that dependency explicit. The client reads both data and version, then updates only if the stored version is still the one it observed. A changed version turns the race into a failed conditional update instead of a lost update.

Software Engineering 21 Sep 2026 6 min read

Request Coalescing Collapses Cache-Miss Bursts

Request Coalescing Collapses Cache-Miss Bursts A cache miss is usually cheap when one caller triggers one backend read. The same miss can become expensive when hundreds of callers arrive for the same key before the first fill completes. Each caller sees an empty cache and starts equivalent work, multiplying load precisely when the cached value is unavailable. Request coalescing places a small concurrency boundary around that fill. The first caller starts the backend operation. Later callers for the same key join the in-flight operation instead of starting another one. When it completes, the result can populate the cache and be delivered to the waiting callers.

Linux 21 Sep 2026 6 min read

io_uring Multishot Accept Keeps One Accept Request Active

A normal accept request has a one-to-one shape: one submitted operation eventually yields one completion. io_uring multishot accept changes that relationship. A single accept SQE can remain active across multiple incoming connections and emit a separate completion queue entry for each accepted socket. The request is persistent, but not permanent. Each CQE carries enough state for the application to tell whether the original request can produce another completion. That boundary matters because a server that treats every successful CQE as proof that accept is still armed can silently stop accepting after the multishot request terminates.

Linux 21 Sep 2026 4 min read

futex_waitv Blocks on Multiple Futex Words in One Wait

A traditional futex wait names one futex word. That maps cleanly to a mutex or condition whose blocking state is represented by one shared 32-bit value. Some synchronization designs instead need a thread to sleep until any member of several independent states changes. futex_waitv() provides that vector wait. The caller supplies an array of wait descriptors, each containing a futex address and an expected value. The kernel checks the vector and blocks only while every entry still matches its expected state.

Software Engineering 21 Sep 2026 6 min read

Fencing Tokens Stop Stale Lease Holders from Writing

Fencing Tokens Stop Stale Lease Holders from Writing A distributed lease can decide which client currently owns a resource, but lease expiry does not instantly stop the previous holder. A process can pause, lose network access, or stall long enough for its lease to expire. Another client then acquires the lease. If the old process resumes and still has access to the protected storage or service, both clients can issue writes.

Software Engineering 21 Sep 2026 7 min read

Bulkheads Isolate Concurrency Across Failure Domains

Bulkheads Isolate Concurrency Across Failure Domains A service can remain reachable while its useful capacity disappears. A slow dependency holds requests open, those requests occupy workers or connection slots, and unrelated traffic waits behind work that cannot finish promptly. The fault began in one path, but a shared resource pool lets it consume capacity needed by every path. Bulkhead isolation partitions that finite capacity. Calls associated with one failure domain receive a bounded share rather than competing without separation for the entire pool. When one partition fills, admission fails or waits within that partition while capacity assigned to other work remains available.

Software Engineering 20 Sep 2026 4 min read

Request Coalescing Collapses Concurrent Cache Misses

Request Coalescing Collapses Concurrent Cache Misses A cache miss can become expensive when many requests ask for the same key at nearly the same time. Without coordination, each caller can start an identical database query, remote call, or computation. The cache eventually fills, but the backend absorbs a burst precisely when the cached value is absent. Request coalescing changes the concurrency boundary. The first caller starts the load and publishes an in-flight entry for that key. Later callers join that entry instead of starting equivalent work. When the load finishes, its result is distributed to the waiting callers and the in-flight entry is removed.

Software Engineering 20 Sep 2026 6 min read

Lease Renewal Needs a Safety Margin Before Expiry

Lease Renewal Needs a Safety Margin Before Expiry A lease grants a holder temporary authority until a recorded expiry. Keeping that authority requires renewal before the deadline. Scheduling renewal at the deadline itself leaves no room for network delay, scheduler pauses, storage latency, or a transient retry. A safer design attempts renewal earlier. The interval between the planned renewal and expiry is a safety margin: time reserved for ordinary uncertainty before the lease is treated as lost.

Software Engineering 20 Sep 2026 7 min read

Fencing Tokens Block Stale Lock Holders at the Resource

Fencing Tokens Block Stale Lock Holders at the Resource A distributed lease can expire while its holder is still running. A process may pause for garbage collection, lose contact with the coordinator, stall under scheduler pressure, or resume after a machine suspension. The lock service can correctly grant the lease to another worker while the old worker still has unfinished work. That gap matters when both workers can reach the protected resource. A lease controls ownership in the coordinator; it does not automatically revoke a delayed database connection, storage request, or RPC that was prepared by the previous holder.

Software Engineering 20 Sep 2026 7 min read

Deadline Propagation Stops Work After Callers Give Up

Deadline Propagation Stops Work After Callers Give Up A timeout at the edge does not automatically stop work deeper in a system. A client may abandon a request after two seconds while an API server continues waiting on another service, which may still be running a database query. The response has lost its consumer, yet CPU time, connections, memory, queue positions, and downstream capacity can remain occupied. Deadline propagation carries the caller’s time budget across those boundaries. Each component receives an absolute deadline or an equivalent remaining budget, refuses work that cannot start in time, and cancels operations when the budget expires. The goal is not merely faster failure. It is to keep useless work from surviving longer than the request that justified it.

Software Engineering 20 Sep 2026 7 min read

Bulkheads Isolate Resource Pools Before Failures Spread

A service can remain healthy at the process level while becoming useless because one workload has consumed every scarce execution resource. A slow dependency can occupy all outbound connections. A noisy tenant can fill every worker slot. A background job can take the same semaphore permits needed by interactive requests. Bulkhead isolation limits that coupling. Instead of letting unrelated work compete for one undifferentiated pool, the system partitions selected resources and gives each class of work a bounded share. Saturation then has a smaller blast radius.

Software Engineering 20 Sep 2026 5 min read

Bulkheads Isolate Concurrency Across Dependencies

Bulkheads Isolate Concurrency Across Dependencies A service can have plenty of CPU and still become unavailable because one dependency stops completing work. Requests waiting on a slow database, remote API, or storage service retain execution slots, connections, memory, and queue positions. If unrelated operations share the same finite pool, one saturated path can consume capacity needed by healthy paths. Bulkhead isolation divides that shared concurrency into explicit budgets. Calls to one dependency or workload class use a bounded pool that other classes cannot exhaust. The pattern does not repair a failing dependency. It limits the amount of local capacity that failure can occupy.

Software Engineering 20 Sep 2026 7 min read

Bounded Queues Turn Overload into Explicit Rejection

Bounded Queues Turn Overload into Explicit Rejection A queue can absorb short bursts when requests arrive faster than workers can finish them. That buffer is useful only while it remains a buffer. If producers can keep adding work without a fixed limit, sustained overload turns the queue into an expanding inventory of requests that may wait long after their results are useful. A bounded queue changes the failure mode. It accepts waiting work up to a deliberate capacity, then refuses additional admission until space becomes available. The service still experiences overload, but the overload appears as an explicit control decision rather than unbounded growth in memory and waiting time.

Software Engineering 20 Sep 2026 6 min read

Backpressure Keeps Producer Speed Tied to Consumer Capacity

A fast producer and a slower consumer can coexist safely only while the gap between their rates remains bounded. If incoming work arrives faster than it can be completed for long enough, buffering does not remove overload. It stores the difference. Backpressure makes that capacity mismatch part of the protocol between components. Instead of accepting work indefinitely, a saturated stage causes upstream code to slow down, wait for capacity, reduce demand, or reject work according to an explicit policy.

Software Engineering 19 Sep 2026 6 min read

Write Skew Breaks Cross-Row Invariants Under Snapshot Isolation

Snapshot isolation can let two transactions commit even when their combined result violates a rule that each transaction checked before writing. The anomaly appears when both transactions read the same logical condition, then write different rows. Because their write sets do not overlap, ordinary write-write conflict detection has nothing to reject. This is write skew. It matters at the boundary between application invariants and database isolation: a transaction may see a consistent snapshot and still participate in a final state that would have failed its own predicate.

Software Engineering 19 Sep 2026 5 min read

Version Columns Turn Database Updates into Conditional State Transitions

A database client can read a row, spend time computing a change, then issue an UPDATE after another transaction has already changed the same row. If the final statement identifies the row only by its primary key, the later write can replace state derived from the intervening transaction without any visible conflict. A version column changes that boundary. The client reads both the application state and a revision value, then includes that revision in the update predicate. The database accepts the write only while the stored revision still matches the state the client observed.

Software Engineering 19 Sep 2026 7 min read

Sequence Counters Detect Concurrent Writes Without Reader Locks

A sequence counter can let readers copy shared state without taking the writer’s lock. The reader samples a counter, copies the protected fields, then samples the counter again. A stable even value at both observations indicates that no writer overlapped the copy under the synchronization contract. A changed or odd value forces the reader to discard the snapshot and retry. This pattern moves work away from reader-side lock ownership, but it does not remove synchronization. Writers still need serialization, counter transitions need defined memory-ordering semantics, and the protected data must remain safe to access during an overlapping write. Those constraints make sequence counters suitable for some read-mostly snapshots and unsafe for data whose lifetime can disappear beneath a reader.

Software Engineering 19 Sep 2026 6 min read

Request Coalescing Collapses Concurrent Cache Misses into One Fill

A cache can reduce steady-state backend traffic yet amplify work at the instant a popular entry expires. If one hundred requests observe the same missing key before any replacement value is stored, a conventional lookup path can send one hundred equivalent reads to the origin. The cache is functioning according to its lookup rules; the amplification comes from concurrency around the empty interval. Request coalescing changes that interval. The first caller for a key starts the fill, while later callers for the same key attach to that in-flight operation instead of starting equivalent work. When the operation completes, its result is distributed to the waiting callers and, when appropriate, stored in the cache.

Software Engineering 19 Sep 2026 8 min read

Open File Description Locks Bind Byte Ranges to File Instances

A byte-range lock can protect the same inode yet have radically different lifetime semantics depending on what owns the lock. Traditional fcntl() record locks are process-associated. Open file description locks instead attach to the kernel open file description referenced by a descriptor. That shift changes which close operation releases a lock, what survives fork(), and whether two threads in one process can contend on the same file region. Linux exposes this model through F_OFD_SETLK, F_OFD_SETLKW, and F_OFD_GETLK. The range model remains familiar: struct flock specifies a read lock, write lock, or unlock together with an offset and length. The significant difference is ownership.

Software Engineering 19 Sep 2026 6 min read

Linux membarrier Moves Memory Ordering Cost to a Coordinating Thread

A concurrent runtime can have thousands of fast-path operations for every rare state transition that requires global coordination. Placing a full memory barrier on every fast path makes each operation pay for that rare transition. Linux membarrier() supports the opposite arrangement: a coordinating thread enters the kernel and forces a defined ordering point across a target set of threads, moving more cost to the infrequent side of the protocol. This is not a generic replacement for atomics, mutexes, or language memory models. It is a Linux kernel interface whose guarantees apply to memory accesses and targeted threads under specific commands. Correct use requires a protocol that already defines which accesses occur before and after the coordination point.

Linux 19 Sep 2026 5 min read

io_uring Multishot Requests Persist Across Completion Events

A normal io_uring request has a simple lifetime: userspace submits one SQE and eventually receives one CQE. Multishot operations change that relationship. One submitted request can remain active in the kernel and produce several completion queue entries as matching events occur. That persistence changes completion handling from a one-CQE-per-request assumption into an explicit lifecycle protocol. The decisive state is carried by IORING_CQE_F_MORE: when the flag is present, the originating request can produce another completion; when it is absent, that multishot request has terminated.

Software Engineering 19 Sep 2026 7 min read

How Language Runtimes Use CPU Cores: Threads, Goroutines, Workers, and Processes

A CPU with many cores does not make application code parallel by itself. The operating system can schedule multiple threads at the same time, but the language and runtime decide how application work reaches those threads. That distinction explains why Go, Rust, C++, Java, JavaScript, and PHP can all use a multicore machine even though their programming models look very different. The useful question is not simply whether a language is “multithreaded.” It is how a unit of application work becomes something the operating system can schedule.

Software Engineering 19 Sep 2026 6 min read

Generation Counters Keep Reused Handles Bound to the Right Resource

A compact handle often looks like an integer because an integer is cheap to store, copy, compare, and pass across an API boundary. In a table-backed resource manager, that integer may simply be an index into a slot array. The representation works until a slot is released and later reused. An old handle can then point at a new resource that happens to occupy the same index. The failure is not an out-of-bounds access. The index can be perfectly valid. The problem is identity: the handle names a storage location, while the caller treats it as the identity of the resource that once occupied that location.

Software Engineering 19 Sep 2026 6 min read

Fencing Tokens Reject Stale Writers After Lease Expiry

A distributed lease can transfer ownership without stopping the process that previously held it. A worker may pause long enough for its lease to expire, then resume after another worker has acquired the same lease. At that point both processes can execute code that was written under the assumption of exclusive ownership. Lease expiry settles ownership in the coordination service. It does not revoke CPU time, cancel an in-flight network request, or erase buffered I/O on the former holder. Fencing tokens address that gap by carrying an ordering value from the ownership decision to the resource being protected.