Skip to content

Reduce boilerplate around MultiStorageClient feature checks - #5269

Merged
Phlip79 merged 7 commits into
NVIDIA:mainfrom
Randl:msc_boilerplate
Jul 20, 2026
Merged

Reduce boilerplate around MultiStorageClient feature checks#5269
Phlip79 merged 7 commits into
NVIDIA:mainfrom
Randl:msc_boilerplate

Conversation

@Randl

@Randl Randl commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
  • I, the PR author, have personally reviewed every line of this PR.

What does this PR do ?

Use a proxy object to handle MultiStorageClient.

Issue tracking

Linked issue: Fixes #4507

Contribution process

Pre-checks

  • I have added relevant unit tests
  • I have added relevant functional tests
  • I have added proper typing to my code Typing guidelines
  • I have added relevant documentation
  • I have run the autoformatter.sh on my PR

@Randl
Randl requested review from a team as code owners June 10, 2026 16:11
@svcnvidia-nemo-ci
svcnvidia-nemo-ci marked this pull request as draft June 10, 2026 16:11
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been automatically converted to draft because all PRs must start as drafts.

When you are ready for review, click Ready for Review to begin the review process. This will:

  1. Add the oncall reviewer (optional reviewer)
  2. Add required review teams based on your changes

See the contribution guide for more details.

@Randl
Randl marked this pull request as ready for review June 10, 2026 16:28

@maanug-nv maanug-nv 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.

Hi @Randl , thanks for tackling this. MSC accesses seem a lot cleaner now!
The implementation of maybe_msc seems problematic though, iiuc it will always be disabled. Since MaybeMultiStorageClient's init is running once the first time this file is imported, it will get initialized to maybe_msc before a user even has the chance to run MultiStorageClientFeature.enable().

Some ideas off the top of my head that might work instead:

  • move MaybeMultiStorageClient and maybe_msc to a different file. If this gets added to any 'init.py' files on accident though, might run into the same issue.
  • construct a MaybeMultiStorageClient each time. ie in the other files, you'd do MaybeMultiStorageClient().os or similar.
  • replace MaybeMultiStorageClient with a function.
  • use lazy initialization of self.open, self.os, etc. this would allow you to still construct it once, and the 'is_enabled()' checks will happen at runtime lazily.
  • add self.open , self.os to MultiStorageClientFeature , and set appropriately in enable() and disable().

Shouldn't take too long to implement any of those I think. Once it's fixed, we can push this through review to get merged.

@Randl
Randl force-pushed the msc_boilerplate branch from 0517b08 to db73042 Compare June 10, 2026 17:11
@copy-pr-bot

copy-pr-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@Randl

Randl commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Yep, you're right @maanug-nv
I've replaced it with dynamic __getattr__

@Randl
Randl force-pushed the msc_boilerplate branch from db73042 to 07c463d Compare June 10, 2026 17:14
@maanug-nv

Copy link
Copy Markdown
Contributor

/ok to test 07c463d

@maanug-nv

maanug-nv commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Code review

Found 4 issues:

  1. load_common discards its return value — always returns None

The refactored line dropped the return that both original branches had (return msc.torch.load(...) / return torch.load(...)). load_common now silently returns None on the happy path, so every caller that consumes the loaded common StateDict gets None and fails downstream. The docstring still says Returns: StateDict.

load_path = os.path.join(checkpoint_dir, COMMON_STATE_FNAME)
try:
maybe_msc.torch.load(load_path, map_location='cpu')
except FileNotFoundError as e:
err_msg = f'Common file {load_path} does not exist'
ckpt_files = [f.name for f in maybe_msc.Path(checkpoint_dir).iterdir()]
logger.debug(f'{err_msg}. Checkpoint directory content: {ckpt_files}')
raise CheckpointingException(err_msg) from e

  1. verify_checkpoint passes strict=False to stdlib os.path.isdir when MSC is disabled

strict=False is an MSC-specific extension; in the original code it was only used in the MultiStorageClientFeature.is_enabled() branch, while the non-MSC branch called plain os.path.isdir(checkpoint_dir). The unified call always passes strict=False, so when MSC is disabled (the default) maybe_msc.os resolves to stdlib os and os.path.isdir(path, strict=False) raises TypeError: isdir() takes ... no keyword arguments, crashing verify_checkpoint.

Args:
checkpoint_dir (str): checkpoint directory
"""
isdir = maybe_msc.os.path.isdir(str(checkpoint_dir), strict=False)
if not isdir:
raise CheckpointingException(f'Checkpoint directory {checkpoint_dir} does not exist')

  1. to_yaml drops the safe_yaml_representers() context manager

The original to_yaml wrapped yaml.safe_dump in with safe_yaml_representers(): (still present in print_yaml just below). Without it, configs containing enums, functools.partial, torch dtypes, etc. raise yaml.representer.RepresenterError at serialization time.

yaml_path: Path where to save the YAML file.
"""
config_dict = self.to_dict()
with maybe_msc.open(yaml_path, "w") as f:
yaml.safe_dump(config_dict, f, default_flow_style=False)
def print_yaml(self) -> None:
"""
Print the config container to the console in YAML format.
"""
config_dict = self.to_dict()
with safe_yaml_representers():
print(yaml.safe_dump(config_dict, default_flow_style=False))

  1. save_integrity_manifest hashes subdirectories on local filesystems

The original local-path branch guarded each entry with entry.is_file() before calling _compute_file_hash; the MSC branch omitted it. The unified loop adopts the MSC behavior for both, so on a local checkpoint directory containing subdirectories, _compute_file_hash opens a directory and raises IsADirectoryError.

manifest: Dict[str, str] = {}
ckpt_path = maybe_msc.Path(checkpoint_dir)
for entry in sorted(ckpt_path.iterdir(), key=lambda p: str(p)):
if entry.name != INTEGRITY_FNAME:
manifest[entry.name] = _compute_file_hash(str(entry))

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@Randl
Randl force-pushed the msc_boilerplate branch 2 times, most recently from 0b81410 to 1a55265 Compare June 10, 2026 18:28
@Randl

Randl commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Fair enough. isdir is a bit annoying, everything else is quite trivial

@maanug-nv

Copy link
Copy Markdown
Contributor

/ok to test 1a55265

@svcnvidia-nemo-ci svcnvidia-nemo-ci removed the waiting-on-maintainers Waiting on maintainers to respond label Jun 23, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Jun 25, 2026
@maanug-nv

Copy link
Copy Markdown
Contributor

/ok to test da12d84

@maanug-nv

Copy link
Copy Markdown
Contributor

Hi @Randl , sorry this is taking so long.
There were some merge conflicts, I manually tried to resolve from Github UI, but looks like I re-introduced import of open_file. There might be some other failures as well.
Can you fix those up?

@Randl

Randl commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@maanug-nv I've pushed the update, do you need to rerun the CI manually?

@maanug-nv

Copy link
Copy Markdown
Contributor

/ok to test 4dc5816

@maanug-nv

Copy link
Copy Markdown
Contributor

/ok to test 1ecd38e

@Phlip79

Phlip79 commented Jul 16, 2026

Copy link
Copy Markdown
Member

/ok to test bbf91bb

@dimapihtar

Copy link
Copy Markdown
Contributor

/ok to test 6578f93

Randl and others added 7 commits July 20, 2026 09:40
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
Signed-off-by: Evgenii Zheltonozhskii <zheltonozhskiy@gmail.com>
@Randl

Randl commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Looks like attempted merge broke something, I fixed and pushed again

@Phlip79

Phlip79 commented Jul 20, 2026

Copy link
Copy Markdown
Member

/ok to test 6cc1dde

@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/29775113671

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

Labels

Approved All necessary approvals have been made community-request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce boilerplate around MultiStorageClient feature checks