<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go Networking &amp; Distributed Systems on Atharva Pandey</title><link>https://atharva.page/series/go-networking--distributed-systems/</link><description>Recent content in Go Networking &amp; Distributed Systems on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Thu, 15 May 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/go-networking--distributed-systems/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: gRPC Basics and Streaming — Protobuf on the wire, types in your code</title><link>https://atharva.page/post/go/go-net-grpc/</link><pubDate>Thu, 15 May 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-grpc/</guid><description>&lt;p&gt;My team migrated a set of internal service APIs from JSON over HTTP/1.1 to gRPC roughly two years ago. The motivating factors were type safety across service boundaries, binary serialization that was 5-10x smaller on the wire, and bidirectional streaming that HTTP/1.1 can&amp;rsquo;t do at all. The migration took about a week per service and the performance improvements were immediately visible in our latency percentiles.&lt;/p&gt;
&lt;p&gt;gRPC is a Remote Procedure Call framework that runs over HTTP/2. The interface is defined in Protocol Buffers — a language-neutral schema language — and the gRPC toolchain generates client and server stubs in Go. You call a method; the framework handles serialization, connection management, and streaming.&lt;/p&gt;</description></item><item><title>Lesson 7: Outbox Pattern — Reliable events without distributed transactions</title><link>https://atharva.page/post/go/go-net-outbox/</link><pubDate>Thu, 20 Mar 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-outbox/</guid><description>&lt;p&gt;Here&amp;rsquo;s a bug that&amp;rsquo;s bitten almost every distributed system I&amp;rsquo;ve worked on: a service saves a record to the database and then publishes an event to Kafka. The record is saved. The process crashes before the publish. Now the database says the order exists, but the fulfillment service never heard about it. The order sits in limbo forever.&lt;/p&gt;
&lt;p&gt;The naive fix — wrapping both operations in a transaction — doesn&amp;rsquo;t work because Kafka isn&amp;rsquo;t a participant in your database transaction. You can&amp;rsquo;t two-phase commit across Postgres and Kafka without a distributed transaction coordinator, and nobody wants to operate one of those in production.&lt;/p&gt;</description></item><item><title>Lesson 6: Eventual Consistency — Your data will be wrong, temporarily</title><link>https://atharva.page/post/go/go-net-eventual-consistency/</link><pubDate>Sat, 25 Jan 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-eventual-consistency/</guid><description>&lt;p&gt;I used to believe that eventual consistency was an exotic property of distributed databases that I&amp;rsquo;d only encounter at Google scale. Then I added a Redis cache to a simple CRUD service and immediately had a bug where users couldn&amp;rsquo;t see their own updates. Welcome to eventual consistency — it shows up the moment you have two places that store the same data.&lt;/p&gt;
&lt;p&gt;Eventual consistency means that if you stop writing to a system, all replicas will eventually converge to the same value. The word &amp;ldquo;eventually&amp;rdquo; can mean milliseconds or minutes, depending on the system. The hard part isn&amp;rsquo;t the definition — it&amp;rsquo;s designing application code that works correctly even when replicas haven&amp;rsquo;t converged yet.&lt;/p&gt;</description></item><item><title>Lesson 5: Message Queues in Go — NATS, RabbitMQ, Kafka — pick your tradeoff</title><link>https://atharva.page/post/go/go-net-message-queues/</link><pubDate>Wed, 20 Nov 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-message-queues/</guid><description>&lt;p&gt;I&amp;rsquo;ve integrated all three of these systems in production Go services, and the question I get most often is: &amp;ldquo;Which one should I use?&amp;rdquo; The honest answer is that it depends on what guarantee you actually need — and most engineers pick based on familiarity or hype rather than requirements. NATS, RabbitMQ, and Kafka solve meaningfully different problems, and choosing the wrong one creates operational pain that no amount of clever application code can fix.&lt;/p&gt;</description></item><item><title>Lesson 4: Connection Pooling — One connection per request is a performance bug</title><link>https://atharva.page/post/go/go-net-conn-pooling/</link><pubDate>Sat, 12 Oct 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-conn-pooling/</guid><description>&lt;p&gt;The first time I benchmarked a Go service against a database, the numbers were embarrassing. Five hundred requests per second, each one taking 30 milliseconds to execute a trivially simple query. The query itself took 2 milliseconds on the database server. The other 28 milliseconds were TCP handshake plus TLS plus PostgreSQL authentication — repeated for every single request because I had no connection pool.&lt;/p&gt;
&lt;p&gt;Connection pooling is one of those topics that feels like an advanced optimization until you discover that nearly every database driver in Go already pools connections by default — you just have to configure the pool instead of leaving it at its default settings, which are almost always wrong for your workload.&lt;/p&gt;</description></item><item><title>Lesson 3: Circuit Breaking — Stop calling the service that's already down</title><link>https://atharva.page/post/go/go-net-circuit-breaker/</link><pubDate>Wed, 28 Aug 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-circuit-breaker/</guid><description>&lt;p&gt;There&amp;rsquo;s a particular kind of production incident that goes like this: Service A calls Service B. Service B starts responding slowly because its database is under pressure. Service A&amp;rsquo;s goroutines pile up waiting for responses. After a few minutes, Service A is out of memory, and now two services are down instead of one. The postmortem note says &amp;ldquo;cascading failure.&amp;rdquo; The fix, which nobody implemented, is a circuit breaker.&lt;/p&gt;
&lt;p&gt;The circuit breaker pattern comes from electrical engineering. When too much current flows through a circuit, the breaker trips — it opens the circuit and prevents more current from flowing until someone resets it. In software, the &amp;ldquo;current&amp;rdquo; is requests, and &amp;ldquo;tripping&amp;rdquo; means refusing to make calls to a failing downstream instead of piling up timeouts and consuming resources.&lt;/p&gt;</description></item><item><title>Lesson 2: Retries with Exponential Backoff — Retry right or retry forever</title><link>https://atharva.page/post/go/go-net-retries/</link><pubDate>Sat, 20 Jul 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-retries/</guid><description>&lt;p&gt;I once worked on a service that retried failed HTTP requests in a tight loop with no backoff and no jitter. When our upstream had a brief outage, every instance of our service fired retries simultaneously, on the same cadence, forever. The upstream came back online, got immediately hammered by a synchronized retry storm from fifty instances, went down again, and the cycle repeated for forty minutes. The &amp;ldquo;retry&amp;rdquo; logic had turned a five-minute outage into a cascading failure.&lt;/p&gt;</description></item><item><title>Lesson 1: HTTP Client Internals — Transport, connection reuse, and the pool you forgot to configure</title><link>https://atharva.page/post/go/go-net-http-client/</link><pubDate>Fri, 14 Jun 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-net-http-client/</guid><description>&lt;p&gt;The first production Go service I wrote hammered our database proxy with thousands of new TCP connections every minute. The proxy started refusing connections. The on-call engineer thought it was the database. It wasn&amp;rsquo;t. It was me — specifically, my decision to create a new &lt;code&gt;http.Client&lt;/code&gt; on every request and then never read the response body to completion. I spent three hours debugging what turned out to be two lines of misunderstood standard library behavior.&lt;/p&gt;</description></item></channel></rss>