Skip to content

Archive

Concurrency

172 articles
Go 07 Sep 2026 11 min read

Suppress Duplicate Concurrent Work in Go with singleflight

A service can become overloaded even when each individual request is reasonable. Imagine a popular product whose cached record expires. Fifty requests arrive almost together, all observe the same cache miss, and all start the same database query. The problem is not ordinary parallelism. Those requests are doing duplicate work for the same result at the same time. golang.org/x/sync/singleflight provides a small mechanism for suppressing that duplication inside one Go process. For a given key, one caller performs the work while concurrent callers for the same key wait and receive the same result. Calls using different keys can still perform their own work.

Go 07 Sep 2026 8 min read

Stream Data Between Go Components with io.Pipe

Many Go APIs meet at io.Reader and io.Writer. That makes components easy to compose until both sides want to drive the operation. A compressor may want an io.Writer where it can emit bytes incrementally, while an uploader wants an io.Reader from which it can pull those bytes. One tempting solution is to write everything into a bytes.Buffer first and upload it afterward. That works, but it turns a streaming pipeline into a whole-payload allocation.

Go 07 Sep 2026 11 min read

Read File Regions Safely in Go with io.ReaderAt

Many file formats are not consumed strictly from beginning to end. An index may point to records at known offsets. A binary container may keep metadata in a header and payloads elsewhere. A server may need to serve several independent byte ranges from the same open file. The obvious approach is to call Seek, then Read. That works when one goroutine owns the file position. It becomes fragile when several operations share the same file because the current offset is mutable state.

Rust 07 Sep 2026 10 min read

Initialize Shared Rust State Once with OnceLock

Applications often need one shared value that is expensive or awkward to construct but should not change after initialization. Examples include parsed configuration, a lookup table, a compiled matcher, or metadata discovered during startup. A plain static works only when the value can be created as a constant. A Mutex<Option<T>> can represent “not initialized yet,” but it also introduces a lock and a mutable state model that the program may not need after setup.

Python 07 Sep 2026 8 min read

Build Reliable Worker Queues in Python with queue.Queue

A worker thread is easy to start. A reliable worker queue is harder. The difficult parts appear when production code must answer questions such as: What happens when producers are faster than consumers? How does the main thread know that processing, rather than merely dequeuing, is complete? How do workers stop without abandoning queued work? What happens if processing raises an exception? Python’s queue.Queue provides the synchronization needed to pass work safely between threads, but correct coordination still depends on a few application-level invariants. The most important are to bound work when memory matters, pair every successful get() with exactly one task_done(), and separate “all work is finished” from “workers should exit.”

Python 07 Sep 2026 11 min read

Bound In-Flight Thread Pool Work in Python

A thread pool limits how many functions run at the same time, but it does not automatically limit how much work your producer can queue. That distinction matters when the input is large or unbounded. A loop can submit millions of tasks to a ThreadPoolExecutor while only a handful of worker threads execute them. The remaining tasks are pending Future objects, along with their arguments and other referenced state. If the producer is much faster than the workers, memory use can grow long before CPU or network capacity is exhausted.

Linux 06 Sep 2026 13 min read

Choose the Right Advisory File Lock on Linux

Two processes can open the same file and both write to it successfully. That is often exactly what Unix applications need, but sometimes the programs are supposed to coordinate: only one worker should update a state file, several readers may share a resource, or a process must avoid changing a byte range while another process is using it. Linux offers several advisory file-locking mechanisms. The confusing part is not how to request a lock. The confusing part is what owns the lock and when that lock disappears.

Linux 05 Sep 2026 11 min read

Wake Linux Event Loops from Other Threads with eventfd

A file-descriptor event loop can wait efficiently for sockets, pipes, timers, and other kernel objects. A common problem appears when work originates somewhere that is not already represented by a file descriptor: another thread changes shared state and needs the loop to wake immediately. Polling shared state on a timer adds latency or wastes wakeups. A condition variable can wake a thread, but it cannot be placed directly in the same poll() or epoll wait set as a socket. A pipe can bridge the two models, but using a pipe only as a wakeup signal means maintaining a read end, a write end, and byte-buffer semantics that the application may not actually need.

Linux 04 Sep 2026 13 min read

Prevent Overlapping Linux Jobs with Advisory File Locks and flock

A scheduled job often looks harmless until two copies run at the same time. A backup takes longer than usual, a second timer fires, and both processes start writing the same output. A maintenance script overlaps with itself and launches duplicate work. A cache refresh runs concurrently and leaves partially coordinated state behind. The problem is not that Linux started the processes incorrectly. The problem is that the application needs a rule saying, “only one cooperating process may enter this critical section at a time.”

Database 04 Sep 2026 11 min read

Prevent Lost Updates with Optimistic Locking in SQL

Two users can read the same database row, make different changes, and both believe their update succeeded. If the second write silently replaces the first, the application has a lost update. This is easy to miss because each individual SQL statement can be valid. The bug appears only when multiple requests overlap in time. One practical way to prevent this is optimistic locking: let readers proceed without holding a database lock, but make every write prove that the row is still the version the writer originally read.

Python 04 Sep 2026 8 min read

Order Dependency-Driven Work in Python with graphlib.TopologicalSorter

Many automation tasks are not really lists. They are dependency graphs. A deployment may need a database migration before the API starts, while static assets can build independently. A data pipeline may need two source extracts before a join can run. A build system may have several targets that become runnable as soon as their prerequisites finish. If you encode this work as one hand-written sequence, you hide the real constraint: which tasks depend on which other tasks. That makes the sequence harder to change and can prevent independent work from running concurrently.

Go 04 Sep 2026 10 min read

Coordinate Shared State with sync.Cond in Go

A goroutine sometimes cannot make progress until shared state changes. A worker may need to wait until a queue contains an item. A producer may need to wait until that queue has free capacity. Several goroutines may need to sleep until a service becomes ready. Polling the state in a loop wastes CPU or forces you to invent arbitrary sleep intervals. Channels solve many coordination problems more directly, but they are not always a natural fit when several goroutines already share state protected by a mutex and need to wait for predicates over that state.

Python 04 Sep 2026 10 min read

Build Portable Readiness Loops in Python with selectors

A network service can handle one connection with straightforward blocking calls: accept a client, read a request, write a response, and repeat. The model becomes awkward when one thread must manage many connections at once. The problem is not that sockets are slow. The problem is that a blocking operation can stop the thread while one connection waits, even though other connections are ready for useful work. Python’s selectors module provides a higher-level way to wait for I/O readiness across multiple file objects. Instead of asking one socket to block until something happens, you register many sockets and ask the selector which ones are currently ready.

Python 04 Sep 2026 9 min read

Avoid Subprocess Pipe Deadlocks in Python

Launching a command from Python is easy. Capturing its output is also easy. The trouble starts when a parent process waits for a child while the child is waiting for the parent to read from a pipe. That circular wait is a deadlock: neither process can make progress even though neither has crashed. This problem is especially confusing because the same code may work during testing and hang only when a command produces more output. Small output fits in an operating-system pipe buffer. Larger output can fill that buffer and expose the incorrect coordination.

Linux 04 Sep 2026 8 min read

Avoid PID Reuse Races on Linux with pidfds

A process ID looks like an identity, but it is really a reusable number. That distinction matters in long-running supervisors, job managers, test harnesses, and other programs that observe a process and then act on it later. Between those two operations, the original process can exit and Linux can eventually reuse the same PID for an unrelated process. Traditional PID-based code can therefore have a time-of-check/time-of-use race: check PID 4242 -> original process exits -> PID 4242 is reused -> signal PID 4242 Linux PID file descriptors, usually called pidfds, provide another model. Instead of repeatedly identifying a task by a reusable integer, a program obtains a file descriptor that refers to a particular process and can use that descriptor with pidfd-aware APIs.

Go 03 Sep 2026 11 min read

Use sync.Pool for Temporary Object Reuse in Go

Repeatedly allocating short-lived helper objects can become expensive in a hot path. A formatter may create temporary buffers for every request, an encoder may allocate scratch space for every record, or a parser may repeatedly construct helper objects that are discarded immediately after use. Go’s sync.Pool can reuse some of those temporary objects across independent operations. That can reduce allocation work and garbage-collector pressure when the same kind of object is created frequently under load.

Database 02 Sep 2026 5 min read

Understanding Write Skew and Transaction Isolation

Transaction isolation is often explained with dirty reads and lost updates, but another anomaly is especially important for multi-row business rules: write skew. Write skew occurs when concurrent transactions read the same valid state, make decisions independently, and update different rows in a way that produces an invalid combined state. Because they do not overwrite the same row, ordinary write-conflict detection may not stop them. A simple invariant Imagine an on-call table where at least one doctor must remain available:

Python 02 Sep 2026 6 min read

Structured Concurrency in Python with asyncio.TaskGroup

Concurrent code becomes difficult to reason about when tasks can outlive the operation that created them. A request handler may return while background tasks are still running, or one task may fail while its siblings continue doing work that is no longer useful. Python’s asyncio.TaskGroup, available since Python 3.11, provides structured concurrency for related asynchronous tasks. Tasks created inside the group belong to a clear lifetime: leaving the async with block waits for them, and failures are handled as a group rather than as detached background events.

Database 02 Sep 2026 7 min read

SQLite WAL Mode: Concurrency, Checkpoints, and Operational Pitfalls

SQLite is often chosen because it keeps deployment simple: an application can get transactional storage without operating a separate database server. As workloads become more concurrent, however, the default rollback journal can make read and write activity interfere more than expected. Write-ahead logging (WAL) changes that coordination model. Readers can usually continue while a writer commits changes, but WAL does not turn SQLite into a multi-writer database. Correct operation still depends on short transactions, sensible busy handling, and checkpoints that can make progress.

Python 02 Sep 2026 4 min read

Request-Scoped State in Python with contextvars

Applications often need small pieces of context to follow a request through several layers: a request ID, tenant identifier, locale, or tracing field. Passing every value through every function is explicit, but can become noisy when the value is cross-cutting rather than part of the function’s business input. Python’s contextvars module provides context-local state designed to work with asynchronous code. Why a normal global is unsafe A module-level variable is shared by all concurrent requests:

Rust 02 Sep 2026 6 min read

Recovering Safely from Poisoned Mutexes in Rust

A mutex protects shared data from concurrent access, but mutual exclusion alone does not guarantee that the data remains valid. A thread can panic halfway through a multi-step update and release the lock during unwinding, leaving the protected value in a state that other threads should not blindly trust. Rust’s standard Mutex records this situation through poisoning. A poisoned mutex is still lockable, but acquiring it returns an error that forces the caller to decide whether continuing is appropriate.

Software Engineering 02 Sep 2026 9 min read

Preventing Lost Updates with HTTP ETags and Conditional Requests

Two clients can read the same resource, make different edits, and then save them seconds apart. Without a concurrency check, the later write can silently replace the earlier one. This is the lost update problem. HTTP already provides a protocol-level mechanism for avoiding that failure: validators such as entity tags (ETags) combined with conditional request headers. Used correctly, they let a client say, “apply this change only if the resource is still the version I read.”

Go 02 Sep 2026 4 min read

Lazy Initialization in Go with sync.OnceValue and sync.OnceValues

Lazy initialization is useful when a value is expensive to build and may never be needed. The difficulty is making that initialization safe when several goroutines request the value at the same time. Go has long provided sync.Once. Since Go 1.21, the sync package also includes OnceValue and OnceValues, helpers that return functions which compute results once and reuse them for later calls. The manual sync.Once pattern A classic implementation looks like this:

Database 01 Sep 2026 5 min read

Transaction Isolation and Safe Database Retries

Database transactions make groups of reads and writes atomic, but atomicity alone does not answer what concurrent transactions are allowed to observe. That is the job of isolation. The practical challenge appears when correct transactions conflict. Strong isolation can intentionally abort one transaction rather than allow an invalid interleaving. Applications need to distinguish those retryable concurrency failures from ordinary errors. Isolation protects invariants, not just statements Consider two concurrent requests that reserve the last available item.