DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
wal.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file wal.h
6 * @brief Write-ahead journal for atomic buffer-to-flash storage (DETWS_ENABLE_WAL).
7 *
8 * A power-loss-safe write-ahead log, the substrate for on-device data stores (dbm / sqlite / nosql). It
9 * is built to the envelope measured on real hardware (docs/FEATURE_PERFORMANCE.md): an SD card over SPI
10 * writes ~1.5 MB/s sequentially but only ~40-100 durable random IOPS with 100+ ms tail spikes, and the
11 * durable-throughput knee is a ~32 KiB write with a flush every ~128-256 KiB. So the log **appends
12 * sequentially** in fixed pages and checkpoints in bulk, never scattering small durable writes.
13 *
14 * **On-media record framing (this file - increment 1, pure + host-tested):** each record is
15 *
16 * [magic u32][seq u64][len u32][crc u32][payload len bytes] (little-endian; ::WAL_RECORD_HEADER = 20)
17 *
18 * where `crc` is a CRC-32 (IEEE 802.3) over the 16 header bytes plus the payload. Recovery on mount walks
19 * records from the start and **stops at the first record with a bad magic, a bad CRC, or a truncated tail**
20 * - i.e. the torn write left by a power loss - so a crash costs at most the last, un-checkpointed record.
21 * This is the atomicity core, and it is pure (no I/O) so it is fully host-testable: feed it a journal
22 * image with a corrupted or truncated tail and it recovers to the last good record.
23 *
24 * The durable store layer - A/B superblock, checkpoint, and mount/recover over a block-device seam - is
25 * built on this codec in wal_store.h; wal_fs.h binds that seam to a real fs::FS file (SD / LittleFS). The
26 * whole path is hardware-verified on an SD card over SPI (checkpoint recovery, torn-tail drop, byte-level
27 * payload persistence, and survival across a chip reset all pass).
28 */
29
30#ifndef DETERMINISTICESPASYNCWEBSERVER_WAL_H
31#define DETERMINISTICESPASYNCWEBSERVER_WAL_H
32
33#include "ServerConfig.h"
34#include <stddef.h>
35#include <stdint.h>
36
37#if DETWS_ENABLE_WAL
38
39/** @brief Bytes of fixed record header before the payload (magic + seq + len + crc). */
40#define WAL_RECORD_HEADER 20
41
42/** @brief Record magic ("WAL1", little-endian) - the resync marker recovery looks for. */
43#define WAL_MAGIC 0x314C4157u
44
45/** @brief CRC-32 (IEEE 802.3, poly 0xEDB88320, init/final 0xFFFFFFFF) over @p data. */
46uint32_t wal_crc32(const uint8_t *data, size_t len);
47
48/**
49 * @name Streaming CRC-32
50 * The same CRC as ::wal_crc32, split so a record header and a large payload can be folded in without
51 * ever buffering both together - which is how the store CRCs an append (header then payload) and how
52 * recovery CRCs a record it reads back from media in small chunks.
53 * @{
54 */
55uint32_t wal_crc32_init(void); ///< seed (0xFFFFFFFF)
56uint32_t wal_crc32_update(uint32_t crc, const uint8_t *d, size_t n); ///< fold @p n bytes into @p crc
57uint32_t wal_crc32_final(uint32_t crc); ///< finalize (xor 0xFFFFFFFF)
58/** @} */
59
60/**
61 * @brief Encode one journal record into @p out.
62 * @param seq the record's monotonic sequence number.
63 * @param payload the record body (may be null when @p len is 0).
64 * @return total bytes written (::WAL_RECORD_HEADER + @p len), or 0 if it does not fit @p cap.
65 */
66size_t wal_record_encode(uint8_t *out, size_t cap, uint64_t seq, const uint8_t *payload, uint32_t len);
67
68/** @brief Per-record callback for ::wal_replay. */
69using WalRecordCb = void (*)(uint64_t seq, const uint8_t *payload, uint32_t len, void *ctx);
70
71/**
72 * @brief Replay a journal image, invoking @p cb for each valid record in order.
73 *
74 * Stops at the first record with a bad magic, a failed CRC, or a length that runs past @p len (a
75 * truncated tail from a power loss). @return the offset just past the last good record - the durable
76 * journal length; any bytes beyond it are the torn tail to discard/overwrite.
77 */
78size_t wal_replay(const uint8_t *img, size_t len, WalRecordCb cb, void *ctx);
79
80#endif // DETWS_ENABLE_WAL
81#endif // DETERMINISTICESPASYNCWEBSERVER_WAL_H
User-facing configuration for DeterministicESPAsyncWebServer.