<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mote</title>
    <description>The latest articles on DEV Community by mote (@motedb).</description>
    <link>https://dev.to/motedb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3796371%2Fa075a83c-f1f4-41e4-ab40-7b42a4fe6565.png</url>
      <title>DEV Community: mote</title>
      <link>https://dev.to/motedb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/motedb"/>
    <language>en</language>
    <item>
      <title>I Tried 4 Async Runtimes on a Raspberry Pi — Only One Didn't Make Me Want to Throw It Out the Window</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Fri, 17 Apr 2026 12:08:53 +0000</pubDate>
      <link>https://dev.to/motedb/i-tried-4-async-runtimes-on-a-raspberry-pi-only-one-didnt-make-me-want-to-throw-it-out-the-window-2p77</link>
      <guid>https://dev.to/motedb/i-tried-4-async-runtimes-on-a-raspberry-pi-only-one-didnt-make-me-want-to-throw-it-out-the-window-2p77</guid>
      <description>&lt;p&gt;Last month I spent three weeks doing something that sounds simple: making an HTTP client work reliably on a Raspberry Pi 4 running a custom Rust service. The service needed to periodically sync sensor data to a cloud endpoint while also handling local database writes. Nothing fancy — maybe 200 lines of logic.&lt;/p&gt;

&lt;p&gt;It took me 2,847 lines of code, 4 different async runtimes, and one very close relationship with my debugger to get it working.&lt;/p&gt;

&lt;p&gt;Here's what actually happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: Tokio — The Standard Choice
&lt;/h2&gt;

&lt;p&gt;Everyone says "just use Tokio." So I did.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;tokio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"full"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my MacBook, it compiled in 12 seconds and ran perfectly. On the Raspberry Pi? Cross-compilation worked, but the binary was 8.3 MB. For a service that was supposed to be lean and embeddable, that felt wrong.&lt;/p&gt;

&lt;p&gt;But the real problem was memory. Under load (simulating 50 concurrent sensor readings + database writes), the RSS crept up to 45 MB. On a Pi with 4 GB of RAM running other services, that's not catastrophic, but it's not great either.&lt;/p&gt;

&lt;p&gt;The worst part: I needed a specific timer implementation that played nice with the Pi's real-time clock, and Tokio's &lt;code&gt;time&lt;/code&gt; module had a subtle drift that accumulated over 24 hours. We're talking milliseconds becoming seconds. When you're timestamping sensor events, that matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&lt;/strong&gt;: Works, but it's like using a sledgehammer to hang a picture frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2: async-std — The Alternative
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;async-std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;async-std felt more ergonomic. The API is closer to what you'd expect from Rust's standard library. File I/O felt more natural. The binary was slightly smaller (7.1 MB).&lt;/p&gt;

&lt;p&gt;But then I hit the wall: async-std's networking stack had a bug with DNS resolution on ARM64 that caused a hang every ~6 hours. I found an open issue from 18 months ago with 47 upvotes and no resolution.&lt;/p&gt;

&lt;p&gt;I tried patching it myself. That's when I realized I'd rather rewrite the whole thing than debug someone else's async DNS resolver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&lt;/strong&gt;: Promising, but production-unsafe on ARM for anything long-running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 3: smol — The Minimalist
&lt;/h2&gt;

&lt;p&gt;smol is beautiful in its simplicity. Small binary (4.2 MB), low memory footprint (22 MB RSS under the same load), and the &lt;code&gt;async-io&lt;/code&gt; crate underneath is surprisingly robust.&lt;/p&gt;

&lt;p&gt;The problem? Dependency hell. smol uses &lt;code&gt;blocking&lt;/code&gt; for sync-to-async bridging, and our database library (SQLite, via &lt;code&gt;rusqlite&lt;/code&gt;) kept deadlocking in subtle ways when called from multiple async tasks. The &lt;code&gt;blocking&lt;/code&gt; crate's thread pool would exhaust, and then... silence. No error, no panic. Just a service that stopped responding.&lt;/p&gt;

&lt;p&gt;I spent two days adding timeout wrappers around every database call before I gave up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&lt;/strong&gt;: Perfect if you control every dependency. We didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 4: embassy — The Embedded Champion
&lt;/h2&gt;

&lt;p&gt;This is where things got interesting. Embassy isn't really an async runtime in the traditional sense — it's an async framework designed for &lt;code&gt;no_std&lt;/code&gt; embedded systems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;embassy-executor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.6"&lt;/span&gt;
&lt;span class="py"&gt;embassy-time&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3"&lt;/span&gt;
&lt;span class="py"&gt;embassy-net&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.4"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait, can you even run Embassy on a Raspberry Pi? Technically, Embassy targets microcontrollers (STM32, nRF, ESP32). But the networking and I/O abstractions work on Linux too, thanks to &lt;code&gt;embassy-net&lt;/code&gt;'s socket backend.&lt;/p&gt;

&lt;p&gt;The binary was 2.8 MB. Memory usage stayed flat at 15 MB under load. The timer was rock-solid (it uses the hardware timer abstraction, and on Linux it maps to the appropriate clock source).&lt;/p&gt;

&lt;p&gt;There was one catch: the learning curve. Embassy's model is fundamentally different. You don't spawn tasks like Tokio — you use &lt;code&gt;Spawner&lt;/code&gt; and &lt;code&gt;embassy_executor::main&lt;/code&gt;. The networking API expects you to think in terms of &lt;code&gt;TcpSocket&lt;/code&gt; objects rather than streams. It took me a full day to restructure the code.&lt;/p&gt;

&lt;p&gt;But once it compiled? It just worked. No memory leaks, no timer drift, no DNS hangs, no thread pool deadlocks. 72 hours of continuous testing without a single hiccup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[embassy_executor::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spawner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Spawner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;embassy_net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;net_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;spawner&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sensor_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="nf"&gt;.ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;spawner&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sync_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Hard Lesson
&lt;/h2&gt;

&lt;p&gt;Here's what I wish someone had told me before I started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Binary size matters on edge devices.&lt;/strong&gt; 8 MB vs 2.8 MB isn't just a number — it's the difference between fitting in a constrained update partition and failing deployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Timer accuracy is a silent killer.&lt;/strong&gt; Most people don't notice until they're correlating events across devices and the timestamps don't line up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;"Standard" runtimes aren't optimized for your hardware.&lt;/strong&gt; Tokio is amazing for servers. It's not optimized for a $35 ARM board with eMMC storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The ecosystem lock-in is real.&lt;/strong&gt; Your choice of async runtime determines which libraries you can use, how you handle errors, and what your deployment looks like.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I'm Using Now
&lt;/h2&gt;

&lt;p&gt;For our robotics work at moteDB, we ended up with a hybrid: Embassy for the embedded layer (sensor I/O, real-time control), and a minimal synchronous Rust core for database operations. We intentionally avoided async in the database layer — synchronous code with a dedicated thread is simpler, more debuggable, and has predictable performance characteristics.&lt;/p&gt;

&lt;p&gt;Sometimes the best async architecture includes knowing when NOT to be async.&lt;/p&gt;

&lt;p&gt;Has anyone else run into the async runtime choice problem on constrained hardware? I'm curious if there are other options I missed — especially anything that bridges the gap between Tokio's ecosystem and Embassy's efficiency.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Spent 3 Months Tuning a Tokio Runtime for My Robot - Here's What No Tutorial Tells You</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:37:50 +0000</pubDate>
      <link>https://dev.to/motedb/i-spent-3-months-tuning-a-tokio-runtime-for-my-robot-heres-what-no-tutorial-tells-you-bdj</link>
      <guid>https://dev.to/motedb/i-spent-3-months-tuning-a-tokio-runtime-for-my-robot-heres-what-no-tutorial-tells-you-bdj</guid>
      <description>&lt;p&gt;Last November, my robot arm started dropping sensor frames at exactly 47ms intervals. Not randomly - exactly 47ms, like clockwork. It would read joint angles perfectly for a while, then miss a window, then recover. The anomaly detector we'd wired into the control loop kept triggering false positives. My teammate Rui and I spent two full weeks convinced the CAN bus driver was broken.&lt;/p&gt;

&lt;p&gt;It wasn't the driver.&lt;/p&gt;

&lt;p&gt;It was &lt;code&gt;#[tokio::main]&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;We were building an AI-driven robot arm that does pick-and-place with semantic understanding. The control loop needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1ms cycle time&lt;/strong&gt; for joint position updates
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10ms window&lt;/strong&gt; to fuse sensor data before inference
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background persistence&lt;/strong&gt; - log everything to a local database so we can replay sessions offline
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack was reasonable: Rust, Tokio for async, a custom message bus, moteDB for embedded storage (vectors + time-series + structured state in one engine). We used &lt;code&gt;#[tokio::main]&lt;/code&gt; because that's what every tutorial shows, with a thread pool spawned for the heavy inference work.&lt;/p&gt;

&lt;p&gt;It worked great on my laptop. It fell apart on the robot.&lt;/p&gt;




&lt;h2&gt;
  
  
  What #[tokio::main] Actually Does (And Doesn't Do)
&lt;/h2&gt;

&lt;p&gt;Here is the thing nobody explains in the "Getting Started with Tokio" guides: &lt;code&gt;#[tokio::main]&lt;/code&gt; spins up a multi-threaded runtime with a number of worker threads equal to the number of logical CPU cores. On a modern dev machine that's 8-16. On a Raspberry Pi 5? 4 cores - and two of them are already pressured by the camera pipeline and the neural inference engine.&lt;/p&gt;

&lt;p&gt;The bigger problem: Tokio's work-stealing scheduler doesn't know anything about real-time priorities. It will cheerfully preempt your 1ms control loop task to service a database flush, a log write, or a DNS resolution that some library decided to make async under the hood.&lt;/p&gt;

&lt;p&gt;That 47ms drop? The Tokio scheduler was occasionally parking our sensor polling task while flushing a batch write to moteDB. The flush was async, perfectly polite, and completely invisible in any standard profiling tool because it showed up as I/O wait rather than CPU time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Surgical Runtime Configuration
&lt;/h2&gt;

&lt;p&gt;Instead of &lt;code&gt;#[tokio::main]&lt;/code&gt;, we switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;control_rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_multi_thread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.worker_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"control-loop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ThreadPriority&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// with tokio-runtime-extensions&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;io_rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_multi_thread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.worker_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-io"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;control_rt&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;run_control_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;io_rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;run_support_tasks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two runtimes. The control loop never shares a thread pool with storage I/O or inference scheduling. After this change, our 47ms drops disappeared entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Things I Wish Someone Had Told Me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;spawn_blocking&lt;/code&gt; is not free&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every call to &lt;code&gt;spawn_blocking&lt;/code&gt; steals a thread from a shared blocking thread pool (default: 512 threads). If you're calling it in a tight loop for sensor serialization, you will exhaust the pool under load. We switched to dedicated &lt;code&gt;std::thread::spawn&lt;/code&gt; for our serialization hot path and kept &lt;code&gt;spawn_blocking&lt;/code&gt; only for true one-offs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Async mutex is slower than you think at high frequency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tokio::sync::Mutex&lt;/code&gt; parks the task and hands control to the scheduler when it contends. At 1ms cycle time, this is catastrophic. For shared state between the control loop and the storage layer, we used &lt;code&gt;std::sync::Mutex&lt;/code&gt; - a blocking primitive - because the lock hold time was microseconds and the task switch overhead of the async version was larger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The database write path must not block the runtime&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where moteDB's design helped us: writes are append-only to a WAL first (sub-microsecond), with the actual B-tree / vector index update deferred to the background runtime. If your embedded database does synchronous index updates on every write, you will feel it in your control loop latency. The write path and the read path need different scheduling contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Pattern
&lt;/h2&gt;

&lt;p&gt;Embedded AI systems are not web servers. On a web server, a 50ms hiccup on one request is invisible to other requests. On a robot, a 50ms hiccup in your control loop is a dropped object, a wrong turn, or a crash.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;#[tokio::main]&lt;/code&gt; default was designed for web services where fairness across tasks is the right trade-off. For real-time embedded work, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: critical tasks on dedicated runtimes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority&lt;/strong&gt;: OS-level thread priorities for the control loop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-blocking storage&lt;/strong&gt;: a database whose write path does not block the scheduler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ended up with a three-layer architecture: hard real-time control loop, soft real-time sensor fusion + inference, and best-effort persistence and telemetry. Each layer has its own Tokio runtime, and they communicate via lock-free channels (tokio::sync::mpsc with bounded capacity).&lt;/p&gt;

&lt;p&gt;The 47ms drops are gone. We have been running stable for three months.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[tokio::main]&lt;/code&gt; is fine for most things. For embedded real-time, it is a footgun.&lt;/li&gt;
&lt;li&gt;Use separate &lt;code&gt;tokio::runtime::Builder&lt;/code&gt; instances to isolate critical paths.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;std::sync::Mutex&lt;/code&gt; beats &lt;code&gt;tokio::sync::Mutex&lt;/code&gt; when lock hold time is microseconds.&lt;/li&gt;
&lt;li&gt;Make sure your storage layer (whatever it is) has non-blocking write semantics.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Has anyone else hit scheduler interference issues in embedded Rust? I'm curious whether the community has converged on better patterns here - or whether this is still a "figure it out yourself" problem.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>iot</category>
      <category>performance</category>
      <category>rust</category>
    </item>
    <item>
      <title>I Spent 3 Months Tuning a Tokio Runtime for My Robot — Here's What No Tutorial Tells You</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:31:04 +0000</pubDate>
      <link>https://dev.to/motedb/i-spent-3-months-tuning-a-tokio-runtime-for-my-robot-heres-what-no-tutorial-tells-you-4oj7</link>
      <guid>https://dev.to/motedb/i-spent-3-months-tuning-a-tokio-runtime-for-my-robot-heres-what-no-tutorial-tells-you-4oj7</guid>
      <description>&lt;p&gt;I Spent 3 Months Tuning a Tokio Runtime for My Robot — Here's What No Tutorial Tells You&lt;/p&gt;

&lt;p&gt;Last November, my robot arm started dropping sensor frames at exactly 47ms intervals. Not randomly — exactly 47ms, like clockwork. It would read joint angles perfectly for a while, then miss a window, then recover. The anomaly detector we'd wired into the control loop kept triggering false positives. My teammate Rui and I spent two full weeks convinced the CAN bus driver was broken.&lt;/p&gt;

&lt;p&gt;It wasn't the driver.&lt;/p&gt;

&lt;p&gt;It was &lt;code&gt;#[tokio::main]&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;We were building an AI-driven robot arm that does pick-and-place with semantic understanding. The control loop needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1ms cycle time&lt;/strong&gt; for joint position updates
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10ms window&lt;/strong&gt; to fuse sensor data before inference
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background persistence&lt;/strong&gt; — log everything to a local database so we can replay sessions offline
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack was reasonable: Rust, Tokio for async, a custom message bus, moteDB for embedded storage (vectors + time-series + structured state in one engine). We used &lt;code&gt;#[tokio::main]&lt;/code&gt; because that's what every tutorial shows, with a thread pool spawned for the heavy inference work.&lt;/p&gt;

&lt;p&gt;It worked great on my laptop. It fell apart on the robot.&lt;/p&gt;




&lt;h2&gt;
  
  
  What #[tokio::main] Actually Does (And Doesn't Do)
&lt;/h2&gt;

&lt;p&gt;Here is the thing nobody explains in the "Getting Started with Tokio" guides: &lt;code&gt;#[tokio::main]&lt;/code&gt; spins up a multi-threaded runtime with a number of worker threads equal to the number of logical CPU cores. On a modern dev machine that's 8-16. On a Raspberry Pi 5? 4 cores — and two of them are already pressured by the camera pipeline and the neural inference engine.&lt;/p&gt;

&lt;p&gt;The bigger problem: Tokio's work-stealing scheduler doesn't know anything about real-time priorities. It will cheerfully preempt your 1ms control loop task to service a database flush, a log write, or a DNS resolution that some library decided to make async under the hood.&lt;/p&gt;

&lt;p&gt;That 47ms drop? The Tokio scheduler was occasionally parking our sensor polling task while flushing a batch write to moteDB. The flush was async, perfectly polite, and completely invisible in any standard profiling tool because it showed up as I/O wait rather than CPU time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Surgical Runtime Configuration
&lt;/h2&gt;

&lt;p&gt;Instead of &lt;code&gt;#[tokio::main]&lt;/code&gt;, we switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Dedicated 1-thread runtime for the control loop&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;control_rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_multi_thread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.worker_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"control-loop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ThreadPriority&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// with tokio-runtime-extensions&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Separate runtime for background I/O (storage, logging, telemetry)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;io_rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_multi_thread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.worker_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-io"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Spawn control loop on dedicated runtime&lt;/span&gt;
    &lt;span class="n"&gt;control_rt&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;run_control_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Spawn storage + inference on IO runtime&lt;/span&gt;
    &lt;span class="n"&gt;io_rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;run_support_tasks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two runtimes. The control loop never shares a thread pool with storage I/O or inference scheduling. After this change, our 47ms drops disappeared entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Things I Wish Someone Had Told Me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;spawn_blocking&lt;/code&gt; is not free&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every call to &lt;code&gt;spawn_blocking&lt;/code&gt; steals a thread from a shared blocking thread pool (default: 512 threads). If you're calling it in a tight loop for sensor serialization, you will exhaust the pool under load. We switched to dedicated &lt;code&gt;std::thread::spawn&lt;/code&gt; for our serialization hot path and kept &lt;code&gt;spawn_blocking&lt;/code&gt; only for true one-offs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Async mutex is slower than you think at high frequency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tokio::sync::Mutex&lt;/code&gt; parks the task and hands control to the scheduler when it contends. At 1ms cycle time, this is catastrophic. For shared state between the control loop and the storage layer, we used &lt;code&gt;std::sync::Mutex&lt;/code&gt; — a blocking primitive — because the lock hold time was microseconds and the task switch overhead of the async version was larger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The database write path must not block the runtime&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where moteDB's design helped us: writes are append-only to a WAL first (sub-microsecond), with the actual B-tree / vector index update deferred to the background runtime. If your embedded database does synchronous index updates on every write, you will feel it in your control loop latency. The write path and the read path need different scheduling contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Pattern
&lt;/h2&gt;

&lt;p&gt;Embedded AI systems are not web servers. On a web server, a 50ms hiccup on one request is invisible to other requests. On a robot, a 50ms hiccup in your control loop is a dropped object, a wrong turn, or a crash.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;#[tokio::main]&lt;/code&gt; default was designed for web services where fairness across tasks is the right trade-off. For real-time embedded work, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: critical tasks on dedicated runtimes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority&lt;/strong&gt;: OS-level thread priorities for the control loop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-blocking storage&lt;/strong&gt;: a database whose write path does not block the scheduler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ended up with a three-layer architecture: hard real-time control loop, soft real-time sensor fusion + inference, and best-effort persistence and telemetry. Each layer has its own Tokio runtime, and they communicate via lock-free channels (tokio::sync::mpsc with bounded capacity).&lt;/p&gt;

&lt;p&gt;The 47ms drops are gone. We have been running stable for three months.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[tokio::main]&lt;/code&gt; is fine for most things. For embedded real-time, it is a footgun.&lt;/li&gt;
&lt;li&gt;Use separate &lt;code&gt;tokio::runtime::Builder&lt;/code&gt; instances to isolate critical paths.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;std::sync::Mutex&lt;/code&gt; beats &lt;code&gt;tokio::sync::Mutex&lt;/code&gt; when lock hold time is microseconds.&lt;/li&gt;
&lt;li&gt;Make sure your storage layer (whatever it is) has non-blocking write semantics.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Has anyone else hit scheduler interference issues in embedded Rust? I'm curious whether the community has converged on better patterns here — or whether this is still a "figure it out yourself" problem.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>performance</category>
      <category>programming</category>
      <category>rust</category>
    </item>
    <item>
      <title>I Stopped Treating My AI Agent's Memory Like a Log File</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Thu, 16 Apr 2026 11:20:00 +0000</pubDate>
      <link>https://dev.to/motedb/i-stopped-treating-my-ai-agents-memory-like-a-log-file-3fj1</link>
      <guid>https://dev.to/motedb/i-stopped-treating-my-ai-agents-memory-like-a-log-file-3fj1</guid>
      <description>&lt;p&gt;Last year, I spent two weeks debugging why my robot kept repeating the same mistake.&lt;/p&gt;

&lt;p&gt;Not a code bug. Not a hardware failure. The robot &lt;em&gt;knew&lt;/em&gt; what it had done wrong the last time. I could see it in the logs. It had stored the error. It just didn't... use that knowledge when the same situation came up again.&lt;/p&gt;

&lt;p&gt;That's when I realized I had been solving the wrong problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Log File Mental Model
&lt;/h2&gt;

&lt;p&gt;Most agent memory systems I've seen follow the same pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent does something&lt;/li&gt;
&lt;li&gt;Store a text description of what happened&lt;/li&gt;
&lt;li&gt;Later, embed it and retrieve it with semantic search&lt;/li&gt;
&lt;li&gt;Inject retrieved context into the next prompt&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's elegant. It works well for conversational AI — the kind that lives in a chat window and helps you write emails.&lt;/p&gt;

&lt;p&gt;But I'm building robots. And the log-file model breaks in ways that aren't obvious until your robot crashes into the same wall for the third time.&lt;/p&gt;

&lt;p&gt;Here's why.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Goes Wrong on the Edge
&lt;/h2&gt;

&lt;p&gt;A robot's environment produces data that doesn't fit in a text string.&lt;/p&gt;

&lt;p&gt;When my drone hit an airflow problem near a building edge, what it experienced was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 47ms IMU reading spike (accelerometer Z-axis: +3.8g)&lt;/li&gt;
&lt;li&gt;A camera frame showing a glass surface at 0.4m&lt;/li&gt;
&lt;li&gt;A motor throttle log&lt;/li&gt;
&lt;li&gt;A GPS coordinate&lt;/li&gt;
&lt;li&gt;A vibration frequency pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I stored a text note: &lt;em&gt;"Building edge caused unexpected turbulence, compensated with throttle adjustment."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two weeks later, same building edge. The agent retrieved the text note. The text said "throttle adjustment." The agent adjusted throttle. It still struggled, because the &lt;em&gt;actual&lt;/em&gt; recovery wasn't about throttle — it was about yaw correction combined with a specific altitude hold. The text summary had lost the operational precision.&lt;/p&gt;

&lt;p&gt;This is the binding problem. The memory exists. The retrieval works. But the stored representation can't carry the real-world nuance.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Memory Actually Needs to Do (For Agents That Act)
&lt;/h2&gt;

&lt;p&gt;After a lot of iteration, I've landed on a different model:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory for acting agents is not a recall system. It's a structured experience index.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-modal by default.&lt;/strong&gt; An experience in the physical world involves sensor readings, visual data, timing, and spatial context — not just a text description of what happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queryable by context, not just keywords.&lt;/strong&gt; "What did I do last time the accelerometer reading was above 3g near a glass surface?" is a different query than "what happened near buildings?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight enough to run on the device.&lt;/strong&gt; A Raspberry Pi can't afford 500ms vector search round-trips mid-flight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent across power cycles.&lt;/strong&gt; Edge AI devices reboot. Memory has to survive that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of the existing options — SQLite, Redis, Chroma, even small embedded vector stores — were designed for this combination.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built Instead
&lt;/h2&gt;

&lt;p&gt;I spent about four months building &lt;a href="https://github.com/motedb/motedb" rel="noopener noreferrer"&gt;moteDB&lt;/a&gt;, a Rust-native embedded database specifically for this use case.&lt;/p&gt;

&lt;p&gt;The core design decisions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Multi-modal storage as a first-class concept.&lt;/strong&gt;&lt;br&gt;
A "record" can contain a vector, a binary blob, structured fields, and a timestamp — not an ORM abstraction on top of text columns. When the drone stores an experience, it stores all modalities together, not in separate tables that need joining later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. No runtime dependencies.&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;cargo add motedb&lt;/code&gt; — that's it. It runs in the same process as your agent. No daemon, no network round-trip, no container to manage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Designed for embedded constraints.&lt;/strong&gt;&lt;br&gt;
Memory budget is explicit. Old records get evicted by policy. It doesn't assume you have 32GB of RAM or SSD storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Query by structured context.&lt;/strong&gt;&lt;br&gt;
You can ask: "Give me the 3 most similar past experiences where sensor reading X was above threshold Y and the outcome was Z." That's a spatial + vector + structured query — all in one call.&lt;/p&gt;




&lt;h2&gt;
  
  
  Does It Actually Help?
&lt;/h2&gt;

&lt;p&gt;The real test: same building edge, three months after I started using moteDB.&lt;/p&gt;

&lt;p&gt;The drone recalled the actual IMU trace from the previous incident — not a text summary of it. Its flight controller could compare the current sensor pattern directly to the stored pattern. Yaw correction happened 400ms earlier than it had previously.&lt;/p&gt;

&lt;p&gt;No crash.&lt;/p&gt;

&lt;p&gt;More importantly: I didn't have to rewrite the memory system every time the robot encountered a new type of sensor data. The schema is flexible. Text, vectors, binary — it stores what the agent actually experiences.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Broader Point
&lt;/h2&gt;

&lt;p&gt;The "agent memory = semantic text search" assumption works for chat-based agents because their world &lt;em&gt;is&lt;/em&gt; text. But if you're building agents that act in physical or structured-data environments, the mismatch compounds fast.&lt;/p&gt;

&lt;p&gt;The memory system needs to match the modality of the environment, not just the modality of the LLM interface.&lt;/p&gt;

&lt;p&gt;I'm still iterating on this. The drone problem is mostly solved. The harder one is multi-agent memory sharing — when two robots have different experiences of the same environment, how do you merge those sensibly?&lt;/p&gt;

&lt;p&gt;Working on it.&lt;/p&gt;




&lt;p&gt;If you're building agents that operate in physical or data-heavy environments, I'd be curious what memory architecture you're using. Are you hitting the same log-file limitation, or have you found something that works well?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cargo add motedb&lt;/code&gt; if you want to experiment. GitHub: &lt;a href="https://github.com/motedb/motedb" rel="noopener noreferrer"&gt;https://github.com/motedb/motedb&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>database</category>
      <category>embedded</category>
    </item>
    <item>
      <title>I Spent 3 Months Tuning a Tokio Runtime for My Robot — Here's What No Tutorial Tells You</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Mon, 13 Apr 2026 10:20:41 +0000</pubDate>
      <link>https://dev.to/motedb/i-spent-3-months-tuning-a-tokio-runtime-for-my-robot-heres-what-no-tutorial-tells-you-5fj6</link>
      <guid>https://dev.to/motedb/i-spent-3-months-tuning-a-tokio-runtime-for-my-robot-heres-what-no-tutorial-tells-you-5fj6</guid>
      <description>&lt;p&gt;I Spent 3 Months Tuning a Tokio Runtime for My Robot — Here's What No Tutorial Tells You&lt;/p&gt;

&lt;p&gt;Last November, my robot arm started dropping sensor frames at exactly 47ms intervals. Not randomly — exactly 47ms, like clockwork. It would read joint angles perfectly for a while, then miss a window, then recover. The anomaly detector we'd wired into the control loop kept triggering false positives. My teammate Rui and I spent two full weeks convinced the CAN bus driver was broken.&lt;/p&gt;

&lt;p&gt;It wasn't the driver.&lt;/p&gt;

&lt;p&gt;It was &lt;code&gt;#[tokio::main]&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;We were building an AI-driven robot arm that does pick-and-place with semantic understanding. The control loop needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1ms cycle time&lt;/strong&gt; for joint position updates
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10ms window&lt;/strong&gt; to fuse sensor data before inference
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background persistence&lt;/strong&gt; — log everything to a local database so we can replay sessions offline
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack was reasonable: Rust, Tokio for async, a custom message bus, moteDB for embedded storage (vectors + time-series + structured state in one engine). We used &lt;code&gt;#[tokio::main]&lt;/code&gt; because that's what every tutorial shows, with a thread pool spawned for the heavy inference work.&lt;/p&gt;

&lt;p&gt;It worked great on my laptop. It fell apart on the robot.&lt;/p&gt;




&lt;h2&gt;
  
  
  What #[tokio::main] Actually Does (And Doesn't Do)
&lt;/h2&gt;

&lt;p&gt;Here is the thing nobody explains in the "Getting Started with Tokio" guides: &lt;code&gt;#[tokio::main]&lt;/code&gt; spins up a multi-threaded runtime with a number of worker threads equal to the number of logical CPU cores. On a modern dev machine that's 8-16. On a Raspberry Pi 5? 4 cores — and two of them are already pressured by the camera pipeline and the neural inference engine.&lt;/p&gt;

&lt;p&gt;The bigger problem: Tokio's work-stealing scheduler doesn't know anything about real-time priorities. It will cheerfully preempt your 1ms control loop task to service a database flush, a log write, or a DNS resolution that some library decided to make async under the hood.&lt;/p&gt;

&lt;p&gt;That 47ms drop? The Tokio scheduler was occasionally parking our sensor polling task while flushing a batch write to moteDB. The flush was async, perfectly polite, and completely invisible in any standard profiling tool because it showed up as I/O wait rather than CPU time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Surgical Runtime Configuration
&lt;/h2&gt;

&lt;p&gt;Instead of &lt;code&gt;#[tokio::main]&lt;/code&gt;, we switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Dedicated 1-thread runtime for the control loop&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;control_rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_multi_thread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.worker_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"control-loop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_priority&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ThreadPriority&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// with tokio-runtime-extensions&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Separate runtime for background I/O (storage, logging, telemetry)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;io_rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_multi_thread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.worker_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.thread_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-io"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Spawn control loop on dedicated runtime&lt;/span&gt;
    &lt;span class="n"&gt;control_rt&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;run_control_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Spawn storage + inference on IO runtime&lt;/span&gt;
    &lt;span class="n"&gt;io_rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;run_support_tasks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two runtimes. The control loop never shares a thread pool with storage I/O or inference scheduling. After this change, our 47ms drops disappeared entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Things I Wish Someone Had Told Me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;spawn_blocking&lt;/code&gt; is not free&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every call to &lt;code&gt;spawn_blocking&lt;/code&gt; steals a thread from a shared blocking thread pool (default: 512 threads). If you're calling it in a tight loop for sensor serialization, you will exhaust the pool under load. We switched to dedicated &lt;code&gt;std::thread::spawn&lt;/code&gt; for our serialization hot path and kept &lt;code&gt;spawn_blocking&lt;/code&gt; only for true one-offs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Async mutex is slower than you think at high frequency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tokio::sync::Mutex&lt;/code&gt; parks the task and hands control to the scheduler when it contends. At 1ms cycle time, this is catastrophic. For shared state between the control loop and the storage layer, we used &lt;code&gt;std::sync::Mutex&lt;/code&gt; — a blocking primitive — because the lock hold time was microseconds and the task switch overhead of the async version was larger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The database write path must not block the runtime&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where moteDB's design helped us: writes are append-only to a WAL first (sub-microsecond), with the actual B-tree / vector index update deferred to the background runtime. If your embedded database does synchronous index updates on every write, you will feel it in your control loop latency. The write path and the read path need different scheduling contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Pattern
&lt;/h2&gt;

&lt;p&gt;Embedded AI systems are not web servers. On a web server, a 50ms hiccup on one request is invisible to other requests. On a robot, a 50ms hiccup in your control loop is a dropped object, a wrong turn, or a crash.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;#[tokio::main]&lt;/code&gt; default was designed for web services where fairness across tasks is the right trade-off. For real-time embedded work, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: critical tasks on dedicated runtimes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority&lt;/strong&gt;: OS-level thread priorities for the control loop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-blocking storage&lt;/strong&gt;: a database whose write path does not block the scheduler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ended up with a three-layer architecture: hard real-time control loop, soft real-time sensor fusion + inference, and best-effort persistence and telemetry. Each layer has its own Tokio runtime, and they communicate via lock-free channels (tokio::sync::mpsc with bounded capacity).&lt;/p&gt;

&lt;p&gt;The 47ms drops are gone. We have been running stable for three months.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#[tokio::main]&lt;/code&gt; is fine for most things. For embedded real-time, it is a footgun.&lt;/li&gt;
&lt;li&gt;Use separate &lt;code&gt;tokio::runtime::Builder&lt;/code&gt; instances to isolate critical paths.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;std::sync::Mutex&lt;/code&gt; beats &lt;code&gt;tokio::sync::Mutex&lt;/code&gt; when lock hold time is microseconds.&lt;/li&gt;
&lt;li&gt;Make sure your storage layer (whatever it is) has non-blocking write semantics.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Has anyone else hit scheduler interference issues in embedded Rust? I'm curious whether the community has converged on better patterns here — or whether this is still a "figure it out yourself" problem.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Replaced SQLite with a Rust Database in My AI Robot — Here's What Happened</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Mon, 13 Apr 2026 01:36:15 +0000</pubDate>
      <link>https://dev.to/motedb/i-replaced-sqlite-with-a-rust-database-in-my-ai-robot-heres-what-happened-1n6k</link>
      <guid>https://dev.to/motedb/i-replaced-sqlite-with-a-rust-database-in-my-ai-robot-heres-what-happened-1n6k</guid>
      <description>&lt;p&gt;I used SQLite for everything.&lt;/p&gt;

&lt;p&gt;For years, it was my default answer to "where do I store stuff." Config files, sensor logs, user data, even model outputs — SQLite handled it. When I started building an AI-powered robot that needed on-device memory, the choice felt obvious: embed SQLite, done.&lt;/p&gt;

&lt;p&gt;Three weeks later, the robot's memory was a mess of incompatible schemas, I was writing custom serialization code for every data type, and I'd accumulated roughly 400 lines of glue code just to store and retrieve things that weren't text or numbers. Image embeddings. Audio fingerprints. Sensor state vectors.&lt;/p&gt;

&lt;p&gt;That's when I started questioning whether SQLite was actually the right tool for this problem, or just the familiar one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI Agents Actually Need to Remember
&lt;/h2&gt;

&lt;p&gt;Here's the thing about robot memory that isn't obvious until you're building it: AI agents don't remember rows of data. They remember &lt;em&gt;moments&lt;/em&gt; — multimodal snapshots of state.&lt;/p&gt;

&lt;p&gt;A moment might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 512-dimensional face embedding from the camera&lt;/li&gt;
&lt;li&gt;A timestamp&lt;/li&gt;
&lt;li&gt;An associated audio clip (4KB PCM)&lt;/li&gt;
&lt;li&gt;A confidence score&lt;/li&gt;
&lt;li&gt;A label ("this is the owner")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In SQLite, storing this requires: a TEXT field for the embedding (serialized JSON or base64), a BLOB for audio, two REAL fields, and a TEXT field. Then on retrieval, you deserialize everything back. For one record, fine. For 10,000 records when you need to find the 5 most similar faces to the one in the current frame? Now you're loading blobs you don't need, deserializing vectors you'll immediately re-encode for comparison, and doing similarity math in Python one row at a time.&lt;/p&gt;

&lt;p&gt;I was making SQLite solve a problem it wasn't designed for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Performance Problem
&lt;/h2&gt;

&lt;p&gt;Let me share the actual numbers from my setup (Raspberry Pi 5, 8GB RAM):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQLite approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store a face embedding (512-dim float32 array): ~2.1ms avg&lt;/li&gt;
&lt;li&gt;Find top-5 similar faces in corpus of 1,000 records: ~340ms (full scan + Python cosine similarity)&lt;/li&gt;
&lt;li&gt;RAM overhead for 1,000 embeddings loaded for comparison: ~180MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; 340ms for recognition is perceptible. A robot that pauses for a third of a second to recognize someone it's seen before feels broken. And 180MB just for embeddings in a device with 8GB — fine now, but what happens at 10,000 records?&lt;/p&gt;

&lt;p&gt;The bottleneck wasn't SQLite being slow at SQL. It was SQLite being the wrong abstraction for vector similarity search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter moteDB
&lt;/h2&gt;

&lt;p&gt;I started working on &lt;a href="https://github.com/motedb/motedb" rel="noopener noreferrer"&gt;moteDB&lt;/a&gt; after hitting this wall. The design goal was narrow: an embedded database written in Rust that handles multimodal data natively and makes vector search a first-class operation, specifically on edge/IoT hardware.&lt;/p&gt;

&lt;p&gt;The core difference from SQLite is the data model. Instead of tables and rows, moteDB stores &lt;em&gt;fragments&lt;/em&gt; — typed data units that can be embeddings, blobs, scalars, or structured records. A "memory" is a collection of fragments with a timestamp and context metadata. Vector search operates on embedding fragments directly without deserialization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Cargo.toml&lt;/span&gt;
&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;motedb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.6"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;motedb&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Mote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VecQuery&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Store a face recognition moment&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mote&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"face"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;embedding_512d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"audio_clip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;audio_bytes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;scalar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.94&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Find top-5 similar faces&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;VecQuery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"face"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;current_embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.top_k&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same logical operation. No serialization. No schema definition. No glue code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers After Switching
&lt;/h2&gt;

&lt;p&gt;Running the same Raspberry Pi 5 benchmarks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;moteDB approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store a face embedding: ~0.3ms avg&lt;/li&gt;
&lt;li&gt;Find top-5 similar in 1,000 records: ~8ms&lt;/li&gt;
&lt;li&gt;RAM for 1,000 embeddings active index: ~22MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 340ms → 8ms improvement on search is the one that changed the robot's behavior. Recognition went from perceptible pause to imperceptible. The 180MB → 22MB improvement means I can scale to 10x the memory corpus before hitting constraints.&lt;/p&gt;

&lt;p&gt;Are these dramatic numbers? Yes, but the comparison is a bit unfair — SQLite is doing general-purpose relational work, moteDB is doing one specific thing. The point isn't "moteDB is faster than SQLite," it's that you're comparing apples to purpose-built apples.&lt;/p&gt;

&lt;h2&gt;
  
  
  When SQLite Is Still the Right Answer
&lt;/h2&gt;

&lt;p&gt;This isn't an "SQLite is bad" post. SQLite remains the right choice for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Config and settings storage&lt;/strong&gt; — nothing beats SQLite's simplicity for "remember this preference"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relational queries across structured data&lt;/strong&gt; — if your data is naturally tabular with joins, use SQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximum ecosystem compatibility&lt;/strong&gt; — SQLite has bindings in every language, tooling everywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs and event history&lt;/strong&gt; — append-only structured records are exactly what SQLite is great at&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The switch to moteDB made sense for my use case because my data was fundamentally multimodal and similarity search was a core operation. If I'd been building a robot that needed to remember &lt;em&gt;facts&lt;/em&gt; and query them relationally ("what's the last time I saw Alice?"), SQLite would have been fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;I wasted three weeks because I reached for the familiar tool instead of asking what the problem actually required. SQLite is excellent — but "excellent general-purpose database" and "right tool for AI agent memory" are different things.&lt;/p&gt;

&lt;p&gt;If you're building AI systems that work with embeddings, sensor data, and multimodal inputs — especially on edge hardware — the database choice deserves more deliberate thought than it usually gets. The ecosystem defaults (SQLite, PostgreSQL with pgvector) work, but they're not optimized for this access pattern.&lt;/p&gt;

&lt;p&gt;I'm curious: what are others using for on-device AI memory storage? Have you hit similar schema/performance walls, or found ways to make SQLite work cleanly with embedding data?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;moteDB is open-source and written in Rust. If you're working on edge AI and want to try it: &lt;code&gt;cargo add motedb&lt;/code&gt;. GitHub: &lt;a href="https://github.com/motedb/motedb" rel="noopener noreferrer"&gt;motedb/motedb&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>database</category>
      <category>ai</category>
      <category>embedded</category>
    </item>
    <item>
      <title>Your Robot Doesn't Need a Vector Store — It Needs a Memory System</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Sun, 12 Apr 2026 23:40:22 +0000</pubDate>
      <link>https://dev.to/motedb/your-robot-doesnt-need-a-vector-store-it-needs-a-memory-system-26fa</link>
      <guid>https://dev.to/motedb/your-robot-doesnt-need-a-vector-store-it-needs-a-memory-system-26fa</guid>
      <description>&lt;p&gt;Last Tuesday, our robot walked into the kitchen for the 31st time that week. Same route. Same question: "What should I prepare for lunch?"&lt;/p&gt;

&lt;p&gt;It had already asked that question. And answered it. And remembered the answer â€” for exactly 8 hours, until the vector store's TTL kicked in and silently wiped it clean.&lt;/p&gt;

&lt;p&gt;That's when I understood: vector databases are solving the wrong problem for embodied AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Semantic Search Trap
&lt;/h2&gt;

&lt;p&gt;Vector databases are brilliant at one thing: finding similar things. "Show me documents about cooking." "Find images that look like this." Great. Wonderful.&lt;/p&gt;

&lt;p&gt;But a robot standing in a kitchen isn't asking semantic questions. It's asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Have I been here before?" (spatial + temporal)&lt;/li&gt;
&lt;li&gt;"How long has the stove been on?" (time-series)&lt;/li&gt;
&lt;li&gt;"Is this object in the same position as last time?" (structured state)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vector similarity can't answer any of these. You need a database that thinks in sequences, timestamps, and relationships â€” not just embeddings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Robots Actually Need
&lt;/h2&gt;

&lt;p&gt;After 3 months of building memory systems for autonomous robots, here's what actually matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Temporal continuity.&lt;/strong&gt; A robot doesn't just need to know what happened. It needs to know when it happened and in what order. Did the person enter before or after the door opened? Vector stores have no concept of time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Spatial context.&lt;/strong&gt; "Kitchen" isn't a semantic cluster â€” it's a location with boundaries, objects, and states. Storing "kitchen" as a vector means you can find similar contexts, but you can't answer "has this specific kitchen been visited in the last hour?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Structured state.&lt;/strong&gt; The stove's temperature. The door's position. The battery level. These are key-value pairs that change over time and need to be queried efficiently without converting everything into vectors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Multimodal fusion.&lt;/strong&gt; Sensor data, LLM outputs, camera frames â€” all need to be stored together and queried across modalities.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hybrid Stack Problem
&lt;/h2&gt;

&lt;p&gt;The industry answer to this is usually: "Use a vector DB for embeddings + Redis for caching + InfluxDB for time-series + PostgreSQL for state."&lt;/p&gt;

&lt;p&gt;That's 4 databases. 4 connection pools. 4 failure modes. And about 50ms of latency per query on a good day.&lt;/p&gt;

&lt;p&gt;For a robot making decisions in real-time, that's not engineering â€” that's architectural debt.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Approach: Embedded Multimodal Storage
&lt;/h2&gt;

&lt;p&gt;The alternative is a single embedded database that handles all of these natively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;motedb&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"robot_memory.motedb"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"visits"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"kitchen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Utc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s"&gt;"robot_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"unit_7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"duration_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;342&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sensors"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stove_temp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;187.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"celsius"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"last_updated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Utc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"visits"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.filter&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"kitchen"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.filter&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Utc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No separate services. No network calls. No latency budget eaten by database round-trips.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Insight
&lt;/h2&gt;

&lt;p&gt;Vector databases are infrastructure for retrieval. Robots need infrastructure for memory.&lt;/p&gt;

&lt;p&gt;Memory isn't just "find the most similar thing." It's: what happened, when, where, and how does it affect what happens next?&lt;/p&gt;

&lt;p&gt;That's a fundamentally different data problem. And it's why the next generation of embedded databases â€” built in Rust, designed for edge AI â€” are approaching it differently.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's your experience with robot memory systems? Do you use separate databases for different data types, or have you found a better unified approach?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>database</category>
      <category>ai</category>
      <category>robotics</category>
    </item>
    <item>
      <title>I Watched My Robot Forget Everything 47 Times a Month — Until I Moved Memory to the Edge</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Sun, 12 Apr 2026 13:41:16 +0000</pubDate>
      <link>https://dev.to/motedb/i-watched-my-robot-forget-everything-47-times-a-month-until-i-moved-memory-to-the-edge-4lkd</link>
      <guid>https://dev.to/motedb/i-watched-my-robot-forget-everything-47-times-a-month-until-i-moved-memory-to-the-edge-4lkd</guid>
      <description>&lt;p&gt;Last Tuesday, my robot was halfway through cleaning the kitchen when the WiFi died.&lt;/p&gt;

&lt;p&gt;Not a big deal, right? Except my robot had "outsourced" its memory to the cloud three years ago — and now it didn't know where the trash can was. It asked me. Three times. In the same spot.&lt;/p&gt;

&lt;p&gt;"That's not a robot," I thought. "That's a very expensive Roomba with amnesia."&lt;/p&gt;

&lt;p&gt;This is the story of how I stopped sending my robot's brain to the cloud — and what I learned about embedded memory along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Cloud Memory Trap
&lt;/h2&gt;

&lt;p&gt;Here's what most robot builders do: every time the robot sees something interesting, it sends data to the cloud. "Kitchen counter, timestamp 14:32." "Coffee mug, moved." "User preferences, 3rd interaction."&lt;/p&gt;

&lt;p&gt;This works great in the lab. You have perfect WiFi. Low latency. Your cloud service scales infinitely.&lt;/p&gt;

&lt;p&gt;But real-world robots don't live in labs.&lt;/p&gt;

&lt;p&gt;They live in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basements with dead zones&lt;/li&gt;
&lt;li&gt;Warehouses with interference&lt;/li&gt;
&lt;li&gt;Outdoor environments with spotty coverage&lt;/li&gt;
&lt;li&gt;Moving vehicles that lose connection constantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When your robot's "memory" lives in the cloud, every WiFi hiccup becomes a cognitive reset.&lt;/p&gt;

&lt;p&gt;I watched my robot "forget" its entire map 47 times in one month. Each time, it had to relearn the layout from scratch. Each time, it bumped into the same table it had mapped 46 times before.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Embedded Database Experiment
&lt;/h2&gt;

&lt;p&gt;So I did what any frustrated engineer does: I integrated an embedded database that runs directly on the robot's onboard computer.&lt;/p&gt;

&lt;p&gt;No network calls. No latency. No "connection refused" errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;motedb&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize local memory store&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/robot/brain/memory.db"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Robot learns: kitchen counter is at position (2.3, 1.8)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"location:kitchen_counter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"first_seen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2024-03-15T14:32:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"visits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when the robot enters the kitchen, it checks local memory first. No WiFi required. The data is always there, always accessible, always fast.&lt;/p&gt;

&lt;p&gt;The difference was immediate: 200ms to query local memory versus 800ms to cloud and back. And zero failures when the network dropped.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned (The Hard Way)
&lt;/h2&gt;

&lt;p&gt;After six months with embedded memory running in production, here's what I've learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Latency Kills Robot UX&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A robot that pauses to "think" (i.e., wait for cloud) feels broken. Users don't know it's a network issue — they think the robot is stupid.&lt;/p&gt;

&lt;p&gt;With local memory, queries take milliseconds. The robot responds instantly. It feels smart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Edge Computing Isn't Just for AI Models&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everyone talks about running AI models at the edge. But the boring stuff matters too: storing state, remembering context, persisting learned information.&lt;/p&gt;

&lt;p&gt;If your robot can't remember where it left off without calling home, it's not truly autonomous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Embedded Databases Are Finally Good Enough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three years ago, I tried SQLite for this. It worked, but lacked features I needed: time-series support, vector similarity search, ACID transactions for concurrent access.&lt;/p&gt;

&lt;p&gt;Now there are purpose-built embedded databases for robotics. Rust-native, no dependencies, single binary. The ecosystem has matured significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Offline-First Changes Everything&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once your robot doesn't depend on the cloud for memory, interesting things happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It works in places with no connectivity&lt;/li&gt;
&lt;li&gt;It responds faster (no round-trip latency)&lt;/li&gt;
&lt;li&gt;It's more private (data stays on-device)&lt;/li&gt;
&lt;li&gt;It scales differently (no server costs per robot)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You start thinking about robots as truly independent agents rather than thin clients to a central brain.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Cloud Still Has Its Place
&lt;/h2&gt;

&lt;p&gt;I'm not saying clouds are bad. Clouds are great for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fleet-wide analytics&lt;/li&gt;
&lt;li&gt;Training data collection&lt;/li&gt;
&lt;li&gt;Remote debugging&lt;/li&gt;
&lt;li&gt;Cross-robot coordination&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the robot's moment-to-moment memory? That should live on the robot.&lt;/p&gt;

&lt;p&gt;It's like asking a human to cloud-source their short-term memory. "Hold on, let me check the cloud what I was doing." Awkward, slow, and occasionally catastrophic.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Would You Do?
&lt;/h2&gt;

&lt;p&gt;I'm curious about how others handle this problem. Do you run memory entirely on-robot? Hybrid approach? Full cloud?&lt;/p&gt;

&lt;p&gt;For robotics use cases where reliability matters more than centralized intelligence, I'm convinced embedded is the way forward.&lt;/p&gt;

&lt;p&gt;What's your experience with robot memory architectures? Let me know in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're building a robot and want to experiment with embedded multimodal storage, check out &lt;a href="https://github.com/motedb/motedb" rel="noopener noreferrer"&gt;moteDB&lt;/a&gt; — it's Rust-native and designed exactly for this use case.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>database</category>
      <category>robotics</category>
    </item>
    <item>
      <title>My Drone Flew 5 Minutes Before Its Brain Crashed — Every Single Time</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Sun, 12 Apr 2026 07:04:42 +0000</pubDate>
      <link>https://dev.to/motedb/my-drone-flew-5-minutes-before-its-brain-crashed-every-single-time-211j</link>
      <guid>https://dev.to/motedb/my-drone-flew-5-minutes-before-its-brain-crashed-every-single-time-211j</guid>
      <description>&lt;h1&gt;
  
  
  My Drone Flew 5 Minutes Before Its Brain Crashed — Every Single Time
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;A true story about why robots shouldn't use SQLite in production&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;The first 47 times my drone crashed, I blamed the flight controller.&lt;/p&gt;

&lt;p&gt;The 48th time, I looked at the logs.&lt;/p&gt;

&lt;p&gt;14:32:07 WARNING: Database write latency exceeded 200ms&lt;br&gt;
14:32:08 ERROR: SQLite busy - database is locked&lt;br&gt;
14:32:08 ERROR: Failed to commit sensor data&lt;br&gt;
14:32:09 WARNING: Falling back to in-memory buffer&lt;br&gt;
14:32:11 ERROR: Buffer overflow - oldest data discarded&lt;br&gt;
14:32:12 CRITICAL: Navigation state corrupted&lt;br&gt;
14:32:13 [CRASH]&lt;/p&gt;

&lt;p&gt;SQLite. The drone was running SQLite. On a flight-critical system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Was Actually Happening
&lt;/h2&gt;

&lt;p&gt;My drone was logging sensor data at 100Hz. Gyroscope, accelerometer, GPS, camera frames, battery state — roughly 2,400 data points per second.&lt;/p&gt;

&lt;p&gt;SQLite can handle that. In theory.&lt;/p&gt;

&lt;p&gt;In practice, there's a problem: write locking. SQLite uses a single writer lock. When the database is busy committing one transaction, every other write has to wait.&lt;/p&gt;

&lt;p&gt;At 100Hz, a 50ms delay means you've already missed 5 sensor readings by the time the write completes. Stack that up over 5 minutes, and you have hundreds of failed writes, a buffer overflow, data gaps that cascade into navigation failures.&lt;/p&gt;

&lt;p&gt;This is how database latency becomes crash #48.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Obvious Fix (That Wasn't)
&lt;/h2&gt;

&lt;p&gt;My first thought: Switch to a faster database. PostgreSQL. TimescaleDB.&lt;/p&gt;

&lt;p&gt;So I spun up a TimescaleDB instance on a local server. Pointed the drone at it.&lt;/p&gt;

&lt;p&gt;Result: network latency 45-120ms, connection drops every 30 seconds, 6-second data gaps.&lt;/p&gt;

&lt;p&gt;Robots don't need cloud databases. They need databases that run on the robot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Embedded Actually Means
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Zero setup - no server process, no configuration files&lt;/li&gt;
&lt;li&gt;Single binary - the database is a library you link into your application&lt;/li&gt;
&lt;li&gt;Local storage - everything lives on the device, survives reboots&lt;/li&gt;
&lt;li&gt;Concurrent writes - designed for high-frequency writes without blocking&lt;/li&gt;
&lt;li&gt;Resilient to power loss - crashes don't corrupt data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SQLite handles 1, 2, and 5 beautifully. But it stumbles on concurrent writes under high-frequency sensor workloads.&lt;/p&gt;

&lt;p&gt;The solution I eventually landed on: moteDB, designed for high-frequency sensor ingestion on embedded hardware. No server. No WiFi dependency.&lt;/p&gt;

&lt;p&gt;The drone flew for 47 minutes on its next flight. First time past the 10-minute mark. No crashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson That Cost Me 48 Crashes
&lt;/h2&gt;

&lt;p&gt;Here's what I should have asked on day one: What happens to my data when the network goes down?&lt;/p&gt;

&lt;p&gt;If the answer is the data is lost - you're building the wrong system.&lt;/p&gt;

&lt;p&gt;Robots operate in the real world. Networks fail. Servers crash. WiFi drops.&lt;/p&gt;

&lt;p&gt;The database should be the last thing that fails, not the first.&lt;/p&gt;




&lt;p&gt;Has your project ever had a database crash that cascaded into a bigger failure? Or have you found creative solutions for local-first data on edge devices?&lt;/p&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>database</category>
      <category>robotics</category>
    </item>
    <item>
      <title>My Drone Flew 5 Minutes Before Its Brain Crashed</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Sun, 12 Apr 2026 07:03:40 +0000</pubDate>
      <link>https://dev.to/motedb/my-drone-flew-5-minutes-before-its-brain-crashed-20o</link>
      <guid>https://dev.to/motedb/my-drone-flew-5-minutes-before-its-brain-crashed-20o</guid>
      <description>&lt;h1&gt;
  
  
  My Drone Flew 5 Minutes Before Its Brain Crashed
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;A true story&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;The first 47 times my drone crashed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Was Actually Happening
&lt;/h2&gt;

&lt;p&gt;SQLite uses a single writer lock. When the database is busy committing one transaction, every other write has to wait.&lt;/p&gt;

&lt;p&gt;At 100Hz, a 50ms delay means you have missed 5 sensor readings.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>database</category>
    </item>
    <item>
      <title>My Robot Said Kitchen 47 Times — And Never Knew It Had Already Been There</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Sun, 12 Apr 2026 06:46:13 +0000</pubDate>
      <link>https://dev.to/motedb/my-robot-said-kitchen-47-times-and-never-knew-it-had-already-been-there-1b1j</link>
      <guid>https://dev.to/motedb/my-robot-said-kitchen-47-times-and-never-knew-it-had-already-been-there-1b1j</guid>
      <description>&lt;h1&gt;
  
  
  My Robot Said Kitchen 47 Times — And Never Knew It Had Already Been There
&lt;/h1&gt;

&lt;p&gt;Last Tuesday, my robot announced Entering kitchen for the 47th time. Same kitchen. Same Tuesday. Same confused voice.&lt;/p&gt;

&lt;p&gt;Id given it a state-of-the-art vector database. Semantic search. Cosine similarity. The whole thing.&lt;/p&gt;

&lt;p&gt;What I hadnt given it? The ability to remember when it last visited the kitchen. Or how long it stayed. Or whether the coffee machine was already on.&lt;/p&gt;

&lt;p&gt;The robot knew what a kitchen felt like — but it had no memory of actually being there.&lt;/p&gt;

&lt;p&gt;This is the gap nobody talks about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vector Search Trap
&lt;/h2&gt;

&lt;p&gt;Every AI robotics tutorial starts the same way: Store your robots experiences as vectors. Use similarity search to find relevant memories.&lt;/p&gt;

&lt;p&gt;Its not wrong. Vector search is incredible. But it creates a specific, subtle failure mode that only shows up when your robot encounters the same situation twice.&lt;/p&gt;

&lt;p&gt;The robot thinks every kitchen is the same kitchen.&lt;/p&gt;

&lt;p&gt;When the robot asks the vector database for the closest kitchen memory, it gets back a description of a past visit — but not the actual temporal or spatial context of what happened. In robotics, context is everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Things Vector Search Cant Tell You
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. When Did This Actually Happen?
&lt;/h3&gt;

&lt;p&gt;Semantic similarity doesnt preserve time. The kitchen from last week looks identical to the kitchen from last year — unless you manually tag timestamps on every embedding.&lt;/p&gt;

&lt;p&gt;But even if you add timestamps, you now have to filter by time before you search semantically. Two separate operations. Two different failure modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. How Long Did It Take?
&lt;/h3&gt;

&lt;p&gt;My robot spent 8 minutes in the kitchen last Tuesday. It spent 23 minutes the time before that. It spent 2 minutes the time before that — because it immediately bumped into the dog bowl and panicked.&lt;/p&gt;

&lt;p&gt;These durations tell you something important: the robots state was different in each visit. Vector search gives you none of this.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Where Exactly In the State Space?
&lt;/h3&gt;

&lt;p&gt;When the robot says kitchen, it doesnt mean just the room. It means proximity to the counter, the refrigerator, whether the stove was on, what the lighting level was, whether the dog was in the room.&lt;/p&gt;

&lt;p&gt;These are structured attributes, not vector embeddings.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Franken-database Problem
&lt;/h2&gt;

&lt;p&gt;So what did I do? I bolted on a time-series database for temporal data. A graph database for spatial relationships. A Redis cache for real-time state.&lt;/p&gt;

&lt;p&gt;Five databases. Four different query languages. One robot that still said Entering kitchen 47 times.&lt;/p&gt;

&lt;p&gt;The problem wasnt that these tools were bad. The problem was that they werent designed to work together — and robotics requires all of these simultaneously, in real-time, on edge hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Needed
&lt;/h2&gt;

&lt;p&gt;I needed one database that could handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vectors — semantic similarity for context matching&lt;/li&gt;
&lt;li&gt;Time-series — when did X happen, and for how long?&lt;/li&gt;
&lt;li&gt;Structured data — key-value attributes that arent vectors&lt;/li&gt;
&lt;li&gt;Single binary — running on a Raspberry Pi, not a data center&lt;/li&gt;
&lt;li&gt;Offline-first — the robot cant call home when the WiFi drops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There wasnt anything off-the-shelf. So I built moteDB — a Rust-native embedded database that handles all four data types in a single engine. No server. No cloud. No dependency hell.&lt;/p&gt;

&lt;p&gt;The robot now stores location, timestamp, duration, structured state, and embeddings all in one place. One query. All dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Question Nobody Asks
&lt;/h2&gt;

&lt;p&gt;We spend so much energy making robots smarter — better models, better sensors, better LLMs.&lt;/p&gt;

&lt;p&gt;But maybe the bottleneck isnt intelligence. Maybe its memory.&lt;/p&gt;

&lt;p&gt;Not how do I make the robot understand this moment? — but how do I make the robot remember the moments that came before?&lt;/p&gt;

&lt;p&gt;Vector search is a powerful tool. But its not a memory system. Its a similarity engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does your robots memory stack look like? Are you using separate tools for vectors, time-series, and structured data — or have you found a better way to unify them?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>database</category>
      <category>robotics</category>
    </item>
    <item>
      <title>I Tried 5 Ways to Give My AI Agent Memory (And Built a Database Instead)</title>
      <dc:creator>mote</dc:creator>
      <pubDate>Sat, 11 Apr 2026 13:50:55 +0000</pubDate>
      <link>https://dev.to/motedb/i-tried-5-ways-to-give-my-ai-agent-memory-and-built-a-database-instead-mdd</link>
      <guid>https://dev.to/motedb/i-tried-5-ways-to-give-my-ai-agent-memory-and-built-a-database-instead-mdd</guid>
      <description>&lt;p&gt;Last month, my AI agent forgot a user's name three times in one conversation.&lt;/p&gt;

&lt;p&gt;Not a complex name. Not a foreign name. "Sarah." The agent asked "What's your name?" then "Nice to meet you, Sarah" then five minutes later: "Sorry, I didn't catch your name."&lt;/p&gt;

&lt;p&gt;Sarah was not impressed. Neither was I.&lt;/p&gt;

&lt;p&gt;I had built what I thought was a sophisticated AI system. It could reason, plan, execute tasks. But it had the memory of a goldfish with amnesia.&lt;/p&gt;

&lt;p&gt;So I went down the rabbit hole of AI agent memory. I tried everything. Here's what actually workedâ€”and what didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: Just Use the Context Window (Spoiler: It Fails)
&lt;/h2&gt;

&lt;p&gt;My first thought: "LLMs have huge context windows now. I'll just stuff everything in there."&lt;/p&gt;

&lt;p&gt;I dumped the entire conversation history into every prompt. Worked greatâ€”for about 10 minutes. Then the context filled up. Older messages got pushed out. Sarah's name was in minute 3. By minute 15, it was gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;: $0.03 per 1K tokens Ã— 20 turns = expensive amnesia&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: 2-3 seconds per response with full history&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Sarah was still forgotten&lt;/p&gt;

&lt;p&gt;Verdict: Context windows are not memory. They're short-term scratchpads.&lt;/p&gt;
&lt;h2&gt;
  
  
  Attempt 2: SQLite + Manual Prompt Engineering
&lt;/h2&gt;

&lt;p&gt;Okay, I need persistent storage. I'll use SQLiteâ€”it's embedded, it's reliable, it's everywhere.&lt;/p&gt;

&lt;p&gt;I built a schema. &lt;code&gt;conversations&lt;/code&gt; table. &lt;code&gt;messages&lt;/code&gt; table. &lt;code&gt;user_profiles&lt;/code&gt; table. Then I wrote code to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract key facts from conversations&lt;/li&gt;
&lt;li&gt;Summarize old context&lt;/li&gt;
&lt;li&gt;Inject relevant memories into prompts&lt;/li&gt;
&lt;li&gt;Manage token budgets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It took three days. It kind of worked. But every new memory type meant schema migrations. Every query meant writing SQL. Every "remember this" feature meant more code.&lt;/p&gt;

&lt;p&gt;My agent code was 30% AI logic, 70% database plumbing.&lt;/p&gt;

&lt;p&gt;Verdict: SQLite remembers data. It doesn't help your AI &lt;em&gt;use&lt;/em&gt; that data intelligently.&lt;/p&gt;
&lt;h2&gt;
  
  
  Attempt 3: Redis for "Fast" Memory
&lt;/h2&gt;

&lt;p&gt;Someone on Reddit said: "Use Redis for agent memory. It's fast."&lt;/p&gt;

&lt;p&gt;Fast, yes. But Redis is a cache, not a memory system. I stored conversation snippets. But Redis doesn't understand &lt;em&gt;meaning&lt;/em&gt;. It can't answer: "Has Sarah mentioned she has a dog?" It just stores key-value pairs.&lt;/p&gt;

&lt;p&gt;I tried adding vector search on top. Then I had Redis + a vector database + SQLite for persistence. Three systems. Three failure modes. Three things to debug at 2 AM.&lt;/p&gt;

&lt;p&gt;Verdict: Fast is useless if it's not &lt;em&gt;smart&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Attempt 4: Pinecone (The Cloud Vector Database)
&lt;/h2&gt;

&lt;p&gt;Vector search! That's what I need. Store embeddings of conversations, search for relevant context.&lt;/p&gt;

&lt;p&gt;Pinecone worked. I could find "similar" past conversations. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50-100ms latency per query (my agent felt sluggish)&lt;/li&gt;
&lt;li&gt;Required internet connection (offline agents = dead agents)&lt;/li&gt;
&lt;li&gt;Another external service to manage, pay for, pray stays up&lt;/li&gt;
&lt;li&gt;No built-in time-series ("what happened 5 minutes ago?" is different from "what's semantically similar?")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, vectors alone aren't enough. I needed the raw text, timestamps, structured state. More glue code. More systems.&lt;/p&gt;

&lt;p&gt;Verdict: Cloud vector search is powerful but wrong for embedded agents.&lt;/p&gt;
&lt;h2&gt;
  
  
  Attempt 5: The Frankenstein Architecture
&lt;/h2&gt;

&lt;p&gt;By this point, my agent's "memory stack" looked like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQLite for structured data&lt;/li&gt;
&lt;li&gt;Redis for caching&lt;/li&gt;
&lt;li&gt;Pinecone for vector search&lt;/li&gt;
&lt;li&gt;Custom code to sync them&lt;/li&gt;
&lt;li&gt;Custom code to decide which to query when&lt;/li&gt;
&lt;li&gt;Custom code to manage consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It worked. Sometimes. But adding a new memory type meant touching 4 systems. Debugging meant checking 4 logs. Deploying meant configuring 4 services.&lt;/p&gt;

&lt;p&gt;I spent more time on memory infrastructure than on the actual AI.&lt;/p&gt;

&lt;p&gt;Verdict: This is insane. There has to be a better way.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Realization: Agents Need a Memory &lt;em&gt;System&lt;/em&gt;, Not a Database
&lt;/h2&gt;

&lt;p&gt;Here's what I learned: AI agents don't just need to &lt;em&gt;store&lt;/em&gt; data. They need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store vectors&lt;/strong&gt; (semantic memoryâ€”"have I seen this before?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store time-series&lt;/strong&gt; (episodic memoryâ€”"what happened when?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store state&lt;/strong&gt; (working memoryâ€”"what am I doing right now?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlate across all three&lt;/strong&gt; ("when I saw that image, what was my battery level?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do it locally&lt;/strong&gt; (no network, no latency, no cloud dependency)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do it fast&lt;/strong&gt; (sub-millisecond queries, not 50ms roundtrips)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No existing database did all of this. So I built one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Meet moteDB: The Database I Wish Existed
&lt;/h2&gt;

&lt;p&gt;moteDB is an embedded multimodal database for AI agents. It's a single Rust crate. No server. No cloud. No configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo add motedb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What makes it different?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vectors + Time-Series + State in one engine&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Store a vector (semantic memory)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="nf"&gt;.embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sarah has a golden retriever named Max"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.vectors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Store time-series data (episodic memory)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.time_series&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_interaction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Store agent state (working memory)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"current_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Sarah"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Query across all three with logical clocks&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="nf"&gt;.query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.vector_similar_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.at_time_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_5_minutes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"current_user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sub-millisecond latency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because it runs in-process, there's no network. The agent calls the database directly. My agent's response time dropped from 2-3 seconds to under 200ms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crash-safe by design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agents crash. Robots lose power. moteDB uses append-only storage with write-ahead logging. When your agent restarts, it remembers exactly where it left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embeddable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No Docker. No Kubernetes. No "just spin up this sidecar." It's a Rust crate that compiles into your agent binary. Runs on Raspberry Pi. Runs on edge devices. Runs anywhere your agent runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;Sarah's agent now remembers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Her name (state)&lt;/li&gt;
&lt;li&gt;That she has a dog named Max (vector search finds this in conversation history)&lt;/li&gt;
&lt;li&gt;That she prefers evening meetings (time-series pattern: always declines morning invites)&lt;/li&gt;
&lt;li&gt;All of this in &amp;lt;10ms, locally, without any cloud calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent feels &lt;em&gt;present&lt;/em&gt;. It remembers context. It learns patterns. It doesn't ask "What's your name?" five minutes after you told it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were starting today, I'd skip attempts 1-5 and go straight to building (or using) a proper agent memory system.&lt;/p&gt;

&lt;p&gt;The lesson: &lt;strong&gt;Don't glue databases together. Your agent's memory is too important to be an afterthought.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;How are you handling agent memory? Are you still stuffing everything into the context window, or have you found something better? I'd love to hear what's working (and what's breaking) in your setups.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>database</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
