rust 143 lines · 3 tabs

Crash-Safe Write-Ahead Log With CRC-Checked Replay in Rust

Shared by codesnips Aug 2026
3 tabs
use std::io::{self, Read, Write};

pub const HEADER_LEN: usize = 8; // 4-byte length + 4-byte crc

#[derive(Debug)]
pub enum DecodeError {
    UnexpectedEof,
    ChecksumMismatch,
    Io(io::Error),
}

impl From<io::Error> for DecodeError {
    fn from(e: io::Error) -> Self {
        DecodeError::Io(e)
    }
}

pub struct Record;

impl Record {
    pub fn encode<W: Write>(w: &mut W, payload: &[u8]) -> io::Result<usize> {
        let crc = crc32fast::hash(payload);
        w.write_all(&(payload.len() as u32).to_le_bytes())?;
        w.write_all(&crc.to_le_bytes())?;
        w.write_all(payload)?;
        Ok(HEADER_LEN + payload.len())
    }

    pub fn decode<R: Read>(r: &mut R) -> Result<Vec<u8>, DecodeError> {
        let mut header = [0u8; HEADER_LEN];
        match r.read_exact(&mut header) {
            Ok(()) => {}
            Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => {
                return Err(DecodeError::UnexpectedEof)
            }
            Err(e) => return Err(DecodeError::Io(e)),
        }
        let len = u32::from_le_bytes(header[0..4].try_into().unwrap()) as usize;
        let want_crc = u32::from_le_bytes(header[4..8].try_into().unwrap());
        let mut payload = vec![0u8; len];
        r.read_exact(&mut payload).map_err(|e| {
            if e.kind() == io::ErrorKind::UnexpectedEof {
                DecodeError::UnexpectedEof
            } else {
                DecodeError::Io(e)
            }
        })?;
        if crc32fast::hash(&payload) != want_crc {
            return Err(DecodeError::ChecksumMismatch);
        }
        Ok(payload)
    }
}
3 files · rust Explain with highlit

A write-ahead log (WAL) is the standard technique for making state changes durable and recoverable: every mutation is first appended to an on-disk log, flushed, and only then applied to in-memory or on-disk structures. If the process crashes, the log is replayed on startup to reconstruct the last committed state. The key properties are that appends are sequential (fast), each record is self-describing, and corruption from a partial write is detectable rather than silently loaded.

The record format tab defines the on-disk framing. Each record is a length prefix, a crc32 checksum, and the payload bytes. Record::encode computes the checksum over the payload with crc32fast and writes everything little-endian; Record::decode reads the frame back and returns DecodeError::ChecksumMismatch when the stored CRC does not match the recomputed one. Framing every entry with its own length and CRC is what makes recovery robust: a torn tail from a crash mid-write is caught by a short read or a bad checksum and treated as end-of-log rather than propagated as bad data.

The Wal writer tab wraps a file opened in append mode. append serializes the entry, encodes a Record, writes it, and calls sync_data so the bytes reach stable storage before the caller proceeds. That fsync is the whole point — without it the OS page cache can lose the write on power failure, defeating the log. The trade-off is latency, which is why real systems batch appends and amortize one fsync across many records; this implementation flushes per append for clarity.

The replay on startup tab shows recovery. Wal::open reads records sequentially through Record::decode and folds each decoded Entry into caller state via a closure. Crucially, when it hits an UnexpectedEof or ChecksumMismatch, it stops cleanly and truncates the file to the last known-good offset, discarding the partial trailing record. This tolerate-torn-tail behavior is what lets a WAL survive a crash during a write.

Developers reach for this pattern whenever an in-memory structure must survive restarts — key-value stores, queues, ledgers. Pitfalls include forgetting fsync, not versioning the record format, and assuming a clean read means a clean disk; the per-record CRC guards against the last of these.


Related snips

ruby
class PostSerializer < ActiveModel::Serializer
  attributes :id, :title, :excerpt, :body, :published_at, :views, :likes_count, :comments_count

  attribute :can_edit, if: :current_user_can_edit?

  belongs_to :author, serializer: UserSummarySerializer

Serializers with ActiveModel::Serializers

rails api serialization
by Alex Kumar 2 tabs
ruby
class PostsController < ApplicationController
  def index
    posts = Post.for_feed.page(params[:page]).per(25)

    render json: {
      data: posts.map { |post| PostSerializer.new(post).as_json },

N+1 Proof Serialization with preloaded associations

rails activerecord performance
by codesnips 3 tabs
sql
-- Logical backup with pg_dump
-- Single database
-- pg_dump -h localhost -U postgres -d mydb -F c -f mydb_backup.dump

-- All databases
-- pg_dumpall -h localhost -U postgres -f all_databases.sql

Database backup and recovery strategies

database backup recovery
by Maria Garcia 2 tabs
java
package com.example.demo.controller;

import com.example.demo.dto.FileMetadata;
import com.example.demo.service.FileStorageService;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;

File upload and download handling

java spring-boot file-upload
by David Kumar 2 tabs
yaml
# Headless Service for stable DNS
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: production

Kubernetes StatefulSets for stateful workloads

kubernetes k8s statefulsets
by Ryan Nakamura 1 tab
ruby
class NotifyFollowersJob
  include Sidekiq::Job

  sidekiq_options queue: :notifications, retry: 5

  def perform(post_id, actor_id)

Safer Background Job Arguments (Serialize IDs only)

rails reliability sidekiq
by codesnips 3 tabs

Share this code

Here's the card — post it anywhere.

Crash-Safe Write-Ahead Log With CRC-Checked Replay in Rust — share card
Link copied