Student starter code (30% baseline)
index.html- Main HTML pagescript.js- JavaScript logicstyles.css- Styling and layoutpackage.json- Dependenciessetup.sh- Setup scriptREADME.md- Instructions (below)๐ก Download the ZIP, extract it, and follow the instructions below to get started!
Welcome to Activity 08! This template teaches you database transactions, ACID properties, and data integrity using a realistic banking system.
By completing this activity, you will:
IMPORTANT: This template includes WORKING CODE! You can see results immediately:
Download SQLite Browser (if you don't have it):
Open the database files:
banking.dbsqlite/schema.sql -> Click "Execute" โถ๏ธsample-data.sql -> Click "Execute" โถ๏ธRun the transaction examples:
sqlite/queries.sql# Make sure you're in the activity folder, then:
docker-compose up -d
# Access MySQL:
docker exec -it w4-mysql mysql -u root -plearning123 contacts_db
# Then load schema:
source /docker-entrypoint-initdb.d/schema.sql;
# Access PostgreSQL:
docker exec -it w4-postgres psql -U postgres contacts_db
# Then load schema:
\i /docker-entrypoint-initdb.d/schema.sql
postgresql/schema.sql and sample-data.sql70% of the code is implemented for you:
Location: sqlite/queries.sql line 229
Your Task: Implement a safe money transfer between two accounts
Success Criteria:
Hints:
BEGIN TRANSACTION;SELECT balance FROM accounts WHERE account_number = 'CHK-1001';AND balance >= 500 in UPDATE to prevent overdraftCOMMIT;Expected Output:
-- Before:
-- CHK-1001: $5,000.00
-- CHK-1003: $3,200.00
-- After:
-- CHK-1001: $4,500.00
-- CHK-1003: $3,700.00
-- Transactions table gains new row:
-- from_account_id=1, to_account_id=3, amount=500, status='completed'
Why This Matters: In banking, money must never disappear or duplicate. Atomic transactions ensure both accounts update together or not at all, preventing financial discrepancies.
Location: sqlite/queries.sql line 270
Your Task: Handle concurrent withdrawal scenario to prevent overdraft
Scenario:
Success Criteria:
Hints:
BEGIN IMMEDIATE TRANSACTION; for write lock (SQLite)SELECT balance FROM accounts WHERE id = 4;UPDATE accounts SET balance = balance - 5000 WHERE id = 4 AND balance >= 5000;SELECT FOR UPDATE to lock the rowExpected Output:
-- Transaction 1:
BEGIN IMMEDIATE TRANSACTION;
-- Check: balance = $8,750 โ
UPDATE accounts SET balance = balance - 5000 WHERE id = 4 AND balance >= 5000;
-- Success: balance = $3,750
COMMIT;
-- Transaction 2:
BEGIN IMMEDIATE TRANSACTION;
-- Check: balance = $3,750 (already reduced!)
UPDATE accounts SET balance = balance - 4500 WHERE id = 4 AND balance >= 4500;
-- Fails: 0 rows affected (insufficient funds)
ROLLBACK;
Why This Matters: Race conditions can cause overdrafts in real banking systems. Proper transaction isolation and locking prevent customers from withdrawing more than they have, even with simultaneous requests.
Location: sqlite/queries.sql line 317
Your Task: Document and demonstrate transaction isolation levels and phenomena
Success Criteria:
Hints:
Expected Documentation Format:
-- ========================================
-- PHENOMENON 1: Dirty Read
-- ========================================
-- Definition: Transaction reads data that another transaction has modified
-- but not yet committed. If the other transaction rolls back, the read
-- data becomes invalid.
-- Session 1 (Transaction A):
BEGIN TRANSACTION;
UPDATE accounts SET balance = 10000 WHERE id = 1;
-- Don't commit yet! This is uncommitted data.
-- Session 2 (Transaction B):
BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;
-- SERIALIZABLE: Shows original value ($5000)
-- READ UNCOMMITTED (if supported): Would show $10000 (dirty read!)
-- Session 1 rolls back:
ROLLBACK; -- Oops, that $10000 was wrong!
-- Session 2 continues:
SELECT balance FROM accounts WHERE id = 1;
-- Still shows $5000 (correct original value)
COMMIT;
-- Result: SERIALIZABLE prevents dirty reads. Transaction B never saw
-- the uncommitted $10000 from Transaction A.
Why This Matters: Understanding isolation levels prevents data corruption bugs in high-concurrency systems. Banking applications must prevent dirty reads to ensure accurate balances, while other systems might trade isolation for performance.
Accounts Table (10 records)
| Field | Type | Purpose | Example |
|---|---|---|---|
id |
INTEGER | Unique identifier | 1, 2, 3... |
account_number |
TEXT | Account ID | CHK-1001, SAV-2001 |
customer_name |
TEXT | Account holder | "Alice Johnson" |
account_type |
TEXT | Account category | checking, savings, business |
balance |
DECIMAL | Current balance | 5000.00 |
created_at |
DATETIME | Account opened | 2024-02-15 10:00:00 |
updated_at |
DATETIME | Last modification | Auto-updates |
Transactions Table (30 records)
| Field | Type | Purpose | Example |
|---|---|---|---|
id |
INTEGER | Unique identifier | 1, 2, 3... |
from_account_id |
INTEGER | Source account (NULL for deposits) | 1 |
to_account_id |
INTEGER | Destination (NULL for withdrawals) | 2 |
amount |
DECIMAL | Transaction amount | 500.00 |
transaction_type |
TEXT | Operation type | deposit, withdrawal, transfer |
status |
TEXT | Current state | pending, completed, failed, cancelled |
description |
TEXT | Transaction note | "Salary deposit" |
timestamp |
DATETIME | When occurred | 2024-03-01 09:00:00 |
Audit Log Table (50+ records)
| Field | Type | Purpose | Example |
|---|---|---|---|
id |
INTEGER | Unique identifier | 1, 2, 3... |
table_name |
TEXT | Affected table | accounts, transactions |
action |
TEXT | Operation type | INSERT, UPDATE, DELETE |
record_id |
INTEGER | Modified record | 5 |
old_values |
TEXT | Before state | "balance:5000" |
new_values |
TEXT | After state | "balance:5500" |
details |
TEXT | Change description | "Balance changed..." |
timestamp |
DATETIME | When logged | 2024-03-01 09:01:00 |
๐ ACID = Data Integrity Guarantee
A - Atomicity
"All or Nothing"
- Transaction completes fully or not at all
- No partial updates
- Example: Money transfer updates both accounts or neither
C - Consistency
"Valid State Always"
- Database enforces all rules and constraints
- No invalid data states
- Example: Balance never goes negative (CHECK constraint)
I - Isolation
"Transactions Don't Interfere"
- Concurrent transactions don't see each other's changes
- Prevents race conditions
- Example: Two withdrawals don't cause overdraft
D - Durability
"Changes are Permanent"
- Committed data survives crashes
- Logged to disk before success
- Example: Committed transfer survives power outage
-- Start transaction
BEGIN TRANSACTION;
BEGIN; -- Short form
-- Save changes permanently
COMMIT;
-- Undo all changes
ROLLBACK;
-- Create checkpoint within transaction
SAVEPOINT savepoint_name;
-- Rollback to checkpoint
ROLLBACK TO SAVEPOINT savepoint_name;
-- Release checkpoint
RELEASE SAVEPOINT savepoint_name;
-- Lock table (PostgreSQL/MySQL)
LOCK TABLE accounts IN EXCLUSIVE MODE;
-- Lock specific row (PostgreSQL)
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- Transaction isolation level (PostgreSQL/MySQL)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Transaction not committing:
COMMIT; at the endBalance goes negative:
CHECK(balance >= 0) constraintAND balance >= amount in UPDATEConcurrent access issues:
BEGIN IMMEDIATE (SQLite) for write lockSELECT FOR UPDATE (PostgreSQL) to lock rowsPRAGMA read_uncommitted; (SQLite)Audit logs not appearing:
.schema accounts (SQLite)SELECT * FROM audit_log ORDER BY timestamp DESC;SAVEPOINT not working:
| Query | Expected Behavior |
|---|---|
| Query 1 | Account balance increases by $1000 |
| Query 2 | ROLLBACK restores original balance |
| Query 3 | $500 transfers from Alice to Bob atomically |
| Query 4 | Transfer only occurs if sufficient funds |
| Query 5 | Partial rollback to savepoint works |
| Query 6 | Uncommitted changes invisible to other sessions |
| Query 7 | Audit log records balance change |
| TODO 8 | $500 transfer completes successfully |
| TODO 9 | Only one withdrawal succeeds |
| TODO 10 | Documentation explains all three phenomena |
Ready for more? Try these bonus features:
| Feature | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| Begin | BEGIN; |
START TRANSACTION; |
BEGIN; |
| Commit | COMMIT; |
COMMIT; |
COMMIT; |
| Rollback | ROLLBACK; |
ROLLBACK; |
ROLLBACK; |
| Savepoint | SAVEPOINT name; |
SAVEPOINT name; |
SAVEPOINT name; |
| Lock table | Not supported | LOCK TABLES x WRITE; |
LOCK TABLE x; |
| Lock row | Automatic | SELECT FOR UPDATE; |
SELECT FOR UPDATE; |
| Level | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| READ UNCOMMITTED | โ | โ | โ |
| READ COMMITTED | โ | โ | โ (default) |
| REPEATABLE READ | โ | โ (default) | โ |
| SERIALIZABLE | โ (default) | โ | โ |
PostgreSQL (explicit row lock):
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- Row is locked until COMMIT/ROLLBACK
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
MySQL (table lock):
LOCK TABLES accounts WRITE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UNLOCK TABLES;
SQLite (automatic locking):
BEGIN IMMEDIATE TRANSACTION;
-- Entire database is locked for writing
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
Your project is complete when:
Once you complete this activity, you'll have:
These skills are critical for backend development, financial systems, and any application requiring data integrity!
Need Help?
Ready to Submit? Complete this activity and submit through the Activity Submission Form
Happy transacting! ๐ฆโจ