Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
74da77a
refactor: use dlopen
richyreachy Jun 24, 2026
17a7fd5
refactor: use dlopen to load libaio
richyreachy Jun 25, 2026
8ef5d2d
fix: log out the warning only once
richyreachy Jun 26, 2026
50c6c8d
fix: add log
richyreachy Jun 26, 2026
ceee226
fix: add log
richyreachy Jun 26, 2026
5e77ee3
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jun 26, 2026
6d774ed
refactor: merge main
richyreachy Jun 26, 2026
e2a2ede
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jun 30, 2026
a2524eb
fix: fix windows build
richyreachy Jul 1, 2026
ac109ae
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 1, 2026
6688e1a
fix: remove libaio dependency
richyreachy Jul 1, 2026
760fb7a
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 2, 2026
f2559b5
fix: remove no use code
richyreachy Jul 2, 2026
d753080
fix: fix makefile
richyreachy Jul 2, 2026
bc7b843
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 2, 2026
292322c
fix: fix ut
richyreachy Jul 3, 2026
a0a9ce9
fix: remove export map
richyreachy Jul 3, 2026
69b1e23
fix: clean code
richyreachy Jul 3, 2026
081aee3
fix: add ci job to test libaio
richyreachy Jul 3, 2026
ab8901a
fix: fix on comment
richyreachy Jul 6, 2026
63f5ff7
fix: remove diskann runtime
richyreachy Jul 6, 2026
5ffc23a
refactor: move libaio loader to ailego
richyreachy Jul 7, 2026
1c7ff60
fix: add io backend
richyreachy Jul 7, 2026
54eaa11
fix: add io backend
richyreachy Jul 7, 2026
9870923
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 7, 2026
f19defb
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 7, 2026
241dd7a
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 8, 2026
e1048c0
fix: remove ci
richyreachy Jul 8, 2026
fde7813
fix: add ci
richyreachy Jul 8, 2026
6496a0b
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 8, 2026
4567d99
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 8, 2026
ca89c30
fix: debug output
richyreachy Jul 8, 2026
01b702c
fix: run ci
richyreachy Jul 8, 2026
e0b12db
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 9, 2026
9385548
feat: change naming
richyreachy Jul 9, 2026
cee2cf1
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 9, 2026
d0a01cd
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 10, 2026
86e4f68
fix: fix naming
richyreachy Jul 10, 2026
360c96d
feat: add back end io func
richyreachy Jul 10, 2026
4aff06f
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 10, 2026
07b2e99
fix: make file
richyreachy Jul 10, 2026
5c378ca
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 10, 2026
845bb38
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 10, 2026
233339b
feat: add python hint
richyreachy Jul 10, 2026
2e9773e
fix: stub and consistency
richyreachy Jul 10, 2026
082d96a
fix: format
richyreachy Jul 10, 2026
35551e4
fix: fix naming
richyreachy Jul 11, 2026
5f62fb8
fix: fix type
richyreachy Jul 11, 2026
ce8c35e
fix for pr comments
iaojnh Jul 14, 2026
a15e5ed
upd libaio workflow
iaojnh Jul 14, 2026
3475b50
Merge branch 'main' into refactor/diskann_dlopen
iaojnh Jul 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions .github/workflows/03-macos-linux-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,6 @@ jobs:
sudo apt-get install -y clang libomp-dev
shell: bash

- name: Install AIO
Comment thread
feihongxu0824 marked this conversation as resolved.
if: runner.os == 'Linux' && runner.arch == 'X64'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libaio-dev
shell: bash

- name: Print CPU info
if: runner.os == 'Linux'
run: lscpu
Expand Down Expand Up @@ -183,3 +175,41 @@ jobs:
./c_api_index_example
./c_api_optimized_example
shell: bash

# ------------------------------------------------------------------ #
# DiskAnn libaio round: install libaio and re-run tests
# ------------------------------------------------------------------ #
- name: Install libaio runtime
if: matrix.platform == 'linux-x64'
run: |
sudo apt-get update -y
# libaio1t64 on Ubuntu 24.04+ (t64 transition), libaio1 on older
sudo apt-get install -y libaio1t64 || sudo apt-get install -y libaio1
echo "=== libaio status (should be present) ==="
dpkg -l | grep -i libaio || true
ldconfig -p | grep libaio || true
shell: bash

- name: Run DiskAnn C++ Tests (w/ libaio)
if: matrix.platform == 'linux-x64'
run: |
cd "$GITHUB_WORKSPACE/build"
ctest -R diskann --output-on-failure --parallel $NPROC
shell: bash

- name: Run DiskAnn Python Tests (w/ libaio)
if: matrix.platform == 'linux-x64'
run: |
cd "$GITHUB_WORKSPACE"
python -m pytest python/tests/test_collection_diskann.py -v
shell: bash

# Verify installing libaio does not affect existing non-DiskAnn tests.
- name: Run HNSW Tests (w/ libaio)
if: matrix.platform == 'linux-x64'
run: |
cd "$GITHUB_WORKSPACE/build"
ctest -R hnsw --output-on-failure --parallel $NPROC
cd "$GITHUB_WORKSPACE"
python -m pytest python/tests/test_hnsw_contiguous_memory.py -v
shell: bash
7 changes: 0 additions & 7 deletions .github/workflows/08-cmake-subproject-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ jobs:
cache: 'pip'
cache-dependency-path: 'pyproject.toml'

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libaio-dev
shell: bash

- name: Set up environment variables
run: |
NPROC=$(nproc 2>/dev/null || echo 2)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/clang_tidy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
if: steps.changed_files.outputs.any_changed == 'true'
run: |
sudo apt-get update
sudo apt-get install -y clang-tidy=1:18.0-59~exp2 cmake ninja-build libomp-dev libaio-dev
sudo apt-get install -y clang-tidy=1:18.0-59~exp2 cmake ninja-build libomp-dev
- name: Setup ccache
if: steps.changed_files.outputs.any_changed == 'true'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nightly_coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ jobs:
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
lcov libaio-dev
sudo apt-get install -y --no-install-recommends lcov
sudo apt-get install -y libaio1t64 || sudo apt-get install -y libaio1
shell: bash

- name: Install dependencies
Expand Down
23 changes: 4 additions & 19 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ else()
endif()
message(STATUS "RABITQ_ARCH_FLAG: ${RABITQ_ARCH_FLAG}")

# DiskAnn support (Linux x86_64 only, requires libaio)
# DiskAnn support (Linux x86_64 only; libaio loaded at runtime via dlopen)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i686|i386" AND NOT ANDROID AND NOT IOS)
set(DISKANN_SUPPORTED ON)
add_definitions(-DDISKANN_SUPPORTED=1)
Expand Down Expand Up @@ -180,24 +180,9 @@ if(BUILD_PYTHON_BINDINGS)
install(TARGETS _zvec LIBRARY DESTINATION ${ZVEC_PY_INSTALL_DIR}/zvec
COMPONENT python)

# DiskAnn ships as a runtime-loaded shared module
# (libzvec_diskann_plugin.so) that is brought online implicitly the
# first time a DiskAnn index is created — users never call any load
# function. The Python extension resolves the module next to _zvec.so
# (see the $ORIGIN rpath in src/binding/python/CMakeLists.txt); the
# module must therefore be installed alongside _zvec.so, i.e. inside the
# zvec package directory as well.
#
# Gate on DISKANN_SUPPORTED, not on the target's existence: on unsupported
# platforms (e.g. macOS / ARM64) the core_knn_diskann target is still
# defined, but built from an empty stub (src/core/algorithm/CMakeLists.txt)
# with zero exported symbols and a runtime load path compiled out
# (#if DISKANN_SUPPORTED). Shipping that stub is pure dead weight, so it is
# only packaged where DiskAnn is real — currently Linux x86_64 with libaio.
if(DISKANN_SUPPORTED)
install(TARGETS core_knn_diskann LIBRARY DESTINATION ${ZVEC_PY_INSTALL_DIR}/zvec
COMPONENT python)
endif()
# DiskAnn is now statically linked into _zvec.so via --whole-archive
# (see src/binding/python/CMakeLists.txt -> core_knn_diskann_static),
# so no separate runtime .so needs to be installed in the wheel.
# Bundle cppjieba's dictionary files so the `jieba` FTS tokenizer works
# out of the box. python/zvec/__init__.py resolves this directory via
# importlib.resources and registers it with set_default_jieba_dict_dir().
Expand Down
4 changes: 3 additions & 1 deletion examples/c++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ set(ZVEC_INCLUDE_DIR ${ZVEC_ROOT_DIR}/src/include)
set(ZVEC_LIB_DIR ${ZVEC_ROOT_DIR}/${HOST_BUILD_DIR}/lib)

# Add include and library search paths
include_directories(${ZVEC_INCLUDE_DIR})
# src/include → public headers (<zvec/...>)
# src → internal headers (<ailego/...>) referenced by public headers
include_directories(${ZVEC_INCLUDE_DIR} ${ZVEC_ROOT_DIR}/src)
set(ZVEC_LIB_SEARCH_DIRS ${ZVEC_LIB_DIR})

# Support multi-config builds (MSVC puts libs in Debug/Release subdirectories)
Expand Down
5 changes: 0 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,6 @@ test-command = "cd {project} && pytest python/tests -v --tb=short"
build-verbosity = 1

[tool.cibuildwheel.linux]
# libaio is required by the C++ backend; install it inside the manylinux
# container (manylinux_2_28 is AlmaLinux 8 based, so use dnf/libaio-devel).
# libaio-devel lives in BaseOS; disable EPEL so a flaky EPEL mirror cannot
# break metadata refresh.
before-all = "dnf install -y --disablerepo=epel libaio-devel"
archs = ["auto"]
environment = { CMAKE_GENERATOR = "Unix Makefiles", CMAKE_BUILD_PARALLEL_LEVEL = "16" }
manylinux-x86_64-image = "manylinux_2_28"
Expand Down
30 changes: 4 additions & 26 deletions python/tests/detail/fixture_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,14 @@
from zvec.typing import DataType, StatusCode, MetricType, QuantizeType
import zvec


# Cache the DiskAnn plugin preload status so we pay the load cost once per
# test session. The plugin normally auto-loads on first DiskAnn use, but we
# preload it explicitly here so a missing libaio / misplaced plugin .so
# surfaces as a clear pytest skip instead of a confusing
# "Create vector column indexer failed" deep inside the collection code path.
_DISKANN_PRELOAD_REASON: str | None = None
_DISKANN_PRELOAD_DONE: bool = False


def _ensure_diskann_runtime_or_reason() -> str | None:
"""Preload the DiskAnn plugin and return None on success or a human-readable
skip reason on failure. Idempotent across calls."""
"""Check whether DiskAnn is available on this platform and return None
on success or a human-readable skip reason on failure. Idempotent across
calls."""
global _DISKANN_PRELOAD_DONE, _DISKANN_PRELOAD_REASON
if _DISKANN_PRELOAD_DONE:
return _DISKANN_PRELOAD_REASON
Expand All @@ -35,22 +30,6 @@ def _ensure_diskann_runtime_or_reason() -> str | None:
_DISKANN_PRELOAD_REASON = "DiskAnn only supported on Linux x86_64"
return _DISKANN_PRELOAD_REASON

if not zvec.is_libaio_available():
_DISKANN_PRELOAD_REASON = (
"libaio is not available on this host; DiskAnn cannot run. "
"Install libaio1 (or libaio1t64 on Ubuntu 24.04+) and retry."
)
return _DISKANN_PRELOAD_REASON

status = zvec.load_diskann_plugin()
if status != zvec.DISKANN_PLUGIN_OK:
_DISKANN_PRELOAD_REASON = (
f"Failed to load DiskAnn plugin (status={status}); "
"check that libzvec_diskann_plugin.so is installed alongside "
"_zvec.so in the Python site-packages directory."
)
return _DISKANN_PRELOAD_REASON

_DISKANN_PRELOAD_REASON = None
return None

Expand Down Expand Up @@ -148,8 +127,7 @@ def full_schema_new(request) -> CollectionSchema:
else:
nullable, has_index, vector_index = True, False, HnswIndexParam()

# Skip DiskAnn tests on unsupported platforms or when the runtime cannot
# be brought up (missing libaio, plugin .so not installed, etc.).
# Skip DiskAnn tests on unsupported platforms.
from zvec.model.param import DiskAnnIndexParam

if isinstance(vector_index, DiskAnnIndexParam):
Expand Down
18 changes: 4 additions & 14 deletions python/tests/test_collection_diskann.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@
# limitations under the License.
"""End-to-end collection tests for the DiskAnn index.

Mirrors ``test_collection_hnsw_rabitq.py`` but targets the DiskAnn plugin.
Mirrors ``test_collection_hnsw_rabitq.py`` but targets the DiskAnn index.

Two platform-level prerequisites are enforced at module import time:

1. DiskAnn is currently built only for Linux x86_64 — other platforms are
skipped wholesale.
2. The DiskAnn backend lives in a *runtime-loaded* plugin
(``libzvec_diskann_plugin.so``). It must be loaded with ``RTLD_GLOBAL |
RTLD_NOW`` BEFORE ``import zvec`` so that the plugin's ``IndexFactory``
singleton is unified with the one inside ``_zvec.so``. After ``import
zvec`` we must also call ``zvec.load_diskann_plugin()`` exactly once.
2. libaio is loaded eagerly (via dlopen) inside DiskAnnBuilder::init() /
DiskAnnStreamer::init(). If libaio is missing, DiskAnn falls back to
synchronous pread() — the tests still run but with degraded performance.

If either prerequisite fails the whole module is skipped so the rest of the
test-suite is not affected.
Expand All @@ -32,7 +30,6 @@
from __future__ import annotations

import math
import os
import platform
import sys

Expand All @@ -46,13 +43,6 @@
reason="DiskAnn plugin is only supported on Linux x86_64",
)

# Promote all symbols in subsequently-loaded DSOs to the global namespace and
# resolve relocations eagerly. This is REQUIRED so the DiskAnn plugin can see
# the ``IndexFactory`` singleton that lives in ``_zvec.so`` and vice versa.
# See: DiskAnn RTLD_GLOBAL + RTLD_NOW Requirement.
if sys.platform == "linux":
sys.setdlopenflags(sys.getdlopenflags() | os.RTLD_GLOBAL | os.RTLD_NOW)

import zvec # noqa: E402

from zvec import ( # noqa: E402
Expand Down
Loading
Loading