Skip to content
← Advanced DevOps

Practical lab guide

Lab: practise SQL and recovery with PostgreSQL

Verify fixture invariants, a bounded lock failure, index evidence, and a logical restore.

Documentation reviewed2026-10-01 · 4 min read · lab time varies
On this page

Scope and prerequisites

Complete the eight PostgreSQL bites in sequence using the isolated learnwithsk-postgres container. Keep the study fixture created in the schema bite unchanged; reversible examples end in rollback. This lab uses synthetic data only. It needs neither MicroBank nor a Kubernetes cluster running.

Record the version, image digest, resource limits, actual results, and failures in a local evidence file. The expected results below give you a comparison for your run; they do not establish that a production recovery has been verified.

1. Assert the data contract

Save as verify.sql:

sql
DO $
BEGIN
  IF (SELECT count(*) FROM study.accounts) <> 3
     OR (SELECT count(*) FROM study.entries) <> 3
     OR (SELECT coalesce(sum(amount_cents), 0) FROM study.entries) <> 2750
     OR (SELECT coalesce(sum(amount_cents), 0) FROM study.entries WHERE account_id = 1) <> 750
     OR NOT EXISTS (SELECT 1 FROM study.accounts WHERE account_id = 3)
     OR EXISTS (SELECT 1 FROM study.entries WHERE account_id = 3)
  THEN
    RAISE EXCEPTION 'Fixture count or balance invariant failed';
  END IF;
END
$;
SELECT 'fixture invariants passed' AS result;
bash
docker exec -i learnwithsk-postgres psql -X -v ON_ERROR_STOP=1 \
  -U postgres -d study < verify.sql

Also run your HAVING and empty-account queries from the SQL bite. The selected accounts should be 2 and 3 respectively. Record the actual rows rather than only a success message.

2. Confirm rejected input stays rejected

Save as constraints.sql. Exception blocks are test assertions: each invalid attempt must raise its expected database error.

sql
DO $
BEGIN
  BEGIN
    INSERT INTO study.entries (account_id, request_key, amount_cents, created_at)
    VALUES (1, 'r-1', 900, now());
    RAISE EXCEPTION 'Duplicate request unexpectedly accepted';
  EXCEPTION WHEN unique_violation THEN NULL;
  END;
  BEGIN
    INSERT INTO study.entries (account_id, request_key, amount_cents, created_at)
    VALUES (999, 'unknown-account', 900, now());
    RAISE EXCEPTION 'Unknown account unexpectedly accepted';
  EXCEPTION WHEN foreign_key_violation THEN NULL;
  END;
  BEGIN
    INSERT INTO study.entries (account_id, request_key, amount_cents, created_at)
    VALUES (1, 'zero-value', 0, now());
    RAISE EXCEPTION 'Zero value unexpectedly accepted';
  EXCEPTION WHEN check_violation THEN NULL;
  END;
  BEGIN
    INSERT INTO study.entries (account_id, request_key, amount_cents, created_at)
    VALUES (1, NULL, 900, now());
    RAISE EXCEPTION 'Null key unexpectedly accepted';
  EXCEPTION WHEN not_null_violation THEN NULL;
  END;
END
$;
SELECT 'constraint checks passed' AS result;
bash
docker exec -i learnwithsk-postgres psql -X -v ON_ERROR_STOP=1 \
  -U postgres -d study < constraints.sql

Run verify.sql again. Failed inserts may advance the identity sequence, but must not create accepted entry rows.

3. Collect contention and query-plan evidence

Run the bounded two-session lock exercise. Keep the lock-timeout message and verify both transactions ended. Then record the before/after plans and unchanged five IDs from Indexes and query plans. Explain the plans PostgreSQL actually chooses, including any difference from your prediction.

4. Back up and restore to a new database

Run in your local lab directory. Do not redirect over a backup you need to keep. study_restore must not already exist; if it does, stop and identify the previous exercise rather than dropping it automatically.

bash
umask 077
docker exec learnwithsk-postgres pg_dump -U postgres -d study \
  --format=custom --schema=study --no-owner --no-acl > study.dump
docker exec -i learnwithsk-postgres pg_restore --list < study.dump
docker exec learnwithsk-postgres createdb -U postgres study_restore
docker exec -i learnwithsk-postgres pg_restore -U postgres -d study_restore \
  --exit-on-error --no-owner --no-acl < study.dump
docker exec -i learnwithsk-postgres psql -X -v ON_ERROR_STOP=1 \
  -U postgres -d study_restore < verify.sql
docker exec -i learnwithsk-postgres psql -X -v ON_ERROR_STOP=1 \
  -U postgres -d study_restore < constraints.sql

Use no TTY when streaming the binary dump. Check every command's exit status before proceeding; a partial dump is not a backup. The assertions should pass against the restored database too. Inspect \d study.entries there.

Because this restore deliberately omits ACLs, study_reader should not initially have schema/table access in study_restore. Reapply its grants in that database, then verify the allowed read and denied write from the roles bite. The role itself remains in this server; a new server would need role provisioning too. pg_restore↗, dump reference↗.

5. Save the evidence and release resources

Your evidence should include the fixture assertions, constraint failures, lock timeout, query plans, role checks, restore duration, and limitations. This is a logical restore on the same server with lab data; it does not establish MicroBank disaster recovery, PITR, or network/TLS security.

After saving anything you need, stop this profile before starting MicroBank:

bash
docker stop learnwithsk-postgres

To resume your own fixture later, use docker start learnwithsk-postgres. When finished permanently, the following removes only the named fixture container and its database volume. This erases both teaching databases. Inspect the names/ownership first; do not substitute MicroBank volumes.

bash
docker rm learnwithsk-postgres
docker volume rm learnwithsk-postgres-data

Keep or remove your local dump according to your evidence needs. Continue with MicroBank database inspection.

Interpret the completed round trip

Expected counts and totals check selected invariants; rejected writes check that selected constraints still apply. Neither alone proves every row matches. The grant rehearsal adds another dimension: restored data without the intended reader permissions is not a complete operational handoff. A failed insert can consume an identity sequence value without creating an entry, so compare row counts rather than expecting contiguous IDs.

If restore verification fails, retain the source fixture and dump, inspect the first reported restore error, and resolve it before claiming success. Keep the original database until the new one is verified. Then move to MicroBank inspection with the fixture's guarantees clearly separated from the application's actual schema.

Your notes and evidence

Record observations, questions, or links to your work. Keep credentials out of your notes.

Loading saved progress…

Back up or restore this path

Progress and notes stay in this browser. A backup contains only this learning path.