<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet href="/static/styles/feed.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
   <title>Pranav V Bhat</title>
   <link>https://prana-vvb.github.io/</link>
   <description>Recent content on Pranav V Bhat</description>
   <language>en-IN</language>
   <webMaster>Pranav V Bhat</webMaster>
   <copyright>© Pranav V Bhat 2026</copyright>
   <lastBuildDate>Wed, 02 Sep 2026 17:05:07 +0000</lastBuildDate>
   <atom:link href="https://prana-vvb.github.io/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>August 2026 Rust Blr meetup</title>
      <link>https://prana-vvb.github.io/notes/rust_blr_22-08-2026.html</link>
      <pubDate>Sat, 22 Aug 2026 00:00:00 +0000</pubDate>
      <author>Pranav V Bhat</author>
      <guid>https://prana-vvb.github.io/notes/rust_blr_22-08-2026.html</guid>
      <description>&lt;h2 id=&#34;building-a-webassembly-interpreter-from-scratch-in-rust---bhavya-bhatthttpsgithubcomspino17tracewasm&#34;&gt;Building a WebAssembly Interpreter from Scratch in Rust - &lt;a href=&#34;https://github.com/spino17/tracewasm&#34;&gt;Bhavya Bhatt&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Existing WASM runtimes are fast but not really observable&lt;/li&gt;&#xA;&lt;li&gt;Goal was to build an interpreter where stack traces can be mapped back to source lang, not WASM&lt;/li&gt;&#xA;&lt;li&gt;WASM basically solves what Docker is doing&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/user-attachments/assets/15541119-d540-40f5-8a30-e43bdca67271&#34; alt=&#34;The author of Docker regarding WASM&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Languages with a Garbage Collector have to compile their whole runtime along with the application code into WASM to work. Rust has no GC =&amp;gt; Better WASM support&lt;/li&gt;&#xA;&lt;li&gt;Languages targeting WASM are truly cross platform&lt;/li&gt;&#xA;&lt;li&gt;WASM code structured as:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-md&#34;&gt;type section (&#xA;    Generic function type definitions that show how many params&#xA;    and how many results different functions of that type can have&#xA;)&#xA;&#xA;imports section&#xA;&#xA;instructions/function body&#xA;&#xA;exports section&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;TraceWASM is a stack based VM&lt;/li&gt;&#xA;&lt;li&gt;A stack holding data references and instructions + memory storing arbitrary data achieves &lt;a href=&#34;https://en.wikipedia.org/wiki/Turing_completeness&#34;&gt;Turing Completeness&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;WASM memory is just a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;To be used, a value must be first pushed onto the top of the stack but values in the &lt;code&gt;locals&lt;/code&gt; space of the stack may be randomly accessed by the programmer to be pushed to the top&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7fba4270636ec4200b90129e4bc53ceb/raw/2633b55ad802242dd2e81644f7904e9e8aa4be9f/stack_based_vm.svg&#34; alt=&#34;WASM stack layout&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;WASM instructions do 3 things: push 0 or more instructions onto the stack, pop 0 or more instructions onto the stack, read data from memory&lt;/li&gt;&#xA;&lt;li&gt;A WASM interpreter is basically just&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;loop {&#xA;    fetch(instructions)&#xA;    pc = exec_instructions(&amp;amp;mut stack, &amp;amp;mut memory) // Mutable references to the stack and memory&#xA;&#xA;    if pc == end { break }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Basic syntax for an instruction: &lt;code&gt;&amp;lt;Required stack&amp;gt;.&amp;lt;Instruction&amp;gt; &amp;lt;Value&amp;gt;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Required stack include stuff like &lt;code&gt;local&lt;/code&gt; (Function params, local vars etc.), &lt;code&gt;global&lt;/code&gt; (Global vars), &lt;code&gt;i32&lt;/code&gt; (Perform work on values of type &lt;code&gt;i32&lt;/code&gt;) and other data types&lt;/li&gt;&#xA;&lt;li&gt;Labels are used to define blocks. Essentially functions and include branches, loops etc. Has to be terminated with &lt;code&gt;end&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Can reference other blocks by using relative depth indexes where 0 is the index of the parent block of the branch, 1 is the grandparent and so on&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-wasm&#34;&gt;(block ;; Scope 1 (Index 1)&#xA;  (block ;; Scope 0 (Index 0)&#xA;    br 1 ;; Jumps to the end of Scope 1&#xA;  )&#xA;)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Parameters of block branched to are left at the top of the stack after branching&lt;/li&gt;&#xA;&lt;li&gt;TraceWASM supports generic memory to be used. Can program linear, cache hirearchial or distributed memory and observe stack traces for each&lt;/li&gt;&#xA;&lt;li&gt;Is event based&lt;/li&gt;&#xA;&lt;li&gt;Register based VMs (like Google&#39;s V8) allow local values to be directly accesses instead of first pushing onto the stack. Can perform tasks in lesser instructions but requires more memory accesses and hence it is less cache friendly&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;building-pgpulse-as-a-native-postgresql-extension---harshit-ruwali&#34;&gt;Building PgPulse as a Native PostgreSQL Extension - Harshit Ruwali&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://blog.sp00fexpl01t.rocks/building-pgpulse-as-a-native-postgresql-extension/&#34;&gt;blog.sp00fexpl01t.rocks&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Lost in Tokio</title>
      <link>https://prana-vvb.github.io/posts/lost_in_tokio.html</link>
      <pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate>
      <author>Pranav V Bhat</author>
      <guid>https://prana-vvb.github.io/posts/lost_in_tokio.html</guid>
      <description>&lt;p&gt;Here, I try to get an abstracted overview of Tokio&#39;s architecture to build the foundation needed to understand its internals in depth later. To understand Tokio and why it exists, we must first look at the problems it was built to solve. We&#39;ll start with the simplest model of execution and gradually introduce the abstractions that lead us to an async runtime.&lt;/p&gt;&#xA;&lt;h2 id=&#34;level-0-synchronous-programming&#34;&gt;Level 0: Synchronous programming&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;Most code that you write is executed sequentially&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;fn synchronous() {&amp;#10;    println!(&#34;1&#34;);&amp;#10;    println!(&#34;2&#34;);&amp;#10;    println!(&#34;3&#34;);&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Completely innocent synchronous function&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;This &lt;em&gt;synchronous&lt;/em&gt; way is perfectly fine for most tasks, but some operations (like network requests or I/O waits) in the chain can be painfully slow.&#xA;They &#39;block&#39; the program from progressing until they are done, resulting in your application just sitting there doing nothing.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;fn evil_synchronous() {&amp;#10;    println!(&#34;Requesting user data...&#34;);&amp;#10;    &amp;#10;    // Execution cannot continue until the database responds.&amp;#10;    let response = get_from_db(&#34;Geronimo&#34;).unwrap(); &amp;#10;    &amp;#10;    println!(&#34;Got data: {response}&#34;);&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Evil and intimidating blocking code&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Blocking delays like this are common when applications wait for I/O operations to finish. But what if your program could do other work while it waits?&lt;/p&gt;&#xA;&lt;h2 id=&#34;level-1-concurrency-and-parallelism-through-os-threads&#34;&gt;Level 1: Concurrency and Parallelism through OS Threads&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;A very naive way to do this would be to &lt;a href=&#34;https://www.microsoft.com/en-us/research/wp-content/uploads/2019/04/fork-hotos19.pdf#page=2&#34;&gt;create a new process for each task&lt;/a&gt;. But this would be very expensive as a new process would require its own isolated memory context and common data would have to be passed between these processes.&lt;/p&gt;&#xA;&lt;p&gt;Instead, we use multiple &lt;a href=&#34;https://en.wikipedia.org/wiki/Thread_(computing)&#34;&gt;&lt;em&gt;threads&lt;/em&gt;&lt;/a&gt; inside a single process.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Thread&lt;/em&gt;: The smallest sequence of programmed instructions that can be managed independently by a scheduler.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;While not as isolated as separate processes, each thread has its own program counter, register set and stack space but share memory space and resources with other threads within the same process.&#xA;Threads can be either handled in userspace by the process spawning them or directly by the OS kernel. Kernel level threads can be independently scheduled by the kernel, allowing for parallel execution when multiple cores are available but they also have higher scheduling and context switching overhead.&lt;/p&gt;&#xA;&lt;p&gt;In Rust, threads are exposed for use via the native &lt;code&gt;std::thread&lt;/code&gt; interface. A thread spawned by &lt;code&gt;std::thread&lt;/code&gt; maps 1:1 onto a kernel level thread scheduled and managed not by Rust but by the OS.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;use std::thread;&amp;#10;use std::time::Duration;&amp;#10;&amp;#10;fn main() {&amp;#10;    let handle = thread::spawn(|| {&amp;#10;        for i in 1..10 {&amp;#10;            println!(&#34;Spawned thread {i}&#34;);&amp;#10;            thread::sleep(Duration::from_millis(1));&amp;#10;        }&amp;#10;    });&amp;#10;&amp;#10;    for i in 1..5 {&amp;#10;        println!(&#34;{i} from the main thread&#34;);&amp;#10;        thread::sleep(Duration::from_millis(1));&amp;#10;    }&amp;#10;&amp;#10;    handle.join().unwrap(); // main thread should not exit until all spawned threads are done&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Concurrent execution with OS threads (From doc.rust-lang.org/book/ch16-01-threads.html)&lt;/div&gt;&lt;/div&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!NOTE]&#xA;&lt;a href=&#34;https://rust-lang.github.io/book/ch17-00-async-await.html#parallelism-and-concurrency&#34;&gt;&lt;strong&gt;The basic difference between Concurrency and Parallelism&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/em&gt; is about structuring multiple independent tasks to execute and progress in overlapping time periods. On a single core, the OS achieves this by rapidly switching between tasks.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Parallelism&lt;/strong&gt;&lt;/em&gt; is when tasks are literally run at the same time across multiple CPU cores.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Although OS threads provide concurrency (and parallelism on multi-core hardware) and are cheaper than creating an entirely new process, they still are relatively expensive.&lt;/p&gt;&#xA;&lt;p&gt;Historically, the simplest way to handle network traffic was to spawn one OS thread per connection. However, OS threads are heavy.&#xA;On Linux, each thread reserves a default 8MB of virtual memory for its stack. If an application tried to &lt;a href=&#34;https://en.wikipedia.org/wiki/C10k_problem&#34;&gt;serve 10,000 concurrent connections&lt;/a&gt; this way, it would demand 80GB of virtual address space.&lt;/p&gt;&#xA;&lt;p&gt;But virtual address space is cheap and abundant on modern systems. The real problem is that the OS kernel has to constantly pause and resume these threads (context switching). This is significantly more expensive while also potentially invalidating cache locality. The CPU would spend all its time just juggling threads rather than doing actual work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;level-2-cooperative-multitasking-with-asyncawaithttpsosphil-oppcomasync-await&#34;&gt;Level 2: Cooperative Multitasking with &lt;a href=&#34;https://os.phil-opp.com/async-await/&#34;&gt;async/.await&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!NOTE]&#xA;&lt;a href=&#34;https://www.geeksforgeeks.org/operating-systems/difference-between-preemptive-and-cooperative-multitasking/&#34;&gt;Preemptive VS Cooperative multitasking&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Preemptive multitasking&lt;/strong&gt;&lt;/em&gt;: The OS allocates each thread a time slice to execute in and forcibly pauses the thread when its time is up no matter what it is doing and runs the next scheduled thread.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Cooperative multitasking&lt;/strong&gt;&lt;/em&gt;: Each task voluntarily yields control back when it is idle or has hit a blocking point, giving us a lower context switching overhead.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Luckily for us, Rust provides a the &lt;a href=&#34;https://rust-lang.github.io/async-book/02_execution/02_future.html&#34;&gt;&lt;code&gt;Future&lt;/code&gt;&lt;/a&gt; trait as an abstraction for asynchronous work. Futures are analogous to a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&#34;&gt;&lt;code&gt;Promise&lt;/code&gt;&lt;/a&gt; from JavaScript, with the main difference being that a &lt;code&gt;Promise&lt;/code&gt; is eagerly executed by the JavaScript runtime while a &lt;code&gt;Future&lt;/code&gt; is lazy until it is polled.&lt;/p&gt;&#xA;&lt;p&gt;Polling is basically giving the future the opportunity to progress by asking, &amp;quot;Hey, make some progress on your work now&amp;quot; The &lt;code&gt;Future&lt;/code&gt; can then respond with either &amp;quot;No, I can&#39;t progress now&amp;quot; (&lt;code&gt;Poll::Pending&lt;/code&gt;) or &amp;quot;Yes, I&#39;m done. Here is the result&amp;quot; (&lt;code&gt;Poll::Ready(val)&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;Rust gives us the &lt;code&gt;async/.await&lt;/code&gt; syntax, allowing us to write asynchronous code in a way that looks similar to synchronous code. This syntax will be &lt;a href=&#34;https://en.wikipedia.org/wiki/Async/await#Implementations&#34;&gt;familiar if you&#39;re coming from JavaScript or Python&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;For example:&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;fn synchronous_io() {&amp;#10;    let resp = fetch_data();&amp;#10;    println!(&#34;{resp}&#34;);&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Standard, blocking I/O&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Can be written as:&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;async fn asynchronous_io() {&amp;#10;    let resp = fetch_data_async().await;&amp;#10;    println!(&#34;{resp}&#34;);&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Asynchronous I/O using async/.await&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;As you can see, the main differences are the &lt;code&gt;async&lt;/code&gt; keyword in the function definition and the &lt;code&gt;.await&lt;/code&gt; postfix operator after an async function call.&lt;br/&gt;&#xA;But what exactly are they doing?&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;async&lt;/code&gt; transforms your function into a &lt;a href=&#34;https://en.wikipedia.org/wiki/Finite-state_machine&#34;&gt;state machine&lt;/a&gt; that implements the &lt;code&gt;Future&lt;/code&gt; trait. Each &lt;code&gt;.await&lt;/code&gt; marks a suspension point and the boundary between different states, allowing the state machine to pause and resume at these points. This state machine also stores context such as local variables, and child Futures that are being awaited.&lt;/p&gt;&#xA;&lt;p&gt;When execution reaches an &lt;code&gt;.await&lt;/code&gt;, the future being awaited is polled. If it is ready, execution continues normally.&#xA;Otherwise, the state machine saves its current state, returns &lt;code&gt;Poll::Pending&lt;/code&gt; to the caller and yields control so that other work can be done while waiting.&#xA;Before doing so, the awaited future typically stores a &lt;a href=&#34;https://doc.rust-lang.org/beta/std/task/struct.Waker.html&#34;&gt;&lt;code&gt;Waker&lt;/code&gt;&lt;/a&gt; that can later be used to arrange for the task to be polled again when progress becomes possible.&lt;/p&gt;&#xA;&lt;p&gt;Later, when the async function is polled again, the state machine resumes execution from the previously saved state.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/56b3474a142b488d7bc4e68c88ee26a220c7c67c/tokio_1.svg&#34; alt=&#34;Simplified state machine generated from async fn asynchronous_io()&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;Very neat! Now let us run this function.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;fn main() {&#xA;    // Remember we need to await a Future to progress it since they are lazy&#xA;    asynchronous_io().await;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Oh no! The Rust compiler requires any function that calls an async function to also be declared with &lt;code&gt;async&lt;/code&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;error[E0728]: `await` is only allowed inside `async` functions and blocks&#xA; --&amp;gt; src/main.rs:6:23&#xA;  |&#xA;5 | fn main() {&#xA;  | --------- this is not `async`&#xA;6 |     asynchronous_io().await;&#xA;  |                       ^^^^^ only allowed inside `async` functions and blocks&#xA;&#xA;For more information about this error, try `rustc --explain E0728`.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is because &lt;code&gt;.await&lt;/code&gt; is a potential suspension point. If the awaited &lt;code&gt;Future&lt;/code&gt; isn&#39;t ready yet, the caller must save its current state, yield control, and later resume execution from where it left off. Ordinary functions are not capable of doing this, only functions marked with &lt;code&gt;async&lt;/code&gt; are.&lt;/p&gt;&#xA;&lt;p&gt;So no worries, we will just declare &lt;code&gt;main&lt;/code&gt; also with the &lt;code&gt;async&lt;/code&gt; keyword.&#xA;Unfortunately, this too does not work.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;error[E0752]: `main` function is not allowed to be `async`&#xA; --&amp;gt; src/main.rs:5:1&#xA;  |&#xA;5 | async fn main() {&#xA;  | ^^^^^^^^^^^^^^^ `main` function is not allowed to be `async`&#xA;&#xA;For more information about this error, try `rustc --explain E0752`.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The reason &lt;code&gt;main&lt;/code&gt; cannot be &lt;code&gt;async&lt;/code&gt; is that someone has to drive the &lt;code&gt;Future&lt;/code&gt; returned by &lt;code&gt;main&lt;/code&gt; also to completion.&lt;/p&gt;&#xA;&lt;p&gt;An async fn doesn&#39;t execute by itself. Calling it just constructs a value containing all the state required to perform the work, but not when. Futures are lazy, so unless something repeatedly polls them, they never make progress. So who does the polling?&lt;/p&gt;&#xA;&lt;h2 id=&#34;level-3-async-runtimes&#34;&gt;Level 3: Async runtimes&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;This is where an async runtime comes into play. Most languages that support async have an async runtime built into the core language runtime and thus support async functions out of the box. Rust on the other hand provides only the foundation such as the &lt;code&gt;Future&lt;/code&gt; and the &lt;code&gt;async/.await&lt;/code&gt; syntax but no async runtime.&lt;/p&gt;&#xA;&lt;p&gt;This is mainly due to Rust being used in many different areas from web development to systems and bare metal/embedded. There no consensus on a &amp;quot;One True Async Runtime&amp;quot; capable of supporting all of them perfectly. Instead, it is up to the developer to choose from many different community provided crates tailored to their use case.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&amp;quot;Rust caters to a vast array of use cases. We simply cannot bundle everything into the core standard library, but the ecosystem provides a crate for almost every need. Just use one of those&amp;quot;&lt;/p&gt;&#xA;&lt;p&gt;— Paraphrased quote from &lt;a href=&#34;https://smallcultfollowing.com/babysteps/&#34;&gt;Niko Matsakis&lt;/a&gt;, Core developer on the Rust programming language&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Tokio is the mostly widely used async runtime at the time of writing and thus will be our focus.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!WARNING]&#xA;Disclaimer: Tokio is actively being developed, so some information here may quickly become out-of-date.&#xA;We will be focusing on parts of &lt;a href=&#34;https://github.com/tokio-rs/tokio&#34;&gt;tokio-rs/tokio&lt;/a&gt; v1.53.1 for the rest of this blog&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The role of any async runtime is to schedule futures for polling, react to them waking up and coordinate the resources required by the futures to make progress.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;executor&lt;/code&gt; is the component of an async runtime responsible for repeatedly polling futures.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;loop {&amp;#10;    match future.poll(&amp;amp;mut context) {&amp;#10;        Poll::Ready(_) =&amp;gt; break,&amp;#10;        Poll::Pending =&amp;gt; {&amp;#10;            // wait for a wakeup&amp;#10;        }&amp;#10;    }&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;A very simple executor&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;This works well for one task but how does it scale?&lt;/p&gt;&#xA;&lt;p&gt;Similar to what we have seen with threads above, real applications almost never have only 1 future. We may have thousands of &lt;code&gt;tasks&lt;/code&gt;, many of which are waiting on I/O while only a small number are actually ready to run.&#xA;But like with threads, how does this solve the problem of either consuming too much memory or slowing down from a lot of context switches?&lt;/p&gt;&#xA;&lt;p&gt;Tokio &lt;code&gt;tasks&lt;/code&gt;, unlike threads are very lightweight and are managed by the Tokio runtime, not the OS scheduler. Because tasks are scheduled in userspace by Tokio, switching between tasks does not require OS thread context switches and has a low overhead. They are also cooperatively scheduled rather than preemptively scheduled.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;A &lt;em&gt;task&lt;/em&gt; is a light weight, non-blocking unit of execution.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Generally, this pattern is known as &lt;a href=&#34;https://en.wikipedia.org/wiki/Green_thread&#34;&gt;green threads&lt;/a&gt; and is similar to Golang&#39;s &lt;a href=&#34;https://tour.golang.org/concurrency/1&#34;&gt;goroutines&lt;/a&gt;. This way, a lot of tasks can be run on a handful of OS threads. A new problem arises now, how to keep track of which tasks are ready to run and which thread it can run on? This leads us to the obvious solution: A scheduler.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-tokio-scheduler&#34;&gt;The Tokio Scheduler&lt;/h3&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;This scheduler is responsible for deciding which runnable task should be executed next. In a simple runtime, this could be as easy as maintaining a queue of ready tasks and repeatedly choosing one to poll. In Tokio, which is a multi-threaded runtime, the scheduler has to coordinate M tasks (theoretically unlimited) across N threads (limited amount). Having only a global queue means every worker threads has to contend for access, increasing synchronization overhead.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!NOTE]&#xA;Tokio, by default, is multi-threaded but can be configured to be a single-threaded event loop AKA &lt;code&gt;current_thread&lt;/code&gt; which can actually be easier to work with in most cases as argued &lt;a href=&#34;https://emschwartz.me/async-rust-can-be-a-pleasure-to-work-with-without-send-sync-static/&#34;&gt;here&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/8237ae9a3e7ae94e417c7ad59a3fa5151cf7f754/scheduler.svg&#34; alt=&#34;The Tokio M:N scheduler&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;As you can see, Tokio solves this by having a global queue of tasks (implemented as a FIFO linked list) shared between all threads along with local queues for each thread/worker.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;/// Growable, MPMC queue used to inject new tasks into the scheduler and as an&amp;#10;/// overflow queue when the local, fixed-size, array queue overflows.&amp;#10;pub(crate) struct Inject&amp;lt;T: &#39;static&amp;gt; {&amp;#10;    shared: Shared&amp;lt;T&amp;gt;,&amp;#10;    synced: Mutex&amp;lt;Synced&amp;gt;,&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Global &#39;Injection&#39; queue definition (From tokio/src/runtime/scheduler/inject.rs#L21-L26)&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;The local queue is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Circular_buffer&#34;&gt;ring buffer&lt;/a&gt; which can hold upto 256 tasks at a time. When this local queue overflows, roughly half of the tasks from the local queue are moved to the global queue and held. This serves as spillway to catch overflowing tasks. Any task that wakes up from a thread which is not a worker thread is also placed into the global queue thus acting as a shared entry point too.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;/// Producer handle. May only be used from a single thread.&amp;#10;pub(crate) struct Local&amp;lt;T: &#39;static&amp;gt; {&amp;#10;    inner: Arc&amp;lt;Inner&amp;lt;T&amp;gt;&amp;gt;,&amp;#10;}&amp;#10;&amp;#10;/// Consumer handle. May be used from many threads.&amp;#10;pub(crate) struct Steal&amp;lt;T: &#39;static&amp;gt;(Arc&amp;lt;Inner&amp;lt;T&amp;gt;&amp;gt;);&amp;#10;&amp;#10;pub(crate) struct Inner&amp;lt;T: &#39;static&amp;gt; {&amp;#10;    /// Concurrently updated by many threads.&amp;#10;    ///&amp;#10;    /// The `UnsignedShort` indices are intentionally wider than strictly&amp;#10;    /// required for buffer indexing in order to provide ABA mitigation and make&amp;#10;    /// it possible to distinguish between full and empty buffers.&amp;#10;    ///&amp;#10;    /// When both `UnsignedShort` values are the same, there is no active&amp;#10;    /// stealer.&amp;#10;    head: AtomicUnsignedLong,&amp;#10;&amp;#10;    /// Only updated by producer thread but read by many threads.&amp;#10;    tail: AtomicUnsignedShort,&amp;#10;&amp;#10;    /// Elements&amp;#10;    buffer: Box&amp;lt;[UnsafeCell&amp;lt;MaybeUninit&amp;lt;task::Notified&amp;lt;T&amp;gt;&amp;gt;&amp;gt;; LOCAL_QUEUE_CAPACITY]&amp;gt;,&amp;#10;}&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Local queue definition (From tokio/src/runtime/scheduler/multi_thread/queue.rs#L28-L57)&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;A worker first checks its local queue for any runnable tasks and only checks the global queue if it runs out of tasks or after a configurable number of local tasks have been scheduled.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/4aa31c580268f968857d4d039cadecd041f1410f/task_hirearchy.svg&#34; alt=&#34;Hirerarchy of choosing tasks&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;This can be compared to how cache locality works. First try to retrieve from the closest source and if not found, move to more distant sources. And similar to how you reach for data from memory when it is not in cache, worker threads reach to steal tasks from other workers.&lt;/p&gt;&#xA;&lt;p&gt;A worker may run out of tasks in both its local and the global queue even while another worker still has a large number of runnable tasks. To keep the workload balanced, an idle worker can steal tasks from another worker&#39;s local queue. Tokio moves roughly half of the tasks during stealing rather than just taking a single task.&lt;/p&gt;&#xA;&lt;p&gt;The stealing operation immediately returns the last task in the stolen batch to the thief for execution and then continues normally.&lt;/p&gt;&#xA;&lt;p&gt;A neat optimization is that each worker has a single element task slot. Any task placed in this slot can bypass both the local and global queue and gets executed first in the next iteration. This effectively results in the last scheduled task to be run next (LIFO). This optimization improves cache locality which benefits message passing patterns and helps to reduce latency.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/ccda86cac78522dfcd567b76a1b16d514dae3fb8/work_stealing.svg&#34; alt=&#34;Work stealing&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;So in the above diagram, Worker 2 would execute Task 3 first and then Task 1 and Task 2 (&lt;code&gt;[3]-&amp;gt;[1]-&amp;gt;[2]&lt;/code&gt;). This lets the thief begin executing immediately rather than enqueueing the entire stolen batch and then performing another queue operation to obtain its first task.&lt;/p&gt;&#xA;&lt;p&gt;Work-stealing involves concurrent, unsynchronized access to head and tail across threads. Because stolen tasks cross thread boundaries, any task spawned on a multi-threaded runtime is forced to satisfy &lt;a href=&#34;https://doc.rust-lang.org/std/marker/trait.Send.html&#34;&gt;&lt;code&gt;Send&lt;/code&gt;&lt;/a&gt; + &lt;a href=&#34;https://doc.rust-lang.org/std/keyword.static.html&#34;&gt;&lt;code&gt;&#39;static&lt;/code&gt;&lt;/a&gt; bounds. Single-threaded Tokio on the other hand requires only &lt;code&gt;&#39;static&lt;/code&gt; to be satisfied since there is no work stealing.&lt;/p&gt;&#xA;&lt;h3 id=&#34;actually-running-tasks&#34;&gt;Actually running Tasks&lt;/h3&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;We&#39;ve established before that Tokio tasks are lightweight units of work. These tasks are distributed among workers when they are runnable. But tasks do not remain runnable forever. Tokio is a runtime designed to handle asynchronous I/O bound tasks which spend most of their lifetime waiting for something to happen.&lt;/p&gt;&#xA;&lt;p&gt;When a task reaches a state where it cannot make any progress without waiting, it returns &lt;code&gt;Poll::Pending&lt;/code&gt;. The I/O operation registers interest in the underlying OS resource, while the task provides a &lt;code&gt;Waker&lt;/code&gt; that can be used to schedule it again when that resource becomes ready.&lt;/p&gt;&#xA;&lt;p&gt;A &lt;code&gt;Waker&lt;/code&gt; is essentially a handle to something that knows how to make a suspended task runnable again. Internally, Rust represents this through a &lt;code&gt;RawWaker&lt;/code&gt; containing a data pointer and a &lt;code&gt;RawWakerVTable&lt;/code&gt;. The vtable tells the runtime what to do when the waker is cloned, woken, referenced without consuming it, or dropped.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;use std::task::{RawWaker, RawWakerVTable, Waker};&amp;#10;&amp;#10;fn wake_task(ptr: *const ()) {&amp;#10;    // schedule the task&amp;#10;}&amp;#10;&amp;#10;const VTABLE: RawWakerVTable = RawWakerVTable::new(&amp;#10;    clone,&amp;#10;    wake_task,&amp;#10;    wake_by_ref,&amp;#10;    drop,&amp;#10;);&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;The vtable and raw waker are defined in the standard library. Tokio&#39;s custom vtable from tokio/src/runtime/task/waker.rs#L118-L119&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Tokio represents a task as a single heap allocation containing the task header, the future, output/state, and scheduler information. A Waker wraps a raw pointer (&lt;code&gt;NonNull&amp;lt;Header&amp;gt;&lt;/code&gt;) with a custom &lt;code&gt;RawWakerVTable&lt;/code&gt;. Calling &lt;code&gt;.wake()&lt;/code&gt; executes an atomic state transition directly on the task’s &lt;code&gt;Header&lt;/code&gt; flags. If the transition succeeds and the task is woken, the task memory pointer is re-enqueued into a scheduler queue.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/9793f577c01b2e8e9124e676d98da0cde95bf704/tokio_whole.svg&#34; alt=&#34;A task&#39;s lifecycle&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;The I/O driver waits for events from the OS for the registered resources and wake the tasks when they become available. Tokio does not actually handle each specific I/O driver by itself but instead relies on the &lt;a href=&#34;https://github.com/tokio-rs/mio&#34;&gt;&lt;code&gt;mio&lt;/code&gt;&lt;/a&gt; crate to abstract system specific drivers and provide a common API for all of them.&lt;/p&gt;&#xA;&lt;p&gt;Note that the I/O driver mechanism is not controlled by a dedicated thread but is integrated into each and every worker.&lt;/p&gt;&#xA;&lt;p&gt;I/O is only one source of task wakeups. Timers and asynchronous synchronization primitives(&lt;code&gt;tokio::sync::*&lt;/code&gt;) can also cause a pending task to become runnable again. A timer can wake a task when its deadline expires, while primitives such as channels, notifications, and semaphores can wake tasks when the state they are waiting for changes.&lt;/p&gt;&#xA;&lt;h3 id=&#34;when-we-are-truly-jobless&#34;&gt;When we are truly jobless&lt;/h3&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;A worker which has finished all its tasks, has no tasks left in its local queue, no tasks to get from the global queue and nothing to steal from other workers is freeloading on precious CPU power. We need to be able to hibernate the worker until it is needed to handle more tasks. Constantly checking for new tasks is just wasteful so Tokio has a park/unpark mechanism for workers to transition into a sleeping state and block instead.&lt;/p&gt;&#xA;&lt;p&gt;This is handled by a dedicated &lt;code&gt;runtime::park&lt;/code&gt; module using a shared runtime driver and a conditional variable (&lt;code&gt;Condvar&lt;/code&gt;) as a fallback since the driver is a shared resource and can be used by only one worker at a time either for I/O related or timing related wakeups.&#xA;&lt;code&gt;park()&lt;/code&gt;/&lt;code&gt;unpark()&lt;/code&gt; calls are coordinated by an &lt;a href=&#34;https://en.wikipedia.org/wiki/Linearizability&#34;&gt;atomic&lt;/a&gt; state machine&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper&#34;&gt;&lt;pre&gt;&lt;code class=&#34;language-rust&#34;&gt;const EMPTY: usize = 0;&amp;#10;const PARKED_CONDVAR: usize = 1;&amp;#10;const PARKED_DRIVER: usize = 2;&amp;#10;const NOTIFIED: usize = 3;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&#34;code-caption&#34;&gt;Atomic state machine states&lt;/div&gt;&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;EMPTY&lt;/code&gt;: Nothing is parked and no pending notification&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;PARKED_CONDVAR&lt;/code&gt;: Worker is about to/has parked on the condition variable&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;PARKED_DRIVER&lt;/code&gt;: Worker is parked through the runtime driver&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;NOTIFIED&lt;/code&gt;: A wake-up has been issued&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;They are connected as below&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/1a6b10c46505be7bff79d0b7a807442b583a93f3/parking_state_machine.svg&#34; alt=&#34;State machine for the parking mechanism&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;This state machine is required to prevent a race condition where a wake up notification is issued just before the worker actually sleeps, causing it to sleep forever.&lt;/p&gt;&#xA;&lt;h3 id=&#34;fin&#34;&gt;Fin.&lt;/h3&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;Putting it all together, let us follow a single asynchronous operation through Tokio&#39;s event loop&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/d1b3be1c0b8a82da128d4f375f0d2c02d64fd175/basic_eloop.svg&#34; alt=&#34;Tokio basic event loop&#34;&gt;&#xA;&lt;/figure&gt;&#xA;</description>
    </item>
    <item>
      <title>clicks</title>
      <link>https://prana-vvb.github.io/clicks/index.html</link>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>Pranav V Bhat</author>
      <guid>https://prana-vvb.github.io/clicks/index.html</guid>
      <description></description>
    </item>
    <item>
      <title>May 2026 Rust Blr meetup</title>
      <link>https://prana-vvb.github.io/notes/rust_blr_16-05-2026.html</link>
      <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
      <author>Pranav V Bhat</author>
      <guid>https://prana-vvb.github.io/notes/rust_blr_16-05-2026.html</guid>
      <description>&lt;h2 id=&#34;async-runtimes-revisitedhttpsgitsanchayanmaitynetsanchayanmaitypresentationssrcbranchmasterasync-runtimes-may-2026runtimespdf---sanchayan&#34;&gt;&lt;a href=&#34;https://git.sanchayanmaity.net/sanchayanmaity/presentations/src/branch/master/async-runtimes-may-2026/runtimes.pdf&#34;&gt;Async runtimes revisited&lt;/a&gt; - Sanchayan&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;h3 id=&#34;why-async&#34;&gt;Why async?&lt;/h3&gt;&#xA;&lt;p&gt;Regular:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Not scalable&lt;/li&gt;&#xA;&lt;li&gt;1 OS thread ~= 8MB ⇒ Can’t use many threads for concurrency otherwise too much RAM consumption&lt;/li&gt;&#xA;&lt;li&gt;Kernel level threads used ⇒ Context switch overhead&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Async:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Handful of OS threads can handle millions of concurrent tasks&lt;/li&gt;&#xA;&lt;li&gt;Yield control to Executor on I/O wait instead of blocking thread in Async model&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;async-rust&#34;&gt;Async Rust&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Lazy evaluation until future is polled (using &lt;code&gt;.await&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Unlike other languages like JS in which an async fn is evaluated as soon as it is called.&lt;/li&gt;&#xA;&lt;li&gt;Avoids overhead&lt;/li&gt;&#xA;&lt;li&gt;Async runtimes provide Executors to handle polling of futures until it finishes execution&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;No callback hell thanks to async/await syntax&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Zero cost state machines&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://medium.com/@avocadi/what-is-epoll-9bbc74272f7c&#34;&gt;epoll&lt;/a&gt;/&lt;a href=&#34;https://people.freebsd.org/~jlemon/kqueue_slides/tsld003.htm&#34;&gt;kqueue&lt;/a&gt; (Readiness model):&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!NOTE]&#xA;Slightly related: nice &lt;a href=&#34;https://www.youtube.com/watch?v=-gP58pozNuM&#34;&gt;video on file descriptors&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/0788fba9034e9464aab66d45a06b81df/raw/993afe2cc76beef6f8775f24214c8a826738ba4b/epoll.svg&#34; alt=&#34;The Readiness model&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://unixism.net/loti/what_is_io_uring.html&#34;&gt;io_uring&lt;/a&gt; (Completion model):&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Don’t have to explicitly call read on ready fd&lt;/li&gt;&#xA;&lt;li&gt;Just read final result directly from Completion queue when OS async finishes execution and notifies&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/0788fba9034e9464aab66d45a06b81df/raw/993afe2cc76beef6f8775f24214c8a826738ba4b/io_uring.svg&#34; alt=&#34;The Completion model&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;h4 id=&#34;async-strats&#34;&gt;Async strats&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;tokio&#xA;&lt;ul&gt;&#xA;&lt;li&gt;M:N tasks:threads&lt;/li&gt;&#xA;&lt;li&gt;Multithreaded by default but can use single (current) thread&lt;/li&gt;&#xA;&lt;li&gt;epoll mechanism (Readiness model)&lt;/li&gt;&#xA;&lt;li&gt;Technically also thread-per-core&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;compio/glommio/monoio&#xA;&lt;ul&gt;&#xA;&lt;li&gt;What is generally known as “thread-per-core”&lt;/li&gt;&#xA;&lt;li&gt;io_uring mechanism mainly (Completion model)&lt;/li&gt;&#xA;&lt;li&gt;compio also supports IOCP (Windows’ completion model I/O interface)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;readiness-model---data-not-returned-until-read-called&#34;&gt;Readiness model - Data not returned until read called&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Process - Is x ready?&lt;/li&gt;&#xA;&lt;li&gt;epoll - Yes&lt;/li&gt;&#xA;&lt;li&gt;Return x to process and process calls read to get final product&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!NOTE]&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;2 syscalls per I/O task lifecycle (&lt;code&gt;epoll_wait&lt;/code&gt; and &lt;code&gt;read&lt;/code&gt;) ⇒ Context switch overhead&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;epoll_ctl&lt;/code&gt; is not counted because it is an amortized, one-time setup cost&lt;/li&gt;&#xA;&lt;li&gt;No batching&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h4 id=&#34;completion-model&#34;&gt;Completion model&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Process - I want to do x&lt;/li&gt;&#xA;&lt;li&gt;SQueue - Ok will do&lt;/li&gt;&#xA;&lt;li&gt;CQueue - Return final product to process&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;[!NOTE]&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Batch submit all tasks in 1 syscall to SQueue (&lt;code&gt;io_uring_enter&lt;/code&gt;. Not required if &lt;code&gt;SQPOLL&lt;/code&gt; setup)&lt;/li&gt;&#xA;&lt;li&gt;But no syscall per I/O task&lt;/li&gt;&#xA;&lt;li&gt;No need for syscall to read from CQueue&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3 id=&#34;work-stealing-in-the-readiness-model&#34;&gt;Work stealing in the Readiness model&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Research paper - &lt;a href=&#34;https://www.csd.uwo.ca/~mmorenom/CS433-CS9624/Resources/Scheduling_multithreaded_computations_by_work_stealing.pdf&#34;&gt;Scheduling multi-threaded computations by work stealing&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Blog - &lt;a href=&#34;https://tokio.rs/blog/2019-10-scheduler&#34;&gt;Making the Tokio scheduler 10x faster&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Basic idea: Idle threads steal work from busy threads’ queue&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Steal oldest waiting tasks (queue head) to minimize contention&lt;/li&gt;&#xA;&lt;li&gt;Current thread executes next task remaining after stealing&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Better for cache locality&lt;/li&gt;&#xA;&lt;li&gt;Emulates call stack&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/7a1472b97344d5bbc596021ed9d0c9c0/raw/ccda86cac78522dfcd567b76a1b16d514dae3fb8/work_stealing.svg&#34; alt=&#34;Work Stealing&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Good load balancing&lt;/li&gt;&#xA;&lt;li&gt;Synchronization overhead between threads + Cold cache penalty in new thread&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Future&lt;/code&gt; has to implement  &lt;code&gt;Send&lt;/code&gt; + &lt;code&gt;&#39;static&lt;/code&gt; trait bounds + also &lt;code&gt;Sync&lt;/code&gt; if concurrent sharing via references&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Send&lt;/code&gt; bound can be relaxed by running only on current thread using &lt;code&gt;tokio::task::LocalSet&lt;/code&gt; but not truly parallel&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Readiness model good to use generally with mixed workloads and unknown load patterns&lt;/p&gt;&#xA;&lt;h3 id=&#34;thread-per-core-and-share-nothing&#34;&gt;Thread-Per-Core and Share-nothing&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;All async runtimes are thread-per-core (TPC)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Difference between tokio and the other so-called “thread-per-core” runtimes is that they are also share-nothing ⇒ No work stealing&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;One thread pinned to each CPU core&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;No synchronization overhead, better cache locality, no contention and predictable tail latency but can cause load imbalance&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Need for app level partitioning of connections&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;But not all workloads can be partitioned&lt;/li&gt;&#xA;&lt;li&gt;Shared state managed through message passing&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Nice &lt;a href=&#34;https://www.reddit.com/r/rust/comments/1pn6010/compio_instead_of_tokio_what_are_the_implications/&#34;&gt;reddit thread&lt;/a&gt; on implications of completion vs readiness&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;compio v nice TPC+SN runtime because actively maintained + supports IOCP&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Completion good for&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Data plane workloads&lt;/li&gt;&#xA;&lt;li&gt;I/O bound workloads&lt;/li&gt;&#xA;&lt;li&gt;Highly cache sensitive workloads&lt;/li&gt;&#xA;&lt;li&gt;When you can partition cleanly&lt;/li&gt;&#xA;&lt;li&gt;Does your tail latency matter?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Cross-core communication is expensive since message passing through channels instead of shared state&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Tokio handles a lot of details in bg but has to be manually handled in compio&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Basically async hard mode but offers way better performance for I/O heavy workloads in return&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Buffer reference shared (borrowed) in Readiness model whereas buffer has to be owned by the kernel in the Completion model until I/O is complete&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Problem for future cancellation in Completion, Has to be explicitly handled&#xA;&lt;ul&gt;&#xA;&lt;li&gt;When cancelled in Readiness, reference goes out of scope and ends&lt;/li&gt;&#xA;&lt;li&gt;In Completion, kernel still has pointer to that memory ⇒ Use-After-Free if it has been deallocated when cancelled&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Incurs “Ownership tax” + can’t do concurrent reads with the same buffer&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Ownership tax = Can’t easily share buffer, Have to handle returned buffer to reuse, Buffer pooling has to be done to avoid constant allocations&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Usually tokio is good enough. Consider Completion model runtimes if I/O is bottleneck or cache issue&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;All links: &lt;a href=&#34;https://adihegde.com/til/rust-blr-16-may-2026&#34;&gt;https://adihegde.com/til/rust-blr-16-may-2026&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;cracking-games-and-how-to-make-them-crack-proof---ishan&#34;&gt;Cracking games and how to make them crack proof - Ishan&lt;/h2&gt;&#xA;&lt;hr/&gt;&#xA;&lt;p&gt;For local first, offline apps&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Bake a password into the binary&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Easily cracked by listing &lt;code&gt;strings&lt;/code&gt; in the binary&lt;/li&gt;&#xA;&lt;li&gt;Can also patch the binary to bypass password checks&lt;/li&gt;&#xA;&lt;li&gt;Only problem is takes a bit of time to search for the exact string or the exact location to patch&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Signed license. Pubkey baked into bin. Verify with privkey on app launch&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Cracked by patching code to bypass check or replace pubkey with your own pubkey&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Hardware fingerprinting. App runs only on one specific system. Verify using system specific details like MAC addr&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Bypass by spoofing fingerprints&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Harden during LTO. Distorts binary a bit making it difficult to understand decompiler output&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Fat LTO&lt;/li&gt;&#xA;&lt;li&gt;Abort on panic instead of unwind (No stack traces to read and get info)&lt;/li&gt;&#xA;&lt;li&gt;Strip debug symbols&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Checks usually done by dynamic libraries in games so patch in your own DLLs in windows instead of the original ones or use &lt;code&gt;LD_PRELOAD&lt;/code&gt; to load your own libraries and bypass checks.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Building a simple Load Balancer in Rust</title>
      <link>https://prana-vvb.github.io/posts/balrs.html</link>
      <pubDate>Sat, 10 Aug 2024 00:00:00 +0000</pubDate>
      <author>Pranav V Bhat</author>
      <guid>https://prana-vvb.github.io/posts/balrs.html</guid>
      <description>&lt;h2 id=&#34;why-do-i-need-a-load-balancer&#34;&gt;Why do I need a Load Balancer?&lt;/h2&gt;&#xA;&lt;p&gt;Let&#39;s say you have a few servers and are hosting a website. Great! Soon your website becomes popular and gets a lot of visitors daily. All well and good until your servers start to become overwhelmed with requests and die.&#xA;How to fix this? By putting a Load Balancer in between the clients and your servers.&lt;/p&gt;&#xA;&lt;p&gt;A Load Balancer distributes incoming network traffic and distributes them across multiple servers to ensure no single server is overwhelmed thus optimizing reliability and resource utilization.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExcnc4MmNkNGkzbDZ6ZG1icW44aG9xZGg2NjNwZmdrbG1xeWNxMmZmZiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/JAC4be8Wr01Lp8dyop/giphy.gif&#34; alt=&#34;A round robin load balancer&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;A Load Balancer can be physical or a software. It can be further classified base on which layer of the &lt;a href=&#34;https://en.wikipedia.org/wiki/OSI_model&#34;&gt;OSI model&lt;/a&gt; they operate at.&lt;/p&gt;&#xA;&lt;p&gt;As part of the &lt;a href=&#34;https://homebrew.hsp-ec.xyz/posts/history/#Tilde&#34;&gt;Tilde 3.0 Summer mentorship program&lt;/a&gt;, the &lt;a href=&#34;https://github.com/homebrew-ec-foss/bal.rs&#34;&gt;Bal.rs&lt;/a&gt; (Pronounced: &lt;code&gt;/ˈbɔːləz/&lt;/code&gt;) team have built a simple L7 Load Balancer in Rust. Rust was chosen due to it&#39;s performance and safety while provding low level control over the system.&lt;hr/&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;getting-started-with-balrs&#34;&gt;Getting started with Bal.rs&lt;/h2&gt;&#xA;&lt;h3 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://doc.rust-lang.org/book/ch01-01-installation.html&#34;&gt;&lt;strong&gt;Rust compiler&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://doc.rust-lang.org/book/ch01-01-installation.html&#34;&gt;&lt;strong&gt;Cargo package manager&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;building-the-application-locally&#34;&gt;Building the Application Locally&lt;/h3&gt;&#xA;&lt;p&gt;Clone the &lt;a href=&#34;https://github.com/homebrew-ec-foss/bal.rs&#34;&gt;repository&lt;/a&gt; and build the application using &lt;code&gt;cargo&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git clone https://github.com/homebrew-ec-foss/bal.rs&#xA;cd bal.rs&#xA;cargo build&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;For a production-ready build, you can use:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cargo build --release&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;hr/&gt;&#xA;&lt;h3 id=&#34;using-the-application&#34;&gt;Using the Application&lt;/h3&gt;&#xA;&lt;p&gt;After building, the main executable will be located in &lt;code&gt;/target/debug&lt;/code&gt; or &lt;code&gt;/target/release&lt;/code&gt; based on the build command used.&#xA;Navigate to the directory and type&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;Balrs help start&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;in the terminal to get a list of available commands.&lt;/p&gt;&#xA;&lt;p&gt;Alternatively, from the root directory of Bal.rs, you can use:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cargo run help start&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;for the same result.&lt;/p&gt;&#xA;&lt;p&gt;While you can configure the Load Balancer using the command line interface, more configuration options are available through the &lt;code&gt;config.yaml&lt;/code&gt; file and multiple different config files can be created.&lt;br&gt;&#xA;This feature enables the use of various configuration profiles without altering the original configuration. The desired profile can be specified through the CLI.&lt;hr/&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;technical-details&#34;&gt;Technical Details&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This section covers only the &lt;code&gt;lb.rs&lt;/code&gt; file which contains the actual Load Balancing logic.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;There are 3 key components of our Load Balancer:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Listener&lt;/strong&gt;: Listens for incoming HTTP requests.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Routing&lt;/strong&gt;: Does the actual load balancing by forwarding the client request to the servers.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Fault Tolerence&lt;/strong&gt;: Makes sure the Load Balancer handles any faults gracefully.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;listener&#34;&gt;Listener&lt;/h3&gt;&#xA;&lt;p&gt;We have used Rust&#39;s &lt;a href=&#34;https://tokio.rs&#34;&gt;&lt;code&gt;tokio&lt;/code&gt;&lt;/a&gt; crate to handle asynchronous processing and the &lt;a href=&#34;https://hyper.rs&#34;&gt;&lt;code&gt;hyper&lt;/code&gt;&lt;/a&gt; crate for networking. Tokio&#39;s &lt;code&gt;TcpListener&lt;/code&gt; is used to listen for incoming connections&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/aaad34c5cb5e8a09b7d0f83004e3f40298f368ef/Listener.svg&#34; alt=&#34;Listener code snippet&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;In this code snippet, we create a &lt;code&gt;TcpListener&lt;/code&gt; instance to listen for incoming traffic and set it to listen on the address of the Load Balancer.&lt;br&gt;&#xA;If the listener is bound to the Load Balancer successfully, we return the listener object for passing incoming requests to the &lt;code&gt;handle_request&lt;/code&gt; function or else the error encountered is displayed.&lt;hr/&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;routing-the-connections&#34;&gt;Routing the Connections&lt;/h3&gt;&#xA;&lt;p&gt;There are 3 functions dealing with client requests.&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Handling incoming requests: &lt;code&gt;handle_request&lt;/code&gt; function&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/aaad34c5cb5e8a09b7d0f83004e3f40298f368ef/Handle.svg&#34; alt=&#34;Handling an incoming request&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;We lock the &lt;code&gt;LoadBalancer&lt;/code&gt; instance to access the server list and filter out any dead servers.&lt;br&gt;&#xA;The function then tries to pass the request to the &lt;code&gt;get_request&lt;/code&gt; function. If this fails, a message is logged and the loop restarts.&#xA;If there are no available servers, a HTTP 500 response is returned.&lt;/p&gt;&#xA;&lt;p&gt;This is a dynamic fault tolerence system that reroutes an incoming request to a different server if one server is not available.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/aaad34c5cb5e8a09b7d0f83004e3f40298f368ef/FaultFlow.svg&#34; alt=&#34;Flowchart of the fault tolerence system&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Forwarding requests to server: &lt;code&gt;get_request&lt;/code&gt; function&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/aaad34c5cb5e8a09b7d0f83004e3f40298f368ef/Get1.svg&#34; alt=&#34;get_request snippet 1&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Gets indexes of the servers and selects the server to be used according to the specified algorithm.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/aaad34c5cb5e8a09b7d0f83004e3f40298f368ef/Get2.svg&#34; alt=&#34;get_request snippet 2&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;Here, the server URL is constructed. Requests are then forwarded to the server using the &lt;code&gt;send_request&lt;/code&gt; function.&#xA;Along with that, a timer is started to measure server response time. The server response is stored in the &lt;code&gt;data&lt;/code&gt; variable.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/aaad34c5cb5e8a09b7d0f83004e3f40298f368ef/Get3.svg&#34; alt=&#34;get_request snippet 3&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;Here, we handle variants of the server response. If we get a successful response, we return the response data or else mark the corresponding server as dead and return &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Retrieve server response: &lt;code&gt;send_request&lt;/code&gt; function&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/41da9994330f1f96dd34a774291e3cab7af0616a/Send1.svg&#34; alt=&#34;send_request snippet 1&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;Parse the URL from the request and extract host and port from it. The port defaults to 80 if not specified.&#xA;Then format the address to a string for a TCP connection.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.githubusercontent.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/8c6f46419c58e90d5e7d1ef529dffb1290a2dec1/Send2.svg&#34; alt=&#34;send_request snippet 2&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;Establish a TCP connection to the formatted address and wrap it in a tokio IO adapter so that it can be used with &lt;code&gt;hyper&lt;/code&gt;.&lt;br&gt;&#xA;A &lt;code&gt;hyper&lt;/code&gt; client is then initialised using a HTTP/1 handshake.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/83eb442c500db2d84dd55ea8e7dfebf7655d24e6/Send3.svg&#34; alt=&#34;send_request snippet 3&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;The HTTP request is prepared with the given URL and &lt;code&gt;HOST&lt;/code&gt; header and sent using the &lt;code&gt;hyper&lt;/code&gt; client.&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/b8f7b4fd97ad58e8bcb4492d78537c501417c635/Send4.svg&#34; alt=&#34;send_request snippet 4&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;The server response body is then collected in chunks and appended to &lt;code&gt;full_body&lt;/code&gt;. The complete response is then converted to &lt;code&gt;Bytes&lt;/code&gt; and returned.&lt;hr/&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;fault-tolerence&#34;&gt;Fault Tolerence&lt;/h3&gt;&#xA;&lt;p&gt;This is a slightly large piece of code that ensures smooth functioning of the Load Balancer. So, let&#39;s break it down.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/b8f7b4fd97ad58e8bcb4492d78537c501417c635/Health1.svg&#34; alt=&#34;Fault tolerence snippet 1&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Here, we create variables to store the required configuration values and clone a &lt;code&gt;LoadBalancer&lt;/code&gt; instance for further use.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/7a6c8a958bf3039f7d5b106a4a195339accea118/Health2.svg&#34; alt=&#34;Fault tolerence snippet 2&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Spawn an asynchronous tokio task for the health checker and create a vector to hold other tokio tasks.&lt;br&gt;&#xA;Each task corresponds to the health check for each server. This is done to ensure all health checks happen simultaneously.&#xA;We create the required number of tasks using a for loop where &lt;code&gt;len&lt;/code&gt; is the number of servers listed in the Load Balancer&#39;s configuration.&lt;/p&gt;&#xA;&lt;p&gt;Inside the task for each server, we retrieve and update relevant server data.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/66cd47cdeebcc88b0f54b6b81ccbc5834a88f4b0/Health3.svg&#34; alt=&#34;Fault tolerence snippet 3&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Using &lt;code&gt;Instant::now()&lt;/code&gt; and calling &lt;code&gt;.elapsed()&lt;/code&gt;, we record the server response time. We check if the server is responding by sending a &lt;code&gt;GET&lt;/code&gt; request to each server with a set timeout and then update the server response time.&lt;br&gt;&#xA;The subtraction from &lt;code&gt;lb.servers[index].connections&lt;/code&gt; is done as to not count the connection opened by the health checker.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/afb3a0517ad2f3b9278cc3cff7019ce12702e592/Health4.svg&#34; alt=&#34;Fault tolerence snippet 4&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;This &lt;code&gt;match&lt;/code&gt; block is used to handle the result of the HTTP request sent by the health checker.&lt;br&gt;&#xA;If the HTTP request is completed sucessfully, we check if the response is an error code(like 404) or if the maximum connections limit is exceeded. This leads to marking of a server as dead and will not be used by the Load Balancer until it is checked again and marked as alive by the health checker.&lt;/p&gt;&#xA;&lt;p&gt;This marks the end of the Health Checker. After this, all the server tasks are awaited on to be periodically executed.&lt;br&gt;&#xA;Here is a simple flowchart of how the health checking process works:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://gist.github.com/Prana-vvb/ff43110750c6fdfc21637e85debbf30a/raw/afb3a0517ad2f3b9278cc3cff7019ce12702e592/HealthCheckFlow.svg&#34; alt=&#34;Health check flow&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Health checker reports as displayed in the terminal:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdGkyeTM3eHAxdTB0aDVlcmwzN3Mwd3RxdnJ0N2IzYzl1NDlyN2JlYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/psU5B2R73mbf6YlMha/giphy.gif&#34; alt=&#34;Health check reports&#34;&gt;&lt;/p&gt;&#xA;&lt;hr/&gt;&#xA;&lt;h2 id=&#34;benchmarks&#34;&gt;Benchmarks&lt;/h2&gt;&#xA;&lt;p&gt;We conducted several tests at different request rates per second (RPS).&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/user-attachments/assets/b17721e1-ec30-4f62-939a-c60ae04040d6&#34; alt=&#34;20,000 RPS&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p align = &#34;center&#34;&gt; Throughput VS Time at 20,000 RPS &lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/user-attachments/assets/661479e2-42b9-4772-a0ae-d67e06506637&#34; alt=&#34;25,000 RPS&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p align = &#34;center&#34;&gt; Throughput VS Time at 25,000 RPS &lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://gist.github.com/user-attachments/assets/e0efbf0d-b752-4048-bfd0-18aa34c59d86&#34; alt=&#34;27,000 RPS&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p align = &#34;center&#34;&gt; Throughput VS Time at 27,000 RPS &lt;/p&gt;&#xA;&lt;p&gt;The tests had to be stopped here due to our hardware limitations but the trends we observed show us that the Bal.rs Load Balancer can handle much higher loads.&lt;/p&gt;&#xA;&lt;hr/&gt;&#xA;&lt;h2 id=&#34;next-steps-and-resources&#34;&gt;Next Steps and Resources&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.rust-lang.org/&#34;&gt;Rust essentials&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;tokio.rs/tokio/tutorial/async&#34;&gt;Asynchronous Programming in Rust using Tokio&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://samwho.dev/load-balancing/&#34;&gt;Basics of Load Balancing Algorithms&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/another-rust-load-balancer/another-rust-load-balancer&#34;&gt;Another Rust Load Balancer&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Pranav V Bhat</title>
      <link>https://prana-vvb.github.io/index.html</link>
      <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
      <author>Pranav V Bhat</author>
      <guid>https://prana-vvb.github.io/index.html</guid>
      <description></description>
    </item>
  </channel>
</rss>
