diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index d4598ad7aba..8eb85cdf7a7 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2813,6 +2813,7 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
/src/third_party/**/cares @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/third_party/**/croaring @10gen/query-execution @svc-auto-approve-bot
/src/third_party/**/fmt @10gen/server-programmability @svc-auto-approve-bot
+/src/third_party/**/folly @10gen/server-workload-scheduling @svc-auto-approve-bot
/src/third_party/**/gperftools @10gen/server-workload-scheduling @svc-auto-approve-bot
/src/third_party/**/grpc @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/third_party/**/icu4c* @10gen/query-execution @svc-auto-approve-bot
diff --git a/sbom.json b/sbom.json
index 9c7afa81131..38472733bd3 100644
--- a/sbom.json
+++ b/sbom.json
@@ -542,6 +542,49 @@
},
"scope": "required"
},
+ {
+ "supplier": {
+ "name": "Organization: github"
+ },
+ "name": "folly",
+ "version": "v2025.04.21.00",
+ "licenses": [
+ {
+ "license": {
+ "id": "Apache-2.0"
+ }
+ }
+ ],
+ "purl": "pkg:github/folly/folly@v2025.04.21.00",
+ "properties": [
+ {
+ "name": "internal:team_responsible",
+ "value": "Workload Scheduling"
+ },
+ {
+ "name": "emits_persisted_data",
+ "value": "false"
+ },
+ {
+ "name": "info_link",
+ "value": "https://github.com/facebook/folly"
+ },
+ {
+ "name": "import_script_path",
+ "value": "src/third_party/folly/scripts/import.sh"
+ }
+ ],
+ "type": "library",
+ "bom-ref": "b2fa8868-e134-4fcb-9641-6344ecfef39e",
+ "evidence": {
+ "occurrences": [
+ {
+ "location": "src/third_party/folly"
+ }
+ ]
+ },
+ "scope": "required"
+ },
{
"supplier": {
"name": "Organization: github"
diff --git a/src/third_party/OWNERS.yml b/src/third_party/OWNERS.yml
index 2512bc875a7..b4c0a3b7582 100644
--- a/src/third_party/OWNERS.yml
+++ b/src/third_party/OWNERS.yml
@@ -24,6 +24,9 @@ filters:
- "fmt":
approvers:
- 10gen/server-programmability
+ - "folly":
+ approvers:
+ - 10gen/server-workload-scheduling
- "gperftools":
approvers:
- 10gen/server-workload-scheduling
diff --git a/src/third_party/folly/BUILD.bazel b/src/third_party/folly/BUILD.bazel
new file mode 100644
index 00000000000..5de099915b9
--- /dev/null
+++ b/src/third_party/folly/BUILD.bazel
@@ -0,0 +1,26 @@
+load("//bazel:mongo_src_rules.bzl", "mongo_cc_library", "mongo_cc_unit_test")
+
+package(default_visibility = ["//visibility:public"])
+
+mongo_cc_library(
+ name = "folly_token_bucket",
+ hdrs = [
+ "dist/folly/TokenBucket.h",
+ ],
+ includes = ["./dist"],
+)
+
+mongo_cc_unit_test(
+ name = "token_bucket_test",
+ srcs = [
+ "test/token_bucket_test.cpp",
+ ],
+ tags = [
+ "mongo_unittest_fifth_group",
+ "server-workload-scheduling",
+ ],
+ deps = [
+ ":folly_token_bucket",
+ "//src/mongo:base",
+ ],
+)
diff --git a/src/third_party/folly/dist/LICENSE b/src/third_party/folly/dist/LICENSE
new file mode 100644
index 00000000000..48bdb128236
--- /dev/null
+++ b/src/third_party/folly/dist/LICENSE
@@ -0,0 +1,200 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
+Files in folly/external/farmhash licensed as follows
+
+ Copyright (c) 2014 Google, Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
diff --git a/src/third_party/folly/dist/README.md b/src/third_party/folly/dist/README.md
new file mode 100644
index 00000000000..680482060de
--- /dev/null
+++ b/src/third_party/folly/dist/README.md
@@ -0,0 +1,259 @@
+Folly: Facebook Open-source Library
+===================================
+
+
+
+
+
+# What is `folly`?
+
+
+
+Folly (acronymed loosely after Facebook Open Source Library) is a
+library of C++17 components designed with practicality and efficiency
+in mind. **Folly contains a variety of core library components used extensively
+at Facebook**. In particular, it's often a dependency of Facebook's other
+open source C++ efforts and place where those projects can share code.
+
+It complements (as opposed to competing against) offerings
+such as Boost and of course `std`. In fact, we embark on defining our
+own component only when something we need is either not available, or
+does not meet the needed performance profile. We endeavor to remove
+things from folly if or when `std` or Boost obsoletes them.
+
+Performance concerns permeate much of Folly, sometimes leading to
+designs that are more idiosyncratic than they would otherwise be (see
+e.g. `PackedSyncPtr.h`, `SmallLocks.h`). Good performance at large
+scale is a unifying theme in all of Folly.
+
+## Check it out in the intro video
+[](https://www.youtube.com/watch?v=Wr_IfOICYSs)
+
+# Logical Design
+
+Folly is a collection of relatively independent components, some as
+simple as a few symbols. There is no restriction on internal
+dependencies, meaning that a given folly module may use any other
+folly components.
+
+All symbols are defined in the top-level namespace `folly`, except of
+course macros. Macro names are ALL_UPPERCASE and should be prefixed
+with `FOLLY_`. Namespace `folly` defines other internal namespaces
+such as `internal` or `detail`. User code should not depend on symbols
+in those namespaces.
+
+# Physical Design
+
+At the top level Folly uses the classic "stuttering" scheme
+`folly/folly` used by Boost and others. The first directory serves as
+an installation root of the library (with possible versioning a la
+`folly-1.0/`), and the second is to distinguish the library when
+including files, e.g. `#include `.
+
+The directory structure is flat (mimicking the namespace structure),
+i.e. we don't have an elaborate directory hierarchy (it is possible
+this will change in future versions). The subdirectory `experimental`
+contains files that are used inside folly and possibly at Facebook but
+not considered stable enough for client use. Your code should not use
+files in `folly/experimental` lest it may break when you update Folly.
+
+The `folly/folly/test` subdirectory includes the unittests for all
+components, usually named `ComponentXyzTest.cpp` for each
+`ComponentXyz.*`. The `folly/folly/docs` directory contains
+documentation.
+
+# What's in it?
+
+Because of folly's fairly flat structure, the best way to see what's in it
+is to look at the headers in [top level `folly/` directory](https://github.com/facebook/folly/tree/main/folly). You can also
+check the [`docs` folder](folly/docs) for documentation, starting with the
+[overview](folly/docs/Overview.md).
+
+Folly is published on GitHub at https://github.com/facebook/folly.
+
+# Build Notes
+
+Because folly does not provide any ABI compatibility guarantees from commit to
+commit, we generally recommend building folly as a static library.
+
+folly supports gcc (5.1+), clang, or MSVC. It should run on Linux (x86-32,
+x86-64, and ARM), iOS, macOS, and Windows (x86-64). The CMake build is only
+tested on some of these platforms; at a minimum, we aim to support macOS and
+Linux (on the latest Ubuntu LTS release or newer.)
+
+## `getdeps.py`
+
+This script is used by many of Meta's OSS tools. It will download and build all of the necessary dependencies first, and will then invoke cmake etc to build folly. This will help ensure that you build with relevant versions of all of the dependent libraries, taking into account what versions are installed locally on your system.
+
+It's written in python so you'll need python3.6 or later on your PATH. It works on Linux, macOS and Windows.
+
+The settings for folly's cmake build are held in its getdeps manifest `build/fbcode_builder/manifests/folly`, which you can edit locally if desired.
+
+### Dependencies
+
+If on Linux or MacOS (with homebrew installed) you can install system dependencies to save building them:
+
+ # Clone the repo
+ git clone https://github.com/facebook/folly
+ # Install dependencies
+ cd folly
+ sudo ./build/fbcode_builder/getdeps.py install-system-deps --recursive
+
+If you'd like to see the packages before installing them:
+
+ ./build/fbcode_builder/getdeps.py install-system-deps --dry-run --recursive
+
+On other platforms or if on Linux and without system dependencies `getdeps.py` will mostly download and build them for you during the build step.
+
+Some of the dependencies `getdeps.py` uses and installs are:
+
+ * a version of boost compiled with C++14 support.
+ * googletest is required to build and run folly's tests.
+
+### Build
+
+This script will download and build all of the necessary dependencies first,
+and will then invoke cmake etc to build folly. This will help ensure that you build with relevant versions of all of the dependent libraries, taking into account what versions are installed locally on your system.
+
+`getdeps.py` currently requires python 3.6+ to be on your path.
+
+`getdeps.py` will invoke cmake etc.
+
+ # Clone the repo
+ git clone https://github.com/facebook/folly
+ cd folly
+ # Build, using system dependencies if available
+ python3 ./build/fbcode_builder/getdeps.py --allow-system-packages build
+
+It puts output in its scratch area:
+
+ * `installed/folly/lib/libfolly.a`: Library
+
+You can also specify a `--scratch-path` argument to control
+the location of the scratch directory used for the build. You can find the default scratch install location from logs or with `python3 ./build/fbcode_builder/getdeps.py show-inst-dir`.
+
+There are also
+`--install-dir` and `--install-prefix` arguments to provide some more
+fine-grained control of the installation directories. However, given that
+folly provides no compatibility guarantees between commits we generally
+recommend building and installing the libraries to a temporary location, and
+then pointing your project's build at this temporary location, rather than
+installing folly in the traditional system installation directories. e.g., if you are building with CMake you can use the `CMAKE_PREFIX_PATH` variable to allow CMake to find folly in this temporary installation directory when
+building your project.
+
+If you want to invoke `cmake` again to iterate, there is a helpful `run_cmake.py` script output in the scratch build directory. You can find the scratch build directory from logs or with `python3 ./build/fbcode_builder/getdeps.py show-build-dir`.
+
+### Run tests
+
+By default `getdeps.py` will build the tests for folly. To run them:
+
+ cd folly
+ python3 ./build/fbcode_builder/getdeps.py --allow-system-packages test
+
+### `build.sh`/`build.bat` wrapper
+
+`build.sh` can be used on Linux and MacOS, on Windows use
+the `build.bat` script instead. Its a wrapper around `getdeps.py`.
+
+## Build with cmake directly
+
+If you don't want to let getdeps invoke cmake for you then by default, building the tests is disabled as part of the CMake `all` target.
+To build the tests, specify `-DBUILD_TESTS=ON` to CMake at configure time.
+
+NB if you want to invoke `cmake` again to iterate on a `getdeps.py` build, there is a helpful `run_cmake.py` script output in the scratch-path build directory. You can find the scratch build directory from logs or with `python3 ./build/fbcode_builder/getdeps.py show-build-dir`.
+
+Running tests with ctests also works if you cd to the build dir, e.g.
+`(cd $(python3 ./build/fbcode_builder/getdeps.py show-build-dir) && ctest)`
+
+### Finding dependencies in non-default locations
+
+If you have boost, gtest, or other dependencies installed in a non-default
+location, you can use the `CMAKE_INCLUDE_PATH` and `CMAKE_LIBRARY_PATH`
+variables to make CMAKE look also look for header files and libraries in
+non-standard locations. For example, to also search the directories
+`/alt/include/path1` and `/alt/include/path2` for header files and the
+directories `/alt/lib/path1` and `/alt/lib/path2` for libraries, you can invoke
+`cmake` as follows:
+
+```
+cmake \
+ -DCMAKE_INCLUDE_PATH=/alt/include/path1:/alt/include/path2 \
+ -DCMAKE_LIBRARY_PATH=/alt/lib/path1:/alt/lib/path2 ...
+```
+
+## Ubuntu LTS, CentOS Stream, Fedora
+
+Use the `getdeps.py` approach above. We test in CI on Ubuntu LTS, and occasionally on other distros.
+
+If you find the set of system packages is not quite right for your chosen distro, you can specify distro version specific overrides in the dependency manifests (e.g. https://github.com/facebook/folly/blob/main/build/fbcode_builder/manifests/boost ). You could probably make it work on most recent Ubuntu/Debian or Fedora/Redhat derived distributions.
+
+At time of writing (Dec 2021) there is a build break on GCC 11.x based systems in lang_badge_test. If you don't need badge functionality you can work around by commenting it out from CMakeLists.txt (unfortunately fbthrift does need it)
+
+## Windows (Vcpkg)
+
+Note that many tests are disabled for folly Windows builds, you can see them in the log from the cmake configure step, or by looking for WINDOWS_DISABLED in `CMakeLists.txt`
+
+That said, `getdeps.py` builds work on Windows and are tested in CI.
+
+If you prefer, you can try Vcpkg. folly is available in [Vcpkg](https://github.com/Microsoft/vcpkg#vcpkg) and releases may be built via `vcpkg install folly:x64-windows`.
+
+You may also use `vcpkg install folly:x64-windows --head` to build against `main`.
+
+## macOS
+
+`getdeps.py` builds work on macOS and are tested in CI, however if you prefer, you can try one of the macOS package managers
+
+### Homebrew
+
+folly is available as a Formula and releases may be built via `brew install folly`.
+
+You may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `main`:
+
+```
+ ./folly/build/bootstrap-osx-homebrew.sh
+```
+
+This will create a build directory `_build` in the top-level.
+
+### MacPorts
+
+Install the required packages from MacPorts:
+
+```
+ sudo port install \
+ boost \
+ cmake \
+ gflags \
+ git \
+ google-glog \
+ libevent \
+ libtool \
+ lz4 \
+ lzma \
+ openssl \
+ snappy \
+ xz \
+ zlib
+```
+
+Download and install double-conversion:
+
+```
+ git clone https://github.com/google/double-conversion.git
+ cd double-conversion
+ cmake -DBUILD_SHARED_LIBS=ON .
+ make
+ sudo make install
+```
+
+Download and install folly with the parameters listed below:
+
+```
+ git clone https://github.com/facebook/folly.git
+ cd folly
+ mkdir _build
+ cd _build
+ cmake ..
+ make
+ sudo make install
+```
diff --git a/src/third_party/folly/dist/folly/TokenBucket.h b/src/third_party/folly/dist/folly/TokenBucket.h
new file mode 100644
index 00000000000..cf01bde8ecd
--- /dev/null
+++ b/src/third_party/folly/dist/folly/TokenBucket.h
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Notice: this code has been modified from the folly source code to be compatible with MongoDB utilities.
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "mongo/platform/compiler.h"
+#include "mongo/stdx/new.h"
+#include "mongo/util/assert_util.h"
+
+namespace folly {
+
+ /**
+ * constexpr_min and constexpr_max are pasted here from folly/ConstexprMath.h to avoid adding more
+ * folly dependencies.
+ */
+
+// When a and b are equivalent objects, we return a to make sorting stable.
+template
+constexpr T constexpr_min(T a, Ts... ts) {
+ T list[] = {ts..., a}; // 0-length arrays are illegal
+ for (auto i = 0u; i < sizeof...(Ts); ++i) {
+ a = list[i] < a ? list[i] : a;
+ }
+ return a;
+}
+
+// TLDR: Prefer using operator< for ordering. And when a and b are equivalent objects, we return b
+// to make sorting stable.
+// See http://stepanovpapers.com/notes.pdf for details.
+template
+constexpr T constexpr_max(T a, Ts... ts) {
+ T list[] = {ts..., a}; // 0-length arrays are illegal
+ for (auto i = 0u; i < sizeof...(Ts); ++i) {
+ a = list[i] < a ? a : list[i];
+ }
+ return a;
+}
+
+struct TokenBucketPolicyDefault {
+ using align =
+ std::integral_constant;
+
+ template
+ using atom = std::atomic;
+
+ using clock = std::chrono::steady_clock;
+
+ using concurrent = std::true_type;
+};
+
+/**
+ * Thread-safe (atomic) token bucket primitive.
+ *
+ * This primitive can be used to implement a token bucket
+ * (http://en.wikipedia.org/wiki/Token_bucket). It handles
+ * the storage of the state in an atomic way, and presents
+ * an interface dealing with tokens, rate, burstSize and time.
+ *
+ * This primitive records the last time it was updated. This allows the
+ * token bucket to add tokens "just in time" when tokens are requested.
+ *
+ * @tparam Policy A policy.
+ */
+template
+class TokenBucketStorage {
+ template
+ using Atom = typename Policy::template atom;
+ using Align = typename Policy::align;
+ using Clock = typename Policy::clock; // do we need clock here?
+ using Concurrent = typename Policy::concurrent;
+
+ static_assert(Clock::is_steady, "clock must be steady"); // do we need clock?
+
+ public:
+ /**
+ * Constructor.
+ *
+ * @param zeroTime Initial time at which to consider the token bucket
+ * starting to fill. Defaults to 0, so by default token
+ * buckets are "empty" after construction.
+ */
+ explicit TokenBucketStorage(double zeroTime = 0) noexcept
+ : zeroTime_(zeroTime) {}
+
+ /**
+ * Copy constructor.
+ *
+ * Thread-safe. (Copy constructors of derived classes may not be thread-safe
+ * however.)
+ */
+ TokenBucketStorage(const TokenBucketStorage& other) noexcept
+ : zeroTime_(other.zeroTime_.load(std::memory_order_relaxed)) {}
+
+ /**
+ * Copy-assignment operator.
+ *
+ * Warning: not thread safe for the object being assigned to (including
+ * self-assignment). Thread-safe for the other object.
+ */
+ TokenBucketStorage& operator=(const TokenBucketStorage& other) noexcept {
+ zeroTime_.store(other.zeroTime(), std::memory_order_relaxed);
+ return *this;
+ }
+
+ /**
+ * Re-initialize token bucket.
+ *
+ * Thread-safe.
+ *
+ * @param zeroTime Initial time at which to consider the token bucket
+ * starting to fill. Defaults to 0, so by default token
+ * bucket is reset to "empty".
+ */
+ void reset(double zeroTime = 0) noexcept {
+ zeroTime_.store(zeroTime, std::memory_order_relaxed);
+ }
+
+ /**
+ * Returns the token balance at specified time (negative if bucket in debt).
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double balance(
+ double rate, double burstSize, double nowInSeconds) const noexcept {
+ invariant(rate > 0);
+ invariant(burstSize > 0);
+ double zt = this->zeroTime_.load(std::memory_order_relaxed);
+ return std::min((nowInSeconds - zt) * rate, burstSize);
+ }
+
+ /**
+ * Consume tokens at the given rate/burst/time.
+ *
+ * Consumption is actually done by the callback function: it's given a
+ * reference with the number of available tokens and returns the number
+ * consumed. Typically the return value would be between 0.0 and available,
+ * but there are no restrictions.
+ *
+ * Note: the callback may be called multiple times, so please no side-effects
+ */
+ template
+ double consume(
+ double rate,
+ double burstSize,
+ double nowInSeconds,
+ const Callback& callback) {
+ invariant(rate > 0);
+ invariant(burstSize > 0);
+
+ double zeroTimeOld;
+ double zeroTimeNew;
+ double consumed;
+ do {
+ zeroTimeOld = zeroTime();
+ double tokens = std::min((nowInSeconds - zeroTimeOld) * rate, burstSize);
+ consumed = callback(tokens);
+ double tokensNew = tokens - consumed;
+ if (consumed == 0.0) {
+ return consumed;
+ }
+
+ zeroTimeNew = nowInSeconds - tokensNew / rate;
+ } while (MONGO_unlikely(
+ !compare_exchange_weak_relaxed(zeroTime_, zeroTimeOld, zeroTimeNew)));
+
+ return consumed;
+ }
+
+ /**
+ * returns the time at which the bucket will have `target` tokens available.
+ *
+ * Caution: it doesn't make sense to ask about target > burstSize
+ *
+ * Eg.
+ * // time debt repaid
+ * bucket.timeWhenBucket(rate, 0);
+ *
+ * // time bucket is full
+ * bucket.timeWhenBucket(rate, burstSize);
+ */
+
+ double timeWhenBucket(double rate, double target) {
+ return zeroTime() + target / rate;
+ }
+
+ /**
+ * Return extra tokens back to the bucket.
+ *
+ * Thread-safe.
+ */
+ void returnTokens(double tokensToReturn, double rate) {
+ invariant(rate > 0);
+
+ returnTokensImpl(tokensToReturn, rate);
+ }
+
+ private:
+ /**
+ * Adjust zeroTime based on rate and tokenCount and return the new value of
+ * zeroTime_. Note: Token count can be negative to move the zeroTime_
+ * into the future.
+ */
+ double returnTokensImpl(double tokenCount, double rate) {
+ auto zeroTimeOld = zeroTime_.load(std::memory_order_relaxed);
+
+ double zeroTimeNew;
+ do {
+ zeroTimeNew = zeroTimeOld - tokenCount / rate;
+
+ } while (MONGO_unlikely(
+ !compare_exchange_weak_relaxed(zeroTime_, zeroTimeOld, zeroTimeNew)));
+ return zeroTimeNew;
+ }
+
+ static bool compare_exchange_weak_relaxed(
+
+ Atom& atom, double& expected, double zeroTime) {
+ if (Concurrent::value) {
+ return atom.compare_exchange_weak(
+ expected, zeroTime, std::memory_order_relaxed);
+ } else {
+ return atom.store(zeroTime, std::memory_order_relaxed), true;
+ }
+ }
+
+ double zeroTime() const {
+ return this->zeroTime_.load(std::memory_order_relaxed);
+ }
+
+ static constexpr size_t AlignZeroTime =
+ constexpr_max(Align::value, alignof(Atom));
+ alignas(AlignZeroTime) Atom zeroTime_;
+};
+
+/**
+ * Thread-safe (atomic) token bucket implementation.
+ *
+ * A token bucket (http://en.wikipedia.org/wiki/Token_bucket) models a stream
+ * of events with an average rate and some amount of burstiness. The canonical
+ * example is a packet switched network: the network can accept some number of
+ * bytes per second and the bytes come in finite packets (bursts). A token
+ * bucket stores up to a fixed number of tokens (the burst size). Some number
+ * of tokens are removed when an event occurs. The tokens are replenished at a
+ * fixed rate. Failure to allocate tokens implies resource is unavailable and
+ * caller needs to implement its own retry mechanism. For simple cases where
+ * caller is okay with a FIFO starvation-free scheduling behavior, there are
+ * also APIs to 'borrow' from the future effectively assigning a start time to
+ * the caller when it should proceed with using the resource. It is also
+ * possible to 'return' previously allocated tokens to make them available to
+ * other users. Returns in excess of burstSize are considered expired and
+ * will not be available to later callers.
+ *
+ * This implementation records the last time it was updated. This allows the
+ * token bucket to add tokens "just in time" when tokens are requested.
+ *
+ * The "dynamic" base variant allows the token generation rate and maximum
+ * burst size to change with every token consumption.
+ *
+ * @tparam Policy A policy.
+ */
+template
+class BasicDynamicTokenBucket {
+ template
+ using Atom = typename Policy::template atom;
+ using Align = typename Policy::align;
+ using Clock = typename Policy::clock;
+ using Concurrent = typename Policy::concurrent;
+
+ static_assert(Clock::is_steady, "clock must be steady");
+
+ public:
+ /**
+ * Constructor.
+ *
+ * @param zeroTime Initial time at which to consider the token bucket
+ * starting to fill. Defaults to 0, so by default token
+ * buckets are "empty" after construction.
+ */
+ explicit BasicDynamicTokenBucket(double zeroTime = 0) noexcept
+ : bucket_(zeroTime) {}
+
+ /**
+ * Copy constructor and copy assignment operator.
+ *
+ * Thread-safe. (Copy constructors of derived classes may not be thread-safe
+ * however.)
+ */
+ BasicDynamicTokenBucket(const BasicDynamicTokenBucket& other) noexcept =
+ default;
+ BasicDynamicTokenBucket& operator=(
+ const BasicDynamicTokenBucket& other) noexcept = default;
+
+ /**
+ * Re-initialize token bucket.
+ *
+ * Thread-safe.
+ *
+ * @param zeroTime Initial time at which to consider the token bucket
+ * starting to fill. Defaults to 0, so by default token
+ * bucket is reset to "empty".
+ */
+ void reset(double zeroTime = 0) noexcept { bucket_.reset(zeroTime); }
+
+ /**
+ * Returns the current time in seconds since Epoch.
+ */
+ static double defaultClockNow() noexcept {
+ auto const now = Clock::now().time_since_epoch();
+ return std::chrono::duration(now).count();
+ }
+
+ /**
+ * Attempts to consume some number of tokens. Tokens are first added to the
+ * bucket based on the time elapsed since the last attempt to consume tokens.
+ * Note: Attempts to consume more tokens than the burst size will always
+ * fail.
+ *
+ * Thread-safe.
+ *
+ * @param toConsume The number of tokens to consume.
+ * @param rate Number of tokens to generate per second.
+ * @param burstSize Maximum burst size. Must be greater than 0.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ * @return True if the rate limit check passed, false otherwise.
+ */
+ bool consume(
+ double toConsume,
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) {
+ invariant(rate > 0);
+ invariant(burstSize > 0);
+
+ if (bucket_.balance(rate, burstSize, nowInSeconds) < 0.0) {
+ return 0;
+ }
+
+ double consumed = bucket_.consume(
+ rate, burstSize, nowInSeconds, [toConsume](double available) {
+ return available < toConsume ? 0.0 : toConsume;
+ });
+
+ invariant(consumed == toConsume || consumed == 0.0);
+ return consumed == toConsume;
+ }
+
+ /**
+ * Similar to consume, but always consumes some number of tokens. If the
+ * bucket contains enough tokens - consumes toConsume tokens. Otherwise the
+ * bucket is drained.
+ *
+ * Thread-safe.
+ *
+ * @param toConsume The number of tokens to consume.
+ * @param rate Number of tokens to generate per second.
+ * @param burstSize Maximum burst size. Must be greater than 0.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ * @return number of tokens that were consumed.
+ */
+ double consumeOrDrain(
+ double toConsume,
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) {
+ invariant(rate > 0);
+ invariant(burstSize > 0);
+
+ if (bucket_.balance(rate, burstSize, nowInSeconds) <= 0.0) {
+ return 0;
+ }
+
+ double consumed = bucket_.consume(
+ rate, burstSize, nowInSeconds, [toConsume](double available) {
+ return constexpr_min(available, toConsume);
+ });
+ return consumed;
+ }
+
+ /**
+ * Return extra tokens back to the bucket.
+ *
+ * Thread-safe.
+ */
+ void returnTokens(double tokensToReturn, double rate) {
+ invariant(rate > 0);
+ invariant(tokensToReturn > 0);
+
+ bucket_.returnTokens(tokensToReturn, rate);
+ }
+
+ /**
+ * Like consumeOrDrain but the call will always satisfy the asked for count.
+ * It does so by borrowing tokens from the future if the currently available
+ * count isn't sufficient.
+ *
+ * Returns a folly::Optional. The optional wont be set if the request
+ * cannot be satisfied: only case is when it is larger than burstSize. The
+ * value of the optional is a double indicating the time in seconds that the
+ * caller needs to wait at which the reservation becomes valid. The caller
+ * could simply sleep for the returned duration to smooth out the allocation
+ * to match the rate limiter or do some other computation in the meantime. In
+ * any case, any regular consume or consumeOrDrain calls will fail to allocate
+ * any tokens until the future time is reached.
+ *
+ * Note: It is assumed the caller will not ask for a very large count nor use
+ * it immediately (if not waiting inline) as that would break the burst
+ * prevention the limiter is meant to be used for.
+ *
+ * Thread-safe.
+ */
+ boost::optional consumeWithBorrowNonBlocking(
+ double toConsume,
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) {
+ invariant(rate > 0);
+ invariant(burstSize > 0);
+
+ if (burstSize < toConsume) {
+ return boost::none;
+ }
+
+ while (toConsume > 0) {
+ double consumed =
+ consumeOrDrain(toConsume, rate, burstSize, nowInSeconds);
+ if (consumed > 0) {
+ toConsume -= consumed;
+ } else {
+ bucket_.returnTokens(-toConsume, rate);
+ double debtPaid = bucket_.timeWhenBucket(rate, 0);
+ double napTime = std::max(0.0, debtPaid - nowInSeconds);
+ return napTime;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Convenience wrapper around non-blocking borrow to sleep inline until
+ * reservation is valid.
+ */
+ bool consumeWithBorrowAndWait(
+ double toConsume,
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) {
+ auto res =
+ consumeWithBorrowNonBlocking(toConsume, rate, burstSize, nowInSeconds);
+ if (res.value_or(0) > 0) {
+ const auto napUSec = static_cast(res.value() * 1000000);
+ std::this_thread::sleep_for(std::chrono::microseconds(napUSec));
+ }
+ return res.has_value();
+ }
+
+ /**
+ * Returns the tokens available at specified time (zero if in debt).
+ *
+ * Use balance() to get the balance of tokens.
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double available(
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) const noexcept {
+ return std::max(0.0, balance(rate, burstSize, nowInSeconds));
+ }
+
+ /**
+ * Returns the token balance at specified time (negative if bucket in debt).
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double balance(
+ double rate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) const noexcept {
+ return bucket_.balance(rate, burstSize, nowInSeconds);
+ }
+
+ private:
+ TokenBucketStorage bucket_;
+};
+
+/**
+ * Specialization of BasicDynamicTokenBucket with a fixed token
+ * generation rate and a fixed maximum burst size.
+ */
+template
+class BasicTokenBucket {
+ private:
+ using Impl = BasicDynamicTokenBucket;
+
+ public:
+ /**
+ * Construct a token bucket with a specific maximum rate and burst size.
+ *
+ * @param genRate Number of tokens to generate per second.
+ * @param burstSize Maximum burst size. Must be greater than 0.
+ * @param zeroTime Initial time at which to consider the token bucket
+ * starting to fill. Defaults to 0, so by default token
+ * bucket is "empty" after construction.
+ */
+ BasicTokenBucket(
+ double genRate, double burstSize, double zeroTime = 0) noexcept
+ : tokenBucket_(zeroTime), rate_(genRate), burstSize_(burstSize) {
+ invariant(rate_ > 0);
+ invariant(burstSize_ > 0);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * Warning: not thread safe!
+ */
+ BasicTokenBucket(const BasicTokenBucket& other) noexcept = default;
+
+ /**
+ * Copy-assignment operator.
+ *
+ * Warning: not thread safe!
+ */
+ BasicTokenBucket& operator=(const BasicTokenBucket& other) noexcept = default;
+
+ /**
+ * Returns the current time in seconds since Epoch.
+ */
+ static double defaultClockNow() noexcept(noexcept(Impl::defaultClockNow())) {
+ return Impl::defaultClockNow();
+ }
+
+ /**
+ * Change rate and burst size.
+ *
+ * Warning: not thread safe!
+ *
+ * @param genRate Number of tokens to generate per second.
+ * @param burstSize Maximum burst size. Must be greater than 0.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ */
+ void reset(
+ double genRate,
+ double burstSize,
+ double nowInSeconds = defaultClockNow()) noexcept {
+ invariant(genRate > 0);
+ invariant(burstSize > 0);
+ const double availTokens = available(nowInSeconds);
+ rate_ = genRate;
+ burstSize_ = burstSize;
+ setCapacity(availTokens, nowInSeconds);
+ }
+
+ /**
+ * Change number of tokens in bucket.
+ *
+ * Warning: not thread safe!
+ *
+ * @param tokens Desired number of tokens in bucket after the call.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ */
+ void setCapacity(double tokens, double nowInSeconds) noexcept {
+ tokenBucket_.reset(nowInSeconds - tokens / rate_);
+ }
+
+ /**
+ * Attempts to consume some number of tokens. Tokens are first added to the
+ * bucket based on the time elapsed since the last attempt to consume tokens.
+ * Note: Attempts to consume more tokens than the burst size will always
+ * fail.
+ *
+ * Thread-safe.
+ *
+ * @param toConsume The number of tokens to consume.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ * @return True if the rate limit check passed, false otherwise.
+ */
+ bool consume(double toConsume, double nowInSeconds = defaultClockNow()) {
+ return tokenBucket_.consume(toConsume, rate_, burstSize_, nowInSeconds);
+ }
+
+ /**
+ * Similar to consume, but always consumes some number of tokens. If the
+ * bucket contains enough tokens - consumes toConsume tokens. Otherwise the
+ * bucket is drained.
+ *
+ * Thread-safe.
+ *
+ * @param toConsume The number of tokens to consume.
+ * @param nowInSeconds Current time in seconds. Should be monotonically
+ * increasing from the nowInSeconds specified in
+ * this token bucket's constructor.
+ * @return number of tokens that were consumed.
+ */
+ double consumeOrDrain(
+ double toConsume, double nowInSeconds = defaultClockNow()) {
+ return tokenBucket_.consumeOrDrain(
+ toConsume, rate_, burstSize_, nowInSeconds);
+ }
+
+ /**
+ * Returns extra token back to the bucket. Cannot be negative.
+ * For negative tokens, setCapacity() can be used
+ */
+ void returnTokens(double tokensToReturn) {
+ return tokenBucket_.returnTokens(tokensToReturn, rate_);
+ }
+
+ /**
+ * Reserve tokens and return time to wait for in order for the reservation to
+ * be compatible with the bucket configuration.
+ */
+ boost::optional consumeWithBorrowNonBlocking(
+ double toConsume, double nowInSeconds = defaultClockNow()) {
+ return tokenBucket_.consumeWithBorrowNonBlocking(
+ toConsume, rate_, burstSize_, nowInSeconds);
+ }
+
+ /**
+ * Reserve tokens. Blocks if need be until reservation is satisfied.
+ */
+ bool consumeWithBorrowAndWait(
+ double toConsume, double nowInSeconds = defaultClockNow()) {
+ return tokenBucket_.consumeWithBorrowAndWait(
+ toConsume, rate_, burstSize_, nowInSeconds);
+ }
+
+ /**
+ * Returns the tokens available at specified time (zero if in debt).
+ *
+ * Use balance() to get the balance of tokens.
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double available(double nowInSeconds = defaultClockNow()) const noexcept {
+ return std::max(0.0, balance(nowInSeconds));
+ }
+
+ /**
+ * Returns the token balance at specified time (negative if bucket in debt).
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double balance(double nowInSeconds = defaultClockNow()) const noexcept {
+ return tokenBucket_.balance(rate_, burstSize_, nowInSeconds);
+ }
+
+ /**
+ * Returns the number of tokens generated per second.
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double rate() const noexcept { return rate_; }
+
+ /**
+ * Returns the maximum burst size.
+ *
+ * Thread-safe (but returned value may immediately be outdated).
+ */
+ double burst() const noexcept { return burstSize_; }
+
+ private:
+ Impl tokenBucket_;
+ double rate_;
+ double burstSize_;
+};
+
+using TokenBucket = BasicTokenBucket<>;
+using DynamicTokenBucket = BasicDynamicTokenBucket<>;
+
+} // namespace folly
diff --git a/src/third_party/folly/scripts/import.sh b/src/third_party/folly/scripts/import.sh
new file mode 100644
index 00000000000..d54e7bde90a
--- /dev/null
+++ b/src/third_party/folly/scripts/import.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+# This script clones the specified version of folly and strips out everything we do not need.
+# Right now, we only use the TokenBucket utility from folly, and so nearly everthing else is removed.
+
+set -o verbose
+set -o errexit
+set -euo pipefail
+IFS=$'\n\t'
+
+set -vx
+
+NAME=folly
+VERSION="v2025.04.21.00"
+REVISION="${VERSION}-mongo"
+
+# get the source
+DEST_DIR=$(git rev-parse --show-toplevel)/src/third_party/folly
+if [[ -d $DEST_DIR/dist ]]; then
+ echo "You must remove '$DEST_DIR/dist' before running $0" >&2
+ exit 1
+fi
+
+git clone --depth 1 --branch $REVISION git@github.com:mongodb-forks/folly.git $DEST_DIR/dist
+
+pushd $DEST_DIR/dist
+find . -mindepth 1 -maxdepth 1 -name ".*" -exec rm -rf {} \;
+find . -mindepth 1 -maxdepth 1 -name "*build*" -exec rm -rf {} \;
+find . -mindepth 1 -maxdepth 1 -name "*CMake*" -exec rm -rf {} \;
+find . -mindepth 1 -maxdepth 1 -iname "buck*" -exec rm -rf {} \;
+rm "CODE_OF_CONDUCT.md"
+rm "CONTRIBUTING.md"
+rm PACKAGE
+rm -rf static
+popd
+
+pushd $DEST_DIR/dist/folly
+rm -R -- */
+find . ! -name 'TokenBucket.h' -type f -exec rm -rf {} +
+popd
diff --git a/src/third_party/folly/test/token_bucket_test.cpp b/src/third_party/folly/test/token_bucket_test.cpp
new file mode 100644
index 00000000000..e0024252f2f
--- /dev/null
+++ b/src/third_party/folly/test/token_bucket_test.cpp
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2025-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * .
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include
+
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+/**
+ * Simple test to ensure the folly::TokenBucket library has been vendored in properly.
+ */
+TEST(TokenBucketTest, CanConsume) {
+ folly::DynamicTokenBucket tokenBucket;
+ ASSERT_TRUE(tokenBucket.consume(1.0, 1.0, 20.0));
+}
+
+} // namespace
+} // namespace mongo