Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions bazel/deps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,17 @@ fp16:
extensions:
- envoy.wasm.runtime.v8

immer:
project_name: "immer"
project_desc: "Header-only library of persistent and immutable data structures for C++"
project_url: "https://github.com/arximboldi/immer"
release_date: "2026-01-29"
use_category:
- dataplane_core
cpe: "N/A"
license: "BSL-1.0"
license_url: "https://github.com/arximboldi/immer/blob/{version}/LICENSE"

googleurl:
project_name: "Chrome URL parsing library"
project_desc: "Chrome URL parsing library"
Expand Down
11 changes: 11 additions & 0 deletions bazel/external/immer.BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("@rules_cc//cc:defs.bzl", "cc_library")

licenses(["notice"]) # BSL-1.0

package(default_visibility = ["//visibility:public"])

cc_library(
name = "immer",
hdrs = glob(["immer/**/*.hpp"]),
includes = ["."],
)
7 changes: 7 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ def envoy_dependencies(skip_targets = []):
_highway()
_dragonbox()
_fp16()
_immer()
_simdutf()
_quiche()
_googleurl()
Expand Down Expand Up @@ -811,6 +812,12 @@ def _fp16():
build_file = "@envoy//bazel/external:fp16.BUILD",
)

def _immer():
external_http_archive(
name = "immer",
build_file = "@envoy//bazel/external:immer.BUILD",
)

def _simdutf():
external_http_archive(
name = "simdutf",
Expand Down
9 changes: 9 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,15 @@ REPOSITORY_LOCATIONS_SPEC = dict(
sha256 = "e2da4f41bae8869f8dee56f4c104e699e7de3a483b5e451fda8e76fbcc66c59a",
urls = ["https://github.com/Maratyszcza/FP16/archive/{version}.zip"],
),
immer = dict(
# Pinned past the v0.9.1 release to pick up the upstream fix for
# https://github.com/arximboldi/immer/issues/274 (pull request 323), which corrects the
# allocation size of the empty map node. Move back to a tagged release once one ships it.
version = "71813305ed1af2173f199e8ee209df5d6a16e766",
strip_prefix = "immer-{version}",
sha256 = "470a1926a1ff57d0de42bc6f68710d988eda31e30ac142c81bcea9a59cdb7828",
urls = ["https://github.com/arximboldi/immer/archive/{version}.tar.gz"],
),
simdutf = dict(
# NOTE: Update together with v8 and proxy_wasm_cpp_host.
version = "8.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Added the ``envoy_dynamic_module_callback_cluster_use_persistent_host_map`` ABI callback so that a
dynamic-modules cluster can opt into a persistent cross-priority host map in place of the
default flat copy-on-write map, making each membership delta O(delta) instead of an O(N) flat copy.
Membership behavior is identical under either backing, and the flat map remains the default. The
Rust SDK exposes this as ``EnvoyCluster::set_use_persistent_host_map``.
1 change: 1 addition & 0 deletions envoy/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ envoy_cc_library(
"//envoy/ssl:context_interface",
"//envoy/ssl:context_manager_interface",
"//envoy/upstream:types_interface",
"@abseil-cpp//absl/functional:function_ref",
"@envoy_api//envoy/config/cluster/v3:pkg_cc_proto",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
Expand Down
56 changes: 52 additions & 4 deletions envoy/upstream/upstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "envoy/upstream/resource_manager.h"
#include "envoy/upstream/types.h"

#include "absl/functional/function_ref.h"
#include "absl/strings/string_view.h"
#include "fmt/format.h"

Expand Down Expand Up @@ -362,6 +363,53 @@ using ExcludedHostVector = Phantom<HostVector, Excluded>;
using HostMap = absl::flat_hash_map<std::string, Upstream::HostSharedPtr>;
using HostMapSharedPtr = std::shared_ptr<HostMap>;
using HostMapConstSharedPtr = std::shared_ptr<const HostMap>;

/**
* Read-only host-by-address lookup published to workers. Backed by either the flat HostMap (the
* default) or a persistent map, selected per cluster via
* `MainPrioritySetImpl::setUsePersistentCrossPriorityHostMap`, so the backing is swappable without
* changing consumers.
*/
class HostLookupTable {
public:
virtual ~HostLookupTable() = default;

/**
* @param address the host address to look up.
* @return HostSharedPtr the host for the address, or nullptr if absent.
*/
virtual HostSharedPtr findHost(absl::string_view address) const PURE;

/**
* @return size_t the number of hosts in the table.
*/
virtual size_t size() const PURE;

/**
* @return bool true if the table holds no hosts.
*/
virtual bool empty() const PURE;

/**
* Invokes a callback for each (address, host) entry. Iteration order is unspecified.
*
* @param cb the callback to invoke for each entry.
*/
virtual void
forEach(absl::FunctionRef<void(const std::string&, const HostSharedPtr&)> cb) const PURE;
};
using HostLookupTableConstSharedPtr = std::shared_ptr<const HostLookupTable>;

/**
* Wraps a flat HostMap in the HostLookupTable interface. Defined in upstream_impl.cc so the
* concrete type stays private. Available to tests and consumers that hold a HostMap but need to
* publish a HostLookupTable.
*
* @param map the flat host map to wrap.
* @return HostLookupTableConstSharedPtr a lookup table backed by the given map.
*/
HostLookupTableConstSharedPtr makeFlatHostLookupTable(HostMapConstSharedPtr map);

using HostVectorSharedPtr = std::shared_ptr<HostVector>;
using HostVectorConstSharedPtr = std::shared_ptr<const HostVector>;

Expand Down Expand Up @@ -590,10 +638,10 @@ class PrioritySet {
virtual const std::vector<HostSetPtr>& hostSetsPerPriority() const PURE;

/**
* @return HostMapConstSharedPtr read only cross priority host map that indexed by host address
* string.
* @return HostLookupTableConstSharedPtr read only cross priority host lookup table indexed by
* host address string.
*/
virtual HostMapConstSharedPtr crossPriorityHostMap() const PURE;
virtual HostLookupTableConstSharedPtr crossPriorityHostMap() const PURE;

/**
* Parameter class for updateHosts.
Expand Down Expand Up @@ -627,7 +675,7 @@ class PrioritySet {
const HostVector& hosts_added, const HostVector& hosts_removed,
std::optional<bool> weighted_priority_health,
std::optional<uint32_t> overprovisioning_factor,
HostMapConstSharedPtr cross_priority_host_map = nullptr) PURE;
HostLookupTableConstSharedPtr cross_priority_host_map = nullptr) PURE;

/**
* Callback provided during batch updates that can be used to update hosts.
Expand Down
15 changes: 15 additions & 0 deletions source/common/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,19 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "persistent_host_map_lib",
srcs = ["persistent_host_map.cc"],
hdrs = ["persistent_host_map.h"],
rbe_pool = "6gig",
deps = [
"//envoy/upstream:upstream_interface",
"@abseil-cpp//absl/functional:function_ref",
"@abseil-cpp//absl/strings:string_view",
"@immer",
],
)

envoy_cc_library(
name = "upstream_lib",
srcs = ["upstream_impl.cc"],
Expand All @@ -393,6 +406,7 @@ envoy_cc_library(
":cluster_factory_lib",
":default_local_address_selector_factory",
":health_checker_lib",
":persistent_host_map_lib",
# TODO(mattklein123): Move the clusters to extensions so they can be compiled out.
":upstream_includes",
":transport_socket_match_lib",
Expand Down Expand Up @@ -504,6 +518,7 @@ envoy_cc_library(
"//source/extensions/upstreams/http:config",
"//source/extensions/upstreams/tcp:config",
"//source/server:transport_socket_config_lib",
"@abseil-cpp//absl/functional:function_ref",
"@abseil-cpp//absl/synchronization",
"@envoy_api//envoy/config/cluster/v3:pkg_cc_proto",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
Expand Down
13 changes: 7 additions & 6 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,8 @@ void ClusterManagerImpl::postThreadLocalClusterUpdate(ClusterManagerCluster& cm_
per_priority.overprovisioning_factor_ = host_set->overprovisioningFactor();
}

HostMapConstSharedPtr host_map = cm_cluster.cluster().prioritySet().crossPriorityHostMap();
HostLookupTableConstSharedPtr host_map =
cm_cluster.cluster().prioritySet().crossPriorityHostMap();

pending_cluster_creations_.erase(cm_cluster.cluster().info()->name());

Expand Down Expand Up @@ -1304,7 +1305,7 @@ void ClusterManagerImpl::postThreadLocalClusterUpdate(ClusterManagerCluster& cm_
ClusterManagerImpl::ClusterInitializationObjectConstSharedPtr
ClusterManagerImpl::addOrUpdateClusterInitializationObjectIfSupported(
const ThreadLocalClusterUpdateParams& params, ClusterInfoConstSharedPtr cluster_info,
LoadBalancerFactorySharedPtr load_balancer_factory, HostMapConstSharedPtr map,
LoadBalancerFactorySharedPtr load_balancer_factory, HostLookupTableConstSharedPtr map,
UnitFloat drop_overload, absl::string_view drop_category) {
if (!deferralIsSupportedForCluster(cluster_info)) {
return nullptr;
Expand Down Expand Up @@ -1386,7 +1387,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::initializeClusterInlineIfExis

ClusterManagerImpl::ClusterInitializationObject::ClusterInitializationObject(
const ThreadLocalClusterUpdateParams& params, ClusterInfoConstSharedPtr cluster_info,
LoadBalancerFactorySharedPtr load_balancer_factory, HostMapConstSharedPtr map,
LoadBalancerFactorySharedPtr load_balancer_factory, HostLookupTableConstSharedPtr map,
UnitFloat drop_overload, absl::string_view drop_category)
: cluster_info_(std::move(cluster_info)), load_balancer_factory_(load_balancer_factory),
cross_priority_host_map_(map), drop_overload_(drop_overload), drop_category_(drop_category) {
Expand All @@ -1399,7 +1400,7 @@ ClusterManagerImpl::ClusterInitializationObject::ClusterInitializationObject(
ClusterManagerImpl::ClusterInitializationObject::ClusterInitializationObject(
const absl::flat_hash_map<int, ThreadLocalClusterUpdateParams::PerPriority>& per_priority_state,
const ThreadLocalClusterUpdateParams& update_params, ClusterInfoConstSharedPtr cluster_info,
LoadBalancerFactorySharedPtr load_balancer_factory, HostMapConstSharedPtr map,
LoadBalancerFactorySharedPtr load_balancer_factory, HostLookupTableConstSharedPtr map,
UnitFloat drop_overload, absl::string_view drop_category)
: per_priority_state_(per_priority_state), cluster_info_(std::move(cluster_info)),
load_balancer_factory_(load_balancer_factory), cross_priority_host_map_(map),
Expand Down Expand Up @@ -1513,7 +1514,7 @@ void ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::updateHost
LocalityWeightsConstSharedPtr locality_weights, const HostVector& hosts_added,
const HostVector& hosts_removed, std::optional<bool> weighted_priority_health,
std::optional<uint32_t> overprovisioning_factor,
HostMapConstSharedPtr cross_priority_host_map) {
HostLookupTableConstSharedPtr cross_priority_host_map) {
ENVOY_LOG(debug, "membership update for TLS cluster {} added {} removed {}", name,
hosts_added.size(), hosts_removed.size());
priority_set_.updateHosts(priority, std::move(update_hosts_params), std::move(locality_weights),
Expand Down Expand Up @@ -1841,7 +1842,7 @@ void ClusterManagerImpl::ThreadLocalClusterManagerImpl::updateClusterMembership(
const std::string& name, uint32_t priority, PrioritySet::UpdateHostsParams update_hosts_params,
LocalityWeightsConstSharedPtr locality_weights, const HostVector& hosts_added,
const HostVector& hosts_removed, bool weighted_priority_health,
uint64_t overprovisioning_factor, HostMapConstSharedPtr cross_priority_host_map) {
uint64_t overprovisioning_factor, HostLookupTableConstSharedPtr cross_priority_host_map) {
ASSERT(thread_local_clusters_.find(name) != thread_local_clusters_.end());
const auto& cluster_entry = thread_local_clusters_[name];
cluster_entry->updateHosts(name, priority, std::move(update_hosts_params),
Expand Down
12 changes: 6 additions & 6 deletions source/common/upstream/cluster_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,20 +454,20 @@ class ClusterManagerImpl : public ClusterManager,
ClusterInitializationObject(const ThreadLocalClusterUpdateParams& params,
ClusterInfoConstSharedPtr cluster_info,
LoadBalancerFactorySharedPtr load_balancer_factory,
HostMapConstSharedPtr map, UnitFloat drop_overload,
HostLookupTableConstSharedPtr map, UnitFloat drop_overload,
absl::string_view drop_category);

ClusterInitializationObject(
const absl::flat_hash_map<int, ThreadLocalClusterUpdateParams::PerPriority>&
per_priority_state,
const ThreadLocalClusterUpdateParams& update_params, ClusterInfoConstSharedPtr cluster_info,
LoadBalancerFactorySharedPtr load_balancer_factory, HostMapConstSharedPtr map,
LoadBalancerFactorySharedPtr load_balancer_factory, HostLookupTableConstSharedPtr map,
UnitFloat drop_overload, absl::string_view drop_category);

absl::flat_hash_map<int, ThreadLocalClusterUpdateParams::PerPriority> per_priority_state_;
const ClusterInfoConstSharedPtr cluster_info_;
const LoadBalancerFactorySharedPtr load_balancer_factory_;
const HostMapConstSharedPtr cross_priority_host_map_;
const HostLookupTableConstSharedPtr cross_priority_host_map_;
UnitFloat drop_overload_{0};
const std::string drop_category_;
};
Expand Down Expand Up @@ -628,7 +628,7 @@ class ClusterManagerImpl : public ClusterManager,
const HostVector& hosts_added, const HostVector& hosts_removed,
std::optional<bool> weighted_priority_health,
std::optional<uint32_t> overprovisioning_factor,
HostMapConstSharedPtr cross_priority_host_map);
HostLookupTableConstSharedPtr cross_priority_host_map);

// Drains any connection pools associated with the removed hosts. All connections will be
// closed gracefully and no new connections will be created.
Expand Down Expand Up @@ -720,7 +720,7 @@ class ClusterManagerImpl : public ClusterManager,
LocalityWeightsConstSharedPtr locality_weights,
const HostVector& hosts_added, const HostVector& hosts_removed,
bool weighted_priority_health, uint64_t overprovisioning_factor,
HostMapConstSharedPtr cross_priority_host_map);
HostLookupTableConstSharedPtr cross_priority_host_map);
void onHostHealthFailure(const HostSharedPtr& host);

ConnPoolsContainer* getHttpConnPoolsContainer(const HostConstSharedPtr& host,
Expand Down Expand Up @@ -931,7 +931,7 @@ class ClusterManagerImpl : public ClusterManager,
*/
ClusterInitializationObjectConstSharedPtr addOrUpdateClusterInitializationObjectIfSupported(
const ThreadLocalClusterUpdateParams& params, ClusterInfoConstSharedPtr cluster_info,
LoadBalancerFactorySharedPtr load_balancer_factory, HostMapConstSharedPtr map,
LoadBalancerFactorySharedPtr load_balancer_factory, HostLookupTableConstSharedPtr map,
UnitFloat drop_overload, absl::string_view drop_category);

bool deferralIsSupportedForCluster(const ClusterInfoConstSharedPtr& info) const;
Expand Down
9 changes: 3 additions & 6 deletions source/common/upstream/host_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ HostUtility::HostStatusSet HostUtility::createOverrideHostStatus(
}

HostUtility::OverrideHostSelectionResult
HostUtility::selectOverrideHost(const HostMap* host_map, HostStatusSet status,
HostUtility::selectOverrideHost(const HostLookupTable* host_map, HostStatusSet status,
LoadBalancerContext* context) {
if (context == nullptr) {
return {};
Expand All @@ -156,16 +156,13 @@ HostUtility::selectOverrideHost(const HostMap* host_map, HostStatusSet status,
return {nullptr, strict_mode, OverrideHostSelectionStatus::NotFound};
}

auto host_iter = host_map->find(override_host->host);
HostConstSharedPtr host = host_map->findHost(override_host->host);

// The override host cannot be found in the host map.
if (host_iter == host_map->end()) {
if (host == nullptr) {
return {nullptr, strict_mode, OverrideHostSelectionStatus::NotFound};
}

HostConstSharedPtr host = host_iter->second;
ASSERT(host != nullptr);

if (status[static_cast<uint32_t>(host->healthStatus())]) {
return {host, strict_mode, OverrideHostSelectionStatus::Success};
}
Expand Down
5 changes: 3 additions & 2 deletions source/common/upstream/host_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ class HostUtility {
* @return OverrideHostSelectionResult containing the selected host, whether strict mode was
* requested, and the reason for the selection outcome.
*/
static OverrideHostSelectionResult
selectOverrideHost(const HostMap* host_map, HostStatusSet status, LoadBalancerContext* context);
static OverrideHostSelectionResult selectOverrideHost(const HostLookupTable* host_map,
HostStatusSet status,
LoadBalancerContext* context);

// Iterate over all per-endpoint metrics, for clusters with `per_endpoint_stats` enabled.
static void
Expand Down
Loading
Loading