Skip to content

Commit 23d09f7

Browse files
excelle08meta-codesync[bot]
authored andcommitted
Add mock_services standalone Thrift server (20 methods, real fbthrift)
Summary: Phase 5 of the FeedSim v2 refactor needs LeafNodeRank to issue real outbound RPCs against a separate Thrift server so Strobelight categorizes the resulting CPU samples into the same rpc-stack/serialization/transport buckets as production multifeed_aggregator. This diff adds the mock_services binary that stands in for the 20 outbound RPC types observed in the production profile. The server is built on real apache::thrift::ThriftServer, not the FeedSimServer hand-rolled AsyncServerSocket loop, so loopback dispatch goes through Cpp2Worker, RocketServerConnection, RequestRpcMetadata, CompactProtocolWriter, etc. exactly as it would in prod. The 20 thrift methods all share the same wire signature `binary <method>(1: binary request, 2: i32 latency_us)` (the design from phase5_researcher_notes section 1, option (c)) and dispatch to a single shared handler body. Distinct method names exist purely so Strobelight per-method attribution lines up with prod. Wire contract: caller writes a uint32 big-endian response_size in the first 4 bytes of `request` and then opaque padding sized to the request percentile. The server sleeps/spins for `latency_us` and replies with `response_size` bytes copied from the Silesia corpus. Short-tail latencies (<200us) burn the IO thread to keep rpc-stack samples on-CPU; longer latencies hop to the global timekeeper. Files added: MockService.thrift (IDL, 20 methods), MockServiceHandler.{h,cc} (single shared runSimulatedRpc body, 20 trivial wrappers behind a macro), MockServiceMain.cc (folly::Init + ThriftServer setup), BUCK (thrift_library + cpp_binary, with a -I flag pulling SilesiaLoader.h from the parent ranking/ dir since that dir has no BUCK file), CMakeLists.txt (open-source build path; mirrors the parent ranking/ pattern). Parent ranking/CMakeLists.txt picks up the new dir via add_subdirectory. The binary ships in the cea.chips.benchpress fbpkg automatically via the existing buck_filegroup glob over packages/feedsim/**. Programmer B (sibling diff in this stack) wires LeafNodeRank's MockServiceAsyncClient and the issueOutboundFanout switch; Programmer C migrates compression to ManagedCompression. No file conflicts with this diff. Differential Revision: D103766817
1 parent ed98993 commit 23d09f7

8 files changed

Lines changed: 635 additions & 2 deletions

File tree

packages/feedsim/third_party/src/CMake/build-fbthrift.cmake

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ ExternalProject_Add(fbthrift
5252
<INSTALL_DIR>/lib/libconcurrency.a
5353
<INSTALL_DIR>/lib/librpcmetadata.a
5454
<INSTALL_DIR>/lib/libthriftmetadata.a
55+
<INSTALL_DIR>/lib/libthriftannotation.a
5556
<INSTALL_DIR>/lib/libthrifttype.a
5657
<INSTALL_DIR>/lib/libthrifttyperep.a
5758
<INSTALL_DIR>/lib/libthriftanyrep.a
58-
<INSTALL_DIR>/lib/libthriftannotation.a
59-
<INSTALL_DIR>/lib/libcommon.a
6059
<INSTALL_DIR>/lib/libruntime.a
60+
<INSTALL_DIR>/lib/libcommon.a
6161
<INSTALL_DIR>/lib/libserverdbginfo.a
62+
<INSTALL_DIR>/lib/libcompiler.a
63+
<INSTALL_DIR>/lib/libwhisker.a
6264
BUILD_COMMAND
6365
cmake --build . --parallel ${BUILD_PARALLEL_JOBS}
6466
)
@@ -73,9 +75,40 @@ ExternalProject_Get_Property(fbthrift INSTALL_DIR)
7375
set(THRIFT1 ${INSTALL_DIR}/bin/thrift1)
7476
set(THRIFTCPP2 ${INSTALL_DIR}/lib/libthriftcpp2.a)
7577

78+
# Use --start-group/--end-group to make link order between fbthrift sub-libs
79+
# irrelevant. The fbthrift static libs have many cyclic dependencies between
80+
# themselves (e.g. libthriftcpp2 <-> librpcmetadata <-> libserverdbginfo) that
81+
# would otherwise require careful manual ordering. With the group, the linker
82+
# will iterate until all cross-references resolve.
83+
#
84+
# Sub-lib map (why each is needed):
85+
# librpcmetadata: CompressionConfig / CodecConfig / ClientMetadata /
86+
# LoggingContext / QuotaReportConfig
87+
# libruntime: apache::thrift::runtime::wasInitialized() /
88+
# getGlobalLegacyClientEventHandlers()
89+
# libcommon: apache::thrift::validate_universal_name()
90+
# libserverdbginfo: apache::thrift::serverdbginfo::ResourcePoolsDbgInfo
91+
# (referenced by ThriftServer's resource pool reporting)
92+
# libcompiler / libwhisker: pulled in transitively by compiler_ast /
93+
# compiler_lib / compiler_base when linking fbthrift's reflection /
94+
# metadata bits.
95+
#
96+
# Note: libquic_thriftcpp2.a and libcompiler_generators.a are produced by the
97+
# fbthrift build but NOT installed by `cmake --install`, so they are not
98+
# referenced here.
7699
set(FBTHRIFT_LIBRARIES
100+
-Wl,--start-group
77101
${INSTALL_DIR}/lib/libthriftcpp2.a
78102
${INSTALL_DIR}/lib/libthriftprotocol.a
103+
${INSTALL_DIR}/lib/librpcmetadata.a
104+
${INSTALL_DIR}/lib/libthriftmetadata.a
105+
${INSTALL_DIR}/lib/libthriftannotation.a
106+
${INSTALL_DIR}/lib/libthrifttype.a
107+
${INSTALL_DIR}/lib/libthrifttyperep.a
108+
${INSTALL_DIR}/lib/libthriftanyrep.a
109+
${INSTALL_DIR}/lib/libruntime.a
110+
${INSTALL_DIR}/lib/libcommon.a
111+
${INSTALL_DIR}/lib/libserverdbginfo.a
79112
${INSTALL_DIR}/lib/libthrift-core.a
80113
${INSTALL_DIR}/lib/libtransport.a
81114
${INSTALL_DIR}/lib/libasync.a
@@ -90,6 +123,9 @@ set(FBTHRIFT_LIBRARIES
90123
${INSTALL_DIR}/lib/libcommon.a
91124
${INSTALL_DIR}/lib/libruntime.a
92125
${INSTALL_DIR}/lib/libserverdbginfo.a
126+
${INSTALL_DIR}/lib/libcompiler.a
127+
${INSTALL_DIR}/lib/libwhisker.a
128+
-Wl,--end-group
93129
)
94130

95131
set(FBTHRIFT_INCLUDE_DIR

packages/feedsim/third_party/src/CMake/build-libaegis.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ else()
6262
INSTALL_COMMAND
6363
${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/zig-out/include <INSTALL_DIR>/include
6464
COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/zig-out/lib <INSTALL_DIR>/lib
65+
# Required so ninja knows libaegis.a is produced by this ExternalProject;
66+
# without this, downstream targets (mock_services) that depend on the .a
67+
# path fail with "missing and no known rule to make it".
68+
BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libaegis.a
6569
)
6670

6771
ExternalProject_Add_StepDependencies(libaegis build zig)

packages/feedsim/third_party/src/workloads/ranking/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ target_link_libraries(FeedSimFramework
258258
Threads::Threads
259259
)
260260

261+
# Build mock_services standalone Thrift server (Phase 5)
262+
add_subdirectory(mock_services)
263+
261264
# Build LeafNodeRank binary
262265

263266
add_executable(LeafNodeRank
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
# Generate the cpp2 thrift bindings for MockService. Mirrors the pattern in
7+
# ../if/CMakeLists.txt.
8+
thrift_library(
9+
"MockService"
10+
"MockService" # services
11+
"cpp2" # Language generator
12+
"" # Options
13+
"${CMAKE_CURRENT_LIST_DIR}" # Directory where the thrift file lives
14+
"${CMAKE_CURRENT_LIST_DIR}" # Output directory
15+
"mock_services"
16+
THRIFT_INCLUDE_DIRECTORIES ${FBTHRIFT_INCLUDE_DIR}
17+
)
18+
add_dependencies(MockService-cpp2-target fbthrift)
19+
set_target_properties(
20+
MockService-cpp2-obj
21+
PROPERTIES POSITION_INDEPENDENT_CODE True
22+
)
23+
target_include_directories(MockService-cpp2-obj
24+
PUBLIC
25+
# The thrift compiler emits #include "mock_services/gen-cpp2/X.h"
26+
# in the generated .cpp files (because include_prefix=mock_services).
27+
# The headers themselves live in this directory (mock_services/),
28+
# so the include search root must be the parent directory.
29+
${CMAKE_CURRENT_LIST_DIR}/..
30+
${FBTHRIFT_INCLUDE_DIR}
31+
${FOLLY_INCLUDE_DIR}
32+
)
33+
target_link_libraries(MockService-cpp2
34+
Threads::Threads
35+
${FOLLY_LIBRARIES}
36+
${FBTHRIFT_LIBRARIES}
37+
${FMT_LIBRARIES}
38+
)
39+
40+
add_executable(mock_services
41+
MockServiceMain.cc
42+
MockServiceHandler.cc
43+
)
44+
add_dependencies(mock_services fbthrift folly libaegis MockService-cpp2-target)
45+
46+
# MockServiceHandler.h includes the generated MockService.h via the
47+
# fbcode-relative path
48+
# `cea/chips/benchpress/packages/feedsim/third_party/src/workloads/ranking/mock_services/gen-cpp2/MockService.h`
49+
# (the form Buck requires; see ../mock_services/BUCK). For the CMake build
50+
# we materialize that path as a symlink into the build tree and add the
51+
# symlink root to the include search path so the same header works under
52+
# both build systems.
53+
set(MOCK_SERVICES_FBCODE_INCLUDE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/fbcode_include_root")
54+
set(MOCK_SERVICES_FBCODE_LINK_PARENT "${MOCK_SERVICES_FBCODE_INCLUDE_ROOT}/cea/chips/benchpress/packages/feedsim/third_party/src/workloads/ranking")
55+
file(MAKE_DIRECTORY "${MOCK_SERVICES_FBCODE_LINK_PARENT}")
56+
if(NOT EXISTS "${MOCK_SERVICES_FBCODE_LINK_PARENT}/mock_services")
57+
file(CREATE_LINK
58+
"${CMAKE_CURRENT_LIST_DIR}"
59+
"${MOCK_SERVICES_FBCODE_LINK_PARENT}/mock_services"
60+
SYMBOLIC)
61+
endif()
62+
63+
target_include_directories(mock_services
64+
PUBLIC
65+
${CMAKE_CURRENT_BINARY_DIR}
66+
${MOCK_SERVICES_FBCODE_INCLUDE_ROOT}
67+
${CMAKE_CURRENT_SOURCE_DIR}
68+
${CMAKE_CURRENT_SOURCE_DIR}/..
69+
${FOLLY_INCLUDE_DIR}
70+
${FBTHRIFT_INCLUDE_DIR}
71+
)
72+
# libfizz.a (pulled in by mock_services as a real ThriftServer) references
73+
# symbols from libsodium (randombytes_buf, sodium_init, crypto_*) and from
74+
# libaegis (aegis128l_*) -- both are crypto backends fizz can be configured
75+
# with. The fizz CMake build uses libsodium via add_subdirectory(...) and
76+
# libaegis as an ExternalProject; the resulting libfizz.a leaves those
77+
# undefined and expects the consumer to provide them.
78+
#
79+
# libsodium: comes from the libsodium-devel RPM installed by install_feedsim.sh
80+
# libaegis: built by the libaegis ExternalProject (build-libaegis.cmake);
81+
# LIBAEGIS_LIBRARIES is set there.
82+
find_library(LIBSODIUM_LIBRARY NAMES sodium PATHS /usr/lib64 /usr/local/lib)
83+
84+
target_link_libraries(mock_services
85+
PRIVATE
86+
MockService-cpp2
87+
${FOLLY_LIBRARIES}
88+
${FBTHRIFT_LIBRARIES}
89+
${FMT_LIBRARIES}
90+
${DOUBLE_CONVERSION_LIBRARY}
91+
${IBERTY_LIBRARIES}
92+
glog::glog
93+
${CMAKE_DL_LIBS}
94+
# libthriftcpp2's ChecksumGenerator references XXH3_*; link the
95+
# system xxhash (rpm xxhash-devel installed by install_feedsim.sh).
96+
xxhash
97+
PUBLIC
98+
Threads::Threads
99+
ZLIB::ZLIB
100+
${BZIP2_LIBRARIES}
101+
${LZ4_LIBRARY}
102+
${ZSTD_LIBRARY}
103+
${SNAPPY_LIBRARY}
104+
${LIBLZMA_LIBRARIES}
105+
${LIBEVENT_LIB}
106+
${JEMALLOC_LIB}
107+
${RSOCKET_LIBRARIES}
108+
# Wrap libfizz / libwangle / libsodium / libaegis in
109+
# --start-group/--end-group so the linker can resolve cyclic deps
110+
# between them.
111+
#
112+
# Why each lib:
113+
# * libfizz / libwangle -- libwangle's FizzAcceptorHandshakeHelper
114+
# references many fizz/server symbols (KTLSCryptoParams::*,
115+
# ServerStateMachine::*, AsyncKTLSSocket vtable,
116+
# fizz_probe_secret_available). Single-pass resolution misses these
117+
# when libwangle is after libfizz.
118+
# * libsodium / libaegis -- libfizz's crypto backends. Without these
119+
# the link fails with hundreds of "undefined reference to
120+
# randombytes_buf / aegis128l_* / sodium_init / etc" errors when
121+
# the binary pulls in fizz/server/ServerStateMachine. They MUST
122+
# be inside the group with libfizz so the linker can pick up
123+
# their symbols when scanning libfizz a second time.
124+
# libsodium comes from the libsodium-devel RPM installed by
125+
# install_feedsim.sh; libaegis is built by the libaegis
126+
# ExternalProject (build-libaegis.cmake).
127+
-Wl,--start-group
128+
${FIZZ_LIBRARIES}
129+
${WANGLE_LIBRARIES}
130+
${LIBSODIUM_LIBRARIES}
131+
${LIBSODIUM_LIBRARY}
132+
${LIBAEGIS_LIBRARIES}
133+
-Wl,--end-group
134+
# folly fibers (pulled in transitively by libthriftcpp2 via the
135+
# new librpcmetadata/libcommon edges) needs boost::context's
136+
# make_fcontext / jump_fcontext. Without these, the executable
137+
# link fails with "undefined reference to boost::context::detail::*".
138+
Boost::boost
139+
Boost::context
140+
Boost::filesystem
141+
Boost::program_options
142+
Boost::regex
143+
Boost::system
144+
${OPENSSL_LIBRARIES}
145+
# Re-list libfolly at the end of the link line so the linker can
146+
# resolve folly symbols (SocketFds, AsyncServerSocket, XlogLevelInfo,
147+
# FunctionScheduler, LogStreamProcessor) referenced from libthriftcpp2
148+
# and friends. Static archive resolution is single-pass; without the
149+
# second mention, those references fall through.
150+
${FOLLY_LIBRARIES}
151+
# Re-list compression libs AFTER the second libfolly mention so
152+
# Ubuntu's default `-Wl,--as-needed` doesn't drop them. Folly's
153+
# Compression.cpp references lzma_/bz2_/lz4_/zstd_/snappy_ symbols;
154+
# on Ubuntu the first mention above appears BEFORE folly, so ld
155+
# drops the DSOs as "not needed" and then fails when it processes
156+
# libfolly's second pass with errors like:
157+
# undefined reference to symbol 'lzma_stream_buffer_bound@@XZ_5.0'
158+
# liblzma.so: DSO missing from command line
159+
# CentOS ld defaults to --no-as-needed so it doesn't trip there.
160+
${LIBLZMA_LIBRARIES}
161+
${BZIP2_LIBRARIES}
162+
${LZ4_LIBRARY}
163+
${ZSTD_LIBRARY}
164+
${SNAPPY_LIBRARY}
165+
ZLIB::ZLIB
166+
)
167+
target_compile_options(mock_services PUBLIC
168+
-fno-omit-frame-pointer
169+
# -ffunction-sections / -fdata-sections paired with -Wl,--gc-sections
170+
# below lets the linker strip unreferenced sections. libfolly references
171+
# io_uring symbols (io_uring_register, io_uring_register_ifq,
172+
# io_uring_register_eventfd) introduced in newer liburing versions; the
173+
# CentOS 9 system liburing is older and does not provide them. Since
174+
# mock_services never invokes the io_uring path, --gc-sections drops
175+
# those references at link time and avoids needing a newer liburing on
176+
# the build host. Same pattern as LeafNodeRank's link options above.
177+
-ffunction-sections
178+
-fdata-sections
179+
)
180+
target_link_options(mock_services PRIVATE -Wl,--gc-sections)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Mock RPC fanout target for FeedSim's LeafNodeRank. Twenty methods, one per
16+
// outbound RPC type observed in the production multifeed_aggregator profile
17+
// (see ~/feedsim_v2/profiles/rpc_dist.json).
18+
//
19+
// All twenty share the same wire signature; the per-method names exist solely
20+
// so Strobelight can attribute samples to each (the generated symbols are
21+
// distinct: MockServiceSvIf::async_eb_<method>, AsyncClient::send_<method>,
22+
// etc.). The handler body is a single shared function; payload sizes and
23+
// latencies are sampled by the caller from the same percentile distribution.
24+
//
25+
// Wire contract: the first 4 bytes of `request` are a big-endian uint32_t
26+
// response_size; the rest is opaque padding sized to the request percentile.
27+
// The server sleeps/spins for `latency_us` and replies with `response_size`
28+
// bytes copied from the Silesia corpus.
29+
//
30+
// Memcache note: mcGet, mcLeaseGet, mcSet, mcLeaseSet are wrapped as Thrift
31+
// methods even though prod uses the memcache binary protocol on the wire.
32+
// Reproducing the memcache framing buys no signal for RPC-stack CPU
33+
// attribution — which is what we are calibrating — and would multiply
34+
// scaffolding for no measurable difference in the categories of interest.
35+
36+
namespace cpp2 mock_services
37+
38+
service MockService {
39+
binary mcGet(1: binary request, 2: i32 latency_us);
40+
binary mcLeaseGet(1: binary request, 2: i32 latency_us);
41+
binary mcSet(1: binary request, 2: i32 latency_us);
42+
binary fetchTopKEntitiesRequest(1: binary request, 2: i32 latency_us);
43+
binary getActionStreamsRequestCompressed2(
44+
1: binary request,
45+
2: i32 latency_us,
46+
);
47+
binary getObjectsFromQueries(1: binary request, 2: i32 latency_us);
48+
binary getSerializedObjects(1: binary request, 2: i32 latency_us);
49+
binary getStatus(1: binary request, 2: i32 latency_us);
50+
binary runFullyRemotePrediction(1: binary request, 2: i32 latency_us);
51+
binary getActionStreamsCompressed2(1: binary request, 2: i32 latency_us);
52+
binary runModelMethod(1: binary request, 2: i32 latency_us);
53+
binary edsMultiGet(1: binary request, 2: i32 latency_us);
54+
binary fetchCandidateScoreRequest(1: binary request, 2: i32 latency_us);
55+
binary mcLeaseSet(1: binary request, 2: i32 latency_us);
56+
binary prefixScan(1: binary request, 2: i32 latency_us);
57+
binary fetchEntityFeatures(1: binary request, 2: i32 latency_us);
58+
binary getUserConsents(1: binary request, 2: i32 latency_us);
59+
binary fciGet(1: binary request, 2: i32 latency_us);
60+
binary multiget(1: binary request, 2: i32 latency_us);
61+
binary FbkeyPointGetRequest(1: binary request, 2: i32 latency_us);
62+
}

0 commit comments

Comments
 (0)