{
  "id": "index_migration",
  "title": "Migrate an Index: Vector Quantization, Resume, Backup, and Wizard",
  "url": "https://redis.io/docs/latest/develop/ai/redisvl/0.20.0/user_guide/how_to_guides/index_migration/",
  "summary": "",
  "content": "\n\n\nThe index migrator is an **experimental** feature. APIs, CLI commands, and\non-disk formats (plans, backups) may change in future releases. Review\nmigration plans carefully before applying them to production indexes.\n\n\nThis guide walks through a **vector quantization** migration\n(`float32` -\u003e `float16`) end to end using the programmatic API. You will\nlearn how to:\n\n- Build a schema patch that describes the change\n- Generate and review a migration **plan** (read-only)\n- **Apply** the migration with a mandatory on-disk backup\n- Find **where the backup lives** and inspect its progress header\n- Understand **crash-safe resume** and safely re-run a migration\n- **Reload original vectors from the backup** (rollback)\n- Build and apply the same migration through the **wizard**\n\nFor conceptual background see\n[Index Migrations](https://redis.io/docs/latest/../../concepts/index-migrations) and the\n[Migrate an Index how-to](https://redis.io/docs/latest/how_to_guides/migrate-indexes).\n\n**Prerequisites:** a running Redis 8.0+ (or Redis Stack) at\n`redis://localhost:6379` and `redisvl` installed.\n\n\n```python\nimport os\nimport glob\nimport numpy as np\nimport yaml\n\nfrom redisvl.index import SearchIndex\nfrom redisvl.redis.utils import array_to_buffer\nfrom redisvl.query import VectorQuery\n\nREDIS_URL = \"redis://localhost:6379\"\nINDEX_NAME = \"products\"\nDIMS = 8\nN_DOCS = 600\n\nnp.random.seed(42)\n\n\ndef delete_matching(client, pattern, batch_size=500):\n    deleted = 0\n    batch = []\n    for key in client.scan_iter(match=pattern, count=batch_size):\n        batch.append(key)\n        if len(batch) \u003e= batch_size:\n            deleted += client.delete(*batch)\n            batch = []\n    if batch:\n        deleted += client.delete(*batch)\n    return deleted\n```\n\n## 1. Create a source index with float32 vectors\n\nWe start with a small Hash index whose `embedding` field stores full\nprecision `float32` vectors, then load some random data.\n\n\n```python\nschema = {\n    \"index\": {\n        \"name\": INDEX_NAME,\n        \"prefix\": \"product\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\"name\": \"name\", \"type\": \"text\"},\n        {\"name\": \"category\", \"type\": \"tag\"},\n        {\n            \"name\": \"embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"algorithm\": \"flat\",\n                \"dims\": DIMS,\n                \"distance_metric\": \"cosine\",\n                \"datatype\": \"float32\",\n            },\n        },\n    ],\n}\n\nindex = SearchIndex.from_dict(schema, redis_url=REDIS_URL)\nif index.exists():\n    index.delete(drop=True)\nstale_keys = delete_matching(index.client, \"product:*\")\nif stale_keys:\n    print(f\"Removed {stale_keys} stale demo key(s)\")\nindex.create()\n\nvectors = np.random.rand(N_DOCS, DIMS).astype(np.float32)\ndata = [\n    {\n        \"name\": f\"product {i}\",\n        \"category\": \"electronics\" if i % 2 == 0 else \"books\",\n        \"embedding\": array_to_buffer(vectors[i], dtype=\"float32\"),\n    }\n    for i in range(N_DOCS)\n]\nkeys = index.load(data)\nprint(f\"Loaded {len(keys)} documents into '{INDEX_NAME}'\")\n```\n\n    Loaded 600 documents into 'products'\n\n\n\n```python\n!rvl index listall --url redis://localhost:6379\n```\n\n    Indices:\n    1. products\n\n\n## 2. Describe the change with a schema patch\n\nA **schema patch** lists only what changes. Here we update the\n`embedding` field's datatype from `float32` to `float16` (a 2x memory\nreduction). We write it to a YAML file the planner can read.\n\n\n```python\npatch = {\n    \"version\": 1,\n    \"changes\": {\n        \"update_fields\": [\n            {\"name\": \"embedding\", \"attrs\": {\"datatype\": \"float16\"}},\n        ]\n    },\n}\n\nwith open(\"schema_patch.yaml\", \"w\") as f:\n    yaml.safe_dump(patch, f, sort_keys=False)\n\nprint(open(\"schema_patch.yaml\").read())\n```\n\n    version: 1\n    changes:\n      update_fields:\n      - name: embedding\n        attrs:\n          datatype: float16\n    \n\n\n## 3. Create a migration plan (read-only)\n\n`create_plan` snapshots the live index, diffs it against the patch, and\nreturns a `MigrationPlan`. **No data is modified.** Review the warnings\nand the classified changes before applying.\n\n\n```python\nfrom redisvl.migration import MigrationPlanner, MigrationExecutor\nfrom redisvl.migration.utils import write_yaml\n\nplanner = MigrationPlanner()\nplan = planner.create_plan(\n    index_name=INDEX_NAME,\n    schema_patch_path=\"schema_patch.yaml\",\n    redis_url=REDIS_URL,\n)\n\nprint(\"Index:     \", plan.source.index_name)\nprint(\"Mode:      \", plan.mode)\nprint(\"Requested: \", plan.requested_changes)\nprint(\"Warnings:  \")\nfor w in plan.warnings:\n    print(\"  -\", w)\n\n# Plans can be persisted to YAML and reloaded later (or via the CLI)\nwrite_yaml(plan.model_dump(), \"migration_plan.yaml\")\nprint(\"\\nSaved plan to migration_plan.yaml\")\n```\n\n    Index:      products\n    Mode:       drop_recreate\n    Requested:  {'version': 1, 'changes': {'add_fields': [], 'remove_fields': [], 'update_fields': [{'name': 'embedding', 'attrs': {'datatype': 'float16'}, 'options': {}}], 'rename_fields': [], 'index': {}}}\n    Warnings:  \n      - Index downtime is required\n    \n    Saved plan to migration_plan.yaml\n\n\n## 4. Apply the migration with a mandatory backup\n\nThe executor requires `backup_dir` before applying any migration. For\nquantization, it writes original vectors to disk before mutating them. If\nyou omit `backup_dir`, or pass a path that cannot be created or written,\nthe migration fails before touching the index. The returned report records\nthe resolved backup directory and any backup file prefixes used.\n\nWe also pass a `progress_callback` to watch each phase.\n\n\nThis drops and recreates the index definition. Documents are preserved;\nonly the index structure and vector encoding change. Pause writes during\nthe migration window.\n\n\n\n```python\nBACKUP_DIR = \"./migration_backups\"\n\n# The migration does not start without a usable backup directory.\nexecutor = MigrationExecutor()\ntry:\n    executor.apply(plan, redis_url=REDIS_URL, backup_dir=None)\nexcept ValueError as exc:\n    print(\"Missing backup_dir is rejected before migration:\", exc)\n\nBAD_BACKUP_DIR = \"./not_a_backup_dir\"\nif os.path.isdir(BAD_BACKUP_DIR):\n    os.rmdir(BAD_BACKUP_DIR)\nwith open(BAD_BACKUP_DIR, \"w\") as f:\n    f.write(\"this file intentionally blocks directory creation\")\ntry:\n    executor.apply(plan, redis_url=REDIS_URL, backup_dir=BAD_BACKUP_DIR)\nexcept ValueError as exc:\n    print(\"Invalid backup_dir is rejected before migration:\", exc)\nfinally:\n    if os.path.exists(BAD_BACKUP_DIR):\n        os.remove(BAD_BACKUP_DIR)\n\n\ndef on_progress(step, detail=None):\n    print(f\"[{step}] {detail or ''}\")\n\n\nreport = executor.apply(\n    plan,\n    redis_url=REDIS_URL,\n    backup_dir=BACKUP_DIR,\n    batch_size=100,\n    num_workers=1,\n    progress_callback=on_progress,\n)\n\nprint(\"\\nResult:           \", report.result)\nprint(\"Total duration:   \", report.timings.total_migration_duration_seconds, \"s\")\nprint(\"Quantize duration:\", report.timings.quantize_duration_seconds, \"s\")\nprint(\"Schema match:     \", report.validation.schema_match)\nprint(\"Doc count match:  \", report.validation.doc_count_match)\nprint(\"Backup dir:       \", report.backup.backup_dir)\nprint(\"Backup prefixes:  \", report.backup.backup_paths)\n\nBACKUP_PREFIX = report.backup.backup_paths[0]\n```\n\n    Missing backup_dir is rejected before migration: A backup directory is required to apply migrations. Provide --backup-dir or backup_dir=...; migrations are not started without a backup directory.\n    Invalid backup_dir is rejected before migration: Could not create or access backup directory './not_a_backup_dir': [Errno 17] File exists: 'not_a_backup_dir'. A writable backup directory is required to safely migrate.\n    [enumerate] Enumerating indexed documents...\n    [enumerate] found 600 documents (0.003s)\n    [dump] Backing up original vectors...\n    [dump] 100/600 docs\n    [dump] 200/600 docs\n    [dump] 300/600 docs\n    [dump] 400/600 docs\n    [dump] 500/600 docs\n    [dump] 600/600 docs\n    [dump] done (0.009s)\n    [drop] Dropping index definition...\n    [drop] done (0.001s)\n    [quantize] Re-encoding vectors from backup...\n    [quantize] 100/600 docs\n    [quantize] 200/600 docs\n    [quantize] 300/600 docs\n    [quantize] 400/600 docs\n    [quantize] 500/600 docs\n    [quantize] 600/600 docs\n    [quantize] done (600 docs in 0.009s)\n    [create] Creating index with new schema...\n\n\n    [create] done (0.004s)\n    [index] Waiting for re-indexing...\n    [index] 22/115 docs (19%)\n\n\n    [index] 600/600 docs (100%)\n    [index] done (0.508s)\n    [validate] Validating migration...\n    [validate] done (0.01s)\n    \n    Result:            succeeded\n    Total duration:    0.554 s\n    Quantize duration: 0.009 s\n    Schema match:      True\n    Doc count match:   True\n    Backup dir:        /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups\n    Backup prefixes:   ['/Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8']\n\n\n## 5. Where is the backup, and what's in it?\n\nBackups are written under `report.backup.backup_dir`. For a single-worker\nHash quantization migration there are two files per index:\n\n- `migration_backup_\u003cindex\u003e_\u003chash\u003e.header` -- JSON: phase + progress counters\n- `migration_backup_\u003cindex\u003e_\u003chash\u003e.data` -- binary: original vectors, batched\n\nThe `\u003chash\u003e` suffix is a short digest of the index name, which avoids\ncollisions. `report.backup.backup_paths` stores the path prefix without\n`.header` or `.data`. Multi-worker migrations record one prefix per\nworker.\n\nBackups are **retained after success** so you can audit or roll back;\ndelete them manually when no longer needed.\n\n\n```python\nfor path in sorted(glob.glob(os.path.join(report.backup.backup_dir, '*'))):\n    size = os.path.getsize(path)\n    print(f\"{path}  ({size:,} bytes)\")\n```\n\n    /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8.data  (47,730 bytes)\n    /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8.header  (209 bytes)\n\n\n\n```python\nfrom redisvl.migration.backup import VectorBackup\n\n# load() takes the path prefix, without .header or .data.\nbackup = VectorBackup.load(BACKUP_PREFIX)\nh = backup.header\nprint(\"backup prefix:             \", BACKUP_PREFIX)\nprint(\"index_name:                \", h.index_name)\nprint(\"phase:                     \", h.phase)\nprint(\"batch_size:                \", h.batch_size)\nprint(\"dump_completed_batches:    \", h.dump_completed_batches)\nprint(\"quantize_completed_batches:\", h.quantize_completed_batches)\n```\n\n    backup prefix:              /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8\n    index_name:                 products\n    phase:                      completed\n    batch_size:                 100\n    dump_completed_batches:     6\n    quantize_completed_batches: 6\n\n\n## 6. Crash-safe resume and checkpointing\n\nIf a migration is interrupted by a crash, network drop, or `Ctrl+C`, just\nre-run the same command with the same `backup_dir`. The executor loads the\nbackup header, validates that it belongs to the planned source index, and\ncontinues from the next unfinished batch.\n\nThe header is the checkpoint for single-index migrations:\n\n- `phase` shows where the previous run stopped: `dump`, `ready`, `active`, or `completed`\n- `dump_completed_batches` counts original-vector batches safely written to `.data`\n- `quantize_completed_batches` counts batches already re-encoded and written back to Redis\n\n```bash\n# Re-running the same CLI command resumes automatically:\nrvl migrate apply --plan migration_plan.yaml \\\n  --backup-dir ./migration_backups --url redis://localhost:6379\n```\n\nBatch migrations use the same per-index backup headers, plus a batch state\nYAML file that records the current index, completed indexes, failed\nindexes, and the `backup_dir`. Resume rejects a different backup directory\nso the checkpoint and backup files stay together.\n\nWhen `phase` is `completed`, re-running is safe if the live index already\nmatches the target schema: the executor detects the finished backup, skips\ncompleted work, and leaves the already-created index in place. If you have\nrolled back and the live index is back on the source schema, the old\ncompleted backup is stale for a new migration run; the executor discards\nthat checkpoint and writes a fresh backup.\n\n\n```python\nskip = backup.header.quantize_completed_batches\nprint(f\"Checkpoint says {skip} batch(es) were already quantized.\")\nprint(f\"Current phase: {backup.header.phase}\")\n\nprint(\"\\nRe-running apply with the same backup_dir to exercise resume detection...\")\nresume_report = executor.apply(\n    plan,\n    redis_url=REDIS_URL,\n    backup_dir=BACKUP_DIR,\n    batch_size=100,\n    num_workers=1,\n    progress_callback=on_progress,\n)\nprint(\"Resume result:     \", resume_report.result)\nprint(\"Resume backup dir: \", resume_report.backup.backup_dir)\nprint(\"Resume prefixes:   \", resume_report.backup.backup_paths)\n```\n\n    Checkpoint says 6 batch(es) were already quantized.\n    Current phase: completed\n    \n    Re-running apply with the same backup_dir to exercise resume detection...\n    [enumerate] skipped (resume from backup)\n    [drop] skipped (already dropped)\n    [quantize] skipped (already completed)\n    [create] Creating index with new schema...\n    [create] done (0.004s)\n    [index] Waiting for re-indexing...\n    [index] 600/600 docs (100%)\n    [index] done (0.001s)\n    [validate] Validating migration...\n    [validate] done (0.008s)\n    Resume result:      succeeded\n    Resume backup dir:  /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups\n    Resume prefixes:    ['/Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8']\n\n\n## 7. Verify the quantized index\n\nThe documents were preserved and the `embedding` field is now `float16`.\nWe reconnect to the live index and run a vector query (encoding the query\nvector to match the new datatype).\n\n\n```python\nrestored = SearchIndex.from_existing(INDEX_NAME, redis_url=REDIS_URL)\nemb = next(f for f in restored.schema.to_dict()['fields'] if f['name'] == 'embedding')\nprint(\"embedding datatype now:\", emb['attrs']['datatype'])\n\nq = VectorQuery(\n    vector=vectors[0].tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"name\", \"category\"],\n    dtype=\"float16\",\n    num_results=3,\n)\nfor r in restored.query(q):\n    print(r[\"name\"], \"| category:\", r[\"category\"], \"| dist:\", r[\"vector_distance\"])\n```\n\n    embedding datatype now: float16\n    product 0 | category: electronics | dist: 0\n    product 223 | category: books | dist: 0.0458984375\n    product 23 | category: books | dist: 0.04736328125\n\n\n## 8. Recover original vectors from the backup (rollback)\n\nBecause the backup holds the original `float32` bytes, you can recover the\npre-migration vector data. The CLI provides a one-liner:\n\n```bash\nrvl migrate rollback --backup-dir ./migration_backups \\\n  --index products --url redis://localhost:6379\n```\n\nBelow is the equivalent **Python API**: iterate the backup batches and\nwrite the original bytes back with `HSET`. Rollback restores **data only**;\nafterwards recreate the original index definition so the index encoding\nmatches the restored vectors again.\n\n\n```python\nclient = restored.client\n\nrestored_count = 0\nfor batch_keys, originals in backup.iter_batches():\n    pipe = client.pipeline(transaction=False)\n    for key in batch_keys:\n        if key in originals:\n            for field_name, original_bytes in originals[key].items():\n                pipe.hset(key, field_name, original_bytes)\n                restored_count += 1\n    pipe.execute()\n\nprint(f\"Restored original bytes for {restored_count} vector field(s)\")\n\n# Recreate the ORIGINAL float32 index definition over the restored data\noriginal_index = SearchIndex.from_dict(schema, redis_url=REDIS_URL)\noriginal_index.create(overwrite=True, drop=False)\n\ncheck = SearchIndex.from_existing(INDEX_NAME, redis_url=REDIS_URL)\nemb = next(f for f in check.schema.to_dict()['fields'] if f['name'] == 'embedding')\nprint(\"embedding datatype after rollback:\", emb['attrs']['datatype'])\n```\n\n    Restored original bytes for 600 vector field(s)\n    embedding datatype after rollback: float32\n\n\n## 9. Build and apply a migration with the wizard\n\nFor exploratory work, `MigrationWizard` can build the same schema patch and\nmigration plan interactively. In a notebook, we script the answers so the\ncell can execute without blocking. The sequence below means: update a\nfield, choose `embedding`, keep the current algorithm, change datatype to\n`float16`, keep the distance metric, then finish.\n\nThe wizard still only creates the patch and plan. Applying the plan remains\na separate reviewed step, and `backup_dir` is still required.\n\n\n```python\nimport builtins\nimport copy\nfrom contextlib import contextmanager\n\nfrom redisvl.migration import MigrationWizard\nfrom redisvl.migration.utils import wait_for_index_ready\n\nWIZARD_INDEX_NAME = \"wizard_products\"\nWIZARD_PREFIX = \"wizard_product\"\nWIZARD_PATCH_PATH = \"wizard_schema_patch.yaml\"\nWIZARD_PLAN_PATH = \"wizard_migration_plan.yaml\"\nWIZARD_TARGET_SCHEMA_PATH = \"wizard_target_schema.yaml\"\nWIZARD_BACKUP_DIR = \"./wizard_migration_backups\"\n\nwizard_schema = copy.deepcopy(schema)\nwizard_schema[\"index\"][\"name\"] = WIZARD_INDEX_NAME\nwizard_schema[\"index\"][\"prefix\"] = WIZARD_PREFIX\n\n# Start from a clean wizard demo index and keyspace.\ntry:\n    existing_wizard_index = SearchIndex.from_existing(\n        WIZARD_INDEX_NAME, redis_url=REDIS_URL\n    )\n    existing_wizard_index.delete(drop=True)\nexcept Exception:\n    pass\ndelete_matching(client, f\"{WIZARD_PREFIX}:*\")\n\nwizard_index = SearchIndex.from_dict(wizard_schema, redis_url=REDIS_URL)\nwizard_index.create()\nwizard_index.load(data, id_field=None)\nwait_for_index_ready(wizard_index)\nprint(f\"Loaded {N_DOCS} documents into '{WIZARD_INDEX_NAME}'\")\n\n\n@contextmanager\ndef scripted_inputs(answers):\n    original_input = builtins.input\n    iterator = iter(answers)\n\n    def fake_input(prompt=\"\"):\n        answer = next(iterator)\n        print(f\"{prompt}{answer}\")\n        return answer\n\n    builtins.input = fake_input\n    try:\n        yield\n    finally:\n        builtins.input = original_input\n\n\nwizard_answers = [\n    \"2\",          # Update field\n    \"embedding\",  # Select the vector field\n    \"\",           # Keep algorithm\n    \"float16\",    # Quantize datatype\n    \"\",           # Keep distance metric\n    \"8\",          # Finish\n]\n\nwith scripted_inputs(wizard_answers):\n    wizard_plan = MigrationWizard().run(\n        index_name=WIZARD_INDEX_NAME,\n        redis_url=REDIS_URL,\n        plan_out=WIZARD_PLAN_PATH,\n        patch_out=WIZARD_PATCH_PATH,\n        target_schema_out=WIZARD_TARGET_SCHEMA_PATH,\n    )\n\nprint(\"\\nWizard patch:\")\nprint(open(WIZARD_PATCH_PATH).read())\nprint(\"Wizard plan mode:\", wizard_plan.mode)\nprint(\"Wizard warnings:\", wizard_plan.warnings)\n```\n\n    Loaded 600 documents into 'wizard_products'\n    Building a migration plan for index 'wizard_products'\n    Current schema:\n    - Index name: wizard_products\n    - Storage type: hash\n      - name (text)\n      - category (tag)\n      - embedding (vector)\n    \n    Choose an action:\n    1. Add field        (text, tag, numeric, geo)\n    2. Update field     (sortable, weight, separator, vector config)\n    3. Remove field\n    4. Rename field     (rename field in all documents)\n    5. Rename index     (change index name)\n    6. Change prefix    (rename all keys)\n    7. Preview patch    (show pending changes as YAML)\n    8. Finish\n    Enter a number: 2\n    Updatable fields:\n    1. name (text)\n    2. category (tag)\n    3. embedding (vector)\n    Select a field to update by number or name: embedding\n    Current vector config for 'embedding':\n      algorithm: FLAT\n      datatype: float32\n      distance_metric: cosine\n      dims: 8 (cannot be changed)\n    \n    Leave blank to keep current value.\n      Algorithm: vector search method (FLAT=brute force, HNSW=graph, SVS-VAMANA=compressed graph)\n    Algorithm [current: FLAT]: \n      Datatype: float16, float32, bfloat16, float64, int8, uint8\n                (float16 reduces memory ~50%, int8/uint8 reduce ~75%)\n    Datatype [current: float32]: float16\n      Distance metric: how similarity is measured (cosine, l2, ip)\n    Distance metric [current: cosine]: \n    \n    Choose an action:\n    1. Add field        (text, tag, numeric, geo)\n    2. Update field     (sortable, weight, separator, vector config)\n    3. Remove field\n    4. Rename field     (rename field in all documents)\n    5. Rename index     (change index name)\n    6. Change prefix    (rename all keys)\n    7. Preview patch    (show pending changes as YAML)\n    8. Finish\n    Enter a number: 8\n    \n    Wizard patch:\n    version: 1\n    changes:\n      add_fields: []\n      remove_fields: []\n      update_fields:\n      - name: embedding\n        attrs:\n          datatype: float16\n        options: {}\n      rename_fields: []\n      index: {}\n    \n    Wizard plan mode: drop_recreate\n    Wizard warnings: ['Index downtime is required']\n\n\n\n```python\nwizard_report = MigrationExecutor().apply(\n    wizard_plan,\n    redis_url=REDIS_URL,\n    backup_dir=WIZARD_BACKUP_DIR,\n    batch_size=100,\n    num_workers=1,\n)\n\nwizard_live = SearchIndex.from_existing(WIZARD_INDEX_NAME, redis_url=REDIS_URL)\nwizard_embedding = next(\n    f for f in wizard_live.schema.to_dict()[\"fields\"] if f[\"name\"] == \"embedding\"\n)\n\nprint(\"Wizard migration result:\", wizard_report.result)\nprint(\"Wizard schema match:    \", wizard_report.validation.schema_match)\nprint(\"Wizard doc count match: \", wizard_report.validation.doc_count_match)\nprint(\"Wizard backup dir:      \", wizard_report.backup.backup_dir)\nprint(\"Wizard backup prefixes: \", wizard_report.backup.backup_paths)\nprint(\"Wizard embedding dtype: \", wizard_embedding[\"attrs\"][\"datatype\"])\n```\n\n    Wizard migration result: succeeded\n    Wizard schema match:     True\n    Wizard doc count match:  True\n    Wizard backup dir:       /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/wizard_migration_backups\n    Wizard backup prefixes:  ['/Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/wizard_migration_backups/migration_backup_wizard_products_def8cdf8']\n    Wizard embedding dtype:  float16\n\n\n## 10. Cleanup (optional)\n\nRemove the demo indexes and the artifacts this notebook created. In\nproduction, delete backups only once you are certain rollback is no longer\nneeded.\n\n\n```python\ndelete_matching(client, \"product:*\")\nif check.exists():\n    check.delete(drop=False)\n\ndelete_matching(client, f\"{WIZARD_PREFIX}:*\")\nif wizard_live.exists():\n    wizard_live.delete(drop=False)\n\nfor backup_dir in (report.backup.backup_dir, wizard_report.backup.backup_dir):\n    for f in glob.glob(os.path.join(backup_dir, '*')):\n        os.remove(f)\n    if os.path.isdir(backup_dir):\n        os.rmdir(backup_dir)\n\nfor f in (\n    \"schema_patch.yaml\",\n    \"migration_plan.yaml\",\n    \"not_a_backup_dir\",\n    WIZARD_PATCH_PATH,\n    WIZARD_PLAN_PATH,\n    WIZARD_TARGET_SCHEMA_PATH,\n):\n    if os.path.exists(f):\n        os.remove(f)\nprint(\"Cleaned up demo indexes, demo keys, backups, and YAML files\")\n```\n\n    Cleaned up demo indexes, demo keys, backups, and YAML files\n\n\n## Learn more\n\n- [Migrate an Index (how-to)](https://redis.io/docs/latest/how_to_guides/migrate-indexes) -- full CLI\n  workflow, batch migration, performance tuning, and troubleshooting\n- [Index Migrations (concepts)](https://redis.io/docs/latest/../../concepts/index-migrations) -- modes,\n  supported vs blocked changes, backup internals, sync vs async\n- For very large datasets, use `num_workers \u003e 1` and the async executor\n  (`AsyncMigrationExecutor`) to parallelize re-encoding.\n",
  "tags": [],
  "last_updated": "2026-06-11T16:10:09-04:00"
}
