<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Two-Phase-Commit on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/two-phase-commit/</link><description>Recent content in Two-Phase-Commit on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 06 Sep 2026 09:57:51 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/two-phase-commit/index.xml" rel="self" type="application/rss+xml"/><item><title>2PC vs Saga: Atomic Commit vs Compensating Transactions</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-2pc-vs-saga-atomic-commit-vs-compensating-transactions/</link><pubDate>Sun, 06 Sep 2026 09:57:51 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-2pc-vs-saga-atomic-commit-vs-compensating-transactions/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both patterns coordinate a transaction that spans multiple services or databases, but they resolve the coordination problem in opposite ways. &lt;strong class="kw"&gt;2PC&lt;/strong&gt; locks every participant until a coordinator confirms all can commit, guaranteeing atomicity at the cost of blocking; &lt;strong class="kw"&gt;Saga&lt;/strong&gt; lets each step commit immediately and unwinds failures afterward with compensating actions, trading strict atomicity for availability and throughput.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;defs&gt;&lt;marker id="arrowA" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"&gt;&lt;path d="M0,0L10,5L0,10z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"&gt;&lt;path d="M0,0L10,5L0,10z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;text x="160" y="28" text-anchor="middle" font-size="20" font-weight="bold" style="fill:var(--primary)"&gt;2PC&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" font-size="20" font-weight="bold" style="fill:var(--primary)"&gt;Saga&lt;/text&gt;&lt;line x1="320" y1="40" x2="320" y2="340" stroke-width="1.5" stroke-dasharray="4,4" style="stroke:var(--border)"/&gt;&lt;rect x="90" y="50" width="140" height="36" rx="4" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="160" y="73" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Coordinator&lt;/text&gt;&lt;rect x="40" y="190" width="70" height="40" rx="4" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="75" y="214" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P1&lt;/text&gt;&lt;rect x="130" y="190" width="70" height="40" rx="4" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="165" y="214" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P2&lt;/text&gt;&lt;rect x="220" y="190" width="70" height="40" rx="4" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="255" y="214" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P3&lt;/text&gt;&lt;line x1="140" y1="86" x2="80" y2="190" stroke-width="1.5" marker-start="url(#arrowA)" marker-end="url(#arrowA)" style="stroke:var(--compare-a)"/&gt;&lt;line x1="160" y1="86" x2="165" y2="190" stroke-width="1.5" marker-start="url(#arrowA)" marker-end="url(#arrowA)" style="stroke:var(--compare-a)"/&gt;&lt;line x1="180" y1="86" x2="250" y2="190" stroke-width="1.5" marker-start="url(#arrowA)" marker-end="url(#arrowA)" style="stroke:var(--compare-a)"/&gt;&lt;text x="160" y="135" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;prepare / vote / commit&lt;/text&gt;&lt;rect x="50" y="245" width="50" height="18" rx="3" stroke-width="1" style="fill:none;stroke:var(--border)"/&gt;&lt;text x="75" y="258" text-anchor="middle" font-size="9" style="fill:var(--secondary)"&gt;locked&lt;/text&gt;&lt;rect x="140" y="245" width="50" height="18" rx="3" stroke-width="1" style="fill:none;stroke:var(--border)"/&gt;&lt;text x="165" y="258" text-anchor="middle" font-size="9" style="fill:var(--secondary)"&gt;locked&lt;/text&gt;&lt;rect x="230" y="245" width="50" height="18" rx="3" stroke-width="1" style="fill:none;stroke:var(--border)"/&gt;&lt;text x="255" y="258" text-anchor="middle" font-size="9" style="fill:var(--secondary)"&gt;locked&lt;/text&gt;&lt;text x="160" y="300" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;all-or-nothing, blocking until ack&lt;/text&gt;&lt;rect x="350" y="140" width="70" height="40" rx="4" stroke-width="1.5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="385" y="164" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;T1&lt;/text&gt;&lt;rect x="440" y="140" width="70" height="40" rx="4" stroke-width="1.5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="475" y="164" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;T2&lt;/text&gt;&lt;rect x="530" y="140" width="70" height="40" rx="4" stroke-width="1.5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="565" y="164" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;T3&lt;/text&gt;&lt;line x1="420" y1="160" x2="440" y2="160" stroke-width="1.5" marker-end="url(#arrowB)" style="stroke:var(--compare-b)"/&gt;&lt;line x1="510" y1="160" x2="530" y2="160" stroke-width="1.5" marker-end="url(#arrowB)" style="stroke:var(--compare-b)"/&gt;&lt;text x="480" y="130" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;local commit, local commit&lt;/text&gt;&lt;line x1="530" y1="220" x2="440" y2="220" stroke-width="1.5" stroke-dasharray="5,4" marker-end="url(#arrowB)" style="stroke:var(--compare-b)"/&gt;&lt;line x1="440" y1="240" x2="350" y2="240" stroke-width="1.5" stroke-dasharray="5,4" marker-end="url(#arrowB)" style="stroke:var(--compare-b)"/&gt;&lt;text x="480" y="212" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;compensate&lt;/text&gt;&lt;text x="395" y="257" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;compensate&lt;/text&gt;&lt;text x="480" y="300" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;independent commits, compensating rollbacks&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;2PC&lt;/th&gt;
&lt;th&gt;Saga&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initiation&lt;/td&gt;
&lt;td&gt;Coordinator sends a prepare request to all participants at once&lt;/td&gt;
&lt;td&gt;First service runs its local transaction and triggers the next step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commit decision&lt;/td&gt;
&lt;td&gt;Coordinator waits for every vote, then issues a single atomic commit or abort&lt;/td&gt;
&lt;td&gt;No central decision; each step commits independently as it finishes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource locking&lt;/td&gt;
&lt;td&gt;Participants hold locks from prepare until the commit acknowledgment arrives&lt;/td&gt;
&lt;td&gt;No cross-step locks; each local transaction commits and releases immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Coordinator aborts and tells all participants to roll back the uncommitted work&lt;/td&gt;
&lt;td&gt;Already-committed steps are undone via explicit compensating transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atomicity guarantee&lt;/td&gt;
&lt;td&gt;True all-or-nothing atomicity across every participant&lt;/td&gt;
&lt;td&gt;No real atomicity; intermediate states are visible until compensations finish&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordination dependency&lt;/td&gt;
&lt;td&gt;Single coordinator is a synchronous, blocking point of failure&lt;/td&gt;
&lt;td&gt;Runs via choreography or a lightweight orchestrator that doesn&amp;rsquo;t hold locks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency and throughput&lt;/td&gt;
&lt;td&gt;Higher latency and lower throughput from synchronous cross-service locking&lt;/td&gt;
&lt;td&gt;Lower per-step latency and higher throughput since nothing blocks across services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation effort&lt;/td&gt;
&lt;td&gt;Relies on XA-compliant resources and a transaction manager&lt;/td&gt;
&lt;td&gt;Requires custom compensating logic and step/state tracking per operation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;2PC provides true &lt;strong class="kw"&gt;atomicity&lt;/strong&gt; across services, while Saga only approximates it through compensations after the fact&lt;/li&gt;
&lt;li&gt;2PC holds &lt;strong class="kw"&gt;locks&lt;/strong&gt; on every participant until the coordinator commits; Saga commits each step locally with no cross-service locking&lt;/li&gt;
&lt;li&gt;Saga failures require explicit &lt;strong class="kw"&gt;compensating transactions&lt;/strong&gt;; 2PC failures simply abort the still-uncommitted transaction&lt;/li&gt;
&lt;li&gt;2PC depends on a synchronous &lt;strong class="kw"&gt;coordinator&lt;/strong&gt; that all participants must trust and wait on; Saga can run via choreography or a non-blocking orchestrator&lt;/li&gt;
&lt;li&gt;2PC trades throughput for consistency, while Saga trades strict consistency for &lt;strong class="kw"&gt;availability&lt;/strong&gt; at scale&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;2PC&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>