Skip to content

[Issue-1464] replace callback with token management API - #1515

Closed
realAaronWu wants to merge 20 commits into
ai-dynamo:mainfrom
realAaronWu:feature-issue-1464-replace-callback-only
Closed

realAaronWu wants to merge 20 commits into
ai-dynamo:mainfrom
realAaronWu:feature-issue-1464-replace-callback-only

Conversation

@realAaronWu

@realAaronWu realAaronWu commented Apr 12, 2026 •

Copy link
Copy Markdown

What?

Replace the cuObject callback-based I/O interface (iDellS3RdmaClient, Pattern A) with manual RDMA token management (CuObjTokenManager, Pattern B) in the Dell ObjectScale engine.

Why?

The current implementation abuses cuObjPut()/cuObjGet() callbacks: callbacks are expected to perform the entire server round-trip synchronously, but ours copy rdma_desc into a context and return 0, with the actual S3 request happening later in postXfer. cuMemObjGetRDMAToken() is designed for exactly this use case -- generate a token, inject it into
your own HTTP request, free it when done. No callbacks, no synchronous I/O pretense.

How?

• New CuObjTokenManager: RAII wrapper around cuObjClient. 1:1 memory registration mapping (each registerMemory = one cuMemObjGetDescriptor). Token generation via cuMemObjGetRDMAToken with buffer_offset=0.
• Rewritten awsS3DellObsClient: Overrides standard putObjectAsync/getObjectAsync to inject RDMA tokens as x-rdma-info headers. Implements standard iS3Client -- no separate RDMA interface, no dynamic_cast.
• Simplified S3DellObsObjEngineImpl: Overrides only registerMem, deregisterMem, getSupportedMems, getClient, and postXfer (offset validation). Inherits prepXfer, checkXfer, releaseReqH from parent.
• Deleted rdma_interface.h (iDellS3RdmaClient no longer needed).
• Extended isValidPrepXferParams to accept VRAM_SEG in addition to DRAM_SEG.
• Consolidated test infrastructure: single mockS3Client for all engines, parameterized Accel/Dell test suites, comprehensive CuObjTokenManager integration tests.

Summary by CodeRabbit

  • New Features

    • Extended memory support to include VRAM in addition to DRAM for Dell ObjectScale S3 operations.
  • Bug Fixes

    • Added validation to ensure local and remote descriptor lists have matching counts during transfer operations.
  • Refactor

    • Simplified internal RDMA token generation process for Dell ObjectScale S3 integration.

Aaron Wu added 4 commits April 10, 2026 18:55
…attern.

What changed:
- New CuObjTokenManager: RAII wrapper. Pool-aware memory
  registration with refcounting de-duplicates per-page registerMem calls
  into a single cuMemObjGetDescriptor per contiguous pool. Supports
  rdma_pool_size backend parameter hint for O(1) registration.
- Rewritten awsS3DellObsClient: overrides standard iS3Client methods
  (putObjectAsync/getObjectAsync) to inject RDMA tokens as x-rdma-info
  headers. No separate iDellS3RdmaClient interface, no dynamic_cast.
  Per-request fallback to parent HTTP body transfer for DRAM; hard fail
  for VRAM (not CPU-addressable).
- Simplified S3DellObsObjEngineImpl: overrides only registerMem,
  deregisterMem, getSupportedMems, getClient. Inherits prepXfer,
  postXfer, checkXfer, releaseReqH unchanged from the parent.
- Deleted rdma_interface.h (iDellS3RdmaClient no longer needed).
Shared code (minimal, outside s3_accel/dell/):
- s3/engine_impl.cpp: widen isValidPrepXferParams to accept VRAM_SEG
  in addition to DRAM_SEG for local memory type (4-line change).
- meson.build: replace rdma_interface.h with cuobj_token_manager.{h,cpp}
  (2-line change).

Signed-off-by: Aaron Wu <aaron.wu@dell.com>
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
…attern.

What changed:
- New CuObjTokenManager: RAII wrapper. Pool-aware memory
  registration with refcounting de-duplicates per-page registerMem calls
  into a single cuMemObjGetDescriptor per contiguous pool. Supports
  rdma_pool_size backend parameter hint for O(1) registration.
- Rewritten awsS3DellObsClient: overrides standard iS3Client methods
  (putObjectAsync/getObjectAsync) to inject RDMA tokens as x-rdma-info
  headers. No separate iDellS3RdmaClient interface, no dynamic_cast.
  Per-request fallback to parent HTTP body transfer for DRAM; hard fail
  for VRAM (not CPU-addressable).
- Simplified S3DellObsObjEngineImpl: overrides only registerMem,
  deregisterMem, getSupportedMems, getClient. Inherits prepXfer,
  postXfer, checkXfer, releaseReqH unchanged from the parent.
- Deleted rdma_interface.h (iDellS3RdmaClient no longer needed).
Shared code (minimal, outside s3_accel/dell/):
- s3/engine_impl.cpp: widen isValidPrepXferParams to accept VRAM_SEG
  in addition to DRAM_SEG for local memory type (4-line change).
- meson.build: replace rdma_interface.h with cuobj_token_manager.{h,cpp}
  (2-line change).

Signed-off-by: Aaron Wu <aaron.wu@dell.com>
@copy-pr-bot

copy-pr-bot Bot commented Apr 12, 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.

@github-actions

Copy link
Copy Markdown

👋 Hi realAaronWu! Thank you for contributing to ai-dynamo/nixl.

Your PR reviewers will review your contribution then trigger the CI to test your changes.

🚀

Aaron Wu and others added 13 commits April 12, 2026 14:54
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
Remove the pool_hint optimization and associated complexity (region
tracking, refcounting, findContainingRegion) from CuObjTokenManager.
The token manager now does a simple 1:1 mapping: each registerMemory()
call triggers one cuMemObjGetDescriptor, each deregisterMemory() maps
to one cuMemObjPutDescriptor, and generateToken() calls
cuMemObjGetRDMAToken with buffer_offset=0.

Signed-off-by: Aaron Wu <aaron.wu@dell.com>
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
… fixes

Refactor OBJ unit tests to support both standard S3 and Dell/Accel
engine configurations, integrating tests from PR ai-dynamo#1630 while fixing
compilation and correctness issues for the Pattern B refactored codebase.

Key changes:

- Extract shared test infrastructure into obj_test_base.h:
  - ObjTestConfig with Standard, CRT, Accel, and Dell configurations
  - Enhanced mockS3Client with per-key outcomes, delays, and error injection
  - mockDellS3Client using iS3Client (not deleted iDellS3RdmaClient)
  - objTestBase, objParamTestFixture, and objTestFixture base classes

- Add obj_cuobj.cpp for Dell/Accel-specific tests (conditionally compiled):
  - 18 objDellTestFixture tests covering RDMA read/write, memory
    registration, transfer failures, descriptor validation, and
    object segment management
  - 14 CuObjTokenManagerTest tests for token generation edge cases
  - Parameterized ObjAccelClientTests suite (Accel + Dell configs)

- Simplify obj.cpp by removing duplicated infrastructure and all
  HAVE_CUOBJ_CLIENT guarded code, now handled in obj_cuobj.cpp

- Update meson.build to conditionally compile obj_cuobj.cpp when
  cuobj_dep is found

Fixes from PR ai-dynamo#1630 original tests:
- Remove rdma_interface.h include (deleted in Pattern B)
- Fix mock client inheritance (iS3Client, not iDellS3RdmaClient)
- Fix registerMem expectations: mock path (tokenMgr_=null) returns
  NIXL_SUCCESS with nullptr metadata, not backend errors
- Fix prepXfer vs postXfer validation: devId-to-key mapping is
  checked in postXfer, not prepXfer
- Remove 3 tests with untestable expectations (descriptor count
  mismatch, unregistered cuObject address, large memory offset)
…place-callback-only

# Conflicts:
#	src/plugins/obj/s3_accel/dell/engine_impl.cpp
#	test/gtest/unit/obj/obj.cpp
#	test/unit/plugins/object/nixl_object_test.cpp
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
…place-callback-only

Resolve conflicts in test/gtest/unit/obj/obj_cuobj.cpp and
test/gtest/unit/obj/obj_test_base.h caused by PR ai-dynamo#1630 landing on main.

Resolution strategy:
- Keep HEAD's CuObjTokenManager architecture (no iDellS3RdmaClient,
  no mockDellS3Client, no rdma_interface.h)
- Keep HEAD's mock behavior: single mockS3Client for all engine types,
  DRAM/VRAM registerMem returns nullptr metadata when tokenMgr_ is null
- Keep HEAD's validation semantics: devId registration checked in
  postXfer, not prepXfer
- Keep HEAD's exceptionThrowingMockS3Client and CuObjTokenManager
  integration tests
- Adopt origin/main's value-initialization (= {}) for nixlBlobDesc
- Adopt origin/main's polling loop and buffer verification in
  testTransferFailure
- Adopt origin/main's DellEngineMismatchedDescriptorCounts test
  (parent-class prepXfer validation, independent of mock design)
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
@realAaronWu
realAaronWu marked this pull request as ready for review May 27, 2026 07:20
@realAaronWu
realAaronWu requested review from a team, brminich, ofer, vvenkates27 and w1ldptr as code owners May 27, 2026 07:20
@coderabbitai

coderabbitai Bot commented May 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors the Dell ObjectScale S3 engine from callback-based cuObject I/O to manual RDMA token management. A new CuObjTokenManager class manages memory registration and generates RDMA tokens. The Dell S3 client is simplified to inject tokens directly into standard async S3 request paths. The engine removes complex lifecycle overrides and delegates memory management to the token manager. Base S3 engine validation now supports VRAM memory. Tests are consolidated and expanded.

Changes

Dell ObjectScale RDMA token management refactor

Layer / File(s) Summary
CuObjTokenManager infrastructure
src/plugins/obj/s3_accel/dell/cuobj_token_manager.h, src/plugins/obj/s3_accel/dell/cuobj_token_manager.cpp
New RAII non-copyable wrapper around cuObjClient with isConnected(), registerMemory()/deregisterMemory() delegation to cuObject, and generatePutToken()/generateGetToken() that throw std::runtime_error on token generation failure.
Base S3 engine VRAM_SEG support
src/plugins/obj/s3/engine_impl.cpp
isValidPrepXferParams now accepts VRAM_SEG as valid local memory type and validates that local/remote descriptor counts match.
Dell S3 client refactor
src/plugins/obj/s3_accel/dell/client.h, src/plugins/obj/s3_accel/dell/client.cpp
awsS3DellObsClient removes iDellS3RdmaClient interface and RDMA-specific methods; constructor now takes shared CuObjTokenManager; putObjectAsync generates PUT token with ContentLength(0), and getObjectAsync generates GET token with byte-range header; both methods include exception handling for token failures.
Dell engine simplification
src/plugins/obj/s3_accel/dell/engine_impl.h, src/plugins/obj/s3_accel/dell/engine_impl.cpp
Removes cuObjClient member and lifecycle overrides (prepXfer, checkXfer, releaseReqH); adds tokenMgr_ initialization; delegates DRAM/VRAM registration to token manager while delegating OBJ_SEG to parent; postXfer validates NIXL_WRITE requests reject non-zero remote offsets.
Build and test infrastructure
src/plugins/obj/meson.build, test/gtest/unit/obj/meson.build, test/gtest/unit/obj/obj_test_base.h
Meson adds cuobj_token_manager.cpp/.h sources and conditionalizes cuobj_dep in test dependencies. Test base consolidates shared utilities, removes Dell RDMA header, streamlines mock S3 client success/error logic, adds doubleCallbackMockS3Client and exceptionThrowingMockS3Client, and changes setupEngine to always use standard mock.
OBJ unit test refactor
test/gtest/unit/obj/obj.cpp
Moves shared test infrastructure to obj_test_base.h, narrows INSTANTIATE_TEST_SUITE_P to Standard and CRT configs, removes in-file exceptionThrowingMockS3Client, and loosens DRAM/VRAM metadata expectations for mock-client paths.
Dell/cuobj test expansion
test/gtest/unit/obj/obj_cuobj.cpp
Adds explicit ObjAccelClientTests instantiation for Accel and Dell; expands end-to-end RDMA transfer tests; adds mismatched descriptor count validation; adds DellUnregisteredRemoteDevId test verifying prepXfer succeeds but postXfer fails; adds DellRegisterMemDramVramMockPath for mock-path behavior; removes older backend-failure tests; adds comprehensive CuObjTokenManagerTest integration suite with hardware-dependent token generation.
Integration test cleanup
test/unit/plugins/object/nixl_object_test.cpp
printProgress rewritten to block-scoped if/else for clarity; VRAM/DRAM deallocation refactored with explicit null-checks before calling cudaFree/free.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ai-dynamo/nixl#1630: Directly related Dell/cuobj refactor affecting the same OBJ unit-test build wiring and test sources to exercise updated cuobj/Dell engine behavior.

Suggested reviewers

  • vvenkates27
  • w1ldptr
  • brminich

Poem

🐰 The Dell engine learns to stand alone,
No callbacks needed—tokens shown!
Register, deregister, tokens flow,
RDMA magic, sleeker than before. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: replacing a callback-based interface with token management API in the Dell ObjectScale engine.
Description check ✅ Passed The description follows the template with all required sections (What, Why, How) well-populated. It provides clear rationale, design justification, and implementation details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Infer (1.2.0)
test/gtest/unit/obj/obj_cuobj.cpp

In file included from test/gtest/unit/obj/obj_cuobj.cpp:33:
test/gtest/unit/obj/obj_test_base.h:20:10: fatal error: 'gtest/gtest.h' file not found
20 | #include <gtest/gtest.h>
| ^~~~~~~~~~~~~~~
1 error generated.
Error: the following clang command did not run successfully:
/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/bin/clang-18
@/tmp/coderabbit-infer/dd317fc8c7aed44837351be3b15e16729f74abd3-a4353e5e49d700d7/tmp/clang_command_.tmp.e74baf.txt
++Contents of '/tmp/coderabbit-infer/dd317fc8c7aed44837351be3b15e16729f74abd3-a4353e5e49d700d7/tmp/clang_command_.tmp.e74baf.txt':
"-cc1" "-load"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
"-add-plugin" "BiniouASTExporter" "-plugin-arg-BiniouASTExporter" "-"
"-plugin-arg-BiniouASTExporter" "PREPEND_CURRENT_DIR=1"
"-plugin-arg-BiniouASTExporter" "MAX_STRING_SIZE=65535" "-cc1" "-triple"

... [truncated 1143 characters] ...

l/lib/clang/18/include"
"-internal-isystem" "/usr/local/include" "-internal-isystem"
"/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
"-internal-externc-isystem" "/usr/include/x86_64-linux-gnu"
"-internal-externc-isystem" "/include" "-internal-externc-isystem"
"/usr/include" "-Wno-ignored-optimization-argument" "-Wno-everything"
"-fdeprecated-macro" "-ferror-limit" "19" "-fgnuc-version=4.2.1"
"-fskip-odr-check-in-gmf" "-fcxx-exceptions" "-fexceptions"
"-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o"
"/tmp/coderabbit-infer/a4353e5e49d700d7/file.o" "-x" "c++"
"test/gtest/unit/obj/obj_cuobj.cpp" "-O0" "-fno-builtin" "-include"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../lib/clang_wrappers/global_defines.h"
"-Wno-everything"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/plugins/obj/s3_accel/dell/cuobj_token_manager.h`:
- Around line 6-7: Replace the current header guard macro
NIXL_OBJ_PLUGIN_S3_DELL_CUOBJ_TOKEN_MANAGER_H in cuobj_token_manager.h with the
full repo-relative form NIXL_SRC_PLUGINS_OBJ_S3_ACCEL_DELL_CUOBJ_TOKEN_MANAGER_H
(uppercase, slashes->underscores, prefixed by NIXL_ and suffixed _H) so the
`#ifndef/`#define/#endif use the repository-wide naming convention and pass style
checks.

In `@src/plugins/obj/s3_accel/dell/DESIGN.md`:
- Around line 31-42: Update the two fenced code blocks under "Before (Pattern
A)" and "After (Pattern B)" to include a language identifier (use "text") and
ensure each fenced block is surrounded by blank lines; specifically modify the
blocks labeled "Before (Pattern A)" and "After (Pattern B)" so they become
```text ... ``` with an empty line above and below each fenced block and
preserve the existing content/indentation.

In `@src/plugins/obj/s3_accel/dell/engine_impl.cpp`:
- Around line 64-68: The constructor currently returns early when
tokenMgr_->isConnected() is false, leaving a half-initialized engine; change
these early returns (the block creating tokenMgr_ using
CuObjTokenManager(CUOBJ_PROTO_RDMA_DC_V1) and the similar block at the later
spot) to throw an exception (e.g., std::runtime_error) with a clear message that
includes the token manager connection failure so the engine creation fails fast
and callers never observe a constructed object with tokenMgr_ or getClient() ==
nullptr.

In `@src/plugins/obj/s3_accel/dell/engine_impl.h`:
- Around line 23-26: Add a Dell-specific preflight in the upload path to reject
non-zero PUT offsets before any async PUT is launched: in the Dell engine
implementation (code around DefaultObjEngineImpl::postXfer / prepXfer usage),
inspect the request's remote_desc.addr.offset (or equivalent write offset field)
whenever the code is about to call getClient()->putObjectAsync() (or
awsS3DellObsClient::putObjectAsync()), and return a synchronous error/abort the
request if offset != 0 instead of forwarding the descriptor; apply the same
check to the other symmetric upload paths that would call putObjectAsync so
invalid non-zero offsets are never enqueued into the async path.

In `@test/gtest/unit/obj/obj_test_base.h`:
- Around line 413-415: In testTransferWithSize when operation == NIXL_READ the
test only asserts test_buffer[0], which is too weak; update the read-data
validation to verify multiple positions: if buffer_size is small (e.g., <= 64)
assert the entire buffer equals the expected pattern (compare every byte in
test_buffer to expected value), otherwise sample a few positions (e.g., index 0,
buffer_size/2, buffer_size-1) and use EXPECT_EQ on those positions; locate the
check using symbols operation, NIXL_READ, test_buffer, buffer_size and replace
the single EXPECT_EQ(test_buffer[0], 'A') with the described full-or-sampled
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: da17eb9c-8df6-4f4e-a099-fb8f671e134b

📥 Commits

Reviewing files that changed from the base of the PR and between 3009db5 and 65c64ab.

📒 Files selected for processing (15)
  • src/plugins/obj/meson.build
  • src/plugins/obj/s3/engine_impl.cpp
  • src/plugins/obj/s3_accel/dell/DESIGN.md
  • src/plugins/obj/s3_accel/dell/client.cpp
  • src/plugins/obj/s3_accel/dell/client.h
  • src/plugins/obj/s3_accel/dell/cuobj_token_manager.cpp
  • src/plugins/obj/s3_accel/dell/cuobj_token_manager.h
  • src/plugins/obj/s3_accel/dell/engine_impl.cpp
  • src/plugins/obj/s3_accel/dell/engine_impl.h
  • src/plugins/obj/s3_accel/dell/rdma_interface.h
  • test/gtest/unit/obj/meson.build
  • test/gtest/unit/obj/obj.cpp
  • test/gtest/unit/obj/obj_cuobj.cpp
  • test/gtest/unit/obj/obj_test_base.h
  • test/unit/plugins/object/nixl_object_test.cpp
💤 Files with no reviewable changes (1)
  • src/plugins/obj/s3_accel/dell/rdma_interface.h

Comment thread src/plugins/obj/s3_accel/dell/cuobj_token_manager.h Outdated
Comment thread src/plugins/obj/s3_accel/dell/DESIGN.md Outdated
Comment thread src/plugins/obj/s3_accel/dell/engine_impl.cpp
Comment thread src/plugins/obj/s3_accel/dell/engine_impl.h Outdated
Comment thread test/gtest/unit/obj/obj_test_base.h Outdated
1. Header guard: use full repo-relative naming convention
   (NIXL_SRC_PLUGINS_OBJ_S3_ACCEL_DELL_CUOBJ_TOKEN_MANAGER_H)

2. DESIGN.md: fix markdown lint (MD031/MD040) — add language
   identifier and blank lines around fenced code blocks

3. engine_impl.cpp: fail fast on CuObjTokenManager connection failure
   by throwing std::runtime_error instead of silently returning from
   the constructor with a half-initialized engine

4. engine_impl: add postXfer override to reject non-zero PUT offsets
   before dispatching async operations. Dell ObjectScale RDMA PUT does
   not support partial writes; failing early prevents partially-enqueued
   multi-descriptor requests.

5. obj_test_base.h: strengthen testTransferWithSize read validation
   from single-byte check to multi-position (front, middle, back)
   to catch partial or offset-corrupted reads

Signed-off-by: Aaron Wu <aaron.wu@dell.com>

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/plugins/obj/s3_accel/dell/engine_impl.h`:
- Around line 87-103: Update the public API docs for the newly overridden method
postXfer: replace the brief class overview above the class to remove the
statement that transfer lifecycle is inherited unchanged and note that Dell
overrides postXfer with RDMA PUT semantics, and add a Doxygen block for postXfer
including `@brief` describing behavior (reject non-zero remote offsets for WRITE,
delegate for READ), `@param` entries for operation, local, remote, remote_agent,
handle, opt_args, and `@return` describing nixl_status_t outcomes; ensure the
comment precedes the postXfer declaration and follows the project's /** ... */
Doxygen style and coding guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a703eb4-6cb2-4de4-b655-2229dbf1b75f

📥 Commits

Reviewing files that changed from the base of the PR and between 65c64ab and 9ac7ad4.

📒 Files selected for processing (5)
  • src/plugins/obj/s3_accel/dell/DESIGN.md
  • src/plugins/obj/s3_accel/dell/cuobj_token_manager.h
  • src/plugins/obj/s3_accel/dell/engine_impl.cpp
  • src/plugins/obj/s3_accel/dell/engine_impl.h
  • test/gtest/unit/obj/obj_test_base.h

Comment thread src/plugins/obj/s3_accel/dell/engine_impl.h
- Class overview: list postXfer as a Dell override, note only prepXfer/
  checkXfer/releaseReqH are still inherited unchanged.
- postXfer docblock: add @brief, @PARAM, @return per project Doxygen
  coding guidelines.

Signed-off-by: Aaron Wu <aaron.wu@dell.com>

@jgoldsch12 jgoldsch12 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.

Approving this PR with some minor comments.

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 have nothing wrong with these changes - they seem purely style-istic and are probably unnecessary

Comment thread test/gtest/unit/obj/obj_cuobj.cpp Outdated
Comment thread src/plugins/obj/s3_accel/dell/DESIGN.md Outdated
Signed-off-by: Aaron Wu <aaron.wu@dell.com>
@dpressle

dpressle commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

👀 Investigating Clang Format Check…

@dpressle

dpressle commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

👀 Investigating PR Size Check…

@dpressle

dpressle commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🤖 CI Triage Agent — PR Size Check · commit dd317fc8

I have everything needed for a full diagnosis. Here is the complete triage report:


Summary: PR #1515 failed the "PR Size Check" because it modifies 871 lines in existing files, exceeding the enforced limit of 500 lines.

Root cause: The CI workflow runs git show --numstat to count lines added to modified files (excluding subprojects/*) and enforces a hard ceiling of 500. PR #1515, on branch feature-issue-1464-replace-callback-only (commit dd317fc), touches 871 lines — 371 lines over the limit — triggering the explicit exit 1:

❌ PR size check failed!
This PR adds 871 lines (excluding subprojects), which exceeds the maximum of 500 lines.

This is a policy enforcement failure, not a code defect. The job completed cleanly with no hangs or infrastructure issues (the entire run finished in under 1 second of wall time).

Implicated commit: [REDACTED:Hex High Entropy String] (branch author, PR #1515)

File: .github/workflows/ — PR Size Check workflow (the check script itself uses git show --numstat --diff-filter=M)

Suggested fix: The PR author has two options:

  1. Split the PR — Break feature-issue-1464-replace-callback-only into smaller, logically cohesive sub-PRs, each staying under 500 modified lines. This is the preferred path and the intent of the size gate.
  2. Request a policy exemption — If the entire 871-line change is genuinely atomic (e.g., a necessary large-scale rename/refactor that cannot be split), a project maintainer can bypass the check by adding an exception label or temporarily raising the threshold in the workflow file. This should be a conscious, reviewed decision — not a routine workaround.

Additionally, a secondary (non-blocking) notice is present in the logs: actions/checkout@v3 is running on the deprecated Node.js 20 runtime. Consider upgrading to actions/checkout@v4 before the forced cutover on June 16, 2026.

Related: #1464 (referenced in the branch name as the parent issue), none found for the size-check policy itself.

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id f36d3359-5d3a-4335-a8cb-70cec09dbd1a in the triage console for the audit trail.

@dpressle

dpressle commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🤖 CI Triage Agent — Clang Format Check · commit dd317fc8

I have a clear and complete picture. Here is the diagnosis:


Summary: PR #1515 fails the PR Size Check — it modifies 871 lines (above the 500-line hard limit enforced by CI).

Root cause: The CI workflow runs a git show --numstat diff against the merge commit and counts all lines added to modified files (excluding subprojects/). PR #1515 introduces 871 net-added lines, which exceeds the project's enforced maximum of 500 lines. The check exits with code 1 and the job (labelled "Clang Format Check" in the webhook trigger, but the actual failing step is the PR Size Check) fails immediately.

The decisive log lines are:

❌ PR size check failed!
This PR adds 871 lines (excluding subprojects), which exceeds the maximum of 500 lines.
##[error]Process completed with exit code 1.

Implicated commit: [REDACTED:Hex High Entropy String] (author from PR #1515 — [Issue-1464] replace callback with token management API)

File: The CI workflow file that enforces the limit (.github/workflows/ — PR size check step), and the source files changed in the PR itself that together accumulate 871 lines.

Suggested fix: The PR author has two options:

  1. Split the PR into smaller, logically independent chunks, each staying under the 500-line threshold. For example, split the callback-removal refactor from the new token-management API additions, and/or separate header changes from implementation changes.
  2. If the size limit is genuinely too restrictive for this feature, the team can discuss raising or exempting the limit in the CI workflow (e.g. with a special label or by adjusting the threshold), but this requires explicit maintainer approval — do not just bump the number unilaterally.

Option 1 (splitting the PR) is the standard and recommended path.

Related: #1515 (the failing PR), Issue #1464 (the parent feature issue)

🛡️ This comment had 1 potential secret(s) redacted (Hex High Entropy String). See request_id ca3889f6-0cae-45f7-b4b2-bc18bed60d9d in the triage console for the audit trail.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/gtest/unit/obj/obj_cuobj.cpp (1)

938-946: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't turn token-generation failures into passing tests.

After mgr.isConnected() and a successful registerMemory(), swallowing std::runtime_error makes these tests pass for both "no RDMA token support here" and real regressions in CuObjTokenManager::generatePutToken / generateGetToken. Please skip explicitly when the environment cannot generate tokens, or assert success otherwise.

💡 One way to preserve hardware gating without reporting false greens
     try {
-        std::string token = mgr.generatePutToken(buffer_, kBufferSize);
+        const std::string token = mgr.generatePutToken(buffer_, kBufferSize);
         // Token should be non-empty if generation succeeded.
         EXPECT_FALSE(token.empty());
     }
-    catch (const std::runtime_error &) {
-        // cuMemObjGetRDMAToken failed — expected if no RDMA NIC is present.
-        // This is not a test failure; token generation requires real hardware.
+    catch (const std::runtime_error &ex) {
+        GTEST_SKIP() << "RDMA token generation unavailable on this system: " << ex.what();
     }
     try {
-        std::string token = mgr.generateGetToken(buffer_, kBufferSize);
+        const std::string token = mgr.generateGetToken(buffer_, kBufferSize);
         EXPECT_FALSE(token.empty());
     }
-    catch (const std::runtime_error &) {
-        // Expected if no RDMA NIC.
+    catch (const std::runtime_error &ex) {
+        GTEST_SKIP() << "RDMA token generation unavailable on this system: " << ex.what();
     }

Also applies to: 962-968

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/gtest/unit/obj/obj_cuobj.cpp`:
- Around line 727-818: The DiagnoseConnection test block in
CuObjTokenManagerTest fails clang-format; reformat the entire
TEST_F(CuObjTokenManagerTest, DiagnoseConnection) function to comply with the
project's clang-format rules (or run the repo's clang-format script/IDE
integration) so indentation, spacing, and line-wrapping match the style; ensure
changes touch the TEST_F block and related variables (buffer_, kBufferSize, mgr,
tests, and all device/memory test code) so the Clang Format check passes.
- Around line 727-818: The test DiagnoseConnection in the TEST_F
CuObjTokenManagerTest should be made opt-in for local debugging; rename or move
it so it won't run by default (for example prefix the test name with DISABLED_
to become DISABLED_DiagnoseConnection) or wrap its body behind a debug-only
helper so normal CI won't execute it; update any references to the test name
accordingly and ensure the fixture symbols (CuObjTokenManagerTest,
DiagnoseConnection, mgr.isConnected()) remain unchanged except for the test name
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c2496cbd-d274-430e-9fb6-99188f4a6de6

📥 Commits

Reviewing files that changed from the base of the PR and between dccaaf0 and dd317fc.

📒 Files selected for processing (1)
  • test/gtest/unit/obj/obj_cuobj.cpp

Comment on lines +727 to 818
TEST_F(CuObjTokenManagerTest, DiagnoseConnection) {
std::cout << "=== CuObjTokenManager connection diagnostics ===" << std::endl;

#ifdef HAVE_CUDA
std::cout << " HAVE_CUDA: yes" << std::endl;

// GPU info
int dev_count = 0;
cudaGetDeviceCount(&dev_count);
std::cout << " CUDA devices: " << dev_count << std::endl;
for (int i = 0; i < dev_count; ++i) {
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, i);
std::cout << " [" << i << "] " << prop.name
<< " (compute " << prop.major << "." << prop.minor << ")" << std::endl;
}
#else
std::cout << " HAVE_CUDA: no" << std::endl;
#endif
std::cout << " useCuda_: " << (useCuda_ ? "true" : "false") << std::endl;
std::cout << " buffer_: " << buffer_ << std::endl;

CuObjTokenManager mgr;
std::cout << " isConnected(): " << (mgr.isConnected() ? "true" : "false") << std::endl;

if (!mgr.isConnected()) {
std::cout << "=== end diagnostics (not connected) ===" << std::endl;
return;
}

// Try different memory types and sizes
struct MemTest {
const char *label;
void *ptr;
size_t size;
std::function<void()> cleanup;
};
std::vector<MemTest> tests;

// Test 1: fixture buffer_ (cudaMalloc or posix_memalign) with full size
tests.push_back({"buffer_ (full 64K)", buffer_, kBufferSize, nullptr});

// Test 2: fixture buffer_ with 4K
tests.push_back({"buffer_ (4K)", buffer_, 4096, nullptr});

#ifdef HAVE_CUDA
// Test 3: fresh cudaMalloc 64K
void *gpu_buf = nullptr;
if (cudaMalloc(&gpu_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMalloc 64K", gpu_buf, kBufferSize,
[gpu_buf]() { cudaFree(gpu_buf); }});
}

// Test 4: cudaMallocHost (pinned host) 64K
void *pinned_buf = nullptr;
if (cudaMallocHost(&pinned_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMallocHost 64K", pinned_buf, kBufferSize,
[pinned_buf]() { cudaFreeHost(pinned_buf); }});
}

// Test 5: cudaMallocManaged (unified) 64K
void *managed_buf = nullptr;
if (cudaMallocManaged(&managed_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMallocManaged 64K", managed_buf, kBufferSize,
[managed_buf]() { cudaFree(managed_buf); }});
}
#endif

// Test 6: posix_memalign 64K
void *sys_buf = nullptr;
posix_memalign(&sys_buf, 4096, kBufferSize);
if (sys_buf) {
tests.push_back({"posix_memalign 64K", sys_buf, kBufferSize,
[sys_buf]() { free(sys_buf); }});
}

for (auto &t : tests) {
cuObjErr_t rc = mgr.registerMemory(t.ptr, t.size);
std::cout << " registerMemory(" << t.label << ", ptr=" << t.ptr
<< ", size=" << t.size << ") = " << rc << std::endl;
if (rc == CU_OBJ_SUCCESS) {
mgr.deregisterMemory(t.ptr);
}
}

// Cleanup
for (auto &t : tests) {
if (t.cleanup) t.cleanup();
}

std::cout << "=== end diagnostics ===" << std::endl;
}

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run clang-format on this diagnostics block before merge.

The current Clang Format Check is failing on this region, so the PR is blocked until this block is reformatted.

🧰 Tools
🪛 GitHub Actions: Clang Format Check / clang-format

[error] 737-737: clang-format check failed (diff found). The std::cout line formatting and line breaks do not match clang-format style.


[error] 761-811: clang-format check failed (diff found). Multiple formatting discrepancies in initializer/list formatting, lambda expressions, and stream line breaks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/gtest/unit/obj/obj_cuobj.cpp` around lines 727 - 818, The
DiagnoseConnection test block in CuObjTokenManagerTest fails clang-format;
reformat the entire TEST_F(CuObjTokenManagerTest, DiagnoseConnection) function
to comply with the project's clang-format rules (or run the repo's clang-format
script/IDE integration) so indentation, spacing, and line-wrapping match the
style; ensure changes touch the TEST_F block and related variables (buffer_,
kBufferSize, mgr, tests, and all device/memory test code) so the Clang Format
check passes.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Make this an opt-in diagnostic instead of a normal unit test.

This body only logs probes and returns early on !mgr.isConnected(), so it stays green even when every registration probe fails. Please move it behind DISABLED_ or a helper that is only run during local debugging.

♻️ Minimal change if you want to keep it in-tree
-TEST_F(CuObjTokenManagerTest, DiagnoseConnection) {
+TEST_F(CuObjTokenManagerTest, DISABLED_DiagnoseConnection) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TEST_F(CuObjTokenManagerTest, DiagnoseConnection) {
std::cout << "=== CuObjTokenManager connection diagnostics ===" << std::endl;
#ifdef HAVE_CUDA
std::cout << " HAVE_CUDA: yes" << std::endl;
// GPU info
int dev_count = 0;
cudaGetDeviceCount(&dev_count);
std::cout << " CUDA devices: " << dev_count << std::endl;
for (int i = 0; i < dev_count; ++i) {
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, i);
std::cout << " [" << i << "] " << prop.name
<< " (compute " << prop.major << "." << prop.minor << ")" << std::endl;
}
#else
std::cout << " HAVE_CUDA: no" << std::endl;
#endif
std::cout << " useCuda_: " << (useCuda_ ? "true" : "false") << std::endl;
std::cout << " buffer_: " << buffer_ << std::endl;
CuObjTokenManager mgr;
std::cout << " isConnected(): " << (mgr.isConnected() ? "true" : "false") << std::endl;
if (!mgr.isConnected()) {
std::cout << "=== end diagnostics (not connected) ===" << std::endl;
return;
}
// Try different memory types and sizes
struct MemTest {
const char *label;
void *ptr;
size_t size;
std::function<void()> cleanup;
};
std::vector<MemTest> tests;
// Test 1: fixture buffer_ (cudaMalloc or posix_memalign) with full size
tests.push_back({"buffer_ (full 64K)", buffer_, kBufferSize, nullptr});
// Test 2: fixture buffer_ with 4K
tests.push_back({"buffer_ (4K)", buffer_, 4096, nullptr});
#ifdef HAVE_CUDA
// Test 3: fresh cudaMalloc 64K
void *gpu_buf = nullptr;
if (cudaMalloc(&gpu_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMalloc 64K", gpu_buf, kBufferSize,
[gpu_buf]() { cudaFree(gpu_buf); }});
}
// Test 4: cudaMallocHost (pinned host) 64K
void *pinned_buf = nullptr;
if (cudaMallocHost(&pinned_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMallocHost 64K", pinned_buf, kBufferSize,
[pinned_buf]() { cudaFreeHost(pinned_buf); }});
}
// Test 5: cudaMallocManaged (unified) 64K
void *managed_buf = nullptr;
if (cudaMallocManaged(&managed_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMallocManaged 64K", managed_buf, kBufferSize,
[managed_buf]() { cudaFree(managed_buf); }});
}
#endif
// Test 6: posix_memalign 64K
void *sys_buf = nullptr;
posix_memalign(&sys_buf, 4096, kBufferSize);
if (sys_buf) {
tests.push_back({"posix_memalign 64K", sys_buf, kBufferSize,
[sys_buf]() { free(sys_buf); }});
}
for (auto &t : tests) {
cuObjErr_t rc = mgr.registerMemory(t.ptr, t.size);
std::cout << " registerMemory(" << t.label << ", ptr=" << t.ptr
<< ", size=" << t.size << ") = " << rc << std::endl;
if (rc == CU_OBJ_SUCCESS) {
mgr.deregisterMemory(t.ptr);
}
}
// Cleanup
for (auto &t : tests) {
if (t.cleanup) t.cleanup();
}
std::cout << "=== end diagnostics ===" << std::endl;
}
TEST_F(CuObjTokenManagerTest, DISABLED_DiagnoseConnection) {
std::cout << "=== CuObjTokenManager connection diagnostics ===" << std::endl;
`#ifdef` HAVE_CUDA
std::cout << " HAVE_CUDA: yes" << std::endl;
// GPU info
int dev_count = 0;
cudaGetDeviceCount(&dev_count);
std::cout << " CUDA devices: " << dev_count << std::endl;
for (int i = 0; i < dev_count; ++i) {
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, i);
std::cout << " [" << i << "] " << prop.name
<< " (compute " << prop.major << "." << prop.minor << ")" << std::endl;
}
`#else`
std::cout << " HAVE_CUDA: no" << std::endl;
`#endif`
std::cout << " useCuda_: " << (useCuda_ ? "true" : "false") << std::endl;
std::cout << " buffer_: " << buffer_ << std::endl;
CuObjTokenManager mgr;
std::cout << " isConnected(): " << (mgr.isConnected() ? "true" : "false") << std::endl;
if (!mgr.isConnected()) {
std::cout << "=== end diagnostics (not connected) ===" << std::endl;
return;
}
// Try different memory types and sizes
struct MemTest {
const char *label;
void *ptr;
size_t size;
std::function<void()> cleanup;
};
std::vector<MemTest> tests;
// Test 1: fixture buffer_ (cudaMalloc or posix_memalign) with full size
tests.push_back({"buffer_ (full 64K)", buffer_, kBufferSize, nullptr});
// Test 2: fixture buffer_ with 4K
tests.push_back({"buffer_ (4K)", buffer_, 4096, nullptr});
`#ifdef` HAVE_CUDA
// Test 3: fresh cudaMalloc 64K
void *gpu_buf = nullptr;
if (cudaMalloc(&gpu_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMalloc 64K", gpu_buf, kBufferSize,
[gpu_buf]() { cudaFree(gpu_buf); }});
}
// Test 4: cudaMallocHost (pinned host) 64K
void *pinned_buf = nullptr;
if (cudaMallocHost(&pinned_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMallocHost 64K", pinned_buf, kBufferSize,
[pinned_buf]() { cudaFreeHost(pinned_buf); }});
}
// Test 5: cudaMallocManaged (unified) 64K
void *managed_buf = nullptr;
if (cudaMallocManaged(&managed_buf, kBufferSize) == cudaSuccess) {
tests.push_back({"cudaMallocManaged 64K", managed_buf, kBufferSize,
[managed_buf]() { cudaFree(managed_buf); }});
}
`#endif`
// Test 6: posix_memalign 64K
void *sys_buf = nullptr;
posix_memalign(&sys_buf, 4096, kBufferSize);
if (sys_buf) {
tests.push_back({"posix_memalign 64K", sys_buf, kBufferSize,
[sys_buf]() { free(sys_buf); }});
}
for (auto &t : tests) {
cuObjErr_t rc = mgr.registerMemory(t.ptr, t.size);
std::cout << " registerMemory(" << t.label << ", ptr=" << t.ptr
<< ", size=" << t.size << ") = " << rc << std::endl;
if (rc == CU_OBJ_SUCCESS) {
mgr.deregisterMemory(t.ptr);
}
}
// Cleanup
for (auto &t : tests) {
if (t.cleanup) t.cleanup();
}
std::cout << "=== end diagnostics ===" << std::endl;
}
🧰 Tools
🪛 GitHub Actions: Clang Format Check / clang-format

[error] 737-737: clang-format check failed (diff found). The std::cout line formatting and line breaks do not match clang-format style.


[error] 761-811: clang-format check failed (diff found). Multiple formatting discrepancies in initializer/list formatting, lambda expressions, and stream line breaks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/gtest/unit/obj/obj_cuobj.cpp` around lines 727 - 818, The test
DiagnoseConnection in the TEST_F CuObjTokenManagerTest should be made opt-in for
local debugging; rename or move it so it won't run by default (for example
prefix the test name with DISABLED_ to become DISABLED_DiagnoseConnection) or
wrap its body behind a debug-only helper so normal CI won't execute it; update
any references to the test name accordingly and ensure the fixture symbols
(CuObjTokenManagerTest, DiagnoseConnection, mgr.isConnected()) remain unchanged
except for the test name change.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants