Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stackable-operator): Add cache structure #943

Merged
merged 16 commits into from
Jan 29, 2025
Merged
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ darling = "0.20.10"
delegate = "0.13.0"
dockerfile-parser = "0.9.0"
ecdsa = { version = "0.16.9", features = ["digest", "pem"] }
educe = { version = "0.6.0", default-features = false, features = ["Clone", "Debug", "Default", "PartialEq"] }
educe = { version = "0.6.0", default-features = false, features = ["Clone", "Debug", "Default", "PartialEq", "Eq"] }
either = "1.13.0"
futures = "0.3.30"
futures-util = "0.3.30"
Expand Down
6 changes: 6 additions & 0 deletions crates/stackable-operator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- Add generic `TtlCache` structure as well as a `UserInformationCache` type ([#943]).

[#943]: https://github.com/stackabletech/operator-rs/pull/943

## [0.85.0] - 2025-01-28

### Changed
Expand Down
111 changes: 111 additions & 0 deletions crates/stackable-operator/src/commons/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::marker::PhantomData;

use educe::Educe;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::time::Duration;

/// [`TtlCache`] with sensible defaults for a user information cache
pub type UserInformationCache = TtlCache<UserInformationCacheDefaults>;

/// Default tunings for [`UserInformationCache`].
#[derive(JsonSchema)]
pub struct UserInformationCacheDefaults;

impl TtlCacheDefaults for UserInformationCacheDefaults {
fn entry_time_to_live() -> Duration {
Duration::from_secs(30)
}

fn max_entries() -> u32 {
10_000
}
}

/// Structure to configure a TTL cache in a product.
#[derive(Deserialize, Educe, JsonSchema, Serialize)]
#[serde(
rename_all = "camelCase",
bound(deserialize = "D: TtlCacheDefaults", serialize = "D: TtlCacheDefaults")
)]
#[schemars(
description = "Least Recently Used (LRU) cache with per-entry time-to-live (TTL) value.",
// We don't care about the fields, but we also use JsonSchema to derive the name for the composite type
bound(serialize = "D: TtlCacheDefaults + JsonSchema")
)]
#[educe(
Clone(bound = false),
Debug(bound = false),
PartialEq(bound = false),
Eq
)]
pub struct TtlCache<D> {
/// Time to live per entry; Entries which were not queried within the given duration, are
/// removed.
#[serde(default = "D::entry_time_to_live")]
pub entry_time_to_live: Duration,

/// Maximum number of entries in the cache; If this threshold is reached then the least
/// recently used item is removed.
#[serde(default = "D::max_entries")]
pub max_entries: u32,

#[serde(skip)]
pub _defaults: PhantomData<D>,
}

impl<D: TtlCacheDefaults> Default for TtlCache<D> {
fn default() -> Self {
Self {
entry_time_to_live: D::entry_time_to_live(),
max_entries: D::max_entries(),
_defaults: PhantomData,
}
}
}

/// A set of default values for [`TtlCache`].
///
/// This is extracted to a separate trait in order to be able to provide different
/// default tunings for different use cases.
pub trait TtlCacheDefaults {
/// The default TTL the entries should have.
fn entry_time_to_live() -> Duration;
/// The default for the maximum number of entries
fn max_entries() -> u32;
}

#[cfg(test)]
mod tests {
use super::*;

type MyCache = TtlCache<UserInformationCacheDefaults>;

#[test]
fn test_defaults() {
let my_cache: MyCache = Default::default();
assert_eq!(my_cache.entry_time_to_live, Duration::from_secs(30));
assert_eq!(my_cache.max_entries, 10_000);
}

#[test]
fn test_deserialization_defaults() {
let my_cache: MyCache = serde_json::from_str("{}").unwrap();

assert_eq!(my_cache.entry_time_to_live, Duration::from_secs(30));
assert_eq!(my_cache.max_entries, 10_000);
}

#[test]
fn test_deserialization() {
let my_cache: MyCache = serde_yaml::from_str("entryTimeToLive: 13h\n").unwrap();

assert_eq!(
my_cache.entry_time_to_live,
Duration::from_hours_unchecked(13)
);
// As the field is not specified we default
assert_eq!(my_cache.max_entries, 10_000);
}
}
1 change: 1 addition & 0 deletions crates/stackable-operator/src/commons/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pub mod affinity;
pub mod authentication;
pub mod cache;
pub mod cluster_operation;
pub mod listener;
pub mod networking;
Expand Down