Camada Zero · 23 · WAL & Durabilidade

Como um banco promete que o seu commit sobrevive a uma queda de energia. O truque é simples e antigo: escreve a mudança num log sequencial e dá fsync ANTES de tocar nas páginas de dado. Roda aqui, derruba no meio, e veja o replay reconstruir tudo.

Memória (RAM · volátil)

WAL buffer (não fsyncado)
Páginas sujas (dirty)

Disco (persistente)

WAL no disco (fsyncado · durável)
Páginas de dado (no disco)
0Entradas no WAL
0Commitadas (fsync)
0Páginas no disco
0Recuperadas (replay)
Clica escrever pra ver o caminho de um write: anexa no WAL, dá fsync (commit durável), e só então suja a página em memória. Depois clica crash antes do checkpoint e recuperar pra ver o replay.
no WAL, não durável fsyncado (durável) página suja (memória) página no disco
WAL = Write-Ahead Log. A regra é uma só: a mudança vai pro log antes de ir pra página de dado, e o commit só é confirmado depois do fsync. O log é append sequencial (barato); as páginas podem ser escritas depois, em lote. Se cair no meio, o restart faz replay do log e reconstrói o que foi commitado. É o D (durabilidade) do ACID.

O caminho de um write em Go

func (db *DB) Put(key, val string) error {
    // 1. Anexa no WAL (append sequencial)
    if _, err := db.wal.Write(encode(key, val)); err != nil {
        return err
    }
    // 2. fsync: só AGORA o commit é durável
    if err := db.wal.Sync(); err != nil { // os.File.Sync()
        return err
    }
    // 3. Atualiza a página em memória (flush pro disco vem depois)
    db.mem[key] = val
    return nil
}

No restart, antes de servir

func (db *DB) Recover() {
    for _, rec := range db.wal.ReadAll() {
        db.apply(rec) // replay: reconstrói o estado commitado
    }
}

🧠 Desafio — WAL & Durabilidade

Mexe no visualizador antes de responder: escreve, derruba, recupera, e tenta no modo "sem WAL". As duas últimas são de reflexão: escreve a sua e só então revela o modelo.