"vscode:/vscode.git/clone" did not exist on "c66f663d1dc90dc490e3b67e99c8eb79cd50cdbd"
Commit 88ad3425 authored by Graham King's avatar Graham King Committed by GitHub
Browse files

feat: Python decorator dynamo_worker takes optional `static` parameter without etcd (#494)

Adds `@dynamo_worker(static = True)` to create a static worker which has a predictable name and hence does not require discovery or `etcd` to be running. There can only be a single static worker per namespace / component / endpoint trio.

This contrasts with the default dynamic `dynamo_worker` endpoints we have now, which get a unique random name (based on namespace/component/endpoint), and are discovered by ingress components using etcd.

Also change the hello_world example to use `dynamo_worker(static = True)` so that it is exercised and demonstrated somewhere.

For NIM.
parent bd8f0804
...@@ -26,25 +26,32 @@ use super::{error, Arc, DistributedRuntime, OnceCell, Result, Runtime, OK}; ...@@ -26,25 +26,32 @@ use super::{error, Arc, DistributedRuntime, OnceCell, Result, Runtime, OK};
use derive_getters::Dissolve; use derive_getters::Dissolve;
use figment::error; use figment::error;
use tokio_util::sync::CancellationToken;
impl DistributedRuntime { impl DistributedRuntime {
pub async fn new(runtime: Runtime, config: DistributedConfig) -> Result<Self> { pub async fn new(runtime: Runtime, config: DistributedConfig) -> Result<Self> {
let secondary = runtime.secondary(); let secondary = runtime.secondary();
let (etcd_config, nats_config) = config.dissolve(); let (etcd_config, nats_config, is_static) = config.dissolve();
let runtime_clone = runtime.clone(); let runtime_clone = runtime.clone();
let etcd_client = secondary let etcd_client = if is_static {
.spawn(async move { None
let client = etcd::Client::new(etcd_config.clone(), runtime_clone) } else {
.await Some(
.context(format!( secondary
"Failed to connect to etcd server with config {:?}", .spawn(async move {
etcd_config let client = etcd::Client::new(etcd_config.clone(), runtime_clone)
))?; .await
OK(client) .context(format!(
}) "Failed to connect to etcd server with config {:?}",
.await??; etcd_config
))?;
OK(client)
})
.await??,
)
};
let nats_client = secondary let nats_client = secondary
.spawn(async move { .spawn(async move {
...@@ -62,11 +69,18 @@ impl DistributedRuntime { ...@@ -62,11 +69,18 @@ impl DistributedRuntime {
nats_client, nats_client,
tcp_server: Arc::new(OnceCell::new()), tcp_server: Arc::new(OnceCell::new()),
component_registry: component::Registry::new(), component_registry: component::Registry::new(),
is_static,
}) })
} }
pub async fn from_settings(runtime: Runtime) -> Result<Self> { pub async fn from_settings(runtime: Runtime) -> Result<Self> {
let config = DistributedConfig::from_settings(); let config = DistributedConfig::from_settings(false);
Self::new(runtime, config).await
}
// Call this if you are using static workers that do not need etcd-based discovery.
pub async fn from_settings_without_discovery(runtime: Runtime) -> Result<Self> {
let config = DistributedConfig::from_settings(true);
Self::new(runtime, config).await Self::new(runtime, config).await
} }
...@@ -74,8 +88,10 @@ impl DistributedRuntime { ...@@ -74,8 +88,10 @@ impl DistributedRuntime {
&self.runtime &self.runtime
} }
pub fn primary_lease(&self) -> etcd::Lease { /// The etcd lease all our components will be attached to.
self.etcd_client.primary_lease() /// Not available for static workers.
pub fn primary_lease(&self) -> Option<etcd::Lease> {
self.etcd_client.as_ref().map(|c| c.primary_lease())
} }
pub fn shutdown(&self) { pub fn shutdown(&self) {
...@@ -84,7 +100,7 @@ impl DistributedRuntime { ...@@ -84,7 +100,7 @@ impl DistributedRuntime {
/// Create a [`Namespace`] /// Create a [`Namespace`]
pub fn namespace(&self, name: impl Into<String>) -> Result<Namespace> { pub fn namespace(&self, name: impl Into<String>) -> Result<Namespace> {
Namespace::new(self.clone(), name.into()) Namespace::new(self.clone(), name.into(), self.is_static)
} }
// /// Create a [`Component`] // /// Create a [`Component`]
...@@ -100,7 +116,12 @@ impl DistributedRuntime { ...@@ -100,7 +116,12 @@ impl DistributedRuntime {
// } // }
pub(crate) fn discovery_client(&self, namespace: impl Into<String>) -> DiscoveryClient { pub(crate) fn discovery_client(&self, namespace: impl Into<String>) -> DiscoveryClient {
DiscoveryClient::new(namespace.into(), self.etcd_client.clone()) DiscoveryClient::new(
namespace.into(),
self.etcd_client
.clone()
.expect("Attempt to get discovery_client on static DistributedRuntime"),
)
} }
pub(crate) fn service_client(&self) -> ServiceClient { pub(crate) fn service_client(&self) -> ServiceClient {
...@@ -123,22 +144,28 @@ impl DistributedRuntime { ...@@ -123,22 +144,28 @@ impl DistributedRuntime {
self.nats_client.clone() self.nats_client.clone()
} }
pub fn etcd_client(&self) -> etcd::Client { pub fn etcd_client(&self) -> Option<etcd::Client> {
self.etcd_client.clone() self.etcd_client.clone()
} }
pub fn child_token(&self) -> CancellationToken {
self.runtime.child_token()
}
} }
#[derive(Dissolve)] #[derive(Dissolve)]
pub struct DistributedConfig { pub struct DistributedConfig {
pub etcd_config: etcd::ClientOptions, pub etcd_config: etcd::ClientOptions,
pub nats_config: nats::ClientOptions, pub nats_config: nats::ClientOptions,
pub is_static: bool,
} }
impl DistributedConfig { impl DistributedConfig {
pub fn from_settings() -> DistributedConfig { pub fn from_settings(is_static: bool) -> DistributedConfig {
DistributedConfig { DistributedConfig {
etcd_config: etcd::ClientOptions::default(), etcd_config: etcd::ClientOptions::default(),
nats_config: nats::ClientOptions::default(), nats_config: nats::ClientOptions::default(),
is_static,
} }
} }
...@@ -146,6 +173,7 @@ impl DistributedConfig { ...@@ -146,6 +173,7 @@ impl DistributedConfig {
let mut config = DistributedConfig { let mut config = DistributedConfig {
etcd_config: etcd::ClientOptions::default(), etcd_config: etcd::ClientOptions::default(),
nats_config: nats::ClientOptions::default(), nats_config: nats::ClientOptions::default(),
is_static: false,
}; };
config.etcd_config.attach_lease = false; config.etcd_config.attach_lease = false;
......
...@@ -74,7 +74,7 @@ pub struct DistributedRuntime { ...@@ -74,7 +74,7 @@ pub struct DistributedRuntime {
runtime: Runtime, runtime: Runtime,
// we might consider a unifed transport manager here // we might consider a unifed transport manager here
etcd_client: transports::etcd::Client, etcd_client: Option<transports::etcd::Client>,
nats_client: transports::nats::Client, nats_client: transports::nats::Client,
tcp_server: Arc<OnceCell<Arc<transports::tcp::server::TcpStreamServer>>>, tcp_server: Arc<OnceCell<Arc<transports::tcp::server::TcpStreamServer>>>,
...@@ -84,4 +84,8 @@ pub struct DistributedRuntime { ...@@ -84,4 +84,8 @@ pub struct DistributedRuntime {
// a single endpoint watcher for both clients, this keeps the number background tasking watching specific // a single endpoint watcher for both clients, this keeps the number background tasking watching specific
// paths in etcd to a minimum. // paths in etcd to a minimum.
component_registry: component::Registry, component_registry: component::Registry,
// Will only have static components that are not discoverable via etcd, they must be know at
// startup. Will not start etcd.
is_static: bool,
} }
...@@ -36,26 +36,29 @@ impl Slug { ...@@ -36,26 +36,29 @@ impl Slug {
Slug::slugify_unique(s.as_ref()) Slug::slugify_unique(s.as_ref())
} }
// /// Turn the string into a valid slug, replacing any not-web-or-nats-safe characters with '-' /* Not currently used but leave it for now
// fn slugify(s: &str) -> Slug { *
// let out = s /// Turn the string into a valid slug, replacing any not-web-or-nats-safe characters with '-'
// .to_lowercase() pub fn slugify(s: &str) -> Slug {
// .chars() let out = s
// .map(|c| { .to_lowercase()
// let is_valid = c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-' || c == '_'; .chars()
// if is_valid { .map(|c| {
// c let is_valid = c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-' || c == '_';
// } else { if is_valid {
// REPLACEMENT_CHAR c
// } } else {
// }) REPLACEMENT_CHAR
// .collect::<String>(); }
// Slug::new(out) })
// } .collect::<String>();
Slug::new(out)
}
*/
/// Like slugify but also add a four byte hash on the end, in case two different strings slug /// Like slugify but also add a four byte hash on the end, in case two different strings slug
/// to the same thing. /// to the same thing.
fn slugify_unique(s: &str) -> Slug { pub fn slugify_unique(s: &str) -> Slug {
let out = s let out = s
.to_lowercase() .to_lowercase()
.chars() .chars()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment