Skip to content

refactor(kuzu): Reflect KuzuDB deprecation and provide users with an exit route (migrating to another DB) - #1302

Open
macunha1 wants to merge 2 commits into
CodeGraphContext:mainfrom
macunha1:main
Open

macunha1 wants to merge 2 commits into
CodeGraphContext:mainfrom
macunha1:main

Conversation

@macunha1

Copy link
Copy Markdown

Why this is worth considering?

CGC currently treats KuzuDB as a normal runtime backend, but KuzuDB is deprecated: it was archived upstream and its Python package no longer installs cleanly on newer Python versions such as Python 3.14. As archived repos are read-only, we can't fix that. That turns Kuzu into an adoption blocker: a user can hit a packaging failure before they ever get to the value of CGC itself.

This exit path is, unfortunately, inevitable. The practical question is whether we make that path explicit and supported, or whether users discover it through broken installs. This PR proposes the cleaner route: keep KuzuDB data reachable through migration, but stop depending on KuzuDB as an active runtime backend.

The product story becomes cleaner for new users:

  • FalkorDB Lite remains the fast local default where it is available.
  • LadybugDB covers cross-platform embedded installs.
  • FalkorDB Remote, Neo4j, and Nornic cover shared or managed graph deployments.
  • Existing KuzuDB users get a documented migration route instead of an unexplained hard cutoff.

That direction lets us keep the "works locally, works with agents, works with teams" message without carrying an archived database dependency in the main install path.

What changed

This branch moves KuzuDB from "supported backend" to "deprecated legacy migration source":

  • Removes the active KuzuDB runtime backend and Kuzu-specific code paths.
  • Updates backend selection so supported choices are falkordb, ladybugdb, falkordb-remote, neo4j, and nornic.
  • Uses LadybugDB as the cross-platform embedded fallback when FalkorDB Lite is not available.
  • Adds automatic legacy KuzuDB migration into the configured supported backend.
  • Adds a dedicated KuzuDB migration guide for users who still have an existing Kuzu store.
  • Updates README, CLI docs, setup docs, troubleshooting docs, VS Code extension copy, and backend comparison docs so the public story matches the supported runtime surface.
  • Fixes the FalkorDB worker path for redis-py 8 Unix socket behavior.
  • Adds unit and integration coverage around migration behavior and backend selection.

Why this route

The alternative is to keep treating KuzuDB as an equal backend and try to work around its already limited state. That may buy short-term compatibility, but it leaves CGC tied to an archived upstream dependency and makes future Python support difficult. Since the deprecation pressure already exists, delaying KuzuDB migration does not avoid the exit.

This route keeps the useful part of KuzuDB support (protecting existing user data) while removing the part that creates ongoing install risk. It also makes the backend matrix easier to communicate:

  • New local users get FalkorDB Lite first, with LadybugDB as the cross-platform embedded option.
  • Teams can use FalkorDB Remote, Neo4j, or Nornic when they want shared infrastructure.
  • Existing KuzuDB users get a migration bridge instead of being left behind.

That feels like the right product tradeoff: less dependency risk for new adoption, without leaving behind existing data and users.

User-facing impact

For new users, installation and setup should be more predictable because the default path no longer depends on an archived package. They will never have to worry about KuzuDB

For existing KuzuDB users, the recommended path is migration. CGC detects legacy KuzuDB stores, exports them through the bundle flow, and imports them into the configured supported backend when the target is empty. The docs also explain the Docker-based migration path for environments where KuzuDB only works under an older Python image. This makes the deprecation visible and gives users a controlled exit path before newer Python environments make that decision for them.

Validation

The branch adds and updates tests for:

  • Legacy KuzuDB migration behavior.
  • Backend selection after removing KuzuDB as a runtime backend.
  • FalkorDB remote/session handling.
  • CLI configuration behavior.

Scope

Try and deprecate KuzuDB as a production runtime backend, with a narrow intent: acknowledge that KuzuDB was archived, won't be supported anymore, and consequently should also be deprecated in CGC's supported runtime surface. Reduce CGC installation and maintenance risk, and give existing KuzuDB data a clear exit path into supported backends.

@vercel

vercel Bot commented Jun 21, 2026

Copy link
Copy Markdown

@macunha1 is attempting to deploy a commit to the shashankss1205's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Shashankss1205

Copy link
Copy Markdown
Collaborator

👋 Thanks for contributing to CodeGraphContext! Since this PR was opened, main has moved forward significantly (we just shipped v0.5.2 and merged ~50 PRs), so this one now has merge conflicts. Could you please rebase onto the latest main and resolve the conflicts? Once it is conflict-free and CI is green, we will review and merge it. Really appreciate your work — thank you! 🙏

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Triage update: thank you for the thorough work here, @macunha1 — upstream KuzuDB's status is a real concern and this deserves a proper decision rather than a drive-by review. Flagging for @Shashankss1205: this PR proposes deprecating the Kùzu backend and giving users a migration path. Main currently still ships Kùzu as a first-class backend (with Ladybug as the successor lineage), so the call on direction needs to come from the project. Leaving open until that decision is made.

@macunha1

macunha1 commented Aug 2, 2026 •

Copy link
Copy Markdown
Author

Triage update: thank you for the thorough work here, @macunha1 — upstream KuzuDB's status is a real concern and this deserves a proper decision rather than a drive-by review. Flagging for @Shashankss1205: this PR proposes deprecating the Kùzu backend and giving users a migration path. Main currently still ships Kùzu as a first-class backend (with Ladybug as the successor lineage), so the call on direction needs to come from the project. Leaving open until that decision is made.

@Shashankss1205 done ✅

Rebased onto the latest main and resolved all the conflicts, and updated the test suite to cover new cases. The PR is conflict-free now. 👌

Triage update: thank you for the thorough work here, @macunha1 — upstream KuzuDB's status is a real concern and this deserves a proper decision rather than a drive-by review. Flagging for @Shashankss1205: this PR proposes deprecating the Kùzu backend and giving users a migration path. Main currently still ships Kùzu as a first-class backend (with Ladybug as the successor lineage), so the call on direction needs to come from the project. Leaving open until that decision is made.

All good. Please let me know once the project direction is decided, or if you see the need for any more changes to the migration path or implementation in this PR. I can update it accordingly.

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Flagging this for a maintainer decision rather than reviewing it as a routine change.

The PR is well-argued and the diff is coherent (24 files, mergeable, no conflicts). But it changes the product's supported-backend story — retiring KùzuDB as a runtime backend and providing a migration path — and that is a direction call for @Shashankss1205, not something to merge on technical correctness alone.

The case you make is real and worth stating plainly for whoever decides:

Against it: KùzuDB is currently the zero-config default on Windows and the FalkorDB fallback, so this touches the default install path for a whole platform, and #1512 shows the simulator is already Kùzu/Ladybug-fragile in ways that would need to move with it.

Two things that would help the decision regardless of outcome:

  1. What happens to a user with an existing Kùzu graph on upgrade — is the migration automatic, prompted, or manual?
  2. Does LadybugDB cover every backend capability Kùzu currently provides? bug(graph): simulator's relationship query uses labels(n1)[0] and a bare pattern predicate, so simulate_metrics always fails on KùzuDB/LadybugDB #1512 suggests at least the simulator's labels(n1)[0] path does not work on either embedded backend today.

Holding rather than merging.

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Thanks for the very thorough write-up — the reasoning about carrying an archived upstream dependency is sound, and the migration guide plus legacy_kuzu_migration.py (with 700 lines of tests) is exactly the right way to protect existing users' data.

Two things before this can land.

1. Three tests fail on a trial-merge onto main:

FAILED tests/unit/core/test_backend_fallback_logging.py::test_falkordb_to_kuzu_fallback_names_kuzu
FAILED tests/unit/core/test_backend_fallback_logging.py::test_kuzu_missing_fallback_names_the_replacement
FAILED tests/unit/core/test_database_falkordb_wrapper.py::test_factory_retries_falkordb_for_a_different_database_path

(1140 pass.) The first two are expected fallout from the deprecation and just need updating to assert the new behaviour. The third points at something bigger — see below.

2. There's a second breaking change bundled in here that isn't in the PR description.
core/__init__.py:190 now makes explicit FalkorDB selection strict:

Database set to 'falkordb' but FalkorDB Lite is not supported or not installed. Explicit FalkorDB selection is strict and will not fall back to LadybugDB.

Today -db falkordb falls back when FalkorDB Lite isn't functional; after this PR it hard-fails. That's a defensible design (explicit beats implicit), but it's an independent behavioural change from the Kuzu deprecation and it's what breaks test_factory_retries_falkordb_for_a_different_database_path. Could you either split it into its own PR or call it out explicitly in the description so it gets its own discussion?

Beyond that, removing a documented backend is a product call rather than a code-review one, so I'm holding this for @Shashankss1205 to weigh in on the direction before merge — the implementation isn't the question here. Worth noting kuzudb is still fully functional on this machine (I used it end-to-end earlier today), so this is a deliberate deprecation rather than a forced one; that makes the timing a judgement call.

Please do fix the three tests in the meantime — that part is unambiguous.

@macunha1

Copy link
Copy Markdown
Author

Thank you for the concrete follow-up @Shashankss1205

Quick status update after rebasing onto current main (v0.6.5):

  • Resolved the conflicts and fixed all three trial-merge failures. The tests now assert strict explicit backend selection while preserving path-scoped FalkorDB retry coverage.

  • Kept strict FalkorDB selection isolated in its own commit (c3a6b35). Selecting FalkorDB now fails loudly instead of silently switching stores.

  • Fixed a LadybugDB 0.11 relationship-batching issue found during parity testing, plus XDG config loading and Falkor worker startup from source layouts.

Local validation covers:

  • 93 affected tests,
  • 19 API compatibility tests, and
  • full E2E graph parity across LadybugDB, FalkorDB, and Neo4j (including 720 CALLS edges, 715 distinct).

@Shashankss1205 Shashankss1205 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the thorough rebase @macunha1 — the strict-selection commit, the XDG fixes, and the parity validation are all solid. But the trial-merge surfaced a structural problem that blocks this as-is, and it sits in the PR's headline feature.

The kuzu and ladybug pybind natives cannot coexist in one process

On the trial-merged tree, the unit suite segfaults at test_ladybug_unwind_batching.py (Linux, ladybug 0.19.1). The chain, fully reproduced:

  1. get_database_manager() runs _maybe_migrate_legacy_kuzudb on every call. On any machine where find_legacy_kuzudb_source finds old data (env/config/sibling — my dev HOME qualifies, as will most upgraders'), the migration path executes import kuzu.
  2. kuzu's pybind module registers its types. Any subsequent ladybug pybind import in the same process then fails with ImportError: generic_type: type "Database" is already registered! (captured by instrumenting ladybug._backend._import_pybind_module).
  3. ladybug._backend.get_pybind_module caches the failed attempt permanently and falls back to the C-API backend — which the PyPI wheel doesn't ship — so every later ladybug use raises RuntimeError: Could not find lbug C API shared library, and some orderings segfault outright in _execute_with_pybind.

The critical part: the migration itself is this exact sequence — _export_legacy_kuzu_bundle(kuzu, …) reads via kuzu, then CGCBundle(target_manager) writes via ladybug, in one process. So the auto-migration a real upgrader triggers is precisely the crashing path. Your E2E parity validation wouldn't catch it because the e2e harness drives each backend in its own subprocess.

Suggested fixes

  • Run the kuzu-side export in a subprocess (kuzu → .cgc bundle on disk is already the design — a python -c/module subprocess that only imports kuzu keeps the natives apart), then import the bundle in-process with the target manager. This also improves the Python 3.14 story: the subprocess can even use a different interpreter if you ever want that escape hatch.
  • Gate the migration probe to once per process/path rather than every get_database_manager call — besides the perf, the probe scanning env/config/sibling on every factory call is what let a mock-based unit test (test_factory_retries_falkordb_for_a_different_database_path) poison the process for the ladybug tests that follow, purely because my machine has legacy data. Tests shouldn't be able to reach the developer's real HOME here.
  • Pin a ladybug floor (ladybug>=0.19): 0.17.1 segfaults inside _execute_with_pybind on the current schema DDL (reproduced before upgrading) — with no floor, users on older installs get a hard crash instead of a version message.

Happy to re-run the full gate as soon as these land — everything else in the trial-merge looked good (unit suite green once the collision is avoided by test selection, and the renames are clean).

@macunha1

Copy link
Copy Markdown
Author

heey @Shashankss1205!!

Thanks for the detailed investigation. I implemented all three safeguards in the latest push because they address separate failure modes:

  • Kuzu export now runs in an isolated subprocess, so Kuzu and Ladybug never register their pybind types in the same process. The resulting .cgc bundle is still imported by the selected backend in-process.

  • Migration discovery is gated once per normalized database path and process. Repeated factory calls no longer rescan the environment or allow tests to reach a developer's legacy data accidentally.

  • Ladybug is pinned to ladybug>=0.19, preventing older releases from crashing during schema DDL.

The subprocess also supports CGC_KUZU_MIGRATION_PYTHON, which provides the Python 3.14 escape hatch you suggested: the archived Kuzu driver can live in a separate older Python environment without becoming a dependency of the CGC runtime.

On top of that, the documented Docker migration remains available as the fallback when a compatible local Python interpreter is unavailable.

Please, let me know in case you find more issues after running the full suite

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Coming back to this during a queue sweep. It conflicts with main now, but I owe you a direction answer before asking for a rebase, because the premise has changed since you opened it.

KùzuDB is no longer on an exit path for CGC — it's become the default embedded backend. The embedded layer (core/database_embedded_kuzu.py) is pinned to kuzu==0.11.3 and carries a substantial compatibility shim: synthesized uid primary keys, a workaround for MERGE-in-UNWIND mis-binding on non-adjacent repeated node keys, and a per-row fallback for node-MERGE-plus-relationship shapes. LadybugDB, one of the four backends in the parity matrix, is a Kùzu fork that delegates straight to that same module. Removing Kùzu today would mean removing the default install experience and half the parity matrix with it.

So I can't take the deprecation framing. That said, two things in this PR are worth keeping, and I don't want them lost with the rest:

  1. The migration route itself. "I have an existing graph in backend A and want it in backend B" is a real user need that CGC answers badly right now, independent of any backend being deprecated. If the export/import path here is backend-agnostic — or can be made so — that is a feature I want, and it would land much faster as its own PR with the deprecation removed.
  2. Whatever drove you to write this. A -3,896-line diff usually means you hit something concrete and painful. If there are specific Kùzu limitations behind it — Limitations of KuzuDB with CGC when using it with claude code #727 covers some of the Claude Code friction, and perf(kuzudb): write_function_call_groups takes 5.8 min for 886 CALLS edges on 80-file project #1168 the write_function_call_groups performance problem — please describe them in an issue. Several are fixable within the compatibility layer, and I'd rather fix them than lose the information about what's broken.

Concretely: I'd close this as-is and open a focused "migrate an indexed graph between backends" PR, which I'd review promptly. If you think I'm wrong about Kùzu's position, make that case here — I'll engage with it seriously, I just can't act on the deprecation without it.

Thanks for the substantial work regardless; the fact that it no longer fits is about where the project went, not about the quality of what you wrote.

@macunha1

macunha1 commented Oct 3, 2026

Copy link
Copy Markdown
Author

Thanks for the direct answer @Shashankss1205.

I agree that the current PR needs to be rebased and narrowed, and that the bundle migration should become backend-agnostic.
I do not agree, however, that KuzuDB should remain a normal CGC runtime backend indefinitely.

KuzuDB is archived, and 0.11.3 is its final release.

The compatibility layer is valuable, but it is CGC-owned mitigation around a frozen engine. It demonstrates that we can keep Kuzu working today; it does not give us an upstream path when a future Python version, compiler, platform, or planner behavior requires another engine change.

We have already crossed that boundary once. #1444 stopped installing Kuzu on Python 3.14 for macOS and Windows because no compatible wheels exist and pip falls back to a native source build.

I reproduced both sides:

  • Current main installs and initializes Kuzu on Python 3.14 Linux because a manylinux wheel exists.
  • Forcing the source-distribution path enters the pyproject.toml build and fails in a clean environment because the native build toolchain is missing.

That means the installation problem was avoided through platform markers, not fixed in Kuzu. macOS and Windows users have already moved away from Kuzu on Python 3.14, so keeping Linux on the archived backend gives us platform-dependent behavior and postpones the same decision until another wheel disappears.

The data boundary makes this more urgent. I created a Kuzu 0.11.3 database and tried to open it with LadybugDB 0.19.1 in a separate process. Ladybug rejected it as not a valid Lbug database file. Existing state therefore requires an explicit export/import migration; changing the dependency or reusing the directory is not sufficient.

LadybugDB is the maintained continuation of Kuzu and already delegates through the same database_embedded_kuzu.py adapter. Retiring the Kuzu runtime does not require deleting that adapter, its compatibility shims, or their tests. We can retain and retarget that coverage to Ladybug. It also removes one backend from the four-backend parity matrix, not half (including Ladybug) with it.

I suggest a staged version of the same direction:

  1. Land the backend-neutral bundle migration first.
  2. Make LadybugDB the canonical maintained backend for the CGC use cases currently served by Kuzu, while marking Kuzu runtime selection as deprecated.
  3. Keep Kuzu available only for migration through the isolated subprocess, a compatible older interpreter, or the documented Docker route.
  4. Remove Kuzu from normal dependency resolution and runtime selection after a migration window, while preserving the migration tooling for existing databases.

This keeps existing user data recoverable and preserves the substantial adapter work. It also moves ordinary users onto one maintained implementation before another Python or platform transition turns the archived dependency into a harder blocker.

I am happy to rebuild this as a focused, rebased series rather than trying to land the current broad diff unchanged. My concern is with dropping the retirement direction entirely: every additional feature or workaround built around the archived runtime increases the eventual migration cost without creating an upstream capable of fixing the underlying engine.

As a final step, I’ll push an updated implementation for one last review. The revised proposal will preserve compatibility with Kuzu as a legacy, upstream-archived backend while positioning LadybugDB as the maintained replacement for all embedded-database use cases currently covered by Kuzu.

+ Remove Kuzu from normal dependency resolution and implicit selection, so
  clean Python 3.14 installs do not depend on an archived native package.
+ Migrate legacy stores through an isolated .cgc exporter before importing
  through the selected manager, which avoids native type collisions and direct
  storage-format reuse.
+ Gate probes by backend and path and refuse uncertain or populated targets, so
  factory retries cannot repeatedly inspect HOME or silently merge data.
+ Keep Kuzu explicit and pinned during the migration window, which preserves
  data recovery without presenting the archived engine as maintained.
+ Name Ladybug as the maintained embedded fallback across installation,
  configuration, architecture, and contributor guidance, so new users are not
  directed toward an archived runtime.
+ Add a one-way migration guide covering backups, an alternate interpreter,
  and Docker or nerdctl, which keeps existing Kuzu data recoverable when local
  wheels are unavailable.
+ Mark Kuzu as explicit legacy compatibility during the migration window and
  document that its storage directory cannot be reused by Ladybug.

@Shashankss1205 Shashankss1205 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conflicting broad Kuzu/Ladybug migration; CI is red. Latest contributor discussion agrees narrower backend-agnostic migration/rebase. Settle maintenance direction before retiring a backend.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog tasks

Development

Successfully merging this pull request may close these issues.

2 participants