Skip to content

Commit

Permalink
feat: add a configuration object for the cache client (#176)
Browse files Browse the repository at this point in the history
* feat: add a configuration object for the cache client

Add a nested configuration object in the style of the other SDKs.

Add pre-built configurations for different environments.
  • Loading branch information
nand4011 authored Mar 11, 2024
1 parent 33fd9f5 commit 2ee3227
Show file tree
Hide file tree
Showing 10 changed files with 374 additions and 8 deletions.
27 changes: 22 additions & 5 deletions src/cache_client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::config::configuration::Configuration;
use crate::grpc::header_interceptor::HeaderInterceptor;
use crate::requests::cache::set_add_elements::{SetAddElements, SetAddElementsRequest};
use crate::requests::cache::MomentoRequest;
Expand All @@ -11,16 +12,21 @@ use tonic::transport::Channel;

pub struct CacheClient {
pub(crate) data_client: ScsClient<InterceptedService<Channel, HeaderInterceptor>>,
pub(crate) configuration: Configuration,
item_default_ttl: Duration,
}

impl CacheClient {
/* constructor */
pub fn new(
credential_provider: CredentialProvider,
configuration: Configuration,
default_ttl: Duration,
) -> MomentoResult<Self> {
let data_channel = utils::connect_channel_lazily(&credential_provider.cache_endpoint)?;
let data_channel = utils::connect_channel_lazily_configurable(
&credential_provider.cache_endpoint,
configuration.transport_strategy.grpc_configuration.clone(),
)?;

let data_interceptor = InterceptedService::new(
data_channel,
Expand All @@ -29,6 +35,7 @@ impl CacheClient {
let data_client = ScsClient::new(data_interceptor);
Ok(CacheClient {
data_client,
configuration,
item_default_ttl: default_ttl,
})
}
Expand All @@ -39,14 +46,19 @@ impl CacheClient {
/// # fn main() -> anyhow::Result<()> {
/// # tokio_test::block_on(async {
/// use std::time::Duration;
/// use momento::{CredentialProviderBuilder};
/// use momento::config::configurations;
/// use momento::CredentialProviderBuilder;
/// use momento::requests::cache::set_add_elements::SetAddElements;
///
/// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string())
/// .build()?;
/// let cache_name = "cache";
///
/// let cache_client = momento::CacheClient::new(credential_provider, Duration::from_secs(5))?;
/// let cache_client = momento::CacheClient::new(
/// credential_provider,
/// configurations::laptop::latest(),
/// Duration::from_secs(5),
///)?;
///
/// let set_add_elements_response = cache_client.set_add_elements(cache_name.to_string(), "set", vec!["element1", "element2"]).await?;
/// assert_eq!(set_add_elements_response, SetAddElements {});
Expand All @@ -71,14 +83,19 @@ impl CacheClient {
/// use momento::requests::cache::set_add_elements::SetAddElementsRequest;
/// tokio_test::block_on(async {
/// use std::time::Duration;
/// use momento::{CredentialProviderBuilder};
/// use momento::config::configurations;
/// use momento::CredentialProviderBuilder;
/// use momento::requests::cache::set_add_elements::SetAddElements;
///
/// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string())
/// .build()?;
/// let cache_name = "cache";
///
/// let cache_client = momento::CacheClient::new(credential_provider, Duration::from_secs(5))?;
/// let cache_client = momento::CacheClient::new(
/// credential_provider,
/// configurations::laptop::latest(),
/// Duration::from_secs(5),
///)?;
///
/// let set_add_elements_response = cache_client.send_request(
/// SetAddElementsRequest::new(cache_name.to_string(), "set", vec!["element1", "element2"])
Expand Down
62 changes: 62 additions & 0 deletions src/config/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::time::Duration;

use crate::config::transport_strategy::TransportStrategy;

/// Configuration for a Momento cache client.
///
/// Static, versioned configurations are provided for different environments:
/// ```
/// use momento::config::configurations;
///
/// /// Use laptop for local development
/// let developer_config = configurations::laptop::latest();
/// /// Use in_region for a typical server environment
/// let server_config = configurations::in_region::v1();
/// ```
/// If you have specific requirements, configurations can also be constructed manually:
/// ```
/// use std::time::Duration;
/// use momento::config::configuration::Configuration;
/// use momento::config::grpc_configuration::{GrpcConfiguration, GrpcConfigurationBuilder};
/// use momento::config::transport_strategy::TransportStrategy;
///
/// let config = Configuration::builder(
/// TransportStrategy::builder(
/// GrpcConfiguration::builder(Duration::from_millis(1000)).build(),
/// )
/// .build(),
/// )
/// .build();
#[derive(Clone)]
pub struct Configuration {
/// Low-level options for network interactions with Momento.
pub(crate) transport_strategy: TransportStrategy,
}

impl Configuration {
pub fn builder(transport_strategy: TransportStrategy) -> ConfigurationBuilder {
ConfigurationBuilder { transport_strategy }
}

/// Returns the duration the client will wait before terminating an RPC with a DeadlineExceeded error.
pub fn deadline_millis(&self) -> Duration {
self.transport_strategy.grpc_configuration.deadline
}
}

pub struct ConfigurationBuilder {
transport_strategy: TransportStrategy,
}

impl ConfigurationBuilder {
pub fn with_transport_strategy(mut self, transport_strategy: TransportStrategy) -> Self {
self.transport_strategy = transport_strategy;
self
}

pub fn build(self) -> Configuration {
Configuration {
transport_strategy: self.transport_strategy,
}
}
}
138 changes: 138 additions & 0 deletions src/config/configurations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/// Provides defaults suitable for a medium-to-high-latency dev environment. Permissive timeouts
/// and relaxed latency and throughput targets.
pub mod laptop {
use std::time::Duration;

use crate::config::configuration::Configuration;
use crate::config::grpc_configuration::GrpcConfiguration;
use crate::config::transport_strategy::TransportStrategy;

/// Latest recommended config for a laptop development environment.
///
/// NOTE: this config may change in future releases to take advantage of improvements
/// we identify for default configurations.
pub fn latest() -> Configuration {
v1()
}

/// V1 config for a laptop development environment.
///
/// This config is guaranteed not to change in future releases of the Momento Rust SDK.
pub fn v1() -> Configuration {
Configuration::builder(
TransportStrategy::builder(
GrpcConfiguration::builder(Duration::from_millis(15000))
.with_keep_alive_while_idle(true)
.with_keep_alive_interval(Duration::from_secs(5000))
.with_keep_alive_timeout(Duration::from_secs(1000))
.build(),
)
.build(),
)
.build()
}
}

/// Provides defaults suitable for an environment where your client is running in the same
/// region as the Momento service. It has more aggressive timeouts than the laptop config.
pub mod in_region {
use std::time::Duration;

use crate::config::configuration::Configuration;
use crate::config::grpc_configuration::GrpcConfiguration;
use crate::config::transport_strategy::TransportStrategy;

/// Latest recommended config for a typical in-region environment.
///
/// NOTE: this config may change in future releases to take advantage of improvements
/// we identify for default configurations.
pub fn latest() -> Configuration {
v1()
}

/// V1 config for a typical in-region environment.
///
/// This config is guaranteed not to change in future releases of the Momento Rust SDK.
pub fn v1() -> Configuration {
Configuration::builder(
TransportStrategy::builder(
GrpcConfiguration::builder(Duration::from_millis(1100))
.with_keep_alive_while_idle(true)
.with_keep_alive_interval(Duration::from_secs(5000))
.with_keep_alive_timeout(Duration::from_secs(1000))
.build(),
)
.build(),
)
.build()
}
}

/// This config prioritizes keeping p99.9 latencies as low as possible, potentially sacrificing
/// some throughput to achieve this. Use this config if low latency is more important in
/// your application than cache availability.
pub mod low_latency {
use std::time::Duration;

use crate::config::configuration::Configuration;
use crate::config::grpc_configuration::GrpcConfiguration;
use crate::config::transport_strategy::TransportStrategy;

/// Latest recommended config for a low-latency environment.
///
/// NOTE: this config may change in future releases to take advantage of improvements
/// we identify for default configurations.
pub fn latest() -> Configuration {
v1()
}

/// V1 config for a low-latency environment.
///
/// This config is guaranteed not to change in future releases of the Momento Rust SDK.
pub fn v1() -> Configuration {
Configuration::builder(
TransportStrategy::builder(
GrpcConfiguration::builder(Duration::from_millis(500))
.with_keep_alive_while_idle(true)
.with_keep_alive_interval(Duration::from_secs(5000))
.with_keep_alive_timeout(Duration::from_secs(1000))
.build(),
)
.build(),
)
.build()
}
}

/// Provides defaults suitable for a typical lambda environment. It has more aggressive timeouts
/// than the laptop config and does not check connection health with a keep-alive.
pub mod lambda {
use std::time::Duration;

use crate::config::configuration::Configuration;
use crate::config::grpc_configuration::GrpcConfiguration;
use crate::config::transport_strategy::TransportStrategy;

/// Latest recommended config for a typical lambda environment.
///
/// NOTE: this config may change in future releases to take advantage of improvements
/// we identify for default configurations.
pub fn latest() -> Configuration {
v1()
}

/// V1 config for a typical lambda environment.
///
/// This config is guaranteed not to change in future releases of the Momento Rust SDK.
pub fn v1() -> Configuration {
Configuration::builder(
TransportStrategy::builder(
GrpcConfiguration::builder(Duration::from_millis(1100))
.with_keep_alive_while_idle(false)
.build(),
)
.build(),
)
.build()
}
}
73 changes: 73 additions & 0 deletions src/config/grpc_configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::time::Duration;

/// Low-level gRPC settings for communicating with Momento.
#[derive(Clone)]
pub struct GrpcConfiguration {
/// The duration the client is willing to wait for an RPC to complete before it is terminated
/// with a DeadlineExceeded error.
pub(crate) deadline: Duration,
/// Indicates whether the client should send keep-alive pings.
///
/// NOTE: keep-alives are very important for long-lived server environments where there may be
/// periods of time when the connection is idle. However, they are very problematic for lambda
/// environments where the lambda runtime is continuously frozen and unfrozen, because the
/// lambda may be frozen before the "ACK" is received from the server. This can cause the
/// keep-alive to timeout even though the connection is completely healthy. Therefore,
/// keep-alives should be disabled in lambda and similar environments.
pub(crate) keep_alive_while_idle: bool,
/// The interval at which keep-alive pings are sent.
pub(crate) keep_alive_interval: Duration,
/// The duration the client is willing to wait for a keep-alive ping to be acknowledged before
/// closing the connection.
pub(crate) keep_alive_timeout: Duration,
}

impl GrpcConfiguration {
pub fn builder(deadline: Duration) -> GrpcConfigurationBuilder {
GrpcConfigurationBuilder {
deadline,
keep_alive_while_idle: true,
keep_alive_interval: Duration::from_secs(5000),
keep_alive_timeout: Duration::from_secs(1000),
}
}
}

/// Builder for `GrpcConfiguration`.
pub struct GrpcConfigurationBuilder {
deadline: Duration,
keep_alive_while_idle: bool,
keep_alive_interval: Duration,
keep_alive_timeout: Duration,
}

impl GrpcConfigurationBuilder {
pub fn with_deadline(mut self, deadline: Duration) -> Self {
self.deadline = deadline;
self
}

pub fn with_keep_alive_while_idle(mut self, keep_alive_while_idle: bool) -> Self {
self.keep_alive_while_idle = keep_alive_while_idle;
self
}

pub fn with_keep_alive_interval(mut self, keep_alive_interval: Duration) -> Self {
self.keep_alive_interval = keep_alive_interval;
self
}

pub fn with_keep_alive_timeout(mut self, keep_alive_timeout: Duration) -> Self {
self.keep_alive_timeout = keep_alive_timeout;
self
}

pub fn build(self) -> GrpcConfiguration {
GrpcConfiguration {
deadline: self.deadline,
keep_alive_while_idle: self.keep_alive_while_idle,
keep_alive_interval: self.keep_alive_interval,
keep_alive_timeout: self.keep_alive_timeout,
}
}
}
8 changes: 8 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// Configuration for the Momento cache client.
pub mod configuration;
/// Pre-built configurations for the Momento cache client.
pub mod configurations;
/// Low-level gRPC settings for communicating with Momento.
pub mod grpc_configuration;
/// Low-level settings for communicating with Momento.
pub mod transport_strategy;
31 changes: 31 additions & 0 deletions src/config/transport_strategy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::config::grpc_configuration::GrpcConfiguration;

/// Low-level settings for communicating with Momento.
#[derive(Clone)]
pub struct TransportStrategy {
/// Low-level gRPC settings for communicating with Momento.
pub(crate) grpc_configuration: GrpcConfiguration,
}

impl TransportStrategy {
pub fn builder(grpc_configuration: GrpcConfiguration) -> TransportStrategyBuilder {
TransportStrategyBuilder { grpc_configuration }
}
}

pub struct TransportStrategyBuilder {
grpc_configuration: GrpcConfiguration,
}

impl TransportStrategyBuilder {
pub fn with_grpc_configuration(mut self, grpc_configuration: GrpcConfiguration) -> Self {
self.grpc_configuration = grpc_configuration;
self
}

pub fn build(self) -> TransportStrategy {
TransportStrategy {
grpc_configuration: self.grpc_configuration,
}
}
}
Loading

0 comments on commit 2ee3227

Please sign in to comment.