Remove IKnobCollection#12927
Conversation
Result of foundationdb-pr-clang-ide on Linux RHEL 9
|
Result of foundationdb-pr-macos-m1 on macOS Ventura 13.x
|
Result of foundationdb-pr-clang-arm on Linux CentOS 7
|
Result of foundationdb-pr-macos on macOS Ventura 13.x
|
Result of foundationdb-pr-clang on Linux RHEL 9
|
Result of foundationdb-pr on Linux RHEL 9
|
Result of foundationdb-pr-cluster-tests on Linux RHEL 9
|
gxglass
left a comment
There was a problem hiding this comment.
Looks fine to me. Claude has some possibly overthought suggestions which I think you can pursue at your discretion.
PR #12927 Analysis
Goal
This PR eliminates the IKnobCollection class hierarchy — an abstract interface with three concrete implementations (ClientKnobCollection, ServerKnobCollection, TestKnobCollection) — and replaces it with simple free functions and direct global pointer variables.
Before: CLIENT_KNOBS was a macro that called IKnobCollection::getGlobalKnobCollection().getClientKnobs() — a virtual dispatch through a unique_ptr singleton. The hierarchy existed so that client-only processes wouldn't accidentally access server knobs (they'd get
internal_error()), and to support a TestKnobCollection type.
After: CLIENT_KNOBS is a plain extern pointer (ClientKnobs const* CLIENT_KNOBS) pointing directly to a file-scope global. SERVER_KNOBS gets the same treatment. Free functions like resetClientKnobs(), setupClientKnobs(), setClientKnob() replace the virtual methods.
The hierarchy (5 files deleted: IKnobCollection.h/.cpp, ClientKnobCollection.h/.cpp, ServerKnobCollection.h/.cpp, TestKnobCollection.h/.cpp) is replaced by:
- New code in ClientKnobs.cpp for client-level knobs
- ServerKnobs.cpp moved from fdbclient/ to fdbserver/core/ (where it logically belongs) with parallel server-level free functions
Cleanliness Assessment
Positive:
- Removes unnecessary abstraction — the virtual dispatch was never needed at runtime; the type was always known statically at each call site
- TestKnobCollection was dead code (Type::TEST was never used outside IKnobCollection.cpp itself)
- Moving ServerKnobs.cpp from fdbclient/ to fdbserver/core/ is a good layering improvement
- Consistent pattern: each level (client, server) has a symmetric set of free functions
- The macro-to-extern-pointer change eliminates a virtual dispatch on every CLIENT_KNOBS-> / SERVER_KNOBS-> access, which happens frequently in hot paths
Minor style issues:
- #include is added to ClientKnobs.cpp and ServerKnobs.cpp — presumably for std::cerr in setupClientKnobs() / setupServerKnobs(). This was already used in the old code (via IKnobCollection::setupKnobs), so it's fine, but is a heavy include. The old code had
it in IKnobCollection.cpp so this is just moved, not new.
Potential Issues
- Duplicate global FlowKnobs/ClientKnobs instances — ODR / state divergence risk (Medium)
Both ClientKnobs.cpp and ServerKnobs.cpp define their own file-scope (namespace {}) global instances:
- ClientKnobs.cpp: FlowKnobs globalFlowKnobs; ClientKnobs globalClientKnobs; ClientKnobs bootstrapGlobalClientKnobs;
- ServerKnobs.cpp: FlowKnobs globalFlowKnobs; ClientKnobs globalClientKnobs; ServerKnobs globalServerKnobs; ClientKnobs bootstrapGlobalClientKnobs; ...
Since these are in anonymous namespaces, they are distinct objects. In a server process, resetServerKnobs() sets FLOW_KNOBS and CLIENT_KNOBS to point to the instances in ServerKnobs.cpp's anonymous namespace, while the instances in ClientKnobs.cpp go unused. This works but is
fragile — if someone calls resetClientKnobs() after resetServerKnobs() (or vice versa), the global pointers would silently switch to pointing at a different set of objects. The old code had a single setGlobalKnobCollection() with an ASSERT(FLOW_KNOBS == &bootstrapGlobalFlowKnobs)
that prevented double-initialization. That assertion is gone.
- Lost bootstrap-to-global transition guard (Medium)
The old setGlobalKnobCollection() had ASSERT(FLOW_KNOBS == &bootstrapGlobalFlowKnobs) ensuring the global knob collection is only initialized once. Neither resetClientKnobs() nor resetServerKnobs() has any equivalent guard. Calling reset multiple times would silently
re-initialize all knobs, potentially discarding runtime modifications.
- CLIENT_KNOBS initial value points to bootstrap, not null (Low - by design)
ClientKnobs.cpp initializes: ClientKnobs const* CLIENT_KNOBS = &bootstrapGlobalClientKnobs;
ServerKnobs.cpp initializes: ServerKnobs const* SERVER_KNOBS = &bootstrapGlobalServerKnobs;
This mirrors the existing FLOW_KNOBS bootstrap pattern. It's correct — code that runs before resetClientKnobs()/resetServerKnobs() gets sane defaults.
- TestKnobs entirely removed — test knob infrastructure is gone (Low)
TestKnobCollection and TestKnobs (with TEST_LONG, TEST_INT, etc.) are deleted. There's no replacement. This is likely fine because Type::TEST was never actually used in any call to setGlobalKnobCollection() — it was dead code. But if any downstream/out-of-tree code depends on
TestKnobs or clearTestKnobs(), it will break.
- const_cast pattern for mutability (Low - pre-existing)
The mutableFlowKnobs() / mutableClientKnobs() / mutableServerKnobs() helpers use const_cast to strip const from the global pointers. This is technically safe because the underlying objects are non-const, but it's a code smell. The old code also mutated through
getMutableGlobalKnobCollection(), so this isn't a regression.
- No CMakeLists.txt changes shown for file moves/deletes
The diff moves ServerKnobs.cpp from fdbclient/ to fdbserver/core/ and deletes several .cpp files. The build system uses fdb_find_sources() which auto-discovers sources, so this likely works automatically. But if fdb_find_sources doesn't scan subdirectories or if there are
explicit source lists, the build could break. Worth verifying.
Summary
┌───────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────┐
│ Issue │ Severity │
├───────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────┤
│ Duplicate anonymous-namespace globals (FlowKnobs, ClientKnobs) in two TUs │ Medium — works but fragile │
├───────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────┤
│ Lost bootstrap assertion guard against double-init │ Medium — silent misconfiguration possible │
├───────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────┤
│ TestKnobs entirely removed (dead code) │ Low │
├───────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────┤
│ const_cast for mutation │ Low (pre-existing pattern) │
├───────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────┤
│ CMake auto-discovery of moved/deleted files │ Low (needs build verification) │
└───────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────┘
Overall this is a solid simplification. The main architectural concern is the duplicated global state across two translation units and the lack of a guard against calling both resetClientKnobs() and resetServerKnobs() or calling either one twice. A simple ASSERT or a flag could
prevent accidental misuse.
Result of foundationdb-pr-clang-ide on Linux RHEL 9
|
Result of foundationdb-pr-macos-m1 on macOS Ventura 13.x
|
Result of foundationdb-pr-clang-arm on Linux CentOS 7
|
Result of foundationdb-pr-macos on macOS Ventura 13.x
|
Result of foundationdb-pr-clang on Linux RHEL 9
|
Result of foundationdb-pr on Linux RHEL 9
|
Result of foundationdb-pr-clang-ide on Linux RHEL 9
|
Result of foundationdb-pr-macos-m1 on macOS Ventura 13.x
|
Result of foundationdb-pr-cluster-tests on Linux RHEL 9
|
Result of foundationdb-pr-clang-arm on Linux CentOS 7
|
Result of foundationdb-pr-clang on Linux RHEL 9
|
Result of foundationdb-pr-macos on macOS Ventura 13.x
|
Result of foundationdb-pr on Linux RHEL 9
|
Result of foundationdb-pr-cluster-tests on Linux RHEL 9
|
This PR is a follow-up to #12683. The
*KnobCollectionhierarchy was necessary for the dynamic knobs implementation, but it's now unnecessarily complex and can be removed. The main benefits of this PR are:makobenchmark I ran prior to this change)fdbserver/core, rather than being part offdbclientCode-Reviewer Section
The general pull request guidelines can be found here.
Please check each of the following things and check all boxes before accepting a PR.
For Release-Branches
If this PR is made against a release-branch, please also check the following:
release-branchormainif this is the youngest branch)