transaction-isolation — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited transaction-isolation (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Transaction isolation is the I axis of ACID: the property and configuration choice that determines what concurrent transactions can observe and which concurrent histories are allowed to commit. It is not just a four-row standard table. The SQL standard names four levels - Read Uncommitted, Read Committed, Repeatable Read, and Serializable - but the practical field includes Snapshot Isolation, Read Committed Snapshot Isolation, MVCC snapshots, key-range/gap locks, predicate locks, SSI, optimistic conflict detection, and distributed timestamp ordering.
Use this skill when the user needs to choose, explain, or debug an isolation contract for a workload. The useful answer names the engine, version, settings, transaction classes, invariants, allowed anomalies, required locks or constraints, and retry behavior. "We use serializable" is not complete until it also says which implementation, what abort or conflict errors look like, and how application code retries the whole transaction safely.
This skill is distinct from broader ACID-primer work outside the active skill corpus, which explains ACID as a whole; cap-theorem-tradeoffs and replication-patterns, which reason about replicas, freshness, and partitions; query-optimization, which diagnoses plan and latency evidence; indexing-strategy, which designs retrieval structures; and entity-relationship-modeling, which owns durable schema and constraints. It composes with those skills when concurrency correctness depends on constraints, index-backed range locks, replica-read routing, or lock-contention evidence, but it owns the isolation decision itself.
Covers:
Does not cover replica freshness, CP/AP/PACELC choices, replication topology, query-plan tuning, durable index-portfolio design, schema modeling, or cross-service transaction orchestration.
Isolation is the part of ACID most often mis-defaulted because it looks like a dropdown and behaves like a proof obligation. The question is not "what level is safest?" or "what is fastest?" The question is "what histories would violate this workload's invariants, and what does this engine actually do with those histories?"
The discipline is per transaction class. A single-row atomic decrement can often be safe at Read Committed if expressed as one conditional UPDATE, while the same business operation can be unsafe if written as read-then-write application logic. A doctor-on-call invariant across rows is vulnerable to write skew under Snapshot Isolation. A cross-table report may only need a transaction snapshot. A uniqueness invariant should be a database constraint when possible, not an isolation-level hope. A work queue may need locking reads such as FOR UPDATE SKIP LOCKED, but that is a deliberately different concurrency contract from serializability.
Modern engines do not remove the need for this skill. PostgreSQL SSI, CockroachDB serializable defaults, Spanner external consistency, SQL Server optimized locking with RCSI, and MySQL next-key locking all solve some classes of anomalies better than old textbook levels, but they also move the burden to retries, version-store capacity, lock range shape, database options, and engine-specific behavior.
| Anomaly | What it is | Common defenses |
|---|---|---|
| Dirty write | One transaction overwrites uncommitted data from another | Prevented by almost all production engines at all useful levels; if allowed, the system is not a safe transactional store |
| Dirty read | Read data written by a transaction that later aborts | Read Committed or stronger; PostgreSQL maps Read Uncommitted to Read Committed |
| Non-repeatable / fuzzy read | Same row is read twice and has changed between reads | Repeatable Read, Snapshot, Serializable, or explicit row locks |
| Phantom read | Same predicate/range query returns a different set of rows | Serializable, key-range/gap/predicate locks, SSI, or predicate materialization |
| Read skew / fractured read | A transaction observes only part of another transaction's committed effects, or combines values from inconsistent times | Transaction-level snapshot, serializable, or engine-specific consistent-read guarantees |
| Lost update | Two transactions read the same value and write back derived values, losing one update | Single-statement atomic update, row lock, optimistic version column, write-write conflict detection, or serializable |
| Write skew | Two transactions read overlapping data, update disjoint rows, and jointly violate an invariant | Serializable/SSI, predicate/range lock, materialized guard row, exclusion/unique constraint, or redesign of invariant storage |
| Read-only transaction anomaly | A read-only transaction sees a snapshot that is inconsistent with every serial order because of concurrent writers | Serializable/SSI, deferrable safe snapshot where supported |
| Serialization anomaly | The committed result cannot be explained by any serial order | Serializable implementation, or abort/retry when the engine detects the dangerous history |
Read this table from the workload upward. Do not ask "which level sounds reasonable?" Ask which anomaly would create a real correctness bug for this transaction class.
| Surface | PostgreSQL | MySQL/InnoDB | SQL Server / Azure SQL | Oracle | CockroachDB / Spanner |
|---|---|---|---|---|---|
| Default posture | Read Committed, MVCC per-statement snapshot | Repeatable Read, consistent read snapshot plus locking-read gap/next-key behavior | Read Committed; lock-based unless RCSI is enabled; Azure SQL defaults RCSI on | Read Committed, statement-level read consistency | CockroachDB defaults to Serializable; Spanner defaults read-write transactions to serializable external consistency |
| Read Uncommitted | Accepted syntax but behaves like Read Committed | Allows dirty reads | Allows dirty reads | Not a normal user level; effectively no dirty reads | Usually unsupported or upgraded |
| Read Committed | Each statement sees data committed before that statement starts | Each consistent read sees a fresh committed snapshot; locking behavior differs from RR; gap locks still exist for FK/duplicate checks | Lock-based RC or RCSI statement snapshot depending on database option | Each query sees data committed before that query starts | CockroachDB RC statements see data committed before each statement and reduce client-side retry burden |
| Repeatable Read / Snapshot | PostgreSQL RR is SI-like: no dirty/non-repeatable/phantom reads in PostgreSQL, but serialization anomalies remain possible | Default; plain consistent reads use a transaction snapshot; locking reads and updates can take next-key/gap locks | Repeatable Read is lock-based and allows phantoms; SNAPSHOT is separate transaction-level row-versioning | No standard RR user level; Serializable/Read Only provide transaction-level read consistency | Spanner Repeatable Read is snapshot isolation and can admit write skew; CockroachDB does not use a traditional RR level |
| Serializable | SSI detects dangerous read/write dependencies and aborts with SQLSTATE 40001 | Stricter than RR; plain SELECT becomes a locking shared read when autocommit is disabled | Key-range locking serializable; optimized locking benefits shrink or disappear outside RC/RCSI | Transaction-level snapshot-style consistency; conflicting writes can raise ORA-08177 | CockroachDB serializable may require client retry; Spanner serializable is externally consistent |
Use this matrix as a starting map, not as proof. Always read the version-specific documentation and check the actual database options. The same application code can move from lock-based Read Committed to statement-snapshot Read Committed merely by enabling RCSI; that is a behavior change, not a harmless performance toggle.
PostgreSQL. Read Uncommitted behaves like Read Committed. Repeatable Read sees a transaction snapshot and prevents phantom reads in PostgreSQL, but serialization anomalies remain possible and updating a row changed after the transaction snapshot can raise SQLSTATE 40001. Serializable uses SSI with SIREAD/predicate locks that do not block writers but can abort transactions with 40001; retry the complete transaction. PostgreSQL 17 added configurable SLRU cache sizing including serializable_buffers; PostgreSQL 19 is a development/beta line and its pg_stat_lock / max_locks_per_transaction notes are operational visibility/capacity notes, not semantic isolation changes.
MySQL/InnoDB. InnoDB offers the four SQL-standard levels and defaults to Repeatable Read. Plain consistent reads within a Repeatable Read transaction use the snapshot established by the first read. Locking reads, updates, and deletes can use record, gap, or next-key locks; range scans can block inserts into covered gaps. MySQL explicitly warns against mixing locking statements and non-locking SELECTs in one Repeatable Read transaction when the intended behavior is usually Serializable. Lock wait timeout is a contention/locking failure, not the same thing as a serialization anomaly; MySQL rolls back the current statement by default unless configured to roll back the whole transaction.
SQL Server / Azure SQL. Read Committed is lock-based unless the database has READ_COMMITTED_SNAPSHOT enabled, in which case READ COMMITTED reads use statement-level row versions. SNAPSHOT is separate, opt-in transaction-level row versioning. SNAPSHOT update conflicts, such as Msg 3960, are not RCSI read behavior. Serializable uses range locks. Optimized locking combines TID locking and Lock After Qualification (LAQ) to reduce lock memory and blocking, but it is platform/option dependent: Azure SQL has different defaults, SQL Server 2025 requires enabling it, ADR is required, and LAQ only applies under Read Committed with RCSI and without conflicting hints or unsupported plan shapes.
Oracle. Oracle Read Committed is statement-level read consistency: every query sees data committed before that query began. Serializable and Read Only are transaction-level read consistency: the transaction sees changes committed before the transaction began plus its own changes. Oracle Serializable can raise ORA-08177 when a read/write transaction tries to update or delete data changed after the transaction began; retry the intended operation or transaction. Long-running Oracle consistent reads can also hit ORA-01555 snapshot-too-old when needed undo has been overwritten; fix transaction duration or undo retention rather than treating it as a normal serialization conflict.
CockroachDB and Spanner. CockroachDB defaults to Serializable and can still require SQLSTATE 40001 client-side retries when automatic retry is impossible; Read Committed exists to reduce retry burden by using statement-level behavior. Spanner supports Serializable and Repeatable Read. Spanner Serializable is externally consistent: transactions behave as if sequential, with real-time ordering guarantees for clients. Spanner Repeatable Read is snapshot isolation, can admit write skew for application constraints not enforced by schema, and may be combined with SELECT ... FOR UPDATE/pessimistic concurrency to validate or lock the data actually read.
SHOW transaction_isolation; SQL Server READ_COMMITTED_SNAPSHOT, ALLOW_SNAPSHOT_ISOLATION, optimized locking, and ADR; MySQL InnoDB isolation and lock timeout behavior; Oracle transaction mode and undo-retention risk; CockroachDB/Spanner isolation and concurrency mode.query-optimization only for the performance diagnosis.| Workload pattern | Risk | Preferred first defenses |
|---|---|---|
| Single-row decrement, inventory claim, account debit | Lost update or negative value if read-then-write | One conditional UPDATE ... WHERE balance >= amount, row lock, or optimistic version column; serializable when logic reads more than the target row |
| Work queue claiming jobs | Duplicate claim or blocking workers | Locking read with skip/wait policy, atomic status transition, and explicit fairness/visibility expectations |
| Multi-row "at least one" or sum invariant | Write skew under SI/RR | Serializable/SSI, a materialized guard row locked or updated by all writers, exclusion/unique constraint, or schema redesign |
| "Check then insert" uniqueness | Race to duplicate | Unique/partial unique constraint first; handle unique violation as possible concurrency conflict only when the app selected candidate keys from current state |
| Range booking / calendar overlap | Phantom or overlap write skew | Exclusion/range constraint where available, range locks, next-key locks, or serializable |
| Cross-table report / read-only export | Non-repeatable read, read skew, read-only anomaly | Transaction-level snapshot; PostgreSQL SERIALIZABLE READ ONLY DEFERRABLE when safe snapshot matters |
| Logical critical section not represented by one row | Interleaving across rows/tables or app-level resource | Transaction-scoped advisory lock or explicitly materialized resource; prove every writer uses the same lock key |
| External API call inside transaction | Retry repeats side effect | Move side effect after commit, use transactional outbox, or make side effect idempotent before enabling retrying isolation |
Explicit locks are tools, not magic. SELECT FOR UPDATE, FOR SHARE, SQL Server hints, advisory locks, and guard rows can be correct when they lock the same object or predicate every writer must pass through. They are harmful when they only lock a row that does not represent the invariant, or when they mask a schema constraint that should live in the database.
When Snapshot Isolation permits write skew because two transactions read the same predicate but update disjoint rows, one mitigation is to materialize the conflict into a concrete object every competing transaction must touch. A guard row, parent row, summary row, booking bucket, or invariant table can turn "at least one doctor must remain on call" or "total allocated capacity must not exceed limit" from an invisible predicate conflict into a row-level write-write or lock conflict the engine can detect under a weaker isolation mode.
A concrete shape is: store the invariant under a parent or summary row such as shift(id, on_call_version, on_call_count), and require every transaction that changes a doctor's on-call status to lock or update that shift row before changing the doctor row. The shared row forces the two transactions to contend on the same resource instead of writing disjoint rows based on stale snapshots.
Use this pattern only when the materialized object faithfully represents the invariant and every writer goes through it. If one code path can update the underlying rows without locking or updating the guard, the pattern is false comfort. Prefer a native unique, exclusion, or check constraint when the database can express the invariant directly; use a guard row when the conflict is real but otherwise too diffuse for row locks to catch.
Treat retryable isolation failures as part of the API contract:
40001; PostgreSQL documentation says retry the complete transaction, including decision logic. It can also be appropriate, with care, to retry deadlocks (40P01) or unique/exclusion violations that are really application-level serialization conflicts.serializable_buffers is a tunable SLRU cache size. Under SSI-heavy workloads with SLRU pressure, review it alongside abort rate and retry latency; it is an operational knob, not a substitute for correct isolation or retry design.40001 and restart transaction for retry errors when it cannot safely auto-retry. Serializable is default, but client-side retry handling remains required for multi-statement transactions that cannot be auto-retried.ORA-08177 when a read/write transaction encounters data changed after the transaction began; retry the intended operation or transaction. Oracle ORA-01555 snapshot-too-old means the undo needed for a consistent read is no longer available; address transaction duration, undo retention, or rollback segment sizing rather than blindly retrying as if it were write contention.innodb_lock_wait_timeout produces ERROR 1205 when a row lock wait exceeds the configured timeout; by default only the current statement is rolled back, so decide whether to rollback/retry the whole transaction or handle the blocked operation explicitly.Retry loops need bounded attempts, backoff/jitter under contention, full rollback before retry, and metrics. Never put non-idempotent external effects inside a retryable transaction body unless an outbox or idempotency key makes repeated attempts safe.
Use a language-specific transaction helper if the driver or framework provides one. Otherwise the shape is:
for attempt in 1..max_attempts:
begin transaction with chosen isolation
try:
read the current database state needed for the decision
re-evaluate business rules from those fresh reads
perform writes
commit
return success
catch retryable isolation error:
rollback the whole transaction
sleep with bounded backoff and jitter
continue
return retry_exhaustedDo not retry just the failed statement after a serialization failure unless the engine explicitly documents statement-level retry for that mode. The retry must re-run the reads and decisions that made the write correct. For lock wait timeouts and snapshot-too-old errors, first classify whether the failure is a transient contention victim, a policy timeout, or version-retention/transaction-duration exhaustion; the right fix may be shorter transactions, different lock order, retention tuning, or a narrower lock footprint, not just more attempts.
Replica reads can look like an isolation bug because the application sees "old data," but the mechanism is different. A dirty read is an uncommitted read inside the transaction system; a stale read is a freshness or routing issue across copies. Read-only replicas and hot standbys can expose snapshot semantics at the replica's replay/apply position, and follower reads can deliberately trade freshness for locality. Keep a short note here when the user asks how a replica read interacts with a transaction snapshot, then route the topology, monotonic-read, GTID, failover, and stale-read policy work to replication-patterns.
No current major database feature makes transaction-isolation analysis obsolete. The trend is the opposite: engines have become more capable and more engine-specific.
serializable_buffers, and PostgreSQL 19 beta adds lock-statistics visibility such as pg_stat_lock; these are operational notes, not replacements for anomaly analysis or retry design.After applying this skill, verify:
40001, ORA-08177, MySQL lock wait timeout, Oracle snapshot-too-old, and SQL Server SNAPSHOT update conflicts.replication-patterns owns the next step.serializable_buffers sizing have been checked separately from application retry correctness.| Instead of this skill | Use | Why |
|---|---|---|
| Explaining the broader ACID frame | broader ACID-primer work outside the active skill corpus | ACID fundamentals owns the four-property transaction vocabulary; this skill owns the I axis in depth |
| Reasoning about replica agreement, partition behavior, CP/AP, or PACELC | cap-theorem-tradeoffs | CAP/PACELC is the distributed-systems consistency/availability frame |
| Designing replication topology, read-after-write routing, failover, or stale-replica policy | replication-patterns | Replication owns multi-copy data placement and freshness; this skill owns transaction interleavings |
| Diagnosing a slow query plan | query-optimization | Query optimization owns plan evidence; this skill only owns lock/conflict correctness when concurrency is the cause |
| Designing the maintained index set | indexing-strategy | Indexing owns retrieval structures; this skill may need an index-backed lock range but not the portfolio design |
| Designing schema, constraints, or invariant storage | entity-relationship-modeling | Data modeling owns durable structure; this skill can hand off when a constraint or guard row is the correct defense |
| Coordinating effects across services or multiple transactions | saga/outbox/distributed-transaction skills | Cross-service coordination is above the single database transaction boundary |
serializable_buffers, pg_stat_lock, and current beta/development operational notes; provenance: used only for operational guidance.innodb_lock_wait_timeout. Vendor truth source for InnoDB RR default, consistent reads, gap/next-key locks, RC semi-consistent reads, Serializable SELECT behavior, and lock wait timeout semantics.<!-- skill-graph-context:start (generated — do not edit by hand) -->
Classification
backend-engineeringtrueengineering/dataWhen to use
what isolation level do we need, is read committed enough, what's write skew, MVCC vs locking, Postgres serializable vs MySQL serializable, RCSI vs snapshot isolation, retry SQLSTATE 40001, ORA-08177, SQL Server 3960, SELECT FOR UPDATE or serializableNot for
Related skills
query-optimization, replication-patterns, entity-relationship-modelingcap-theorem-tradeoffs, entity-relationship-modeling, query-optimization, indexing-strategy, replication-patternsConcept
Grounding
universalhttps://www.postgresql.org/docs/current/transaction-iso.html, https://www.postgresql.org/docs/current/mvcc-serialization-failure-handling.html, https://www.postgresql.org/docs/17/release-17.html, https://www.postgresql.org/docs/19/release-19.html, https://dev.mysql.com/doc/refman/8.4/en/innodb-transaction-isolation-levels.html, https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html, https://dev.mysql.com/doc/refman/8.4/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout, https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql, https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-transaction-locking-and-row-versioning-guide, https://learn.microsoft.com/en-us/sql/relational-databases/performance/optimized-locking, https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/data-concurrency-and-consistency.html, https://docs.oracle.com/en/error-help/db/ora-08177/, https://docs.oracle.com/en/error-help/db/ora-01555/, https://www.cockroachlabs.com/docs/stable/transactions, https://www.cockroachlabs.com/docs/stable/read-committed, https://www.cockroachlabs.com/docs/stable/transaction-retry-error-reference, https://docs.cloud.google.com/spanner/docs/isolation-levels, https://docs.cloud.google.com/spanner/docs/transactions, https://github.com/ept/hermitage, https://github.com/jepsen-io/elle, https://jepsen.io/consistency, https://jepsen.io/consistency/models/snapshot-isolation, https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf, https://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-786.pdfKeywords
isolation level, read committed, repeatable read, serializable, snapshot isolation, SSI, MVCC, RCSI, write skew, serialization failure<!-- skill-graph-context:end -->
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.