<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="301183" data-post-id="301183">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="davydog187" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/davydog187/120/10201_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  davydog187
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Fair enough on performance, and I mostly agree with what you’re saying, but I wouldn’t start there. You can get a lot of mileage just from tracing Phoenix + Ecto.</p>
<p>Observability is a garden, you need to tend to it. Over-instrumenting has a cost, you will either</p>
<ol>
<li>Run out of your event quota quickly</li>
<li>Be forced to sample and lose data</li>
<li>Spend more resources than you intended on shipping spans out of your app (same goes for logging/etc!)</li>
</ol> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="301183" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tracking-down-slow-queries-in-ecto/58121/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-301183" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="301183"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #21"></div>
  </section>
</div>
    <div class="postbit" id="301909" data-post-id="301909">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="gmile" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/gmile/120/33982_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  gmile
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Before moving forward with integration with OpenTelemetry &amp; Cloud Traces in Google Cloud, we’ve implemented a very simple way to match “slow queries” to the code. It’s based on small piece of code that:</p>
<ol>
<li>creates an ETS table,</li>
<li>attaches a telemetry handler to write queries and stacktraces (and a single sample of query parameters for that query) to the ETS table,</li>
<li>a function to scan ETS table for a matching query using a string fragment of the query.</li>
</ol>
<p>Sample parameters are not really necessary, but are a convenience to be able to later quickly re-run the query, or assemble an <code>EXPLAIN (...)</code> from it. On a typical day our app issues maybe under 1000 unique SQL queries. The in-memory size of ETS table I’ve seen so far was well under 20 megabytes.</p>
<p>The script looks like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule EctoStacktraces do
  def setup() do
    :ets.new(:queries_and_stacktraces, [:set, :named_table, :public])
    :telemetry.attach("ecto-stacktrace-tracking", [:my_application, :repo, :query], &amp;handle_event/4, %{})
  end

  def filter(string) do
    {:ok, regex} =
      string
      |&gt; Regex.escape()
      |&gt; Regex.compile()

    find = fn {query, _stacktrace, _cast_params, _measurements} = item, acc -&gt;
      if String.match?(query, regex) do
        [item | acc]
      else
        acc
      end
    end

    :ets.foldl(find, [], :queries_and_stacktraces)
  end

  def handle_event([:my_application, :repo, :query], measurements, metadata, _config) do
    :ets.insert(:queries_and_stacktraces, {metadata[:query], metadata[:stacktrace], metadata[:cast_params], measurements})
  end
end
</code></pre>
<p>Knowing a slow query reported by tools like “Query Insights” (feature of Google Cloud SQL):</p>
<p></p><div class="lightbox-wrapper"><a class="lightbox" href="https://forum.elixirforum.com/uploads/default/original/3X/a/8/a8825b6f368970d8d3b6e3fdc409cbe40abf0637.jpeg" data-download-href="https://forum.elixirforum.com/uploads/default/a8825b6f368970d8d3b6e3fdc409cbe40abf0637" title="image" rel="nofollow"><img src="https://forum.elixirforum.com/uploads/default/optimized/3X/a/8/a8825b6f368970d8d3b6e3fdc409cbe40abf0637_2_690x213.jpeg" alt="image" data-base62-sha1="o2Hvfhl5oRgkDGotm5owP0cjjkH" width="690" height="213" srcset="https://forum.elixirforum.com/uploads/default/optimized/3X/a/8/a8825b6f368970d8d3b6e3fdc409cbe40abf0637_2_690x213.jpeg, https://forum.elixirforum.com/uploads/default/optimized/3X/a/8/a8825b6f368970d8d3b6e3fdc409cbe40abf0637_2_1035x319.jpeg 1.5x, https://forum.elixirforum.com/uploads/default/original/3X/a/8/a8825b6f368970d8d3b6e3fdc409cbe40abf0637.jpeg 2x" data-dominant-color="F2F3F3"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">image</span><span class="informations">1200×371 35.4 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div><p></p>
<p>…we’ve been able to track suspicious down to the code using this technique:</p>
<ol>
<li>
<p>run:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">EctoStacktraces.setup()
</code></pre>
<p>This is done either by connecting to a running node remotely, or as part of <code>application.ex</code> for example,</p>
</li>
<li>
<p>some time goes by to let the slow query manifest itself,</p>
</li>
<li>
<p>then, knowing <code>INNER JOIN (SELECT ARRAY_AGG(sf0."path")</code> is part of the slow query:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[{query, stacktrace, _sample_params, _measurements}] =
  EctoStacktraces.filter(~s{INNER JOIN (SELECT ARRAY_AGG(sf0."path")})

IO.inspect(stacktrace)
</code></pre>
</li>
</ol>
<p>This part <code>INNER JOIN (SELECT ARRAY_AGG(sf0."path")</code> is taken from a service that reports slow queries, in our case it’s “Query Insights” feature in Google Cloud:</p>
<p>The above is very simple and obviously doesn’t survive process exit, for example detaching from a remote session, or restarting the application. But it helped us move forward with optimising several long-standing hard-to-locate SQL queries in the app. Also, looking at some of the stacktraces helped reveal code that hides calls to DB in private functions, that don’t show up in the stacktrace <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="301909" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/tracking-down-slow-queries-in-ecto/58121/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-301909" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="301909"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>