Skip to content

feat: add safe index configuration serialization - #780

Open
Claire-ovo wants to merge 2 commits into
redis:mainfrom
Claire-ovo:feat/index-config-serialization
Open

Claire-ovo wants to merge 2 commits into
redis:mainfrom
Claire-ovo:feat/index-config-serialization

Conversation

@Claire-ovo

@Claire-ovo Claire-ovo commented Oct 22, 2026 •

Copy link
Copy Markdown

Motivation

SearchIndex and AsyncSearchIndex can load schemas but cannot export a portable index configuration. This adds shared serialization with connection details omitted by default. Closes #493. Thanks to @thakoreh for the earlier work in #525 / #542; this implementation revisits the URL and restore concerns raised there.

Changes

  • Add synchronous to_dict() / to_yaml() methods shared by both index classes. Schema-only output remains compatible with existing factory calls; serialization does not create a Redis client or read the environment.
  • With include_connection=True, export a connection mapping containing redis_url, allowlisted connection_kwargs, and requires_override. Strip URL userinfo, all query parameters and fragments; omit unsupported options and injected-client internals.
  • Refuse implicit restoration after omissions. A caller must provide a replacement URL/client and any needed options, so a redacted configuration cannot silently connect to a different REDIS_URL. Explicit endpoint overrides discard stored options; explicit option mappings replace rather than merge. from_existing() instances also require a replacement because those factories internally supply a client.
  • Preserve IPv6 authorities, Sentinel host lists and Unix socket URLs; use safe YAML loading and serialize before opening an output file.

The connection mapping and conservative omission policy are proposed for maintainer review; no format agreement is assumed. This does not add secret management or serialize live Redis data/client state.

Release Notes

Adds index configuration serialization for sync and async indexes. Existing schema-only factories remain supported. Connection exports with omitted settings require an explicit complete connection at restore time. Suggested release label: auto:minor.

Review record

Reviewed at dd3ad554e2f5e6feaff233a6dafb74a49c41e5f3 by Codex and isolated review agents using pre-pr-review (the runtime model identifier is not exposed in this session). This implementation and review used AI assistance.

Lenses: correctness (factories and sync/async parity), security (credential omission and restoration), design (public API), tests (regressions), docs (new public behavior). Skipped: Redis server behavior (no server commands, reply handling or server defaults changed).

  • Six concerns evaluated; three retained and resolved, followed by review of the final commit.
  • Fixed: valid hostless URLs were rejected (redisvl/index/index.py:898); clarify the internally injected-client policy of from_existing() (docs/api/searchindex.rst:59); add independent query/fragment guard tests (tests/unit/test_index_serialization.py:165). An in-memory mutation removing that guard now makes all four new cases fail; it previously passed the suite.
  • Rejected: changing client provenance/ownership solely to export from_existing() connections (conservative exclusion is intentional); making async-index YAML export asynchronous (the API is explicitly synchronous); accepting credential-only overrides for incomplete metadata (complete replacement avoids implicit endpoint changes).
  • Open for the reviewer: approve the public connection mapping and allowlist. The earlier issue discussion has not yet received format feedback.

Verified on Python 3.12.14 / redis-py 8.1.0:

  • uv run pytest --confcutdir=tests/unit tests/unit/test_index_serialization.py tests/unit/test_schema.py tests/unit/test_index_gc_finalizer.py tests/unit/test_index_schema_type_issue.py -q -o log_cli=false --tb=short: 118 passed (80 serialization cases, 38 existing regressions).
  • make format, make check-types: passed; mypy checked 120 source files.
  • uv run pre-commit run --files redisvl/index/index.py tests/unit/test_index_serialization.py docs/api/searchindex.rst: passed both configured hooks; git diff --check origin/main...HEAD: passed.
  • uv run sphinx-build -q -E -b html docs docs/_build/html: succeeded; warnings are in unchanged docs/dependency docstrings. Incremental build of the changed page also succeeded.

Additional verification at the same head: REDIS_IMAGE=public.ecr.aws/docker/library/redis:8.4 uv run pytest tests/unit/test_index_serialization.py tests/integration/test_search_index.py tests/integration/test_async_search_index.py -q -o log_cli=false --tb=short --maxfail=3 passed all 179 cases (80 serialization unit cases and 99 existing sync/async index integration cases) using the unmodified repository Docker fixture. The Docker Official Images ECR mirror was used because Docker Hub resolution timed out; image digest: sha256:6a12a187547eb2eac4da4528f9d9787d69f3848e513480baedfd75e3b2ac393e.

A separate manual smoke check passed 16 export/restore/create/load/fetch/count cases across sync/async, dict/YAML, HASH/JSON, and unauthenticated/password-protected disposable Redis 8.4 instances. It checked password omission and refusal to restore redacted metadata without explicit connection credentials, with a deliberately unreachable REDIS_URL as the environment fallback. These 16 cases are manual verification, not additions to the repository test suite. Disposable indexes and containers were cleaned up.

Not checked: real TLS/Sentinel/Cluster connections, provider APIs, notebooks and the full test/dependency-version matrix. Earlier Docker setup and Docker Hub pull failures occurred before assertions; the successful mirror-backed run above supersedes the former local Docker blocker. Full-repository pre-commit was also attempted: its code-quality hook passed, while pinned codespell changed the existing Unparseable comment in redisvl/redis/connection.py:460; that unrelated edit was reverted and the changed-file run passed. Local checks are not a claim about GitHub CI.


Note

Medium Risk
Changes public factory/restore behavior for configs with connection metadata and encodes security-sensitive rules around credentials and implicit REDIS_URL use; mistakes could break deployments that relied on env-based restore after export.

Overview
Adds portable index configuration export/import for SearchIndex and AsyncSearchIndex via shared to_dict() / to_yaml() on the base index class. Default output stays schema-only (no Redis connection, no REDIS_URL), so existing from_dict / from_yaml behavior is unchanged.

With include_connection=True, exports a connection block (redis_url, allowlisted connection_kwargs, requires_override) that strips credentials, URL query/fragment, and non-safe client options. Restoring redacted or injected-client configs requires an explicit redis_url or redis_client—no silent fallback to the environment. from_dict / from_yaml now accept full exported configs and apply explicit override rules (replacement URL/client drops stored kwargs; partial overrides are rejected).

Docs cover serialization semantics; a large unit test suite locks down round-trips, redaction, and restore safety for sync and async indexes.

Reviewed by Cursor Bugbot for commit dd3ad55. Bugbot is set up for automated code reviews on this repo. Configure here.

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

None yet

Development

Successfully merging this pull request may close these issues.

Add SearchIndex/AsyncSearchIndex serialization helpers

1 participant