-
Notifications
You must be signed in to change notification settings - Fork 270
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
distribute: Add a backend cache (#2122)
`NewDistribute` will be used to implement per-route traffic distribution policies so that, for instance, each request route may use a different traffic split. In this setup, we want each instance--re-constructed as configuration changes--to reuse a common set of concrete services so that load balancer state need not be lost when a policy changes. This change adds a `distribute::BackendCache` module that produces a `NewDistribute` for an updated, cached set of backends.
- Loading branch information
Showing
7 changed files
with
155 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use super::{params, NewDistribute}; | ||
use linkerd_stack::{layer, NewService, Param}; | ||
use parking_lot::Mutex; | ||
use std::{fmt::Debug, hash::Hash, sync::Arc}; | ||
|
||
/// A [`NewService`] that produces [`NewDistribute`]s using a shared cache of | ||
/// backends. | ||
/// | ||
/// On each call to [`NewService::new_service`], the cache extracts a new set of | ||
/// [`params::Backends`] from the target to determine which | ||
/// services should be added/removed from the cache. | ||
#[derive(Debug)] | ||
pub struct BackendCache<K, N, S>(Arc<Inner<K, N, S>>); | ||
|
||
#[derive(Debug)] | ||
struct Inner<K, N, S> { | ||
new_backend: N, | ||
backends: Mutex<ahash::AHashMap<K, S>>, | ||
} | ||
|
||
// === impl BackendCache === | ||
|
||
impl<K, N, S> BackendCache<K, N, S> { | ||
pub fn new(new_backend: N) -> Self { | ||
Self(Arc::new(Inner { | ||
new_backend, | ||
backends: Default::default(), | ||
})) | ||
} | ||
|
||
pub fn layer() -> impl layer::Layer<N, Service = Self> + Clone { | ||
layer::mk(Self::new) | ||
} | ||
} | ||
|
||
impl<T, K, N> NewService<T> for BackendCache<K, N, N::Service> | ||
where | ||
T: Param<params::Backends<K>>, | ||
K: Eq + Hash + Clone + Debug, | ||
N: NewService<K>, | ||
N::Service: Clone, | ||
{ | ||
type Service = NewDistribute<K, N::Service>; | ||
|
||
fn new_service(&self, target: T) -> Self::Service { | ||
let params::Backends(backends) = target.param(); | ||
|
||
let mut cache = self.0.backends.lock(); | ||
|
||
// Remove all backends that aren't in the updated set of addrs. | ||
cache.retain(|backend, _| { | ||
if backends.contains(backend) { | ||
true | ||
} else { | ||
tracing::debug!(?backend, "Removing"); | ||
false | ||
} | ||
}); | ||
|
||
// If there are additional addrs, cache a new service for each. | ||
debug_assert!(backends.len() >= cache.len()); | ||
if backends.len() > cache.len() { | ||
cache.reserve(backends.len()); | ||
for backend in &*backends { | ||
// Skip rebuilding targets we already have a stack for. | ||
if cache.contains_key(backend) { | ||
tracing::trace!(?backend, "Retaining"); | ||
continue; | ||
} | ||
|
||
tracing::debug!(?backend, "Adding"); | ||
cache.insert( | ||
backend.clone(), | ||
self.0.new_backend.new_service(backend.clone()), | ||
); | ||
} | ||
} | ||
|
||
NewDistribute::from(cache.clone()) | ||
} | ||
} | ||
|
||
impl<K, N, S> Clone for BackendCache<K, N, S> { | ||
fn clone(&self) -> Self { | ||
Self(self.0.clone()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters