<?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 Kinoko's TIL Log</title><link>https://kinoko-tech-blog-theta.vercel.app/tags/distributed-systems/</link><description>Recent content in Distributed-Systems on Kinoko's TIL Log</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>pippimotta@gmail.com (Kinoko)</managingEditor><webMaster>pippimotta@gmail.com (Kinoko)</webMaster><copyright>&amp;copy; Brewed by Kinoko</copyright><lastBuildDate>Sun, 14 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kinoko-tech-blog-theta.vercel.app/tags/distributed-systems/index.xml" rel="self" type="application/rss+xml"/><item><title>GCP Pub-Sub Message Lifecycle Mental Model</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/gcp-pub-sub-message-lifecycle-mental-model/</link><pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/gcp-pub-sub-message-lifecycle-mental-model/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;All core GCP Pub/Sub settings can be mapped to four stages of a message&amp;rsquo;s lifecycle: how long it lives, how many to pull at once, how long to wait for a response, and what to do on failure.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;The parameters scattered across the docs look like a lot, but each one guards a specific stage in a message&amp;rsquo;s journey from publish to final processing (or abandonment). Organizing them by lifecycle stage &amp;ndash; instead of memorizing each one individually &amp;ndash; lets you instantly locate what any new parameter controls. This mental model also ties together the responsibility boundaries of the three resource layers: Topic, Subscription, and Dead Letter.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>All core GCP Pub/Sub settings can be mapped to four stages of a message&rsquo;s lifecycle: how long it lives, how many to pull at once, how long to wait for a response, and what to do on failure.</p>
<h2 id="explanation">Explanation</h2>
<p>The parameters scattered across the docs look like a lot, but each one guards a specific stage in a message&rsquo;s journey from publish to final processing (or abandonment). Organizing them by lifecycle stage &ndash; instead of memorizing each one individually &ndash; lets you instantly locate what any new parameter controls. This mental model also ties together the responsibility boundaries of the three resource layers: Topic, Subscription, and Dead Letter.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<h3 id="1-parameters-by-lifecycle-stage">1. Parameters by lifecycle stage</h3>
<table>
	<thead>
			<tr>
					<th>Stage</th>
					<th>Key parameter</th>
					<th>One-liner</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Retention</td>
					<td><code>MessageRetentionDuration</code></td>
					<td>How long an unacked message stays alive</td>
			</tr>
			<tr>
					<td>Retention</td>
					<td><code>TopicRetentionDuration</code></td>
					<td>How long the topic keeps a message after ack (for replay)</td>
			</tr>
			<tr>
					<td>Flow Control</td>
					<td><code>MaxOutstandingMessages</code></td>
					<td>Max unacked messages a subscriber can hold</td>
			</tr>
			<tr>
					<td>Flow Control</td>
					<td><code>MaxOutstandingBytes</code></td>
					<td>Same, but measured in bytes (OR logic with messages)</td>
			</tr>
			<tr>
					<td>Flow Control</td>
					<td><code>NumGoroutines</code></td>
					<td>Number of parallel workers running callbacks</td>
			</tr>
			<tr>
					<td>Ack/Extension</td>
					<td><code>AckDeadline</code></td>
					<td>Seconds to ack before redelivery</td>
			</tr>
			<tr>
					<td>Ack/Extension</td>
					<td><code>MaxExtension</code></td>
					<td>Upper bound on client library auto-extending the deadline</td>
			</tr>
			<tr>
					<td>Retry/DLQ</td>
					<td><code>MaxDeliveryAttempts</code></td>
					<td>Max attempts before moving to DLQ</td>
			</tr>
			<tr>
					<td>Retry/DLQ</td>
					<td><code>MinBackoff</code> / <code>MaxBackoff</code></td>
					<td>Exponential backoff range for push retry</td>
			</tr>
			<tr>
					<td>Semantics</td>
					<td><code>ExactlyOnceDelivery</code></td>
					<td>Whether to enable exactly-once (has throughput cost)</td>
			</tr>
			<tr>
					<td>Semantics</td>
					<td><code>EnableMessageOrdering</code></td>
					<td>Preserve order within the same ordering key</td>
			</tr>
	</tbody>
</table>
<p>The relationship between the three flow control params: <code>MaxOutstandingMessages</code> is the number of seats in the waiting room, <code>NumGoroutines</code> is the number of exam rooms, and <code>MaxOutstandingBytes</code> is the physical space limit of the waiting room.</p>
<h3 id="2-topic-vs-subscription-two-layer-responsibilities">2. Topic vs Subscription: two-layer responsibilities</h3>
<p>A Topic is a named channel responsible for fan-out: one published message gives each subscription its own complete copy. A Subscription handles load balancing: with N pods underneath, they act as competing consumers where the same message goes to only one pod. Each layer has its own job &ndash; the Topic decides &ldquo;which systems receive this event,&rdquo; and the Subscription decides &ldquo;how this system distributes workload internally.&rdquo;</p>
<h3 id="3-subscription-to-topic-binding-is-infrastructure">3. Subscription-to-Topic binding is infrastructure</h3>
<p>A Subscription binds to a Topic at creation time. After that, application code only uses <code>client.Subscription(&quot;name&quot;)</code> to retrieve the existing resource without referencing the topic. This is the same pattern as K8s Pod <code>volumeMounts</code> pointing to a PVC name &ndash; the infrastructure layer binds things up front, and the application layer just uses the name. In practice, this step is usually handled by Terraform, not written in application code.</p>
<h3 id="4-dead-letter-policy-isolates-poison-messages">4. Dead Letter Policy isolates poison messages</h3>
<p>When a message gets nacked or times out repeatedly up to <code>MaxDeliveryAttempts</code>, the broker stops sending it to the original subscription and forwards it to a dead letter topic. A DLQ topic is just a regular topic that semantically collects &ldquo;messages that could not be processed.&rdquo; The core value is separating &ldquo;cannot process&rdquo; from &ldquo;not yet processed,&rdquo; preventing a single poison message from occupying <code>MaxOutstandingMessages</code> quota and dragging down overall throughput. This is even more critical with <code>EnableMessageOrdering</code> on &ndash; messages behind the same ordering key would all be blocked.</p>
]]></content:encoded></item></channel></rss>