Milvus: a high-performance, cloud-native vector database built for scalable vector ANN search (47k stars)
What it does
All text below is quoted directly from the project README.
>Milvus is a high-performance, cloud-native vector database built for scalable vector ANN search
Why Milvus:
- High Performance at Scale and High Availability: Milvus features a distributed architecture that separates compute and storage.
- Support for Various Vector Index Types and Hardware Acceleration: Milvus separates the system and core vector search engine, allowing it to support all major vector index types that are optimized for different scenarios, including HNSW, IVF, FLAT (brute-force), SCANN, and DiskANN, with quantization-based variations and mmap.
- Flexible Multi-tenancy and Hot/Cold Storage: Milvus supports multi-tenancy through isolation at database, collection, partition, or partition key level.
- Sparse Vector for Full Text Search and Hybrid Search: In addition to semantic search through dense vector, Milvus also natively supports full text search with BM25 as well as learned sparse embeddings such as SPLADE and BGE-M3.
- Data Security and Fine-grain Access Control: Milvus ensures data security by implementing mandatory user authentication, TLS encryption, and Role-Based Access Control (RBAC).
>Milvus Lite is a lightweight version good for quickstart in python with pip install.
Repository
milvus-io/milvus
Repository stats
|Metric|Value|
|:-|:-|
|Stars|46,536|
|Forks|4.3k|
|Last release|v3.0.5 (2026-10-30)|
|Languages|Go 60.7%, C++ 21.3%, Python 17%|
|Contributors|365|
|AI contributor accounts (Claude / Codex / Cursor / Copilot)|@claude (668 commits), @codex (36 commits), @cursoragent (5 commits)|
|AI co-authored commits|Claude: 672, Codex: 26, Cursor: 5|
Main contributor
|Field|Value|
|:-|:-|
|GitHub|@congqixia|
|Commits to this repo|1,818 (top contributor)|
|Followers|64|
──────────────────────────────
📌 If you want to browse more repos like this, we've organized them by category and rankings here: bestgithubrepositories.com
🗂️ We're also archiving repos where possible, so useful projects don't disappear when they're removed from GitHub.
2 points
I ran ripgrep, Serena, claude-context and my own tool on the same repo
There are a lot of ways to let a coding agent find code now. Plain grep, a language server behind MCP, a vector database, a local index. Every README says its way is the right one, so I wanted to see them do the same job on the same code.
I build one of these tools, Semlith, so treat my bias accordingly. That's also why I'm showing the questions it got wrong.
The setup is Flask 3.1.3 on an M1 MacBook Air with 8 GB. Versions are ripgrep 15.2.0, Serena 2.1.0, claude-context 0.1.15 (with Milvus 3.0.3 and Ollama running nomic-embed-text) and semlith 0.50.0.
git clone --depth 1 --branch 3.1.3 https://github.com/pallets/flask && cd flask
The four approaches in one table
| | ripgrep | Serena | claude-context | Semlith |
|---|---|---|---|---|
| What it is | Text search, no index | Language servers behind MCP | Embeddings in Milvus | Local index, embeddings plus keyword plus code graph |
| Finds code by meaning | No | No | Yes | Yes |
| Edits code | No | Yes, by symbol | No | No, read only |
| Setup on Flask | Nothing | 30.9 s to index | About 21 min (CPU in Docker) | 84.3 s to index |
| Moving parts | One binary | Python env plus language servers | Docker, Milvus, Ollama, npx | One binary |
| Reads PDF, Word, Excel | No | No | No | Yes |
The rest of this post is how I got those numbers.
ripgrep, when you know the name
When the agent already knows the identifier, nothing beats grep.
rg -n max_content_length src
src/flask/wrappers.py:55: _max_content_length: int | None = None
src/flask/wrappers.py:60: def max_content_length(self) -> int | None:
src/flask/wrappers.py:80: if self._max_content_length is not None:
src/flask/wrappers.py:81: return self._max_content_length
src/flask/wrappers.py:84: return super().max_content_length
src/flask/wrappers.py:88: @max_content_length.setter
src/flask/wrappers.py:89: def max_content_length(self, value: int | None) -> None:
src/flask/wrappers.py:90: self._max_content_length = value
Eight lines, all of them right, in milliseconds.
Now ask the same thing the way a person would, without the name. An agent turns "limit how big an uploaded request body can be" into words and greps for them.
rg -n -i "upload|big" src
src/flask/cli.py:297: a bigger role. Typically it's created automatically by the
src/flask/helpers.py:542: @app.route("/uploads/<path:name>")
src/flask/helpers.py:545: app.config['UPLOAD_FOLDER'], name, as_attachment=True
src/flask/helpers.py:549: files or uploads. Uses :func:`~werkzeug.security.safe_join` to
None of these is the answer, because wrappers.py never says "upload" or "big". This is where an agent starts guessing more words and opening more files.
Serena, when you want a language server
Serena gives the agent symbol tools backed by real language servers, over 40 languages. Since version 2 it shows up as a REPL, so the agent writes a little Python against Serena's API.
uv venv -p 3.11 venv-serena
uv pip install -p venv-serena "serena-agent @ git+https://github.com/oraios/serena@v2.1.0"
serena project index .
Indexing took 30.9 seconds. Given the name, it's precise.
s.lsp.find_symbol("max_content_length")
# [{"name_path": "Request/max_content_length", "kind": "Method",
# "relative_path": "src/flask/wrappers.py", "body_location": {"start_line": 87, ...}}]
References come back in about 2 seconds and they come from the language server, so they are real references, not text matches. Line numbers count from zero, which caught me out the first time.
Without the name though, Serena falls back to a regex pattern search, and that returns the same lines grep did.
s.fs.search_for_pattern("upload|big", relative_path="src")
The thing Serena does that none of the others do is edit. Replace a function body, insert after a symbol, rename across files. If your agent spends most of its time changing code it already found, that's a real advantage.
claude-context, run fully local
claude-context is Zilliz's MCP server. It chunks code, embeds it and stores the vectors in Milvus, with hybrid search on top. By default it wants an OpenAI key for embeddings and a Zilliz Cloud key for the database. It also supports a fully local setup, so that's what I ran.
curl -sSL https://github.com/milvus-io/milvus/releases/download/v3.0.3/milvus-standalone-docker-compose.yaml -o docker-compose.yaml
docker compose up -d
docker run -d --name cc-ollama --cpus=4 -p 127.0.0.1:11434:11434 ollama/ollama:0.40.13
docker exec cc-ollama ollama pull nomic-embed-text
EMBEDDING_PROVIDER=Ollama EMBEDDING_MODEL=nomic-embed-text \
OLLAMA_HOST=http://127.0.0.1:11434 MILVUS_ADDRESS=localhost:19530 \
npx -y @zilliz/claude-context-mcp@0.1.15
The images came to 6.14 GB and the first npx start pulled 418 MB of packages. While it worked I watched its connections, and it only talked to Milvus and Ollama on the machine.
Two things bit me. On the first run Milvus exited mid-index while Ollama had every CPU, so I capped Ollama at four. The second one is worth knowing if you use it. get_indexing_status said this at 92 seconds.
✅ Codebase '…/flask' is fully indexed and ready for search.
📊 Statistics: 100 files, 100 chunks
It wasn't done. The server's own log finished at 1,266.9 seconds with 86 files and 1,837 chunks. If your agent trusts that status and searches early, it searches a partial index without knowing.
That 21 minutes isn't a fair speed test either. Docker on a Mac has no GPU access, so Ollama embedded on the CPU. A native Ollama install would be quicker, I just didn't measure it.
Semlith, the one I build
semlith index .
indexed 211 files (2778 chunks, 5 images) in 84.3s — 0 already indexed, 4 skipped (4 empty), 0 removed
That count includes the .rst docs, which claude-context skips by default. Serena was quicker to set up here, since it doesn't embed anything.
!Horizontal bar chart of the time to index Flask 3.1.3 before the first search. ripgrep needs no index, Serena takes 30.9 seconds, Semlith 84.3 seconds and claude-context 1,266.9 seconds with Ollama embedding on the CPU in Docker.
Setup time on the same checkout. Serena was the quickest of the indexed tools, and claude-context's time is CPU-only embedding in Docker.
semlith search -k 5 "limit how big an uploaded request body can be"
1. 0.087 vf src/flask/wrappers.py:59-71
2. 0.083 vf src/flask/wrappers.py:91-106
3. 0.077 v docs/web-security.rst:26-38
4. 0.076 vf docs/patterns/fileuploads.rst:135-153
5. 0.072 vf src/flask/wrappers.py:105-123
5 hits in 360.027667ms
The first hit is the property. The letters after the score say how it was found, v for the vector list and f for the keyword list.
The question everyone got wrong
I asked all of them where Flask decides which error handler runs for an exception. The answer is _find_error_handler in src/flask/sansio/app.py.
Grep did best. rg -n -i "error handler" src lands on line 826 of that file, the function's docstring, because the question uses the same words the code does.
Semlith's top five were all in docs/errorhandling.rst. Those pages explain the order Flask uses, so they answer the question, but they aren't the code. claude-context had the function at rank 8.
So "semantic search beats grep" isn't true in general. When the question already uses the code's own words, grep is very hard to beat.
Bigger samples
Three questions on one repo prove nothing, so here's what I could measure at scale.
On SWE-bench Verified, 500 real GitHub issues, the issue text is the query and the gold is every file the fix edits. Semlith had a right file in its top 10 for 76.8% of them, against 47.0% for one ripgrep pass ranked and read in order. That's retrieval only, not whether an agent then fixes the bug.
On 20 issues from SWE-bench Lite, every tool got the same checkout and issue text. Serena 1.7.0 finished 16 of 20 (4 timed out) and had the right file in its top 5 for 25.0% of those, against 56.2% for Semlith on the same 16. That run predates Serena's REPL, so 2.1.0 might do differently. claude-context isn't in that comparison yet, so I won't give it a number.
And the one that keeps me honest. On 50 questions about a single unfamiliar repo, headless Claude Code on Opus scored 92% with grep alone and 92% with Semlith installed beside it. A strong agent with room to explore gets there with grep.
What each one costs before you use it
Every MCP server sends its tool definitions at session start, and the agent pays for them on every turn. I counted the tools/list result at about four bytes a token.
Serena tools: 2 4544 bytes, about 1136 tokens
semlith tools: 8 2668 bytes, about 667 tokens
Both are small. Serena keeps the list short with a REPL, then the agent reads 9,292 characters of instructions when it calls initial_instructions.
Which I'd pick
Honestly they do different jobs, and you can run more than one.
- One repo, a strong model, names you mostly know. Grep is enough.
- An agent that edits a lot by symbol. Serena.
- You already run Milvus or Zilliz Cloud, or want to pick the embedding provider. claude-context.
- Many repos, documents next to the code, nothing to sign up for. That's the case I built Semlith for.
The full runs with every command are on semlith.com and the benchmark harness is in the repo at semlith/semlith.
What are you using for code search with your agents right now, and where does it fall over?
1 reaction · 1 comment
Enhancing Distributed Streaming in Milvus: Remote TransformLog Subscriptions and MVCC Barriers
Enhancing Distributed Streaming in Milvus: Remote TransformLog Subscriptions and MVCC Barriers
Distributed systems that rely on streaming replication face a delicate balance between efficiency, consistency, and fault tolerance. Milvus, a popular vector database, recently tackled a subtle but critical issue in its streaming replication mechanism. By introducing remote TransformLog subscriptions with ordered, resumable delivery and multi-version concurrency control (MVCC) barriers, Milvus now supports more reliable recovery and consistent query progress tracking.
In this article, we'll explore the problem Milvus faced, the technical mechanisms introduced in the fix, and the broader lessons for anyone implementing distributed streaming with strong consistency guarantees.
---
The Problem: Inefficient Recovery and Inconsistent Streaming Progress
Before this change, Milvus’s streaming replication lacked support for remote TransformLog subscriptions that could deliver entries in order and resume from interruptions. This gap led to two main issues:
- Inefficient recovery: Without resumable cursors and ordered delivery, replaying logs during recovery was unreliable and potentially redundant.
- Inconsistent query progress tracking: The system did not preserve MVCC barriers properly, which are essential for snapshot isolation during queries. This caused checkpoints to advance prematurely and queries to see inconsistent or stale data.
Additionally, the backpressure mechanism was simplistic — it relied on a scanner mode rather than actual cache occupancy, which failed to regulate load effectively and maintain stable delivery rates.
These issues combined to create a streaming replication system that was fragile under failure conditions and complicated to maintain strong consistency guarantees.
---
The Fix: Remote Subscriptions, MVCC Barriers, and Cache-Aware Backpressure
Remote TransformLog Subscriptions with Resumable Cursors
The core innovation was introducing a new SubscribeTransform RPC mechanism that supports both local and remote owner routing. This subscription model includes a handshake protocol to establish and close subscriptions gracefully. Key features include:
- Ordered Entry and SyncUp delivery: Ensuring that TransformLog entries arrive in the correct sequence, which is crucial for consistent replay and state updates.
- Resumable cursors: If a connection drops or is interrupted, the subscription can resume from the last known position, avoiding redundant processing and speeding up recovery.
- Backoff and reconnect strategies: Failed subscription attempts back off exponentially, and reconnects are suspended when there are no active subscriptions, conserving resources.
- Idle stream reclamation: Streams that are no longer in use are cleaned up promptly, preventing resource leaks.
Aligning Recovery with WAL Consumer
Recovery logic was aligned with the Write-Ahead Log (WAL) Consumer by filtering replay events before they reach data-only handlers. This ensures that replayed events are consistent and that recovery proceeds in a controlled manner.
MVCC Barrier Coordination
MVCC barriers are preserved and scoped progress is published in WAL order without advancing durable checkpoints prematurely. This means:
- Volatile barriers are queued until RecoveryStorage confirms prefix completeness: This prevents queries from seeing inconsistent snapshots.
- Durable checkpoints advance only after safe conditions: Avoiding premature checkpoint advancement maintains snapshot isolation and query correctness.
Cache-Aware Backpressure
The previous backpressure mechanism based on scanner mode was replaced with a more sophisticated cache-occupancy-based admission control:
- WAB cache uses capacity and TTL eviction: This ensures that the cache does not overflow and that stale entries are evicted timely.
- RecoveryStorage consumption constraints: These constraints help regulate admission based on actual resource usage.
- Configurable slowdown and rejection thresholds: When the cache occupancy crosses certain thresholds, the system slows down or rejects new entries to maintain stability.
Additional Improvements
- WALSummary reads reuse verified chunk footers: This optimization prevents repopulation of retired entries, improving efficiency.
- Support for independent reader cancellation and GC snapshot protection: This enhances the robustness of concurrent reads and garbage collection.
---
Takeaway: Combining Resumable Cursors, MVCC Barriers, and Backpressure for Strong Consistency
Implementing distributed streaming subscriptions with strong consistency is challenging. Milvus’s approach highlights several key principles:
- Resumable cursors and handshake protocols are essential for robust, ordered, and fault-tolerant delivery.
- MVCC barriers must be carefully coordinated to preserve snapshot isolation and prevent premature checkpoint advancement.
- Backpressure should be cache-aware and resource-sensitive, not simplistic or mode-based, to maintain stability under load.
Together, these mechanisms enable efficient recovery, consistent query progress tracking, and reliable streaming replication.
For engineers building distributed streaming systems, this case study underscores the importance of integrating consistency controls with resource management and connection resilience.
---
This article is based on the merged PR in the milvus-io/milvus repository titled "feat: add remote TransformLog subscriptions and MVCC barriers." Read the full breakdown on nossletter.tech.
---
Originally published on Nossletter.
Backporting Row-Level Security to Milvus 2.6: A Deep Dive into Enabling Fine-Grained Access Control
Why Row-Level Security Matters in Milvus 2.6
Milvus, a popular vector database, thrives in scenarios requiring fast similarity search and analytics over large-scale vector data. With the rise of multi-tenant deployments and cloud-native architectures, enforcing fine-grained access control becomes critical. Row-level security (RLS) allows administrators to restrict data access at the individual row level, ensuring users only see data they are authorized to access.
Before this backport, Milvus 2.6 lacked RLS support, limiting its usability in secure, multi-tenant environments. This article explores the technical challenge of backporting the RLS feature from a newer Milvus version into 2.6, highlighting the intricacies of adapting modern security logic to an older codebase.
---
The Problem: Missing Row-Level Security in Milvus 2.6
Milvus 2.6 users faced a significant limitation: no ability to enforce row-level access policies during queries, data manipulation language (DML) operations, or bulk imports. This gap meant that all users with access to a collection could potentially retrieve or modify any data in it, violating the principle of least privilege.
The original RLS implementation introduced in later Milvus versions depended on a set of APIs, data structures, and synchronization mechanisms absent in 2.6. For example:
- Advanced metadata management: Newer versions use enhanced metadata stores to keep track of policies and tags.
- Proxy synchronization: Proxies maintain consistent snapshots of policies and tags to enforce security uniformly.
- Policy evaluation logic: Complex evaluation engines determine whether a given user query or DML operation complies with RLS policies.
- Protobuf and import parsing: Updated serialization formats and import mechanisms support richer policy data.
These dependencies made a straightforward backport impossible, as 2.6's codebase lacked the necessary infrastructure.
---
The Technical Solution: Backporting RLS with Compatibility
The core challenge was to isolate the RLS feature set from its newer dependencies and adapt it to fit within the constraints of Milvus 2.6. The backport involved several key technical steps:
1. Metadata Handling Adaptation
RLS relies heavily on metadata to store policies and tags associated with rows. The backport reimplemented metadata handling using 2.6's existing storage and retrieval mechanisms, ensuring that policy data could be stored, updated, and queried without relying on newer metadata APIs.
2. Proxy Synchronization Without New Membership Filters
In newer versions, proxies synchronize policy and tag snapshots using advanced membership filters to maintain consistency. Since these filters were unavailable in 2.6, the backport introduced a synchronization mechanism compatible with the older proxy architecture, ensuring that all proxies had coherent views of the current policies and tags.
3. Policy Evaluation and Enforcement
The evaluation engine was adapted to work with the older protobuf definitions and API constraints. This involved:
- Decoding policy tags correctly with 2.6's protobuf support.
- Validating policies in batches to minimize performance overhead.
- Recovering policy states gracefully after restarts or failures.
The enforcement logic was integrated into query processing, DML operations, and bulk imports, ensuring that row-level policies were consistently applied across all data access paths.
4. Maintaining Parser and Import Mechanisms
To avoid regressions, the backport preserved 2.6’s existing parser and bulk import mechanisms. Instead of replacing these components, the RLS feature was carefully woven into the existing code paths, ensuring seamless integration without disrupting established workflows.
5. Incorporating Fixes from the RLS7 Branch
The backport included critical fixes originally developed in the RLS7 branch, such as tag decoding improvements, batch validation enhancements, and policy recovery strategies. These fixes were adapted to 2.6’s environment, ensuring parity in behavior with the master branch.
---
Lessons Learned: Backporting Complex Features Across Versions
This backport illustrates several important lessons for engineering teams:
- Decouple core logic from dependencies: Successful backporting requires isolating feature logic from newer APIs and data structures, enabling adaptation to older environments.
- Adapt serialization and parsing carefully: Differences in protobuf versions and import formats can cause subtle bugs, so thorough testing and adjustment are essential.
- Preserve existing functionality: Integrating new features should avoid disrupting established workflows, particularly in widely used components like parsers and importers.
- Synchronize distributed components thoughtfully: Proxy synchronization mechanisms must be compatible with the target version’s architecture to maintain consistency.
By carefully balancing these factors, the Milvus team enabled secure, multi-tenant usage of 2.6 without sacrificing stability or performance.
---
This article is based on the Milvus PR titled "feat: [2.6] backport row-level security" from the milvus-io/milvus repository. Read the full breakdown on nossletter.tech.
---
Originally published on Nossletter.
@ShenSeanChen Hello, may I ask if the local memory of waku-Agent can be compatible with Milvus? As an option? If possible, could I submit the issue and the PR?
milvus-io/milvus
16 views
Mentions
Milvus: a high-performance, cloud-native vector database built for scalable vector ANN search (47k stars) What it does All text below is quoted directly from the project README. >Milvus is a high-performance, cloud-native vector database built for scalable vector ANN search Why Milvus: - High Performance at Scale and High Availability: Milvus features a distributed architecture that separates compute and storage. - Support for Various Vector Index Types and Hardware Acceleration: Milvus separates the system and core vector search engine, allowing it to support all major vector index types that are optimized for different scenarios, including HNSW, IVF, FLAT (brute-force), SCANN, and DiskANN, with quantization-based variations and mmap. - Flexible Multi-tenancy and Hot/Cold Storage: Milvus supports multi-tenancy through isolation at database, collection, partition, or partition key level. - Sparse Vector for Full Text Search and Hybrid Search: In addition to semantic search through dense vector, Milvus also natively supports full text search with BM25 as well as learned sparse embeddings such as SPLADE and BGE-M3. - Data Security and Fine-grain Access Control: Milvus ensures data security by implementing mandatory user authentication, TLS encryption, and Role-Based Access Control (RBAC). >Milvus Lite is a lightweight version good for quickstart in python with pip install. Repository milvus-io/milvus Repository stats |Metric|Value| |:-|:-| |Stars|46,536| |Forks|4.3k| |Last release|v3.0.5 (2026-10-30)| |Languages|Go 60.7%, C++ 21.3%, Python 17%| |Contributors|365| |AI contributor accounts (Claude / Codex / Cursor / Copilot)|@claude (668 commits), @codex (36 commits), @cursoragent (5 commits)| |AI co-authored commits|Claude: 672, Codex: 26, Cursor: 5| Main contributor |Field|Value| |:-|:-| |GitHub|@congqixia| |Commits to this repo|1,818 (top contributor)| |Followers|64| ────────────────────────────── 📌 If you want to browse more repos like this, we've organized them by category and rankings here: bestgithubrepositories.com 🗂️ We're also archiving repos where possible, so useful projects don't disappear when they're removed from GitHub.
I ran ripgrep, Serena, claude-context and my own tool on the same repo There are a lot of ways to let a coding agent find code now. Plain grep, a language server behind MCP, a vector database, a local index. Every README says its way is the right one, so I wanted to see them do the same job on the same code. I build one of these tools, Semlith, so treat my bias accordingly. That's also why I'm showing the questions it got wrong. The setup is Flask 3.1.3 on an M1 MacBook Air with 8 GB. Versions are ripgrep 15.2.0, Serena 2.1.0, claude-context 0.1.15 (with Milvus 3.0.3 and Ollama running nomic-embed-text) and semlith 0.50.0. git clone --depth 1 --branch 3.1.3 https://github.com/pallets/flask && cd flask The four approaches in one table | | ripgrep | Serena | claude-context | Semlith | |---|---|---|---|---| | What it is | Text search, no index | Language servers behind MCP | Embeddings in Milvus | Local index, embeddings plus keyword plus code graph | | Finds code by meaning | No | No | Yes | Yes | | Edits code | No | Yes, by symbol | No | No, read only | | Setup on Flask | Nothing | 30.9 s to index | About 21 min (CPU in Docker) | 84.3 s to index | | Moving parts | One binary | Python env plus language servers | Docker, Milvus, Ollama, npx | One binary | | Reads PDF, Word, Excel | No | No | No | Yes | The rest of this post is how I got those numbers. ripgrep, when you know the name When the agent already knows the identifier, nothing beats grep. rg -n max_content_length src src/flask/wrappers.py:55: _max_content_length: int | None = None src/flask/wrappers.py:60: def max_content_length(self) -> int | None: src/flask/wrappers.py:80: if self._max_content_length is not None: src/flask/wrappers.py:81: return self._max_content_length src/flask/wrappers.py:84: return super().max_content_length src/flask/wrappers.py:88: @max_content_length.setter src/flask/wrappers.py:89: def max_content_length(self, value: int | None) -> None: src/flask/wrappers.py:90: self._max_content_length = value Eight lines, all of them right, in milliseconds. Now ask the same thing the way a person would, without the name. An agent turns "limit how big an uploaded request body can be" into words and greps for them. rg -n -i "upload|big" src src/flask/cli.py:297: a bigger role. Typically it's created automatically by the src/flask/helpers.py:542: @app.route("/uploads/<path:name>") src/flask/helpers.py:545: app.config['UPLOAD_FOLDER'], name, as_attachment=True src/flask/helpers.py:549: files or uploads. Uses :func:`~werkzeug.security.safe_join` to None of these is the answer, because wrappers.py never says "upload" or "big". This is where an agent starts guessing more words and opening more files. Serena, when you want a language server Serena gives the agent symbol tools backed by real language servers, over 40 languages. Since version 2 it shows up as a REPL, so the agent writes a little Python against Serena's API. uv venv -p 3.11 venv-serena uv pip install -p venv-serena "serena-agent @ git+https://github.com/oraios/serena@v2.1.0" serena project index . Indexing took 30.9 seconds. Given the name, it's precise. s.lsp.find_symbol("max_content_length") # [{"name_path": "Request/max_content_length", "kind": "Method", # "relative_path": "src/flask/wrappers.py", "body_location": {"start_line": 87, ...}}] References come back in about 2 seconds and they come from the language server, so they are real references, not text matches. Line numbers count from zero, which caught me out the first time. Without the name though, Serena falls back to a regex pattern search, and that returns the same lines grep did. s.fs.search_for_pattern("upload|big", relative_path="src") The thing Serena does that none of the others do is edit. Replace a function body, insert after a symbol, rename across files. If your agent spends most of its time changing code it already found, that's a real advantage. claude-context, run fully local claude-context is Zilliz's MCP server. It chunks code, embeds it and stores the vectors in Milvus, with hybrid search on top. By default it wants an OpenAI key for embeddings and a Zilliz Cloud key for the database. It also supports a fully local setup, so that's what I ran. curl -sSL https://github.com/milvus-io/milvus/releases/download/v3.0.3/milvus-standalone-docker-compose.yaml -o docker-compose.yaml docker compose up -d docker run -d --name cc-ollama --cpus=4 -p 127.0.0.1:11434:11434 ollama/ollama:0.40.13 docker exec cc-ollama ollama pull nomic-embed-text EMBEDDING_PROVIDER=Ollama EMBEDDING_MODEL=nomic-embed-text \ OLLAMA_HOST=http://127.0.0.1:11434 MILVUS_ADDRESS=localhost:19530 \ npx -y @zilliz/claude-context-mcp@0.1.15 The images came to 6.14 GB and the first npx start pulled 418 MB of packages. While it worked I watched its connections, and it only talked to Milvus and Ollama on the machine. Two things bit me. On the first run Milvus exited mid-index while Ollama had every CPU, so I capped Ollama at four. The second one is worth knowing if you use it. get_indexing_status said this at 92 seconds. ✅ Codebase '…/flask' is fully indexed and ready for search. 📊 Statistics: 100 files, 100 chunks It wasn't done. The server's own log finished at 1,266.9 seconds with 86 files and 1,837 chunks. If your agent trusts that status and searches early, it searches a partial index without knowing. That 21 minutes isn't a fair speed test either. Docker on a Mac has no GPU access, so Ollama embedded on the CPU. A native Ollama install would be quicker, I just didn't measure it. Semlith, the one I build semlith index . indexed 211 files (2778 chunks, 5 images) in 84.3s — 0 already indexed, 4 skipped (4 empty), 0 removed That count includes the .rst docs, which claude-context skips by default. Serena was quicker to set up here, since it doesn't embed anything. !Horizontal bar chart of the time to index Flask 3.1.3 before the first search. ripgrep needs no index, Serena takes 30.9 seconds, Semlith 84.3 seconds and claude-context 1,266.9 seconds with Ollama embedding on the CPU in Docker. Setup time on the same checkout. Serena was the quickest of the indexed tools, and claude-context's time is CPU-only embedding in Docker. semlith search -k 5 "limit how big an uploaded request body can be" 1. 0.087 vf src/flask/wrappers.py:59-71 2. 0.083 vf src/flask/wrappers.py:91-106 3. 0.077 v docs/web-security.rst:26-38 4. 0.076 vf docs/patterns/fileuploads.rst:135-153 5. 0.072 vf src/flask/wrappers.py:105-123 5 hits in 360.027667ms The first hit is the property. The letters after the score say how it was found, v for the vector list and f for the keyword list. The question everyone got wrong I asked all of them where Flask decides which error handler runs for an exception. The answer is _find_error_handler in src/flask/sansio/app.py. Grep did best. rg -n -i "error handler" src lands on line 826 of that file, the function's docstring, because the question uses the same words the code does. Semlith's top five were all in docs/errorhandling.rst. Those pages explain the order Flask uses, so they answer the question, but they aren't the code. claude-context had the function at rank 8. So "semantic search beats grep" isn't true in general. When the question already uses the code's own words, grep is very hard to beat. Bigger samples Three questions on one repo prove nothing, so here's what I could measure at scale. On SWE-bench Verified, 500 real GitHub issues, the issue text is the query and the gold is every file the fix edits. Semlith had a right file in its top 10 for 76.8% of them, against 47.0% for one ripgrep pass ranked and read in order. That's retrieval only, not whether an agent then fixes the bug. On 20 issues from SWE-bench Lite, every tool got the same checkout and issue text. Serena 1.7.0 finished 16 of 20 (4 timed out) and had the right file in its top 5 for 25.0% of those, against 56.2% for Semlith on the same 16. That run predates Serena's REPL, so 2.1.0 might do differently. claude-context isn't in that comparison yet, so I won't give it a number. And the one that keeps me honest. On 50 questions about a single unfamiliar repo, headless Claude Code on Opus scored 92% with grep alone and 92% with Semlith installed beside it. A strong agent with room to explore gets there with grep. What each one costs before you use it Every MCP server sends its tool definitions at session start, and the agent pays for them on every turn. I counted the tools/list result at about four bytes a token. Serena tools: 2 4544 bytes, about 1136 tokens semlith tools: 8 2668 bytes, about 667 tokens Both are small. Serena keeps the list short with a REPL, then the agent reads 9,292 characters of instructions when it calls initial_instructions. Which I'd pick Honestly they do different jobs, and you can run more than one. - One repo, a strong model, names you mostly know. Grep is enough. - An agent that edits a lot by symbol. Serena. - You already run Milvus or Zilliz Cloud, or want to pick the embedding provider. claude-context. - Many repos, documents next to the code, nothing to sign up for. That's the case I built Semlith for. The full runs with every command are on semlith.com and the benchmark harness is in the repo at semlith/semlith. What are you using for code search with your agents right now, and where does it fall over?
Enhancing Distributed Streaming in Milvus: Remote TransformLog Subscriptions and MVCC Barriers Enhancing Distributed Streaming in Milvus: Remote TransformLog Subscriptions and MVCC Barriers Distributed systems that rely on streaming replication face a delicate balance between efficiency, consistency, and fault tolerance. Milvus, a popular vector database, recently tackled a subtle but critical issue in its streaming replication mechanism. By introducing remote TransformLog subscriptions with ordered, resumable delivery and multi-version concurrency control (MVCC) barriers, Milvus now supports more reliable recovery and consistent query progress tracking. In this article, we'll explore the problem Milvus faced, the technical mechanisms introduced in the fix, and the broader lessons for anyone implementing distributed streaming with strong consistency guarantees. --- The Problem: Inefficient Recovery and Inconsistent Streaming Progress Before this change, Milvus’s streaming replication lacked support for remote TransformLog subscriptions that could deliver entries in order and resume from interruptions. This gap led to two main issues: - Inefficient recovery: Without resumable cursors and ordered delivery, replaying logs during recovery was unreliable and potentially redundant. - Inconsistent query progress tracking: The system did not preserve MVCC barriers properly, which are essential for snapshot isolation during queries. This caused checkpoints to advance prematurely and queries to see inconsistent or stale data. Additionally, the backpressure mechanism was simplistic — it relied on a scanner mode rather than actual cache occupancy, which failed to regulate load effectively and maintain stable delivery rates. These issues combined to create a streaming replication system that was fragile under failure conditions and complicated to maintain strong consistency guarantees. --- The Fix: Remote Subscriptions, MVCC Barriers, and Cache-Aware Backpressure Remote TransformLog Subscriptions with Resumable Cursors The core innovation was introducing a new SubscribeTransform RPC mechanism that supports both local and remote owner routing. This subscription model includes a handshake protocol to establish and close subscriptions gracefully. Key features include: - Ordered Entry and SyncUp delivery: Ensuring that TransformLog entries arrive in the correct sequence, which is crucial for consistent replay and state updates. - Resumable cursors: If a connection drops or is interrupted, the subscription can resume from the last known position, avoiding redundant processing and speeding up recovery. - Backoff and reconnect strategies: Failed subscription attempts back off exponentially, and reconnects are suspended when there are no active subscriptions, conserving resources. - Idle stream reclamation: Streams that are no longer in use are cleaned up promptly, preventing resource leaks. Aligning Recovery with WAL Consumer Recovery logic was aligned with the Write-Ahead Log (WAL) Consumer by filtering replay events before they reach data-only handlers. This ensures that replayed events are consistent and that recovery proceeds in a controlled manner. MVCC Barrier Coordination MVCC barriers are preserved and scoped progress is published in WAL order without advancing durable checkpoints prematurely. This means: - Volatile barriers are queued until RecoveryStorage confirms prefix completeness: This prevents queries from seeing inconsistent snapshots. - Durable checkpoints advance only after safe conditions: Avoiding premature checkpoint advancement maintains snapshot isolation and query correctness. Cache-Aware Backpressure The previous backpressure mechanism based on scanner mode was replaced with a more sophisticated cache-occupancy-based admission control: - WAB cache uses capacity and TTL eviction: This ensures that the cache does not overflow and that stale entries are evicted timely. - RecoveryStorage consumption constraints: These constraints help regulate admission based on actual resource usage. - Configurable slowdown and rejection thresholds: When the cache occupancy crosses certain thresholds, the system slows down or rejects new entries to maintain stability. Additional Improvements - WALSummary reads reuse verified chunk footers: This optimization prevents repopulation of retired entries, improving efficiency. - Support for independent reader cancellation and GC snapshot protection: This enhances the robustness of concurrent reads and garbage collection. --- Takeaway: Combining Resumable Cursors, MVCC Barriers, and Backpressure for Strong Consistency Implementing distributed streaming subscriptions with strong consistency is challenging. Milvus’s approach highlights several key principles: - Resumable cursors and handshake protocols are essential for robust, ordered, and fault-tolerant delivery. - MVCC barriers must be carefully coordinated to preserve snapshot isolation and prevent premature checkpoint advancement. - Backpressure should be cache-aware and resource-sensitive, not simplistic or mode-based, to maintain stability under load. Together, these mechanisms enable efficient recovery, consistent query progress tracking, and reliable streaming replication. For engineers building distributed streaming systems, this case study underscores the importance of integrating consistency controls with resource management and connection resilience. --- This article is based on the merged PR in the milvus-io/milvus repository titled "feat: add remote TransformLog subscriptions and MVCC barriers." Read the full breakdown on nossletter.tech. --- Originally published on Nossletter.
Backporting Row-Level Security to Milvus 2.6: A Deep Dive into Enabling Fine-Grained Access Control Why Row-Level Security Matters in Milvus 2.6 Milvus, a popular vector database, thrives in scenarios requiring fast similarity search and analytics over large-scale vector data. With the rise of multi-tenant deployments and cloud-native architectures, enforcing fine-grained access control becomes critical. Row-level security (RLS) allows administrators to restrict data access at the individual row level, ensuring users only see data they are authorized to access. Before this backport, Milvus 2.6 lacked RLS support, limiting its usability in secure, multi-tenant environments. This article explores the technical challenge of backporting the RLS feature from a newer Milvus version into 2.6, highlighting the intricacies of adapting modern security logic to an older codebase. --- The Problem: Missing Row-Level Security in Milvus 2.6 Milvus 2.6 users faced a significant limitation: no ability to enforce row-level access policies during queries, data manipulation language (DML) operations, or bulk imports. This gap meant that all users with access to a collection could potentially retrieve or modify any data in it, violating the principle of least privilege. The original RLS implementation introduced in later Milvus versions depended on a set of APIs, data structures, and synchronization mechanisms absent in 2.6. For example: - Advanced metadata management: Newer versions use enhanced metadata stores to keep track of policies and tags. - Proxy synchronization: Proxies maintain consistent snapshots of policies and tags to enforce security uniformly. - Policy evaluation logic: Complex evaluation engines determine whether a given user query or DML operation complies with RLS policies. - Protobuf and import parsing: Updated serialization formats and import mechanisms support richer policy data. These dependencies made a straightforward backport impossible, as 2.6's codebase lacked the necessary infrastructure. --- The Technical Solution: Backporting RLS with Compatibility The core challenge was to isolate the RLS feature set from its newer dependencies and adapt it to fit within the constraints of Milvus 2.6. The backport involved several key technical steps: 1. Metadata Handling Adaptation RLS relies heavily on metadata to store policies and tags associated with rows. The backport reimplemented metadata handling using 2.6's existing storage and retrieval mechanisms, ensuring that policy data could be stored, updated, and queried without relying on newer metadata APIs. 2. Proxy Synchronization Without New Membership Filters In newer versions, proxies synchronize policy and tag snapshots using advanced membership filters to maintain consistency. Since these filters were unavailable in 2.6, the backport introduced a synchronization mechanism compatible with the older proxy architecture, ensuring that all proxies had coherent views of the current policies and tags. 3. Policy Evaluation and Enforcement The evaluation engine was adapted to work with the older protobuf definitions and API constraints. This involved: - Decoding policy tags correctly with 2.6's protobuf support. - Validating policies in batches to minimize performance overhead. - Recovering policy states gracefully after restarts or failures. The enforcement logic was integrated into query processing, DML operations, and bulk imports, ensuring that row-level policies were consistently applied across all data access paths. 4. Maintaining Parser and Import Mechanisms To avoid regressions, the backport preserved 2.6’s existing parser and bulk import mechanisms. Instead of replacing these components, the RLS feature was carefully woven into the existing code paths, ensuring seamless integration without disrupting established workflows. 5. Incorporating Fixes from the RLS7 Branch The backport included critical fixes originally developed in the RLS7 branch, such as tag decoding improvements, batch validation enhancements, and policy recovery strategies. These fixes were adapted to 2.6’s environment, ensuring parity in behavior with the master branch. --- Lessons Learned: Backporting Complex Features Across Versions This backport illustrates several important lessons for engineering teams: - Decouple core logic from dependencies: Successful backporting requires isolating feature logic from newer APIs and data structures, enabling adaptation to older environments. - Adapt serialization and parsing carefully: Differences in protobuf versions and import formats can cause subtle bugs, so thorough testing and adjustment are essential. - Preserve existing functionality: Integrating new features should avoid disrupting established workflows, particularly in widely used components like parsers and importers. - Synchronize distributed components thoughtfully: Proxy synchronization mechanisms must be compatible with the target version’s architecture to maintain consistency. By carefully balancing these factors, the Milvus team enabled secure, multi-tenant usage of 2.6 without sacrificing stability or performance. --- This article is based on the Milvus PR titled "feat: [2.6] backport row-level security" from the milvus-io/milvus repository. Read the full breakdown on nossletter.tech. --- Originally published on Nossletter.
@ShenSeanChen Hello, may I ask if the local memory of waku-Agent can be compatible with Milvus? As an option? If possible, could I submit the issue and the PR? milvus-io/milvus