Back to KB
Difficulty
Intermediate
Read Time
7 min

## [](#what-is-an-atomic-transaction)What Is an Atomic Transaction?

By Codcompass Team··7 min read

Atomic Transactions with Rollback Semantics in Reactive Signal Systems

Current Situation Analysis

Traditional reactive batching mechanisms (batch / transaction) only coalesce effect reruns to reduce computational overhead. They lack rollback semantics, meaning that if an operation fails mid-execution—especially across await boundaries—intermediate state mutations leak to downstream subscribers. This results in:

  • Partial State Commitment: Effects run with inconsistent snapshots, causing UI flicker or invalid application states.
  • Manual Rollover Boilerplate: Developers must manually track pre-operation values and restore them on error, which is error-prone and breaks composability.
  • Async Boundary Fragility: Standard batching doesn't natively handle Promise resolution/rejection, making it impossible to guarantee atomicity across asynchronous workflows.
  • Nested Isolation Failure: Without a dedicated write-log stack, inner transaction failures either corrupt outer state or require complex manual cleanup.

WOW Moment: Key Findings

ApproachFlush FrequencyRollback CapabilityAsync Boundary SafetyIntermediate State LeakageImplementation Complexity
Regular batch/transaction1 on exit❌ None⚠️ Manual handling required🔴 High (leaks on failure)🟢 Low
Manual Pre-Value Tracking1 on exit✅ Yes (manual)⚠️ Error-prone across await🟡 Medium (depends on dev)🔴 High
Atomic Transaction1 on success✅ Automatic🟢 Native Promise support🟢 Zero (strict isolation)🟡 Medium

Key Findings:

  • Atomic transactions guarantee single-flush commitment on success and complete state restoration on failure.
  • The write-log stack architecture enables safe nested transactions without cross-contamination.
  • Lazy recomputation of computed nodes post-rollback eliminates unnecessary synchronous work while maintaining consistency.

Core Solution

The implementation extends the scheduler with a depth-tracked write log, a muted scheduling flag, and explicit commit/rollback pathways. The signal.set() method hooks into the atomic context to record pre-write values only when equality checks pass.

Extending scheduler.ts

import { markStale } from "./computed.js";
import type { Node } from "./graph.js";

export interface Schedulable { run(): void; disposed?: boolean }

// Internal node shape used by signal/computed
export type InternalNode<T = unknown> = { value: T };

// Write log for atomic transactions
type WriteLog = Map<(Node & InternalNode<unknown>), unknown>;

const queue = new Set<Schedulable>();
let scheduled = false;

// > 0 means we are inside batch/transaction mode (delay microtask flushing)
let batchDepth = 0;

// Atomic transaction depth and log stack
let atomicDepth = 0;
const atomicLogs: WriteLog[] = [];

// Mute scheduling during rollback to prevent scheduleJob from creating new work
let muted = 0;

expor

🎉 Mid-Year Sale — Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register — Start Free Trial

7-day free trial · Cancel anytime · 30-day money-back