← All projects
Build 03Relational database enginev1.0 · CI passing

ForgeDB

Everyone uses databases. I built one to see what happens inside.

A relational database engine in Java with no third-party libraries: its own SQL parser, binary records in 4 KB disk pages, an LRU buffer pool, persistent metadata and a B+ tree index. Every row is stored, found, updated and deleted by code I wrote.

RoleSolo · Java rebuild + tests
LanguageJava 17
DependenciesZero
Tested20,000-key tree · restart persistence
Runs asInteractive SQL shell
01 · Follow a queryPick one

One line of SQL. Seven layers of engine.

Most projects talk to a database through a driver. ForgeDB goes one layer deeper. Pick a query to see which parts of the engine it touches, and why some queries are fast.

Queries

01
Shell

Interactive prompt, EXEC file.sql

02
SQL parser

Text → immutable statement objects

03
Engine

Routes each statement

04
Catalog

Schemas, keys, indexes

05
Record manager

Rows ↔ binary

06
B+ tree index

Key → page + slot

07
Buffer pool

LRU pages in memory

08
4 KB pages on disk

students.records · students_pk.index

02 · Inside a pageExactly 4,096 bytes

Every table is a chain of 4 KB pages.

Rows are encoded as fixed-length binary records. Each page starts with a 12-byte header that links it to its neighbors and counts its rows. When deletes empty a page, ForgeDB reuses it instead of growing the file.

One page
BYTES 0–3Prev page
BYTES 4–7Next page
BYTES 8–11Row count
row 1row 2row 3…
012FIXED-LENGTH ROWS (INT · FLOAT · CHAR(N))4095
PAGE 0prev −1
PAGE 1full of rows
PAGE 2empty → reused
PAGE 3next −1
03 · The B+ treeTry a lookup

Find one row without reading every page.

Each table gets a B+ tree on its primary key, with sorted, linked leaves and real leaf and internal splits as it grows. An equality lookup walks a few nodes instead of scanning the whole table.

Look up a key
Pick a key to watch the search path.
Design trade-off: after deletes or primary-key changes, ForgeDB rebuilds the index. Simpler to reason about, while insert and search stay real B+ tree algorithms.
04 · The buffer poolLeast recently used

Disk is slow. So keep the hot pages close.

Recently used pages stay in memory. When the pool is full, the least recently used page is evicted, and if it was changed it's written back to disk first. Click pages to read or write them.

Buffer pool · 4 slots
0HITS
0DISK READS
0DIRTY FLUSHES
Request a page
Pool is empty. Every first read goes to disk.
05 · ProofTests + CI

Tested where databases usually break.

Tree splits, page boundaries, deletes and restarts are where storage bugs hide, so that's where the tests push. They run on every push with GitHub Actions.

20K
Deep B+ tree

20,000 keys with a tiny tree order to force internal splits and root growth.

1,000
Storage stress

Rows across many pages: deletes, page reuse, a primary-key update and an index rebuild.

↻
Restart persistence

Close the engine, reopen it, and verify every row and index survived.

SQL
End-to-end smoke

Create, insert, select, update and delete through the real parser and shell.

06 · Try itA real session

It's a real shell. Here's a session.

Build it with one script, no Maven required, and run java -jar build/forgedb.jar. This replays the demo from the repo.

FORGEDB 1.0.0↻ REPLAY
07 · ScopeWhat's in, what's next

Small on purpose. Honest about it.

ForgeDB covers the core path from SQL to bytes on disk. The features real databases add on top are the natural next steps, and the hardest ones are the ones I'd build next.

Built 08

SQL subset: CREATE, INSERT, SELECT, UPDATE, DELETELIVE
WHERE with = <> < > <= >= and ANDLIVE
INT, FLOAT, CHAR(N) · primary-key checksLIVE
Binary records in linked 4 KB pagesLIVE
Empty-page reuse after deletesLIVE
LRU buffer pool with dirty flushingLIVE
B+ tree primary-key index, indexed lookupsLIVE
Persistent catalog, records and index filesLIVE

Next 06

Write-ahead log & crash recoveryNEXT
Transactions & rollbackPLANNED
Concurrent clients & lockingPLANNED
Joins and column projectionPLANNED
Query planning & cost optimizationPLANNED
Variable-length recordsPLANNED

Not another app on top of a database. The database itself, page by page.

Java 17SQL parserB+ treeLRU buffer poolBinary storageGitHub ActionsZero dependencies
↑↓ NAVIGATE↵ OPENESC CLOSE