<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: wheresthelag</title>
    <description>The latest articles on Forem by wheresthelag (@wheresthelag).</description>
    <link>https://forem.com/wheresthelag</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1105786%2Fdd6736f3-da5e-4004-bc79-af8331ea4d0b.png</url>
      <title>Forem: wheresthelag</title>
      <link>https://forem.com/wheresthelag</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wheresthelag"/>
    <language>en</language>
    <item>
      <title>Tracing a 2s Latency Spike to a Single SQL Query</title>
      <dc:creator>wheresthelag</dc:creator>
      <pubDate>Wed, 06 May 2026 08:45:05 +0000</pubDate>
      <link>https://forem.com/wheresthelag/tracing-a-2s-latency-spike-to-a-single-sql-query-me2</link>
      <guid>https://forem.com/wheresthelag/tracing-a-2s-latency-spike-to-a-single-sql-query-me2</guid>
      <description>&lt;p&gt;We received an alert around early morning 4AM indicating that our checkout service latency had jumped from its usual 50ms p99 to over 2 seconds. There were no errors, CPU usage was normal, and the database appeared healthy. Despite everything looking fine in the logs, users were clearly experiencing delays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial Checks&lt;/strong&gt;&lt;br&gt;
We started with the usual suspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application metrics: CPU and memory utilization were stable.&lt;/li&gt;
&lt;li&gt;Database health: PostgreSQL showed no signs of stress.&lt;/li&gt;
&lt;li&gt;Slow query logs: No entries, even with the threshold set to 1 second.&lt;/li&gt;
&lt;li&gt;Redis/cache layer: Operating as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Logs Weren’t Enough&lt;/strong&gt;&lt;br&gt;
Our logs provided detailed information about individual events such as HTTP requests, SQL executions, and service interactions. However, they lacked the context needed to understand how time was spent across the entire request lifecycle. Logs answered what happened, but not where the time went.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request Trace Overview&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Incoming Request (~2.1s total)&lt;br&gt;
├── Auth Service (~120ms)&lt;br&gt;
├── Business Logic (~150ms)&lt;br&gt;
└── Database Call (~35ms execution + ~2.05s data transfer)&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
To gain better visibility, we examined the transaction trace using opmanager nexus. The trace revealed that while the database executed the query quickly, the application thread spent significant time waiting to read the response from the network buffer &lt;code&gt;(SocketInputStream.read())&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identifying the Root Cause&lt;/strong&gt;&lt;br&gt;
The SQL query involved was straightforward:&lt;br&gt;
&lt;code&gt;SELECT * FROM inventory_logs WHERE item_id = ?;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A recent schema update had introduced a JSONB column storing detailed audit information. For frequently updated items, this column had grown to more than 15MB per row. Because of the &lt;code&gt;SELECT * statement&lt;/code&gt;, the application fetched this entire payload, leading to significant network transfer and deserialization overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;br&gt;
We updated the query to retrieve only the necessary columns:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT status, last_updated&lt;br&gt;
FROM inventory_logs&lt;br&gt;
WHERE item_id = ?;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The impact was immediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database time: Reduced from ~35ms to ~12ms.&lt;/li&gt;
&lt;li&gt;Result processing time: Dropped from ~2.05s to a negligible level.&lt;/li&gt;
&lt;li&gt;End-to-end latency: Improved from ~2.1 seconds to ~12ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Not all slow requests are caused by slow queries.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;SELECT *&lt;/code&gt; in production systems.&lt;/li&gt;
&lt;li&gt;Distinguish between query execution and data transfer time.&lt;/li&gt;
&lt;li&gt;Logs provide events, not end-to-end context.&lt;/li&gt;
&lt;li&gt;Distributed tracing is essential for accurate root cause analysis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The invisible bottleneck&lt;/strong&gt;&lt;br&gt;
This incident highlighted how performance bottlenecks can arise from subtle changes in data access patterns. Even well-indexed queries can introduce latency if they return more data than necessary. Differentiating between query execution and data transfer is essential for accurate root cause analysis.&lt;/p&gt;

</description>
      <category>apm</category>
      <category>postgres</category>
      <category>webdev</category>
      <category>monitoring</category>
    </item>
  </channel>
</rss>
