This is an automated email from the ASF dual-hosted git repository.

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new ab532daf Add UBRing data format negotiation (#3507)
ab532daf is described below

commit ab532daf10b52c1b683e2dc7cdfacb8ec96987d2
Author: Chuang Zhang <[email protected]>
AuthorDate: Mon Sep 21 16:38:08 2026 +0800

    Add UBRing data format negotiation (#3507)
    
    * Add UBRing data format negotiation (#34)
    
    * Fix unaligned UBRing ACK access
    
    * Use direct list initialization for HelloMessage
    
    ---------
    
    Co-authored-by: BGQ99 <[email protected]>
---
 src/brpc/ubshm/ub_endpoint.cpp | 145 ++++++++++++++++++++++++++++++++++-------
 src/brpc/ubshm/ub_endpoint.h   |  28 +++++++-
 test/brpc_ubring_unittest.cpp  |  55 ++++++++++++++++
 3 files changed, 200 insertions(+), 28 deletions(-)

diff --git a/src/brpc/ubshm/ub_endpoint.cpp b/src/brpc/ubshm/ub_endpoint.cpp
index 19bca4f9..44685eb3 100644
--- a/src/brpc/ubshm/ub_endpoint.cpp
+++ b/src/brpc/ubshm/ub_endpoint.cpp
@@ -59,13 +59,33 @@ static const size_t MAGIC_STR_LEN = 2;
 static const size_t HELLO_MSG_LEN_MIN = 64;
 static const size_t ACK_MSG_LEN = 4;
 static uint16_t g_ub_hello_msg_len = 64;
-static uint16_t g_ub_hello_version = 2;
+static uint16_t g_ub_hello_version = 3;
 static uint16_t g_ub_impl_version = 1;
 
 static const uint32_t ACK_MSG_UB_OK = 0x1;
 
 static butil::Mutex* g_ubring_resource_mutex = nullptr;
 
+void HelloFormatExtension::Serialize(void* data) const {
+    char* current_pos = static_cast<char*>(data);
+    const uint16_t net_extension_len = butil::HostToNet16(extension_len);
+    memcpy(current_pos, &net_extension_len, sizeof(net_extension_len));
+    current_pos += sizeof(net_extension_len);
+    const uint16_t net_format_id = butil::HostToNet16(format_id);
+    memcpy(current_pos, &net_format_id, sizeof(net_format_id));
+}
+
+void HelloFormatExtension::Deserialize(const void* data) {
+    const char* current_pos = static_cast<const char*>(data);
+    uint16_t net_extension_len;
+    memcpy(&net_extension_len, current_pos, sizeof(net_extension_len));
+    extension_len = butil::NetToHost16(net_extension_len);
+    current_pos += sizeof(net_extension_len);
+    uint16_t net_format_id;
+    memcpy(&net_format_id, current_pos, sizeof(net_format_id));
+    format_id = butil::NetToHost16(net_format_id);
+}
+
 void HelloMessage::Serialize(void* data) const {
     char* current_pos = static_cast<char*>(data);
     const uint16_t net_msg_len = butil::HostToNet16(msg_len);
@@ -140,6 +160,7 @@ void UBShmEndpoint::Reset() {
     delete _ub_ring;
     _ub_ring = nullptr;
     _poller_sid = INVALID_SOCKET_ID;
+    _negotiated_data_format = UBR_DATA_FORMAT_NONE;
     _state = UNINIT;
 }
 
@@ -328,6 +349,7 @@ inline void UBShmEndpoint::TryReadOnTcp() {
 
 void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
     UBShmEndpoint* ep = static_cast<UBShmEndpoint*>(arg);
+    ep->_negotiated_data_format = UBR_DATA_FORMAT_NONE;
     SocketUniquePtr s(ep->_socket);
     UBConnect::RunGuard rg((UBConnect*)s->_app_connect.get());
 
@@ -350,7 +372,7 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
     }
 
     ep->_state = C_HELLO_SEND;
-    HelloMessage local_msg;
+    HelloMessage local_msg{};
     local_msg.msg_len = g_ub_hello_msg_len;
     local_msg.hello_ver = g_ub_hello_version;
     local_msg.impl_ver = g_ub_impl_version;
@@ -395,7 +417,7 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
     }
     HelloMessage remote_msg;
     remote_msg.Deserialize(data);
-    if (remote_msg.msg_len < HELLO_MSG_LEN_MIN) {
+    if (remote_msg.msg_len != HELLO_MSG_LEN_MIN) {
         LOG(WARNING) << "Fail to parse Hello Message length from server:"
                      << s->description();
         s->SetFailed(EPROTO, "Fail to complete ubring handshake from %s: %s",
@@ -404,22 +426,56 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
         return nullptr;
     }
 
-    if (remote_msg.msg_len > HELLO_MSG_LEN_MIN) {
-        // TODO: Read Hello Message customized data
-        // Just for future use, should not happen now
-    }
-
+    UbrDataFormat selected_format = UBR_DATA_FORMAT_NONE;
     if (!HelloNegotiationValid(remote_msg)) {
         LOG(WARNING) << "Fail to negotiate with server, fallback to tcp:"
                      << s->description();
         ub_transport->_ub_state = UBShmTransport::UB_OFF;
     } else {
-        ep->_state = C_MAP_REMOTE_SHM;
-        if (ep->_ub_ring->UbrMapRemoteShm(&local_trx_shm, shm_name) < 0) {
-            LOG(WARNING) << "Fail to map the remote shm, fallback to tcp:" << 
s->description();
+        HelloFormatExtension local_extension = {
+            HelloFormatExtension::WIRE_SIZE, UBR_DATA_FORMAT_LEGACY_64};
+        local_extension.Serialize(data);
+        ep->_state = C_FORMAT_SEND;
+        if (ep->WriteToFd(data, HelloFormatExtension::WIRE_SIZE) < 0) {
+            const int saved_errno = errno;
+            PLOG(WARNING) << "Fail to send format extension to server:"
+                          << s->description();
+            s->SetFailed(saved_errno,
+                    "Fail to complete ubring handshake from %s: %s",
+                    s->description().c_str(), berror(saved_errno));
+            ep->_state = FAILED;
+            return nullptr;
+        }
+
+        ep->_state = C_FORMAT_WAIT;
+        if (ep->ReadFromFd(data, HelloFormatExtension::WIRE_SIZE) < 0) {
+            const int saved_errno = errno;
+            PLOG(WARNING) << "Fail to read format extension from server:"
+                          << s->description();
+            s->SetFailed(saved_errno,
+                    "Fail to complete ubring handshake from %s: %s",
+                    s->description().c_str(), berror(saved_errno));
+            ep->_state = FAILED;
+            return nullptr;
+        }
+        HelloFormatExtension remote_extension;
+        remote_extension.Deserialize(data);
+        if (remote_extension.extension_len != HelloFormatExtension::WIRE_SIZE 
||
+            remote_extension.format_id == UBR_DATA_FORMAT_NONE ||
+            remote_extension.format_id != local_extension.format_id) {
+            LOG(WARNING) << "Fail to negotiate data format with server, "
+                         << "fallback to tcp:" << s->description();
             ub_transport->_ub_state = UBShmTransport::UB_OFF;
         } else {
-            ub_transport->_ub_state = UBShmTransport::UB_ON;
+            selected_format = UBR_DATA_FORMAT_LEGACY_64;
+            ep->_state = C_MAP_REMOTE_SHM;
+            if (ep->_ub_ring->UbrMapRemoteShm(&local_trx_shm, shm_name) < 0) {
+                LOG(WARNING) << "Fail to map the remote shm, fallback to tcp:"
+                             << s->description();
+                ub_transport->_ub_state = UBShmTransport::UB_OFF;
+            } else {
+                ub_transport->_ub_state = UBShmTransport::UB_ON;
+            }
         }
     }
 
@@ -428,8 +484,8 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
     if (ub_transport->_ub_state != UBShmTransport::UB_OFF) {
         flags |= ACK_MSG_UB_OK;
     }
-    uint32_t* tmp = (uint32_t*)data;
-    *tmp = butil::HostToNet32(flags);
+    const uint32_t net_flags = butil::HostToNet32(flags);
+    memcpy(data, &net_flags, sizeof(net_flags));
     if (ep->WriteToFd(data, ACK_MSG_LEN) < 0) {
         const int saved_errno = errno;
         PLOG(WARNING) << "Fail to send Ack Message to server:" << 
s->description();
@@ -440,6 +496,7 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
     }
 
     if (ub_transport->_ub_state == UBShmTransport::UB_ON) {
+        ep->_negotiated_data_format = selected_format;
         ep->_state = ESTABLISHED;
         ep->_ub_ring->UbrUnlinkLocalShm();
         LOG_IF(INFO, FLAGS_ub_trace_verbose) 
@@ -457,6 +514,7 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
 
 void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
     UBShmEndpoint* ep = static_cast<UBShmEndpoint*>(arg);
+    ep->_negotiated_data_format = UBR_DATA_FORMAT_NONE;
     SocketUniquePtr s(ep->_socket);
 
     LOG_IF(INFO, FLAGS_ub_trace_verbose)
@@ -497,7 +555,7 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
     HelloMessage remote_msg;
     remote_msg.Deserialize(data);
     LOG_IF(INFO, FLAGS_ub_trace_verbose) << "server receive handshake message 
: " << remote_msg.toString();
-    if (remote_msg.msg_len < HELLO_MSG_LEN_MIN) {
+    if (remote_msg.msg_len != HELLO_MSG_LEN_MIN) {
         LOG(WARNING) << "Fail to parse Hello Message length from client:"
                      << s->description();
         s->SetFailed(EPROTO, "Fail to complete ubring handshake from %s: %s",
@@ -505,11 +563,6 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
         ep->_state = FAILED;
         return nullptr;
     }
-    if (remote_msg.msg_len > HELLO_MSG_LEN_MIN) {
-        // TODO: Read Hello Message customized header
-        // Just for future use, should not happen now
-    }
-
     if (!HelloNegotiationValid(remote_msg)) {
         LOG(WARNING) << "Fail to negotiate with client, fallback to tcp:"
                      << s->description();
@@ -543,7 +596,7 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
     }
 
     ep->_state = S_HELLO_SEND;
-    HelloMessage local_msg;
+    HelloMessage local_msg{};
     local_msg.msg_len = g_ub_hello_msg_len;
     if (ub_transport->_ub_state == UBShmTransport::UB_OFF) {
         local_msg.impl_ver = 0;
@@ -565,6 +618,45 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
         return nullptr;
     }
 
+    UbrDataFormat selected_format = UBR_DATA_FORMAT_NONE;
+    if (HelloNegotiationValid(remote_msg) &&
+        HelloNegotiationValid(local_msg)) {
+        ep->_state = S_FORMAT_WAIT;
+        if (ep->ReadFromFd(data, HelloFormatExtension::WIRE_SIZE) < 0) {
+            const int saved_errno = errno;
+            PLOG(WARNING) << "Fail to read format extension from client:"
+                          << s->description();
+            s->SetFailed(saved_errno,
+                    "Fail to complete ubring handshake from %s: %s",
+                    s->description().c_str(), berror(saved_errno));
+            ep->_state = FAILED;
+            return nullptr;
+        }
+        HelloFormatExtension remote_extension;
+        remote_extension.Deserialize(data);
+        HelloFormatExtension local_extension = {
+            HelloFormatExtension::WIRE_SIZE, UBR_DATA_FORMAT_NONE};
+        if (remote_extension.extension_len == HelloFormatExtension::WIRE_SIZE 
&&
+            remote_extension.format_id == UBR_DATA_FORMAT_LEGACY_64) {
+            local_extension.format_id = UBR_DATA_FORMAT_LEGACY_64;
+            selected_format = UBR_DATA_FORMAT_LEGACY_64;
+        } else {
+            ub_transport->_ub_state = UBShmTransport::UB_OFF;
+        }
+        local_extension.Serialize(data);
+        ep->_state = S_FORMAT_SEND;
+        if (ep->WriteToFd(data, HelloFormatExtension::WIRE_SIZE) < 0) {
+            const int saved_errno = errno;
+            PLOG(WARNING) << "Fail to send format extension to client:"
+                          << s->description();
+            s->SetFailed(saved_errno,
+                    "Fail to complete ubring handshake from %s: %s",
+                    s->description().c_str(), berror(saved_errno));
+            ep->_state = FAILED;
+            return nullptr;
+        }
+    }
+
     ep->_state = S_ACK_WAIT;
     if (ep->ReadFromFd(data, ACK_MSG_LEN) < 0) {
         const int saved_errno = errno;
@@ -575,11 +667,13 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
         return nullptr;
     }
 
-    uint32_t* tmp = (uint32_t*)data;
-    uint32_t flags = butil::NetToHost32(*tmp);
+    uint32_t net_flags;
+    memcpy(&net_flags, data, sizeof(net_flags));
+    const uint32_t flags = butil::NetToHost32(net_flags);
     if (flags & ACK_MSG_UB_OK) {
-        if (ub_transport->_ub_state == UBShmTransport::UB_OFF) {
-            LOG(WARNING) << "Fail to parse Hello Message length from client:"
+        if (ub_transport->_ub_state == UBShmTransport::UB_OFF ||
+            selected_format == UBR_DATA_FORMAT_NONE) {
+            LOG(WARNING) << "Invalid successful ACK from client:"
                          << s->description();
             s->SetFailed(EPROTO, "Fail to complete ub handshake from %s: %s",
                     s->description().c_str(), berror(EPROTO));
@@ -587,6 +681,7 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
             return nullptr;
         } else {
             ub_transport->_ub_state = UBShmTransport::UB_ON;
+            ep->_negotiated_data_format = selected_format;
             ep->_state = ESTABLISHED;
             ep->_ub_ring->UbrUnlinkLocalShm();
             LOG_IF(INFO, FLAGS_ub_trace_verbose) 
diff --git a/src/brpc/ubshm/ub_endpoint.h b/src/brpc/ubshm/ub_endpoint.h
index a29a0927..2d7b1f55 100644
--- a/src/brpc/ubshm/ub_endpoint.h
+++ b/src/brpc/ubshm/ub_endpoint.h
@@ -43,6 +43,23 @@ DECLARE_int32(ub_poller_num);
 DECLARE_bool(ub_edisp_unsched);
 DECLARE_bool(ub_disable_bthread);
 
+enum UbrDataFormat {
+    UBR_DATA_FORMAT_NONE = 0,
+    UBR_DATA_FORMAT_LEGACY_64 = 1,
+};
+
+struct HelloFormatExtension {
+    // The V3 format extension is a fixed-size frame. A different wire size
+    // requires negotiation through a new hello version.
+    static const uint16_t WIRE_SIZE = 4;
+
+    uint16_t extension_len;
+    uint16_t format_id;
+
+    void Serialize(void* data) const;
+    void Deserialize(const void* data);
+};
+
 struct HelloMessage {
     void Serialize(void* data) const;
     void Deserialize(void* data);
@@ -134,12 +151,16 @@ private:
         C_ALLOC_SHM = 0x1,
         C_HELLO_SEND = 0x2,
         C_HELLO_WAIT = 0x3,
-        C_MAP_REMOTE_SHM = 0x4,
-        C_ACK_SEND = 0x5,
+        C_FORMAT_SEND = 0x4,
+        C_FORMAT_WAIT = 0x5,
+        C_MAP_REMOTE_SHM = 0x6,
+        C_ACK_SEND = 0x7,
         S_HELLO_WAIT = 0x11,
         S_ALLOC_SHM = 0x12,
         S_HELLO_SEND = 0x13,
-        S_ACK_WAIT = 0x14,
+        S_FORMAT_WAIT = 0x14,
+        S_FORMAT_SEND = 0x15,
+        S_ACK_WAIT = 0x16,
         ESTABLISHED = 0x100,
         FALLBACK_TCP = 0x200,
         FAILED = 0x300
@@ -184,6 +205,7 @@ private:
     SocketId _socket_id;
 
     State _state;
+    UbrDataFormat _negotiated_data_format{UBR_DATA_FORMAT_NONE};
 
     // ub resource
     ubring::UBRing* _ub_ring{nullptr};
diff --git a/test/brpc_ubring_unittest.cpp b/test/brpc_ubring_unittest.cpp
index d3d0a7ce..295d3f12 100644
--- a/test/brpc_ubring_unittest.cpp
+++ b/test/brpc_ubring_unittest.cpp
@@ -64,6 +64,50 @@ protected:
     std::string buffer;
 };
 
+TEST(HelloFormatExtensionTest, serialize_deserialize_roundtrip) {
+    brpc::ubring::HelloFormatExtension extension = {
+        brpc::ubring::HelloFormatExtension::WIRE_SIZE,
+        brpc::ubring::UBR_DATA_FORMAT_LEGACY_64};
+    char buffer[brpc::ubring::HelloFormatExtension::WIRE_SIZE] = {};
+
+    extension.Serialize(buffer);
+
+    brpc::ubring::HelloFormatExtension decoded = {};
+    decoded.Deserialize(buffer);
+    EXPECT_EQ(extension.extension_len, decoded.extension_len);
+    EXPECT_EQ(extension.format_id, decoded.format_id);
+}
+
+TEST(HelloFormatExtensionTest, serialize_uses_network_byte_order) {
+    brpc::ubring::HelloFormatExtension extension = {0x0102, 0x0304};
+    char buffer[brpc::ubring::HelloFormatExtension::WIRE_SIZE] = {};
+    const unsigned char expected[] = {0x01, 0x02, 0x03, 0x04};
+
+    extension.Serialize(buffer);
+
+    EXPECT_EQ(0, memcmp(expected, buffer, sizeof(expected)));
+}
+
+TEST(HelloFormatExtensionTest, deserialize_none_format) {
+    const unsigned char buffer[] = {0x00, 0x04, 0x00, 0x00};
+    brpc::ubring::HelloFormatExtension extension = {};
+
+    extension.Deserialize(buffer);
+
+    EXPECT_EQ(4, extension.extension_len);
+    EXPECT_EQ(brpc::ubring::UBR_DATA_FORMAT_NONE, extension.format_id);
+}
+
+TEST(HelloFormatExtensionTest, deserialize_unknown_format) {
+    const unsigned char buffer[] = {0x00, 0x04, 0x12, 0x34};
+    brpc::ubring::HelloFormatExtension extension = {};
+
+    extension.Deserialize(buffer);
+
+    EXPECT_EQ(4, extension.extension_len);
+    EXPECT_EQ(0x1234, extension.format_id);
+}
+
 TEST_F(HelloMessageTest, serialize_deserialize_roundtrip) {
     msg.msg_len = 64;
     msg.hello_ver = 2;
@@ -226,6 +270,17 @@ using brpc::ubring::UBShmEndpointTest;
 
 TEST_F(UBShmEndpointTest, construct_initial_state) {
     ASSERT_NE(nullptr, _ep);
+    EXPECT_EQ(brpc::ubring::UBR_DATA_FORMAT_NONE,
+              _ep->_negotiated_data_format);
+}
+
+TEST_F(UBShmEndpointTest, reset_clears_negotiated_data_format) {
+    _ep->_negotiated_data_format = brpc::ubring::UBR_DATA_FORMAT_LEGACY_64;
+
+    _ep->Reset();
+
+    EXPECT_EQ(brpc::ubring::UBR_DATA_FORMAT_NONE,
+              _ep->_negotiated_data_format);
 }
 
 TEST_F(UBShmEndpointTest, allocate_client_resources_real_shm) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to