<?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: Cole Hellman</title>
    <description>The latest articles on DEV Community by Cole Hellman (@hellmaca).</description>
    <link>https://dev.to/hellmaca</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F104505%2Fcef19eaa-65bc-4bb6-9081-969e6ebd76f4.png</url>
      <title>DEV Community: Cole Hellman</title>
      <link>https://dev.to/hellmaca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hellmaca"/>
    <language>en</language>
    <item>
      <title>My embedding server died and I didn't notice for two weeks</title>
      <dc:creator>Cole Hellman</dc:creator>
      <pubDate>Mon, 20 Jul 2026 17:20:21 +0000</pubDate>
      <link>https://dev.to/hellmaca/my-embedding-server-died-and-i-didnt-notice-for-two-weeks-1hol</link>
      <guid>https://dev.to/hellmaca/my-embedding-server-died-and-i-didnt-notice-for-two-weeks-1hol</guid>
      <description>&lt;p&gt;I built a small MCP server called &lt;a href="https://github.com/colehellman/codicil" rel="noopener noreferrer"&gt;Codicil&lt;/a&gt; that indexes docs in a repo and lets an AI coding assistant search them instead of guessing. Runbooks, config notes, whatever I've written down about my homelab. It's just for me, one repo, one user, nobody else touches it.&lt;/p&gt;

&lt;p&gt;For months it ran against a real Ollama box doing embeddings with &lt;code&gt;nomic-embed-text&lt;/code&gt;, so every query was an actual semantic search over vectors. Then I decommissioned that box for unrelated reasons and the embedding endpoint went dark.&lt;/p&gt;

&lt;p&gt;I didn't notice for something like two weeks. Queries still came back with reasonable answers so I just kept using it. Turns out it had fallen back to grepping the raw files the whole time, and it never said a word about it.&lt;/p&gt;

&lt;p&gt;I don't remember exactly what tipped me off at this point, just that at some point I went looking and realized it had been running on keyword fallback for a while.&lt;/p&gt;

&lt;p&gt;Honestly my first thought was that this is a little embarrassing. I'm the only person who uses this thing, so I'm also the only monitoring it has, and I went two weeks without checking. But then I actually looked at why it didn't just break, and that part I don't think is embarrassing at all.&lt;/p&gt;

&lt;p&gt;Most tools like this have one failure mode when the embedding service goes away: they stop working. No embedding, no vector, no search. I didn't want that, so a bunch of the code is just "what happens if this specific thing isn't there" handling, scattered around instead of centralized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No embedding host reachable? Falls back to grepping files off disk directly.&lt;/li&gt;
&lt;li&gt;Index empty or never built? Same fallback.&lt;/li&gt;
&lt;li&gt;Reindex dies halfway through a file? New chunks get written before old ones are deleted, so a crash mid-run leaves you stale, not empty.&lt;/li&gt;
&lt;li&gt;Two processes hit the store at once? This one actually got me for real. A long-running &lt;code&gt;serve&lt;/code&gt; process and a one-off &lt;code&gt;codicil index&lt;/code&gt; from a cron job raced each other. There's an exclusive &lt;code&gt;fcntl.flock&lt;/code&gt; on the store now (&lt;code&gt;fcntl.flock(fd, LOCK_EX | LOCK_NB)&lt;/code&gt;), so the second process gets refused outright instead of getting to touch anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that is exotic, it's just annoying to write and easy to skip. I skipped some of it the first time through, which is how the process-race thing happened.&lt;/p&gt;

&lt;p&gt;Keyword search is worse than semantic search, for the record. It doesn't get synonyms, it just counts overlapping words. If I ran both side by side on the same queries, semantic wins most of the time. But it only wins when the embedding host is actually up, and mine wasn't, for two weeks, and I didn't know.&lt;/p&gt;

&lt;p&gt;There's also a dumber bug I found while doing all this that's worth mentioning because it's the kind of thing that's obvious in hindsight: each embedding model gets its own Chroma collection now (&lt;code&gt;docs_&amp;lt;sha256-of-model-name&amp;gt;[:12]&lt;/code&gt;), because Chroma fixes a collection's vector dimension the first time you write to it. Before that fix, switching embedding models against the same collection would've just broken silently the next time you tried to write a different-sized vector into it. I hadn't hit this one in practice, I just noticed it could happen and fixed it before it did.&lt;/p&gt;

&lt;p&gt;I've been putting off publishing this because I kept trying to make the ending land on some bigger point about AI tooling and knowledge decay, and every version of that ending sounded like something off a conference slide. So I'm just going to leave it here: the embedding host died, I didn't notice, and the reason I didn't notice is the only part of this worth writing down.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Codicil's open source: &lt;a href="https://github.com/colehellman/codicil" rel="noopener noreferrer"&gt;github.com/colehellman/codicil&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
    </item>
  </channel>
</rss>
