<?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>Distributed-Systems on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/distributed-systems/</link><description>Recent content in Distributed-Systems on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 06 Sep 2026 10:15:21 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/distributed-systems/index.xml" rel="self" type="application/rss+xml"/><item><title>Single-Region vs Multi-Region: One Deployment Footprint vs Many</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-single-region-vs-multi-region-one-deployment-footprint-vs-ma/</link><pubDate>Sun, 06 Sep 2026 10:15:21 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-single-region-vs-multi-region-one-deployment-footprint-vs-ma/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Single-region deployments run all infrastructure and data in one geographic location, keeping operations simple but exposing the system to regional outages and higher latency for distant users. Multi-region deployments replicate infrastructure and data across multiple geographic locations, trading &lt;strong class="kw"&gt;operational simplicity&lt;/strong&gt; for &lt;strong class="kw"&gt;resilience and locality&lt;/strong&gt;. The right choice depends on your availability targets, compliance needs, and how much complexity your team can absorb.&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;text x="160" y="36" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;Single-Region&lt;/text&gt;&lt;text x="480" y="36" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;Multi-Region&lt;/text&gt;&lt;g&gt;&lt;rect x="40" y="70" width="240" height="230" rx="10" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="4 3"/&gt;&lt;text x="160" y="92" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;us-east-1&lt;/text&gt;&lt;rect x="70" y="110" width="180" height="36" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="133" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Load Balancer&lt;/text&gt;&lt;rect x="70" y="160" width="180" height="36" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="183" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;App Servers&lt;/text&gt;&lt;rect x="70" y="210" width="180" height="36" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="233" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Primary Database&lt;/text&gt;&lt;text x="160" y="270" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Region outage = full downtime&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="340" y="60" width="130" height="120" rx="10" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="405" y="78" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;eu-west-1&lt;/text&gt;&lt;rect x="355" y="90" width="100" height="26" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="405" y="107" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;App Servers&lt;/text&gt;&lt;rect x="355" y="128" width="100" height="26" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="405" y="145" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;DB Replica&lt;/text&gt;&lt;rect x="480" y="60" width="130" height="120" rx="10" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="545" y="78" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;ap-south-1&lt;/text&gt;&lt;rect x="495" y="90" width="100" height="26" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="545" y="107" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;App Servers&lt;/text&gt;&lt;rect x="495" y="128" width="100" height="26" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="545" y="145" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;DB Replica&lt;/text&gt;&lt;rect x="410" y="200" width="130" height="36" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="475" y="223" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Global Router&lt;/text&gt;&lt;line x1="475" y1="200" x2="405" y2="116" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="475" y1="200" x2="545" y2="116" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="405" y1="154" x2="545" y2="154" style="stroke:var(--compare-b)" stroke-width="1" stroke-dasharray="3 3"/&gt;&lt;text x="475" y="170" text-anchor="middle" font-size="9" style="fill:var(--secondary)"&gt;data sync&lt;/text&gt;&lt;text x="475" y="270" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;One region fails, others serve traffic&lt;/text&gt;&lt;/g&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;Single-Region&lt;/th&gt;
&lt;th&gt;Multi-Region&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request entry point&lt;/td&gt;
&lt;td&gt;Single DNS/load balancer target in one region&lt;/td&gt;
&lt;td&gt;Global load balancer or DNS routing to nearest healthy region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data placement&lt;/td&gt;
&lt;td&gt;One primary datastore, one location&lt;/td&gt;
&lt;td&gt;Data replicated or partitioned across regions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency model&lt;/td&gt;
&lt;td&gt;Straightforward strong consistency within one datastore&lt;/td&gt;
&lt;td&gt;Trade-offs between strong and eventual consistency across replicas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency for global users&lt;/td&gt;
&lt;td&gt;High latency for users far from the region&lt;/td&gt;
&lt;td&gt;Low latency via routing to the closest region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure blast radius&lt;/td&gt;
&lt;td&gt;Regional outage takes down the entire system&lt;/td&gt;
&lt;td&gt;Regional outage degrades capacity but other regions keep serving&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment and rollout complexity&lt;/td&gt;
&lt;td&gt;Single pipeline, single environment to manage&lt;/td&gt;
&lt;td&gt;Coordinated rollouts, versioning, and config across regions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost profile&lt;/td&gt;
&lt;td&gt;Lower infrastructure and data transfer cost&lt;/td&gt;
&lt;td&gt;Higher cost from duplicated infrastructure and cross-region transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance and data residency&lt;/td&gt;
&lt;td&gt;Limited to rules of the single region&lt;/td&gt;
&lt;td&gt;Can satisfy data residency laws by keeping data in-region&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;Single-region has one &lt;strong class="kw"&gt;failure domain&lt;/strong&gt;; multi-region isolates failures so an outage in one region doesn&amp;rsquo;t take the whole system down&lt;/li&gt;
&lt;li&gt;Multi-region requires solving &lt;strong class="kw"&gt;data replication&lt;/strong&gt; and consistency across distant datastores, which single-region avoids entirely&lt;/li&gt;
&lt;li&gt;Multi-region cuts &lt;strong class="kw"&gt;latency&lt;/strong&gt; for geographically dispersed users by serving requests from the nearest region&lt;/li&gt;
&lt;li&gt;Multi-region needs a &lt;strong class="kw"&gt;global router&lt;/strong&gt; or DNS-based traffic manager, adding a layer absent in single-region setups&lt;/li&gt;
&lt;li&gt;Operational and infrastructure &lt;strong class="kw"&gt;cost&lt;/strong&gt; scales up sharply with each additional region&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;Single-Region&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Load Shedding vs Request Completion: Rejecting Early vs Finishing In-Flight Work</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-load-shedding-vs-request-completion-rejecting-early-vs-finis/</link><pubDate>Sun, 06 Sep 2026 10:14:11 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-load-shedding-vs-request-completion-rejecting-early-vs-finis/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;When a service is overloaded, it must choose between two competing policies: &lt;strong class="kw"&gt;load shedding&lt;/strong&gt;, which rejects excess requests at the door before they consume resources, and &lt;strong class="kw"&gt;request completion&lt;/strong&gt;, which guarantees every admitted request runs to its natural end. The choice determines whether overload shows up as explicit client-visible rejections or as growing queues and degraded latency for everyone still being served.&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;line x1="320" y1="50" x2="320" y2="320" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;text x="160" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Load Shedding&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Request Completion&lt;/text&gt;&lt;line x1="20" y1="80" x2="95" y2="80" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;line x1="20" y1="130" x2="95" y2="130" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;line x1="20" y1="180" x2="95" y2="180" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;line x1="20" y1="230" x2="95" y2="230" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;rect x="105" y="60" width="30" height="200" style="fill:none;stroke:var(--content)" stroke-width="1.5" stroke-dasharray="3,3"/&gt;&lt;text x="120" y="52" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;gate&lt;/text&gt;&lt;line x1="135" y1="80" x2="200" y2="80" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="135" y1="130" x2="200" y2="130" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="200" y="65" width="90" height="90" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="245" y="115" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Accepted&lt;/text&gt;&lt;line x1="112" y1="173" x2="128" y2="187" style="stroke:var(--secondary)" stroke-width="2"/&gt;&lt;line x1="112" y1="187" x2="128" y2="173" style="stroke:var(--secondary)" stroke-width="2"/&gt;&lt;line x1="112" y1="223" x2="128" y2="237" style="stroke:var(--secondary)" stroke-width="2"/&gt;&lt;line x1="112" y1="237" x2="128" y2="223" style="stroke:var(--secondary)" stroke-width="2"/&gt;&lt;text x="150" y="210" style="fill:var(--secondary)" font-size="11"&gt;shed (503)&lt;/text&gt;&lt;text x="160" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Rejects excess before admission&lt;/text&gt;&lt;line x1="325" y1="100" x2="345" y2="100" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;line x1="325" y1="160" x2="345" y2="160" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;line x1="325" y1="220" x2="345" y2="220" style="stroke:var(--content)" stroke-width="1.5"/&gt;&lt;rect x="345" y="70" width="70" height="180" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="380" y="62" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;admitted&lt;/text&gt;&lt;line x1="415" y1="160" x2="440" y2="160" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="440" y="100" width="80" height="120" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="92" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;in-flight&lt;/text&gt;&lt;line x1="520" y1="160" x2="540" y2="160" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;circle cx="575" cy="160" r="28" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polyline points="562,160 572,170 590,145" style="fill:none;stroke:var(--compare-b)" stroke-width="2.5"/&gt;&lt;text x="575" y="122" text-anchor="middle" style="fill:var(--primary)" font-size="11"&gt;complete&lt;/text&gt;&lt;text x="480" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Every admitted request runs to completion&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;Load Shedding&lt;/th&gt;
&lt;th&gt;Request Completion&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle stage&lt;/td&gt;
&lt;td&gt;Applied at admission, before a request enters processing&lt;/td&gt;
&lt;td&gt;Applied after admission, to work already in flight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;Fires when queue depth, CPU, or latency crosses an overload threshold&lt;/td&gt;
&lt;td&gt;Is the default behavior for any request that was accepted, regardless of load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource cost of the decision&lt;/td&gt;
&lt;td&gt;Cheap — rejects with a fast, minimal-work response&lt;/td&gt;
&lt;td&gt;Expensive — the request&amp;rsquo;s resources are already committed and must be paid out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client-facing outcome&lt;/td&gt;
&lt;td&gt;Explicit rejection (e.g. HTTP 503), client must retry later&lt;/td&gt;
&lt;td&gt;Eventual success or failure on the request&amp;rsquo;s own merits, no artificial cutoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect on accepted traffic&lt;/td&gt;
&lt;td&gt;Protects tail latency for accepted requests by removing excess load&lt;/td&gt;
&lt;td&gt;Risks rising tail latency and queueing as accepted work competes for the same resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prioritization&lt;/td&gt;
&lt;td&gt;Can selectively drop low-priority or cheap-to-reject traffic first&lt;/td&gt;
&lt;td&gt;Typically processes admitted work FIFO, with no mid-flight reordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure mode if misapplied&lt;/td&gt;
&lt;td&gt;Too aggressive shedding rejects healthy capacity and wastes headroom&lt;/td&gt;
&lt;td&gt;No shedding at all leads to resource exhaustion and cascading failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation layer&lt;/td&gt;
&lt;td&gt;Load balancer, API gateway, or admission-control middleware&lt;/td&gt;
&lt;td&gt;Service handler or business logic that owns the request once accepted&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;Load shedding acts at the front door, before &lt;strong class="kw"&gt;admission&lt;/strong&gt;; completion policy governs work already in-flight.&lt;/li&gt;
&lt;li&gt;Shedding trades a guaranteed rejection for protecting &lt;strong class="kw"&gt;tail latency&lt;/strong&gt; of everything else being served.&lt;/li&gt;
&lt;li&gt;Completing every accepted request avoids wasting &lt;strong class="kw"&gt;sunk cost&lt;/strong&gt; already spent, but risks resource exhaustion under sustained overload.&lt;/li&gt;
&lt;li&gt;Shedding can prioritize which traffic to drop, while completion is typically &lt;strong class="kw"&gt;FIFO&lt;/strong&gt; once work is admitted.&lt;/li&gt;
&lt;li&gt;Over-aggressive shedding causes false rejections; refusing to shed at all invites &lt;strong class="kw"&gt;cascading failure&lt;/strong&gt;.&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;Load Shedding&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Deep Queues vs Backpressure: Absorbing Load vs Signaling It Back</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-deep-queues-vs-backpressure-absorbing-load-vs-signaling-it-b/</link><pubDate>Sun, 06 Sep 2026 10:12:17 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-deep-queues-vs-backpressure-absorbing-load-vs-signaling-it-b/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both are strategies for handling load spikes between a fast producer and a slower consumer, but they differ in where the excess work goes. &lt;strong class="kw"&gt;Deep queues&lt;/strong&gt; buffer the overflow in memory or disk so the producer never has to slow down, while &lt;strong class="kw"&gt;backpressure&lt;/strong&gt; pushes a signal upstream so the producer itself throttles before the system gets overwhelmed. The choice determines whether your system trades memory and latency for decoupling, or throughput for bounded stability.&lt;/p&gt;</description></item><item><title>Idempotency vs Simplicity: Safe Retries vs Minimal Design</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-idempotency-vs-simplicity-safe-retries-vs-minimal-design/</link><pubDate>Sun, 06 Sep 2026 10:10:19 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-idempotency-vs-simplicity-safe-retries-vs-minimal-design/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;This compares two competing goals when designing an operation or API: making it safe to repeat (&lt;strong class="kw"&gt;idempotency&lt;/strong&gt;) versus keeping it easy to build and reason about (&lt;strong class="kw"&gt;simplicity&lt;/strong&gt;). The tension matters because guarding against duplicate execution almost always adds state and logic that a minimal implementation would otherwise skip.&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;line x1="320" y1="10" x2="320" y2="350" stroke-dasharray="4,4" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="160" y="26" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Idempotency&lt;/text&gt;&lt;text x="480" y="26" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Simplicity&lt;/text&gt;&lt;rect x="110" y="45" width="100" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="65" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;line x1="140" y1="77" x2="140" y2="135" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;line x1="180" y1="77" x2="180" y2="135" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="108" y="106" font-size="9" style="fill:var(--secondary)"&gt;req #1&lt;/text&gt;&lt;text x="183" y="106" font-size="9" style="fill:var(--secondary)"&gt;retry&lt;/text&gt;&lt;rect x="100" y="138" width="120" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="158" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Server&lt;/text&gt;&lt;line x1="160" y1="170" x2="160" y2="195" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="85" y="195" width="150" height="34" rx="4" style="fill:none;stroke:var(--compare-a)" stroke-dasharray="3,3" stroke-width="1.5"/&gt;&lt;text x="160" y="216" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;key store: A seen&lt;/text&gt;&lt;line x1="160" y1="229" x2="160" y2="268" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="105" y="270" width="110" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="290" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;executed once&lt;/text&gt;&lt;text x="160" y="320" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;retries collapse to one result&lt;/text&gt;&lt;rect x="430" y="45" width="100" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="65" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;line x1="460" y1="77" x2="460" y2="135" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="500" y1="77" x2="500" y2="135" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="428" y="106" font-size="9" style="fill:var(--secondary)"&gt;req #1&lt;/text&gt;&lt;text x="503" y="106" font-size="9" style="fill:var(--secondary)"&gt;retry&lt;/text&gt;&lt;rect x="420" y="138" width="120" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="158" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Server&lt;/text&gt;&lt;line x1="460" y1="170" x2="430" y2="220" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="500" y1="170" x2="530" y2="220" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;rect x="380" y="222" width="100" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="430" y="242" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;executed&lt;/text&gt;&lt;rect x="480" y="222" width="100" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="530" y="242" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;executed again&lt;/text&gt;&lt;text x="480" y="290" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;retries run twice, no dedup&lt;/text&gt;&lt;text x="480" y="320" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;no key store, fewer moving parts&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;Idempotency&lt;/th&gt;
&lt;th&gt;Simplicity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Design intent&lt;/td&gt;
&lt;td&gt;Guarantee repeated execution has the same effect as one execution&lt;/td&gt;
&lt;td&gt;Minimize the number of moving parts and decisions in the implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handling duplicate requests&lt;/td&gt;
&lt;td&gt;Detects and ignores repeats using an idempotency key or natural key&lt;/td&gt;
&lt;td&gt;Processes each incoming request as new, with no duplicate detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State required&lt;/td&gt;
&lt;td&gt;Needs a dedup store (key, result, TTL) to remember prior executions&lt;/td&gt;
&lt;td&gt;Stateless with respect to prior calls, nothing extra to persist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior on client retry&lt;/td&gt;
&lt;td&gt;Safe to retry any number of times; result is unchanged&lt;/td&gt;
&lt;td&gt;Retry re-runs the operation, risking duplicate side effects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;td&gt;Callers can blindly retry after timeouts without side-effect risk&lt;/td&gt;
&lt;td&gt;Callers must add their own checks before retrying after a failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation cost&lt;/td&gt;
&lt;td&gt;Extra code for key generation, storage, locking, and expiry&lt;/td&gt;
&lt;td&gt;Fewer edge cases, less code, faster to build and review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing burden&lt;/td&gt;
&lt;td&gt;Must cover concurrent duplicates, race conditions, and key expiry&lt;/td&gt;
&lt;td&gt;Test surface limited to the core logic path, no dedup scenarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best-fit workloads&lt;/td&gt;
&lt;td&gt;Payments, distributed queues, webhooks, multi-step workflows&lt;/td&gt;
&lt;td&gt;Internal read-only endpoints, prototypes, low-stakes single-writer ops&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;&lt;strong class="kw"&gt;Idempotency&lt;/strong&gt; trades extra state for safety, &lt;strong class="kw"&gt;simplicity&lt;/strong&gt; trades safety for fewer parts&lt;/li&gt;
&lt;li&gt;Idempotent operations rely on a &lt;strong class="kw"&gt;dedup key&lt;/strong&gt; that a simple implementation has no reason to store&lt;/li&gt;
&lt;li&gt;Simplicity pushes retry-safety responsibility onto the &lt;strong class="kw"&gt;caller&lt;/strong&gt; instead of the server&lt;/li&gt;
&lt;li&gt;Idempotency adds &lt;strong class="kw"&gt;testing surface&lt;/strong&gt; for concurrency and expiry that simple code avoids entirely&lt;/li&gt;
&lt;li&gt;The right choice depends on whether duplicate &lt;strong class="kw"&gt;side effects&lt;/strong&gt; are tolerable for the operation&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;Idempotency&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Retries vs Load Amplification: Resilience Tactic vs Its Failure Mode</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-retries-vs-load-amplification-resilience-tactic-vs-its-failu/</link><pubDate>Sun, 06 Sep 2026 10:09:08 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-retries-vs-load-amplification-resilience-tactic-vs-its-failu/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;strong class="kw"&gt;Retries&lt;/strong&gt; mask transient failures by having a client re-attempt a request that timed out or errored, trading latency for reliability. &lt;strong class="kw"&gt;Load amplification&lt;/strong&gt; is what happens when those same retries compound: a struggling downstream service receives multiplied traffic from many clients retrying at once, turning a partial slowdown into a full outage. The design challenge is keeping the first from causing the second.&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,0 L10,5 L0,10 z" 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,0 L10,5 L0,10 z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4,4"/&gt;&lt;text x="160" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Retries&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Load Amplification&lt;/text&gt;&lt;circle cx="100" cy="80" r="26" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="100" y="85" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Client&lt;/text&gt;&lt;rect x="60" y="280" width="80" height="42" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="100" y="305" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Service&lt;/text&gt;&lt;line x1="90" y1="104" x2="70" y2="280" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="20" y="175" style="fill:var(--secondary)" font-size="10"&gt;req 1 (timeout)&lt;/text&gt;&lt;line x1="110" y1="104" x2="130" y2="280" style="stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#arrowA)"/&gt;&lt;text x="128" y="195" style="fill:var(--secondary)" font-size="10"&gt;retry (backoff+jitter)&lt;/text&gt;&lt;text x="100" y="345" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;1 extra request, delayed&lt;/text&gt;&lt;circle cx="400" cy="70" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="400" y="74" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;C1&lt;/text&gt;&lt;circle cx="480" cy="58" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="62" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;C2&lt;/text&gt;&lt;circle cx="560" cy="70" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="560" y="74" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;C3&lt;/text&gt;&lt;rect x="440" y="258" width="90" height="14" style="fill:none;stroke:var(--border)" stroke-width="1"/&gt;&lt;rect x="432" y="270" width="106" height="14" style="fill:none;stroke:var(--border)" stroke-width="1"/&gt;&lt;rect x="424" y="282" width="122" height="14" style="fill:none;stroke:var(--border)" stroke-width="1"/&gt;&lt;rect x="440" y="298" width="90" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2.5"/&gt;&lt;text x="485" y="320" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Service&lt;/text&gt;&lt;line x1="393" y1="90" x2="460" y2="298" style="stroke:var(--compare-b)" stroke-width="1.2" marker-end="url(#arrowB)"/&gt;&lt;line x1="405" y1="90" x2="470" y2="298" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrowB)"/&gt;&lt;line x1="477" y1="78" x2="483" y2="298" style="stroke:var(--compare-b)" stroke-width="1.2" marker-end="url(#arrowB)"/&gt;&lt;line x1="485" y1="78" x2="495" y2="298" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrowB)"/&gt;&lt;line x1="555" y1="90" x2="505" y2="298" style="stroke:var(--compare-b)" stroke-width="1.2" marker-end="url(#arrowB)"/&gt;&lt;line x1="565" y1="90" x2="515" y2="298" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrowB)"/&gt;&lt;text x="485" y="345" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;6x request volume, queue growing&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;Retries&lt;/th&gt;
&lt;th&gt;Load Amplification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;A single failed or timed-out request at the client&lt;/td&gt;
&lt;td&gt;Many clients (or one client&amp;rsquo;s retries) hitting an already degraded service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nature&lt;/td&gt;
&lt;td&gt;Deliberate resilience mechanism&lt;/td&gt;
&lt;td&gt;Emergent side effect of that mechanism under stress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Per-request, client-local decision&lt;/td&gt;
&lt;td&gt;Fleet-wide, system-level consequence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing pattern&lt;/td&gt;
&lt;td&gt;Delayed re-attempt, ideally with exponential backoff and jitter&lt;/td&gt;
&lt;td&gt;Requests pile up faster than the service can drain them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect on downstream load&lt;/td&gt;
&lt;td&gt;Small, bounded increase of one extra attempt&lt;/td&gt;
&lt;td&gt;Multiplicative increase, often several times baseline traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worst-case outcome&lt;/td&gt;
&lt;td&gt;Slightly higher latency for the caller&lt;/td&gt;
&lt;td&gt;Cascading failure or full outage from a retry storm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mitigation&lt;/td&gt;
&lt;td&gt;Retry budgets, idempotency keys, capped attempt counts&lt;/td&gt;
&lt;td&gt;Circuit breakers, load shedding, backpressure, rate limiting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability signal&lt;/td&gt;
&lt;td&gt;Retry count and retry rate per endpoint&lt;/td&gt;
&lt;td&gt;Request rate vs baseline, queue depth, error rate spike&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;&lt;strong class="kw"&gt;Retries&lt;/strong&gt; are a client-side decision; load amplification is a system-wide consequence that emerges when many retries overlap&lt;/li&gt;
&lt;li&gt;A single retry adds one extra attempt, but a &lt;strong class="kw"&gt;retry storm&lt;/strong&gt; can multiply traffic several times over in seconds&lt;/li&gt;
&lt;li&gt;&lt;strong class="kw"&gt;Exponential backoff&lt;/strong&gt; with jitter reduces retry-driven amplification by spreading re-attempts over time instead of synchronizing them&lt;/li&gt;
&lt;li&gt;Amplification is contained with &lt;strong class="kw"&gt;circuit breakers&lt;/strong&gt; and load shedding at the service, not by removing retries entirely&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;Retries&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Short vs Long Timeouts: Failing Fast vs Tolerating Slowness</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-short-vs-long-timeouts-failing-fast-vs-tolerating-slowness/</link><pubDate>Sun, 06 Sep 2026 10:07:39 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-short-vs-long-timeouts-failing-fast-vs-tolerating-slowness/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Timeouts define how long a caller waits for a response before giving up, and the duration you pick trades off resource protection against tolerance for slow-but-valid work. &lt;strong class="kw"&gt;Short timeouts&lt;/strong&gt; fail fast and protect callers from cascading slowness, while &lt;strong class="kw"&gt;long timeouts&lt;/strong&gt; give operations more room to complete under load or over slow links at the cost of holding resources longer.&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;text x="160" y="40" text-anchor="middle" font-size="20" style="fill:var(--primary)"&gt;Short Timeout&lt;/text&gt;&lt;text x="480" y="40" text-anchor="middle" font-size="20" style="fill:var(--primary)"&gt;Long Timeout&lt;/text&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;circle cx="60" cy="100" r="14" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="60" y="104" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Call&lt;/text&gt;&lt;line x1="74" y1="100" x2="200" y2="100" style="stroke:var(--compare-a)" stroke-width="2" marker-end="url(#arrowA)"/&gt;&lt;rect x="200" y="85" width="60" height="30" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="230" y="104" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Server&lt;/text&gt;&lt;line x1="70" y1="140" x2="200" y2="140" style="stroke:var(--compare-a)" stroke-width="3"/&gt;&lt;text x="135" y="160" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;wait window: 200ms&lt;/text&gt;&lt;path d="M200 140 L188 134 L188 146 Z" style="fill:var(--compare-a)"/&gt;&lt;rect x="210" y="180" width="90" height="36" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="255" y="202" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Timeout error&lt;/text&gt;&lt;text x="255" y="235" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;fails fast, retries quickly&lt;/text&gt;&lt;circle cx="380" cy="100" r="14" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="380" y="104" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Call&lt;/text&gt;&lt;line x1="394" y1="100" x2="520" y2="100" style="stroke:var(--compare-b)" stroke-width="2" marker-end="url(#arrowB)"/&gt;&lt;rect x="520" y="85" width="60" height="30" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="550" y="104" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Server&lt;/text&gt;&lt;line x1="390" y1="140" x2="560" y2="140" style="stroke:var(--compare-b)" stroke-width="3"/&gt;&lt;text x="475" y="160" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;wait window: 30s&lt;/text&gt;&lt;rect x="495" y="180" width="90" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="540" y="202" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Response arrives&lt;/text&gt;&lt;text x="540" y="235" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;tolerates slow work&lt;/text&gt;&lt;line x1="40" y1="270" x2="280" y2="270" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="160" y="290" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;frees threads/connections sooner&lt;/text&gt;&lt;text x="160" y="308" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;risk: false failures under load&lt;/text&gt;&lt;line x1="360" y1="270" x2="600" y2="270" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="480" y="290" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;holds resources longer per call&lt;/text&gt;&lt;text x="480" y="308" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;risk: cascading pileup/exhaustion&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&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;Short Timeout&lt;/th&gt;
&lt;th&gt;Long Timeout&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request initiation&lt;/td&gt;
&lt;td&gt;Caller sets an aggressive deadline immediately on send&lt;/td&gt;
&lt;td&gt;Caller allows a generous window before send returns control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior under normal latency&lt;/td&gt;
&lt;td&gt;Succeeds well within budget, negligible overhead&lt;/td&gt;
&lt;td&gt;Succeeds with unused slack, no functional difference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior under slow dependency&lt;/td&gt;
&lt;td&gt;Aborts before slow-but-valid work finishes, causing false failures&lt;/td&gt;
&lt;td&gt;Waits out transient slowness, letting valid work complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource holding&lt;/td&gt;
&lt;td&gt;Frees threads, sockets, and connection pool slots quickly&lt;/td&gt;
&lt;td&gt;Ties up threads, sockets, and pool slots for the full wait&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure propagation&lt;/td&gt;
&lt;td&gt;Fails fast, enabling quick retry or fallback logic&lt;/td&gt;
&lt;td&gt;Delays failure detection, slowing retries and fallback triggers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System behavior under overload&lt;/td&gt;
&lt;td&gt;Sheds load quickly, protecting upstream and downstream services&lt;/td&gt;
&lt;td&gt;Risks thread/connection exhaustion and cascading backpressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry and circuit breaker interaction&lt;/td&gt;
&lt;td&gt;Pairs well with fast retries and quick breaker tripping&lt;/td&gt;
&lt;td&gt;Delays breaker tripping, masking degradation until timeout expires&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tuning basis&lt;/td&gt;
&lt;td&gt;Set near p99 latency of a healthy, fast dependency&lt;/td&gt;
&lt;td&gt;Set to cover legitimate worst-case work like batch jobs or large payloads&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;A &lt;strong class="kw"&gt;short timeout&lt;/strong&gt; favors quick failure detection over completing genuinely slow requests&lt;/li&gt;
&lt;li&gt;A &lt;strong class="kw"&gt;long timeout&lt;/strong&gt; risks &lt;strong class="kw"&gt;resource exhaustion&lt;/strong&gt; when many calls stall simultaneously&lt;/li&gt;
&lt;li&gt;Short timeouts pair naturally with &lt;strong class="kw"&gt;fast retries&lt;/strong&gt;, while long timeouts delay circuit breaker activation&lt;/li&gt;
&lt;li&gt;Choosing either wrong direction turns normal &lt;strong class="kw"&gt;latency variance&lt;/strong&gt; into either false failures or cascading pileups&lt;/li&gt;
&lt;li&gt;The right value depends on the dependency&amp;rsquo;s actual &lt;strong class="kw"&gt;p99 latency&lt;/strong&gt;, not a guessed constant&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;Short Timeout&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Sync vs Async APIs: Blocking Calls vs Non-Blocking Callbacks</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-sync-vs-async-apis-blocking-calls-vs-non-blocking-callbacks/</link><pubDate>Sun, 06 Sep 2026 10:05:37 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-sync-vs-async-apis-blocking-calls-vs-non-blocking-callbacks/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A &lt;strong class="kw"&gt;synchronous call&lt;/strong&gt; blocks the caller until the server returns a result, tying up a thread or connection for the full round trip. An &lt;strong class="kw"&gt;asynchronous call&lt;/strong&gt; returns immediately with an acknowledgment and delivers the actual result later via a callback, event, or poll, letting the caller do other work in the meantime.&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;text x="320" y="40" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Synchronous API&lt;/text&gt;&lt;text x="60" y="99" font-size="13" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;text x="320" y="80" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;blocked - thread waits&lt;/text&gt;&lt;line x1="100" y1="100" x2="580" y2="100" stroke-width="2" style="stroke:var(--compare-a)"/&gt;&lt;polygon points="580,94 592,100 580,106" style="fill:var(--compare-a)"/&gt;&lt;rect x="220" y="92" width="200" height="16" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;circle cx="220" cy="100" r="5" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;circle cx="420" cy="100" r="5" stroke-width="1.5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="220" y="122" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;request sent&lt;/text&gt;&lt;text x="420" y="122" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;response received&lt;/text&gt;&lt;line x1="220" y1="106" x2="220" y2="135" stroke-width="1" stroke-dasharray="3 3" style="stroke:var(--border)"/&gt;&lt;line x1="420" y1="106" x2="420" y2="135" stroke-width="1" stroke-dasharray="3 3" style="stroke:var(--border)"/&gt;&lt;rect x="220" y="135" width="200" height="10" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:var(--border)"/&gt;&lt;text x="320" y="161" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;server processing&lt;/text&gt;&lt;line x1="40" y1="188" x2="600" y2="188" stroke-width="1" stroke-dasharray="4 4" style="stroke:var(--border)"/&gt;&lt;text x="320" y="213" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Asynchronous API&lt;/text&gt;&lt;text x="60" y="259" font-size="13" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;rect x="250" y="233" width="140" height="18" stroke-width="1" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="320" y="246" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;client free: other work&lt;/text&gt;&lt;line x1="100" y1="260" x2="580" y2="260" stroke-width="2" style="stroke:var(--compare-b)"/&gt;&lt;polygon points="580,254 592,260 580,266" style="fill:var(--compare-b)"/&gt;&lt;circle cx="220" cy="260" r="5" stroke-width="1.5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;circle cx="420" cy="260" r="5" stroke-width="1.5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="220" y="283" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;request sent&lt;/text&gt;&lt;text x="420" y="283" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;callback received&lt;/text&gt;&lt;line x1="220" y1="266" x2="220" y2="300" stroke-width="1" stroke-dasharray="3 3" style="stroke:var(--border)"/&gt;&lt;line x1="420" y1="266" x2="420" y2="300" stroke-width="1" stroke-dasharray="3 3" style="stroke:var(--border)"/&gt;&lt;rect x="220" y="300" width="200" height="10" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:var(--border)"/&gt;&lt;text x="320" y="326" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;server working&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;Sync API&lt;/th&gt;
&lt;th&gt;Async API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request initiation&lt;/td&gt;
&lt;td&gt;Caller invokes and immediately awaits the result on the same call&lt;/td&gt;
&lt;td&gt;Caller invokes and gets an immediate acknowledgment or handle (future, promise, message ID), not the result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response delivery&lt;/td&gt;
&lt;td&gt;Result returned in-line over the same connection/thread that made the call&lt;/td&gt;
&lt;td&gt;Result delivered later via callback, event, webhook, or by polling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caller behavior while waiting&lt;/td&gt;
&lt;td&gt;Thread or connection is blocked and cannot do other work&lt;/td&gt;
&lt;td&gt;Caller is free to continue other work or serve other requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency model&lt;/td&gt;
&lt;td&gt;Needs roughly one thread or connection per in-flight call&lt;/td&gt;
&lt;td&gt;A single thread or event loop can multiplex many in-flight calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Errors surface immediately as exceptions or status codes at the call site&lt;/td&gt;
&lt;td&gt;Errors arrive out-of-band later and must be matched back to the original request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordering and sequencing&lt;/td&gt;
&lt;td&gt;Strict: caller code executes in the exact order calls complete&lt;/td&gt;
&lt;td&gt;Responses can arrive out of order, requiring correlation IDs to reassemble sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency impact on caller&lt;/td&gt;
&lt;td&gt;Caller&amp;rsquo;s total latency equals the full round trip&lt;/td&gt;
&lt;td&gt;Caller&amp;rsquo;s perceived latency is just the time to ack; real work overlaps with other tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation complexity&lt;/td&gt;
&lt;td&gt;Simpler code: straightforward call and return&lt;/td&gt;
&lt;td&gt;More complex: needs callback/promise/event handling and explicit state tracking&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;Sync calls &lt;strong class="kw"&gt;block&lt;/strong&gt; the caller until the response arrives, while async calls return control immediately.&lt;/li&gt;
&lt;li&gt;Async APIs scale better under load because they avoid &lt;strong class="kw"&gt;thread-per-request&lt;/strong&gt; limits inherent to blocking calls.&lt;/li&gt;
&lt;li&gt;Sync errors surface in-line at the call site; async errors require &lt;strong class="kw"&gt;correlation&lt;/strong&gt; back to the original request.&lt;/li&gt;
&lt;li&gt;Async responses can arrive out of order, adding &lt;strong class="kw"&gt;sequencing&lt;/strong&gt; complexity that sync calls never face.&lt;/li&gt;
&lt;li&gt;Sync code is easier to trace and debug since execution follows a single linear &lt;strong class="kw"&gt;call stack&lt;/strong&gt;.&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;Sync API&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Push vs Pull: Who Initiates the Data Transfer</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-push-vs-pull-who-initiates-the-data-transfer/</link><pubDate>Sun, 06 Sep 2026 10:02:10 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-push-vs-pull-who-initiates-the-data-transfer/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Push and pull describe which side initiates a data transfer between two systems: in a &lt;strong class="kw"&gt;push&lt;/strong&gt; model the source sends data as soon as it&amp;rsquo;s ready, while in a &lt;strong class="kw"&gt;pull&lt;/strong&gt; model the consumer requests data on its own schedule. The choice shapes latency, backpressure handling, and how tightly the two sides are coupled in time.&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;text x="160" y="36" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;Push&lt;/text&gt;&lt;text x="480" y="36" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;Pull&lt;/text&gt;&lt;rect x="60" y="70" width="110" height="56" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="115" y="103" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Source&lt;/text&gt;&lt;rect x="60" y="200" width="110" height="56" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="115" y="233" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Consumer&lt;/text&gt;&lt;line x1="115" y1="126" x2="115" y2="200" style="stroke:var(--compare-a)" stroke-width="2" marker-end="url(#arrowA)"/&gt;&lt;text x="140" y="165" font-size="11" style="fill:var(--secondary)"&gt;sends data&lt;/text&gt;&lt;text x="140" y="178" font-size="11" style="fill:var(--secondary)"&gt;when ready&lt;/text&gt;&lt;circle cx="115" cy="85" r="4" style="fill:var(--compare-a)"/&gt;&lt;rect x="380" y="70" width="110" height="56" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="435" y="103" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Source&lt;/text&gt;&lt;rect x="380" y="200" width="110" height="56" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="435" y="233" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Consumer&lt;/text&gt;&lt;line x1="435" y1="200" x2="435" y2="126" style="stroke:var(--compare-b)" stroke-width="2" marker-end="url(#arrowB)"/&gt;&lt;text x="460" y="165" font-size="11" style="fill:var(--secondary)"&gt;requests data&lt;/text&gt;&lt;text x="460" y="178" font-size="11" style="fill:var(--secondary)"&gt;on its schedule&lt;/text&gt;&lt;line x1="120" y1="126" x2="120" y2="126" style="stroke:var(--border)"/&gt;&lt;text x="115" y="280" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Source controls timing&lt;/text&gt;&lt;text x="435" y="280" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Consumer controls timing&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&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;Push&lt;/th&gt;
&lt;th&gt;Pull&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initiator&lt;/td&gt;
&lt;td&gt;Source system triggers the transfer&lt;/td&gt;
&lt;td&gt;Consumer system triggers the transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing control&lt;/td&gt;
&lt;td&gt;Source decides when data is sent&lt;/td&gt;
&lt;td&gt;Consumer decides when to fetch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency to consumer&lt;/td&gt;
&lt;td&gt;Near-immediate once source has data&lt;/td&gt;
&lt;td&gt;Bounded by polling interval, not source readiness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backpressure handling&lt;/td&gt;
&lt;td&gt;Source must slow down or buffer if consumer is overwhelmed&lt;/td&gt;
&lt;td&gt;Consumer naturally paces itself by requesting only when ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coupling&lt;/td&gt;
&lt;td&gt;Source needs to know consumer&amp;rsquo;s address/endpoint&lt;/td&gt;
&lt;td&gt;Consumer needs to know source&amp;rsquo;s address/endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource cost when idle&lt;/td&gt;
&lt;td&gt;No wasted work; nothing sent if no updates&lt;/td&gt;
&lt;td&gt;Repeated requests even when nothing changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Source retries or queues if delivery fails&lt;/td&gt;
&lt;td&gt;Consumer retries the pull on its own next cycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical mechanisms&lt;/td&gt;
&lt;td&gt;Webhooks, pub/sub, server-sent events&lt;/td&gt;
&lt;td&gt;Polling, cron jobs, request/response APIs&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;&lt;strong class="kw"&gt;Push&lt;/strong&gt; minimizes latency by sending data the instant it&amp;rsquo;s available, while &lt;strong class="kw"&gt;pull&lt;/strong&gt; bounds latency to the polling interval.&lt;/li&gt;
&lt;li&gt;Pull gives the consumer natural &lt;strong class="kw"&gt;backpressure&lt;/strong&gt; control since it only asks for data when ready to process it.&lt;/li&gt;
&lt;li&gt;Push requires the source to hold a reference to every consumer&amp;rsquo;s endpoint, increasing &lt;strong class="kw"&gt;fan-out coupling&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Pull wastes resources on &lt;strong class="kw"&gt;empty polls&lt;/strong&gt; when there&amp;rsquo;s nothing new to fetch.&lt;/li&gt;
&lt;li&gt;Push systems need retry or queueing logic on the sender side; pull systems just retry the request on the next cycle.&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;Push&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Total Ordering vs Partitioned Ordering: One Global Sequence vs Per-Key Order</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-total-ordering-vs-partitioned-ordering-one-global-sequence-v/</link><pubDate>Sun, 06 Sep 2026 10:01:47 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-total-ordering-vs-partitioned-ordering-one-global-sequence-v/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;In distributed logs and message queues, &lt;strong class="kw"&gt;total ordering&lt;/strong&gt; guarantees every event in the system is seen in one single sequence, while &lt;strong class="kw"&gt;partitioned ordering&lt;/strong&gt; only guarantees order within each partition or key, letting unrelated events interleave freely. The choice trades a single-writer bottleneck for horizontal scalability, and it directly determines how strong an ordering guarantee downstream consumers can rely on.&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;line x1="320" y1="45" x2="320" y2="345" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4,4"/&gt;&lt;text x="160" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="17" font-weight="bold"&gt;Total Ordering&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="17" font-weight="bold"&gt;Partitioned Ordering&lt;/text&gt;&lt;circle cx="80" cy="55" r="8" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;circle cx="160" cy="55" r="8" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;circle cx="240" cy="55" r="8" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="80" y1="63" x2="140" y2="92" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;line x1="160" y1="63" x2="160" y2="92" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;line x1="240" y1="63" x2="180" y2="92" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;rect x="30" y="95" width="40" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="50" y="117" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;1&lt;/text&gt;&lt;rect x="76" y="95" width="40" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="96" y="117" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;2&lt;/text&gt;&lt;rect x="122" y="95" width="40" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="142" y="117" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;3&lt;/text&gt;&lt;rect x="168" y="95" width="40" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="188" y="117" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;4&lt;/text&gt;&lt;rect x="214" y="95" width="40" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="234" y="117" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;5&lt;/text&gt;&lt;rect x="260" y="95" width="40" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="280" y="117" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;6&lt;/text&gt;&lt;line x1="160" y1="129" x2="160" y2="168" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,178 154,166 166,166" style="fill:var(--compare-a)"/&gt;&lt;rect x="100" y="180" width="120" height="34" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="202" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Consumer&lt;/text&gt;&lt;text x="160" y="250" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;One sequence: 1 to 2 to 3 to 4 to 5 to 6&lt;/text&gt;&lt;text x="160" y="268" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;All producers merge into a single ordered log&lt;/text&gt;&lt;text x="345" y="76" style="fill:var(--primary)" font-size="13" font-weight="bold"&gt;P1&lt;/text&gt;&lt;rect x="380" y="60" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="395" y="80" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;1&lt;/text&gt;&lt;rect x="416" y="60" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="431" y="80" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;2&lt;/text&gt;&lt;rect x="452" y="60" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="467" y="80" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;3&lt;/text&gt;&lt;line x1="482" y1="75" x2="516" y2="75" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="524,75 514,70 514,80" style="fill:var(--compare-b)"/&gt;&lt;rect x="526" y="60" width="70" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="561" y="80" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;C1&lt;/text&gt;&lt;text x="345" y="161" style="fill:var(--primary)" font-size="13" font-weight="bold"&gt;P2&lt;/text&gt;&lt;rect x="380" y="145" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="395" y="165" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;1&lt;/text&gt;&lt;rect x="416" y="145" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="431" y="165" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;2&lt;/text&gt;&lt;rect x="452" y="145" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="467" y="165" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;3&lt;/text&gt;&lt;line x1="482" y1="160" x2="516" y2="160" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="524,160 514,155 514,165" style="fill:var(--compare-b)"/&gt;&lt;rect x="526" y="145" width="70" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="561" y="165" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;C2&lt;/text&gt;&lt;text x="345" y="246" style="fill:var(--primary)" font-size="13" font-weight="bold"&gt;P3&lt;/text&gt;&lt;rect x="380" y="230" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="395" y="250" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;1&lt;/text&gt;&lt;rect x="416" y="230" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="431" y="250" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;2&lt;/text&gt;&lt;rect x="452" y="230" width="30" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="467" y="250" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;3&lt;/text&gt;&lt;line x1="482" y1="245" x2="516" y2="245" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="524,245 514,240 514,250" style="fill:var(--compare-b)"/&gt;&lt;rect x="526" y="230" width="70" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="561" y="250" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;C3&lt;/text&gt;&lt;text x="480" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Order guaranteed only within each partition&lt;/text&gt;&lt;text x="480" y="318" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;No ordering guarantee across P1, P2, P3&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;Total Ordering&lt;/th&gt;
&lt;th&gt;Partitioned Ordering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ordering scope&lt;/td&gt;
&lt;td&gt;Every event in the system shares one single global sequence&lt;/td&gt;
&lt;td&gt;Order guaranteed only among events sharing the same partition or key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How order is assigned&lt;/td&gt;
&lt;td&gt;A single sequencer, leader, or log appends events one at a time&lt;/td&gt;
&lt;td&gt;A partitioner (e.g. hash of key) routes events into independent per-partition logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write throughput&lt;/td&gt;
&lt;td&gt;Bounded by the single serialization point; writes cannot be parallelized&lt;/td&gt;
&lt;td&gt;Scales horizontally, since each partition accepts writes independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer guarantee&lt;/td&gt;
&lt;td&gt;Any consumer reading the full stream sees an identical event order&lt;/td&gt;
&lt;td&gt;A consumer only sees ordered events within the partitions it reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-entity relationships&lt;/td&gt;
&lt;td&gt;Causal relationships between unrelated entities are preserved&lt;/td&gt;
&lt;td&gt;Events for different keys can arrive interleaved or out of relative order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect of adding capacity&lt;/td&gt;
&lt;td&gt;Adding nodes doesn&amp;rsquo;t help; throughput stays capped by the single stream&lt;/td&gt;
&lt;td&gt;Adding partitions increases throughput, but existing key-to-partition mapping must stay stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure behavior&lt;/td&gt;
&lt;td&gt;Sequencer or leader failure stalls or requires careful recovery to preserve order&lt;/td&gt;
&lt;td&gt;A failed partition affects only its own keys; other partitions keep processing&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;Total ordering guarantees a single &lt;strong class="kw"&gt;global sequence&lt;/strong&gt;; partitioned ordering guarantees order only &lt;strong class="kw"&gt;per key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Total order requires a &lt;strong class="kw"&gt;single writer&lt;/strong&gt; or sequencer, capping throughput, while partitioned order enables &lt;strong class="kw"&gt;parallel writes&lt;/strong&gt; across partitions.&lt;/li&gt;
&lt;li&gt;Partitioned ordering scales by adding &lt;strong class="kw"&gt;more partitions&lt;/strong&gt;; scaling total order needs a fundamentally different design.&lt;/li&gt;
&lt;li&gt;A sequencer failure threatens the entire order in total ordering, while failure in partitioned ordering is &lt;strong class="kw"&gt;isolated per partition&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Total ordering preserves causality between unrelated entities; partitioned ordering only preserves it within the same &lt;strong class="kw"&gt;partition key&lt;/strong&gt;.&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;Total Ordering&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>At-Least-Once vs Exactly-Once: Delivery Guarantees Compared</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-at-least-once-vs-exactly-once-delivery-guarantees-compared/</link><pubDate>Sun, 06 Sep 2026 09:59:45 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-at-least-once-vs-exactly-once-delivery-guarantees-compared/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Messaging and stream-processing systems must pick a delivery guarantee: does a message arrive at least one time (possibly more), or does its effect happen precisely once no matter how many retries occur? &lt;strong class="kw"&gt;At-least-once&lt;/strong&gt; favors simplicity and throughput by retrying until acknowledged, at the cost of possible duplicates; &lt;strong class="kw"&gt;exactly-once&lt;/strong&gt; layers on deduplication or transactional coordination so retries never produce a second effect. The choice matters because a duplicate side effect — a double charge, a double email, a double stock decrement — can be catastrophic or merely annoying depending on the domain.&lt;/p&gt;</description></item><item><title>Choreography vs Orchestration: Who Drives the Workflow</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-choreography-vs-orchestration-who-drives-the-workflow/</link><pubDate>Sun, 06 Sep 2026 09:56:16 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-choreography-vs-orchestration-who-drives-the-workflow/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both patterns coordinate a multi-step business process across independent services, but they differ in where the coordination logic lives. In &lt;strong class="kw"&gt;choreography&lt;/strong&gt;, each service reacts to events and decides its own next move with no central brain; in &lt;strong class="kw"&gt;orchestration&lt;/strong&gt;, a dedicated controller tells every service what to do and in what order.&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" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;text x="150" y="30" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Choreography&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Orchestration&lt;/text&gt;&lt;rect x="50" y="140" width="90" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="95" y="164" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Order Svc&lt;/text&gt;&lt;rect x="150" y="60" width="90" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="195" y="84" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Payment Svc&lt;/text&gt;&lt;rect x="150" y="220" width="90" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="195" y="244" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Shipping Svc&lt;/text&gt;&lt;path d="M108,140 C125,112 138,100 150,90" fill="none" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="140" y="105" font-size="9" style="fill:var(--secondary)"&gt;event&lt;/text&gt;&lt;path d="M235,100 C262,150 262,180 235,222" fill="none" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="248" y="165" font-size="9" style="fill:var(--secondary)"&gt;event&lt;/text&gt;&lt;path d="M150,238 C110,220 90,200 95,180" fill="none" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="90" y="215" font-size="9" style="fill:var(--secondary)"&gt;event&lt;/text&gt;&lt;rect x="400" y="150" width="120" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="460" y="174" text-anchor="middle" font-size="11" style="fill:var(--primary)"&gt;Orchestrator&lt;/text&gt;&lt;rect x="400" y="50" width="120" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="460" y="74" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Payment Svc&lt;/text&gt;&lt;rect x="340" y="230" width="100" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="390" y="254" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Order Svc&lt;/text&gt;&lt;rect x="480" y="230" width="100" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="530" y="254" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Shipping Svc&lt;/text&gt;&lt;line x1="450" y1="150" x2="450" y2="92" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="472" y1="92" x2="472" y2="150" style="stroke:var(--compare-b)" stroke-width="1" stroke-dasharray="3,3"/&gt;&lt;line x1="420" y1="190" x2="400" y2="228" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="438" y1="228" x2="445" y2="192" style="stroke:var(--compare-b)" stroke-width="1" stroke-dasharray="3,3"/&gt;&lt;line x1="500" y1="190" x2="520" y2="228" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="540" y1="228" x2="525" y2="192" style="stroke:var(--compare-b)" stroke-width="1" stroke-dasharray="3,3"/&gt;&lt;text x="150" y="340" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;no central controller&lt;/text&gt;&lt;text x="480" y="340" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;commands out, responses back&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;Choreography&lt;/th&gt;
&lt;th&gt;Orchestration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;Any service publishes an event when something happens&lt;/td&gt;
&lt;td&gt;A client or event calls the orchestrator to start the process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coordination logic&lt;/td&gt;
&lt;td&gt;Distributed across each service&amp;rsquo;s event handlers&lt;/td&gt;
&lt;td&gt;Centralized in one orchestrator component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communication style&lt;/td&gt;
&lt;td&gt;Asynchronous events broadcast to whoever is listening&lt;/td&gt;
&lt;td&gt;Explicit commands and replies directed at specific services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step sequencing&lt;/td&gt;
&lt;td&gt;Emergent from chained event subscriptions&lt;/td&gt;
&lt;td&gt;Explicitly defined as a workflow or state machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Each service listens for failure events and compensates locally&lt;/td&gt;
&lt;td&gt;Orchestrator detects failure and drives compensating transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adding a new step&lt;/td&gt;
&lt;td&gt;Add a listener; no existing service needs to change&lt;/td&gt;
&lt;td&gt;Update the orchestrator&amp;rsquo;s workflow definition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability&lt;/td&gt;
&lt;td&gt;Hard to see the full process; requires distributed tracing&lt;/td&gt;
&lt;td&gt;Process state is visible in one place, easy to audit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coupling&lt;/td&gt;
&lt;td&gt;Low coupling between services, higher coupling to event schema&lt;/td&gt;
&lt;td&gt;Services decoupled from each other, but coupled to the orchestrator&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;Choreography spreads decision-making across services via &lt;strong class="kw"&gt;events&lt;/strong&gt;; orchestration centralizes it in a single &lt;strong class="kw"&gt;controller&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choreography scales extensibility easily but makes the overall process hard to &lt;strong class="kw"&gt;trace&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Orchestration makes the workflow explicit and easy to &lt;strong class="kw"&gt;audit&lt;/strong&gt;, at the cost of a single point of coordination.&lt;/li&gt;
&lt;li&gt;Compensation logic lives in each service under choreography, but is driven centrally under &lt;strong class="kw"&gt;orchestration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Orchestration introduces a dependency on the &lt;strong class="kw"&gt;orchestrator&lt;/strong&gt; itself as new coupling, even as it decouples the services from each other.&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;Choreography&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Queue vs Event Log: Consume-Once Delivery vs Replayable Stream</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-queue-vs-event-log-consume-once-delivery-vs-replayable-strea/</link><pubDate>Sun, 06 Sep 2026 09:55:24 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-queue-vs-event-log-consume-once-delivery-vs-replayable-strea/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A message queue and an event log both move data from producers to consumers, but they differ in what happens after a message is read. A queue treats delivery as a one-time handoff where each message is &lt;strong class="kw"&gt;consumed once&lt;/strong&gt; and then removed, while an event log keeps every event in an ordered, &lt;strong class="kw"&gt;replayable&lt;/strong&gt; sequence that multiple independent readers can consume at their own pace. This distinction drives how each handles multiple consumers, failure recovery, and historical reprocessing.&lt;/p&gt;</description></item><item><title>Shared Database vs Database Per Service: Data Ownership in Microservices</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-shared-database-vs-database-per-service-data-ownership-in-mi/</link><pubDate>Sun, 06 Sep 2026 09:52:35 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-shared-database-vs-database-per-service-data-ownership-in-mi/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;This compares two data architecture patterns for microservices: a &lt;strong class="kw"&gt;shared database&lt;/strong&gt; where multiple services read and write the same schema, versus &lt;strong class="kw"&gt;database per service&lt;/strong&gt; where each service owns an isolated data store. The choice determines how tightly services are coupled, how transactions and queries span service boundaries, and how independently teams can deploy and scale.&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;text x="160" y="30" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Shared Database&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Database Per Service&lt;/text&gt;&lt;line x1="320" y1="50" x2="320" y2="330" stroke-width="1" stroke-dasharray="4,4" style="stroke:var(--border)"/&gt;&lt;rect x="30" y="70" width="110" height="44" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="85" y="97" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Service A&lt;/text&gt;&lt;rect x="30" y="155" width="110" height="44" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="85" y="182" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Service B&lt;/text&gt;&lt;rect x="30" y="240" width="110" height="44" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="85" y="267" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Service C&lt;/text&gt;&lt;rect x="210" y="110" width="90" height="150" rx="8" stroke-width="2" style="fill:var(--compare-a-soft);stroke:var(--compare-a)"/&gt;&lt;text x="255" y="180" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Shared&lt;/text&gt;&lt;text x="255" y="198" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;DB&lt;/text&gt;&lt;line x1="140" y1="92" x2="210" y2="145" stroke-width="1.5" style="stroke:var(--compare-a)"/&gt;&lt;line x1="140" y1="177" x2="210" y2="185" stroke-width="1.5" style="stroke:var(--compare-a)"/&gt;&lt;line x1="140" y1="262" x2="210" y2="225" stroke-width="1.5" style="stroke:var(--compare-a)"/&gt;&lt;rect x="350" y="70" width="100" height="44" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="400" y="97" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Service A&lt;/text&gt;&lt;rect x="480" y="70" width="90" height="44" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="525" y="97" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;DB A&lt;/text&gt;&lt;line x1="450" y1="92" x2="480" y2="92" stroke-width="1.5" style="stroke:var(--compare-b)"/&gt;&lt;rect x="350" y="155" width="100" height="44" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="400" y="182" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Service B&lt;/text&gt;&lt;rect x="480" y="155" width="90" height="44" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="525" y="182" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;DB B&lt;/text&gt;&lt;line x1="450" y1="177" x2="480" y2="177" stroke-width="1.5" style="stroke:var(--compare-b)"/&gt;&lt;rect x="350" y="240" width="100" height="44" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="400" y="267" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Service C&lt;/text&gt;&lt;rect x="480" y="240" width="90" height="44" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)"/&gt;&lt;text x="525" y="267" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;DB C&lt;/text&gt;&lt;line x1="450" y1="262" x2="480" y2="262" stroke-width="1.5" style="stroke:var(--compare-b)"/&gt;&lt;text x="160" y="345" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Single point of coupling &amp;amp; contention&lt;/text&gt;&lt;text x="480" y="345" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Isolated data, independent scaling&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;Shared Database&lt;/th&gt;
&lt;th&gt;Database Per Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Schema ownership&lt;/td&gt;
&lt;td&gt;One schema shared and often co-owned by multiple teams&lt;/td&gt;
&lt;td&gt;Each service exclusively owns and evolves its own schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write path&lt;/td&gt;
&lt;td&gt;Any service can write directly to shared tables&lt;/td&gt;
&lt;td&gt;Writes go only through the owning service&amp;rsquo;s API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-service queries&lt;/td&gt;
&lt;td&gt;Simple SQL joins across tables in one database&lt;/td&gt;
&lt;td&gt;Requires API calls, data replication, or an aggregation layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed transactions&lt;/td&gt;
&lt;td&gt;Native ACID transactions across affected tables&lt;/td&gt;
&lt;td&gt;Needs sagas or eventual consistency to span services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema migrations&lt;/td&gt;
&lt;td&gt;Any change risks breaking other services using the table&lt;/td&gt;
&lt;td&gt;Migrations are local and safe to run independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Independent scaling&lt;/td&gt;
&lt;td&gt;Database becomes a shared bottleneck under load&lt;/td&gt;
&lt;td&gt;Each store can be scaled or tuned to its own service&amp;rsquo;s needs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology choice&lt;/td&gt;
&lt;td&gt;All services locked into one database engine&lt;/td&gt;
&lt;td&gt;Each service can pick the best-fit database technology&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure isolation&lt;/td&gt;
&lt;td&gt;A database outage or lock contention affects every service&lt;/td&gt;
&lt;td&gt;An outage is contained to the owning service&amp;rsquo;s data&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;Shared database allows cheap &lt;strong class="kw"&gt;cross-table joins&lt;/strong&gt; but couples every consuming service to one schema&lt;/li&gt;
&lt;li&gt;Database per service enforces &lt;strong class="kw"&gt;service autonomy&lt;/strong&gt; at the cost of needing sagas for cross-service transactions&lt;/li&gt;
&lt;li&gt;Schema changes in a shared database require coordinating &lt;strong class="kw"&gt;multiple teams&lt;/strong&gt;, while per-service schemas change independently&lt;/li&gt;
&lt;li&gt;A shared database creates a single &lt;strong class="kw"&gt;failure domain&lt;/strong&gt;; per-service databases contain outages to one service&lt;/li&gt;
&lt;li&gt;Polyglot persistence — choosing different database engines per need — is only possible with &lt;strong class="kw"&gt;database per service&lt;/strong&gt;&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;Shared Database&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Last-Write-Wins vs Merging: Resolving Conflicting Writes</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-last-write-wins-vs-merging-resolving-conflicting-writes/</link><pubDate>Sun, 06 Sep 2026 09:50:29 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-last-write-wins-vs-merging-resolving-conflicting-writes/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;When two replicas accept concurrent writes to the same key, a system must reconcile them. &lt;strong class="kw"&gt;Last-Write-Wins&lt;/strong&gt; picks a single winner by timestamp and discards the rest, while &lt;strong class="kw"&gt;Merging&lt;/strong&gt; combines both writes into a new value using domain-specific or CRDT logic.&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;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;&lt;text x="160" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Last-Write-Wins&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Merging&lt;/text&gt;&lt;rect x="40" y="55" width="110" height="45" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="95" y="75" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Write A&lt;/text&gt;&lt;text x="95" y="91" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;t=10, val=1&lt;/text&gt;&lt;rect x="170" y="55" width="110" height="45" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="225" y="75" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Write B&lt;/text&gt;&lt;text x="225" y="91" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;t=12, val=2&lt;/text&gt;&lt;line x1="95" y1="100" x2="160" y2="150" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="225" y1="100" x2="160" y2="150" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;circle cx="160" cy="155" r="5" style="fill:var(--compare-a)"/&gt;&lt;line x1="160" y1="160" x2="160" y2="200" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="90" y="205" width="140" height="50" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;text x="160" y="227" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;val = 2&lt;/text&gt;&lt;text x="160" y="243" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;(highest timestamp wins)&lt;/text&gt;&lt;text x="160" y="280" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;Write A silently discarded&lt;/text&gt;&lt;rect x="360" y="55" width="110" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="415" y="75" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Write A&lt;/text&gt;&lt;text x="415" y="91" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;t=10, val=1&lt;/text&gt;&lt;rect x="490" y="55" width="110" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="545" y="75" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Write B&lt;/text&gt;&lt;text x="545" y="91" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;t=12, val=2&lt;/text&gt;&lt;line x1="415" y1="100" x2="480" y2="150" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="545" y1="100" x2="480" y2="150" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;circle cx="480" cy="155" r="5" style="fill:var(--compare-b)"/&gt;&lt;line x1="480" y1="160" x2="480" y2="200" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="405" y="205" width="150" height="50" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="480" y="227" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;{A:1, B:2}&lt;/text&gt;&lt;text x="480" y="243" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;(both values combined)&lt;/text&gt;&lt;text x="480" y="280" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;No data lost, app may reconcile&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;Last-Write-Wins&lt;/th&gt;
&lt;th&gt;Merging&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Conflict trigger&lt;/td&gt;
&lt;td&gt;Fires when two writes to the same key arrive with overlapping validity, regardless of content&lt;/td&gt;
&lt;td&gt;Fires the same way, but treats both writes as valid inputs rather than competitors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolution mechanism&lt;/td&gt;
&lt;td&gt;Compares timestamps (or version numbers) and keeps the highest one&lt;/td&gt;
&lt;td&gt;Applies a merge function, CRDT join, or three-way diff to combine both values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data/metadata required&lt;/td&gt;
&lt;td&gt;A reliable clock or monotonic counter per write&lt;/td&gt;
&lt;td&gt;Version vectors, causal history, or a semantically defined merge operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application involvement&lt;/td&gt;
&lt;td&gt;None — resolution is automatic and content-agnostic&lt;/td&gt;
&lt;td&gt;Requires the app or data structure to define what &amp;lsquo;combining&amp;rsquo; means&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outcome for the losing write&lt;/td&gt;
&lt;td&gt;Discarded entirely, no trace remains&lt;/td&gt;
&lt;td&gt;Incorporated into the final merged state, nothing is dropped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency guarantee&lt;/td&gt;
&lt;td&gt;Deterministic convergence, but the winner may be arbitrary relative to causality&lt;/td&gt;
&lt;td&gt;Deterministic convergence that also respects the semantics of both updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance overhead&lt;/td&gt;
&lt;td&gt;Minimal — a single comparison per conflict&lt;/td&gt;
&lt;td&gt;Higher — merge logic, extra metadata, and sometimes multi-way comparisons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure mode&lt;/td&gt;
&lt;td&gt;Silent data loss under clock skew or concurrent writes at the same timestamp&lt;/td&gt;
&lt;td&gt;Unresolvable merge conflicts that surface to the application or user&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;LWW resolves conflicts purely by comparing &lt;strong class="kw"&gt;timestamps&lt;/strong&gt;, keeping only one write.&lt;/li&gt;
&lt;li&gt;Merging combines concurrent writes using a &lt;strong class="kw"&gt;merge function&lt;/strong&gt; or CRDT join instead of picking a single winner.&lt;/li&gt;
&lt;li&gt;LWW can cause &lt;strong class="kw"&gt;silent data loss&lt;/strong&gt; when clocks skew or writes race within the same tick.&lt;/li&gt;
&lt;li&gt;Merging needs &lt;strong class="kw"&gt;semantic knowledge&lt;/strong&gt; of the data type to combine values correctly.&lt;/li&gt;
&lt;li&gt;LWW adds negligible overhead per write; merging trades that simplicity for &lt;strong class="kw"&gt;correctness&lt;/strong&gt; under concurrency.&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;Last-Write-Wins&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Primary vs Replica Reads: Strong Consistency vs Read Scaling</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-primary-vs-replica-reads-strong-consistency-vs-read-scaling/</link><pubDate>Sun, 06 Sep 2026 09:47:10 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-primary-vs-replica-reads-strong-consistency-vs-read-scaling/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;In a replicated database, read queries can be routed to the &lt;strong class="kw"&gt;primary&lt;/strong&gt; node or to one of the &lt;strong class="kw"&gt;read replicas&lt;/strong&gt;. The choice trades guaranteed data freshness for the ability to scale read throughput and reduce load on the write path.&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;rect x="260" y="20" width="120" height="48" rx="6" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="320" y="49" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;Client&lt;/text&gt;&lt;rect x="90" y="160" width="160" height="64" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="186" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Primary&lt;/text&gt;&lt;text x="170" y="205" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;handles all writes&lt;/text&gt;&lt;rect x="390" y="160" width="160" height="64" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="470" y="186" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Replica&lt;/text&gt;&lt;text x="470" y="205" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;read-only copy&lt;/text&gt;&lt;line x1="250" y1="192" x2="390" y2="192" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="5,4"/&gt;&lt;path d="M250,192 L390,192" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="5,4" marker-end="url(#arrowRep)"/&gt;&lt;text x="320" y="180" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;async replication (lag)&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowRep" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--border)"/&gt;&lt;/marker&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;path d="M300,68 L190,160" style="stroke:var(--compare-a)" stroke-width="1.5" fill="none" marker-end="url(#arrowA)"/&gt;&lt;text x="215" y="110" text-anchor="middle" style="fill:var(--compare-a)" font-size="11"&gt;write&lt;/text&gt;&lt;path d="M310,68 L235,160" style="stroke:var(--compare-a)" stroke-width="1.5" fill="none" stroke-dasharray="4,3" marker-end="url(#arrowA)"/&gt;&lt;text x="290" y="130" text-anchor="middle" style="fill:var(--compare-a)" font-size="11"&gt;read (fresh)&lt;/text&gt;&lt;path d="M340,68 L450,160" style="stroke:var(--compare-b)" stroke-width="1.5" fill="none" stroke-dasharray="4,3" marker-end="url(#arrowB)"/&gt;&lt;text x="400" y="130" text-anchor="middle" style="fill:var(--compare-b)" font-size="11"&gt;read (maybe stale)&lt;/text&gt;&lt;text x="170" y="260" text-anchor="middle" style="fill:var(--secondary)" font-size="12"&gt;single node, no lag&lt;/text&gt;&lt;text x="470" y="260" text-anchor="middle" style="fill:var(--secondary)" font-size="12"&gt;scales out horizontally&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;Primary Reads&lt;/th&gt;
&lt;th&gt;Replica Reads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read target&lt;/td&gt;
&lt;td&gt;Always the single primary node&lt;/td&gt;
&lt;td&gt;Any of one or more read replicas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency guarantee&lt;/td&gt;
&lt;td&gt;Read-your-writes, strongly consistent&lt;/td&gt;
&lt;td&gt;Eventual consistency, may lag behind writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication lag exposure&lt;/td&gt;
&lt;td&gt;None, reads the current write state directly&lt;/td&gt;
&lt;td&gt;Exposed to lag, from milliseconds to seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contention with writes&lt;/td&gt;
&lt;td&gt;Reads compete with writes for CPU, locks, and I/O&lt;/td&gt;
&lt;td&gt;Writes on primary don&amp;rsquo;t directly compete with replica reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read throughput scaling&lt;/td&gt;
&lt;td&gt;Bounded by single node capacity&lt;/td&gt;
&lt;td&gt;Scales horizontally by adding more replicas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency profile&lt;/td&gt;
&lt;td&gt;Consistent, no wait for replication to catch up&lt;/td&gt;
&lt;td&gt;Can be lower if replica is geographically closer, but variable under lag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failover behavior&lt;/td&gt;
&lt;td&gt;Node failure requires promotion and brief write/read outage&lt;/td&gt;
&lt;td&gt;Load balancer can reroute to another healthy replica&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use case&lt;/td&gt;
&lt;td&gt;Financial transactions, read-after-write flows, admin views&lt;/td&gt;
&lt;td&gt;Analytics, reporting, public APIs, dashboards&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;&lt;strong class="kw"&gt;Primary reads&lt;/strong&gt; guarantee read-your-writes consistency; &lt;strong class="kw"&gt;replica reads&lt;/strong&gt; may return stale data due to lag.&lt;/li&gt;
&lt;li&gt;Replica reads scale horizontally by adding &lt;strong class="kw"&gt;read replicas&lt;/strong&gt;; primary reads are bottlenecked by a &lt;strong class="kw"&gt;single node&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Primary reads compete with write traffic for resources; replica reads isolate read load via &lt;strong class="kw"&gt;replication&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong class="kw"&gt;Replication lag&lt;/strong&gt; on replicas ranges from milliseconds to seconds depending on network and write volume.&lt;/li&gt;
&lt;li&gt;Failover on the primary causes brief unavailability; replica failures are masked by &lt;strong class="kw"&gt;load balancing&lt;/strong&gt; across peers.&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;Primary Reads&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Cache Freshness vs Hit Rate: Correctness vs Efficiency</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-cache-freshness-vs-hit-rate-correctness-vs-efficiency/</link><pubDate>Sun, 06 Sep 2026 09:45:20 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-cache-freshness-vs-hit-rate-correctness-vs-efficiency/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Cache freshness measures whether the data returned by a cache still matches the current state of its source of truth, while hit rate measures how often requests are answered directly from the cache instead of falling through to the origin. The two metrics pull in opposite directions: optimizing for &lt;strong class="kw"&gt;freshness&lt;/strong&gt; means shorter TTLs and more origin traffic, while optimizing for &lt;strong class="kw"&gt;hit rate&lt;/strong&gt; means longer TTLs and a higher chance of serving stale data. Tuning a cache well means choosing the right balance point for that specific data&amp;rsquo;s tolerance for staleness.&lt;/p&gt;</description></item><item><title>Local vs Shared Cache: Per-Instance Memory vs Centralized Cache Service</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-local-vs-shared-cache-per-instance-memory-vs-centralized-cac/</link><pubDate>Sun, 06 Sep 2026 09:43:40 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-local-vs-shared-cache-per-instance-memory-vs-centralized-cac/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A &lt;strong class="kw"&gt;local cache&lt;/strong&gt; stores data in the memory of a single application process, giving the fastest possible reads but no visibility into what other instances hold. A &lt;strong class="kw"&gt;shared cache&lt;/strong&gt; lives in a separate service that every instance queries over the network, trading a bit of latency for one consistent view of cached data across the whole fleet. The choice shapes how you handle invalidation, scaling, and failure in a multi-instance deployment.&lt;/p&gt;</description></item><item><title>Range vs Hash Partitioning: Ordered Splits vs Scattered Buckets</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-range-vs-hash-partitioning-ordered-splits-vs-scattered-bucke/</link><pubDate>Sun, 06 Sep 2026 09:41:37 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-range-vs-hash-partitioning-ordered-splits-vs-scattered-bucke/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Range and hash partitioning are two strategies for splitting a table&amp;rsquo;s rows across multiple partitions or nodes based on a partition key. Range partitioning assigns rows to &lt;strong class="kw"&gt;contiguous key intervals&lt;/strong&gt; (like date ranges), preserving order for efficient range scans but risking uneven load. Hash partitioning runs the key through a &lt;strong class="kw"&gt;hash function&lt;/strong&gt; to scatter rows evenly, trading away ordering for balanced, predictable distribution.&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;text x="160" y="28" text-anchor="middle" font-size="16" font-weight="bold" style="fill:var(--primary)"&gt;RANGE PARTITIONING&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" font-size="16" font-weight="bold" style="fill:var(--primary)"&gt;HASH PARTITIONING&lt;/text&gt;&lt;line x1="320" y1="10" x2="320" y2="345" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="160" y="45" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;incoming keys&lt;/text&gt;&lt;text x="480" y="45" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;incoming keys&lt;/text&gt;&lt;rect x="40" y="50" width="80" height="30" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="120" y="50" width="80" height="30" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="200" y="50" width="80" height="30" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="80" y="70" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;1-33&lt;/text&gt;&lt;text x="160" y="70" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;34-66&lt;/text&gt;&lt;text x="240" y="70" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;67-100&lt;/text&gt;&lt;rect x="360" y="50" width="80" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="440" y="50" width="80" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="520" y="50" width="80" height="30" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="400" y="70" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;1-33&lt;/text&gt;&lt;text x="480" y="70" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;34-66&lt;/text&gt;&lt;text x="560" y="70" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;67-100&lt;/text&gt;&lt;line x1="80" y1="80" x2="80" y2="168" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="160" y1="80" x2="160" y2="168" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="240" y1="80" x2="240" y2="168" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="400" y1="80" x2="560" y2="168" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="480" y1="80" x2="400" y2="168" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="560" y1="80" x2="480" y2="168" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="128" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;hash(key)&lt;/text&gt;&lt;rect x="50" y="170" width="60" height="40" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="130" y="170" width="60" height="40" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="210" y="170" width="60" height="40" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="80" y="194" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P1&lt;/text&gt;&lt;text x="160" y="194" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P2&lt;/text&gt;&lt;text x="240" y="194" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P3&lt;/text&gt;&lt;rect x="370" y="170" width="60" height="40" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="450" y="170" width="60" height="40" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="530" y="170" width="60" height="40" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="400" y="194" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P1&lt;/text&gt;&lt;text x="480" y="194" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P2&lt;/text&gt;&lt;text x="560" y="194" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;P3&lt;/text&gt;&lt;text x="160" y="240" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Ordered, contiguous ranges&lt;/text&gt;&lt;text x="480" y="240" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Scattered, uniform spread&lt;/text&gt;&lt;text x="160" y="264" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Easy to extend: add a boundary&lt;/text&gt;&lt;text x="480" y="264" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Costly to resize: rehash keys&lt;/text&gt;&lt;text x="160" y="288" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Risk: skew on hot ranges&lt;/text&gt;&lt;text x="480" y="288" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Risk: no range pruning&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;Range Partitioning&lt;/th&gt;
&lt;th&gt;Hash Partitioning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Partition key requirement&lt;/td&gt;
&lt;td&gt;Needs an orderable key with defined boundaries (dates, IDs)&lt;/td&gt;
&lt;td&gt;Any key works; only needs to be hashable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row-to-partition mapping&lt;/td&gt;
&lt;td&gt;Explicit boundary rules assign rows to intervals&lt;/td&gt;
&lt;td&gt;Hash function output (often mod N) selects the bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data distribution&lt;/td&gt;
&lt;td&gt;Can be skewed if key values aren&amp;rsquo;t uniformly spread&lt;/td&gt;
&lt;td&gt;Near-uniform if the hash function distributes well&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Range/scan queries&lt;/td&gt;
&lt;td&gt;Prunes to only the partitions covering the range&lt;/td&gt;
&lt;td&gt;Must fan out and scan every partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Point/equality lookups&lt;/td&gt;
&lt;td&gt;Requires a boundary search to find the right partition&lt;/td&gt;
&lt;td&gt;Direct O(1) computation locates the partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adding or removing partitions&lt;/td&gt;
&lt;td&gt;Cheap: append or split a boundary at the edge&lt;/td&gt;
&lt;td&gt;Expensive: reshuffles most existing keys unless using consistent hashing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hotspot behavior&lt;/td&gt;
&lt;td&gt;Sequential writes (recent dates, auto-increment IDs) pile onto one partition&lt;/td&gt;
&lt;td&gt;Spreads writes evenly but destroys any physical data locality&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;Range partitioning preserves &lt;strong class="kw"&gt;order&lt;/strong&gt;, letting the query planner prune partitions; hash partitioning optimizes purely for &lt;strong class="kw"&gt;even distribution&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Growing the partition count is a cheap boundary edit in range partitioning but forces a &lt;strong class="kw"&gt;rehash&lt;/strong&gt; of most keys in hash partitioning&lt;/li&gt;
&lt;li&gt;Range schemes are exposed to &lt;strong class="kw"&gt;skew&lt;/strong&gt; when writes cluster in a narrow key window; hash schemes avoid this at the cost of locality&lt;/li&gt;
&lt;li&gt;Point lookups under hashing are a direct &lt;strong class="kw"&gt;hash computation&lt;/strong&gt;, while range lookups need a &lt;strong class="kw"&gt;boundary search&lt;/strong&gt; through ordered intervals&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;Range Partitioning&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Sync vs Async Replication: When the Write Actually Commits</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-sync-vs-async-replication-when-the-write-actually-commits/</link><pubDate>Sun, 06 Sep 2026 09:39:49 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-sync-vs-async-replication-when-the-write-actually-commits/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Synchronous and asynchronous replication differ in exactly one moment: when the primary tells the client a write succeeded. &lt;strong class="kw"&gt;Sync replication&lt;/strong&gt; waits for the replica to confirm before acknowledging, while &lt;strong class="kw"&gt;async replication&lt;/strong&gt; acknowledges immediately and copies the data afterward. That single timing difference cascades into everything else — latency, throughput, and how much data you can lose on failover.&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" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;text x="160" y="22" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Sync Replication&lt;/text&gt;&lt;text x="480" y="22" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Async Replication&lt;/text&gt;&lt;rect x="90" y="40" width="140" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="65" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Client&lt;/text&gt;&lt;rect x="90" y="150" width="140" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="175" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Primary&lt;/text&gt;&lt;rect x="90" y="270" width="140" height="40" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="295" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Replica&lt;/text&gt;&lt;line x1="150" y1="80" x2="150" y2="150" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="60" y="115" style="fill:var(--secondary)" font-size="10"&gt;1. write&lt;/text&gt;&lt;line x1="150" y1="190" x2="150" y2="270" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="60" y="235" style="fill:var(--secondary)" font-size="10"&gt;2. replicate&lt;/text&gt;&lt;line x1="210" y1="270" x2="210" y2="190" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="213" y="235" style="fill:var(--secondary)" font-size="10"&gt;3. ack&lt;/text&gt;&lt;line x1="170" y1="150" x2="170" y2="80" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="173" y="115" style="fill:var(--secondary)" font-size="10"&gt;4. commit&lt;/text&gt;&lt;text x="160" y="330" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Client waits for step 3&lt;/text&gt;&lt;rect x="410" y="40" width="140" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="65" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Client&lt;/text&gt;&lt;rect x="410" y="150" width="140" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="175" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Primary&lt;/text&gt;&lt;rect x="410" y="270" width="140" height="40" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="295" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Replica&lt;/text&gt;&lt;line x1="470" y1="80" x2="470" y2="150" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="390" y="115" style="fill:var(--secondary)" font-size="10"&gt;1. write&lt;/text&gt;&lt;line x1="500" y1="150" x2="500" y2="80" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="503" y="115" style="fill:var(--secondary)" font-size="10"&gt;2. commit&lt;/text&gt;&lt;line x1="480" y1="190" x2="480" y2="270" style="stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="5,4" marker-end="url(#arrowB)"/&gt;&lt;text x="390" y="235" style="fill:var(--secondary)" font-size="10"&gt;3. replicate later&lt;/text&gt;&lt;text x="480" y="330" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Client returns at step 2&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;Sync Replication&lt;/th&gt;
&lt;th&gt;Async Replication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write acknowledgment&lt;/td&gt;
&lt;td&gt;Waits for replica confirmation before committing&lt;/td&gt;
&lt;td&gt;Commits on primary alone, replicates after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commit latency&lt;/td&gt;
&lt;td&gt;Includes network round-trip to replica&lt;/td&gt;
&lt;td&gt;Bound only by primary&amp;rsquo;s local write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data consistency&lt;/td&gt;
&lt;td&gt;Replica is always up to date at commit time&lt;/td&gt;
&lt;td&gt;Replica can lag behind primary momentarily&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput under load&lt;/td&gt;
&lt;td&gt;Degrades as replica distance or count grows&lt;/td&gt;
&lt;td&gt;Unaffected by replica speed or distance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replica or network failure&lt;/td&gt;
&lt;td&gt;Writes block or fail until replica responds&lt;/td&gt;
&lt;td&gt;Writes continue uninterrupted on primary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failover data loss&lt;/td&gt;
&lt;td&gt;None — replica always has the committed write&lt;/td&gt;
&lt;td&gt;Possible — unreplicated writes are lost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication lag monitoring&lt;/td&gt;
&lt;td&gt;Not applicable — lag is structurally zero&lt;/td&gt;
&lt;td&gt;Critical — must track and alert on lag&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;&lt;strong class="kw"&gt;Commit timing&lt;/strong&gt; is the root difference: sync waits, async doesn&amp;rsquo;t&lt;/li&gt;
&lt;li&gt;Sync trades &lt;strong class="kw"&gt;latency&lt;/strong&gt; for a zero-data-loss guarantee on failover&lt;/li&gt;
&lt;li&gt;Async trades &lt;strong class="kw"&gt;durability&lt;/strong&gt; for consistently fast local commits&lt;/li&gt;
&lt;li&gt;Multi-region setups favor async since &lt;strong class="kw"&gt;round-trip time&lt;/strong&gt; would make sync commits too slow&lt;/li&gt;
&lt;li&gt;Async requires active &lt;strong class="kw"&gt;lag monitoring&lt;/strong&gt; that sync simply doesn&amp;rsquo;t need&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;Sync Replication&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Replication vs Sharding: Copying Data vs Splitting Data</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-replication-vs-sharding-copying-data-vs-splitting-data/</link><pubDate>Sun, 06 Sep 2026 09:39:10 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-replication-vs-sharding-copying-data-vs-splitting-data/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both are techniques for scaling a database beyond a single node, but they solve different problems: &lt;strong class="kw"&gt;replication&lt;/strong&gt; copies the entire dataset onto multiple nodes to boost availability and read capacity, while &lt;strong class="kw"&gt;sharding&lt;/strong&gt; splits the dataset into disjoint partitions across nodes to boost storage and write capacity. Large-scale systems typically use both together — sharding for horizontal scale, replication within each shard for durability.&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;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;
&lt;text x="160" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Replication&lt;/text&gt;
&lt;text x="480" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Sharding&lt;/text&gt;
&lt;rect x="100" y="50" width="120" height="50" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="160" y="72" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Primary&lt;/text&gt;
&lt;text x="160" y="88" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;A B C D&lt;/text&gt;
&lt;line x1="140" y1="100" x2="105" y2="172" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;line x1="180" y1="100" x2="225" y2="172" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;rect x="50" y="175" width="110" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="105" y="197" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Replica A&lt;/text&gt;
&lt;text x="105" y="213" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;A B C D&lt;/text&gt;
&lt;rect x="170" y="175" width="110" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="225" y="197" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Replica B&lt;/text&gt;
&lt;text x="225" y="213" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;A B C D&lt;/text&gt;
&lt;text x="160" y="255" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;full dataset, copied&lt;/text&gt;
&lt;text x="160" y="272" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;to every node&lt;/text&gt;
&lt;rect x="420" y="50" width="120" height="50" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="480" y="72" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Router&lt;/text&gt;
&lt;text x="480" y="88" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;key lookup&lt;/text&gt;
&lt;line x1="460" y1="100" x2="430" y2="172" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;line x1="500" y1="100" x2="550" y2="172" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;rect x="375" y="175" width="110" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="430" y="197" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Shard 1&lt;/text&gt;
&lt;text x="430" y="213" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;keys A-M&lt;/text&gt;
&lt;rect x="495" y="175" width="110" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="550" y="197" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Shard 2&lt;/text&gt;
&lt;text x="550" y="213" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;keys N-Z&lt;/text&gt;
&lt;text x="480" y="255" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;dataset split into&lt;/text&gt;
&lt;text x="480" y="272" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;disjoint subsets&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;Replication&lt;/th&gt;
&lt;th&gt;Sharding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary goal&lt;/td&gt;
&lt;td&gt;Increase availability and read capacity&lt;/td&gt;
&lt;td&gt;Increase storage and write capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data distribution&lt;/td&gt;
&lt;td&gt;Full dataset copied to every node&lt;/td&gt;
&lt;td&gt;Dataset split into disjoint partitions across nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write path&lt;/td&gt;
&lt;td&gt;Writes go to primary, then propagate to replicas&lt;/td&gt;
&lt;td&gt;Writes routed to the single shard owning the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read path&lt;/td&gt;
&lt;td&gt;Any replica (or primary) can serve any read&lt;/td&gt;
&lt;td&gt;Read must be routed to the shard holding the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node failure impact&lt;/td&gt;
&lt;td&gt;Data survives since other copies exist&lt;/td&gt;
&lt;td&gt;That shard&amp;rsquo;s data becomes unavailable unless also replicated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency concern&lt;/td&gt;
&lt;td&gt;Replication lag between primary and replicas&lt;/td&gt;
&lt;td&gt;Cross-shard transactions and joins are hard to coordinate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling ceiling&lt;/td&gt;
&lt;td&gt;Bounded by primary&amp;rsquo;s write throughput&lt;/td&gt;
&lt;td&gt;Bounded by cross-shard coordination and key hotspots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational overhead&lt;/td&gt;
&lt;td&gt;Failover and leader election&lt;/td&gt;
&lt;td&gt;Shard key design, rebalancing, and resharding&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;Replication duplicates the same data everywhere; sharding partitions it so each node holds only a slice&lt;/li&gt;
&lt;li&gt;Replication scales &lt;strong class="kw"&gt;reads&lt;/strong&gt; and durability; sharding scales &lt;strong class="kw"&gt;writes&lt;/strong&gt; and total storage&lt;/li&gt;
&lt;li&gt;Sharding introduces a routing layer that must know which shard owns a given key&lt;/li&gt;
&lt;li&gt;Losing a replica is harmless, but losing an unreplicated shard causes real &lt;strong class="kw"&gt;data loss&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Production systems commonly combine both: shard for scale, replicate each shard for resilience&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;Replication&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Strong vs Eventual Consistency: Data Replication Tradeoff</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-strong-vs-eventual-consistency-data-replication-tradeoff/</link><pubDate>Sun, 06 Sep 2026 09:36:30 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-strong-vs-eventual-consistency-data-replication-tradeoff/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Strong and eventual consistency describe how distributed systems handle replicated data after a write. &lt;strong class="kw"&gt;Strong consistency&lt;/strong&gt; guarantees every read reflects the latest write by blocking until replicas agree, while &lt;strong class="kw"&gt;eventual consistency&lt;/strong&gt; returns immediately and lets replicas converge in the background. The choice trades write latency and availability against read freshness.&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;text x="160" y="32" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Strong Consistency&lt;/text&gt;&lt;text x="480" y="32" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Eventual Consistency&lt;/text&gt;&lt;line x1="320" y1="50" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;&lt;circle cx="55" cy="110" r="20" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="55" y="115" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;rect x="130" y="90" width="70" height="36" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="165" y="112" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Primary&lt;/text&gt;&lt;rect x="240" y="55" width="65" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="272" y="75" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;Replica 1&lt;/text&gt;&lt;rect x="240" y="128" width="65" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="272" y="148" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;Replica 2&lt;/text&gt;&lt;line x1="75" y1="108" x2="128" y2="108" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;line x1="200" y1="100" x2="238" y2="75" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;line x1="200" y1="118" x2="238" y2="140" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="165" y="178" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;write blocks until&lt;/text&gt;&lt;text x="165" y="190" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;all replicas ack&lt;/text&gt;&lt;line x1="55" y1="135" x2="55" y2="210" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;rect x="20" y="235" width="280" height="60" rx="4" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;text x="160" y="260" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Any read, any replica,&lt;/text&gt;&lt;text x="160" y="278" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;always returns latest value&lt;/text&gt;&lt;circle cx="395" cy="110" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="395" y="115" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;rect x="470" y="90" width="70" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="505" y="112" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Node&lt;/text&gt;&lt;rect x="580" y="55" width="55" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="607" y="75" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;Replica 1&lt;/text&gt;&lt;rect x="580" y="128" width="55" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="607" y="148" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;Replica 2&lt;/text&gt;&lt;line x1="415" y1="103" x2="468" y2="103" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="468" y1="117" x2="415" y2="117" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="440" y="132" text-anchor="middle" font-size="9" style="fill:var(--secondary)"&gt;ACK immediate&lt;/text&gt;&lt;line x1="540" y1="98" x2="578" y2="75" style="stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#arrowB)"/&gt;&lt;line x1="540" y1="118" x2="578" y2="140" style="stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#arrowB)"/&gt;&lt;text x="550" y="188" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;async, delayed&lt;/text&gt;&lt;rect x="360" y="235" width="260" height="60" rx="4" style="fill:none;stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;text x="490" y="258" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Read from Replica 1 may&lt;/text&gt;&lt;text x="490" y="276" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;return stale value until t+Δ&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"&gt;&lt;path d="M0,0 L6,3 L0,6 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&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;Strong Consistency&lt;/th&gt;
&lt;th&gt;Eventual Consistency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write acknowledgment&lt;/td&gt;
&lt;td&gt;Ack returned only after write is durably applied to all (or a quorum of) replicas&lt;/td&gt;
&lt;td&gt;Ack returned as soon as the write hits the local/primary node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication propagation&lt;/td&gt;
&lt;td&gt;Synchronous — write blocks until replicas confirm&lt;/td&gt;
&lt;td&gt;Asynchronous — replication happens in the background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read guarantee&lt;/td&gt;
&lt;td&gt;Every read reflects the most recent write (linearizable)&lt;/td&gt;
&lt;td&gt;Reads may return stale data until replicas converge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflict handling&lt;/td&gt;
&lt;td&gt;Prevented upfront via consensus/locking that serializes writes&lt;/td&gt;
&lt;td&gt;Resolved after the fact via LWW, vector clocks, or CRDTs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write latency&lt;/td&gt;
&lt;td&gt;Higher — pays network round-trip cost to replicas/quorum&lt;/td&gt;
&lt;td&gt;Lower — commits locally before propagating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior under partition&lt;/td&gt;
&lt;td&gt;Unavailable or degraded if quorum can&amp;rsquo;t be reached (CP)&lt;/td&gt;
&lt;td&gt;Stays available, serving from whichever replica is reachable (AP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical mechanism&lt;/td&gt;
&lt;td&gt;Consensus protocols like Paxos/Raft, synchronous quorum writes&lt;/td&gt;
&lt;td&gt;Gossip protocols, anti-entropy repair, background sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use cases&lt;/td&gt;
&lt;td&gt;Banking ledgers, inventory counts, leader election&lt;/td&gt;
&lt;td&gt;Social feeds, DNS, shopping carts, CDN caches&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;Strong consistency blocks the write until a &lt;strong class="kw"&gt;quorum&lt;/strong&gt; confirms; eventual consistency acks after a local commit.&lt;/li&gt;
&lt;li&gt;This is the classic &lt;strong class="kw"&gt;CAP tradeoff&lt;/strong&gt;: strong favors consistency during a partition, eventual favors availability.&lt;/li&gt;
&lt;li&gt;Strong relies on &lt;strong class="kw"&gt;consensus protocols&lt;/strong&gt; like Raft; eventual relies on background &lt;strong class="kw"&gt;anti-entropy&lt;/strong&gt; repair.&lt;/li&gt;
&lt;li&gt;Only strong consistency guarantees &lt;strong class="kw"&gt;read-after-write&lt;/strong&gt;; eventual consistency allows a stale-read window.&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;Strong Consistency&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Consistency vs Availability: The CAP Theorem Tradeoff</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-consistency-vs-availability-the-cap-theorem-tradeoff/</link><pubDate>Sun, 06 Sep 2026 09:35:36 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-consistency-vs-availability-the-cap-theorem-tradeoff/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;When a distributed system suffers a network partition, it must choose between &lt;strong class="kw"&gt;consistency&lt;/strong&gt; (every node sees the same data, even if that means rejecting requests) and &lt;strong class="kw"&gt;availability&lt;/strong&gt; (every request gets a response, even if the data might be stale). This CAP theorem tradeoff shapes how databases behave under failure and directly affects correctness guarantees versus uptime.&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;line x1="320" y1="10" x2="320" y2="350" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="6,5"/&gt;&lt;text x="160" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="17" font-weight="bold"&gt;Consistency (CP)&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="17" font-weight="bold"&gt;Availability (AP)&lt;/text&gt;&lt;rect x="100" y="44" width="120" height="36" rx="5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="67" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Client&lt;/text&gt;&lt;rect x="420" y="44" width="120" height="36" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="67" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Client&lt;/text&gt;&lt;line x1="140" y1="80" x2="105" y2="140" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="105,140 112,131 118,138" style="fill:var(--compare-a)"/&gt;&lt;line x1="175" y1="140" x2="185" y2="80" style="stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="4,3"/&gt;&lt;polygon points="185,80 178,86 182,90" style="fill:var(--compare-a)"/&gt;&lt;text x="210" y="105" text-anchor="middle" style="fill:var(--compare-a)" font-size="11"&gt;503 blocked&lt;/text&gt;&lt;line x1="460" y1="80" x2="425" y2="140" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="425,140 432,131 438,138" style="fill:var(--compare-b)"/&gt;&lt;line x1="495" y1="140" x2="505" y2="80" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="505,80 498,86 502,90" style="fill:var(--compare-b)"/&gt;&lt;text x="530" y="105" text-anchor="middle" style="fill:var(--compare-b)" font-size="11"&gt;200 OK (stale)&lt;/text&gt;&lt;rect x="60" y="140" width="100" height="50" rx="5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="110" y="170" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Node A&lt;/text&gt;&lt;rect x="200" y="140" width="100" height="50" rx="5" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="250" y="170" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Node B&lt;/text&gt;&lt;path d="M180 135 L172 150 L188 165 L180 180 L172 195" style="stroke:var(--border);fill:none" stroke-width="2"/&gt;&lt;text x="180" y="122" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;partition&lt;/text&gt;&lt;rect x="380" y="140" width="100" height="50" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="430" y="170" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Node A&lt;/text&gt;&lt;rect x="520" y="140" width="100" height="50" rx="5" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="570" y="170" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Node B&lt;/text&gt;&lt;path d="M500 135 L492 150 L508 165 L500 180 L492 195" style="stroke:var(--border);fill:none" stroke-width="2"/&gt;&lt;text x="500" y="122" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;partition&lt;/text&gt;&lt;text x="160" y="235" text-anchor="middle" style="fill:var(--secondary)" font-size="12"&gt;Waits for quorum, rejects request&lt;/text&gt;&lt;text x="160" y="260" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Guarantee: no stale reads&lt;/text&gt;&lt;text x="480" y="235" text-anchor="middle" style="fill:var(--secondary)" font-size="12"&gt;Answers immediately from local data&lt;/text&gt;&lt;text x="480" y="260" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Guarantee: no downtime&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;Consistency (CP)&lt;/th&gt;
&lt;th&gt;Availability (AP)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Normal operation (no partition)&lt;/td&gt;
&lt;td&gt;Behaves identically to any healthy cluster; all replicas agree&lt;/td&gt;
&lt;td&gt;Behaves identically to any healthy cluster; all replicas agree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behavior when a partition occurs&lt;/td&gt;
&lt;td&gt;Nodes that cannot confirm quorum stop responding&lt;/td&gt;
&lt;td&gt;All nodes keep responding regardless of quorum status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write handling during partition&lt;/td&gt;
&lt;td&gt;Writes are rejected or queued until enough replicas are reachable&lt;/td&gt;
&lt;td&gt;Writes are accepted locally and replicated once the partition heals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read handling during partition&lt;/td&gt;
&lt;td&gt;Reads are blocked or errored if the latest value can&amp;rsquo;t be confirmed&lt;/td&gt;
&lt;td&gt;Reads are served from whatever local replica is reachable, even if stale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client-facing failure mode&lt;/td&gt;
&lt;td&gt;Client sees a timeout or explicit error (e.g. 503)&lt;/td&gt;
&lt;td&gt;Client sees a successful response that may contain outdated data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data guarantee provided&lt;/td&gt;
&lt;td&gt;Linearizability - no two nodes ever disagree on current state&lt;/td&gt;
&lt;td&gt;Liveness - the system always answers, correctness may lag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovery after partition heals&lt;/td&gt;
&lt;td&gt;Resumes cleanly; no conflicting writes existed since they were blocked&lt;/td&gt;
&lt;td&gt;Must reconcile diverging writes via vector clocks, LWW, or CRDTs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Representative systems&lt;/td&gt;
&lt;td&gt;HBase, Zookeeper, MongoDB (default majority writes)&lt;/td&gt;
&lt;td&gt;Cassandra, DynamoDB, Riak&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;The tradeoff only bites during an actual &lt;strong class="kw"&gt;network partition&lt;/strong&gt; - outside of that, both behave the same.&lt;/li&gt;
&lt;li&gt;Consistency requires a &lt;strong class="kw"&gt;quorum&lt;/strong&gt; agreement before answering, which can mean refusing requests.&lt;/li&gt;
&lt;li&gt;Availability guarantees a response but risks returning &lt;strong class="kw"&gt;stale data&lt;/strong&gt; to the client.&lt;/li&gt;
&lt;li&gt;The choice determines whether you need a &lt;strong class="kw"&gt;conflict resolution&lt;/strong&gt; strategy for divergent writes after recovery.&lt;/li&gt;
&lt;li&gt;Many production databases offer &lt;strong class="kw"&gt;tunable consistency&lt;/strong&gt;, letting you pick per-operation rather than a single global stance.&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;Consistency (CP)&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Push vs Pull Architecture: Who Initiates the Data Transfer</title><link>https://comparison.metacog.co.kr/posts/2026-08-04-push-vs-pull-architecture-who-initiates-the-data-transfer/</link><pubDate>Tue, 04 Aug 2026 05:24:00 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-04-push-vs-pull-architecture-who-initiates-the-data-transfer/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Push and pull architecture describe who initiates data transfer between a producer and a consumer: in a &lt;strong class="kw"&gt;push model&lt;/strong&gt; the producer sends data the moment it&amp;rsquo;s ready, while in a &lt;strong class="kw"&gt;pull model&lt;/strong&gt; the consumer requests data on its own schedule. The choice shapes latency, coupling, and how systems handle scale or downtime.&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,0 L10,5 L0,10 z" 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,0 L10,5 L0,10 z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="6,6"/&gt;&lt;text x="160" y="32" text-anchor="middle" style="fill:var(--primary)" font-size="20" font-weight="bold"&gt;PUSH&lt;/text&gt;&lt;text x="160" y="50" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;(producer-initiated)&lt;/text&gt;&lt;rect x="80" y="70" width="160" height="60" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="105" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;Producer&lt;/text&gt;&lt;rect x="80" y="250" width="160" height="60" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="285" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;Consumer&lt;/text&gt;&lt;line x1="160" y1="130" x2="160" y2="245" style="stroke:var(--compare-a)" stroke-width="2" marker-end="url(#arrowA)"/&gt;&lt;text x="175" y="190" style="fill:var(--secondary)" font-size="12"&gt;data pushed&lt;/text&gt;&lt;text x="480" y="32" text-anchor="middle" style="fill:var(--primary)" font-size="20" font-weight="bold"&gt;PULL&lt;/text&gt;&lt;text x="480" y="50" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;(consumer-initiated)&lt;/text&gt;&lt;rect x="400" y="70" width="160" height="60" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="105" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;Consumer&lt;/text&gt;&lt;rect x="400" y="250" width="160" height="60" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="285" text-anchor="middle" style="fill:var(--content)" font-size="14"&gt;Producer&lt;/text&gt;&lt;line x1="465" y1="130" x2="465" y2="245" style="stroke:var(--compare-b)" stroke-width="2" marker-end="url(#arrowB)"/&gt;&lt;text x="450" y="180" text-anchor="end" style="fill:var(--secondary)" font-size="12"&gt;request&lt;/text&gt;&lt;line x1="495" y1="245" x2="495" y2="130" style="stroke:var(--compare-b)" stroke-width="2" marker-end="url(#arrowB)"/&gt;&lt;text x="510" y="200" text-anchor="start" style="fill:var(--secondary)" font-size="12"&gt;response&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;Push&lt;/th&gt;
&lt;th&gt;Pull&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initiator of transfer&lt;/td&gt;
&lt;td&gt;Producer sends data as soon as it&amp;rsquo;s available&lt;/td&gt;
&lt;td&gt;Consumer requests data on its own schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data freshness&lt;/td&gt;
&lt;td&gt;Near-real-time; consumer receives updates immediately&lt;/td&gt;
&lt;td&gt;Bounded by poll interval; can lag between requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer control&lt;/td&gt;
&lt;td&gt;Producer dictates pace; consumer must keep up&lt;/td&gt;
&lt;td&gt;Consumer sets pace and can throttle or batch requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coupling&lt;/td&gt;
&lt;td&gt;Producer must track and address its consumers&lt;/td&gt;
&lt;td&gt;Producer stays unaware of who is asking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability with consumers&lt;/td&gt;
&lt;td&gt;Fan-out cost grows with each new subscriber&lt;/td&gt;
&lt;td&gt;Each consumer&amp;rsquo;s load stays independent of the others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline or slow consumers&lt;/td&gt;
&lt;td&gt;Missed pushes risk data loss without a buffer or queue&lt;/td&gt;
&lt;td&gt;Consumer simply polls again later, no data lost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle resource usage&lt;/td&gt;
&lt;td&gt;Zero overhead when there is nothing new to send&lt;/td&gt;
&lt;td&gt;Wastes cycles and requests polling when nothing changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical examples&lt;/td&gt;
&lt;td&gt;Webhooks, WebSockets, pub/sub message brokers&lt;/td&gt;
&lt;td&gt;REST polling, cron jobs, RSS feed readers&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;Push delivers data the instant it&amp;rsquo;s produced, minimizing &lt;strong class="kw"&gt;latency&lt;/strong&gt; at the cost of straining consumer readiness.&lt;/li&gt;
&lt;li&gt;Pull lets the consumer control &lt;strong class="kw"&gt;pacing&lt;/strong&gt;, avoiding overload but risking staleness between requests.&lt;/li&gt;
&lt;li&gt;Push requires the producer to maintain a &lt;strong class="kw"&gt;subscriber list&lt;/strong&gt;, increasing coupling and fan-out complexity.&lt;/li&gt;
&lt;li&gt;Pull wastes cycles on &lt;strong class="kw"&gt;empty polls&lt;/strong&gt; when nothing has changed since the last request.&lt;/li&gt;
&lt;li&gt;Push needs a buffering or queueing layer to survive consumer &lt;strong class="kw"&gt;downtime&lt;/strong&gt; without losing data.&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;Push&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>SOA vs Microservices: Enterprise Integration vs Independent Deployability</title><link>https://comparison.metacog.co.kr/posts/2026-08-04-soa-vs-microservices-enterprise-integration-vs-independent-d/</link><pubDate>Tue, 04 Aug 2026 05:17:08 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-04-soa-vs-microservices-enterprise-integration-vs-independent-d/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;SOA and microservices are both approaches to composing systems from independently callable services, but they differ sharply in scope and philosophy. &lt;strong class="kw"&gt;SOA&lt;/strong&gt; centralizes communication and governance through an enterprise service bus to integrate large, often legacy systems, while &lt;strong class="kw"&gt;microservices&lt;/strong&gt; decentralize communication, data, and deployment into small, independently shippable units. The distinction matters because it drives very different tooling, team structures, and failure characteristics.&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;text x="150" y="30" text-anchor="middle" font-size="20" font-weight="bold" style="fill:var(--primary)"&gt;SOA&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="20" font-weight="bold" style="fill:var(--primary)"&gt;Microservices&lt;/text&gt;&lt;line x1="320" y1="50" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;&lt;circle cx="70" cy="90" r="20" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="70" y="94" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;S1&lt;/text&gt;&lt;circle cx="230" cy="90" r="20" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="230" y="94" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;S2&lt;/text&gt;&lt;circle cx="70" cy="230" r="20" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="70" y="234" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;S3&lt;/text&gt;&lt;circle cx="230" cy="230" r="20" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="230" y="234" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;S4&lt;/text&gt;&lt;line x1="70" y1="90" x2="150" y2="160" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="230" y1="90" x2="150" y2="160" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="70" y1="230" x2="150" y2="160" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="230" y1="230" x2="150" y2="160" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="95" y="140" width="110" height="40" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="150" y="164" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;ESB&lt;/text&gt;&lt;line x1="150" y1="180" x2="150" y2="290" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="95" y="290" width="110" height="36" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="150" y="313" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Shared DB&lt;/text&gt;&lt;text x="150" y="345" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Central bus + shared data&lt;/text&gt;&lt;circle cx="400" cy="90" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="400" y="94" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;M1&lt;/text&gt;&lt;circle cx="560" cy="90" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="560" y="94" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;M2&lt;/text&gt;&lt;circle cx="400" cy="230" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="400" y="234" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;M3&lt;/text&gt;&lt;circle cx="560" cy="230" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="560" y="234" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;M4&lt;/text&gt;&lt;line x1="400" y1="90" x2="560" y2="90" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="400" y1="90" x2="400" y2="230" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="560" y1="90" x2="560" y2="230" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="400" y1="230" x2="560" y2="230" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="330" y="55" width="40" height="18" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="350" y="68" text-anchor="middle" font-size="9" style="fill:var(--content)"&gt;db&lt;/text&gt;&lt;rect x="590" y="55" width="40" height="18" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="610" y="68" text-anchor="middle" font-size="9" style="fill:var(--content)"&gt;db&lt;/text&gt;&lt;rect x="330" y="252" width="40" height="18" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="350" y="265" text-anchor="middle" font-size="9" style="fill:var(--content)"&gt;db&lt;/text&gt;&lt;rect x="590" y="252" width="40" height="18" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.2"/&gt;&lt;text x="610" y="265" text-anchor="middle" font-size="9" style="fill:var(--content)"&gt;db&lt;/text&gt;&lt;text x="480" y="345" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Direct calls, DB per service&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;SOA&lt;/th&gt;
&lt;th&gt;Microservices&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Communication mechanism&lt;/td&gt;
&lt;td&gt;Services talk through a central Enterprise Service Bus using protocols like SOAP/WS-*&lt;/td&gt;
&lt;td&gt;Services talk directly via lightweight REST/gRPC calls or message brokers, no mandatory central bus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service granularity&lt;/td&gt;
&lt;td&gt;Coarse-grained, often modeling whole business processes&lt;/td&gt;
&lt;td&gt;Fine-grained, each service owns a single business capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data ownership&lt;/td&gt;
&lt;td&gt;Services frequently share a common database or canonical data model&lt;/td&gt;
&lt;td&gt;Each service owns and manages its own private database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment unit&lt;/td&gt;
&lt;td&gt;Services often share application servers or deployment packages&lt;/td&gt;
&lt;td&gt;Each service is deployed and scaled independently, typically in its own container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technology stack&lt;/td&gt;
&lt;td&gt;Standardized enterprise-wide on common platforms and protocols&lt;/td&gt;
&lt;td&gt;Polyglot — each team chooses its own language, framework, and datastore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fault isolation&lt;/td&gt;
&lt;td&gt;The ESB is a potential single point of failure affecting many services&lt;/td&gt;
&lt;td&gt;Failures are isolated to individual services, limiting blast radius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Governance &amp;amp; teams&lt;/td&gt;
&lt;td&gt;Centralized architecture review board and IT governance&lt;/td&gt;
&lt;td&gt;Decentralized ownership by small, autonomous teams per service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical origin&lt;/td&gt;
&lt;td&gt;Emerged from large-scale enterprise integration needs in the 2000s&lt;/td&gt;
&lt;td&gt;Emerged from cloud-native, DevOps-driven practices in the 2010s&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;SOA centralizes routing and transformation logic in an &lt;strong class="kw"&gt;ESB&lt;/strong&gt;, while microservices push that logic into the endpoints themselves.&lt;/li&gt;
&lt;li&gt;Microservices mandate &lt;strong class="kw"&gt;database per service&lt;/strong&gt;, whereas SOA services commonly share a data layer.&lt;/li&gt;
&lt;li&gt;SOA favors &lt;strong class="kw"&gt;reusable coarse-grained services&lt;/strong&gt; across the enterprise; microservices favor small, single-purpose services.&lt;/li&gt;
&lt;li&gt;Microservices deploy and scale via &lt;strong class="kw"&gt;independent containers&lt;/strong&gt;, while SOA services often share application servers.&lt;/li&gt;
&lt;li&gt;SOA relies on heavyweight standards like &lt;strong class="kw"&gt;SOAP/WS-*&lt;/strong&gt;; microservices typically use lightweight REST or gRPC.&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;SOA&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Client-Server vs Peer-to-Peer: Network Architecture Compared</title><link>https://comparison.metacog.co.kr/posts/2026-08-04-client-server-vs-peer-to-peer-network-architecture-compared/</link><pubDate>Tue, 04 Aug 2026 05:15:26 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-04-client-server-vs-peer-to-peer-network-architecture-compared/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Client-Server and peer-to-peer describe who talks to whom on a network: one funnels every request through a &lt;strong class="kw"&gt;central server&lt;/strong&gt;, the other lets nodes exchange data directly as &lt;strong class="kw"&gt;equal peers&lt;/strong&gt;. The choice shapes scalability, fault tolerance, and who ultimately controls the data.&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;text x="160" y="30" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Client-Server&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Peer-to-Peer&lt;/text&gt;&lt;line x1="320" y1="50" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;rect x="130" y="70" width="100" height="40" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="180" y="95" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Server&lt;/text&gt;&lt;line x1="180" y1="110" x2="70" y2="240" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="180" y1="110" x2="180" y2="280" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="180" y1="110" x2="290" y2="240" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;circle cx="70" cy="255" r="22" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;circle cx="180" cy="295" r="22" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;circle cx="290" cy="255" r="22" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="70" y="259" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;C&lt;/text&gt;&lt;text x="180" y="299" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;C&lt;/text&gt;&lt;text x="290" y="259" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;C&lt;/text&gt;&lt;text x="180" y="330" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;All requests routed through server&lt;/text&gt;&lt;g style="stroke:var(--compare-b)" stroke-width="1" opacity="0.55"&gt;&lt;line x1="480" y1="90" x2="575" y2="159"/&gt;&lt;line x1="480" y1="90" x2="539" y2="271"/&gt;&lt;line x1="480" y1="90" x2="421" y2="271"/&gt;&lt;line x1="480" y1="90" x2="385" y2="159"/&gt;&lt;line x1="575" y1="159" x2="539" y2="271"/&gt;&lt;line x1="575" y1="159" x2="421" y2="271"/&gt;&lt;line x1="575" y1="159" x2="385" y2="159"/&gt;&lt;line x1="539" y1="271" x2="421" y2="271"/&gt;&lt;line x1="539" y1="271" x2="385" y2="159"/&gt;&lt;line x1="421" y1="271" x2="385" y2="159"/&gt;&lt;/g&gt;&lt;circle cx="480" cy="90" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;circle cx="575" cy="159" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;circle cx="539" cy="271" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;circle cx="421" cy="271" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;circle cx="385" cy="159" r="20" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="94" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;P&lt;/text&gt;&lt;text x="575" y="163" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;P&lt;/text&gt;&lt;text x="539" y="275" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;P&lt;/text&gt;&lt;text x="421" y="275" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;P&lt;/text&gt;&lt;text x="385" y="163" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;P&lt;/text&gt;&lt;text x="480" y="330" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Peers connect directly to each other&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;Client-Server&lt;/th&gt;
&lt;th&gt;Peer-to-Peer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node roles&lt;/td&gt;
&lt;td&gt;Clients and servers have fixed, asymmetric roles&lt;/td&gt;
&lt;td&gt;Every node acts as both client and server (servent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection establishment&lt;/td&gt;
&lt;td&gt;Clients connect to a known server address (DNS/IP)&lt;/td&gt;
&lt;td&gt;Nodes discover peers via bootstrap lists, DHTs, or trackers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request handling&lt;/td&gt;
&lt;td&gt;Server processes and responds to each client request&lt;/td&gt;
&lt;td&gt;Any peer can serve or request data from any other peer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource provisioning&lt;/td&gt;
&lt;td&gt;Server owns the compute, storage, and bandwidth&lt;/td&gt;
&lt;td&gt;Resources are contributed and shared across participating peers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability pattern&lt;/td&gt;
&lt;td&gt;Scaling requires adding server capacity or replicas&lt;/td&gt;
&lt;td&gt;Scaling often improves as more peers join and share load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fault tolerance&lt;/td&gt;
&lt;td&gt;Server outage disrupts all clients (single point of failure)&lt;/td&gt;
&lt;td&gt;Network tolerates individual peer failures; no single point of failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security &amp;amp; trust&lt;/td&gt;
&lt;td&gt;Trust is centralized; server enforces auth and access control&lt;/td&gt;
&lt;td&gt;Trust is distributed; peers must verify each other independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical examples&lt;/td&gt;
&lt;td&gt;Web apps, REST APIs, email, banking systems&lt;/td&gt;
&lt;td&gt;BitTorrent, blockchain networks, LAN gaming&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;Client-Server relies on a &lt;strong class="kw"&gt;central server&lt;/strong&gt; as the single source of truth; peer-to-peer distributes data with no authoritative hub.&lt;/li&gt;
&lt;li&gt;Adding capacity in client-server means scaling the &lt;strong class="kw"&gt;server tier&lt;/strong&gt;; in peer-to-peer, each new node can add capacity to the network.&lt;/li&gt;
&lt;li&gt;A server outage is a &lt;strong class="kw"&gt;single point of failure&lt;/strong&gt; for client-server, while peer-to-peer degrades gracefully as peers leave.&lt;/li&gt;
&lt;li&gt;Client-server centralizes &lt;strong class="kw"&gt;access control&lt;/strong&gt;, while peer-to-peer pushes trust and verification onto each peer.&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;Client-Server&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Synchronous vs Asynchronous Communication: Blocking Calls vs Non-Blocking Messaging</title><link>https://comparison.metacog.co.kr/posts/2026-08-04-synchronous-vs-asynchronous-communication-blocking-calls-vs/</link><pubDate>Tue, 04 Aug 2026 05:12:35 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-04-synchronous-vs-asynchronous-communication-blocking-calls-vs/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Synchronous communication is a model where the caller sends a request and then &lt;strong class="kw"&gt;blocks&lt;/strong&gt;, halting its own execution until a response arrives. Asynchronous communication lets the caller fire off a message and continue working immediately, handling the eventual reply through a &lt;strong class="kw"&gt;callback or queue&lt;/strong&gt; whenever it arrives. The choice shapes latency tolerance, resource usage, failure handling, and how tightly services are coupled in time.&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" font-family="sans-serif"&gt;
&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;
&lt;text x="160" y="35" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Synchronous&lt;/text&gt;
&lt;text x="480" y="35" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Asynchronous&lt;/text&gt;
&lt;line x1="90" y1="55" x2="90" y2="325" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;
&lt;line x1="240" y1="55" x2="240" y2="325" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;
&lt;text x="90" y="52" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Caller&lt;/text&gt;
&lt;text x="240" y="52" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Service&lt;/text&gt;
&lt;line x1="90" y1="90" x2="240" y2="110" style="stroke:var(--compare-a)" stroke-width="2"/&gt;
&lt;polygon points="240,110 230,105 230,115" style="fill:var(--compare-a)"/&gt;
&lt;text x="150" y="88" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;request&lt;/text&gt;
&lt;rect x="83" y="110" width="14" height="120" rx="2" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="65" y="175" text-anchor="middle" style="fill:var(--secondary)" font-size="11" transform="rotate(-90 65 175)"&gt;blocked&lt;/text&gt;
&lt;rect x="233" y="110" width="14" height="100" rx="2" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="260" y="160" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;processing&lt;/text&gt;
&lt;line x1="240" y1="210" x2="90" y2="230" style="stroke:var(--compare-a)" stroke-width="2"/&gt;
&lt;polygon points="90,230 100,225 100,235" style="fill:var(--compare-a)"/&gt;
&lt;text x="165" y="215" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;response&lt;/text&gt;
&lt;text x="90" y="255" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;resumes&lt;/text&gt;
&lt;line x1="410" y1="55" x2="410" y2="325" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;
&lt;line x1="560" y1="55" x2="560" y2="325" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;
&lt;text x="410" y="52" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Caller&lt;/text&gt;
&lt;text x="560" y="52" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Service&lt;/text&gt;
&lt;line x1="410" y1="90" x2="560" y2="105" style="stroke:var(--compare-b)" stroke-width="2"/&gt;
&lt;polygon points="560,105 550,100 550,110" style="fill:var(--compare-b)"/&gt;
&lt;text x="480" y="88" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;send&lt;/text&gt;
&lt;rect x="385" y="115" width="50" height="30" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="410" y="134" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;other work&lt;/text&gt;
&lt;rect x="553" y="105" width="14" height="120" rx="2" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="590" y="165" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;processing&lt;/text&gt;
&lt;line x1="560" y1="225" x2="410" y2="255" style="stroke:var(--compare-b)" stroke-width="2" stroke-dasharray="5 3"/&gt;
&lt;polygon points="410,255 420,250 420,260" style="fill:var(--compare-b)"/&gt;
&lt;text x="500" y="235" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;callback / event&lt;/text&gt;
&lt;rect x="385" y="265" width="50" height="30" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="410" y="284" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;handle reply&lt;/text&gt;
&lt;line x1="20" y1="60" x2="20" y2="320" style="stroke:var(--secondary)" stroke-width="1"/&gt;
&lt;polygon points="20,320 16,310 24,310" style="fill:var(--secondary)"/&gt;
&lt;text x="20" y="335" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;time&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;Synchronous&lt;/th&gt;
&lt;th&gt;Asynchronous&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Call initiation&lt;/td&gt;
&lt;td&gt;Caller sends request and immediately waits for it to complete&lt;/td&gt;
&lt;td&gt;Caller sends a message and continues its own execution right away&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution model&lt;/td&gt;
&lt;td&gt;Caller thread blocks until the response returns inline&lt;/td&gt;
&lt;td&gt;Caller thread is free; response is handled via callback, event, or poll later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coupling in time&lt;/td&gt;
&lt;td&gt;Both sender and receiver must be available and reachable at the same moment&lt;/td&gt;
&lt;td&gt;Sender and receiver need not be online simultaneously; a broker bridges the gap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response delivery&lt;/td&gt;
&lt;td&gt;Direct return value over the same connection used for the request&lt;/td&gt;
&lt;td&gt;Message queue, event bus, webhook, or polling delivers the result separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Failure surfaces immediately to the caller as an exception or timeout&lt;/td&gt;
&lt;td&gt;Failure is detected later via retries, dead-letter queues, or timeout callbacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordering &amp;amp; concurrency&lt;/td&gt;
&lt;td&gt;One call in flight per thread, so ordering is implicit and easy to reason about&lt;/td&gt;
&lt;td&gt;Many calls can be in flight concurrently, so ordering must be handled explicitly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource usage&lt;/td&gt;
&lt;td&gt;Thread and connection are held open for the full duration of the call&lt;/td&gt;
&lt;td&gt;Thread is released immediately; resources are consumed only during actual processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical transport&lt;/td&gt;
&lt;td&gt;REST/HTTP request-response, gRPC unary calls, direct RPC&lt;/td&gt;
&lt;td&gt;Message queues (Kafka, RabbitMQ, SQS), event streams, webhooks&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;Synchronous callers &lt;strong class="kw"&gt;block&lt;/strong&gt; until a response returns; asynchronous callers proceed without waiting.&lt;/li&gt;
&lt;li&gt;Synchronous ties sender and receiver together in time; asynchronous decouples them through a &lt;strong class="kw"&gt;broker&lt;/strong&gt; or queue.&lt;/li&gt;
&lt;li&gt;Synchronous failures surface immediately as timeouts or exceptions; asynchronous failures often need &lt;strong class="kw"&gt;dead-letter&lt;/strong&gt; handling or retries.&lt;/li&gt;
&lt;li&gt;Synchronous holds a thread or connection open for the call&amp;rsquo;s duration; asynchronous frees the caller, trading immediacy for &lt;strong class="kw"&gt;throughput&lt;/strong&gt;.&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;Synchronous&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Cache-Aside vs Write-Through vs Write-Behind: Caching Strategies Compared</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-cache-aside-vs-write-through-vs-write-behind-caching-strateg/</link><pubDate>Sun, 02 Aug 2026 23:52:10 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-cache-aside-vs-write-through-vs-write-behind-caching-strateg/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Caching strategies differ mainly in who updates the cache and when. &lt;strong class="kw"&gt;Cache-Aside&lt;/strong&gt; leaves the application responsible for loading data into the cache on a miss and writing straight to the database, while &lt;strong class="kw"&gt;write-through/write-behind&lt;/strong&gt; caches push writes through the cache itself — synchronously for durability or asynchronously for speed. The choice shapes consistency guarantees, crash-safety, and how much write latency the app absorbs.&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,0 L10,5 L0,10 z" 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,0 L10,5 L0,10 z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;text x="16" y="28" font-size="15" font-weight="bold" style="fill:var(--primary)"&gt;Cache-Aside&lt;/text&gt;&lt;rect x="20" y="50" width="70" height="36" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="55" y="72" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;App&lt;/text&gt;&lt;rect x="285" y="50" width="80" height="36" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="325" y="72" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Cache&lt;/text&gt;&lt;rect x="545" y="50" width="70" height="36" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="580" y="72" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;DB&lt;/text&gt;&lt;line x1="95" y1="68" x2="280" y2="68" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;text x="185" y="60" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;read&lt;/text&gt;&lt;line x1="370" y1="68" x2="540" y2="68" stroke-dasharray="5,4" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)" marker-start="url(#arrowA)"/&gt;&lt;text x="455" y="95" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;on miss: fetch + populate&lt;/text&gt;&lt;line x1="10" y1="130" x2="630" y2="130" stroke-dasharray="4,4" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="16" y="153" font-size="15" font-weight="bold" style="fill:var(--primary)"&gt;Write-Through&lt;/text&gt;&lt;rect x="20" y="168" width="70" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="55" y="190" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;App&lt;/text&gt;&lt;rect x="285" y="168" width="80" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="325" y="190" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Cache&lt;/text&gt;&lt;rect x="545" y="168" width="70" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="580" y="190" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;DB&lt;/text&gt;&lt;line x1="95" y1="186" x2="280" y2="186" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="185" y="178" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;write&lt;/text&gt;&lt;line x1="370" y1="186" x2="540" y2="186" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="455" y="178" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;sync write&lt;/text&gt;&lt;line x1="10" y1="248" x2="630" y2="248" stroke-dasharray="4,4" style="stroke:var(--border)" stroke-width="1"/&gt;&lt;text x="16" y="271" font-size="15" font-weight="bold" style="fill:var(--primary)"&gt;Write-Behind&lt;/text&gt;&lt;rect x="20" y="286" width="70" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="55" y="308" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;App&lt;/text&gt;&lt;rect x="285" y="286" width="80" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="325" y="308" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Cache&lt;/text&gt;&lt;rect x="545" y="286" width="70" height="36" rx="4" stroke-dasharray="4,3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="580" y="308" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;DB&lt;/text&gt;&lt;line x1="95" y1="304" x2="280" y2="304" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="185" y="296" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;write (fast ack)&lt;/text&gt;&lt;line x1="370" y1="304" x2="540" y2="304" stroke-dasharray="5,4" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="455" y="296" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;async flush (delayed)&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;Cache-Aside&lt;/th&gt;
&lt;th&gt;Write-Through / Write-Behind&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read path&lt;/td&gt;
&lt;td&gt;App checks the cache first; on a miss, it reads from the DB itself and populates the cache&lt;/td&gt;
&lt;td&gt;Cache is always kept current on writes, so reads simply hit the cache without app-managed fallback logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write path&lt;/td&gt;
&lt;td&gt;App writes directly to the DB; the cache entry is invalidated or left stale until the next read&lt;/td&gt;
&lt;td&gt;App writes only to the cache; the cache layer propagates the write to the DB itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write latency perceived by app&lt;/td&gt;
&lt;td&gt;Only DB write latency, since the cache isn&amp;rsquo;t touched on write&lt;/td&gt;
&lt;td&gt;Write-Through waits for both cache and DB commit; Write-Behind returns after the cache write only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consistency between cache and DB&lt;/td&gt;
&lt;td&gt;Brief staleness window possible between invalidation and the next read&lt;/td&gt;
&lt;td&gt;Write-Through stays consistent immediately; Write-Behind lags until the queued flush completes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data loss risk on crash&lt;/td&gt;
&lt;td&gt;None — the DB is always the write target, so a cache crash loses nothing&lt;/td&gt;
&lt;td&gt;Write-Through has no loss; Write-Behind can lose unflushed writes if the cache crashes before flush&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation complexity&lt;/td&gt;
&lt;td&gt;App owns miss handling and invalidation logic explicitly&lt;/td&gt;
&lt;td&gt;Cache layer owns persistence logic; Write-Behind adds a queue/flush scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use case&lt;/td&gt;
&lt;td&gt;Read-heavy workloads with unpredictable key access, e.g. Redis in front of a relational DB&lt;/td&gt;
&lt;td&gt;Write-Through suits systems needing instant durability; Write-Behind suits high write-throughput systems tolerant of brief loss, e.g. metrics buffers&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;Cache-Aside puts the application in charge of both fetch-on-miss and invalidation, while write-through/write-behind push that responsibility into the &lt;strong class="kw"&gt;cache layer&lt;/strong&gt; itself&lt;/li&gt;
&lt;li&gt;Only Write-Behind delivers real &lt;strong class="kw"&gt;write latency&lt;/strong&gt; savings by acknowledging before the DB commit completes&lt;/li&gt;
&lt;li&gt;Write-Through guarantees immediate durability at write time, while Write-Behind trades some of that &lt;strong class="kw"&gt;durability&lt;/strong&gt; for throughput&lt;/li&gt;
&lt;li&gt;Cache-Aside is the only strategy where a cache outage never risks &lt;strong class="kw"&gt;data loss&lt;/strong&gt;, since writes never pass through it&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;Cache-Aside&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Monitoring vs Observability: Knowing Something's Wrong vs Knowing Why</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-monitoring-vs-observability-knowing-something-s-wrong-vs-kno/</link><pubDate>Sun, 02 Aug 2026 08:34:10 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-monitoring-vs-observability-knowing-something-s-wrong-vs-kno/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Monitoring watches a predefined set of metrics, logs, and checks against known failure modes and alerts you when thresholds are breached. Observability is a property of a system built so that its internal state can be inferred from its external outputs, letting you investigate questions you didn&amp;rsquo;t think to ask in advance. The distinction matters because monitoring answers &amp;lsquo;is something wrong?&amp;rsquo; while observability answers &amp;lsquo;why is it wrong?&amp;rsquo; for failures you&amp;rsquo;ve never seen before.&lt;/p&gt;</description></item><item><title>Failover vs Fallback: Redundant Takeover vs Degraded Alternative</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-failover-vs-fallback-redundant-takeover-vs-degraded-alternat/</link><pubDate>Sun, 02 Aug 2026 08:18:29 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-failover-vs-fallback-redundant-takeover-vs-degraded-alternat/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Failover and fallback both describe what a system does when something breaks, but they differ in what changes. Failover swaps a failed component for an identical redundant one so behavior stays the same, while fallback switches to a different, usually simpler or lower-fidelity path when the preferred one is unavailable. Confusing the two leads to designs that promise seamless continuity but actually degrade functionality, or vice versa.&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;text x="160" y="30" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Failover&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Fallback&lt;/text&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;&lt;rect x="100" y="50" width="120" height="36" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="73" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;line x1="160" y1="86" x2="160" y2="115" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,122 155,113 165,113" style="fill:var(--compare-a)"/&gt;&lt;rect x="70" y="124" width="180" height="48" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="143" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Primary (Active)&lt;/text&gt;&lt;text x="160" y="160" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;same behavior&lt;/text&gt;&lt;line x1="130" y1="130" x2="190" y2="166" style="stroke:var(--compare-a)" stroke-width="2.5"/&gt;&lt;line x1="190" y1="130" x2="130" y2="166" style="stroke:var(--compare-a)" stroke-width="2.5"/&gt;&lt;path d="M70,190 C40,220 40,240 70,255" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;polygon points="70,262 63,252 77,254" style="fill:var(--compare-a)"/&gt;&lt;rect x="70" y="258" width="180" height="48" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="277" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Standby (identical)&lt;/text&gt;&lt;text x="160" y="294" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;takes over, same output&lt;/text&gt;&lt;text x="160" y="330" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;redundant component, unchanged function&lt;/text&gt;&lt;rect x="420" y="50" width="120" height="36" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="73" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;line x1="480" y1="86" x2="480" y2="115" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,122 475,113 485,113" style="fill:var(--compare-b)"/&gt;&lt;rect x="390" y="124" width="180" height="48" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="143" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Primary Path&lt;/text&gt;&lt;text x="480" y="160" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;full behavior&lt;/text&gt;&lt;line x1="450" y1="130" x2="510" y2="166" style="stroke:var(--compare-b)" stroke-width="2.5"/&gt;&lt;line x1="510" y1="130" x2="450" y2="166" style="stroke:var(--compare-b)" stroke-width="2.5"/&gt;&lt;line x1="480" y1="172" x2="480" y2="250" style="stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;polygon points="480,258 473,248 487,248" style="fill:var(--compare-b)"/&gt;&lt;rect x="390" y="258" width="180" height="48" rx="6" style="fill:none;stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="5 3"/&gt;&lt;text x="480" y="277" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Fallback (degraded/default)&lt;/text&gt;&lt;text x="480" y="294" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;reduced or cached response&lt;/text&gt;&lt;text x="480" y="330" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;alternate path, changed function&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;Failover&lt;/th&gt;
&lt;th&gt;Fallback&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core action&lt;/td&gt;
&lt;td&gt;Switch to a redundant, identical component&lt;/td&gt;
&lt;td&gt;Switch to a different, usually simpler alternative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Functional parity&lt;/td&gt;
&lt;td&gt;Preserves full functionality and quality&lt;/td&gt;
&lt;td&gt;Often reduced functionality, accuracy, or freshness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical scope&lt;/td&gt;
&lt;td&gt;Infrastructure/system level (servers, nodes, DCs)&lt;/td&gt;
&lt;td&gt;Application/logic level (methods, values, services)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;Health check or heartbeat failure detection&lt;/td&gt;
&lt;td&gt;Exception, timeout, cache miss, or unmet condition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example&lt;/td&gt;
&lt;td&gt;Active database node dies; standby replica takes over queries transparently&lt;/td&gt;
&lt;td&gt;Live pricing API call fails; app falls back to last cached price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovery expectation&lt;/td&gt;
&lt;td&gt;Usually paired with failback once primary recovers&lt;/td&gt;
&lt;td&gt;Often stays on fallback until explicitly retried or root cause fixed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User-visible impact&lt;/td&gt;
&lt;td&gt;Ideally none, if failover is seamless&lt;/td&gt;
&lt;td&gt;Often visible as a lower-quality or generic result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design goal&lt;/td&gt;
&lt;td&gt;High availability / continuity of service&lt;/td&gt;
&lt;td&gt;Graceful degradation / resilience of a single call or feature&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;Failover replaces a broken component with an equivalent one; fallback replaces a preferred behavior with a lesser one.&lt;/li&gt;
&lt;li&gt;Failover targets infrastructure-level continuity (nodes, clusters, regions); fallback targets code-level resilience (a single function or request).&lt;/li&gt;
&lt;li&gt;Failover implies redundancy of identical capability; fallback implies acceptance of reduced capability.&lt;/li&gt;
&lt;li&gt;Failover is often followed by &amp;lsquo;failback&amp;rsquo; to the restored primary; fallback usually persists until the underlying issue is resolved or retried.&lt;/li&gt;
&lt;li&gt;A system can use both together: infrastructure fails over to a standby, while an individual call within that system falls back to cached data.&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;Failover&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>