pub struct SecretSyncer<B: SecretBackend, E: KeyEncryptor + Clone, const V: usize = 256, const S: usize = 32> { /* private fields */ }Expand description
Background task that keeps an InMemorySecretGroup up-to-date by polling storage.
SecretSyncer is the read side of the key-management system. It:
- Initial load — calls
SecretBackend::load_allonce at startup to hydrate the ring buffer and promote the most-recently-activated key ascurrent. - Poll loop — periodically calls
SecretBackend::poll_newto pick up keys added after the cursor. Keys withactivated_atin the future are stored in the ring but only promoted tocurrentonce their activation time arrives (via a spawned timer task).
A hash-based dedup cache prevents redundant KeyEncryptor::decrypt calls when the same
ciphertext is seen again (e.g. after a service restart or a backend re-delivery).
The poll interval is adaptive: if a rotation is expected soon (based on rotation_interval),
the syncer wakes earlier so it picks up the new key promptly; otherwise it sleeps for the
configured poll_interval.
§Type parameters
B— backend that implementsSecretBackendE— encryptor that implementsKeyEncryptorV— ring buffer size (must match theInMemorySecretGrouppassed in, default 256)S— key size in bytes (default 32)
§Standalone use
Use SecretSyncer directly when your instances should only read keys, not rotate them:
let group = Arc::new(InMemorySecretGroup::<256, 32>::new(0, [0u8; 32]));
let mut syncer: SecretSyncer<_, _, 256, 32> = SecretSyncer::new(
"session-tokens",
Arc::clone(&group),
backend,
encryptor,
Duration::from_secs(3600),
None,
);
let token = CancellationToken::new();
let cursor = syncer.initial_load(&token).await?;
tokio::spawn(syncer.run(token, cursor));Implementations§
Source§impl<B: SecretBackend, E: KeyEncryptor + Clone, const V: usize, const S: usize> SecretSyncer<B, E, V, S>
impl<B: SecretBackend, E: KeyEncryptor + Clone, const V: usize, const S: usize> SecretSyncer<B, E, V, S>
Sourcepub fn new(
group_id: impl Into<String>,
secret: Arc<InMemorySecretGroup<V, S>>,
backend: B,
encryptor: E,
rotation_interval: Duration,
poll_interval: Option<Duration>,
) -> Self
pub fn new( group_id: impl Into<String>, secret: Arc<InMemorySecretGroup<V, S>>, backend: B, encryptor: E, rotation_interval: Duration, poll_interval: Option<Duration>, ) -> Self
Create a new SecretSyncer.
§Arguments
group_id— identifies the logical key group in storagesecret— the in-memory ring buffer to keep populatedbackend— implementsSecretBackendencryptor— used to decrypt ciphertext from storage before placing keys in the ringrotation_interval— expected time between rotations; used to compute a smart early wake-up before the next key is due, reducing promotion latencypoll_interval— base polling cadence;Noneuses the 5-second default
Sourcepub async fn initial_load(
&mut self,
token: &CancellationToken,
) -> Result<(SystemTime, i64), B::Error>
pub async fn initial_load( &mut self, token: &CancellationToken, ) -> Result<(SystemTime, i64), B::Error>
Load all existing keys from storage and hydrate the ring buffer.
Must be called once before run. Returns a cursor
(max_activated_at, max_id) that marks the newest record seen; pass this directly to
run so the poll loop starts from where the initial load left off.
Keys already present in the ring are not re-decrypted (hash dedup). Keys with
activated_at in the future are stored but not yet promoted; a timer task is spawned
for each to promote them at the right moment.
The token parameter is threaded through only for future cancellability of long-running
initial loads; it is not yet acted upon inside the method body.
Sourcepub async fn run(self, token: CancellationToken, cursor: (SystemTime, i64))
pub async fn run(self, token: CancellationToken, cursor: (SystemTime, i64))
Run the poll loop until token is cancelled.
Consumes self; pass to tokio::spawn after calling initial_load.
On backend errors the syncer backs off for 30 seconds before retrying. Decryption errors for individual records are logged and skipped; the loop continues.
Auto Trait Implementations§
impl<B, E, const V: usize, const S: usize> Freeze for SecretSyncer<B, E, V, S>
impl<B, E, const V: usize, const S: usize> RefUnwindSafe for SecretSyncer<B, E, V, S>where
B: RefUnwindSafe,
E: RefUnwindSafe,
impl<B, E, const V: usize, const S: usize> Send for SecretSyncer<B, E, V, S>
impl<B, E, const V: usize, const S: usize> Sync for SecretSyncer<B, E, V, S>
impl<B, E, const V: usize, const S: usize> Unpin for SecretSyncer<B, E, V, S>
impl<B, E, const V: usize, const S: usize> UnsafeUnpin for SecretSyncer<B, E, V, S>where
B: UnsafeUnpin,
E: UnsafeUnpin,
impl<B, E, const V: usize, const S: usize> UnwindSafe for SecretSyncer<B, E, V, S>where
B: UnwindSafe,
E: UnwindSafe,
Blanket Implementations§
Source§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
Source§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read moreSource§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read moreSource§fn aggregate_filter<P>(self, f: P) -> Self::Output
fn aggregate_filter<P>(self, f: P) -> Self::Output
Source§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoSql for T
impl<T> IntoSql for T
Source§fn into_sql<T>(self) -> Self::Expression
fn into_sql<T>(self) -> Self::Expression
self to an expression for Diesel’s query builder. Read moreSource§fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
&self to an expression for Diesel’s query builder. Read moreSource§impl<T, Conn> RunQueryDsl<Conn> for T
impl<T, Conn> RunQueryDsl<Conn> for T
Source§fn execute<'conn, 'query>(
self,
conn: &'conn mut Conn,
) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
fn execute<'conn, 'query>( self, conn: &'conn mut Conn, ) -> <Conn as AsyncConnectionCore>::ExecuteFuture<'conn, 'query>
Source§fn load<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
fn load<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
Source§fn load_stream<'conn, 'query, U>(
self,
conn: &'conn mut Conn,
) -> Self::LoadFuture<'conn>where
Conn: AsyncConnectionCore,
U: 'conn,
Self: LoadQuery<'query, Conn, U> + 'query,
fn load_stream<'conn, 'query, U>(
self,
conn: &'conn mut Conn,
) -> Self::LoadFuture<'conn>where
Conn: AsyncConnectionCore,
U: 'conn,
Self: LoadQuery<'query, Conn, U> + 'query,
Stream] with the returned rows. Read moreSource§fn get_result<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
fn get_result<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, LoadNext<Pin<Box<Self::Stream<'conn>>>>>
Source§fn get_results<'query, 'conn, U>(
self,
conn: &'conn mut Conn,
) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
fn get_results<'query, 'conn, U>( self, conn: &'conn mut Conn, ) -> AndThen<Self::LoadFuture<'conn>, TryCollect<Self::Stream<'conn>, Vec<U>>>
Vec with the affected rows. Read more