Commit related changes together
Maya changes a task to done. Taskboard records both the task state and an activity event describing the change.
Those writes belong to one business operation. If the task changes but the activity insert fails, the history is incomplete. If the activity exists but the task update fails, the history describes something that never happened.
A database transaction groups the writes into one commit. Either PostgreSQL commits the group, or it rolls back the group's changes.
// Illustrative transaction shape.
await db.transaction(async tx => {
await updateTask(tx, scope, input);
await insertActivity(tx, scope, event);
});
Both functions must use tx. Calling the ordinary db object for one write can place that write outside the transaction.
Atomic does not mean isolated from every race
Atomicity means that the transaction's writes commit together. Isolation controls what concurrent transactions can observe and how their work interacts.
PostgreSQL's usual READ COMMITTED isolation lets each statement see data committed before that statement begins. Another transaction may commit between two reads in your transaction. Being inside a transaction does not automatically freeze every row the operation depends on.
Constraints, row locks, conditional updates, or stronger isolation can establish the additional guarantees a business rule needs. The next lesson uses a conditional update to protect concurrent task edits.
External calls are outside PostgreSQL
PostgreSQL cannot roll back an email already sent or a payment already accepted by Stripe. Holding a transaction open while calling a provider also holds database resources during a network wait.
Taskboard records durable background work in PostgreSQL and performs external work afterward. The transaction commits the business change together with the job that represents the required follow-up. Delivery can still fail and retry, so that follow-up needs its own failure handling.
The same rule applies to real-time notifications. Sending an event before commit can tell connected clients about data that later rolls back. A notification should represent a committed fact.
Can a database rollback unsend an invitation email?
No. The SMTP server is a separate system. A transaction can protect the invitation row and its delivery job, but external delivery needs a separate reliability design.
Transactions protect groups of database changes. Concurrency and external side effects require additional guarantees.
Read api/src/tasks.ts Read api/src/jobs.ts