Hosted onhoraciohdev.dev.hyper.mediavia theHypermedia Protocol

How Erlang Beam fixes this

BEAM avoids this class of problem mostly by not letting shared mutable state be touched directly by many concurrent execution contexts.

The core idea is:

many processes ↓ send messages one owner process owns the resource/state ↓ serializes operations no lock stampede

In BEAM terms

Erlang processes are lightweight and isolated:

    each process has its own heap

    no shared mutable memory by default

    processes communicate by message passing

    if a resource needs serialization, you usually model it as one process owning that resource

So for a database writer, BEAM-style architecture would be:

sqlite_writer process owns SQLite connection other processes send {write, Op} messages writer executes writes one by one writer replies when done

That means callers do not all race into BEGIN IMMEDIATE. They queue in the mailbox of the writer process.

Compared to current Go/SQLite situation

Current shape:

goroutine A -> BEGIN IMMEDIATE goroutine B -> BEGIN IMMEDIATE goroutine C -> BEGIN IMMEDIATE goroutine D -> BEGIN IMMEDIATE SQLite busy handler arbitrates

BEAM-style shape:

process A -> send message process B -> send message process C -> send message process D -> send message sqlite_writer: handle A handle B handle C handle D

The second shape gives you explicit control over:

    fairness

    batching

    priority

    backpressure

    dropping/coalescing low-value writes

    avoiding 10s lock timeout storms

What BEAM gets right here

BEAM schedulers multiplex many processes fairly, but the important part is architectural:

shared resources become processes, not mutexes.

A GenServer is the classic pattern:

GenServer.call(SQLiteWriter, {:write, query}) GenServer.cast(SQLiteWriter, {:best_effort_peer_update, peer})

Then SQLiteWriter can decide:

    synchronous important writes: execute and reply

    best-effort writes: batch

    duplicate peer updates: coalesce

    huge blob indexing: process in chunks and yield between chunks

    low-priority domain updates: delay/drop under pressure

Equivalent fix in this Go codebase

A BEAM-inspired design would be a single write-owner goroutine:

WriteQueue goroutine owns writer admission all SQLite writes submit jobs queue executes one write tx at a time

For this repo specifically:

    peerWriter is already a small BEAM-like actor for peer writes.

    Problem: not all peer writes use it; connect() still bypasses it.

    Bigger problem: PutMany, domain updates, connect updates, etc. still compete directly at SQLite.

So the BEAM answer would be:

Don’t let every goroutine call BEGIN IMMEDIATE. Make SQLite writing an owned service/actor with a mailbox.

That is the global fix pattern.

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime