ЁЯПл The SchoolтА║ЁЯЧДя╕П DatabasesтА║ЁЯУ╝ рдзрдбрд╛ 15 тАФ Engine рдЪреНрдпрд╛ рдЖрдд: рдЖрдзреА log
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУ╝ рдзрдбрд╛ 15 тАФ Engine рдЪреНрдпрд╛ рдЖрдд: рдЖрдзреА log

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 15 ┬╖ рдорд╛рдЧреАрд▓: lesson-14-advanced-sql ┬╖ рдкреБрдвреАрд▓: lesson-16-replication-failover

рднрд╛рдЧ 3 тАФ рдЕрдзрд┐рдХ рдЦреЛрд▓рд╛рдд. рдзрдбреЗ 01тАУ12 рдиреА рд░реЗрдХреЙрд░реНрдб рд░реВрдо рдмрд╛рдВрдзрд▓реА рдЖрдгрд┐ рдЪрд╛рд▓рд╡рд▓реА. рдзрдбреЗ 13тАУ18 рд╣реЗ interviews рдЖрдгрд┐ incidents рдордзреНрдпреЗ рд╡рд┐рдЪрд╛рд░рд▓реЗ рдЬрд╛рдгрд╛рд░реЗ рд╡рд┐рд╖рдп рдЖрд╣реЗрдд тАФ рдкреНрд░рддреНрдпреЗрдХ рддреНрдпрд╛рдЪ school.db рд╡рд░ рдкреНрд░рддреНрдпрдХреНрд╖ рдЪрд╛рд▓рддреЛ.


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ15, рдЖрдгрд┐ db/demo.py рдордзрд▓реЗ wal(): рддреЗ school.db рд▓рд╛ WAL mode рдордзреНрдпреЗ рдиреЗрддреЗ, reader transaction рдЪреНрдпрд╛ рдордзреНрдпрд╛рдд рдЕрд╕рддрд╛рдирд╛ writer рд▓рд╛ commit рдХрд░реВ рджреЗрддреЗ, reader рдЖрдкрд▓рд╛ snapshot рдЯрд┐рдХрд╡реВрди рдареЗрд╡рддреЛ рд╣реЗ рджрд╛рдЦрд╡рддреЗ, school.db-wal рдореЛрдЬрддреЗ, рддреНрдпрд╛рдЪрд╛ checkpoint рдХрд░рддреЗ, рдЖрдгрд┐ рдкрд░рдд рдкреВрд░реНрд╡реАрдЪреНрдпрд╛ mode рдордзреНрдпреЗ рдиреЗрддреЗ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдореЛрдареНрдпрд╛ рдиреЛрдВрджрд╡рд╣реАрддрд▓реЗ рдкрд╛рди рдкреБрдиреНрд╣рд╛ рд▓рд┐рд╣рд┐рдгреЗ рд╣рд│реВ рдЖрдгрд┐ рдзреЛрдХреНрдпрд╛рдЪреЗ рдЕрд╕рддреЗ тАФ рдЕрд░реНрдзреНрдпрд╛рд╡рд░ рд╡реАрдЬ рдЧреЗрд▓реА рддрд░ рдкрд╛рди рдЕрд░реНрдзреЗ рдЬреБрдиреЗ, рдЕрд░реНрдзреЗ рдирд╡реЗ рд░рд╛рд╣рддреЗ. рдореНрд╣рдгреВрди рдХрд╛рд│рдЬреАрдкреВрд░реНрд╡рдХ рдХрд╛рдо рдХрд░рдгрд╛рд░реА рдЕрднрд┐рд▓реЗрдЦрдкрд╛рд▓ рдЖрдзреА рдбрд╛рдпрд░реАрдд ЁЯУ╝ рдПрдХ рдУрд│ рд▓рд┐рд╣рд┐рддреЗ: "page 7: Aishwarya тЖТ Aishwarya S". рдбрд╛рдпрд░реАрддрд▓реА рдУрд│ рдХрд╛рдЧрджрд╛рд╡рд░ рд╕реБрд░рдХреНрд╖рд┐рдд рдЭрд╛рд▓реНрдпрд╛рд╡рд░рдЪ рддреА "рдЭрд╛рд▓реЗ тЬЕ" рдореНрд╣рдгрддреЗ. рдирдВрддрд░, рд╢рд╛рдВрдд рд╡реЗрд│реА, рддреА рдбрд╛рдпрд░реАрддрд▓реНрдпрд╛ рдУрд│реА рдореЛрдареНрдпрд╛ рдиреЛрдВрджрд╡рд╣реАрдд рдЙрддрд░рд╡рддреЗ. рд╡реАрдЬ рдЧреЗрд▓реА, рддрд░ рддреА рдбрд╛рдпрд░реА рд╡рд╛рдЪрддреЗ рдЖрдгрд┐ рдХрд╛рдо рдкреВрд░реНрдг рдХрд░рддреЗ. рддреА рдбрд╛рдпрд░реА рдореНрд╣рдгрдЬреЗ write-ahead log (WAL).

рдЖрдгрд┐ рддреА рдХрд╛рдо рдХрд░рдд рдЕрд╕рддрд╛рдирд╛, рдПрдХ рдорд┐рдирд┐рдЯрд╛рдкреВрд░реНрд╡реА рдиреЛрдВрджрд╡рд╣реА рдЙрдШрдбрд▓реЗрд▓реНрдпрд╛ рд╡рд╛рдЪрдгрд╛рд▒реНрдпрд╛рд▓рд╛ рдкрд╛рди рддреНрдпрд╛рдиреЗ рд╕реБрд░реБрд╡рд╛рдд рдХреЗрд▓реА рддреЗрд╡реНрд╣рд╛ рдЬрд╕реЗ рд╣реЛрддреЗ рддрд╕реЗрдЪ рджрд┐рд╕рдд рд░рд╛рд╣рддреЗ тАФ рдЦреЛрд▓реАрдЪрд╛ рддреНрдпрд╛рдЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ рдлреЛрдЯреЛ. рд╣реЗрдЪ MVCC: readers writers рдирд╛ рдХрдзреА рдЕрдбрд╡рдд рдирд╛рд╣реАрдд, writers readers рдирд╛ рдХрдзреА рдЕрдбрд╡рдд рдирд╛рд╣реАрдд.

ЁЯЧ║я╕П рдЖрдХреГрддреА

sequenceDiagram
    participant R as ЁЯСА reader
    participant DB as ЁЯЧДя╕П school.db (WAL mode)
    participant W as тЬНя╕П writer
    R->>DB: BEGIN ┬╖ SELECT name тЖТ 'Aishwarya'
    W->>DB: UPDATE тЖТ 'Aishwarya S' ┬╖ COMMIT (appended to the WAL)
    DB-->>W: OK тАФ no waiting for the reader
    R->>DB: SELECT again тЖТ still 'Aishwarya' (snapshot)
    R->>DB: COMMIT ┬╖ new SELECT тЖТ 'Aishwarya S'

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/database/lesson-diagrams.html#l15

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рд╣реЗ рддреАрди рдЧреЛрд╖реНрдЯреА рд╕рдордЬрд╛рд╡рддреЗ рдЬреНрдпрд╛ рд▓реЛрдХ рдЕрдиреНрдпрдерд╛ рдбреЛрд│реЗ рдЭрд╛рдХреВрди рдорд╛рдирддрд╛рдд: OK рдорд┐рд│рд╛рд▓реЗрд▓рд╛ commit рд╡реАрдЬ рдЧреЗрд▓реНрдпрд╛рд╡рд░рд╣реА рдХрд╛ рдЯрд┐рдХрддреЛ, Postgres рдордзреНрдпреЗ readers writers рдЪреА рд╡рд╛рдЯ рдХрд╛ рдкрд╛рд╣рдд рдирд╛рд╣реАрдд, рдЖрдгрд┐ рддреБрдореНрд╣реА рдлрдХреНрдд UPDATE рдХрд░рдд рдЕрд╕рддрд╛рдирд╛ table рдХрд╛ рд╡рд╛рдвреВ рд╢рдХрддреЛ (VACUUM рдЪреА рд╡рд╛рдЯ рдкрд╛рд╣рдгрд╛рд▒реНрдпрд╛ рдЬреБрдиреНрдпрд╛ versions). рд╣рд╛рдЪ log replication (L16) рдЖрдгрд┐ change data capture (L18) рд▓рд╛ рдкреБрд░рд╡рддреЛ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

db/demo.py рдордзрд▓реЗ wal() тАФ рдПрдХрд╛рдЪ file рд▓рд╛ рджреЛрди рдЦрд░реА connections, рдЖрдгрд┐ PRAGMA wal_checkpoint(TRUNCATE) рдЪреНрдпрд╛ рдЖрдзреА рдЖрдгрд┐ рдирдВрддрд░ рдореЛрдЬрд▓реЗрд▓реА -wal file.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 db/demo.py wal
python3 - <<'EOF'
import sqlite3
c = sqlite3.connect("db/school.db")
print(c.execute("PRAGMA journal_mode").fetchone())          # delete тАФ the demo switched it back
print(c.execute("PRAGMA journal_mode=WAL").fetchone())      # try it: now open a second python and write while you read
c.execute("PRAGMA journal_mode=DELETE")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

journal mode before: delete тЖТ after: wal; writer рдХреЛрдгрддреНрдпрд╛рд╣реА lock error рд╢рд┐рд╡рд╛рдп 'Aishwarya S' commit рдХрд░рддреЛ; рддреНрдпрд╛рдЪ transaction рдордзрд▓реНрдпрд╛ reader рд▓рд╛ рдЕрдЬреВрдирд╣реА 'Aishwarya' рджрд┐рд╕рддреЗ, рдЖрдгрд┐ рддреНрдпрд╛рдЪреНрдпрд╛ COMMIT рдирдВрддрд░ 'Aishwarya S'; -wal file рдХрд╛рд╣реА kilobytes рдЪреА рдЕрд╕рддреЗ, рдордЧ checkpoint рдирдВрддрд░ 0 bytes.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреБрдореНрд╣реА durability рдЖрдгрд┐ snapshot isolation рдШрдбрддрд╛рдирд╛ рдкрд╛рд╣рд┐рд▓реЗ: рдЖрдзреА log, рдирдВрддрд░ pages, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ reader рдЦреЛрд▓реАрдЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ рдлреЛрдЯреЛ рдареЗрд╡рддреЛ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: WAL settings, checkpoint tuning, autovacuum рдЖрдгрд┐ long-running-transaction alerts рд╣реЗ database on-call рдЪреЗ рдиреЗрд╣рдореАрдЪреЗ рд╡рд┐рд╖рдп рдЖрд╣реЗрдд.

тПня╕П рдкреБрдвреЗ

git checkout lesson-16-replication-failover тАФ рддреЛ log рдЦреЛрд▓реАрдЪреНрдпрд╛ рдПрдХрд╛ рдкреНрд░рддреАрдХрдбреЗ рдкрд╛рдард╡рд╛.

ЁЯУ╝ Lesson 15 тАФ Inside the engine: the log comes first

ЁЯУН You are here: Lesson 15 of 18 ┬╖ Previous: lesson-14-advanced-sql ┬╖ Next: lesson-16-replication-failover

Part 3 тАФ going deeper. Lessons 01тАУ12 built and ran the record room. Lessons 13тАУ18 are the topics interviews and incidents ask about тАФ each one runs for real on the same school.db.


ЁЯУж What's in this branch

Lessons 01тАУ15, plus wal() in db/demo.py: it switches school.db to WAL mode, lets a writer commit while a reader is in the middle of a transaction, shows the reader keeping its snapshot, measures school.db-wal, checkpoints it, and switches back.

ЁЯзТ Explain like I'm 5

Rewriting a page in the big register is slow and risky тАФ if the lights go out halfway, the page is half old, half new. So the careful archivist first writes one line in a diary ЁЯУ╝: "page 7: Aishwarya тЖТ Aishwarya S". Only when the diary line is safely on paper does she say "done тЬЕ". She copies diary lines into the big register later, when it is quiet. If the lights go out, she reads the diary and finishes the job. That diary is the write-ahead log (WAL).

And while she works, a reader who opened the register a minute ago keeps seeing the page as it was when they started тАФ their own photo of the room. That is MVCC: readers never block writers, writers never block readers.

ЁЯЧ║я╕П Diagram

sequenceDiagram
    participant R as ЁЯСА reader
    participant DB as ЁЯЧДя╕П school.db (WAL mode)
    participant W as тЬНя╕П writer
    R->>DB: BEGIN ┬╖ SELECT name тЖТ 'Aishwarya'
    W->>DB: UPDATE тЖТ 'Aishwarya S' ┬╖ COMMIT (appended to the WAL)
    DB-->>W: OK тАФ no waiting for the reader
    R->>DB: SELECT again тЖТ still 'Aishwarya' (snapshot)
    R->>DB: COMMIT ┬╖ new SELECT тЖТ 'Aishwarya S'

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/database/lesson-diagrams.html#l15

тЭУ What

ЁЯдФ Why

It explains three things people otherwise take on faith: why an OK'd commit survives a power cut, why readers don't wait for writers in Postgres, and why a table can grow when you only UPDATE (old versions waiting for VACUUM). The same log feeds replication (L16) and change data capture (L18).

ЁЯФз How (in this repo)

wal() in db/demo.py тАФ two real connections to the same file, and the -wal file measured before and after PRAGMA wal_checkpoint(TRUNCATE).

ЁЯзк Try it

python3 db/demo.py wal
python3 - <<'EOF'
import sqlite3
c = sqlite3.connect("db/school.db")
print(c.execute("PRAGMA journal_mode").fetchone())          # delete тАФ the demo switched it back
print(c.execute("PRAGMA journal_mode=WAL").fetchone())      # try it: now open a second python and write while you read
c.execute("PRAGMA journal_mode=DELETE")
EOF

тЬЕ Verify тАФ what you should see

journal mode before: delete тЖТ after: wal; the writer commits 'Aishwarya S' with no lock error; the reader in the same transaction still sees 'Aishwarya', and 'Aishwarya S' after its COMMIT; the -wal file has a few kilobytes, then 0 bytes after the checkpoint.

ЁЯПБ What you just proved

You watched durability and snapshot isolation happen: the log first, pages later, and each reader keeping its own photo of the room.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: WAL settings, checkpoint tuning, autovacuum and long-running-transaction alerts are standard database on-call topics.

тПня╕П Next

git checkout lesson-16-replication-failover тАФ ship that log to a copy of the room.

тЖР Previousadvanced sqlNext тЖТreplication failover

This page is the lesson's README from the lesson-15-inside-the-engine branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.