# AWS Secrets Manager Rust Caching Client ![CI](https://github.com/adamjq/aws-secretsmanager-cache-rust/actions/workflows/ci.yml/badge.svg) This crate provides a client for in-process caching of secrets from AWS Secrets Manager for Rust applications. It's heavily inspired by the [AWS Secrets Manager Go Caching Client](https://github.com/aws/aws-secretsmanager-caching-go) and the [AWS SDK for Rust](https://github.com/awslabs/aws-sdk-rust). The client internally uses an LRU (least-recently used) caching scheme that provides O(1) insertions and O(1) lookups for cached values. ## Getting started To use this client you must have: - A Rust development environment - An Amazon Web Services (AWS) account to access secrets stored in AWS Secrets Manager and use AWS SDK for Rust. ## Usage The following sample demonstrates how to get started using the client: ```rust use aws_sdk_secretsmanager::Client; use aws_secretsmanager_cache::SecretCache; #[tokio::main] async fn main() { // instantiate an AWS SecretsManager client using the AWS Rust SDK let aws_config = aws_config::from_env().load().await; let client = Client::new(&aws_config); let mut cache = SecretCache::new(client); match cache.get_secret_string("YOUR_SECRET_ID".to_string()).send().await { Ok(secret_value) => { // use secret value } // e.g. ResourceNotFoundException: Secrets Manager can't find the specified secret. Err(e) => println!("ERROR: {}", e), } } ``` ### Forcing cache refreshes If a secret has been rotated since the last value was fetched and cached, and hasn't expired in the cache, it's necessary to force a cache refresh for the value by calling AWS and updating the value. This can be done with `force_refresh()`, for example: ```rust match cache .get_secret_string("YOUR_SECRET_ID".to_string()) .force_refresh() .send() .await ``` ## Cache Configuration - `max_cache_size usize` The maximum number of secrets to maintain in the cache before evicting the least frequently accessed - `cache_item_ttl u128` The number of nanoseconds a cached secret will be considered valid before the secret value requires a refresh. Refreshing happens synchronously. ```rust use aws_sdk_secretsmanager::Client; use aws_secretsmanager_cache::{CacheConfig, SecretCache}; use std::time; #[tokio::main] async fn main() { let aws_config = aws_config::from_env().load().await; let client = Client::new(&aws_config); // cache configuration with 30 second expiry time and maximum 1000 secrets let cache_config = CacheConfig::new() .cache_item_ttl(time::Duration::from_secs(30).as_nanos()) .max_cache_size(1000); let mut cache = SecretCache::new_with_config(client, cache_config); } ``` ## Global Caching Certain cloud environments like AWS Lambda encourage initializing clients in the global scope to avoid initialization for each function invocation. This can be achieved using the `lazy_static` crate, for example: ```rust use async_once::AsyncOnce; use aws_sdk_secretsmanager::Client; use aws_secretsmanager_cache::SecretCache; use lazy_static::lazy_static; use std::sync::Mutex; // store the cache in the global scope - useful for runtime environments like AWS Lambda lazy_static! { static ref CACHE: AsyncOnce> = AsyncOnce::new(async { Mutex::new(SecretCache::new(Client::new( &aws_config::from_env().load().await, ))) }); } #[tokio::main] async fn main() { // use cache } ``` ## Development ### Linting The project uses [rustfmt](https://github.com/rust-lang/rustfmt) and [clippy](https://github.com/rust-lang/rust-clippy) for formatting and linting. Follow the instructions to install `rustfmt` and `clippy` and run: ```bash cargo fix ``` ### Tests Run unit tests locally with: ```bash cargo test ``` ## License Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) or the [MIT license](https://opensource.org/licenses/MIT), at your option. Files in the project may not be copied, modified, or distributed except according to those terms.