Skip to content

Migration followup#1135

Open
EbiRider wants to merge 50 commits into
mainfrom
migration-followup
Open

Migration followup#1135
EbiRider wants to merge 50 commits into
mainfrom
migration-followup

Conversation

@EbiRider

Copy link
Copy Markdown
Collaborator

in this follow up we addressing multiple issues for migration

  1. the double m suffix issue from back to back migrations which made subsequent migrations fail
  2. tertiary node not being registered on the target's tertiary node
  3. cleanup of the code for unused variables and parameters
  4. extracting hublvol control logic out of the migration flow to ensure detach and attach of the hublvol respect it's needed cooldown of ten seconds

Comment thread simplyblock_core/services/hub_controller_manager.py Fixed
Comment thread simplyblock_core/services/hub_controller_manager.py Fixed
Comment thread simplyblock_core/services/hub_controller_manager.py Fixed
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Fixed
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Fixed
@EbiRider EbiRider force-pushed the migration-followup branch from d2676d7 to 77b6059 Compare June 30, 2026 20:43
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Fixed

@mxsrc mxsrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm a bit lost with the PR, the description is not really enough for me to understand what is going on. Ideally we'd split the PR up into indivudal changes, not sure whether that's feasible. Currently the description says the PR does four things, but there is definitely more going on. Questions I have from a quick glance that I can't really answer, that don't seem to correspond to any of the items the description indicates:

  • What are the test scripts that are being introduced for, who is supposed to execute them?
  • What's the purpose of the fault injection logic, where is it used from?
  • Why does the PR introduce replication logic, my understanding is that it's supposed to fix volume migration

Please help me review this, as it stands I don't understand what is going on.

# Undocumented chaos-testing switch: when set at cluster create, a transparent
# SPDK delay bdev is inserted between each device's nvme bdev and its alceml so
# a device can be made to "hang" on demand (see device_controller device-hang).
enable_hang_device: bool = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we need to very carefully think about the implications of integrating fault injection into the business logic. Where is this called from? I cannot find it in this PR. Is there no other way of achieving this?

Comment on lines +219 to +220
_lvol_base = lvol.lvol_bdev[:-len(_MIGRATION_BDEV_SUFFIX)] if lvol.lvol_bdev.endswith(_MIGRATION_BDEV_SUFFIX) else lvol.lvol_bdev
bdev_short = _lvol_base + _MIGRATION_BDEV_SUFFIX

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not the only place where this is failing. A proper fix uses the idempotent helper that is already present consistently: 0a9690a.

Comment on lines +682 to +683
_lvol_base = lvol.lvol_bdev[:-len(_MIGRATION_BDEV_SUFFIX)] if lvol.lvol_bdev.endswith(_MIGRATION_BDEV_SUFFIX) else lvol.lvol_bdev
bdev_short = _lvol_base + _MIGRATION_BDEV_SUFFIX

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is too easy to miss, same comment as above. If you repeat code, use a helper.

@EbiRider EbiRider force-pushed the migration-followup branch from 526b846 to e9941c7 Compare July 7, 2026 19:48

@mxsrc mxsrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's a few minor things that should change, I left comments for that. The two larger things are that quite a few places ignore errors instead of escalating them, sometimes logging a warning, sometimes not. In some places this doesn't seem right, I think this should be revisited. As for the core logic, I can't really judge that, this, and the pre-existing code doesn't really document the intent, but from a code-perspective I think we can go forward.

Comment thread simplyblock_core/rpc_client.py Outdated

def bdev_lvol_rename(self, old_name, new_name):
params = {"old_name": old_name, "new_name": new_name}
return self._request("bdev_lvol_rename", params)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

New RPCs should use _request3.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done, using _request3 for the rpc now

Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py
Comment on lines 540 to 565
current = snap.snap_ref_id
current = snap.snap_ref_id or snap.prev_snap_uuid
except KeyError:
break

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This, and _get_snap_ancestry seem redundant. Is there no way of deduplicating them?
At least _protect_snap_and_ancestors can basically be candidate_set - _collect_snap_ancestry(), can't it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py
Comment on lines +437 to +438
status = primary_rpc.bdev_lvol_get_lvol_delete_status(bdev_name)
if status in (0, 2):
break
if status == 1:
time.sleep(0.25)
else:
logger.warning(f"delete bdev {bdev_name}: unexpected status {status}")
if status != 1:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be folded into the loop condition.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

@EbiRider EbiRider requested a review from mxsrc July 9, 2026 02:10

@mxsrc mxsrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gotta look into something else and didn't finish the review, leaving my comments here for now. Something I noticed, not necessarily for now, but a common pattern is to do an operation on a node, as well as their secondary and primary. One single helper would help doing this correctly, ensuring correct error handling.

Comment on lines +907 to +909
_short_tgt = _snap_tgt_short_name(_s) # SNAP_Xm (in-flight)
_short_canonical = _snap_short_name(_s) # SNAP_X (post-rename)
_short_am = _short_canonical + _MIGRATION_BDEV_SUFFIX_DONE # SNAP_Xam (fallback)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This will hit the same fault scenario we had with the BDEV_SUFFIX, won't it? If the m suffix is already applied, simply adding this will make it SNAP_Xmam?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

after migration we have a rename operation, which should bring the lvols back to the same name they had on source on the target unless there's a different lvol with exact same name so I'm am suffix for those cases
that only happnes if there's an unrelated lvol with the same lvol on the target

f"_rename_migrated_bdevs: {role} rename {label} (non-fatal): {exc}")
if exists:
return _EXISTS
return True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm struggling to follow this function. It returns either a boolean (or rather, always True), or some string sentinel. It also adds relevant logic to RPCClient.bdev_lvol_rename. The comments are a bit unclear to me and violate abstraction layers, the RPCClient should contain any knowledge about RPC workings, like the mapping of error codes.

So the suggestion is to add an exists_ok flag to RPCClient.bdev_lvol_rename, and raise an RPCException subclass that indicates the entity already exists. With that primitive, we can lose the sentinel, and _rename_with_fallback becomes much more readable.

In addition to handling the RPC logic, it does the rename on all three nodes that are responsible for the LVol. It doesn't handle inconsistencies or failures correctly though open questions are:

  • what happens if the function encounters an existing inconsistency where the target exists on only one of the nodes. There is no way to handle this easily, but at least we need to be aware of it
  • Errors of any of the rename (except the first) are ignored and flagged as non-fatal. Is that correct? It introduces an inconsistency that follow up code has to deal with.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this i been working on mainly on migration-followup2 branch which it changed I'll save this comment for that branch

"""
if _SKIP_RENAME_BDEVS:
logger.info("_rename_migrated_bdevs: skipped (SKIP_RENAME_BDEVS=True)")
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems like a debugging provision? In that case I prefer not to mainline it.

Comment on lines +554 to +556
def _protect_snap_and_ancestors(snap_uuid, candidate_set):
"""Remove *snap_uuid* and all its ancestors from *candidate_set*."""
candidate_set -= _collect_snap_ancestry(snap_uuid)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

At this point we don't need the helper anymore, the operation can simply be used at the call-site.

@mxsrc mxsrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the changes, quite a few good additions in here. I think we need one more iteration though.

The situation with the _get_target_{secondary,tertiary}_node should be handled by:

  • adding documentation for why in some cases a suspended node should be returned
  • passing the source node ID parameter consistently
  • removing the default to enforce this for future changes
  • reviewing the _get_{secondary,tertiary}_rpc helpers: This needs to mirror the logic for the node get helpers, keeping this in sync is evidently hard. Maybe they should be removed, and instead use _make_rpc(_get_{secondary_target_tertiary_node(...)). If they are used for source nodes as well, probably we best introduce separate helpers for that indicating them.

Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
Comment on lines +219 to +221
if (node_offline_devices > 0
or (node_online_devices == 0 and node.status != StorageNode.STATUS_REMOVED)
or node.status == StorageNode.STATUS_OFFLINE):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't seem to be related to the other changes, why is it here?

Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
Comment thread simplyblock_core/services/tasks_runner_lvol_migration.py Outdated
EbiRider added 3 commits July 10, 2026 15:18
# Conflicts:
#	simplyblock_core/controllers/migration_controller.py
#	simplyblock_core/services/tasks_runner_lvol_migration.py

@mxsrc mxsrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good, let's go forward. Some of the suggestions are still valid, but they shouldn't block merging this.

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