pub struct MySqlSessionStore(/* private fields */);Expand description
A server-side session store using MySQL as its backend.
§Implementation details
This store uses sqlx to interact with MySQL.
All session records are stored in a single table. You can use
migrate to create the table and index
required by the store in the database.
Alternatively, you can use migration_query
to get the SQL query that creates the table and index in order to run it yourself
(e.g. as part of your database migration scripts).
§MySQL version requirements
This implementation requires MySQL 5.7.8+ or MariaDB 10.2+ for JSON support. For optimal performance with JSON operations, MySQL 8.0+ is recommended.
Implementations§
Source§impl MySqlSessionStore
impl MySqlSessionStore
Sourcepub fn new(pool: MySqlPool) -> Self
pub fn new(pool: MySqlPool) -> Self
Creates a new MySQL session store instance.
It requires a pool of MySQL connections to interact with the database where the session records are stored.
Sourcepub fn migration_query() -> &'static str
pub fn migration_query() -> &'static str
Return the query used to create the sessions table and index.
§Implementation details
The query is designed to be idempotent, meaning it can be run multiple times without causing any issues. If the table and index already exist, the query does nothing.
§MySQL version requirements
This query requires MySQL 5.7.8+ or MariaDB 10.2+ for JSON column support.
§Alternatives
You can use this method to add the query to your database migration scripts.
Alternatively, you can use migrate
to run the query directly on the database.
Sourcepub async fn migrate(&self) -> Result<(), Error>
pub async fn migrate(&self) -> Result<(), Error>
Create the sessions table and index in the database.
This method is idempotent, meaning it can be called multiple times without causing any issues. If the table and index already exist, this method does nothing.
If you prefer to run the query yourself, rely on migration_query
to get the SQL that’s being executed.
Trait Implementations§
Source§impl Clone for MySqlSessionStore
impl Clone for MySqlSessionStore
Source§fn clone(&self) -> MySqlSessionStore
fn clone(&self) -> MySqlSessionStore
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for MySqlSessionStore
impl Debug for MySqlSessionStore
Source§impl From<MySqlSessionStore> for SessionStore
impl From<MySqlSessionStore> for SessionStore
Source§fn from(value: MySqlSessionStore) -> Self
fn from(value: MySqlSessionStore) -> Self
Source§impl SessionStorageBackend for MySqlSessionStore
impl SessionStorageBackend for MySqlSessionStore
Source§fn create<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
record: SessionRecordRef<'life2>,
) -> Pin<Box<dyn Future<Output = Result<(), CreateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn create<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
record: SessionRecordRef<'life2>,
) -> Pin<Box<dyn Future<Output = Result<(), CreateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Creates a new session record in the store using the provided ID. When a conflicting session id is present, we perform a simple upsert. This is a deliberate decision given we can’t return an error and keep atomicity at the same time.
Even when using a guard clause, which we’d expect to amount to a noop:
ON DUPLICATE KEY UPDATE deadline = IF(sessions.deadline < UNIX_TIMESTAMP(), VALUES(deadline), sessions.deadline), state = IF(sessions.deadline < UNIX_TIMESTAMP(), VALUES(state), sessions.state)
affected_rows() is still non-zero. This seems to be a kink in MySQL.
Source§fn update<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
record: SessionRecordRef<'life2>,
) -> Pin<Box<dyn Future<Output = Result<(), UpdateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn update<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
record: SessionRecordRef<'life2>,
) -> Pin<Box<dyn Future<Output = Result<(), UpdateError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Update the state of an existing session in the store.
It overwrites the existing record with the provided one.
Source§fn update_ttl<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
ttl: Duration,
) -> Pin<Box<dyn Future<Output = Result<(), UpdateTtlError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn update_ttl<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
ttl: Duration,
) -> Pin<Box<dyn Future<Output = Result<(), UpdateTtlError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Update the TTL of an existing session record in the store.
It leaves the session state unchanged.
Source§fn load<'life0, 'life1, 'async_trait>(
&'life0 self,
session_id: &'life1 SessionId,
) -> Pin<Box<dyn Future<Output = Result<Option<SessionRecord>, LoadError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn load<'life0, 'life1, 'async_trait>(
&'life0 self,
session_id: &'life1 SessionId,
) -> Pin<Box<dyn Future<Output = Result<Option<SessionRecord>, LoadError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Loads an existing session record from the store using the provided ID.
If a session with the given ID exists, it is returned. If the session
does not exist or has been invalidated (e.g., expired), None is
returned.
Source§fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
) -> Pin<Box<dyn Future<Output = Result<(), DeleteError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 SessionId,
) -> Pin<Box<dyn Future<Output = Result<(), DeleteError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Deletes a session record from the store using the provided ID.
If the session exists, it is removed from the store.
Source§fn change_id<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
old_id: &'life1 SessionId,
new_id: &'life2 SessionId,
) -> Pin<Box<dyn Future<Output = Result<(), ChangeIdError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn change_id<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
old_id: &'life1 SessionId,
new_id: &'life2 SessionId,
) -> Pin<Box<dyn Future<Output = Result<(), ChangeIdError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Change the session id associated with an existing session record.
The server-side state is left unchanged.
Source§fn delete_expired<'life0, 'async_trait>(
&'life0 self,
batch_size: Option<NonZeroUsize>,
) -> Pin<Box<dyn Future<Output = Result<usize, DeleteExpiredError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn delete_expired<'life0, 'async_trait>(
&'life0 self,
batch_size: Option<NonZeroUsize>,
) -> Pin<Box<dyn Future<Output = Result<usize, DeleteExpiredError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Delete expired sessions from the database.
If batch_size is provided, the query will delete at most batch_size expired sessions.
In either case, if successful, the method returns the number of expired sessions that
have been deleted.
§When should you delete in batches?
If there are a lot of expired sessions in the database, deleting them all at once can cause performance issues. By deleting in batches, you can limit the number of sessions deleted in a single query, reducing the impact.
§Example
Delete expired sessions in batches of 1000:
use pavex_session::SessionStore;
use pavex_session_sqlx::MySqlSessionStore;
use pavex_tracing::fields::{
error_details,
error_message,
ERROR_DETAILS,
ERROR_MESSAGE
};
use std::time::Duration;
let backend = MySqlSessionStore::new(pool);
let store = SessionStore::new(backend);
let batch_size = Some(1000.try_into().unwrap());
let batch_sleep = Duration::from_secs(60);
loop {
if let Err(e) = store.delete_expired(batch_size).await {
tracing::event!(
tracing::Level::ERROR,
{ ERROR_MESSAGE } = error_message(&e),
{ ERROR_DETAILS } = error_details(&e),
"Failed to delete a batch of expired sessions",
);
}
tokio::time::sleep(batch_sleep).await;
}Auto Trait Implementations§
impl Freeze for MySqlSessionStore
impl !RefUnwindSafe for MySqlSessionStore
impl Send for MySqlSessionStore
impl Sync for MySqlSessionStore
impl Unpin for MySqlSessionStore
impl !UnwindSafe for MySqlSessionStore
Blanket Implementations§
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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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 more§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling [Attribute] value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi [Quirk] value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the [Condition] value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);