Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# One shared build directory for all 45 crates in this repo, placed OUTSIDE
# the repo root — as a sibling of your clone, not inside it.
#
# Two reasons it lives outside rather than in ./target:
#
# 1. mdbook's source directory is the repo root (`src = "."` in book.toml),
# and mdbook copies every non-markdown file under src into the rendered
# book. It has no exclude mechanism and does not honour .gitignore, so a
# target/ dir anywhere under the root gets duplicated into book/ on every
# build. That is how book/ once reached 7.2 GB.
# 2. The 44 topic crates are deliberately independent packages, not workspace
# members, so each one would otherwise build its own copy of rand,
# criterion and friends. Sharing one directory means sharing those builds:
# it is the difference between 6.6 GB and about 1.5 GB, and it makes
# ./verify.sh's cold run several times faster.
#
# Nothing here changes how any individual crate builds — `cargo run`,
# `cargo test` and `cargo bench` all work exactly as the guides describe from
# inside any topics/*/experiments directory. If you would rather keep artifacts
# inside your clone, delete this file; just run `cargo clean` before
# `mdbook build` if you do.
[build]
target-dir = "../.dlp-target"
53 changes: 50 additions & 3 deletions .github/workflows/book.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
name: book

# The book is built on pull requests as well as on master, because the two ways
# it breaks — a mermaid diagram that no longer parses, and a SUMMARY.md entry
# pointing at a moved file — are both invisible in markdown and obvious in the
# rendered output. Catching those after the merge means catching them after they
# have already deployed to Pages.
#
# Only a push to master deploys. Pull requests build and stop.

on:
push:
branches: [master]
pull_request:
workflow_dispatch:

# Least privilege by default; the deploy job elevates for itself.
permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
group: book-${{ github.ref }}
cancel-in-progress: true

jobs:
Expand Down Expand Up @@ -42,13 +50,52 @@ jobs:
- name: Bundle PDF into site
run: cp book/pdf/output.pdf book/html/database-learning-path.pdf

# Every chapter in SUMMARY.md must have produced a page, and every mermaid
# block must have reached the renderer as a mermaid block rather than as a
# plain code fence. Both are silent failures in markdown and obvious in the
# rendered output, which is the whole reason to check here.
- name: Check the rendered book
run: |
fail=0
checked=0
while read -r page; do
# mdbook renders README.md as the directory's index.html
case "$page" in
*/README.md) html="book/html/${page%README.md}index.html" ;;
README.md) html="book/html/index.html" ;;
*) html="book/html/${page%.md}.html" ;;
esac
checked=$((checked + 1))
if [ ! -f "$html" ]; then
echo "::error::SUMMARY.md lists $page but $html was not rendered"
fail=1
fi
done < <(grep -oE '\]\(([^)]+\.md)\)' SUMMARY.md | sed 's/](//; s/)//' | sort -u)
echo "checked $checked chapters from SUMMARY.md"

# print.html concatenates every page, so it is excluded or every
# diagram would be counted twice.
src=$(grep -rho '^```mermaid' --include='*.md' . --exclude-dir=book --exclude-dir=drafts | wc -l | tr -d ' ')
out=$(grep -rho 'class="mermaid"' book/html --include='*.html' --exclude='print.html' | wc -l | tr -d ' ')
echo "mermaid blocks: $src in source, $out rendered"
if [ "$out" -lt "$src" ]; then
echo "::error::$((src - out)) mermaid block(s) did not render as mermaid"
fail=1
fi
exit $fail

- uses: actions/upload-pages-artifact@v3
if: github.event_name == 'push'
with:
path: book/html

deploy:
needs: build
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
Expand Down
92 changes: 92 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: verify

# The repo's central claim is that every performance figure in the guides comes
# from code in the repo that you can run. This workflow is what keeps that
# claim honest: it runs the same ./verify.sh a reader would, so a lane that
# stops building or stops running fails the build instead of quietly rotting.
#
# Note on `cargo test`: it is NOT the gate here. Each topic ships two exercise
# lanes as `todo!()` stubs whose tests are the specification, so a red test
# suite is the intended state of a fresh clone. The bin lanes in verify.sh are
# the part that must always run.

on:
push:
branches: [master]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: verify-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: ubuntu-latest
# a cold full run is ~20 min of compiling plus ~15 min of benchmarks
timeout-minutes: 75
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/.ci-target
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

# Each job sets CARGO_TARGET_DIR to an absolute in-workspace path, which
# takes precedence over .cargo/config.toml's ../.dlp-target. Necessary
# because actions/cache rejects any path containing ".." — it logs
# "Invalid pattern" and then silently caches nothing, so every run
# recompiles all 45 crates from scratch. The reason the checked-in config
# points outside the clone is mdbook, which never runs in this workflow.
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
${{ github.workspace }}/.ci-target
key: verify-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: verify-${{ runner.os }}-

- name: Every measured lane must build and run
run: ./verify.sh --summary

warnings:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/.ci-target
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
${{ github.workspace }}/.ci-target
key: warn-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: warn-${{ runner.os }}-

# rustc warnings only — deliberately NOT clippy. A reader's first
# experience of a topic crate should not be a wall of noise on top of the
# intentional todo!()s, and stub bodies that legitimately ignore their
# arguments carry a local #[allow] with a reason. Style lints across 44
# independent teaching crates are a different argument, not this gate.
- name: No rustc warnings in the experiment crates
env:
RUSTFLAGS: -D warnings
run: |
fail=0
for d in topics/*/experiments capstone; do
echo "::group::$d"
(cd "$d" && cargo build --all-targets --quiet) || fail=1
echo "::endgroup::"
done
exit $fail
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
target/
Cargo.lock
.DS_Store
/book/
/mermaid.min.js
Expand Down
14 changes: 9 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ A self-paced database-internals learning path, rendered as an mdBook (`book.toml
## Content rules

- **Never assert a number you did not verify.** Download and read the actual paper; cite the section or table it came from. If a figure cannot be checked against the source, it does not go in.
- **Every performance claim must come from code in this repo.** Write the experiment, run it, and record the measurement. `./verify.sh` runs every measured benchmark lane — a new topic's lane belongs in its `BENCHES` list.
- **Report the negative result.** If a published technique does not reproduce on the local generator, that is the finding: say so, explain which premise is absent, and add an exercise that constructs the case where it holds. (Topic 42's multi-hit booster is the worked example.)
- **Generators are seeded**, so every figure reproduces exactly apart from timings.
- **Every performance claim must come from code in this repo.** Write the experiment, run it, and record the measurement. `./verify.sh` runs every measured lane — a new topic's lane belongs in its `BENCHES` list, and the headline belongs in `FINDINGS.md`. Two topics (4, 10) deliberately have no lane because their benches measure only the reader's code; say so in the README rather than inventing a number.
- **A benchmark that prints an implausible number is a bug in the benchmark.** Topic 12 once reported 19,047,619 GB/s from a hoisted timing loop. `black_box` the inputs, and sanity-check any figure against the hardware's actual limits before recording it.
- **Report the negative result.** If a published technique does not reproduce on the local generator, that is the finding: say so, explain which premise is absent, and add an exercise that constructs the case where it holds. (Topic 42's multi-hit booster is the worked example; topic 3's non-step-function height ladder is another.)
- **Generators are seeded**, so every figure reproduces exactly apart from timings. Lockfiles are committed for the same reason.
- **Exercise lanes must degrade, not crash.** A bench binary on a fresh clone prints its provided lanes and a `[stub — ...]` note for the rest, and exits 0. Never let a `todo!()` panic hide a measurement above it.

## Topic package shape

Each `topics/NN-name/` contains: `README.md` (study guide, opening with *the problem, measured* — the provided benchmark lane's real output), four to seven `reading-*.md` guides in the concept-first format (framing lead → "the problem in one sentence" → numbered `### Step N` sections → how to read the source → questions → done-when → references), `notes.md` (predictions vs measurements, paper numbers, cross-topic threads, open questions), and `experiments/` — a Rust crate with **lane 1 implemented and two lanes stubbed**, where the stub tests are the specification and the reference numbers live in `notes.md`.
Each `topics/NN-name/` contains: `README.md` (study guide, opening with *the problem, measured* — the provided benchmark lane's real output), four to seven `reading-*.md` guides in the concept-first format (framing lead → "the problem in one sentence" → numbered `### Step N` sections → how to read the source → questions → `## Done when` checklist → references), `notes.md` (a `## Baseline (provided lane, <machine>, measured <date>)` section recording the real output, *then* the reader's prediction worksheet — leave those cells empty, they are the exercise), and `experiments/` — a Rust crate with **lane 1 implemented and two lanes stubbed**, where the stub tests are the specification and the reference numbers live in `notes.md`.

When adding a topic: PLAN.md section, the package above, a capstone `M`NN milestone row in PROGRESS.md, SUMMARY.md entries whose link titles match the on-disk H1s exactly, a SESSION-LOG.md entry carrying every measured number, and one commit.
When adding a topic: PLAN.md section, the package above, a `FINDINGS.md` row, a `verify.sh` lane, a capstone `M`NN milestone row in PROGRESS.md, SUMMARY.md entries whose link titles match the on-disk H1s exactly, a SESSION-LOG.md entry (with a `## date — topic NN — title` heading) carrying every measured number, and one commit.

Reference clones live in `~/repos`; the commit each was read at is recorded once in the pin table at the end of `resources/codebases.md` — regenerate with `python3 tools/pin-table.py`, never hand-edit it.
37 changes: 32 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,22 @@ Every topic follows the same shape, because the shape is what makes it checkable
then `### Step N` sections that build each concept using only terms defined in
earlier steps, then how to read the source material with the concepts in hand,
questions to answer, a "done when" checklist, and references.
- **`notes.md`** — a predictions-vs-measurements table (write the prediction *before*
running the benchmark), the paper numbers worth keeping, worked cross-topic threads,
- **`notes.md`** — a `## Baseline (provided lane, <machine>, measured <date>)` section
recording the provided lane's real output with the analysis, then a
predictions-vs-measurements worksheet. **The worksheet's cells are meant to be
empty**: they are the reader's exercise, filled in before running the benchmark, not
a gap to be backfilled. Then the paper numbers worth keeping, cross-topic threads,
and open questions.
- **`experiments/`** — a Rust crate with **lane 1 implemented** and **two lanes
stubbed**. The stub tests are the specification; the reference numbers live in
`notes.md`.
`notes.md`. A bench binary must print its provided lanes and a `[stub — ...]` note
for the unimplemented ones, then exit 0 — a `todo!()` panic must never take down a
measurement above it.

Two topics (4 and 10) deliberately have no provided lane, because their benchmarks
measure only the reader's own implementation. Their READMEs open by saying so and by
giving the arithmetic or the external oracle to predict against instead. That is a
legitimate shape for a topic; inventing a number to fill the slot is not.

## Conventions

Expand All @@ -65,8 +75,11 @@ Every topic follows the same shape, because the shape is what makes it checkable
and add an exercise to construct the case where it holds. (See topic 42's
multi-hit booster for the worked example.)
- **Code reading is done against pinned clones** under `~/repos/` rather than vendored
here, with the commit recorded in the guide so the `file:line` anchors mean
something.
here. The commit each clone was read at is recorded **once**, in the pin table at
the end of [resources/codebases.md](resources/codebases.md), so the thousands of
`file:line` anchors in the guides mean something. Regenerate it with
`python3 tools/pin-table.py` after cloning or updating a reference repo — putting
a SHA in each guide instead would mean thousands of them drifting separately.
- **Generators are seeded.** Anyone must be able to reproduce a figure exactly.
- **Notes capture *why* a design wins** and what it trades away — not summaries.

Expand All @@ -83,11 +96,25 @@ every push to `master` and deploys to GitHub Pages. Before committing content, b
locally and check that mermaid diagrams render and internal links resolve — a broken
link is invisible in markdown and obvious in the book.

A second workflow ([verify.yml](.github/workflows/verify.yml)) runs
`./verify.sh --summary` and a `-D warnings` build of all 45 crates on every push and
pull request. It is the gate that keeps the repo's central claim true, so a lane that
stops running is a red build. Note that `cargo test` is deliberately **not** a gate:
the stub tests are the specification and are supposed to fail on a fresh clone.

One mdbook wrinkle worth knowing: `src = "."`, so mdbook copies every non-markdown
file under the repo root into `book/`. It honours neither `.gitignore` nor any exclude
list, which is why cargo artifacts are pushed outside the clone by
[.cargo/config.toml](.cargo/config.toml). If you remove that file, run `cargo clean`
before `mdbook build` or you will copy gigabytes of build output into the book.

## Running the experiments

```bash
./verify.sh # every measured lane, with output
./verify.sh --summary # just the pass/fail table
./verify.sh --list # every lane and what it measures, run nothing
./verify.sh --criterion # also the slow criterion lanes (topic 0)
./verify.sh 40 41 # only these topics

cd topics/40-security-attack-graphs/experiments
Expand Down
Loading
Loading