Skip to content

feat(migrate): [4/7] Batch migration with multi-index support#563

Closed
nkanu17 wants to merge 8 commits intofeat/migrate-asyncfrom
feat/migrate-batch
Closed

feat(migrate): [4/7] Batch migration with multi-index support#563
nkanu17 wants to merge 8 commits intofeat/migrate-asyncfrom
feat/migrate-batch

Conversation

@nkanu17
Copy link
Copy Markdown
Collaborator

@nkanu17 nkanu17 commented Apr 1, 2026

Summary

Adds batch migration support for migrating multiple indexes in a single operation. Includes glob-based index selection, ordered execution, failure policies, and checkpoint-based state tracking for crash-safe resume.

Usage

# Plan a batch migration for indexes matching a pattern
rvl migrate batch-plan --schema-patch patch.yaml --pattern "*_idx"

# Plan for specific indexes
rvl migrate batch-plan --schema-patch patch.yaml --indexes idx1,idx2,idx3

# Apply the batch plan
rvl migrate batch-apply --plan batch_plan.yaml

# Resume an interrupted batch
rvl migrate batch-resume --state batch_state.yaml

# Check batch status
rvl migrate batch-status --state batch_state.yaml

What is included

  • Batch planner (redisvl/migration/batch_planner.py): BatchMigrationPlanner that generates a BatchPlan from glob patterns, comma-separated lists, or index files. Validates each index against the schema patch and marks non-applicable ones as skipped.
  • Batch executor (redisvl/migration/batch_executor.py): BatchMigrationExecutor that runs individual migrations sequentially with per-index error isolation, checkpoint state persistence, and configurable failure policies (fail_fast or continue_on_error).
  • Models: BatchPlan, BatchState, BatchReport, and related Pydantic models for serialization.
  • Init updates (redisvl/migration/__init__.py): Exports for batch classes.
  • Unit tests (tests/unit/test_batch_migration.py): Tests covering batch planning, execution, resume, and failure handling.

Details

  • Failure policies: fail_fast aborts on the first failure; continue_on_error skips failed indexes and continues.
  • State tracking: Writes batch_state.yaml after each index completes, enabling resume from the exact point of interruption.
  • Resume with retry: batch-resume --retry-failed retries previously failed indexes.
  • Per-index reports: Each index migration produces its own report in the --report-dir directory.

PR Stack

PR Branch Description
#560 feat/migrate-core Core planner, validator, sync executor, and basic CLI
#561 feat/migrate-executor Quantization reliability layer
#562 feat/migrate-async Async planner, executor, and validator
#563 (this) feat/migrate-batch Batch migration with multi-index support
#564 feat/migrate-wizard Interactive migration wizard
#565 feat/migrate-cli-docs Full CLI commands and documentation
#566 feat/migrate-benchmarks Performance benchmarks

@jit-ci
Copy link
Copy Markdown

jit-ci bot commented Apr 1, 2026

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e05293097f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 1, 2026

@codex review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds batch migration capabilities to redisvl.migration, enabling users to plan and execute migrations across multiple RediSearch indexes using a shared schema patch, with ordered execution, checkpoint/resume, and batch-level reporting.

Changes:

  • Introduces BatchMigrationPlanner for multi-index selection (explicit list, file, glob pattern) and per-index applicability detection.
  • Introduces BatchMigrationExecutor for sequential batch execution with checkpoint state tracking, resume, failure policies, per-index reports, and batch summary reporting.
  • Adds unit + integration test coverage for planning, execution, checkpointing/resume, failure policies, and progress callbacks.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
redisvl/migration/batch_planner.py New batch planner to select indexes and generate a BatchPlan from a shared schema patch.
redisvl/migration/batch_executor.py New batch executor to apply batch plans with checkpointing, resume, and reporting.
redisvl/migration/__init__.py Exports batch planner/executor and batch models from the migration package.
tests/unit/test_batch_migration.py Unit tests for batch planning and execution behavior using mocks.
tests/integration/test_batch_migration_integration.py End-to-end batch migration integration tests against real Redis.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e05293097f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 1, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e05293097f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Fix status mismatch: executor writes 'success' to match BatchState.success_count
- Pass rename_operations to get_vector_datatype_changes
- Validate failure_policy early (reject unknown values)
- Make update_fields applicability rename-aware
- Fix progress position during resume (correct offset)
- Fix fail-fast: leave remaining in state for checkpoint resume
- Atomic checkpoint writes (write to .tmp then rename)
- Sanitize index_name in report filenames (path traversal)
- Add assert guard for fnmatch pattern type
nkanu17 added a commit that referenced this pull request Apr 2, 2026
Remove unused Path, MagicMock, and patch imports.
@nkanu17 nkanu17 force-pushed the feat/migrate-async branch from 0087dcf to 8642ec7 Compare April 2, 2026 00:30
@nkanu17 nkanu17 force-pushed the feat/migrate-batch branch from e052930 to 634cfa1 Compare April 2, 2026 00:30
@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 2, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 634cfa110f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@nkanu17 nkanu17 force-pushed the feat/migrate-async branch from 8642ec7 to 7a1ef9a Compare April 2, 2026 03:58
nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Fix status mismatch: executor writes 'success' to match BatchState.success_count
- Pass rename_operations to get_vector_datatype_changes
- Validate failure_policy early (reject unknown values)
- Make update_fields applicability rename-aware
- Fix progress position during resume (correct offset)
- Fix fail-fast: leave remaining in state for checkpoint resume
- Atomic checkpoint writes (write to .tmp then rename)
- Sanitize index_name in report filenames (path traversal)
- Add assert guard for fnmatch pattern type
nkanu17 added a commit that referenced this pull request Apr 2, 2026
Remove unused Path, MagicMock, and patch imports.
@nkanu17 nkanu17 force-pushed the feat/migrate-batch branch from 634cfa1 to cecfd9d Compare April 2, 2026 03:58
nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Add rename target collision validation in batch applicability check
- Propagate infrastructure errors (ConnectionError, TimeoutError) instead of silently marking as not applicable
@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 2, 2026

@codex review

1 similar comment
@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 2, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cecfd9d979

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@nkanu17 nkanu17 force-pushed the feat/migrate-async branch from b96771b to ef8fca8 Compare April 2, 2026 15:09
nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Fix status mismatch: executor writes 'success' to match BatchState.success_count
- Pass rename_operations to get_vector_datatype_changes
- Validate failure_policy early (reject unknown values)
- Make update_fields applicability rename-aware
- Fix progress position during resume (correct offset)
- Fix fail-fast: leave remaining in state for checkpoint resume
- Atomic checkpoint writes (write to .tmp then rename)
- Sanitize index_name in report filenames (path traversal)
- Add assert guard for fnmatch pattern type
nkanu17 added a commit that referenced this pull request Apr 2, 2026
Remove unused Path, MagicMock, and patch imports.
Copilot AI review requested due to automatic review settings April 2, 2026 15:09
nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Add rename target collision validation in batch applicability check
- Propagate infrastructure errors (ConnectionError, TimeoutError) instead of silently marking as not applicable
nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Account for renames before rejecting field additions in batch applicability check
- Fields being renamed away free their name for new additions
@nkanu17 nkanu17 force-pushed the feat/migrate-batch branch from 9d0329b to 0f42737 Compare April 2, 2026 15:09
nkanu17 added a commit that referenced this pull request Apr 2, 2026
- Update plan_path when loading existing state if caller provides a new path
- Handles cases where original path was empty or pointed to deleted temp dir
@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 2, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f42737c9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +273 to +277
except (
ConnectionError,
OSError,
TimeoutError,
redis.exceptions.ConnectionError,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Propagate Redis timeouts instead of marking indexes skipped

The infrastructure-error escape hatch in _check_index_applicability omits redis.exceptions.TimeoutError, so Redis timeouts fall into the broad except Exception path and are reported as applicable=False with a skip reason. In a transient outage, this silently converts operational failures into “not applicable” indexes, producing an incomplete batch plan instead of failing fast and retrying later.

Useful? React with 👍 / 👎.

Comment on lines +191 to +194
return self.apply(
batch_plan,
state_path=state_path,
report_dir=report_dir,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist overridden batch plan path during resume

resume() loads plan_path = batch_plan_path or state.plan_path, but the subsequent apply() call does not pass that resolved plan_path. When callers provide batch_plan_path to recover from a stale checkpoint path, the state file is never updated with the override; if execution stops again before finishing, the next resume without an explicit path can fail by reusing the old invalid plan_path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +41 to +43
# --- NEW: validate failure_policy early ---
"""Create a batch migration plan for multiple indexes.

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The function docstring is not the first statement in create_batch_plan() because it is preceded by a comment, so Python won’t treat it as a real docstring (and the string literal becomes a no-op expression). Move the docstring to be the first statement in the function body (place the comment below it) to restore introspection/help() behavior and avoid lint warnings.

Copilot uses AI. Check for mistakes.
# Pattern matching -- pattern is guaranteed non-None at this point
assert pattern is not None, "pattern must be set when reaching fnmatch"
all_indexes = list_indexes(redis_client=redis_client)
matched = [idx for idx in all_indexes if fnmatch.fnmatch(idx, pattern)]
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Pattern matching uses fnmatch.fnmatch(), which is OS-dependent (case-insensitive on Windows). Redis index names are typically case-sensitive, so this can produce inconsistent selection across platforms. Prefer fnmatch.fnmatchcase() (or explicitly normalize) to make matching deterministic.

Suggested change
matched = [idx for idx in all_indexes if fnmatch.fnmatch(idx, pattern)]
matched = [idx for idx in all_indexes if fnmatch.fnmatchcase(idx, pattern)]

Copilot uses AI. Check for mistakes.
Comment on lines +171 to +198
state = self._load_state(state_path)
plan_path = batch_plan_path or state.plan_path
if not plan_path or not plan_path.strip():
raise ValueError(
"No batch plan path available. Provide batch_plan_path explicitly, "
"or ensure the checkpoint state contains a valid plan_path."
)
batch_plan = self._load_batch_plan(plan_path)

# Optionally retry failed indexes
if retry_failed:
failed_names = [
idx.name for idx in state.completed if idx.status == "failed"
]
state.remaining = failed_names + state.remaining
state.completed = [idx for idx in state.completed if idx.status != "failed"]
# Write updated state back to file so apply() picks up the changes
self._write_state(state, state_path)

# Re-run apply with the updated state
return self.apply(
batch_plan,
state_path=state_path,
report_dir=report_dir,
redis_url=redis_url,
redis_client=redis_client,
progress_callback=progress_callback,
)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

resume() resolves plan_path but doesn’t pass it through to apply() as batch_plan_path. If the checkpoint state has an empty/stale plan_path, resume will work once but the state file will remain unchanged, causing subsequent resume calls without an explicit batch_plan_path to fail again. Pass batch_plan_path=plan_path into apply() (and/or persist it to state) so the checkpoint becomes self-contained.

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +141
# Check failure policy
if (
index_state.status == "failed"
and batch_plan.failure_policy == "fail_fast"
):
# Leave remaining indexes in state.remaining so that
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

BatchMigrationExecutor.apply() relies on string equality checks for failure_policy (only 'fail_fast' has special handling). If a BatchPlan is loaded/edited and contains a typo/unknown policy, behavior silently degrades to continue-on-error. Validate batch_plan.failure_policy against the supported set at the start of apply()/resume() and raise a ValueError on invalid values (mirroring BatchMigrationPlanner).

Copilot uses AI. Check for mistakes.
failed_names = [
idx.name for idx in state.completed if idx.status == "failed"
]
state.remaining = failed_names + state.remaining
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

When retry_failed=True, state.remaining is rebuilt via failed_names + state.remaining, which can introduce duplicates if an index is already present in remaining (or if the checkpoint contains duplicates). That would re-run migrations unexpectedly and can make progress reporting confusing. Consider de-duplicating while preserving order (e.g., stable unique) when rebuilding remaining.

Suggested change
state.remaining = failed_names + state.remaining
# Prepend failed names to remaining, de-duplicating while preserving order
combined = failed_names + state.remaining
state.remaining = list(dict.fromkeys(combined))

Copilot uses AI. Check for mistakes.
Comment on lines +335 to +341
if idx_state.status in ("succeeded", "success"):
succeeded += 1
elif idx_state.status == "failed":
failed += 1
else:
skipped += 1

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Status handling is inconsistent: BatchIndexState/BatchIndexReport document 'success' but _build_batch_report also treats 'succeeded' as success. This makes counting/normalization error-prone (and unknown statuses fall into the 'skipped' bucket). Consider normalizing status values when loading/writing state (or use a constrained enum/Literal) so reports and state files use a single canonical vocabulary.

Copilot uses AI. Check for mistakes.
"completed": [
{
"name": index_names[0],
"status": "succeeded",
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The checkpoint state written in this integration test uses status='succeeded', but BatchMigrationExecutor writes 'success' (and BatchIndexState docs list 'success'). Using a different status value makes the test state less representative and can mask issues in code that relies on canonical statuses (e.g., BatchState.success_count). Update the test checkpoint to use 'success' unless you are explicitly testing backward-compat normalization.

Suggested change
"status": "succeeded",
"status": "success",

Copilot uses AI. Check for mistakes.
nkanu17 added 8 commits April 2, 2026 11:28
- Pass existing snapshot to create_plan_from_patch to avoid double Redis round-trip
- Use _get_client() instead of _redis_client for lazy async client initialization
- Remap datatype_changes keys to post-rename field names before quantization
- Only resume from completed checkpoint when source index is actually gone
…ion, ordered execution, and state tracking

Batch planner and executor for migrating multiple indexes in a single
operation. Supports glob-based index selection, ordered execution with
per-index state tracking, checkpoint/resume semantics, and batch-level
reporting with fail-fast or continue-on-error policies.

Includes batch unit and integration tests.
- Fix status mismatch: executor writes 'success' to match BatchState.success_count
- Pass rename_operations to get_vector_datatype_changes
- Validate failure_policy early (reject unknown values)
- Make update_fields applicability rename-aware
- Fix progress position during resume (correct offset)
- Fix fail-fast: leave remaining in state for checkpoint resume
- Atomic checkpoint writes (write to .tmp then rename)
- Sanitize index_name in report filenames (path traversal)
- Add assert guard for fnmatch pattern type
Remove unused Path, MagicMock, and patch imports.
- Add rename target collision validation in batch applicability check
- Propagate infrastructure errors (ConnectionError, TimeoutError) instead of silently marking as not applicable
- Account for renames before rejecting field additions in batch applicability check
- Fields being renamed away free their name for new additions
- Update plan_path when loading existing state if caller provides a new path
- Handles cases where original path was empty or pointed to deleted temp dir
@nkanu17 nkanu17 force-pushed the feat/migrate-async branch from ef8fca8 to 28fb826 Compare April 2, 2026 15:30
@nkanu17 nkanu17 force-pushed the feat/migrate-batch branch from 0f42737 to 7a9f38f Compare April 2, 2026 15:30
@nkanu17 nkanu17 changed the title feat(migrator): [4/7] Batch migration with multi-index glob selection, ordered execution, and state tracking feat(migrate): [4/7] Batch migration with multi-index support Apr 2, 2026
@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 2, 2026

@codex review

@nkanu17
Copy link
Copy Markdown
Collaborator Author

nkanu17 commented Apr 2, 2026

Closing in favor of restructured PR stack (Option A ordering).

@nkanu17 nkanu17 closed this Apr 2, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7a9f38fde1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +273 to +278
except (
ConnectionError,
OSError,
TimeoutError,
redis.exceptions.ConnectionError,
) as e:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Propagate Redis timeout errors during applicability checks

_check_index_applicability intends to re-raise infrastructure failures, but this handler misses redis.exceptions.TimeoutError; in redis-py, that timeout type is distinct from Python's built-in TimeoutError, so it falls through to the generic except Exception and gets converted into applicable=False. A transient Redis timeout will therefore silently mark an index as skipped instead of failing planning, producing incomplete batch plans without surfacing the real connectivity failure.

Useful? React with 👍 / 👎.

Comment on lines +191 to +194
return self.apply(
batch_plan,
state_path=state_path,
report_dir=report_dir,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist provided plan path when resuming

When resume() is called with an explicit batch_plan_path, the method loads that plan but then calls apply() without forwarding the same path. As a result, _init_or_load_state() keeps the old/empty state.plan_path, so if execution is interrupted again, a later resume() without another explicit path can fail with "No batch plan path available," undermining crash-safe multi-step resume behavior.

Useful? React with 👍 / 👎.

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.

2 participants