<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ruby and Rust developer specializing in backend performance - Paweł Urbanek</title>
    <description>Backend developer with over 10 years of experience. Specializing in Ruby on Rails, Rust, PostgreSQL, and backend performance.</description>
    <link>https://pawelurbanek.com/</link>
    <atom:link href="https://pawelurbanek.com/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Rustikon Talk - Rust Performance Profiling using hotpath-rs</title>
        <description>&lt;p&gt;Recently I gave a talk at the &lt;a href=&quot;https://www.rustikon.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Rustikon conference&lt;/a&gt; about &lt;a href=&quot;https://hotpath.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;hotpath-rs&lt;/a&gt; - a Rust profiler focused on time, memory, CPU, and async performance debugging. I cover the project’s origin story and how it works under the hood. I also show a live instrumentation demo featuring the &lt;a href=&quot;https://zed.dev&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Zed editor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was my first conference talk in 8+ years, so I hope you enjoy it!&lt;/p&gt;

&lt;div class=&quot;vid-container&quot;&gt;
  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/ir1WC_rO2Fk&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; encrypted-media&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&quot;links-and-resources&quot;&gt;Links and resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://hotpath.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;hotpath.rs documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/pawurb/zed/pull/2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Instrumented Zed fork&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mstange/samply&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;samply profiler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 17 May 2026 22:49:07 +0200</pubDate>
        <link>https://pawelurbanek.com/rust-hotpath-rustikon</link>
        <guid isPermaLink="true">https://pawelurbanek.com/rust-hotpath-rustikon</guid>
      </item>
    
      <item>
        <title>Lessons Learned Building High-Performance Rust Profiler</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://nnethercote.github.io/perf-book/profiling.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Rust performance book&lt;/a&gt; features over a dozen different profiling tools. So I’m not sure if the world needed a new Rust profiler. Still, I spent the last 6+ months building &lt;a href=&quot;https://hotpath.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;hotpath-rs&lt;/a&gt;. In this post, I’ll describe the design decisions behind the library and share a few performance challenges I encountered while working on it. We’ll go deep into the low-level details: cache-line contention, async futures instrumentation, and decoding raw CPU traces back into Rust symbols.&lt;/p&gt;

&lt;h2 id=&quot;hotpath-profiler-101&quot;&gt;hotpath profiler 101&lt;/h2&gt;

&lt;p&gt;The next section is a brief overview of the library. &lt;a href=&quot;#profiling-the-profiler&quot;&gt;Click here&lt;/a&gt; if you want to jump straight into the implementation details.&lt;/p&gt;

&lt;p&gt;Over the last months, the &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; profiler has grown to over 100k downloads on &lt;a href=&quot;https://crates.io/crates/hotpath/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;crates.io&lt;/a&gt; and is slowly gaining more adoption in the Rust ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;hotpath-rs download stats on crates.io&quot; title=&quot;hotpath-rs download stats on crates.io&quot; loading=&quot;lazy&quot; src=&quot;/assets/hotpath_download_stats-e8b4f6608aeeee71954445c4401a2d7a58c2e440023c6456f527ee521d934817.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Before diving into implementation details, let’s quickly look at what &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; does and why I built it. It’s an &lt;em&gt;“all-in-one”&lt;/em&gt; Rust profiler / debugging toolkit designed to quickly identify performance bottlenecks.&lt;/p&gt;

&lt;p&gt;The core idea is to combine multiple sources of data into reports that are &lt;em&gt;quick and easy&lt;/em&gt; to mentally parse. You need only two macros, &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::main&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::measure&lt;/code&gt;, to get started with instrumenting your codebase.&lt;/p&gt;

&lt;p&gt;Let’s see it in action:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/257f0fb9e2d5d069a87cc076acdc4b07d278218d/crates/test-tokio-async/examples/overview.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;examples/overview.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hotpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sync_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_mul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;measure]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sync_alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;measure]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;async_sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;tokio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_millis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[tokio&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;sync_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;sync_alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;async_sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example features 3 functions, showcasing different modes of execution present in any Rust program:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sync_work&lt;/code&gt; - synchronous function that executes a CPU-bound code&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sync_alloc&lt;/code&gt; - synchronous function that allocates memory&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;async_sleep&lt;/code&gt; - function that sleeps asynchronously. A bit artificial, but it’s meant to simulate a slow async I/O. Waiting for an SQL query or an HTTP endpoint would yield similar performance results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at the example, can you tell what is the REAL bottleneck?&lt;/p&gt;

&lt;p&gt;Let’s see a hotpath report output before we answer this question:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run --example overview --features='hotpath,hotpath-alloc,hotpath-cpu'&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[hotpath] 15.61s | timing, alloc, cpu

timing - Function execution time metrics.
+------------------------+-------+----------+----------+---------+---------+
| Function               | Calls | Avg      | P95      | Total   | % Total |
+------------------------+-------+----------+----------+---------+---------+
| cpu_basic::main        | 1     | 15.62 s  | 15.62 s  | 15.61 s | 100.00% |
+------------------------+-------+----------+----------+---------+---------+
| cpu_basic::async_sleep | 1000  | 11.77 ms | 12.10 ms | 11.77 s | 75.42%  |
+------------------------+-------+----------+----------+---------+---------+
| cpu_basic::sync_work   | 1000  | 3.14 ms  | 4.59 ms  | 3.14 s  | 20.11%  |
+------------------------+-------+----------+----------+---------+---------+
| cpu_basic::sync_alloc  | 1000  | 1.23 µs  | 2.62 µs  | 1.23 ms | 0.01%   |
+------------------------+-------+----------+----------+---------+---------+

alloc-bytes - Exclusive allocation bytes by each function.
Total: 1.1 MB
+------------------------+-------+----------+----------+-----------+---------+
| Function               | Calls | Avg      | P95      | Total     | % Total |
+------------------------+-------+----------+----------+-----------+---------+
| cpu_basic::sync_alloc  | 1000  | 1.0 KB   | 1.0 KB   | 1000.0 KB | 89.28%  |
+------------------------+-------+----------+----------+-----------+---------+
| cpu_basic::main        | 1     | 120.1 KB | 120.1 KB | 120.1 KB  | 10.72%  |
+------------------------+-------+----------+----------+-----------+---------+
| cpu_basic::async_sleep | 1000  | 0 B      | 0 B      | 0 B       | 0.00%   |
+------------------------+-------+----------+----------+-----------+---------+
| cpu_basic::sync_work   | 1000  | 0 B      | 0 B      | 0 B       | 0.00%   |
+------------------------+-------+----------+----------+-----------+---------+

cpu - CPU sampling attribution per function (exclusive). 
+------------------------+---------+---------+
| Function               | Samples | % Total |
+------------------------+---------+---------+
| cpu_basic::sync_work   | 1915914 | 56.13%  |
+------------------------+---------+---------+
| cpu_basic::async_sleep | 14056   | 0.41%   |
+------------------------+---------+---------+
| cpu_basic::sync_alloc  | 1581    | 0.05%   |
+------------------------+---------+---------+
samply load /tmp/hotpath/61089-1778083683167502000/hp.json.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;center annotation&quot;&gt;hotpath timing, alloc, and CPU usage report.&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Optionally, you can run &lt;code class=&quot;highlighter-rouge&quot;&gt;samply load&lt;/code&gt; to see an interactive call tree or flamegraph generated with &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Samply optional full interactive call tree &quot; title=&quot;Samply optional full interactive call tree &quot; loading=&quot;lazy&quot; src=&quot;/assets/samply_flamegraph-951a9a9da55478f68c0e46087ce093d409a56eb5234c165b8abe14f3c2623362.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can identify a different bottleneck depending on which report section we analyze:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;async_sleep&lt;/code&gt; - takes ~75% of total program execution time, but less than 1% of CPU.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sync_alloc&lt;/code&gt; - took less than 1% of processing time but is responsible for ~90% of memory allocations.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sync_work&lt;/code&gt; - burned ~56% of total CPU cycles but took only ~20% of processing time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I came up with this example to exaggerate the core idea behind &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt;’s approach to profiling: &lt;strong&gt;different performance signals tell different stories&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This distinction is especially important for async-heavy Rust programs, where &lt;em&gt;“slow”&lt;/em&gt; often means waiting on I/O, timers, channels, or the runtime scheduler rather than actually burning CPU. Traditional CPU profilers like &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;perf&lt;/code&gt; can show where CPU time is spent, but they cannot reliably capture time futures spend suspended while waiting to be polled by the runtime.&lt;/p&gt;

&lt;p&gt;While working on Rust performance issues, I often found myself jumping between multiple specialized tools - &lt;a href=&quot;https://github.com/mstange/samply&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt;&lt;/a&gt; for CPU usage, &lt;a href=&quot;https://github.com/cmyr/cargo-instruments&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cargo-instruments&lt;/code&gt;&lt;/a&gt; for allocations, and &lt;a href=&quot;https://github.com/tokio-rs/console&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tokio-console&lt;/code&gt;&lt;/a&gt; for async behavior… &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; was built to bring those different signals together into a single report that’s fast to read and easy to reason about.&lt;/p&gt;

&lt;p&gt;Naturally, this famous XKCD comic felt very relevant…&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;How standards proliferate&quot; title=&quot;How standards proliferate&quot; loading=&quot;lazy&quot; src=&quot;/assets/xkcd_standards-df82520804ccf257db609f5ed40521b92a5c3e2366d1bcbd4a75d45ea8559d21.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;&lt;a href=&quot;https://xkcd.com/927/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Source: xkcd.com/927&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;live-profiling-tui&quot;&gt;Live profiling TUI&lt;/h3&gt;

&lt;p&gt;Another unique feature of &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; is live profiling and debugging capabilities. You can see it in action by running:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh demo.hotpath.rs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It executes an interactive TUI program that displays its own live performance metrics.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;hotpath-rs TUI showing its own performance metrics&quot; title=&quot;hotpath-rs TUI showing its own performance metrics&quot; loading=&quot;lazy&quot; src=&quot;/assets/hotpath_tui_demo-5e0452728e18fdeb76da933c31b0b64117a7aa49f0c770d5d710f5af0989627f.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;hotpath-rs TUI displaying its own instrumented future performance and lifetime metrics&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;You can think of it as &lt;em&gt;“debugging on steroids”&lt;/em&gt;. &lt;a href=&quot;https://hotpath.rs/data_flow&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::channel!&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::stream!&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::future!&lt;/code&gt; macros&lt;/a&gt; allow you to inspect data flowing in your system in real time. The required code changes are minimal because macros return exactly the same types and are 100% no-op unless the &lt;code class=&quot;highlighter-rouge&quot;&gt;--features=hotpath&lt;/code&gt; is enabled.&lt;/p&gt;

&lt;p&gt;In my current workflow, I don’t use &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; TUI extensively; I prefer the static one-off reports and &lt;a href=&quot;https://hotpath.rs/benchmarks&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;A/B benchmarks&lt;/a&gt;. But if you’re working on a long-running interactive program, you might find it helpful.&lt;/p&gt;

&lt;p&gt;Where I &lt;em&gt;do&lt;/em&gt; find the TUI dashboard useful is for optimizing the &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; itself. I maintain an instrumented fork of the &lt;a href=&quot;https://zed.dev&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Zed editor&lt;/a&gt;, and test every new feature of hotpath against it. Given the scale and complexity of Zed, I treat it as the ultimate stress test, ensuring that lib should work with most other Rust programs.&lt;/p&gt;

&lt;p&gt;You can check out the fork with instructions on how to run it yourself &lt;a href=&quot;https://github.com/pawurb/zed/pull/2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;from this PR&lt;/a&gt;, or see instrumented Zed in action in the short demo:&lt;/p&gt;

&lt;div class=&quot;vid-container&quot;&gt;
  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/mTNtYXCpJNI&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; encrypted-media&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Hopefully, I have your curiosity now. Read on if you’re interested in how the library works under the hood and the techniques I use to keep it performant.&lt;/p&gt;

&lt;h2 id=&quot;profiling-the-profiler&quot;&gt;Profiling the profiler&lt;/h2&gt;

&lt;p&gt;The core mechanism behind hotpath instrumentation is RAII (Resource Acquisition Is Initialization) guards. Here’s a simplified explanation of what &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::measure&lt;/code&gt; macro compiles to:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hotpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sync_alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;becomes:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sync_alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_guard&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MeasurementGuard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s a simplified implementation of the &lt;code class=&quot;highlighter-rouge&quot;&gt;MeasurementGuard&lt;/code&gt; struct:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MeasurementGuard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Instant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Measurement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Drop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MeasurementGuard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.start&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;measurement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Measurement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.sender&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;measurement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;MeasurementGuard&lt;/code&gt; uses &lt;code class=&quot;highlighter-rouge&quot;&gt;std::time::Instant&lt;/code&gt; to measure time between its creation and drop, which is determined by how long an instrumented function executes. It’s the so-called &lt;em&gt;“wall-clock timing”&lt;/em&gt; approach to profiling programs, which is significantly different from CPU sampling. The primary difference is in the results you’ll get when measuring &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; functions, which are notoriously hard to benchmark using only CPU profiling. Check out &lt;a href=&quot;https://hotpath.rs/sampling_comparison&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this docs section&lt;/a&gt; for an in-depth analysis of these two approaches.&lt;/p&gt;

&lt;h3 id=&quot;fixing-the-mpsc-channel-bottleneck&quot;&gt;Fixing the MPSC channel bottleneck&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;MeasurementGuard&lt;/code&gt;’s &lt;code class=&quot;highlighter-rouge&quot;&gt;Drop&lt;/code&gt; trait implementation is the &lt;em&gt;hottest hot path&lt;/em&gt; in the project. It’s executed inline with the instrumented code, so it directly impacts the performance of measured programs.&lt;/p&gt;

&lt;p&gt;In the initial library versions, the code resembled the above implementation, i.e., measurements were sent for background processing one by one. I’ve measured (using &lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/cde66185b086290dff28cfe36cea2532a70694c5/crates/test-tokio-async/examples/benchmark_noop.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;benchmark_noop.rs&lt;/code&gt;&lt;/a&gt;) the per-instrumentation impact to be &lt;code class=&quot;highlighter-rouge&quot;&gt;~500ns&lt;/code&gt;. Far from ideal if you want to instrument functions that execute on a microsecond scale.&lt;/p&gt;

&lt;p&gt;I’ve used &lt;a href=&quot;https://github.com/mstange/samply&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt;&lt;/a&gt; to inspect overhead, and it produced the following output:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Samply Thread::unpark bottleneck&quot; title=&quot;Samply Thread::unpark bottleneck&quot; loading=&quot;lazy&quot; src=&quot;/assets/samply_unpark-88a96a96a1e8af2abadef555f1506df0c06b40f5c57aff7ccbeb68f74c174633.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;samply call tree &lt;code&gt;Thread::unpark&lt;/code&gt; bottleneck&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;As you can see ~22%, of all CPU traces landed in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread::unpark&lt;/code&gt; method. Apparently, even if messages are sent to a crossbeam MPSC channel every 1 microsecond, a receiver thread enters the &lt;code class=&quot;highlighter-rouge&quot;&gt;parked&lt;/code&gt; state and has to be &lt;code class=&quot;highlighter-rouge&quot;&gt;unparked&lt;/code&gt; to receive the next message. The solution was to send messages in batches, gathered per thread. It completely eliminated the &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread::unpark&lt;/code&gt; samples and reduced the avg. overhead per measurement to &lt;code class=&quot;highlighter-rouge&quot;&gt;~80ns&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I did follow up on this issue, and found out that there’s a mismatch in how &lt;code class=&quot;highlighter-rouge&quot;&gt;crossbeam-channel&lt;/code&gt; handles receivers depending on whether they are used with a &lt;code class=&quot;highlighter-rouge&quot;&gt;select!&lt;/code&gt; macro. A directly subscribed receiver uses a spin wait with backoff logic to rectify exactly this &lt;code class=&quot;highlighter-rouge&quot;&gt;Thread::park&lt;/code&gt; issue. But &lt;code class=&quot;highlighter-rouge&quot;&gt;select!&lt;/code&gt; macro bypasses this mechanism and causes the receiver thread to &lt;code class=&quot;highlighter-rouge&quot;&gt;park&lt;/code&gt; even if data arrives in microsecond-scale intervals. I’ve &lt;a href=&quot;https://github.com/crossbeam-rs/crossbeam/pull/1251&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;submitted a PR&lt;/a&gt; trying to address this. Not sure if it’s the correct solution, but if you’re using &lt;code class=&quot;highlighter-rouge&quot;&gt;crossbeam_channel::select!&lt;/code&gt; macro with high-frequency data, then, batching will likely measurably improve performance.&lt;/p&gt;

&lt;p&gt;BTW, sending instrumentation data to a separate thread for processing is the most performance-critical area in the &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt;’s codebase. It directly impacts the performance of benchmarked programs and affects measurements. If you have an idea for a data-sharing mechanism with lower overhead than &lt;code class=&quot;highlighter-rouge&quot;&gt;crossbeam&lt;/code&gt; channels, plz get in touch!&lt;/p&gt;

&lt;h3 id=&quot;alloc-metrics-cpu-cache-busting&quot;&gt;Alloc metrics CPU cache busting&lt;/h3&gt;

&lt;p&gt;Measuring memory allocations uses a system that is slightly more complicated than RAII guards with an internal timer. I’ve borrowed the initial implementation from the &lt;a href=&quot;https://github.com/fornwall/allocation-counter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;allocation-counter crate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It uses a thread-local stack to attribute allocations and deallocations to each thread:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadAllocStats&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AtomicU64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alloc_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AtomicU64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dealloc_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AtomicU64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_THREADS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;THREAD_ALLOC_STATS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadAllocStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_THREADS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadAllocStats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ThreadAllocStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_THREADS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A static preallocated array of &lt;code class=&quot;highlighter-rouge&quot;&gt;AtomicU64&lt;/code&gt;, one per thread. When the &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-alloc&lt;/code&gt; feature is enabled, we hook into a system allocator and proxy allocations and deallocations through our custom &lt;code class=&quot;highlighter-rouge&quot;&gt;CountingAllocator&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;cfg_if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;cfg_if!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; #&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;feature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hotpath-alloc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;#[global_allocator]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GLOBAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;allocator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CountingAllocator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;allocator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CountingAllocator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GlobalAlloc&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CountingAllocator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nn&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;lib_on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;track_alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dealloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nn&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;lib_on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;track_dealloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.dealloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;GlobalAlloc&lt;/code&gt; trait defines &lt;code class=&quot;highlighter-rouge&quot;&gt;alloc&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;dealloc&lt;/code&gt;, called for &lt;em&gt;EVERY&lt;/em&gt; single allocation and deallocation in the instrumented Rust program.&lt;/p&gt;

&lt;p&gt;That’s how we can attribute total alloc/dealloc stats for each thread. I use the &lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/68088f88df2339f9547cdd0a2c1c3f939729f2e0/crates/test-tokio-async/examples/benchmark_alloc.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;benchmark_alloc.rs&lt;/code&gt;&lt;/a&gt; script to measure the overhead of an exaggerated function that does nothing but allocate memory:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hotpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1u8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s the benchmark execution time depending on the number of threads:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo build &lt;span class=&quot;nt&quot;&gt;--example&lt;/span&gt; benchmark_alloc &lt;span class=&quot;nt&quot;&gt;--features&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'hotpath,hotpath-alloc'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--release&lt;/span&gt; 
hyperfine &lt;span class=&quot;nt&quot;&gt;--warmup&lt;/span&gt; 3 &lt;span class=&quot;s1&quot;&gt;'HOTPATH_ALLOC_NUM_THREADS=$THREADS_NUM ./target/release/examples/benchmark_alloc'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table style=&quot;width:100%&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Threads&lt;/th&gt;
      &lt;th&gt;Time (ms)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;~210&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;~720&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;~1000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;~3700&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;~4400&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;This benchmark simulates an extreme case of a Rust program that does nothing but allocate. Surprisingly, the initial implementation worked without issues, even with the Zed debug build, which spawns ~60 threads. But &lt;em&gt;you wouldn’t believe&lt;/em&gt;, a simple trick to instantly speed up Rust programs with multiple threads competing for a shared static resource:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadAllocStats&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;/// Thread ID (0 unused)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AtomicU64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alloc_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AtomicU64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dealloc_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AtomicU64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here are the same benchmark results after prepending &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAllocStats&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;#[repr(align(64))]&lt;/code&gt; annotation:&lt;/p&gt;

&lt;table style=&quot;width:100%&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Threads&lt;/th&gt;
      &lt;th&gt;Before (ms)&lt;/th&gt;
      &lt;th&gt;After (ms)&lt;/th&gt;
      &lt;th&gt;% Improvement&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;~210&lt;/td&gt;
      &lt;td&gt;~210&lt;/td&gt;
      &lt;td&gt;0%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;~720&lt;/td&gt;
      &lt;td&gt;~215&lt;/td&gt;
      &lt;td&gt;70%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;~1000&lt;/td&gt;
      &lt;td&gt;~280&lt;/td&gt;
      &lt;td&gt;72%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;~3700&lt;/td&gt;
      &lt;td&gt;~500&lt;/td&gt;
      &lt;td&gt;86%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;~4400&lt;/td&gt;
      &lt;td&gt;~660&lt;/td&gt;
      &lt;td&gt;85%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;This technique, called &lt;em&gt;“cache padding”&lt;/em&gt;, forces each &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAllocStats&lt;/code&gt; instance to start at a 64-byte boundary, matching a common CPU cache-line size. Without padding, &lt;code class=&quot;highlighter-rouge&quot;&gt;ThreadAllocStats&lt;/code&gt; in memory size was 24 bytes, so two instances could fit in a single CPU cache line. With multiple threads invalidating each other’s caches, performance was getting worse with each added thread. Check out the story of &lt;a href=&quot;https://www.youtube.com/watch?v=-Xe9vQL0ZBY&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;optimizing zlib-rb implementation&lt;/a&gt; for an in-depth exploration of this technique.&lt;/p&gt;

&lt;h2 id=&quot;async-functions-memory-tracking&quot;&gt;Async functions memory tracking&lt;/h2&gt;

&lt;p&gt;Another challenge was tracking memory allocations for instrumented functions. For synchronous functions, the implementation is still largely based on the &lt;a href=&quot;https://github.com/fornwall/allocation-counter&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;allocation-counter crate&lt;/a&gt;. It uses &lt;code class=&quot;highlighter-rouge&quot;&gt;thread_local&lt;/code&gt; data (&lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/68088f88df2339f9547cdd0a2c1c3f939729f2e0/crates/hotpath/src/lib_on/functions/alloc/core.rs#L121&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;AllocationInfoStack&lt;/code&gt;&lt;/a&gt; if you’re interested in the impl details) for tracking allocations. We use a RAII guard pattern similar to wall-clock time measurement:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AllocMeasurementGuard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'static&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Measurement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;push_alloc_frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Drop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AllocMeasurementGuard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pop_alloc_frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.sender&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Measurement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
          &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s a simplified implementation to explain how it works. We maintain a &lt;code class=&quot;highlighter-rouge&quot;&gt;thread_local&lt;/code&gt; stack and use &lt;code class=&quot;highlighter-rouge&quot;&gt;push_alloc_frame&lt;/code&gt; to mark which synchronous function is currently executing. &lt;code class=&quot;highlighter-rouge&quot;&gt;pop_alloc_frame&lt;/code&gt; marks functions as completed and reads allocation metrics for background processing.&lt;/p&gt;

&lt;p&gt;But &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; Rust functions, depending on the runtime implementation, may migrate between threads. For example, the default &lt;code class=&quot;highlighter-rouge&quot;&gt;#[tokio::main]&lt;/code&gt; macro spawns threads for each virtual CPU. You can easily check it in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Threads&lt;/code&gt; tab of a hotpath TUI:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;hotpath-rs TUI showing per thread metrics&quot; title=&quot;hotpath-rs TUI showing per thread metrics&quot; loading=&quot;lazy&quot; src=&quot;/assets/hotpath_threads_tab-19ed78a9d9d3556bbf2bb983db6ae64f5399af31e7808b015a705a54baa1de94.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;hotpath TUI displays per-thread CPU and memory metrics&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;So, for &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; functions our thread-local data won’t work. Allocation info gathered for the function on thread A is no longer available if it executes &lt;code class=&quot;highlighter-rouge&quot;&gt;pop_alloc_frame&lt;/code&gt; on a thread B.&lt;/p&gt;

&lt;p&gt;The previous version of &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; allowed async allocations tracking only for &lt;code class=&quot;highlighter-rouge&quot;&gt;#[tokio::main(flavor = &quot;current_thread&quot;)]&lt;/code&gt; runtime. I thought that as long as we use a single thread, measurements should be accurate. Unit tests returned correct results, and it supposedly worked. But benchmarking an Axum server app using the &lt;code class=&quot;highlighter-rouge&quot;&gt;current_thread&lt;/code&gt; runtime, under a &lt;a href=&quot;https://github.com/hatoo/oha&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;multithreaded oha loadtest&lt;/a&gt;, produced &lt;em&gt;“interesting”&lt;/em&gt; results.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;hotpath-rs buggy benchmark of async functions memory&quot; title=&quot;hotpath-rs buggy benchmark of async functions memory&quot; loading=&quot;lazy&quot; src=&quot;/assets/runtime_memory_before-3c47b884490e0dcb80585605956e79935ad079766700407be05c6a40f7d93458.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Can you spot an issue here?&lt;/p&gt;

&lt;p&gt;A ~60s benchmark supposedly churned &lt;strong&gt;~400GB of memory&lt;/strong&gt; with a single &lt;code class=&quot;highlighter-rouge&quot;&gt;routes::serve_doc_page&lt;/code&gt; method call allocating ~23MB on avg.&lt;/p&gt;

&lt;p&gt;The bug was caused by using an RAII guard for instrumenting an asynchronous function, which, contrary to synchronous calls, does not execute continuously. Runtime can yield control of a future at any time and start executing a different one, even on a single thread. It caused the allocation metrics of measured functions to intertwine, massively inflating the numbers.&lt;/p&gt;

&lt;p&gt;The solution was to instrument &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; functions as futures, which they really are. The current version of hotpath uses a similar implementation for tracking async memory usage:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstrumentedFuture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;#[pin]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FutureEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;future_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Future&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstrumentedFuture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;'_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Poll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nn&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;guard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push_alloc_stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.inner&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.poll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;guard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop_alloc_stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.sender&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Measurement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.future_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We wrap every instrumented function/future into an &lt;code class=&quot;highlighter-rouge&quot;&gt;InstrumentedFuture&lt;/code&gt; struct, which holds a pinned reference (&lt;code class=&quot;highlighter-rouge&quot;&gt;inner&lt;/code&gt;) to the original future. The &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::measure&lt;/code&gt; macro handles all the wrapping. And we &lt;code class=&quot;highlighter-rouge&quot;&gt;push/pop&lt;/code&gt; the allocations data stack when runtime calls &lt;code class=&quot;highlighter-rouge&quot;&gt;poll&lt;/code&gt; on our wrapper. Counterintuitively, the &lt;code class=&quot;highlighter-rouge&quot;&gt;poll&lt;/code&gt; function is synchronous, so it’s guaranteed to execute continuously, and our allocation info no longer gets mixed up with other asynchronous calls.&lt;/p&gt;

&lt;p&gt;And here’s the same benchmark after a fix:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;hotpath-rs fixed benchmark of async functions memory&quot; title=&quot;hotpath-rs fixed benchmark of async functions memory&quot; loading=&quot;lazy&quot; src=&quot;/assets/runtime_memory_after-981ffe1df940648b78331cf128f2fa117fcd515bc24e6724dcd665dc243f5f81.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This mechanism does not use any runtime-specific APIs, nor does it require setting additional config like &lt;code class=&quot;highlighter-rouge&quot;&gt;RUSTFLAGS=&quot;--cfg tokio_unstable&quot;&lt;/code&gt;, or any changes to your project other than the &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::measure&lt;/code&gt; macro. The newest version of hotpath can correctly attribute memory allocations regardless of which runtime you’re using, &lt;code class=&quot;highlighter-rouge&quot;&gt;tokio&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;smol&lt;/code&gt;, or anything custom.&lt;/p&gt;

&lt;h2 id=&quot;cpu-profiling-integration&quot;&gt;CPU profiling integration&lt;/h2&gt;

&lt;p&gt;CPU profiling is a recent addition to the library. The core idea behind &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; is combining different performance signals into easy-to-understand reports. Wall-clock timing and memory allocation metrics are useful on their own, but they don’t show the full picture.&lt;/p&gt;

&lt;p&gt;An initial prototype used the &lt;a href=&quot;https://github.com/tikv/pprof-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pprof-rs crate&lt;/a&gt;, allowing sampling of CPU execution within the same process. I liked the simplicity of this approach, but it did not scale. The debug build of Zed slowed down to a crawl with &lt;code class=&quot;highlighter-rouge&quot;&gt;pprof-rs&lt;/code&gt; profiling enabled.&lt;/p&gt;

&lt;p&gt;A solution was to integrate &lt;a href=&quot;https://github.com/mstange/samply&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;samply profiler&lt;/a&gt;. It’s running in a separate process, so it works even with the debug profile Zed. Integration required a hacky workaround, because launching &lt;code class=&quot;highlighter-rouge&quot;&gt;samply record --pid&lt;/code&gt; from the same process or its direct child caused it to get stuck. My best guess is that attaching the profiler temporarily suspends the target process and all its children. A workaround consists of a separate &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-samply&lt;/code&gt; binary, that spawns in a detached mode, and only then can we launch &lt;code class=&quot;highlighter-rouge&quot;&gt;samply record&lt;/code&gt; without crashing the host process. &lt;em&gt;“It’s broken, but it works”&lt;/em&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-cpu&lt;/code&gt; mode is available since the recent &lt;code class=&quot;highlighter-rouge&quot;&gt;0.16.0&lt;/code&gt; release.&lt;/p&gt;

&lt;p&gt;One caveat is that on Linux you have to prefix profiled programs with &lt;code class=&quot;highlighter-rouge&quot;&gt;setsid -w&lt;/code&gt;, otherwise they exit before a final report is produced. &lt;a href=&quot;https://hotpath.rs/cpu_profiling&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;See docs&lt;/a&gt; for more info.&lt;/p&gt;

&lt;h3 id=&quot;interpreting-raw-cpu-traces&quot;&gt;Interpreting raw CPU traces&lt;/h3&gt;
&lt;p&gt;The host process and &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-samply&lt;/code&gt; communicate via a custom file-based API. Once &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; allocations and timing profiling are completed, lib creates a &lt;code class=&quot;highlighter-rouge&quot;&gt;stop-profiling&lt;/code&gt; file in a predefined location, and it communicates that we should also finish the CPU sampling by sending a &lt;code class=&quot;highlighter-rouge&quot;&gt;SIGINT&lt;/code&gt; signal to &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt;. Later a &lt;code class=&quot;highlighter-rouge&quot;&gt;done&lt;/code&gt; file creation communicates that the &lt;code class=&quot;highlighter-rouge&quot;&gt;hp.json.gz&lt;/code&gt; file with profiling data is ready to be parsed. I came up with the hacky protocol by chatting with my AI friends. Let me know if you can think of a cleaner way to establish cross-process communication.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;hp.json.gz&lt;/code&gt; is where things start to get interesting. This file contains &lt;em&gt;raw&lt;/em&gt; data about CPU samples collected by &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt; profiler. Very raw:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;threads&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cpu_nested&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;218490&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;isMainThread&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stringArray&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cpu_nested&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0ea0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0c10&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;resourceTable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;length&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;lib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;funcTable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;length&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;resource&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;frameTable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;length&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;address&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;ea&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;func&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;nativeSymbol&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stackTable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;length&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;frame&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;samples&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;length&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stack&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;timeDeltas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;threadCPUDelta&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;950&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;center annotation&quot;&gt;A minimal example of samply profiling output&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice that this JSON does not include any information about function symbols, so to attribute collected samples to our instrumented functions, we have to map them manually.&lt;/p&gt;

&lt;p&gt;We have to start by mapping symbol data to addresses. We can do it by extracting symbol ranges from the program binary. That’s why you have to use a custom profile if you want to profile programs running in a &lt;code class=&quot;highlighter-rouge&quot;&gt;--release&lt;/code&gt; mode:&lt;/p&gt;

&lt;div class=&quot;language-toml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[profile.profiling]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;inherits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;release&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Otherwise, the symbol data is not in the binary. &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; uses &lt;a href=&quot;https://github.com/gimli-rs/object&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;object crate&lt;/a&gt; to extract ranges corresponding to known function symbols.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/c43f94b963ed5c5ec8338a166206d0a51e6cf13e/crates/test-tokio-async/examples/cpu_nested.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;examples/cpu_nested.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hotpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;heavy_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_mul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;measure]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;light_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;measure]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;outer_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;heavy_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;light_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;outer_work&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It produces the following data structure:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;2980&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;3348&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;cpu_nested::heavy_work&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;3348&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;3656&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;cpu_nested::light_work&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;3656&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;3904&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;cpu_nested::outer_work&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The JSON profile does not directly say: &lt;em&gt;“this sample belongs to &lt;code class=&quot;highlighter-rouge&quot;&gt;heavy_work&lt;/code&gt;“&lt;/em&gt;. Instead, it stores sampled stack traces as compact table references.&lt;/p&gt;

&lt;p&gt;Here’s how we can resolve the symbol:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;samples.stack[0] =&amp;gt; 1
stackTable.frame[1] =&amp;gt; 1
frameTable.address[1] =&amp;gt; 0x0c10 =&amp;gt; 3088
symbol_ranges.contains(3088) =&amp;gt; cpu_nested::heavy_work
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s our leaf function symbol!&lt;/p&gt;

&lt;h3 id=&quot;exclusive-vs-inclusive-cpu-attribution&quot;&gt;Exclusive vs inclusive CPU attribution&lt;/h3&gt;

&lt;p&gt;For exclusive CPU attribution, this is enough. The sample is attributed to the first instrumented function found in the leaf frame.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-cpu&lt;/code&gt; supports &lt;code class=&quot;highlighter-rouge&quot;&gt;HOTPATH_CPU_INCLUSIVE=1&lt;/code&gt; config, which attributes CPU usage to both instrumented children and parent functions.&lt;/p&gt;

&lt;p&gt;Running &lt;code class=&quot;highlighter-rouge&quot;&gt;cpu_nested&lt;/code&gt; in a default exclusive CPU attributions mode produces:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run --example cpu_nested --features='hotpath,hotpath-cpu'&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+------------------------+---------+---------+
| Function               | Samples | % Total |
+------------------------+---------+---------+
| cpu_nested::heavy_work | 4420575 | 86.44%  |
+------------------------+---------+---------+
| cpu_nested::light_work | 627107  | 12.26%  |
+------------------------+---------+---------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and in inclusive mode, we get:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HOTPATH_CPU_INCLUSIVE=1 cargo run --example cpu_nested --features='hotpath,hotpath-cpu'&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+------------------------+---------+---------+
| Function               | Samples | % Total |
+------------------------+---------+---------+
| cpu_nested::main       | 5235886 | 99.06%  |
+------------------------+---------+---------+
| cpu_nested::outer_work | 5234714 | 99.04%  |
+------------------------+---------+---------+
| cpu_nested::heavy_work | 4515297 | 85.43%  |
+------------------------+---------+---------+
| cpu_nested::light_work | 719417  | 13.61%  |
+------------------------+---------+---------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These two modes are useful for analyzing how children’s instrumented functions usage relates to their parents.&lt;/p&gt;

&lt;p&gt;So how can we attribute CPU traces to non-leaf instrumented functions? The answer is… &lt;em&gt;LINKED LISTS&lt;/em&gt;! (╯°□°）╯︵ ┻━┻&lt;/p&gt;

&lt;p&gt;Apparently, they &lt;em&gt;do&lt;/em&gt; exist outside of LeetCode…&lt;/p&gt;

&lt;p&gt;So coming back to our example:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;samples.stack[0] =&amp;gt; 1
stackTable.frame[1] =&amp;gt; 1
frameTable.address[1] =&amp;gt; 0x0c10 =&amp;gt; 3088
symbol_ranges.contains(3088) =&amp;gt; cpu_nested::heavy_work
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To find the parent frame, we have to follow the &lt;code class=&quot;highlighter-rouge&quot;&gt;prefix&lt;/code&gt; field:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;stackTable.prefix[1] =&amp;gt; 0

stack node 0:
  stackTable.frame[0] =&amp;gt; 0
  frameTable.address[0] =&amp;gt; 0x0ea0 =&amp;gt; 3744
  symbol_ranges.contains(3744) =&amp;gt; cpu_nested::outer_work

stackTable.prefix[0] =&amp;gt; null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;null&lt;/code&gt; means we’ve reached the root of the sampled stack and can stop walking the linked list. This is the core mechanism behind inclusive CPU attribution in &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-cpu&lt;/code&gt; mode.&lt;/p&gt;

&lt;p&gt;I owe linked lists an apology. ┬─┬ノ( º _ ºノ)&lt;/p&gt;

&lt;h3 id=&quot;cpu-delta-vs-samples-count&quot;&gt;CPU delta vs. samples count&lt;/h3&gt;

&lt;p&gt;So we can now attribute CPU samples to our instrumented functions. Now consider the following example:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/257f0fb9e2d5d069a87cc076acdc4b07d278218d/crates/test-tokio-async/examples/cpu_park_vs_busy.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;examples/cpu_park_vs_busy.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;COMPUTE_BATCH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;measure]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;busy_compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_mul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;measure]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;park_main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;park_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Instant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_secs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;busy_compute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COMPUTE_BATCH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;park_main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_secs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cargo run --example cpu_park_vs_busy --features='hotpath,hotpath-cpu'&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;timing - Execution duration of functions.
+--------------------------------+-------+-----------+-----------+---------+---------+
| Function                       | Calls | Avg       | P95       | Total   | % Total |
+--------------------------------+-------+-----------+-----------+---------+---------+
| cpu_park_vs_busy::main         | 1     | 10.22 s   | 10.23 s   | 10.22 s | 100.00% |
+--------------------------------+-------+-----------+-----------+---------+---------+
| cpu_park_vs_busy::park_main    | 1     | 5.00 s    | 5.00 s    | 5.00 s  | 48.95%  |
+--------------------------------+-------+-----------+-----------+---------+---------+
| cpu_park_vs_busy::busy_compute | 16415 | 304.46 µs | 333.57 µs | 5.00 s  | 48.91%  |
+--------------------------------+-------+-----------+-----------+---------+---------+

cpu - CPU sampling attribution per function (exclusive)
+--------------------------------+---------+---------+
| Function                       | Samples | % Total |
+--------------------------------+---------+---------+
| cpu_park_vs_busy::busy_compute | 5044956 | 99.05%  |
+--------------------------------+---------+---------+
| cpu_park_vs_busy::park_main    | 1780    | 0.03%   |
+--------------------------------+---------+---------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, in terms of timing, &lt;code class=&quot;highlighter-rouge&quot;&gt;park_main&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;busy_compute&lt;/code&gt; each took ~50% of the processing time. But CPU usage is reported at 99% vs 0.03%. Let’s compare it to the &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt; UI report:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;samply UI reporting parked thread data&quot; title=&quot;samply UI reporting parked thread data&quot; loading=&quot;lazy&quot; src=&quot;/assets/samply_park_report-69e1934b3c004987ae3a30f72b78d47d4aac82abb4b042c64dc6c15791cfeaf3.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It reports a 50/50 split similar to the &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; wall-clock time report. The only hint of CPU usage is a yellow chart at the top of the report.&lt;/p&gt;

&lt;p&gt;I’ve considered different approaches and, for now, settled on using &lt;code class=&quot;highlighter-rouge&quot;&gt;threadCPUDelta&lt;/code&gt; rather than timing or sample count to calculate CPU attribution %. So the default &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt; call traces UI attributes samples to a function symbol, even if the CPU is dormant (i.e., thread in &lt;code class=&quot;highlighter-rouge&quot;&gt;parked&lt;/code&gt; state) when the sample is collected. Recreating this approach would replicate the wall-clock timing (except for &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; functions! see &lt;a href=&quot;https://hotpath.rs/sampling_comparison&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sampling comparison&lt;/a&gt; for an in-depth info on this topic).&lt;/p&gt;

&lt;p&gt;So now &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-cpu&lt;/code&gt; highlights areas where the CPU really &lt;em&gt;“goes brrr”&lt;/em&gt; and augments the report with this new perspective.&lt;/p&gt;

&lt;p&gt;Future &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; releases will likely add more CPU instrumentation modes; feedback on which approach is best is appreciated!&lt;/p&gt;

&lt;h3 id=&quot;inline-compiler-hints&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;inline&lt;/code&gt; compiler hints&lt;/h3&gt;

&lt;p&gt;The current &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath-cpu&lt;/code&gt; implementation uses one more hack to correctly resolve the function symbols:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/hotpath-rs/blob/257f0fb9e2d5d069a87cc076acdc4b07d278218d/crates/test-tokio-async/examples/cpu_inline.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;examples/always_inlined.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;hotpath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;#[inline(always)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;always_inlined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_mul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[hotpath&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.wrapping_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;always_inlined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;black_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;heavy_work&lt;/code&gt; uses an &lt;code class=&quot;highlighter-rouge&quot;&gt;#[inline(always)]&lt;/code&gt; annotation, which causes its address symbol range to disappear from the binary. The current implementation of &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath::measure&lt;/code&gt; macro implicitly strips user-provided &lt;code class=&quot;highlighter-rouge&quot;&gt;inline&lt;/code&gt; annotation, and applies &lt;code class=&quot;highlighter-rouge&quot;&gt;inline(never)&lt;/code&gt; to prevent this issue. Working on this post, I’ve noticed that &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt; can still correctly attribute samples even to inlined functions, it even reports in the UI that the function was inlined:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;samply UI reporting an inlined function&quot; title=&quot;samply UI reporting an inlined function&quot; loading=&quot;lazy&quot; src=&quot;/assets/samply_inlined-d7efac1a2f029c7ed5f1b5ad66fca81b7bda92f3cddd9ceb96b2f78ca1d9cdf8.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Apparently, &lt;code class=&quot;highlighter-rouge&quot;&gt;samply&lt;/code&gt; uses a &lt;a href=&quot;https://github.com/gimli-rs/gimli&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;gimli crate&lt;/a&gt; to resolve symbols based on DWARF format. I did not get around to researching this topic yet, but future &lt;code class=&quot;highlighter-rouge&quot;&gt;hotpath&lt;/code&gt; releases will likely drop the &lt;code class=&quot;highlighter-rouge&quot;&gt;inline(never)&lt;/code&gt; hack and use DWARF format parsing in case symbol ranges are missing from the binary.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;https://github.com/pawurb/hotpath-rs/issues/341&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this GH issue&lt;/a&gt; if you would like to contribute this improvement.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;A large chunk of my career was dedicated to optimizing Ruby on Rails applications, where numbers below a millisecond rarely even appeared in logs. So working on problems where nanosecond-scale improvements can have a significant impact has completely changed my perspective on performance engineering. &lt;em&gt;Profiling the profiler&lt;/em&gt; is fun. I’m looking forward to continuing work on &lt;a href=&quot;https://github.com/pawurb/hotpath-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;hotpath-rs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;BTW, as much as I enjoy feeding LLMs with my writing, if you’re a human reading this, please leave a comment here &lt;a href=&quot;https://old.reddit.com/r/rust/comments/1tb7n30/lessons_learned_building_highperformance_rust/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;or on Reddit&lt;/a&gt; so I know you exist.&lt;/p&gt;
</description>
        <pubDate>Tue, 12 May 2026 18:30:40 +0200</pubDate>
        <link>https://pawelurbanek.com/rust-performance-profiling</link>
        <guid isPermaLink="true">https://pawelurbanek.com/rust-performance-profiling</guid>
      </item>
    
      <item>
        <title>How to Automate MEV Analysis on EVM Chains using OpenClaw MCP</title>
        <description>&lt;p&gt;Searching for MEV profit opportunities often requires tedious blockchain monitoring and analysis. In this tutorial, I’ll describe how to automate this process using the &lt;a href=&quot;https://mevlog.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog-rs&lt;/a&gt; MCP interface and the &lt;a href=&quot;https://openclaw.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenClaw&lt;/a&gt; agent. We’ll use a secure HTTPS connection and Telegram chat for communication. We’ll also discuss the pros and cons of using OpenClaw vs. a manual MCP integration.&lt;/p&gt;

&lt;h2 id=&quot;initial-setup-and-rpc-requirements&quot;&gt;Initial setup and RPC requirements&lt;/h2&gt;

&lt;p&gt;I don’t think the world needs another article on a basic OpenClaw setup. Any tutorial on &lt;a href=&quot;https://www.youtube.com/results?search_query=openclaw+ubuntu&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;running OpenClaw with a Ubuntu VPS&lt;/a&gt; should work. I will assume you already have an OpenClaw integration with a communication interface, such as Telegram, configured.&lt;/p&gt;

&lt;p&gt;For an MCP integration, you’ll need an LLM agent configured and running locally. For the rest of this article, we will use the &lt;a href=&quot;https://claude.com/product/claude-code&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Claude Code CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An optional but useful prerequisite for this tutorial is running a Geth full node on the same machine as OpenClaw. Local node offers significantly better performance for scanning larger block ranges. You can also use a premium RPC endpoint, e.g., from &lt;a href=&quot;https://www.alchemy.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Alchemy&lt;/a&gt;. But if you want a quick and easy way to test this setup, you can also use the built-in &lt;a href=&quot;https://chainlist.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ChainList integration&lt;/a&gt;, which automatically selects the fastest free RPC endpoint. But, please remember that free RPC endpoints are often throttled, don’t provide access to older data, and disable EVM tracing features.&lt;/p&gt;

&lt;h2 id=&quot;configuring-an-mcp-server-with-mevlog-rs&quot;&gt;Configuring an MCP server with mevlog-rs&lt;/h2&gt;

&lt;p&gt;Let’s start with a less “magical” setup. We will first configure a local MCP server and later a remote one to enable querying blockchain data by simply talking to your LLM agent. Full reference is in the &lt;a href=&quot;https://github.com/pawurb/mevlog-rs/blob/main/docs/MCP.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog-rs MCP docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, install an mevlog-rs CLI with the MCP feature enabled:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mevlog &lt;span class=&quot;nt&quot;&gt;--features&lt;/span&gt; mcp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Start by verifying if basic querying works:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; latest &lt;span class=&quot;nt&quot;&gt;--rpc-url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ETH_RPC_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should see JSON output with txs from the recent block.&lt;/p&gt;

&lt;p&gt;Now run a mevlog MCP server locally:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog mcp &lt;span class=&quot;nt&quot;&gt;--rpc-url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ETH_RPC_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, configure it with your Claude Code instance:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude mcp add &lt;span class=&quot;nt&quot;&gt;--transport&lt;/span&gt; http mevlog http://localhost:6671
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! You can query blockchain through an LLM interface by just asking questions like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;query the last 50 blocks for transactions that transferred PEPE (&lt;code class=&quot;highlighter-rouge&quot;&gt;0x6982508145454ce325ddbe47a25d4ec3d2311933&lt;/code&gt;) token&lt;/li&gt;
  &lt;li&gt;find transactions that transferred over 100k USDC (&lt;code class=&quot;highlighter-rouge&quot;&gt;0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&lt;/code&gt;) in the last 200 blocks&lt;/li&gt;
  &lt;li&gt;which tx transferred the most DAI (&lt;code class=&quot;highlighter-rouge&quot;&gt;0x6b175474e89094c44da98b954eedeac495271d0f&lt;/code&gt;) in the last 6 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;etc.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Claude Code using mevlog using an MCP interface&quot; title=&quot;Claude Code using mevlog using an MCP interface&quot; src=&quot;/assets/claude-code-mevlog-8dc9e02e8c0d6a2551a0f61dd829f3733f6336182252b56956f39492c0f78d74.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;Claude Code mevlog-rs MCP integration&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;configuring-a-remote-mcp-endpoint-for-better-rpc-performance&quot;&gt;Configuring a remote MCP endpoint for better RPC performance&lt;/h2&gt;

&lt;p&gt;The above setup assumes that the &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; CLI executes on your local machine. For production-like deployments, this is usually not optimal. The largest bottleneck of querying blockchains is downloading the data. &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; uses caching extensively, so repetitive queries against the same block ranges will be significantly faster, but initial data must be downloaded over the wire.&lt;/p&gt;

&lt;p&gt;For the best download performance, I recommend a proprietary full node. But you’re likely not run it in a local network but on an external VPS. To configure &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; with an external node, you’ll need an authenticated HTTPS gateway using NGINX. So let’s do it now.&lt;/p&gt;

&lt;p&gt;I won’t elaborate on configuring SSH access or running a full node on a proprietary VPS. Check &lt;a href=&quot;/ethereum-node-aws&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my other tutorial&lt;/a&gt; for detailed info on how to do it.&lt;/p&gt;

&lt;p&gt;To expose your MCP endpoint externally, you’ll need a similar nginx config:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[::]:80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mcphost.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;301&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$host$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;[::]:443&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mcphost.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;ssl_certificate&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/etc/nginx/ssl/mcphost.crt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;ssl_certificate_key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/etc/nginx/ssl/mcphost.key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;ssl_protocols&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TLSv1.2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TLSv1.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;ssl_session_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;shared:SSL:10m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;ssl_session_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/mcp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://127.0.0.1:6671/mcp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_http_version&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-For&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Connection&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_buffering&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_request_buffering&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;chunked_transfer_encoding&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_read_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3600s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_send_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3600s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/.well-known/oauth-authorization-server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://127.0.0.1:6671/.well-known/oauth-authorization-server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_http_version&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/.well-known/oauth-protected-resource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://127.0.0.1:6671/.well-known/oauth-protected-resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_http_version&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I usually use a &lt;a href=&quot;https://cloudflare.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cloudflare&lt;/a&gt; proxy and origin certificates to configure a secure SSL connection. Alternatively, you can use &lt;a href=&quot;https://certbot.eff.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Certbot&lt;/a&gt;. BTW, if you already have an OpenClaw running on your VPS, you can ask him to configure it; he’ll know what to do.&lt;/p&gt;

&lt;p&gt;Now start the authenticated MCP process on a VPS:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;MEVLOG_MCP_AUTH_TOKEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;mcp_password mevlog mcp &lt;span class=&quot;nt&quot;&gt;--rpc-url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ETH_RPC_URL&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;127.0.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and configure your Claude Code to use this external endpoint:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;claude mcp add &lt;span class=&quot;nt&quot;&gt;--transport&lt;/span&gt; http mevlog-remote https://mcphost.com &lt;span class=&quot;nt&quot;&gt;--header&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Authorization: Bearer mcp_password&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and use it as you did before. It’s a simple way to use a high-performance, externally hosted node to query the blockchain from a local LLM agent.&lt;/p&gt;

&lt;h3 id=&quot;how-to-query-evm-blockchains-with-the-openclaw-llm-agent&quot;&gt;How to query EVM blockchains with the OpenClaw LLM agent&lt;/h3&gt;

&lt;p&gt;The MCP server required some manual configuration. If you’re more into “agentic workflows”, the OpenClaw setup might work better for you. Once you already have an OpenClaw configured, getting started with mevlog-rs is straightforward. There’s a dedicated &lt;a href=&quot;https://github.com/pawurb/mevlog-rs/blob/main/skills/mevlog-cli/SKILL.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SKILL.md&lt;/a&gt; file that explains how to use the CLI directly without the MCP endpoint. A recommended setup is to run OpenClaw on the same VPS as your RPC node to ensure the best performance.&lt;/p&gt;

&lt;p&gt;Just ask your OpenClaw to import the skill into memory, and provide an RPC (local or remote) endpoint, and you’re good to go.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;OpenClaw agent using mevlog using a dedicated skill&quot; title=&quot;OpenClaw agent using mevlog using a dedicated skill&quot; src=&quot;/assets/openclaw-mevlog-7111743dcf8b180888b1fa4ce0be02fbcb8c0ca301b05440ecb3384b7736b2d8.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;center annotation&quot;&gt;Query EVM chains by chatting with your OpenClaw agent&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I find OpenClaw setup useful for running scheduled queries and reporting directly to a Telegram channel. You can configure cron tasks like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;every 1h check and notify me if any transaction transferred more than 1 million USDC&lt;/li&gt;
  &lt;li&gt;each day at 8AM send me a report on which 10 txs paid the most for gas in the last 24 hours&lt;/li&gt;
  &lt;li&gt;how many txs did &lt;code class=&quot;highlighter-rouge&quot;&gt;jaredfromsubway.eth&lt;/code&gt; send in the last 24h, and how much was his total gas spending&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;etc.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;I wish I had access to similar tooling when I was full-time focused on building MEV bots some time ago. It would have saved me dozens of hours of manually querying blockchains for relevant txs. I hope you’ll find some of these techniques useful. Please send any feature requests or bug reports.&lt;/p&gt;

&lt;p&gt;Also, stay tuned: I’m currently working on a major release of the &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; CLI that will massively speed up and improve querying across large block ranges.&lt;/p&gt;
</description>
        <pubDate>Sat, 25 Apr 2026 13:38:40 +0200</pubDate>
        <link>https://pawelurbanek.com/openclaw-mev-mcp</link>
        <guid isPermaLink="true">https://pawelurbanek.com/openclaw-mev-mcp</guid>
      </item>
    
      <item>
        <title>Using LLMs and MCP to Debug PostgreSQL Performance in Rails</title>
        <description>&lt;p&gt;I’ve recently automated a large portion of my Rails performance audits. In this tutorial, I’ll describe how to configure an AI-powered PG performance debugging. We will cover using LLMs with custom MCP (model context protocol) &lt;a href=&quot;https://n8n.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;n8n&lt;/a&gt; integration. We will also discuss the legal and security implications of connecting AI to the production database.&lt;/p&gt;

&lt;h2 id=&quot;how-to-optimize-postgresql-performance-with-ai&quot;&gt;How to optimize PostgreSQL performance with AI?&lt;/h2&gt;

&lt;p&gt;I’m not sure if the current blogosphere needs yet another introduction to &lt;a href=&quot;https://modelcontextprotocol.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MCP&lt;/a&gt;. Instead, here’s a sneak peek of what we will be building in this tutorial:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;rails-pg-extras-mcp Slack n8n integration diagnose&quot; title=&quot;rails-pg-extras-mcp Slack n8n integration diagnose&quot; src=&quot;/assets/pg-mcp-diagnose-fb9adf197897d3995a0358a1cb7695bf200ffd7cf6055abfaf4c1648adba8a5c.png&quot; /&gt;
&lt;img class=&quot;center-image&quot; alt=&quot;rails-pg-extras-mcp Slack n8n integration explain analyze&quot; title=&quot;rails-pg-extras-mcp Slack n8n integration explain analyze&quot; src=&quot;/assets/pg-mcp-analyze-bd0212b714b9adeec57b85e8b2e84f4bf37f6fead0bcbff49a197f86bbeb6d8a.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TLDR&lt;/em&gt; Slack bot debugging PG performance.&lt;/p&gt;

&lt;p&gt;A Slack bot connected to the PostgreSQL database? Can it run arbitrary SQL queries? Does it send production data to OpenAI servers? Is it any good in nontrivial Rails apps? Will it yolo a &lt;code class=&quot;highlighter-rouge&quot;&gt;DROP TABLE&lt;/code&gt; on prod at 3 AM?&lt;/p&gt;

&lt;p&gt;Let’s answer all these questions.&lt;/p&gt;

&lt;h2 id=&quot;debugging-context-provided-by-the-pg-extras-gem&quot;&gt;Debugging context provided by the pg-extras gem&lt;/h2&gt;

&lt;p&gt;Copy-pasting queries from &lt;a href=&quot;https://github.com/heroku/heroku-pg-extras&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;heroku-pg-extras&lt;/a&gt; one weekend turned out to be a good thing. I’ve been maintaining the &lt;a href=&quot;https://github.com/pawurb/rails-pg-extras&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rails-pg-extras gem&lt;/a&gt; for over 6 years now. The project’s relative popularity helped me onboard multiple clients and earn a living as a Rails performance consultant for over four years.&lt;/p&gt;

&lt;p&gt;A significant part of my performance audits and subsequent retainer offering was interpreting the output of the pg-extras gem. It currently exposes ~40 methods. A solid entry point is the &lt;code class=&quot;highlighter-rouge&quot;&gt;diagnose&lt;/code&gt; method, which aggregates over a dozen checks to produce a report about the database health. By configuring the web interface, you can get a nice dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;rails-pg-extras web dashboard&quot; title=&quot;rails-pg-extras web dashboard&quot; src=&quot;/assets/pg-extras-diagnose-web-902027d5af0e434b394b0999b99d7d0ae31da1da2fee369315d9e0bd0a255890.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;rails-pg-extras web dashboard diagnose report&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Rails projects with a dynamic development pace and sizeable traffic are likely to discover new db-level inefficiencies every week. But, deep dive into metadata exposed by the pg-extras API can be tedious and repetitive process.&lt;/p&gt;

&lt;p&gt;Here’s where a brand new &lt;a href=&quot;https://github.com/pawurb/rails-pg-extras-mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rails-pg-extras-mcp gem&lt;/a&gt; makes an entrance.&lt;/p&gt;

&lt;h2 id=&quot;automating-pg-extras-performance-debugging-with-llms&quot;&gt;Automating pg-extras Performance Debugging with LLMs&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rails-pg-extras-mcp&lt;/code&gt; is a relatively simple (~300 boilerplate LOC total) wrapper on top of &lt;code class=&quot;highlighter-rouge&quot;&gt;rails-pg-extras&lt;/code&gt;. It exposes the API in a format digestible by LLMs. By connecting it to your AI model of choice, you can automate the repetitive task of hunting for inefficiencies in the database layer.&lt;/p&gt;

&lt;p&gt;This gem is a core component of the previously showcased Slack bot demo. Slack bot is just an arbitrary choice of interface, configured with the help of the &lt;a href=&quot;https://n8n.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;n8n&lt;/a&gt; platform:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;rails-pg-extras-mcp n8n integration schema&quot; title=&quot;rails-pg-extras-mcp n8n integration schema&quot; src=&quot;/assets/pg-mcp-n8n-e0ec040641c7d37fa6c6a9a951a8ebee949f2fc0b4690ccdc6320529cd78f523.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;rails-pg-extras MCP with Slack bot n8n schema&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;MCP protocol is universal, so you can use it with local Cursor IDE, Claude Code, OpenAI API, basically any modern LLM.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;rails-pg-extras-mcp Claude Desktop integration&quot; title=&quot;rails-pg-extras-mcp Claude Desktop integration&quot; src=&quot;/assets/pg-extras-mcp-claude-22e6f777e0fe69902c043a894111944c5ce27a6d53fa888c36bba3d4ee4cecae.jpg&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;Claude Desktop rails-pg-extras MCP integration&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I like the Slack bot interface because it works well for team collaboration. Realistically, you cannot expect LLM to resolve PostgreSQL issues with 100% accuracy. However, the usual bottleneck of working with pg-extras is making sense of its verbose output. A raw output is often illegible:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Raw rails-pg-extras metadata output&quot; title=&quot;Raw rails-pg-extras metadata output&quot; src=&quot;/assets/raw-pg-extras-output-8d3c42a8b2c00d586fc0978f983834abd5d80b42a89a835101fb55fe42298c8f.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;Raw rails-pg-extras output&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;LLMs do a fantastic job, extracting useful info from raw data and reformatting it to vastly simplify human analysis. I’ve configured the demo Slack bot to only reply if mentioned by the &lt;code class=&quot;highlighter-rouge&quot;&gt;@n8n&lt;/code&gt; handle. A development team can discuss ongoing db issues on a dedicated channel and augment context by &lt;em&gt;predigested&lt;/em&gt; metadata from the LLM.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Slack bot PostgreSQL debugging discussion&quot; title=&quot;Slack bot PostgreSQL debugging discussion&quot; src=&quot;/assets/slack-bot-pg-discussion-8d80bfd959d63956d4f13d1a4fe03559716e33023fe599254c567caac27bf9c6.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;LLM-powered PG debugging session&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Please remember the Slack bot this is just a choice of interface. You can configure LLMs to email you weekly database healthcheck reports PDF, talk to the database directly from the Cursor IDE chatbox etc.&lt;/p&gt;

&lt;p&gt;So it looks pretty cool, but can you use it on production without summoning the GDPR police?&lt;/p&gt;

&lt;h2 id=&quot;legal-and-security-concerns-of-using-llms-with-production-data&quot;&gt;Legal and security concerns of using LLMs with production data&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;DISCLAIMER: I’m not a lawyer, and this is not legal advice. Please do your own research before exposing any sensitive data to LLMs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rails-pg-extras&lt;/code&gt; core component is &lt;a href=&quot;https://github.com/pawurb/ruby-pg-extras&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ruby-pg-extras gem&lt;/a&gt;. It defines &lt;a href=&quot;https://github.com/pawurb/ruby-pg-extras/tree/main/lib/ruby_pg_extras/queries&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;em&gt;metadata queries&lt;/em&gt;&lt;/a&gt; with performance debug data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;METADATA&lt;/em&gt; is an critical keyword here. It means that you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;rails-pg-extras&lt;/code&gt; MCP integration without giving LLMs access to ANY business logic-related tables. Additionally, even query argument values are not exposed. Here’s a sample output excerpt of the &lt;code class=&quot;highlighter-rouge&quot;&gt;calls&lt;/code&gt; method, which lists top bottlenecks:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT first_name, last_name, email FROM customers WHERE email LIKE $1 ORDER BY last_name LIMIT $2     
SELECT * FROM orders WHERE order_date &amp;gt;= $1 ORDER BY order_date DESC LIMIT $2                          
SELECT * FROM customers WHERE loyalty_points &amp;gt; $1 ORDER BY loyalty_points DESC LIMIT $2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, values are replaced by &lt;code class=&quot;highlighter-rouge&quot;&gt;$1&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;$2&lt;/code&gt; placeholders. It means that in the default &lt;code class=&quot;highlighter-rouge&quot;&gt;rails-pg-extras-mcp&lt;/code&gt; configuration, LLMs cannot read any sensitive data.&lt;/p&gt;

&lt;p&gt;To strengthen this assumption, you can configure a dedicated user with read access only to the metadata tables:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_viewer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NOLOGIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;WITH&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PASSWORD&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'your_password'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_viewer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CONNECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;your_db_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USAGE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SCHEMA&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_stat_statements&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_stat_activity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_locks&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_stat_user_indexes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_stat_all_tables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_stat_database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_namespace&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_relation_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_indexes_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_table_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pg_total_relation_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extras_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and configure the ENV value:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RAILS_PG_EXTRAS_MCP_DATABASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;postgres://extras_user:your_password@localhost:5432/your_db_name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m not sure if it’s enough to keep the GDPR inspectors happy. But, it’s an order of magnitude safer compared to letting LLMs roam free with a full-blown read-write db access.&lt;/p&gt;

&lt;h3 id=&quot;enabling-optional-explain-analyze-mode&quot;&gt;Enabling optional EXPLAIN ANALYZE mode&lt;/h3&gt;

&lt;p&gt;If you feel adventurous, you can toggle &lt;code class=&quot;highlighter-rouge&quot;&gt;PG_EXTRAS_MCP_EXPLAIN_ENABLED&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;PG_EXTRAS_MCP_EXPLAIN_ANALYZE_ENABLED&lt;/code&gt; flags. They add &lt;code class=&quot;highlighter-rouge&quot;&gt;explain&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;explain_analyze&lt;/code&gt; tools to the MCP interface:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;LLM using EXPLAIN ANALYZE to debug query performance&quot; title=&quot;LLM using EXPLAIN ANALYZE to debug query performance&quot; src=&quot;/assets/llm-explain-analyze-b88edd73d93e5c3a90be22f8e3de6cacf8a2ed54ae0c888e5fd501c210b3171b.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;center annotation&quot;&gt;LLM running EXPLAIN ANALYZE on a bottleneck query&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;This feature allows LLMs to run &lt;code class=&quot;highlighter-rouge&quot;&gt;EXPLAIN&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;EXPLAIN ANALYZE&lt;/code&gt; on selected SQL queries. Hand-crafting queries for such analysis by replacing &lt;code class=&quot;highlighter-rouge&quot;&gt;$1, $2&lt;/code&gt; placeholders with matching data can be a little tedious and time-consuming. LLMs do a fantastic job automating this process.&lt;/p&gt;

&lt;p&gt;A critical difference is that it requires read access to the underlying tables. And while &lt;code class=&quot;highlighter-rouge&quot;&gt;EXPLAIN&lt;/code&gt; outputs the only theoretical execution plan, &lt;code class=&quot;highlighter-rouge&quot;&gt;EXPLAIN ANALYZE&lt;/code&gt; runs the actual query and reports real execution metrics. It comes with an increased risk of potentially exposing sensitive data to the LLMs. Or even writing to the database if you forget to configure read-only access. I did put &lt;a href=&quot;https://github.com/pawurb/rails-pg-extras-mcp/blob/main/lib/rails_pg_extras_mcp/validate_query.rb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;some effort&lt;/a&gt; into preventing LLMs from injecting malicious SQL.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;LLM SQL injection attempt&quot; title=&quot;LLM SQL injection attempt&quot; src=&quot;/assets/explain-analyze-delete-5d1a94a7f261d9f84ad8aae6c5a4444e35704b41c05412e52557866cf4f19723.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;No hesitation&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;But I wouldn’t bet that a crafty LLM cannot sneak a &lt;code class=&quot;highlighter-rouge&quot;&gt;DROP TABLE&lt;/code&gt; SQL injection past these safeguards. If you have feedback on how this security layer could be improved, please get in touch.&lt;/p&gt;

&lt;h2 id=&quot;how-much-does-it-cost&quot;&gt;How much does it cost?&lt;/h2&gt;

&lt;p&gt;So far, I’ve had the most success with OpenAI &lt;code class=&quot;highlighter-rouge&quot;&gt;gpt-4o&lt;/code&gt; model. During the two weekends I spent testing the setup, sending ~200 Slack messages, I managed to rack up a whopping total of $1.56 in API charges.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;OpenAI API cost dashboard&quot; title=&quot;OpenAI API cost dashboard&quot; src=&quot;/assets/open-ai-api-cost-9beaa92c3cfda2c2b0d1286e5fcd40378806f9ef02fd3565ccb20465351c85c7.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Anthropic models seem significantly more expensive. &lt;code class=&quot;highlighter-rouge&quot;&gt;Claude Sonnet 4&lt;/code&gt; costs ~$0.05 per chat message, and &lt;code class=&quot;highlighter-rouge&quot;&gt;Claude Opus 4&lt;/code&gt; devoured $1 for a single performance analysis. I was unable to distinguish the difference in quality of output depending on the model, but I did not spend much time cross-testing them. An interesting strategy could be to use a pricey deep-thinking model for weekly performance reports, and a cheaper one for everyday chat usage.&lt;/p&gt;

&lt;p&gt;On top of that you’ll need &lt;a href=&quot;https://n8n.io/pricing/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;an n8n instance&lt;/a&gt; for ~$20 month, but you can also self-host it.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Make sure to check out the &lt;a href=&quot;https://github.com/pawurb/rails-pg-extras-mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rails-pg-extras-mcp readme&lt;/a&gt; for info on how to use it with your Rails app. The project in an early POC stage, so contributions are welcome.&lt;/p&gt;

&lt;p&gt;A Slack bot, some LLM magic, a few hundred lines of glue code, and just like that… I’ve automated myself out of PG consulting gigs. The future looks scary. I’ll be off now to practice my plumbing skills.&lt;/p&gt;
</description>
        <pubDate>Tue, 22 Jul 2025 00:09:04 +0200</pubDate>
        <link>https://pawelurbanek.com/rails-postgresql-mcp</link>
        <guid isPermaLink="true">https://pawelurbanek.com/rails-postgresql-mcp</guid>
      </item>
    
      <item>
        <title>My puts Debugging Workflow in Rails Apps</title>
        <description>&lt;p&gt;Puts debugging is the best. In this tutorial, I’ll walk you through my workflow for debugging Rails codebases using a custom toolkit inspired by Rust. We will also discuss how to smuggle your debug tools, through even the most rigorous code review process.&lt;/p&gt;

&lt;h2 id=&quot;why-and-how-to-puts-debug-ruby-code&quot;&gt;Why and how to puts debug Ruby code?&lt;/h2&gt;

&lt;p&gt;I’ve never been a fan of (or properly learned) using debug breakpoints. While I sometimes use &lt;a href=&quot;https://github.com/ruby/debug&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ruby debug&lt;/code&gt;&lt;/a&gt;, most of the time, &lt;em&gt;I’m a puts debugger&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My favorite way to debug is via an automated test suite. I usually have the &lt;a href=&quot;https://github.com/AlexB52/retest&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;retest&lt;/code&gt; gem&lt;/a&gt; running in the background to automatically execute relevant tests on each file save. If I need to narrow the scope, I use a shortcut to copy the current filename with the line number and run a single test:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rs spec/models/user_spec.rb:42
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;center annotation&quot;&gt;&lt;code&gt;rs&lt;/code&gt; is a alias for &lt;code&gt;bundle exec rspec&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;So far, I’ve relied on quick-and-dirty print statements like these:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'!!!!!!!!!!!!!!'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'first_user'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'second_user'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;second_user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'!!!!!!!!!!!!!!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please don’t tell me you haven’t written something like this before. It highlights a few issues with the default way to debug in Ruby.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!!!!!!!!!!!!!!!&lt;/code&gt; - annotation is often needed. &lt;code class=&quot;highlighter-rouge&quot;&gt;p&lt;/code&gt; debug statements don’t stand out enough in a verbose test/logs output.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;p 'first_user', first_user&lt;/code&gt; - manually adding labels is necessary to identify which debug statement is which.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;p&lt;/code&gt; method calls are hard to clean up later since they might already exist elsewhere in the codebase.&lt;/p&gt;

&lt;h2 id=&quot;introducing-dbg-rb---a-simple-ruby-puts-debugging-helper&quot;&gt;Introducing &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; - a simple Ruby puts debugging helper&lt;/h2&gt;

&lt;p&gt;Recently, I’ve been writing more Rust than Ruby. I really came to appreciate its built-in &lt;a href=&quot;https://doc.rust-lang.org/std/macro.dbg.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dbg!&lt;/code&gt; macro&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;derive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;user1&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;password1&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;dbg!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// [src/main.rs:15:5] user = User {&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//     username: &quot;user1&quot;,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//     password: &quot;password1&quot;,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//     active: true,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It produces a verbose, source-located output, together with the name of the variable. Coming back to Ruby, it felt odd that such a simple and handy helper isn’t available out of the box.&lt;/p&gt;

&lt;p&gt;Hence, the birth of the &lt;a href=&quot;https://github.com/pawurb/dbg-rb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; gem&lt;/a&gt;. It’s my attempt to recreate the Rust &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg!&lt;/code&gt; macro in Ruby:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dbg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:as_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;dbg-rb debug output&quot; title=&quot;dbg-rb debug output&quot; src=&quot;/assets/dbg_base-d979825f94e67009a5af5d62fc63d47ebeb8bb92cd7d64a66bb1c87cd9c2c292.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It prints the filename, line number, and source expression — all with colored output for quick scanning. It also works for method and keyword arguments. For me, knowing the source location and seeing labeled variables names makes debugging faster and far more pleasant to work with. What’s more, the &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg&lt;/code&gt; method is unique, so it’s trivial to grep and clean up after use.&lt;/p&gt;

&lt;p&gt;For more details and configuration options, check out the documentation in the &lt;a href=&quot;https://github.com/pawurb/dbg-rb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;inline-setup&quot;&gt; How to use dbg-rb in Rails without adding it to the Gemfile?&lt;/h2&gt;

&lt;p&gt;I find &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; useful in my everyday Rails work. But I understand that not all the devs will share this enthusiasm. When doing performance audits, I needed a way to use this tool without cluttering the dependency list — especially in teams that are strict about gem additions.&lt;/p&gt;

&lt;p&gt;In theory it’s possible to add &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; to the IRB config to include it in the Rails console context. But it has little value; I rarely debug directly in the console. I use a test suite for that. It means that &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; has to be available in the &lt;code class=&quot;highlighter-rouge&quot;&gt;bundle exec&lt;/code&gt; context. But how can you add a gem to the Gemfile without modifying it?&lt;/p&gt;

&lt;p&gt;In theory, I could use a local development branch with a custom Gemfile, but cherry-picking changes before submitting a PR is cumbersome.&lt;/p&gt;

&lt;p&gt;Here’s a &lt;em&gt;hack&lt;/em&gt; that I came up with. It’s similar to &lt;a href=&quot;/rails-debug-aliases&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my previous post on Rails debug aliases&lt;/a&gt;. You can inject &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; into a Rails app via a local initializer file that’s ignored by Git globally.&lt;/p&gt;

&lt;p&gt;Start by creating the file with the following contents:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;~/.gitignore_global&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;config/initializers/dbg_rb.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now configure your git to use it:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git config &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; core.excludesfile ~/.gitignore_global
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next you can add an inlined &lt;em&gt;lite&lt;/em&gt; version of the &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; file to your Rails app initializers folder:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://raw.githubusercontent.com/pawurb/dbg-rb/refs/heads/main/inline/dbg_rb.rb &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; config/initializers/dbg_rb.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! You can now use &lt;code class=&quot;highlighter-rouge&quot;&gt;dbg&lt;/code&gt; to debug local variables and parameters with automatically appended labels:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dbg&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;# [spec/user_spec.rb:20] user = #&amp;lt;User id: 1, team_id: 1, slack_id: &quot;develop&quot;, pseudonym: ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;None of these steps leaves a trace in the git diff, so you can apply it to any Ruby/Rails project without a code review process. Debug tools like this are safe for local use. But be cautious if you’re introducing less obvious techniques — always consult your team before adding anything bypassing standard workflows.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;I find the additional context provided by &lt;a href=&quot;https://github.com/pawurb/dbg-rb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dbg-rb&lt;/code&gt; gem&lt;/a&gt; helpful for non-trivial debugging sessions. And if you’re a &lt;em&gt;“vibe debugger”&lt;/em&gt;, feeding this extra info to the LLMs increases your chance to quickly produce a correct answer. I wish a similar tool was available by default like in Rust. Unfortunatelly, this &lt;a href=&quot;https://bugs.ruby-lang.org/issues/20987&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ruby feature proposal&lt;/a&gt; has received little traction so far. If you have suggestions for improving dbg-rb — feedback, PRs, and bug reports are always appreciated.&lt;/p&gt;
</description>
        <pubDate>Mon, 19 May 2025 07:58:21 +0200</pubDate>
        <link>https://pawelurbanek.com/rails-puts-debug</link>
        <guid isPermaLink="true">https://pawelurbanek.com/rails-puts-debug</guid>
      </item>
    
      <item>
        <title>How to Extract long-tail MEV Profit from Uniswap</title>
        <description>&lt;p&gt;Any imbalance that makes it past the Mempool is a potential source of MEV profit. In this post, I describe the process of discovering, analyzing, and executing a long-tail MEV strategy on the Ethereum Mainnet. We will discuss Mempool monitoring, oracle backrunning, recurring Defi exploits, and more. I’ll also showcase how to use &lt;a href=&quot;https://mevlog.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog.rs&lt;/a&gt; CLI and web interface for finding long-tail profit opportunities.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: The information provided in this blog post is for educational purposes only and should not be treated as financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spoiler alert: I made $6.50&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-discover-and-profit-from-imbalanced-erc20-pools&quot;&gt;How to discover and profit from imbalanced ERC20 pools?&lt;/h2&gt;

&lt;p&gt;Code examples for this tutorial are available &lt;a href=&quot;https://github.com/pawurb/uniswap-skimmer-mev&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;in the repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core of the strategy we will explore is called ”skimming”. It’s a mechanism implemented by the UniswapV2 protocol, that is designed to balance token reserves.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;UniswapV2 skim interface&quot; title=&quot;UniswapV2 skim interface&quot; src=&quot;/assets/skim-swap-interface-1511c34b4fe3c938de9d4741c3a9153f39fe4b71af6a34c5a8b938b1b235a3c3.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;UniswapV2 pools have a built-in skimming feature, and MEV bot operators can use it to profit from imbalanced pools.&lt;/p&gt;

&lt;p&gt;This is how it works: UniswapV2 pool has two sources of data on its current ERC20 tokens balances. Internally, it keeps &lt;code class=&quot;highlighter-rouge&quot;&gt;reserve0&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;reserve1&lt;/code&gt; storage slots, but actual token balances are determined by calling the &lt;code class=&quot;highlighter-rouge&quot;&gt;balanceOf(address)&lt;/code&gt; method on the ERC20 token contract.&lt;/p&gt;

&lt;p&gt;If &lt;code class=&quot;highlighter-rouge&quot;&gt;balanceOf&lt;/code&gt; of &lt;code class=&quot;highlighter-rouge&quot;&gt;token0&lt;/code&gt; returns a value higher than &lt;code class=&quot;highlighter-rouge&quot;&gt;reserve0&lt;/code&gt;, a surplus amount can be &lt;em&gt;“skimmed”&lt;/em&gt; from the pool or used to make the standard &lt;code class=&quot;highlighter-rouge&quot;&gt;swap&lt;/code&gt; operation. This is an expected behavior for a UniswapV2 pool. Any &lt;code class=&quot;highlighter-rouge&quot;&gt;swap&lt;/code&gt; operation is executed by first transferring the input token to the pool. Usually, these details are abstracted away by the &lt;code class=&quot;highlighter-rouge&quot;&gt;UniswapV2Router&lt;/code&gt; contract. However, MEV bot operators typically use the &lt;code class=&quot;highlighter-rouge&quot;&gt;swap&lt;/code&gt; method directly, transferring the input token without the help of the router.&lt;/p&gt;

&lt;p&gt;Skimming is arguably one of the simplest MEV strategies:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you don’t need any capital, except for gas fees, to execute it&lt;/li&gt;
  &lt;li&gt;custom contract is optional; you can call a pool contract directly&lt;/li&gt;
  &lt;li&gt;off-chain calculations are relatively straightforward, or you can call the &lt;code class=&quot;highlighter-rouge&quot;&gt;skim&lt;/code&gt; method without any maths needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can imagine, these opportunities are highly competitive. I’ve run a skimmer bot on a few EVM chains. On side chains, it’s still possible to regularly find a few cents or even 10+ dollars lying around. But the Mainnet is insanely competitive.&lt;/p&gt;

&lt;p&gt;I’ve collected a list of ~20 Mainnet UniswapV2-compatible factories with over 400k pools. Here’s a Rust snippet you can use to discover factory contracts by observing &lt;code class=&quot;highlighter-rouge&quot;&gt;Sync(uint112,uint112)&lt;/code&gt; logs and some duck-typing Solidity calls:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/uniswap-skimmer-mev/blob/main/examples/discover_factories.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;examples/discover_factories.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;sol!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint112&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uint112&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;#[sol(rpc)]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IUniV2Pair&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;#[sol(rpc)]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IUniV2Factory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;allPairsLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;uint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[tokio&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;known_factories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Discovering factories...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rpc_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;wss://ethereum-rpc.publicnode.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WsConnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rpc_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ProviderBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.on_ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Sync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGNATURE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.from_block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;BlockNumberOrTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Latest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.subscribe_logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into_stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ipair&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IUniV2Pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ipair&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;._0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ifactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IUniV2Factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_pairs_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ifactory&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.allPairsLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_pairs_length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_pairs_length&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;._0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_pairs_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;U256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZERO&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;known_factories&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Found new UniV2 factory: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;known_factories&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running it for a while should produce a similar output:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo run &lt;span class=&quot;nt&quot;&gt;--example&lt;/span&gt; discover_factories
&lt;span class=&quot;c&quot;&gt;# Discovering factories...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Found new UniV2 factory: 0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Found new UniV2 factory: 0x5fa0060fcfea35b31f7a5f6025f0ff399b98edf1&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Found new UniV2 factory: 0xc0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Found new UniV2 factory: 0x115934131916c8b277dd010ee02de363c09d037c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It should allow you to quickly gather addresses of active, UniswapV2-compatible factories on any EVM chain. Later, you can get all the pool addresses from the factory contract. Alternatively you can rework this script to analyze past log entries instead of live monitoring.&lt;/p&gt;

&lt;p&gt;For my skimmer bot, I’m using aggregate contract similar to &lt;a href=&quot;https://github.com/flashbots/simple-arbitrage/blob/master/contracts/UniswapFlashQuery.sol&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;UniswapFlashQuery&lt;/a&gt;. With this setup running concurrently on a local Reth node, it’s possible to scan these 400k pools for imbalances in ~10 seconds. It was a decent improvement because the initial synchronous and non-batched version took over 15 minutes to scan the same dataset.&lt;/p&gt;

&lt;p&gt;It means that all of the new blocks are covered. But, I’ve observed only a few imbalances that made it past the mempool and were immediately snatched by other MEV bots.&lt;/p&gt;

&lt;p&gt;Skimming strategy is well-known, so no wonder there was no easy profit there. But I’ve decided to dig deeper.&lt;/p&gt;

&lt;h2 id=&quot;one-exploit-a-day&quot;&gt;One exploit a day&lt;/h2&gt;

&lt;p&gt;One reason for the UniV2 pool to become imbalanced are so-called &lt;em&gt;“rebasing tokens”&lt;/em&gt;. These are ERC20 tokens, with &lt;code class=&quot;highlighter-rouge&quot;&gt;balanceOf(address)&lt;/code&gt; implementation that does not map directly to the balances storage slot, but has a dynamic component:&lt;/p&gt;

&lt;p&gt;A popular token implementing such mechanism is &lt;a href=&quot;https://etherscan.io/address/0xae7ab96520de3a18e5e111b5eaab095312d7fe84&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Staked Ether (stETH)&lt;/a&gt; by Lido. Here a source code excerpt:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;external&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;returns&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getPooledEthByShares&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_sharesOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getPooledEthByShares&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_sharesAmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;returns&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_sharesAmount&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_getTotalPooledEther&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_getTotalShares&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see &lt;code class=&quot;highlighter-rouge&quot;&gt;balanceOf&lt;/code&gt; delegates to &lt;code class=&quot;highlighter-rouge&quot;&gt;getPooledEthByShares&lt;/code&gt; that multiplies &lt;code class=&quot;highlighter-rouge&quot;&gt;_sharesAmount&lt;/code&gt; static storage by results of &lt;code class=&quot;highlighter-rouge&quot;&gt;_getTotalPooledEther()&lt;/code&gt; and divides by &lt;code class=&quot;highlighter-rouge&quot;&gt;_getTotalShares()&lt;/code&gt;. It means that when outputs of these method calls change, balance of &lt;code class=&quot;highlighter-rouge&quot;&gt;stETH&lt;/code&gt; in the UniswapV2 pool will increase (while &lt;code class=&quot;highlighter-rouge&quot;&gt;reserve0&lt;/code&gt; stays the same) making skimming possible.&lt;/p&gt;

&lt;p&gt;Call tracing can be useful for quickly spotting ERC20 tokens with non-standard balance logic. Let’s first use &lt;a href=&quot;https://github.com/pawurb/mevlog-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog-rs CLI&lt;/a&gt; to detect a sample transaction that swapped the stETH and WETH tokens on UniswapV2:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 100:latest --event 0xae7ab96520de3a18e5e111b5eaab095312d7fe84 --event 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 --event &quot;Sync(uint112,uint112)&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and now visit &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog.rs&lt;/code&gt; to analyze  &lt;a href=&quot;https://mevlog.rs/trace?tx_hash=0x71e48e1ced0a3f4460b212d6c89f84d8268d455fe2d8a6b1c1193fa074c243d6&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a sample matching transaction call traces&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;stETH balanceOf call traces&quot; title=&quot;stETH balanceOf call traces&quot; loading=&quot;lazy&quot; src=&quot;/assets/steth-balanceof-17bf7954adedc3d43520733e23ce58643a6096185985a70edbd61ae1e02a6e39.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It proxies output of the &lt;code class=&quot;highlighter-rouge&quot;&gt;cast run&lt;/code&gt; command, and since the website uses a Reth archive node, it will work even for older transactions.&lt;/p&gt;

&lt;p&gt;You can see that the &lt;code class=&quot;highlighter-rouge&quot;&gt;balanceOf&lt;/code&gt; call of the stETH (&lt;code class=&quot;highlighter-rouge&quot;&gt;0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84&lt;/code&gt;) contract triggers subsequent calls to the &lt;code class=&quot;highlighter-rouge&quot;&gt;getApp&lt;/code&gt; method. Compare it to &lt;code class=&quot;highlighter-rouge&quot;&gt;balanceOf&lt;/code&gt; of WETH (&lt;code class=&quot;highlighter-rouge&quot;&gt;0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2&lt;/code&gt;), which produces only a simple &lt;code class=&quot;highlighter-rouge&quot;&gt;Return&lt;/code&gt; statement. This technique can help detect tokens with nonstandard (potentially rebasing) balance logic without digging through the smart contracts’ source code.&lt;/p&gt;

&lt;p&gt;To observe how stETH rebase is extracted, I’ve added a balance monitoring script to investigate what happens when stETH balances increases.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;tokio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rpc_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RPC_WS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RPC_WS is not set&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WsConnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rpc_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ProviderBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.on_ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block_stream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.subscribe_blocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into_stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ERC20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STETH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;balance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steth&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TARGET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;._0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block_stream&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_balance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steth&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TARGET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;._0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_balance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;balance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;balance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_balance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;notify_slack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;New stETH balance: {}, block: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;balance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After getting the block number, I checked what landed on top by running a &lt;a href=&quot;https://github.com/pawurb/mevlog-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog-rs CLI command&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 21916169 -p 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As expected, position 0 in this block is a &lt;a href=&quot;https://etherscan.io/tx/0x5afb7ef3d0151ad93d2bd2808b4f333763774635699e52b9846d5cd69abc6403&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Lido oracle update transaction&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Lido oracle update&quot; title=&quot;Lido oracle update&quot; src=&quot;/assets/steth-oracle-fe91e3213cfda7ce154f79437c33b165b36fe59b499abc825df3cd1ee0791d84.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 21916169 -p 1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Followed by &lt;a href=&quot;https://etherscan.io/tx/0x2705cd69eb7cb4ed55bd5cfae6db8ddbc7692ce84cf5914f8702c96799e2e69a&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a weird transaction&lt;/a&gt; executing the expected &lt;code class=&quot;highlighter-rouge&quot;&gt;skim&lt;/code&gt; operation and a dozen &lt;em&gt;“Idol”&lt;/em&gt; NFT token transfers. This MEV bot operator is also responsible enough to immediately start farming all the skimmed stETH tokens:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Idol NFT token attack&quot; title=&quot;Idol NFT token attack&quot; src=&quot;/assets/idol-mev-a5b538ce812392e1fd74e90745fd8fb3e85baadc1a8f2115e3ec2291f64a5217.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On a side note, the &lt;a href=&quot;https://mevlog.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog.rs&lt;/a&gt; web version is handy for quickly checking the surrounding context of any transaction. Here’s a &lt;a href=&quot;https://mevlog.rs/inspect?tx_hash=0x5afb7ef3d0151ad93d2bd2808b4f333763774635699e52b9846d5cd69abc6403&amp;amp;before=0&amp;amp;after=1&amp;amp;reverse=on&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;link&lt;/a&gt; showing data for these two neighboring transactions. On Etherscan, it takes like 5+ clicks to check what’s before and after a transaction from the same block.&lt;/p&gt;

&lt;p&gt;You can also get the same data from the CLI:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog tx 0x5afb7ef3d0151ad93d2bd2808b4f333763774635699e52b9846d5cd69abc6403 -a 1 -r --trace revm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It produces:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Gas Price:         4.82 GWEI&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Gas Tx Cost:       0.00449 ETH | $8.92&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Coinbase Transfer: 0.23117 ETH | $458.65&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Real Tx Cost:      0.23567 ETH | $467.57&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Real Gas Price:    252.75 GWEI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The MEV bot paid &lt;code class=&quot;highlighter-rouge&quot;&gt;~0.23 ETH&lt;/code&gt; bribe to the coinbase miner account and pocketed &lt;code class=&quot;highlighter-rouge&quot;&gt;~0.026/$55&lt;/code&gt; of profit. After some digging I’ve found that MEV bots apparently incorporated &lt;a href=&quot;https://rekt.news/theidolsnft-rekt/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a past Idol NFT token exploit&lt;/a&gt; into their strategy. &lt;a href=&quot;https://4130580353-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fba7s5jnnrOzcoSBMQAGq%2Fuploads%2FaHFdiiTIbOAfE09s9boc%2FThe%20Idols%20Audit%20(WhiteHatDAO).pdf?alt=media&amp;amp;token=b66b4b16-826d-476c-a2de-b11261585099&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Despite the audit&lt;/a&gt;, a vulnerability was discovered around two years after the protocol launch. It allows the owner of a single NFT token to extract all the stETH staking rewards.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://etherscan.io/tx/0x5e989304b1fb61ea0652db4d0f9476b8882f27191c1f1d2841f8977cb8c5284c&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The initial attack&lt;/a&gt; did not even have to backrun the oracle update or excessively bribe the miner. But it’s almost identical to the recurring MEV extraction that is still taking place. &lt;a href=&quot;https://etherscan.io/token/0x439cac149b935ae1d726569800972e1669d17094#code&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Idol NFTs contract&lt;/a&gt; does not use an upgradeable proxy, so this recurring exploit is here to stay.&lt;/p&gt;

&lt;p&gt;Before the exploit, the stETH rebase &lt;a href=&quot;https://etherscan.io/tx/0xbdd6c2a7d9a4491501309e13384c9de8f170794f5c2c4c7379a47e5cc284400a&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;yielded only ~$150 total profit&lt;/a&gt;, with ~%98 going for the bribe. Thanks to the Idol NFTs, profit is now significantly higher. It’s awesome to see MEV searchers constantly evolving and learning from each other.&lt;/p&gt;

&lt;h2 id=&quot;how-to-find-less-competitive-mev-strategies&quot;&gt;How to find less competitive MEV strategies?&lt;/h2&gt;

&lt;p&gt;I don’t really want to become an exploitooor for $50, so I kept searching. I returned to the imbalanced UniswapV2 pool that I discovered using the aggregate reserves scanner.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;eETH swap profit&quot; title=&quot;eETH swap profit&quot; src=&quot;/assets/eeth-swap-opportunity-f900baf4100f7375c945829460697edff7141a14ba5d38f5ac80a923174ce3f4.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Apparently, &lt;a href=&quot;https://etherscan.io/address/0x6db0fe375ccb8ac3c2a0984d678bcd99981ddcb6&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;WETH/eETH Sushi pool&lt;/a&gt; was not skimmed on time, and over $1 of potential profit made it past the Mempool. I’ve determined that exactly like stETH, eeETH is a rebasing token with oracle-based balance updates. One problem with this strategy was that &lt;a href=&quot;https://etherscan.io/tx/0x5d41c1912b990b6b9fe07f7ee268ae98e7f5e0609187b4cd1de36e19cb239105&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;some eETH oracle update transactions&lt;/a&gt; were massive at over 4 MILLION gas, making it less likely to sneak them into the Flashbots priority queue. That’s probably the reason why this opportunity made it past the Mempool. I’ve found a single bot account that did the Mempool oracle backrunning for this pair, with &lt;a href=&quot;https://mevlog.rs/trace?tx_hash=0xb166a45256acc62c98bea0092e203239d9435fb99f615760289a0015c8578e0e&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the following methods call trace&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;eETH inefficient skim tx traces&quot; title=&quot;eETH inefficient skim tx traces&quot; src=&quot;/assets/trace-inefficient-tx-1973be2d71edb565bc5e33b433752d61997c2c8fc5468bbcd88dc3de5b9bd19d.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This bot operator is using a Sushi router &lt;code class=&quot;highlighter-rouge&quot;&gt;swapExactTokensForTokensSupportingFeeOnTransferTokens&lt;/code&gt; method instead of a low-level &lt;code class=&quot;highlighter-rouge&quot;&gt;swap&lt;/code&gt;. His transaction uses over 200k gas compared to 120k from my initial simulation. I’ve decided to pursue this opportunity.&lt;/p&gt;

&lt;h3 id=&quot;simulating-mev-profit-opportunity-with-foundry&quot;&gt;Simulating MEV profit opportunity with Foundry&lt;/h3&gt;

&lt;p&gt;My go-to way to validate an MEV strategy is to write a Forge test/simulation. I’ve started by downloading the eETH token codebase using &lt;code class=&quot;highlighter-rouge&quot;&gt;cast&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cast &lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;0x17144556fd3424edc8fc8a4c940b2d04936d17eb &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; steth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It uses a proxy pattern, so make sure to use the correct address. After digging into the source code, I’ve found a single method that I care about:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;eETH rebase method source&quot; title=&quot;eETH rebase method source&quot; src=&quot;/assets/eeth-rebase-method-4395b0fb0d17527225b2a7bc4a58e96027d220e42db79dbc58e893ce4c21567b.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Calling it changes token balances, making skimming possible. But it’s restricted to the admin account. Here’s the forge simulation that I’ve implemented:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/uniswap-skimmer-mev/blob/main/test/EEthRebase.t.sol&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;test/EEthRebase.t.sol&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;contract&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EEthRebase&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;IUniswapV2Pair&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sushi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;IUniswapV2Pair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x6db0fe375ccB8AC3c2a0984D678Bcd99981Ddcb6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;IERC20&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eeth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IERC20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x35fA164735182de50811E8e2E824cFb9B6118ac2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;IERC20&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;weth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IERC20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0xbeef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;test_Rebase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;deal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ether&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eethBalanceBefore&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eeth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sushi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Eeth balance before: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eethBalanceBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;broadcastRawTransaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rebase_payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eethBalanceAfter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eeth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sushi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;surplus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eethBalanceAfter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eethBalanceBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;uint112&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserve0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;uint112&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserve1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sushi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getReserves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Eeth balance after: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;eethBalanceAfter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Eeth surplus: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;surplus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myWethBalanceBefore&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;weth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;My Weth balance before: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myWethBalanceBefore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;wethAmountOut&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getAmountOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;surplus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserve0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserve1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;wethAmountOut: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;wethAmountOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;sushi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;wethAmountOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myWethBalanceAfter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;weth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;balanceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;My Weth balance after: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myWethBalanceAfter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getAmountOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amountIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserveIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserveOut&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pure&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;returns&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amountOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amountInWithFee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amountIn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;997&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;numerator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amountInWithFee&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserveOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;uint256&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;denominator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reserveIn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;amountInWithFee&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;amountOut&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;numerator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;denominator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running it produces:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;forge &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--fork-url&lt;/span&gt; https://eth.merkle.io &lt;span class=&quot;nt&quot;&gt;--fork-block-number&lt;/span&gt; 21986784 &lt;span class=&quot;nt&quot;&gt;--via-ir&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# [PASS] test_Rebase() (gas: 177393)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Logs:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   Eeth balance before: 16701405221952935511&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   Eeth balance after: 16701674447724070712&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   Eeth surplus: 269225771135201&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   My Weth balance before: 0&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   wethAmountOut: 266528025931141&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   My Weth balance after: 266528025931141&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The beauty of simulations is that you don’t have to worry about protocol implementation details. eETH token logic is proxies all the way down, but all I care about is: &lt;em&gt;moar token == better&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The test shows that broadcasting (&lt;code class=&quot;highlighter-rouge&quot;&gt;vm.broadcastRawTransaction&lt;/code&gt;) the oracle update transaction changes the pair’s eETH token balances, making the profitable &lt;code class=&quot;highlighter-rouge&quot;&gt;swap&lt;/code&gt; operation possible. BTW you can produce a raw, singed payload of any transaction to use for forge broadcasting by running:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cast tx 0x589d4025ac9337f5bfc465b342896be78e978cf5ff6517dd6d3ac60a4869ebef --raw&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It was time to set the trap for the next oracle update tx. To do it I’ve configured a similar mempool monitoring script:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/uniswap-skimmer-mev/blob/main/examples/find_swap_txs.rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;example/find_swap_txs.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;sol!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[tokio&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;main]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rpc_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RPC_WS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RPC_WS is not set&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WsConnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rpc_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ProviderBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.on_ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx_stream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.subscribe_pending_transactions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.into_stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.fuse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracer&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;: &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callTracer&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracerConfig&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;: { &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withLog&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;: true }}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx_stream&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get_transaction_by_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracer_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;serde_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GethDebugTracingOptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracing_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;GethDebugTracingCallOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracing_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracing_opts&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.with_tracing_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracer_opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;.debug_trace_call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                &lt;span class=&quot;nn&quot;&gt;BlockId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;BlockNumberOrTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Latest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;tracing_opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;// println!(&quot;Error tracing tx: {}&quot;, e);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nn&quot;&gt;GethTrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CallTracer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_calls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;collect_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_calls&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.topics&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topics&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.is_empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGNATURE_HASH&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Found swap tx {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;collect_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CallFrame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.calls&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;collect_calls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running it produces:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo run &lt;span class=&quot;nt&quot;&gt;--example&lt;/span&gt; find_swap_txs
&lt;span class=&quot;c&quot;&gt;# Found swap tx 0xb9e154a84948889b93accb1d08e15b816dd92f111ce7e8f6d9df0bfbbeba0f8c&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Found swap tx 0xa8bb148a32e0c4bb37b2dda6c08f8c1aa3ad8fadc28725eaed5a09885a8fe910&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll need a WebSockets RPC endpoint with &lt;code class=&quot;highlighter-rouge&quot;&gt;debug&lt;/code&gt; RPC methods enabled to run this script. It monitors the mempool and traces all the discovered transactions using the &lt;a href=&quot;https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;built-in &lt;code class=&quot;highlighter-rouge&quot;&gt;{tracer: 'callTracer'}&lt;/code&gt;&lt;/a&gt;. Later, it extracts all the logs and prints tx hashes of transactions that emit matching logs.&lt;/p&gt;

&lt;p&gt;To execute the eETH oracle backrun I’m monitoring for the &lt;code class=&quot;highlighter-rouge&quot;&gt;Rebase(uint256,uint256)&lt;/code&gt; event generated by a transaction originating from the &lt;a href=&quot;https://etherscan.io/address/0x12582a27e5e19492b4fcd194a60f8f5e1aa31b0f&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;eETH admin account&lt;/a&gt;. It eliminates the need to trace every single transaction in the mempool. But please remember that for mempool monitoring, you’ll probably need a proprietary full node or a commercial 3rd party plan. This mode of operation is likely to exhaust free RPC node limits quickly.&lt;/p&gt;

&lt;p&gt;Once you get a matching tx hash, the rest of the process goes as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;use &lt;code class=&quot;highlighter-rouge&quot;&gt;get_raw_transaction_by_hash&lt;/code&gt; method to get a signed transaction payload&lt;/li&gt;
  &lt;li&gt;spawn a local Anvil process forking of the mainnet state&lt;/li&gt;
  &lt;li&gt;change the local fork state by submitting signed transaction using the &lt;code class=&quot;highlighter-rouge&quot;&gt;send_raw_transaction&lt;/code&gt; method&lt;/li&gt;
  &lt;li&gt;calculate a profitable swap amount and prepare your skimming transaction payload&lt;/li&gt;
  &lt;li&gt;prepare a Flashbots-compatible bundle with oracle update and swap tx and submit it to the builders using &lt;code class=&quot;highlighter-rouge&quot;&gt;eth_sendBundle&lt;/code&gt; RPC call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Describing all these steps with code examples would bloat the size of this post. But let me know in the comments if you’d like to read a more detailed tutorial on executing this strategy.&lt;/p&gt;

&lt;h2 id=&quot;step-three-is-profit&quot;&gt;Step three is profit&lt;/h2&gt;

&lt;p&gt;Contrary to my previous post about &lt;a href=&quot;/revm-alloy-anvil-arbitrage&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Revm arbitrage simulations&lt;/a&gt; this time I have some actual profit to show for. Here I am, &lt;a href=&quot;https://etherscan.io/tx/0x66ca46fd4743cb9324e7406f2721ce563d02eeba2155630976e3b3e215ba7354&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;doxxing myself for the internet points&lt;/a&gt;. My first eETH oracle backrunning transaction made ~$0.35 of profit!&lt;/p&gt;

&lt;p&gt;For a few days after submitting the first successful oracle backrun, I was dominating this corner of the blockchain and earned a total of ~$6.50:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;eETH swap profit&quot; title=&quot;eETH swap profit&quot; src=&quot;/assets/eeth-rebase-profit-e7e1c559ad994aa13a40f8f117d8c614e170131de87e85b266639d2eb5e0a976.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Later, I shifted my focus to releasing the web version of &lt;a href=&quot;https://mevlog.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog.rs&lt;/a&gt;. After migrating from the Geth full node to the Reth archive node, I’ve only recently fixed the tracing config bug.  So, maybe, this strategy will print a few more $ before the end of the month.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Honest work meme&quot; title=&quot;Honest work meme&quot; src=&quot;/assets/honest-work-df86edacd02cad7b64ab8fd6e7cb9f6f83ef173bdc0f0fe27e76a6cea29906fe.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Finding any loophole in the current state of the Mainnet MEV game is always fun. I hope some of the described tools and techniques will prove helpful for your daily MEV search. And maybe help discover opportunities that can buy more than a cup of coffee. I’m up for more mempool spelunking in the near future so don’t forget to like and subscribe to get notified about new posts.&lt;/p&gt;
</description>
        <pubDate>Tue, 25 Mar 2025 09:29:37 +0100</pubDate>
        <link>https://pawelurbanek.com/uniswap-mev-profit</link>
        <guid isPermaLink="true">https://pawelurbanek.com/uniswap-mev-profit</guid>
      </item>
    
      <item>
        <title>How to Discover long-tail MEV Strategies using Revm</title>
        <description>&lt;p&gt;It’s extremely challenging for MEV newcomers to profit from popular strategies. That’s why, I’ve refined my searching approach to focus on niche and less obvious opportunities. In this blog post, I’ll showcase a Rust CLI tool that helps me query blockchain data to discover long-tail MEV. I will also describe Revm vs RPC EVM tracing techniques and their performance characteristics.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: The information provided in this blog post is for educational purposes only and should not be treated as financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-monitor-evm-chains-for-mev-opportunities&quot;&gt;How to monitor EVM chains for MEV opportunities?&lt;/h2&gt;

&lt;p&gt;Searching for long-tail MEV requires investigating and analyzing nonobvious transaction details.&lt;/p&gt;

&lt;p&gt;Popular tools like &lt;a href=&quot;https://libmev.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;libmev.com&lt;/a&gt; or &lt;a href=&quot;https://eigenphi.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;eigenphi.io&lt;/a&gt; focus on short-tail MEV like arbitrage and liquidations. It’s possible to extract all the transaction data from Etherscan, but I find it cumbersome to click around web UIs. It takes over five clicks to get neighboring (before/after) tx info… Also, please tell me why does &lt;a href=&quot;https://etherscan.io/txs?block=22012328&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Etherscan display block transactions&lt;/a&gt; in reverse order? Isn’t the top of the block always the most interesting??&lt;/p&gt;

&lt;p&gt;(╯°□°）╯︵ ┻━┻&lt;/p&gt;

&lt;p&gt;I’ve found myself writing one-off scripts parsing blockchain data to extract relevant info. So, I’ve decided to develop them into a simple to use CLI for querying blockchain with configurable EVM tracing and filtering options.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/mevlog-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog-rs CLI&lt;/a&gt; is inspired by &lt;a href=&quot;https://github.com/paradigmxyz/cryo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cryo&lt;/a&gt;, &lt;a href=&quot;https://github.com/flashbots/mev-inspect-py&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mev-inspect-py&lt;/a&gt;, and &lt;a href=&quot;https://book.getfoundry.sh/reference/cast/cast-run&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cast run&lt;/a&gt;. It’s a command line tool that outputs EVM transaction details in a visual and quick-to-digest format:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;mevlog-rs EVM log monitoring CLI&quot; title=&quot;mevlog-rs EVM log monitoring CLI&quot; loading=&quot;lazy&quot; src=&quot;/assets/jared-tx-95d580b0670e0f4ddc1bfee0c52ae662296143de3c2d39024c5e7abb12b90c19.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;annotation center&quot;&gt;mevlog-rs displaying one of the infamous sandwiches by jaredfromsubway.eth&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I wanted to build a tool that makes it simple to query and lookup transactions using publicly accessible endpoints. If anything catches your interest you can use the more advanced tooling via clickable links to investigate further.&lt;/p&gt;

&lt;p&gt;By using a local &lt;a href=&quot;https://openchain.xyz/signatures&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;openchain.xyz signatures database&lt;/a&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; displays human readable data instead of hex blobs. Root method calls and events provide an instant overview of what the transaction is all about. Thanks to EVM tracing capabilities, &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; tracks how much bribe was paid to the coinbase and calculates the real effective gas price (i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;(gas_price + bribe) / gas_used&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can also filter transactions by regular expressions matching emitted event signatures and addresses.&lt;/p&gt;

&lt;p&gt;Another feature is detecting tx storage changes, allowing to filter txs by smart contract addresses/ERC 20 tokens that were affected. There’s also &lt;a href=&quot;https://ens.domains/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ENS domains&lt;/a&gt; integration.&lt;/p&gt;

&lt;p&gt;A few examples of currently supported queries:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;find &lt;code class=&quot;highlighter-rouge&quot;&gt;jaredfromsubway.eth&lt;/code&gt; transactions from the last 20 blocks that landed in positions 0-5:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 20:latest -p 0:5 --from jaredfromsubway.eth&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;unknown method signature contract call in top position (likely an MEV bot):&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 10:latest --method &quot;&amp;lt;Unknown&amp;gt;&quot; -p 0&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;query last 50 blocks for transactions in the top 10 slots that transferred PEPE token:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 50:latest -p 0:10 --event &quot;Transfer(address,address,uint256)|0x6982508145454ce325ddbe47a25d4ec3d2311933&quot;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;blocks between 22034300 and 22034320, position 0 transaction that did not emit any &lt;code class=&quot;highlighter-rouge&quot;&gt;Swap&lt;/code&gt; event:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 22034300:22034320 -p 0 --not-event &quot;/(Swap).+/&quot;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;search blocks range for events containing &lt;code class=&quot;highlighter-rouge&quot;&gt;rebase&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Transfer&lt;/code&gt; keywords:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 22045400:22045420 --event &quot;/(?i)(rebase).+/&quot; --event &quot;/(Transfer).+/&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All the above queries use only standard block and logs input. It means that you can use publicly available RPC endpoints like &lt;code class=&quot;highlighter-rouge&quot;&gt;https://eth.merkle.io&lt;/code&gt; and it should not get rate limited. These queries don’t need state data to execute so it works even for very old blocks on non-archive nodes:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 14061012 --rpc-url https://eth.merkle.io&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Mucho bribe&quot; title=&quot;Mucho bribe&quot; loading=&quot;lazy&quot; src=&quot;/assets/old-block-2f490da4efe5f59094aa38f7ebf6b2334c8f4118d55d7f5369dd43a39711b5ad.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;annotation center&quot;&gt;Querying over 3yo block data on a public non-archive node&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;revm-powered-evm-tracing&quot;&gt;Revm-powered EVM tracing&lt;/h3&gt;

&lt;p&gt;By enabling &lt;code class=&quot;highlighter-rouge&quot;&gt;--trace [rpc|revm]&lt;/code&gt; flag you can query by more conditions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;query last 5 blocks for top transactions that paid over 0.002 ETH total (including coinbase bribe) transaction cost:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 5:latest -p 0 --real-tx-cost ge20000000000000000 --trace revm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href=&quot;https://etherscan.io/tx/0x06fed3f7dc71194fe3c2fd379ef1e8aaa850354454ea9dd526364a4e24853660&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sample matching tx&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Mucho bribe&quot; title=&quot;Mucho bribe&quot; loading=&quot;lazy&quot; src=&quot;/assets/big-bribe-tx-0b9870eb39916c625be5e50bf57bdceddf2e30e5deb393399195eda860b8c86a.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It shows how important it is to track coinbase transfers when analyzing tx costs. This tx paid only $0.33 in standard gas price but landed at the top on the block by bribing the validator over $11k.&lt;/p&gt;

&lt;p&gt;You can also filter by real per gas price:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 5:latest -p 0:5 --real-gas-price ge10000000000 --trace revm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It will display all the txs that paid more than 5 gwei of gas price per unit.&lt;/p&gt;

&lt;p&gt;Another example of EVM tracing powered query:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;find txs that changed storage slots of the &lt;a href=&quot;https://etherscan.io/address/0xba12222222228d8ba445958a75a0704d566bf2c8&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Balancer vault contract&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 10:latest --touching 0xba12222222228d8ba445958a75a0704d566bf2c8 --trace rpc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It uses EVM state tracing to match contracts whose state was affected by transactions. It’s more general purpose than event-based filtering.&lt;/p&gt;

&lt;h3 id=&quot;how-to-analyze-mev-sandwich-attacks&quot;&gt;How to analyze MEV sandwich attacks?&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog tx&lt;/code&gt; method let’s you investigate a single transaction. A grep-like querying API allows to check what was the surrounding (potential sandwich) context. For example, after finding this &lt;a href=&quot;https://etherscan.io/tx/0x2aaf037240be70f05468c2c6d2e3fe948aea11783c3f90a2237c0c6978b6667b&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;jared tx in position 1&lt;/a&gt; you can easily check what was the &lt;a href=&quot;https://etherscan.io/tx/0x2998b7b1ec356d27e2895c07fba852e002995d95ac806dd9a031d7b3af5f537f&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;transaction that it backrunned&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog tx -b 1 0x2aaf037240be70f05468c2c6d2e3fe948aea11783c3f90a2237c0c6978b6667b&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In this particular example, it looks like it’s an rETH token oracle update.&lt;/p&gt;

&lt;p&gt;If you want to investigate sandwich attacks by jared you can start from the following query:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog search -b 20:latest -p 2 --from jaredfromsubway.eth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The lower &lt;em&gt;“bun”&lt;/em&gt; of a sandwich is usually in the 2nd position in a block. After grabbing the tx hash you can see preceeding txs like this:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog tx -B 2 0x29c93f92350f3dac6e729ccc5139345128b168ac15c0212854d171b8606a6358&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It looks like &lt;a href=&quot;https://etherscan.io/tx/0xab276edbc5d7d5a90e434109bbf3c73850d6128f41622750971c974d5361a4d5&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this poor swap tx&lt;/a&gt; did indeed fall prey to jared.&lt;/p&gt;

&lt;p&gt;There’s also a live monitoring mode supporting the same query methods as &lt;code class=&quot;highlighter-rouge&quot;&gt;filter&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog watch -p 0:5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I invite you to &lt;a href=&quot;https://github.com/pawurb/mevlog-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;check out the repo&lt;/a&gt; for a full breakdown of the currently available filtering methods. You can start using the CLI by running:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/pawurb/mevlog-rs
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;mevlog-rs
cargo &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or install from the &lt;a href=&quot;https://crates.io/crates/mevlog&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;crates.io&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mevlog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One installed you can run:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog watch &lt;span class=&quot;nt&quot;&gt;--rpc-url&lt;/span&gt; https://eth.merkle.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I could not find a CLI tool with similar features. But please let me know in the comments if I’m reinventing the wheel.&lt;/p&gt;

&lt;p&gt;Read on if you’re interested in the performance, usability, and EVM tracing-related challenges I’ve faced when building the MVP.&lt;/p&gt;

&lt;h2 id=&quot;sqlite-and-ens-integration&quot;&gt;SQLite and ENS integration&lt;/h2&gt;

&lt;p&gt;Mevlog uses a local &lt;a href=&quot;https://openchain.xyz/signatures&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;openchain.xyz signatures database&lt;/a&gt;. Compressed (~120MB) SQLite database file with ~800k event and over 2 million method signatures is downloaded on the first run. It allows to display human-readable info about emitted events and root method calls.&lt;/p&gt;

&lt;p&gt;This is my first time using SQLite with this amount of data in a single table. It works seamlessly, fetching method signatures from the table with over 2 million rows in &lt;code class=&quot;highlighter-rouge&quot;&gt;~50-200µs&lt;/code&gt; (microseconds) per query. That’s 5-20 queries per millisecond! I’ve never seen similar numbers when working with PostgreSQL. Relying on locally cached SQLite enables the event and method name regexp search without using Etherscan API calls or parsing ABI files. Uncompressed database file currently weights ~260MB.&lt;/p&gt;

&lt;p&gt;ENS integration was another interesting challenge. Initial implementation did 3 RPC calls to get the hashed node address, resolver, and eventually the name, causing ~400ms overhead per name lookup. For ~100 tx per block that’s 40s overhead. By moving node hash calculation off-chain and aggregating two remaining calls &lt;a href=&quot;https://etherscan.io/address/0xc69c0eb9ec6e71e97c1ed25212d203ad5010d8b2#code&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;with a custom contract&lt;/a&gt; I’ve managed to reduce synchronous overhead to ~100ms.&lt;/p&gt;

&lt;p&gt;Another optimization was delegating ENS resolutions to the &lt;a href=&quot;https://github.com/zkat/cacache-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cacache-rs file cache&lt;/a&gt; in the background thread. With these changes displaying ENS names has virtually no performance overhead.&lt;/p&gt;

&lt;h2 id=&quot;optimal-evm-tracing-strategies-with-revm-and-rpc&quot;&gt;Optimal EVM tracing strategies with Revm and RPC&lt;/h2&gt;

&lt;p&gt;However the most significant implementation challenge was adding the EVM tracing. We need to know how much ETH is bribed to the &lt;code class=&quot;highlighter-rouge&quot;&gt;coinbase&lt;/code&gt; to calculate the real gas price. To do it, we need to extract an internal subtransaction trace with empty calldata and &lt;code class=&quot;highlighter-rouge&quot;&gt;to&lt;/code&gt; set at the current &lt;code class=&quot;highlighter-rouge&quot;&gt;coinbase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This implementation is relatively simple for nodes that support the &lt;code class=&quot;highlighter-rouge&quot;&gt;debug_traceTransaction&lt;/code&gt; RPC method. It’s possible to recursively extract subtraces output from the &lt;a href=&quot;https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;built-in &lt;code class=&quot;highlighter-rouge&quot;&gt;{tracer: 'callTracer'}&lt;/code&gt;&lt;/a&gt; until there’s a match.  But I wanted mevlog to also work with free nodes that don’t expose the debug APIs.&lt;/p&gt;

&lt;p&gt;From my previous project, I knew that Revm supports tracing capabilities via the &lt;a href=&quot;https://github.com/paradigmxyz/revm-inspectors&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;revm-inspectors crate&lt;/a&gt;. Actually, that’s what &lt;a href=&quot;https://book.getfoundry.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;foundry&lt;/a&gt; uses under the hood for &lt;code class=&quot;highlighter-rouge&quot;&gt;cast run 0x123...&lt;/code&gt; to print these detailed call traces:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;cast run transaction traces&quot; title=&quot;cast run transaction traces&quot; loading=&quot;lazy&quot; src=&quot;/assets/cast-run-traces-1ae7c465e562549494bdd0f858789f8b097c6ca7819f2a02a96b03118db9a1ed.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;Executing previous transactions from the block&quot;&lt;/code&gt; output is critical here. Contrary to &lt;code class=&quot;highlighter-rouge&quot;&gt;debug_traceTransaction&lt;/code&gt;, Revm does not know the exact state of transactions further down the block, so to trace tx at slot 10 it has to first execute and commit the 9 preceeding txs. There’s a &lt;code class=&quot;highlighter-rouge&quot;&gt;--quick&lt;/code&gt; flag that ignores previous state changes, but using it would fail most of the transactions.&lt;/p&gt;

&lt;p&gt;I did not manage to wrap my head around the low-level Revm forking implementation of &lt;code class=&quot;highlighter-rouge&quot;&gt;cast run&lt;/code&gt;. Instead I’ve used kind of a shortcut. To trace transactions from block &lt;code class=&quot;highlighter-rouge&quot;&gt;N&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; &lt;a href=&quot;https://github.com/pawurb/mevlog-rs/blob/06322c813354041063c2099b070f05f4c6d531a1/src/misc/revm_tracing.rs#L38C14-L38C23&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;spawns the Anvil process to fork off the &lt;code class=&quot;highlighter-rouge&quot;&gt;N-1&lt;/code&gt; block&lt;/a&gt; and uses Anvil provider with the Revm &lt;code class=&quot;highlighter-rouge&quot;&gt;SharedBackend&lt;/code&gt;. Later, it passes the block &lt;code class=&quot;highlighter-rouge&quot;&gt;N&lt;/code&gt; context to the Revm simulation engine and sequentially executes and traces all the transactions.&lt;/p&gt;

&lt;p&gt;Let’s compare the performance of tracing the same transaction in the 10th slot with &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;cast run&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;mevlog tx 0x2884d487f3fa0c5d983abb415a25c3be0982b50d4107bdabead1498b652f38ad &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; revm
&lt;span class=&quot;c&quot;&gt;# 0.10s user 0.05s system 1% cpu 9.462 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;cast run 0x2884d487f3fa0c5d983abb415a25c3be0982b50d4107bdabead1498b652f38ad
&lt;span class=&quot;c&quot;&gt;# 0.57s user 0.09s system 7% cpu 8.213 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve measured it against an external RPC endpoint, so any difference in performance is probably due to networking conditions. It indicates that despite the &lt;code class=&quot;highlighter-rouge&quot;&gt;N-1&lt;/code&gt; Anvil forking shenanigans &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog --trace revm&lt;/code&gt; seems to be working correctly.&lt;/p&gt;

&lt;p&gt;It’s worth noting that both commands rely on the &lt;a href=&quot;https://github.com/foundry-rs/foundry-fork-db&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;foundry-fork-db&lt;/a&gt; crate. So subsequent traces against the same transactions will be much faster. In this case, it is down to ~1s. &lt;code class=&quot;highlighter-rouge&quot;&gt;foundry-fork-db&lt;/code&gt; caches all the relevant data slots in a local file cache. Let’s see how much data is needed to execute the previously mentioned transaction:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;RUST_LOG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;debug cast run 0x2884d487f3fa0c5d983abb415a25c3be0982b50d4107bdabead1498b652f38ad
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Based on the cast logs output, without cached data this command triggers over 200 RPC calls to the origin. That’s the price for running local Revm simulations because all the relevant storage slots must first be downloaded.&lt;/p&gt;

&lt;p&gt;Mevlog also supports the &lt;code class=&quot;highlighter-rouge&quot;&gt;--trace rpc&lt;/code&gt; mode that relies on the &lt;code class=&quot;highlighter-rouge&quot;&gt;debug_traceTransaction&lt;/code&gt; method. It’s not available on free RPC endpoints, but it can trace transactions over 3rd party RPC in less than a second without caching. Revm-based simulations are great  to compare different scenarios on the same storage dataset. But for one-off simulations (and especially for txs further down the block), RPC tracing will usually deliver a better performance.&lt;/p&gt;

&lt;p&gt;For example, the first Revm tracing of top 50 txs from a sample block:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 22053318 &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 0:50 &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; revm  
&lt;span class=&quot;c&quot;&gt;# 0.75s user 0.43s system 4% cpu 24.437 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;second (with cache):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 22053318 &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 0:50 &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; revm  
&lt;span class=&quot;c&quot;&gt;# 0.21s user 0.08s system 10% cpu 2.618 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and using RPC tracing:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 22053318 &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 0:50 &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; rpc  
&lt;span class=&quot;c&quot;&gt;# 0.10s user 0.09s system 2% cpu 8.260 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and no tracing (i.e. no access to coinbase bribe data):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 22053318 &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 0:50 
&lt;span class=&quot;c&quot;&gt;# 0.06s user 0.05s system 6% cpu 1.749 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;future-plans&quot;&gt;Future plans&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; is currently in an MVP phase, and many implementation details are far from optimal. Let’s see how long it takes to query the last 1000 blocks for matching event names on a local Geth node:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 1000:latest &lt;span class=&quot;nt&quot;&gt;--event&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Sync(uint112,uin112)&quot;&lt;/span&gt; 
&lt;span class=&quot;c&quot;&gt;# real    2m55.397s&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# user    1m28.724s&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# sys     1m12.775s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and last 250 blocks with &lt;code class=&quot;highlighter-rouge&quot;&gt;revm&lt;/code&gt; tracing:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;mevlog search &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 250:latest &lt;span class=&quot;nt&quot;&gt;--event&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Sync(uint112,uin112)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; revm
&lt;span class=&quot;c&quot;&gt;# real    3m24.163s&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# user    0m46.712s&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# sys     0m26.668s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Rookie numbers&quot; title=&quot;Rookie numbers&quot; loading=&quot;lazy&quot; src=&quot;/assets/bump-9521a7f2ad12f4b2706c2c96f735d4ee205ed45c4ad4b39c26fbeef4c560aaf4.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In its current state, &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; is only useful for small block ranges querying and live monitoring. For example, the current &lt;code class=&quot;highlighter-rouge&quot;&gt;search&lt;/code&gt; implementation is terribly inneficient fetching logs and transactions data block by block without any batching or concurrency.&lt;/p&gt;

&lt;p&gt;For future releases I’m considering to piggyback on the &lt;a href=&quot;https://github.com/paradigmxyz/cryo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cryo crate&lt;/a&gt; for data extraction. Let’s compare the numbers for fetching the last 1000 blocks data:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cryo blocks logs transactions &lt;span class=&quot;nt&quot;&gt;--blocks&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1000&lt;/span&gt;:latest
&lt;span class=&quot;c&quot;&gt;# collection summary&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# ──────────────────&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - total duration: 6.556 seconds&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - total chunks: 2&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - chunks errored:   0 / 2 (0.0%)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - chunks skipped:   0 / 2 (0.0%)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - chunks collected: 2 / 2 (100.0%)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - blocks collected: 1,000&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - blocks per second:     152.5&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - blocks per minute:   9,151.0&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - blocks per hour:   549,062.7&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#     - blocks per day: 13,177,503.7&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - rows written: 552,669&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s a decent &lt;code class=&quot;highlighter-rouge&quot;&gt;~98%&lt;/code&gt; speedup. With this setup, &lt;code class=&quot;highlighter-rouge&quot;&gt;mevlog&lt;/code&gt; could provide a performant, MEV-focused querying API on top of &lt;code class=&quot;highlighter-rouge&quot;&gt;cryo&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/mevlog-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mevlog-rs&lt;/a&gt; is in the beta stage so any feedback would be appreciated. I’m just starting to use it for my daily searching tasks. But it already helped me discover and execute a long-tail MEV strategy on the Mainnet. Next week I’m planning to release a detailed post describing the process, so stay tuned for updates.&lt;/p&gt;
</description>
        <pubDate>Mon, 17 Mar 2025 06:29:37 +0100</pubDate>
        <link>https://pawelurbanek.com/long-tail-mev-revm</link>
        <guid isPermaLink="true">https://pawelurbanek.com/long-tail-mev-revm</guid>
      </item>
    
      <item>
        <title>Lessons Learned Migrating my SAAS to Rails 8</title>
        <description>&lt;p&gt;My side project &lt;a href=&quot;https://abot.app&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Abot for Slack&lt;/a&gt;, has been around for ~7 years, i.e., since Rails &lt;em&gt;5.1&lt;/em&gt;. It’s now yielding passive income while running on an almost complete autopilot. But I’m still keeping its dependencies up-to-date. The recent migration to Rails 8 was arguably the most impactful in the app’s lifetime. In this blog post, I’ll describe features introduced in the newest version of the framework and how they affected my project.&lt;/p&gt;

&lt;h2 id=&quot;kamal-vs-dokku&quot;&gt;Kamal vs Dokku&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;TLDR&lt;/em&gt; initial impressions migrating Abot from &lt;a href=&quot;https://dokku.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dokku&lt;/a&gt; to &lt;a href=&quot;https://kamal-deploy.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kamal&lt;/a&gt;: it’s better, apart from one major flaw.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/profitable-slack-bot-rails&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Abot started as a weekend project&lt;/a&gt; deployed to Heroku free dynos. Later, I migrated it to AWS EC2 with the RDS PostgreSQL database using &lt;a href=&quot;https://dokku.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dokku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Initial dokku setup&quot; title=&quot;Initial dokku setup&quot; src=&quot;/assets/dokku-gitlog-635bdb61b04ba1b4bb268dfcadb071cbfa963b767601abbba265dffc2ec7d7d6.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;The initial Dokku setup long long time ago&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Dokku requires a bit more dev-ops expertise than PAAS solutions, but after the initial setup, it offers a workflow almost identical to Heroku. I’ve used Dokku for over 4 years, and it provided a seamless deployment experience with very few glitches along the way. This is one of the OS projects that I &lt;a href=&quot;https://opencollective.com/dokku&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;regularly donate to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But the times are changing, and &lt;a href=&quot;https://kamal-deploy.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Kamal&lt;/a&gt; is a fancy new tool that everyone is talking about.&lt;/p&gt;

&lt;p&gt;Kamal’s great advantage is a declarative one-file config, providing an instant overview of the current state of the project. With Dokku, similar insight is scattered across several commands.&lt;/p&gt;

&lt;p&gt;Another benefit is seamless deployment setup. Just point Kamal to a barebones VPS, and everything gets deployed via a single &lt;code class=&quot;highlighter-rouge&quot;&gt;kamal setup&lt;/code&gt; command. Dokku requires manual installation on the server unless configured with a custom Ansible playbook or similar tool.&lt;/p&gt;

&lt;p&gt;Yet another win for Kamal is support for multi-server architecture. With Dokku, you’re limited to single server deployments, so you have to fit all the services, including databases, caches, etc., on a single VPS.&lt;/p&gt;

&lt;p&gt;One thing that’s missing from Kamal is the built-in Nginx integration. Dokku provides a customizable Nginx template allowing for fine-tuned configuration of headers, SSL, compression, etc. With Kamal, you’d need to manually configure Nginx in front of the proxy to achieve the same effect. Apparently, the &lt;a href=&quot;https://github.com/basecamp/thruster&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;thruster gem&lt;/a&gt; can provide similar features as Nginx, but I have not explored it yet. In the end I’ve reimplemented each of the Nginx config tweaks directly in Rails or Cloudflare.&lt;/p&gt;

&lt;p&gt;But here comes the previously mentioned &lt;em&gt;major flaw&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;kamal-registry-based-deployments&quot;&gt;Kamal registry-based deployments&lt;/h3&gt;

&lt;p&gt;Dokku uses a &lt;em&gt;git-based&lt;/em&gt; deployment flow, i.e. each &lt;code class=&quot;highlighter-rouge&quot;&gt;git push&lt;/code&gt; triggers a new docker image build combining the base image with app-specific &lt;code class=&quot;highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;. A new docker image is built directly on the target VPS and kept locally.&lt;/p&gt;

&lt;p&gt;Kamal requires a nonoptional &lt;code class=&quot;highlighter-rouge&quot;&gt;registry.server&lt;/code&gt; (defaults to &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;hub.docker.com&quot;&lt;/code&gt;), &lt;code class=&quot;highlighter-rouge&quot;&gt;registry.username&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;registry.password&lt;/code&gt; config values. These are credentials for one of the commercial (DockerHub, GitHub, Digital Ocean) or custom docker registry service. It’s possible to &lt;a href=&quot;https://distribution.github.io/distribution/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;spin up your own registry&lt;/a&gt;, but it’s cumbersome. Built images are pushed to the registry and later downloaded by a target host.&lt;/p&gt;

&lt;p&gt;An issue is that the default Kamal configuration exposes Rails application source code in a public image hosted on Docker Hub. In theory, it could leak nonpublic projects if someone followed the Kamal tutorial without analyzing each step. I wish the Kamal team was more explicit about this behavior in the docs and learning materials.&lt;/p&gt;

&lt;p&gt;A way to prevent exposing project source code is manually creating the private image up-front or changing the default visibility &lt;a href=&quot;https://hub.docker.com/repository-settings/default-privacy&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;in DockerHub settings&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Docker hub privacy settings&quot; title=&quot;Docker hub privacy settings&quot; src=&quot;/assets/docker-hub-privacy-c6275aaae649ec547ece349dd21417e9857a73180024f415dd95fdd3bd2f2ce9.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;center annotation&quot;&gt;DockerHub image privacy settings&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;A free DockerHub account can host only a single private image. Even &lt;a href=&quot;https://www.docker.com/pricing/#monthly&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the $11/month plan&lt;/a&gt; offers only 5GB storage, so with image sizes at ~1GB it might not be enough. A $16/month offer with 50GB space for private images could be necessary for many users. It might not seem expensive. But a choice between exposing source code or incurring monthly costs could be discouraging for someone just exploring the Rails stack.&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href=&quot;https://github.com/basecamp/kamal/discussions/510#discussioncomment-11291136&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;there are rumors&lt;/a&gt; about the Kamal team working on a way to deploy containers, without relying on 3rd party commercial services. In my opinion, this change would significantly enhance Kamal’s appeal.&lt;/p&gt;

&lt;h2 id=&quot;goodbye-redis&quot;&gt;Goodbye Redis&lt;/h2&gt;

&lt;p&gt;Since I started working in Rails around a decade ago, the &lt;em&gt;Redis + Sidekiq&lt;/em&gt; combo was the de-facto standard for most projects. Rails 8 challenges this status quo by defaulting to the &lt;a href=&quot;https://github.com/rails/solid_queue&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;solid_queue gem&lt;/a&gt; for background processing. Solid Queue uses SQL database storage instead of Redis. Together with &lt;a href=&quot;https://github.com/rails/mission_control-jobs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mission_control-jobs UI&lt;/a&gt; it’s a drop-in replacement for Sidekiq. It means that we can now get rid of another process and dependency to maintain.&lt;/p&gt;

&lt;p&gt;Caching was another area where I’ve seen widespread use of Redis. On the side node, reusing the same Redis instance for both Sidekiq and caching has always been a fun source of sneaky bugs when both processes’ data interfere despite the namespaces…&lt;/p&gt;

&lt;p&gt;(╯°□°)╯︵ ┻━┻&lt;/p&gt;

&lt;p&gt;Past traumas aside, with the introduction of &lt;a href=&quot;https://github.com/rails/solid_cache&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;solid_cache gem&lt;/a&gt; modern Rails apps can remove Redis from their stack, simplifying the infrastructure. While conducting &lt;a href=&quot;/optimize-rails-performance&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my Rails performance audits&lt;/a&gt;, I’ve seen projects where Redis was responsible for a significant portion of monthly infra costs. Migrating to SQL-based solutions can result in a major cost reduction for many Rails projects. Also, as per Rails 8 marketing: &lt;em&gt;“you can now cache weeks instead of hours worth of data”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of monthly AWS cost for &lt;a href=&quot;https://aws.amazon.com/elasticache/pricing/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ElastiCache Redis&lt;/a&gt; with a specified RAM vs &lt;a href=&quot;https://aws.amazon.com/rds/pricing/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;RDS database&lt;/a&gt; disk space:&lt;/p&gt;

&lt;table style=&quot;width:100%&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;Size&lt;/th&gt;
    &lt;th&gt;Redis RAM&lt;/th&gt;
    &lt;th&gt;RDS disk space&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;1.3GB&lt;/td&gt;
    &lt;td&gt;~$23&lt;/td&gt;
    &lt;td&gt;~$0.16&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;6.3GB&lt;/td&gt;
    &lt;td&gt;~$110&lt;/td&gt;
    &lt;td&gt;~$0.78&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;52GB&lt;/td&gt;
    &lt;td&gt;~$900&lt;/td&gt;
    &lt;td&gt;~$6.5&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;210GB&lt;/td&gt;
    &lt;td&gt;~$3300&lt;/td&gt;
    &lt;td&gt;~$26&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;annotation center&quot;&gt;Size values were chosen to find matching ElastiCache instance&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;RDS incurs additional db instance costs, but on a larger, scale they will be insignificant. Cache-specific databases execute only simple queries over well-indexed tables, i.e. no joins or &lt;em&gt;seq scans&lt;/em&gt;. That’s why caching database CPU/RAM can usually be much smaller than primary. But, workload and traffic patterns differ for each project, so please run your benchmarks before making any production infra changes. More cost-aware projects can also squeeze both databases on a single RDS instance.&lt;/p&gt;

&lt;p&gt;I did calculations for ElastiCache, so you could slightly reduce the cost by running Redis on EC2 instances. But RAM will always be an order of magnitude more expensive than disk space.&lt;/p&gt;

&lt;p&gt;I’ve seen comments that migrating to &lt;em&gt;“non-memory”&lt;/em&gt; cache storage will result in a performance drop. So let’s see the numbers in action.&lt;/p&gt;

&lt;h3 id=&quot;measuring-non-memory-caching-performance&quot;&gt;Measuring non-memory caching performance&lt;/h3&gt;

&lt;p&gt;I’ve run the following benchmark script against different Rails cache stores:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sample.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cache&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Please take this benchmark with a grain of salt. It’s a straightforward script that does not simulate how the cache store behaves under a constant load or when there’s more data.&lt;/p&gt;

&lt;p&gt;It distributes read and write operations in 80/20 relation. Here’s the result for different store types:&lt;/p&gt;

&lt;table style=&quot;width:100%&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;Store&lt;/th&gt;
    &lt;th&gt;Total time&lt;/th&gt;
    &lt;th&gt;Avg. time&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;FileStore EBS&lt;/td&gt;
    &lt;td&gt;20.0s&lt;/td&gt;
    &lt;td&gt;~4ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Redis/Memcache&lt;/td&gt;
    &lt;td&gt;20.1s&lt;/td&gt;
    &lt;td&gt;~4ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;FileStore EFS&lt;/td&gt;
    &lt;td&gt;22.4s&lt;/td&gt;
    &lt;td&gt;~4.5ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;SolidCache SQLite&lt;/td&gt;
    &lt;td&gt;24.4s&lt;/td&gt;
    &lt;td&gt;~4.8ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;SolidCache PG&lt;/td&gt;
    &lt;td&gt;27.05s&lt;/td&gt;
    &lt;td&gt;~5.4ms&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;For me, the main takeaway from the above table is that in the context of web applications, the caching layer is &lt;em&gt;fast enough&lt;/em&gt;, regardless of the underlying store type. The amount of data that can be cached is vastly more important than sub-millisecond performance improvements.&lt;/p&gt;

&lt;p&gt;Although the &lt;em&gt;in-memory&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Redis/Memcache&lt;/code&gt; should be the fastest, they are on par with the &lt;code class=&quot;highlighter-rouge&quot;&gt;File Store EBS&lt;/code&gt;. The reason is probably the lack of networking overhead for local file storage.&lt;/p&gt;

&lt;p&gt;Another interesting observation is that the &lt;code class=&quot;highlighter-rouge&quot;&gt;FileStore&lt;/code&gt; is consistently &lt;em&gt;~10-20%&lt;/em&gt; faster than &lt;code class=&quot;highlighter-rouge&quot;&gt;SolidCache SQLite&lt;/code&gt;. It makes sense as SQLite is a layer of abstraction on top of the file system, so it incurs additional overhead. I’m not 100% sure if this metric will hold true for systems under production-like load. But if you want to squeeze out a few more microseconds then the the good ol’ &lt;code class=&quot;highlighter-rouge&quot;&gt;ActiveSupport::Cache::FileStore&lt;/code&gt; instead of SQLite could be a better choice.&lt;/p&gt;

&lt;p&gt;Please remember that &lt;a href=&quot;https://aws.amazon.com/ebs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AWS EBS&lt;/a&gt; (even &lt;code class=&quot;highlighter-rouge&quot;&gt;gp3&lt;/code&gt;) volumes are notoriously slow. When running the benchmark locally on an MBP, I’m observing ~3x better performance for &lt;code class=&quot;highlighter-rouge&quot;&gt;FileStore&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;SolidCache SQLite&lt;/code&gt;. So if you want to maximize the SQLite and cache performance, choosing a VPS provider with high-speed NVMe disks is probably worth it.&lt;/p&gt;

&lt;p&gt;While we’re at data stores, I would like to highlight the &lt;a href=&quot;https://aws.amazon.com/efs/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AWS EFS file system&lt;/a&gt;. It’s EBS-equivalent but can be shared between multiple EC2 instances. It’s ~10x more expensive than EBS, but using it instead of PostgreSQL would mean saving money on database instances. It could be an interesting choice for projects using SQLite with multi-server architecture or for leveraging a shared file-based caching layer. I’m currently working on a blog post covering more details on using EFS with Rails. You can &lt;a href=&quot;https://x.com/_pawurb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;follow&lt;/a&gt; or &lt;a class=&quot;link-grey&quot; href=&quot;https://eepurl.com/dhuFg5&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;subscribe&lt;/a&gt; to get notified when it’s live.&lt;/p&gt;

&lt;h3 id=&quot;using-a-custom-compressor-for-rails-cache&quot;&gt;Using a custom compressor for Rails cache&lt;/h3&gt;

&lt;p&gt;Another exciting feature introduced in Rails 8 is support for custom cache compressors. I’ve built the &lt;a href=&quot;https://github.com/pawurb/rails-brotli-cache&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rails-brotli-cache gem&lt;/a&gt; around this idea, so it’s awesome to see it upstream. The feature has not made it to the Rails guides yet, but hopefully, &lt;a href=&quot;https://github.com/rails/rails/pull/53036&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this PR&lt;/a&gt; will get merged sooner or later. As of now &lt;code class=&quot;highlighter-rouge&quot;&gt;compressor&lt;/code&gt; config is described only in &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ActiveSupport::Cache::Store&lt;/code&gt; docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve tested a few compression algorithms when working on rails-brotli-cache (&lt;a href=&quot;https://github.com/pawurb/rails-brotli-cache/blob/main/benchmarks/main.rb&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;benchmark&lt;/a&gt;). Choosing &lt;em&gt;“the best one”&lt;/em&gt; is a matter of tradeoffs between performance and compression ratio. But &lt;a href=&quot;https://github.com/SpringMT/zstd-ruby&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;zstd-ruby&lt;/a&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;level: 10&lt;/code&gt; config is a &lt;em&gt;quick &amp;amp; easy&lt;/em&gt; choice, offering speed and compression superior to both &lt;code class=&quot;highlighter-rouge&quot;&gt;gzip&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;brotli&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;lib/zstd_compressor.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;zstd-ruby&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ZSTDCompressor&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;deflate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Zstd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;level: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inflate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x78&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Zlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inflate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Zstd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decompress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;config/production.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache_store&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:solid_cache_store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;compressor: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ZSTDCompressor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Applying this config should improve caching read/write performance by &lt;em&gt;~30%&lt;/em&gt; and compression ratio by &lt;em&gt;~20%&lt;/em&gt;. If you want to use this feature make sure to include:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load_defaults&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;7.1&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;in &lt;code class=&quot;highlighter-rouge&quot;&gt;config/application.rb&lt;/code&gt;. This change is backward compatible, so you can start using the new algorithm without clearing the current cache contents.&lt;/p&gt;

&lt;h2 id=&quot;sqlite-vs-postgresql&quot;&gt;SQLite vs PostgreSQL&lt;/h2&gt;

&lt;p&gt;In Rails 8 SQLite becomes a &lt;em&gt;first-class&lt;/em&gt; citizen. This &lt;a href=&quot;https://www.youtube.com/watch?v=wFUy120Fts8&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;talk by Stephen Margheim&lt;/a&gt; provides an in-depth look at the changes introduced and explains how SQLite’s support and performance improved.&lt;/p&gt;

&lt;p&gt;I briefly considered switching Abot’s primary database to SQLite instead of PostgreSQL. I’d love to cut the monthly RDS costs, but I’m not comfortable keeping the business-critical data on the server. &lt;em&gt;“cattle not pets”&lt;/em&gt; they said. I’m used to regularly nuking and reprovisioning EC2 instances, so the chances of accidentally wiping production data skyrocket with SQLite.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/benbjohnson/litestream&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;litestream&lt;/a&gt; is an awesome tool for continuously backing up SQLite. But I’d rather not bet the project’s existence on it working seamlessly.&lt;/p&gt;

&lt;p&gt;Another disadvantage of SQLite vs. PG is the lack of advanced monitoring. Tools like &lt;a href=&quot;https://github.com/ankane/pghero&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PgHero&lt;/a&gt;, (&lt;em&gt;shameless plug alert!&lt;/em&gt;) &lt;a href=&quot;https://github.com/pawurb/rails-pg-extras&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rails-pg-extras&lt;/a&gt;, or &lt;a href=&quot;https://github.com/pawurb/pg-locks-monitor&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pg-locks-monitor&lt;/a&gt; provide detailed insights into what’s going under the hood in PostgreSQL.&lt;/p&gt;

&lt;p&gt;With SQLite going mainstream in the Rails world, hopefully, open source will soon catch up, and we’ll have more ways to analyze it. I’ve started building &lt;a href=&quot;https://github.com/pawurb/rails-sqlite-extras&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rails-sqlite-extras&lt;/a&gt;. But I don’t have much SQLite exp, so ideas on how to improve it are welcome.&lt;/p&gt;

&lt;p&gt;For now, I’m happy to keep Abot’s solid queue and cache databases on SQLite and primary on PostgreSQL.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Rails 8 is a decent step forward. It makes things cheaper, faster, more productive. There are some drawbacks, like the Kamal mandatory registry, but I hope it will be sorted out soon.&lt;/p&gt;
</description>
        <pubDate>Tue, 14 Jan 2025 08:29:37 +0100</pubDate>
        <link>https://pawelurbanek.com/rails-8-features</link>
        <guid isPermaLink="true">https://pawelurbanek.com/rails-8-features</guid>
      </item>
    
      <item>
        <title>How to Send deadbeef Transactions with Alloy</title>
        <description>&lt;p&gt;&lt;em&gt;0x0000000&lt;/em&gt; accounts are so last season. Now it’s all about vanity tx prefixes. In this blog post, we will learn how to impress your Etherscan followers and fry a few CPU cores along the way.&lt;/p&gt;

&lt;h2 id=&quot;vanity-txs&quot;&gt;Vanity txs&lt;/h2&gt;

&lt;p&gt;It all started from &lt;a href=&quot;https://x.com/bertcmiller/status/1834218771432587623&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this X thread&lt;/a&gt;. Apparently, there’s a &lt;code class=&quot;highlighter-rouge&quot;&gt;bigbrainchad.eth&lt;/code&gt; bot, that extracts the MEV in style. All his txs hashes start with the &lt;code class=&quot;highlighter-rouge&quot;&gt;0xbeef&lt;/code&gt; prefix:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;bigbrainchad.eth txs prefix&quot; title=&quot;bigbrainchad.eth txs prefix&quot; src=&quot;/assets/bigchad_txs-8ec996dd8bc7a017917ce1fd40ced76f2657e018e3c18ff0097b3ae92ebb9881.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I immediately got jealous and also wanted to send a few cool-looking txs. Hence the birth of &lt;a href=&quot;https://github.com/pawurb/alloy-deadbeef&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;alloy-deadbeef&lt;/a&gt; lib, and soon &lt;a href=&quot;https://etherscan.io/tx/0xdeadbeef380c35dab8477b4cc3eed8c665f334e9e11fe9698281dbda0b9829c6&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my vanity tx&lt;/a&gt; landed on the mainnet:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;0xdeadbeef on etherscan&quot; title=&quot;0xdeadbeef on etherscan&quot; src=&quot;/assets/deadbeef-tx-etherscan-1ffe5fc415aa2737eb40a878a537935c64acf82ccd85d3e79f2580f91bb728d6.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Read on to learn about the lib’s implementation details and performance overhead of sending such fancy txs.&lt;/p&gt;

&lt;h2 id=&quot;how-to-brute-force-eth-tx-prefix&quot;&gt;How to brute-force ETH tx prefix?&lt;/h2&gt;

&lt;p&gt;You can use &lt;a href=&quot;https://github.com/pawurb/alloy-deadbeef&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;alloy-deadbeef&lt;/a&gt; as an &lt;a href=&quot;https://alloy.rs/building-with-alloy/understanding-fillers.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;alloy filler&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ProviderBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.filler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;DeadbeefFiller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;beef&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.wallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;.on_http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All the txs sent from this provider will land with a &lt;code class=&quot;highlighter-rouge&quot;&gt;0xbeef&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;Or generate a tx object that will commit to the chain with the desired prefix (&lt;em&gt;unless you change any of its data!&lt;/em&gt;):&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransactionRequest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deadbeef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DeadbeefFiller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;beef&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefixed_tx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deadbeef&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.prefixed_tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The lib brute forces a matching hash prefix, by incrementing the transaction &lt;code class=&quot;highlighter-rouge&quot;&gt;gas_limit&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; attribute. More details on that later.&lt;/p&gt;

&lt;p&gt;We’re working with &lt;code class=&quot;highlighter-rouge&quot;&gt;16^n&lt;/code&gt; complexity here. Even Rust takes a while to crunch these numbers. To optimize performance, the lib is maxing out parallelism by spanning &lt;code class=&quot;highlighter-rouge&quot;&gt;tokio&lt;/code&gt; task for each CPU available. The core loop looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/pawurb/alloy-deadbeef/blob/9b52f9268c498e9593e72f84c400cdc0b03f1db8/src/lib.rs#L233&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;src/lib.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_capacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;U256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;select!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;biased&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.recv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;future&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.value&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap_or_default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;U256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tx_hash_for_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{:x}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash_str&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.starts_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; - &lt;span class=&quot;n&quot;&gt;starting_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_iters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_cores&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nd&quot;&gt;info!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Found matching tx hash: {tx_hash} after ~{total_iters} iterations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tx_hash_for_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TransactionRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EthereumWallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;U256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FixedBytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx_envelope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tx_envelope&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.encode_2718&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;keccak256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tx_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tx hash is generated by running &lt;code class=&quot;highlighter-rouge&quot;&gt;keccak256&lt;/code&gt; on a signed transaction body, which is why we need to provide an &lt;code class=&quot;highlighter-rouge&quot;&gt;EthereumWallet&lt;/code&gt; object. We use a &lt;code class=&quot;highlighter-rouge&quot;&gt;select!&lt;/code&gt; macro to kill the remaining loops when the correct prefix is found.&lt;/p&gt;

&lt;p&gt;Initially, I’ve used &lt;code class=&quot;highlighter-rouge&quot;&gt;Arc&amp;lt;Mutex&amp;lt;bool&amp;gt;&amp;gt;&lt;/code&gt; like this:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_done&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But multiple threads competing for the mutex lock on each loop did wreck the performance. It was ~6x slower than &lt;a href=&quot;https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;tokio &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Mutex vs CancellationToken performance&quot; title=&quot;Mutex vs CancellationToken performance&quot; src=&quot;/assets/mutex_token_perf-9fcdfa26c624eb03f73bfbd72ef90bf195f13a7f5938d70b961054e6b9d16cc7.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the end, I’ve switched to &lt;a href=&quot;https://docs.rs/tokio/latest/tokio/sync/broadcast/fn.channel.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tokio::sync::broadcast::channel&lt;/code&gt;&lt;/a&gt;. Contrary to &lt;code class=&quot;highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;, it does not require a separate crate, and is just as fast.&lt;/p&gt;

&lt;p&gt;Another interesting discovery was that &lt;a href=&quot;https://docs.rs/tokio/latest/tokio/time/fn.sleep.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tokio::time::sleep(Duration::from_millis(0))&lt;/code&gt;&lt;/a&gt; has a delay of ~1ms. It killed the performance until I replaced it with &lt;a href=&quot;https://docs.rs/futures/latest/futures/future/fn.ready.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;futures::future::ready(())&lt;/code&gt;&lt;/a&gt;, which is instant.&lt;/p&gt;

&lt;h2 id=&quot;benchmarking-tx-prefix-generation&quot;&gt;Benchmarking tx prefix generation&lt;/h2&gt;

&lt;p&gt;I wanted to include a benchmark showing how long on avg. it takes to find a matching prefix. But I’ve found out it’s very random. &lt;code class=&quot;highlighter-rouge&quot;&gt;0xbeef&lt;/code&gt; can take anywhere between &lt;em&gt;200ms&lt;/em&gt; and &lt;em&gt;900ms&lt;/em&gt;, depending on the rest of the transaction attributes. Instead, I’ve AI-ed my way to finding how many iterations are needed to produce the desired prefix with 99% probability:&lt;/p&gt;

&lt;table style=&quot;width:100%&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;Length&lt;/th&gt;
    &lt;th&gt;Probability (1 in)&lt;/th&gt;
    &lt;th&gt;Iterations for ~99% certainty&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;1&lt;/td&gt;
    &lt;td&gt;1/16&lt;/td&gt;
    &lt;td&gt;~72&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;2&lt;/td&gt;
    &lt;td&gt;1/256&lt;/td&gt;
    &lt;td&gt;~1,180&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;3&lt;/td&gt;
    &lt;td&gt;1/4,096&lt;/td&gt;
    &lt;td&gt;~18,900&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;4&lt;/td&gt;
    &lt;td&gt;1/65,536&lt;/td&gt;
    &lt;td&gt;~302,000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;5&lt;/td&gt;
    &lt;td&gt;1/1,048,576&lt;/td&gt;
    &lt;td&gt;~4,830,000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;6&lt;/td&gt;
    &lt;td&gt;1/16,777,216&lt;/td&gt;
    &lt;td&gt;~77,900,000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;7&lt;/td&gt;
    &lt;td&gt;1/268,435,456&lt;/td&gt;
    &lt;td&gt;~1,240,000,000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;8&lt;/td&gt;
    &lt;td&gt;1/4,294,967,296&lt;/td&gt;
    &lt;td&gt;~19,800,000,000&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;9&lt;/td&gt;
    &lt;td&gt;1/68,719,476,736&lt;/td&gt;
    &lt;td&gt;~316,000,000,000&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;An interesting takeaway is that increasing the gas limit by up to 300,000 should not cause any issues with landing the transaction on-chain. The current implementation of &lt;em&gt;alloy-deadbeef&lt;/em&gt; iterates on &lt;code class=&quot;highlighter-rouge&quot;&gt;gas_limit&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt; for prefixes up to 4 characters. It means that we can also flex for &lt;a href=&quot;https://etherscan.io/tx/0xbeefe3fef705a6220161a8074998eeb3a423d60a2b866cf33749bef3a2103ab7&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;non-payable transactions&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Non-payable vanity tx&quot; title=&quot;Non-payable vanity tx&quot; src=&quot;/assets/nonpayable-beef-92a19992f733c53a6fbe3c0d02ada288937df31a58592674e01807a360a4e924.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can force a specific iteration mode like this:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deadbeef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DeadbeefFiller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;beef&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wallet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;deadbeef&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.set_iteration_mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;IterationMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s now analyze the maximum processing time. Assume we’re using a computer with 4 CPU cores and searching for 4-character prefix. Each loop has to process a maximum of &lt;em&gt;302,000 / 4&lt;/em&gt; i.e. &lt;em&gt;75,500&lt;/em&gt; iterations. So iteration starts from the following values for each of the loops:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;0 - 0&lt;/li&gt;
  &lt;li&gt;1 - 75500&lt;/li&gt;
  &lt;li&gt;2 - 151000&lt;/li&gt;
  &lt;li&gt;3 - 256500&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The table contains info on what’s the maximum processing time on my MBP M2 and beefed out 48 vCPUs Hetzner:&lt;/p&gt;

&lt;table style=&quot;width:100%&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;Length&lt;/th&gt;
    &lt;th&gt;MBP M2 12 CPUs&lt;/th&gt;
    &lt;th&gt;Hetzner VPS 48 vCPUs&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;1&lt;/td&gt;
    &lt;td&gt;547.58µs&lt;/td&gt;
    &lt;td&gt;397.69µs&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;2&lt;/td&gt;
    &lt;td&gt;5.81ms&lt;/td&gt;
    &lt;td&gt;3.18ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;3&lt;/td&gt;
    &lt;td&gt;123.86ms&lt;/td&gt;
    &lt;td&gt;45.38ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;4&lt;/td&gt;
    &lt;td&gt;1.82s&lt;/td&gt;
    &lt;td&gt;755.83ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;5&lt;/td&gt;
    &lt;td&gt;29.62s&lt;/td&gt;
    &lt;td&gt;12.08s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;6&lt;/td&gt;
    &lt;td&gt;~8 minutes*&lt;/td&gt;
    &lt;td&gt;~3 minutes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;7&lt;/td&gt;
    &lt;td&gt;~128 minutes*&lt;/td&gt;
    &lt;td&gt;~48 minutes*&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;8&lt;/td&gt;
    &lt;td&gt;~34 hours*&lt;/td&gt;
    &lt;td&gt;~13 hours*&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;9&lt;/td&gt;
    &lt;td&gt;~23 days*&lt;/td&gt;
    &lt;td&gt;~8 days*&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;* extrapolated&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Hetzner VPS maxed out&quot; title=&quot;Hetzner VPS maxed out&quot; src=&quot;/assets/hetzner_vps_max-0c07ab5addd89ce7bdbf49e3d0b414da4e66be06fd0f934edd7adb2661caea7d.png&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;annotation center&quot;&gt;Hetzner VPS maxed out crunching &lt;code&gt;0xdeadbeef&lt;/code&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;You can see that each additional prefix length increases processing time by ~16x, just as we’ve calculated. But these are the worst-case numbers.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;deadbeef tx logs&quot; title=&quot;deadbeef tx logs&quot; src=&quot;/assets/deadbeef-tx-sent-114652cabe6dedab7b7448f4a2be8a353204e8faf1a25bda559b904217c722a9.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://etherscan.io/tx/0xdeadbeef380c35dab8477b4cc3eed8c665f334e9e11fe9698281dbda0b9829c6&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;My &lt;code class=&quot;highlighter-rouge&quot;&gt;0xdeadbeef&lt;/code&gt; tx&lt;/a&gt; in practice took ~1.5 hour and slightly over 2 billion iterations to generate. According to my AI friends, the probability of this happening was at ~37%.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Our research shows that it is possible to efficiently use up to 4 character-long prefixes. It could be a simple way to intimidate your MEV competition and assert alpha bot dominance. &lt;a href=&quot;https://github.com/pawurb/alloy-deadbeef&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;alloy-deadbeef&lt;/a&gt; was a fun weekend project. I’d love to learn how to optimize it further, so please send over any suggestions/PRs.&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Dec 2024 09:59:17 +0100</pubDate>
        <link>https://pawelurbanek.com/alloy-deadbeef-vanity</link>
        <guid isPermaLink="true">https://pawelurbanek.com/alloy-deadbeef-vanity</guid>
      </item>
    
      <item>
        <title>How to use Cloudflare Workers proxy with Rust</title>
        <description>&lt;p&gt;Visits counter was a critical feature of every website just 20 years ago. In this tutorial, we will implement it with Rust Cloudflare Workers by adding persistence and dynamic behaviors to an otherwise static page. We will also discuss other practical use cases of CF workers edge proxy.&lt;/p&gt;

&lt;h2 id=&quot;static-blog-with-cf-edge-caching&quot;&gt;Static blog with CF edge caching&lt;/h2&gt;

&lt;div class=&quot;center&quot;&gt;
    &lt;p class=&quot;visits-counter&quot;&gt; 
    Visits: [VISITS_COUNT]
    &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This blog is a static &lt;a href=&quot;https://jekyllrb.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;JekyllRB&lt;/a&gt; website hosted on an EC2 behind an NGINX proxy. Additionally, it’s using a &lt;em&gt;Cache everything&lt;/em&gt; Cloudflare cache rule with the following header for each HTML page:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cache-control: public, max-age=3600
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Cloudflare cache everything rule&quot; title=&quot;Cloudflare cache everything rule&quot; src=&quot;/assets/cache-everything-rule-489e81c38c5ba1b7e92360beee9bcbe2df8d636739cc7d2dfb4557cad8a324ff.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can check it by running this cURL:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; https://pawelurbanek.com/cloudflare-workers-rust | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;cache

&lt;span class=&quot;c&quot;&gt;# cache-control: public, max-age=3600&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# cf-cache-status: HIT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cf-cache-status: HIT&lt;/code&gt; indicates that the request did not reach the origin server but downloaded the HTML page from the CF edge cache. It ensures the best loading performance because edge locations are always close to the readers.&lt;/p&gt;

&lt;p&gt;Each HTML page is cached for 10 minutes. I trigger the following HTTP call when releasing an update to clear the edge cache locations:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;s2&quot;&gt;&quot;https://api.cloudflare.com/client/v4/zones/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CLOUDFLARE_ZONE_ID&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/purge_cache&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;X-Auth-Email: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CLOUDFLARE_EMAIL&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;X-Auth-Key: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CLOUDFLARE_API_KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--data&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;purge_everything&quot;:true}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Still, if you refresh this page, the hit counter below will increase:&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
    &lt;p class=&quot;visits-counter&quot;&gt; 
    Visits: [VISITS_COUNT]
    &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;No JS involved. What kind of sorcery is this!?&lt;/p&gt;

&lt;h2 id=&quot;basic-cloudflare-worker-with-kv-store&quot;&gt;Basic Cloudflare worker with KV store&lt;/h2&gt;

&lt;p&gt;To implement this feature I’ve used &lt;a href=&quot;https://developers.cloudflare.com/workers/configuration/routing/routes/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Cloudflare Worker Routes&lt;/a&gt;. They work similarly to &lt;a href=&quot;https://aws.amazon.com/lambda/edge/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Lambde@Edge&lt;/a&gt;, i.e., workers sitting in front of the cache can reprocess a response before sending it to the client. Currently, it natively supports Javascript, Typescript, Python, and Rust, but &lt;a href=&quot;https://developers.cloudflare.com/workers/languages/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;more languages are supported by Wasm (Web Assembly)&lt;/a&gt;. In this tutorial we will use Rust.&lt;/p&gt;

&lt;p&gt;Unfortunately, Cloudflare currently supports creating only Javascript workers directly from the UI. So to get started you’ll need to &lt;a href=&quot;https://www.rust-lang.org/tools/install&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;install Rust locally&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next run these commands to install necessary dependencies:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;rustup target add wasm32-unknown-unknown
cargo &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;cargo-generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And initialize your project using &lt;a href=&quot;https://github.com/cloudflare/workers-rs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a template&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo generate cloudflare/workers-rs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Select a &lt;strong&gt;Hello world&lt;/strong&gt; template and name your project. It creates the following worker implementation:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;src/lib.rs&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[event(fetch)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;console_error_panic_hook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can test it by running:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx wrangler dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you can access it at &lt;em&gt;http://localhost:8787&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Cloudflare worker running locally&quot; title=&quot;Cloudflare worker running locally&quot; src=&quot;/assets/local-cf-worker-d51a8a7b838c70ea11476514615b79c45a18d036327a5da10ac31b54ef43f67e.png&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;center annotation&quot;&gt;Cloudflare worker process running locally&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Let’s make this example more interesting by adding a visits counter using &lt;a href=&quot;https://developers.cloudflare.com/kv/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;KV storage&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;src/lib.rs&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[event(fetch)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.kv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;visits&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap_or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap_or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nn&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Visits: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example reads number of visits from a key-value store and increments it on each request. If you test it in the browser, you’ll notice that the counter increments by 2 on each visit. It’s because of the &lt;code class=&quot;highlighter-rouge&quot;&gt;GET /favicon.ico&lt;/code&gt; request which also triggers the hit.&lt;/p&gt;

&lt;p&gt;Let’s now deploy our counter to production. You have to start by creating a production key-value store:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx wrangler kv namespace create visits
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command will prompt you to login to your Cloudflare account. Don’t forget to add the output to your &lt;code class=&quot;highlighter-rouge&quot;&gt;wrangler.toml&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;wrangler.toml&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;kv_namespaces]]
binding &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;visits&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;XXX&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re now ready to go live by typing:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx wrangler deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running this command will output the URL to access your first deployed Cloudflare worker.&lt;/p&gt;

&lt;h2 id=&quot;enabling-cloudflare-workers-proxy&quot;&gt;Enabling Cloudflare workers proxy&lt;/h2&gt;

&lt;p&gt;Let’s make our worker more useful by hooking it up as a proxy to a live website. This tutorial assumes that you have a website that uses Cloudflare DNS with proxy enabled i.e. cloud should be orange:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Cloudflare proxy enabled&quot; title=&quot;Cloudflare proxy enabled&quot; src=&quot;/assets/dns-proxy-on-a9fb4536b1c2dc3a6f0317e454522f657fd1575f9f63fed40984ed798fcd6a9a.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now go to &lt;strong&gt;Workers Routes&lt;/strong&gt; in your domain’s settings and click &lt;strong&gt;Add route&lt;/strong&gt;. Now select a page for which you want to activate your worker. As for &lt;strong&gt;Request limit failure mode&lt;/strong&gt; select &lt;strong&gt;Fail open&lt;/strong&gt; so that your page will keep working even if you exhaust the worker’s quota. On a free plan you can use 100k requests/day and up to 10 million a month.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;center-image&quot; alt=&quot;Worker proxy route config&quot; title=&quot;Worker proxy route config&quot; src=&quot;/assets/worker-route-config-5c9a4d1c25929cb16f2c8d73e6c2d1c7afb57dd5dfbf6c1f19fd6b0ac35cda4f.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you enable it, your website will display a blank page with a counter we’ve configured previously. Here’s how you can enable the proxy mode:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[event(fetch)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.kv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;visits&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap_or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap_or&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WORKER_ENV&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;production&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;Fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;p&amp;gt;Visits: [VISITS_COUNTER]&amp;lt;/p&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin_response&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[VISITS_COUNTER]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.with_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;origin_response&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.headers_mut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Last-Modified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We removed the &lt;code class=&quot;highlighter-rouge&quot;&gt;Last-Modified&lt;/code&gt; header to avoid blank page errors, &lt;a href=&quot;https://simonhearne.com/2022/empty-responses-cloudflare-workers-sites/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;as described here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We use a &lt;code class=&quot;highlighter-rouge&quot;&gt;WORKER_ENV&lt;/code&gt; config variable, to allow testing our worker locally. You’ll have to create &lt;code class=&quot;highlighter-rouge&quot;&gt;.devs.vars&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;WORKER_ENV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;development&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and add:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[vars]
WORKER_ENV = &quot;production&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to &lt;code class=&quot;highlighter-rouge&quot;&gt;wrangler.toml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this configuration in place, locally, you can work with a mocked version of an origin page. On production, we use &lt;code class=&quot;highlighter-rouge&quot;&gt;Fetch::Request(req).send().await?&lt;/code&gt; call to download our target website and reprocess its body.&lt;/p&gt;

&lt;p&gt;Our modification is just replacing all the occurences of the &lt;code class=&quot;highlighter-rouge&quot;&gt;[VISITS_COUNTER]&lt;/code&gt; string with the count fetched from the KV store.&lt;/p&gt;

&lt;p&gt;All you have to do now is embed the placeholder anywhere in your origin page and deploy the updated worker. The hit counter is live, and you can feel like the web design is &lt;a href=&quot;https://code.divshot.com/geo-bootstrap/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;back to its former glory&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;center&quot;&gt;
    &lt;p class=&quot;visits-counter&quot;&gt; 
    Visits: [VISITS_COUNT]
    &lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;security-headers-with-rust-cloudflare-workers-proxy&quot;&gt;Security headers with Rust Cloudflare workers proxy&lt;/h2&gt;

&lt;p&gt;But if visit counters are not your thing, the workers proxy offers great flexibility in modifying any part of the HTTP response.&lt;/p&gt;

&lt;p&gt;A practical application of this is adding security headers to systems that otherwise don’t support it. I’ve known companies whose primary landing page used a niche CMS, without an NGINX proxy. So adding custom headers was not possible without a general infrastructure overhaul.&lt;/p&gt;

&lt;p&gt;You can easily overcome this limitation by implementing the following worker:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[event(fetch)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WORKER_ENV&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;production&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;Fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.await&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mock&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;origin_response&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;strict-transport-security&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;max-age=31536000; includeSubDomains; preload&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;x-frame-options&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SAMEORIGIN&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Last-Modified&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;origin_response&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.with_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With similar worker, you should always be able to score an A+ on &lt;a href=&quot;https://securityheaders.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;securityheaders.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Rust, Wasm, CDN, and edge cache workers… I wonder how devs implemented hit counters 20 years ago without all this advanced tech. Anyway, Cloudflare Workers is an interesting tool that is worth knowing. In some cases it could offer a simple solution to otherwise complex devops tasks.&lt;/p&gt;
</description>
        <pubDate>Wed, 02 Oct 2024 09:56:01 +0200</pubDate>
        <link>https://pawelurbanek.com/cloudflare-workers-rust</link>
        <guid isPermaLink="true">https://pawelurbanek.com/cloudflare-workers-rust</guid>
      </item>
    
  </channel>
</rss>
