Skip to content

Improve URMA build support, mock isolation, and transport reliability - #25

Open
cw20050111-prog wants to merge 29 commits into
LinQuickDev:urma_transportfrom
cw20050111-prog:urma_transport_dev
Open

Improve URMA build support, mock isolation, and transport reliability#25
cw20050111-prog wants to merge 29 commits into
LinQuickDev:urma_transportfrom
cw20050111-prog:urma_transport_dev

Conversation

@cw20050111-prog

@cw20050111-prog cw20050111-prog commented Aug 25, 2026

Copy link
Copy Markdown

What problem does this PR solve?

Related issue: apache/brpc 3428

This PR addresses URMA review findings around Bazel support, mock isolation, build compatibility, transport limits, observability, and TCP fallback.

What is changed?

  • Add Bazel support for local UMDK headers through URMA_ROOT.
  • Add BRPC_DOWNLOAD_URMA_HEADERS to control the Bazel download fallback.
  • Add Bazel-specific URMA build instructions in the English and Chinese documentation.
  • Add an independent WITH_URMA_MOCK / BRPC_WITH_URMA_MOCK switch so mock builds cannot silently replace real URMA builds.
  • Make the URMA mock compatible with C++14 and --werror.
  • Add a Makefile CI job covering the C++14 URMA mock build.
  • Cap a single SEND WR at 4096 bytes and split larger messages into multiple WRs.
  • Add readable completion-status logging and more detailed failure context.
  • Zero-initialize SGE fields before posting work requests.
  • Allow an URMA client to fall back to TCP when connecting to a server running in TCP mode.
  • Add an integration test for the URMA-to-TCP fallback path.
  • Remove personal Graphify entries from .gitignore.

CMake and hardware validation

Environment: aarch64 openEuler, umdk-urma 26.06.0-B020, real URMA hardware between node1 and node3.

  • CMake build with real liburma completed successfully.
  • The URMA-to-TCP fallback integration test passed on real URMA hardware.
  • Full brpc_urma_unittest: 11 tests passed, 4 mock-only tests skipped, and the process exited cleanly.
  • Cross-node messages from 4096 bytes to 1 MB completed with zero errors when the 4096-byte WR limit was enabled.
  • Raising the single-WR limit to 65536 reproduced message failures, confirming that the device-reported max_msg_size=65536 cannot be used as the transport limit.

Bazel validation

Environment: Bazel 7.4.1, aarch64 openEuler, UMDK installed under /usr.

  • Bzlmod resolved @umdk//:urma_headers successfully with URMA_ROOT=/usr and BRPC_DOWNLOAD_URMA_HEADERS=false.
  • The real URMA build completed successfully and generated bazel-bin/libbrpc.a and bazel-bin/libbrpc.so.
  • readelf confirmed that the real build links liburma.so.0.
  • The explicit mock build completed successfully with both BRPC_WITH_URMA=true and BRPC_WITH_URMA_MOCK=true.
  • The mock build does not link liburma, and nm confirmed that it defines urma_create_context itself.
  • With no URMA_ROOT and BRPC_DOWNLOAD_URMA_HEADERS=false, repository resolution failed with the expected clear error instead of downloading UMDK.

WORKSPACE-mode UMDK repository resolution also succeeded. Full WORKSPACE-mode analysis on this aarch64 host is blocked by the repository's existing pinned rules_perl, which only registers x86_64 toolchains. This limitation is unrelated to the URMA changes; the Bzlmod real and mock builds both completed successfully.

cw20050111-prog and others added 29 commits August 21, 2026 14:25
…works

The umdk repo ships its own src/urma/BUILD.bazel, which makes Bazel
treat src/urma/ as a separate package. glob() in our umdk.BUILD
(rooted at the umdk repo root) can't cross that boundary, so
hdrs = glob(["src/urma/lib/urma/**/include/*.h"]) silently resolved to
an empty list. The cc_library then exported no header inputs, so
compiling anything that #includes urma_api.h failed with "No such
file or directory" even though the -isystem path was correct and the
file existed on disk.

Confirmed via GitHub Actions on both ubuntu-22.04 (x86_64) and
ubuntu-24.04-arm (arm64): `bazel build --define=BRPC_WITH_URMA=true //:brpc`
failed identically on both before this patch_cmds fix.
Reviewer dwh110 pointed out that src/brpc/urma/mock_urma.cpp had no
independent feature switch: whenever liburma wasn't found, CMake/Make
silently linked the mock, which can produce a binary that looks
URMA-capable but can't reach real hardware.

Add WITH_URMA_MOCK (CMake) / --with-urma-mock (config_brpc.sh),
default OFF. When WITH_URMA is enabled and liburma isn't found, the
build now fails with a clear message unless the mock is explicitly
requested, instead of substituting it implicitly. Document the new
flag in docs/en/urma.md and docs/cn/urma.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add conditional linking for URMA library based on availability.
Added a comment regarding linking with liburma based on brpc build options.
Added logic to find the URMA library and handle its absence.
Removed conditional check for URMA library and related messages.
Update logic for linking liburma based on header presence.
Add local design notes and Graphify artifacts to .gitignore
Ensure bond_include is always symlinked, adding a check for its presence.
…C++14 clean

The cmake-unittest-urma-mock CI job failed to compile: the urma_user_ctl()
mock added in 9b540ac was pasted into the middle of urma_create_context(),
so the file had a function definition nested inside another function
("error: a function-definition is not allowed here before '{' token").
Move urma_user_ctl() out to file scope, after urma_create_context().

While verifying the fix, the same sources were built the way a real
deployment builds them (Makefile path, -std=c++14, --werror) and three
further problems showed up:

* mock_urma.cpp used std::shared_mutex, which is C++17. config_brpc.sh
  pins -std=c++14, so `--with-urma --with-urma-mock` could not compile at
  all outside CMake. Use std::shared_timed_mutex (C++14, same reader/
  writer semantics) behind a MockSharedMutex alias.
* mock_urma.cpp ignored the results of write()/read() on the JFCE eventfd
  with a plain (void) cast, which glibc's warn_unused_result still
  diagnoses; capture the result instead.
* urma_endpoint.cpp compared a signed bthread_tag_t against
  _poller_groups.size() (-Wsign-compare), and brpc_urma_unittest.cpp
  memset() a ParsedHello, which is non-trivial (-Wclass-memaccess).

Add a make-compile-urma-mock CI job so the C++14/--werror combination is
covered from now on; the existing CMake job builds with the compiler's
default standard and cannot catch it.

Verified locally: CMake mock build + ctest, CMake build against a real
liburma.so (mock compiled as a shared library standing in for the SDK),
and the Makefile build of every URMA object with --werror --with-urma
--with-urma-mock. All 14 brpc_urma_unittest cases pass in both CMake
configurations.
WITH_URMA_MOCK=ON was silently ignored on any machine that has liburma
installed: CMake checked "is liburma found?" first and only consulted the
switch in the not-found branch. config_brpc.sh had the same inversion.

That makes a mock build mean different things on different machines --
the mock on a CI image without liburma, a hardware build on a developer
box that happens to have the SDK -- which is exactly the "the mock lacks
an independent feature switch" problem the switch was added to solve. It
also disagreed with Bazel, where brpc_with_urma_mock is a specialization
of brpc_with_urma and therefore already wins whenever both defines are
set.

Check the explicit opt-in first, so all three build systems agree. When
the switch overrides a real liburma, CMake emits a WARNING and
config_brpc.sh an info line naming the library being bypassed. Not
finding liburma without the switch still fails the build, unchanged.

Found on an aarch64 box with umdk-urma installed: -DWITH_URMA_MOCK=ON
configured as mock=0 there, leaving the mock side of the build
unbuildable and untestable.
On CTP -- which is what brpc advertises in MakeLocalParsedHello -- a
single URMA SEND may not carry more than 4096 bytes. Over-size WRs are
not rejected by urma_post_jetty_send_wr: the call succeeds and reports a
successful completion, but the payload never arrives intact, so the
peer's parser waits forever for bytes that never come. The RPC only
fails on timeout, with clean logs on both ends.

The send path previously bounded a WR only by the peer's advertised recv
block size (urma_buffer_size - sizeof(IOBuf::Block), 8160 by default) --
twice the real limit. Clamp it by the transport limit as well so larger
messages are split across WRs. No protocol or handshake change is
needed: the receiver already appends each completion to _read_buf in
order and the protocol parser reassembles them, exactly as it does today
for messages larger than one recv block.

The device's own dev_cap.max_msg_size cannot be used as this bound.
Measured on aarch64 with umdk-urma 26.06.0-B020 the device reports
65536, yet every message above 4096 fails on the wire.

Also:
- add --urma_max_msg_size so the bound can be raised where the transport
  really does carry larger messages, still clamped by the device value;
- report the full completion record (direction, opcode, lengths, jetty
  ids, window state) when a WR fails instead of a bare status number,
  and map urma_cr_status_t to a readable name;
- zero-initialize the on-stack SGE array -- cut_into_sglist() sets
  addr/len/tseg but never user_tseg.

Verified across two aarch64 nodes over real URMA hardware: with the cap
in place every attachment size from 0 to 1MB completes with zero errors;
with the cap raised to the device-reported 65536, everything from 4096
up fails. Single-node loopback behaves differently from a real link and
must not be used to validate this path.
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.

1 participant