Skip to content

Add IPC_V2 data path for UBRing IPC - #3573

Merged
chenBright merged 3 commits into
apache:masterfrom
LinQuickDev:ubshm_transport_fix_3-2
Oct 13, 2026
Merged

chenBright merged 3 commits into
apache:masterfrom
LinQuickDev:ubshm_transport_fix_3-2

Conversation

@zchuango

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: #3463

Problem Summary:

This is the second PR in Phase 3 of Issue #3463.

The first Phase 3 PR (#3507) introduced backward-safe UBRing data-format negotiation, but both IPC and UBS still use the existing LEGACY_64 data path. It divides data into 60-byte payload chunks and stages each chunk through local_msg_space before publishing a 64-byte slot with Copy64Byte.

This design keeps the two shared-memory backends on a common data path, but it prevents local IPC from using larger slots and copying iovec data directly into shared memory.

This PR adds the negotiated IPC_V2 format for the IPC backend. IPC_V2 uses 4096-byte slots and a dedicated SPSC ring with direct iovec-to-shared-memory copying. It is enabled only when both peers select IPC_V2; otherwise, the connection safely falls back to TCP.

The UBS backend continues to select LEGACY_64, and its existing data path remains unchanged.

What is changed and the side effects?

Changed:

  • Add UBR_DATA_FORMAT_IPC_V2.
  • Select the preferred data format according to the local shared-memory backend:
    • IPC selects IPC_V2.
    • UBS continues to select LEGACY_64.
  • Require both peers to select the same supported format before enabling UBRing.
  • Fall back to TCP when the selected formats do not match.
  • Add a fixed IPC_V2 slot layout:
    • 4096-byte slot;
    • 16-byte header;
    • 4080-byte payload;
    • 64-byte alignment;
    • explicit EMPTY and READY ownership states.
  • Add an SPSC IPC_V2 ring using release/acquire publication and recycling semantics.
  • Copy iovec data directly into IPC_V2 shared-memory slots instead of first copying it into the legacy 64-byte intermediate message buffer.
  • Add IPC_V2 support for:
    • writev and readv;
    • multi-slot messages;
    • partial reads;
    • ring wrap-around;
    • backpressure;
    • readable/writable checks;
    • peer notification.
  • Keep format-specific capacity, cursor, and initialization state in the existing UBRing transaction.
  • Initialize each peer's local receive queue before enabling the IPC_V2 data path.
  • Use the shared-memory length carried by the existing 64-byte Base Hello when mapping the remote queue.
  • Add focused tests for the IPC_V2 layout, queue mapping, initialization, format selection, read/write dispatch, boundary conditions, partial reads, wrap-around, backpressure, invalid input, error mapping, and concurrent SPSC operation.
  • Keep the existing LEGACY_64 slot layout, 60-byte payload, Copy64Byte, and UBS data path unchanged.

The existing handshake wire format is not changed by this PR:

  • The Base Hello remains 64 bytes.
  • The V3 format extension remains a fixed 4-byte message.
  • No additional handshake bytes are introduced.

Tests:

Full CMake build passed.
30 tests from 10 test suites passed in brpc_ubring_unittest.

Smoke-test results:

  • An IPC_V2 client and server successfully negotiated UBRing.
  • 353678 RPC requests completed with 0 failures.
  • Both peers confirmed successful UBRing establishment.

Side effects:

  • Performance effects:

    IPC_V2 reduces the number of slots required for larger messages and removes the legacy local_msg_space -> Copy64Byte staging copy from the IPC path.

    The 4096-byte slot size was selected from the evaluated IPC_V2 microbenchmark candidates as a balance between throughput and the number of available slots.

    Small messages still occupy one 4096-byte IPC_V2 slot, so the number of simultaneously queued small messages is lower than with the legacy 64-byte layout.

    UBS has no data-path performance change because it continues to use LEGACY_64.

  • Breaking backward compatibility:

    No wire-format compatibility break is introduced.

    IPC_V2 is enabled only when both peers select it. An IPC_V2 peer connected to a peer that only supports LEGACY_64 falls back to TCP.

    IPC and UBS peers also fall back to TCP when their selected data formats differ.

    The existing Base Hello and V3 format-extension layouts remain unchanged, so no additional bytes can remain unread in the TCP stream during fallback.

Timer and teardown lifecycle changes are intentionally out of scope and remain covered by the separate Phase 2 work in #3509.


Check List:

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Format fallback can leave active server resources behind, and a client setup failure can retain shared-memory mappings.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

This PR adds a negotiated IPC_V2 data path to UBRing so local IPC can copy directly into larger shared-memory slots. UBS retains the legacy path, and peers with different formats fall back to TCP.

Changes:

  • Add 4096-byte IPC_V2 slots and an SPSC ring for direct iovec reads and writes.
  • Select a data format by shared-memory backend without changing the handshake wire layout.
  • Add layout, ring, dispatch, and format-selection tests.
File Description
test/​brpc_ubring_unittest.cpp Adds IPC_V2 and format-selection tests.
src/​brpc/​ubshm/​ubr_trx.h Stores format and read-offset state.
src/​brpc/​ubshm/​ubr_msg_v2.h Declares the IPC_V2 slot and ring interfaces.
src/​brpc/​ubshm/​ubr_msg_v2.cpp Implements IPC_V2 queue operations.
src/​brpc/​ubshm/​ubr_data_format.h Defines the IPC_V2 format identifier.
src/​brpc/​ubshm/​ub_ring.h Declares format-specific ring operations.
src/​brpc/​ubshm/​ub_ring.cpp Adds IPC_V2 dispatch and queue setup.
src/​brpc/​ubshm/​ub_endpoint.h Declares format-selection helpers.
src/​brpc/​ubshm/​ub_endpoint.cpp Negotiates formats during the handshake.
src/​brpc/​ubshm/​shm/​shm_mgr.h Exposes the selected shared-memory backend.
src/​brpc/​ubshm/​shm/​shm_mgr.cpp Implements the backend getter.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

local_extension.format_id =
static_cast<uint16_t>(selected_format);
} else {
ub_transport->_ub_state = UBShmTransport::UB_OFF;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. The format-mismatch fallback needs explicit rollback of the provisional server resources. This overlaps with #3509, which changes timer cancellation, cleanup ownership, and transaction slot reuse. I plan to update this PR after #3509 lands, then implement the rollback using the new lifecycle APIs and add coverage for this fallback path.

Comment on lines +1223 to +1226
if (UNLIKELY(rc != UBRING_OK)) {
LOG(ERROR) << "Initialize client data format failed, local_name="
<< local_name << ", format=" << format;
return rc;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed that this failure path needs explicit cleanup. The specific “fewer than two slots” example may not be reachable through the normal IPC allocation path because shared memory is required to be at least 4 MiB and allocation-unit aligned, but the error path should still release all provisional resources. I’ll address it after updating this PR with #3509 and add failure-path coverage.

@wwbmmm

wwbmmm commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

LGTM

@zchuango

Copy link
Copy Markdown
Contributor Author

@chenBright Do you have any further comments on this PR? If not, I'll go ahead and merge it.

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

LGTM

@chenBright
chenBright merged commit 82ff823 into apache:master Oct 13, 2026
25 checks passed
Sherlock0203 added a commit to Sherlock0203/brpc that referenced this pull request Oct 16, 2026
Bring the negotiation and IPC_V2 work that landed on master (apache#3507,
apache#3573) into this branch so it can be merged back without conflicts.

Those master changes were built on the timerfd-era close path, which
this branch had replaced with the bthread timer facade,
generation-checked pool teardown and the cleanup worker, so both sides
evolved the same ubshm core after 9334389.

Resolution:
- Close/clear core (UbrTrxClose, UbrTrxCloseCallback,
  UbrClearResourceCheck, ClearTrxResource, UbrPassiveClearTrx,
  UbrTrxCloseCheck) keeps this branch's versions. Master's extra
  start_time/op parameters were unused, and UbrCloseType only chose
  between DeleteTimerSafe and DeleteTimer, which the facade subsumes:
  UbrTimerDelAndWait degrades to the non-blocking delete inside a
  callback. The unused UbrCloseType enum is therefore dropped.
- Master's format-aware initialization is kept: UbrServerTrxInit keeps
  the UbrDataFormat parameter and the Initialize*LegacyFormat split,
  UbrAllocateServerShm/UbrMapRemoteShm(UbrMapRemoteShmAddTimer) keep
  passing it, and ApplyAndMapLocalShm no longer presets the rx capacity
  (InitializeClientLocalLegacyFormat owns that now). All IPC_V2 ring
  code and the negotiation handshake are master's, unchanged.
- Master-side UNLIKELY/LIKELY are converted to BAIDU_UNLIKELY/
  BAIDU_LIKELY, matching the module convention from this branch's style
  pass; common.h keeps the plain aliases.
- This branch's fixes that master lacked merge on top: the poller
  re-ADD refresh, butil::atomic for the poller-group flag, the lazy
  bthread timers replacing TimerInit, and the cleanup-worker changes
  (leaked queue, start retry instead of the terminal latch).
- test/brpc_ubring_unittest.cpp: master's IPC_V2 assertions on the
  removed timer_fd/hb_timer_fd fields now check the unarmed
  close_timer/hb_timer slots instead. The UbrCleanupWorkerTest that
  PR LinQuickDev#74's squash dropped is restored (the commit describes it and the
  g_ub_test_worker_start_rc hook it exercises, but only the two source
  files made it into the squash), so both sides' suites run: 31 tests.

Tested: cmake --build build --target brpc_ubring_unittest (WITH_UBRING
=ON, BUILD_UNIT_TESTS=ON) and ./build/test/brpc_ubring_unittest
(31/31), also under ASAN (detect_leaks=0, 31/31). e2e with
example/ubring_performance --use_ubring=true over the IPC backend:
UBRING_* mappings confirmed, the negotiated IPC_V2 data path sustains
~148k QPS at ~6us, phase teardowns are clean, and a server kill -9
is handled without errors or residue.
Sherlock0203 added a commit to Sherlock0203/brpc that referenced this pull request Oct 19, 2026
Bring the apache master line up to f0239d7 into this branch in a
single merge, replacing the two stacked merges with master@36c2d12e
and the macOS fix only. Everything the branch needs from that line is
included: the data-format negotiation (apache#3507), the IPC_V2 data path
(apache#3573), the macOS OpenSSL 4.0 build fix (apache#3616: X509 accessors return
const pointers in the current macos-26-arm64 runner image, fixed with
auto*), and the newer fixes, notably the null-termination of the peer
shm_name in HelloMessage::Deserialize (apache#3605), the MPSC queue node
release ordering (apache#3620) and the MultiFlatMap bucket-head erasure fix
(apache#3617).

Those master changes were built on the timerfd-era close path, which
this branch had replaced with the bthread timer facade,
generation-checked pool teardown and the cleanup worker, so both sides
evolved the same ubshm core after 9334389.

Resolution:
- Close/clear core (UbrTrxClose, UbrTrxCloseCallback,
  UbrClearResourceCheck, ClearTrxResource, UbrPassiveClearTrx,
  UbrTrxCloseCheck) keeps this branch's versions. Master's extra
  start_time/op parameters were unused, and UbrCloseType only chose
  between DeleteTimerSafe and DeleteTimer, which the facade subsumes:
  UbrTimerDelAndWait degrades to the non-blocking delete inside a
  callback. The unused UbrCloseType enum is therefore dropped.
- Master's format-aware initialization is kept: UbrServerTrxInit keeps
  the UbrDataFormat parameter and the Initialize*LegacyFormat split,
  UbrAllocateServerShm/UbrMapRemoteShm(UbrMapRemoteShmAddTimer) keep
  passing it, and ApplyAndMapLocalShm no longer presets the rx capacity
  (InitializeClientLocalLegacyFormat owns that now). All IPC_V2 ring
  code and the negotiation handshake are master's, unchanged, with
  master-side UNLIKELY/LIKELY converted to the module's BAIDU_* macros.
- This branch's fixes that master lacked merge on top: the poller
  re-ADD refresh, butil::atomic for the poller-group flag, the lazy
  bthread timers replacing TimerInit, and the cleanup-worker changes
  (leaked queue, start retry instead of the terminal latch).
- test/brpc_ubring_unittest.cpp: master's IPC_V2 assertions on the
  removed timer_fd/hb_timer_fd fields check the unarmed
  close_timer/hb_timer slots instead, and the UbrCleanupWorkerTest that
  PR LinQuickDev#74's squash dropped is restored alongside master's apache#3605
  hardening of the full_shm_name test. Both sides' suites run.

Tested: the close/cleanup/IPC_V2 resolution was verified on the
equivalent tree before apache#3605 came in -- cmake WITH_UBRING=ON build,
31/31 unit tests, the same under ASAN (detect_leaks=0), and an
example/ubring_performance e2e over the IPC backend (negotiated IPC_V2
path at ~148k QPS, clean teardowns, server kill -9 handled). The new
master commits are additive to that resolution; cmake --build build
--target brpc_ubring_unittest and ./build/test/brpc_ubring_unittest
pass on this exact tree as well.
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.

5 participants