diff --git a/README.md b/README.md index ebc4214..7cce628 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,11 @@ $ cargo add async-mongodb-session ``` ## Overview -By default this library utilises the document expiration feature based on [specific clock time](https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time) supported by mongodb to auto-expire the session. +This library utilises the document expiration feature in mongodb and is based on [specific clock time](https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time). -For other option to offloading the session expiration to the mongodb layer check the [Advance options](#advance-options). +The expiry index is applied to the collection when a new session object is created. + +As document expiration is a [background task](https://docs.mongodb.com/manual/core/index-ttl/#timing-of-the-delete-operation) some stale sessions may be present in the database but won't be returned by this library. ## Example with tide Create an HTTP server that keep track of user visits in the session. @@ -95,47 +97,6 @@ async fn main() -> tide::Result<()> { Ok(()) } ``` -## Advance options -a [specified number of seconds](https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-after-a-specified-number-of-seconds) or in a - -The specified number of seconds approach is designed to enable the session time out to be managed at the mongodb layer. This approach provides a globally consistent session timeout across multiple processes but has the downside that all services using the same session collection must use the same timeout value. - -The specific clock time clock time approach is where you require more flexibility on your session timeouts such as a different session timeout per running service or you would prefer to manage the session time out at the process level. This is more flexible but might lead to some perceived inconsistency in session timeout depending on your upgrade/rollout strategy. - -The management of the expiry feature fits into the 12 factor [admin process definintion](https://12factor.net/admin-processes) so it's recommended to use an process outside of your web application to manage the expiry parameters. - -## Manual configuration - -A `created` property is available on the root of the session document that so the [expiry feature](https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-after-a-specified-number-of-seconds) can be used in the configuration. - -If your application code to create a session store is something like: -``` -let store = MongodbSessionStore::connect("mongodb://127.0.0.1:27017", "db_name", "coll_session").await?; -``` - -Then the script to create the expiry would be: -``` -use db_name; -db.coll_session.createIndex( { "created": 1 } , { expireAfterSeconds: 300 } ); -``` - -If you wish to redefine the session duration then the index must be dropped first using: -``` -use db_name; -db.coll_session.dropIndex( { "created": 1 }) -db.coll_session.createIndex( { "created": 1 } , { expireAfterSeconds: 300 } ); -``` - -Other way to set create the index is using `index_on_created` passing the amount of seconds to expiry after the session. - -Also, an `expireAt` property is available on the root of the session document IFF the session expire is set. Note that [async-session doesn't set by default](https://github.com/http-rs/async-session/blob/main/src/session.rs#L98). - -To enable this [expiry feature](https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time) at `index` for `expireAt` should be created calling `index_on_expiry_at` function or with this script ( following the above example ) - -``` -use db_name; -db.coll_session.createIndex( { "expireAt": 1 } , { expireAfterSeconds: 0 } ); -``` ## Test diff --git a/src/lib.rs b/src/lib.rs index f7819ed..37ec7e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ use async_session::chrono::{Duration, Utc}; use async_session::{async_trait, Result, Session, SessionStore}; use mongodb::bson; -use mongodb::bson::doc; +use mongodb::bson::{doc, Bson}; use mongodb::options::{ReplaceOptions, SelectionCriteria}; use mongodb::Client; @@ -28,7 +28,6 @@ pub struct MongodbSessionStore { client: mongodb::Client, db: String, coll_name: String, - ttl: usize, } impl MongodbSessionStore { @@ -72,7 +71,6 @@ impl MongodbSessionStore { client, db: db.to_string(), coll_name: coll_name.to_string(), - ttl: 1200, // 20 mins by default. } } @@ -95,34 +93,6 @@ impl MongodbSessionStore { Ok(()) } - /// Get the default ttl value in seconds. - /// ```rust - /// # fn main() -> async_session::Result { async_std::task::block_on(async { - /// # use async_mongodb_session::MongodbSessionStore; - /// let store = - /// MongodbSessionStore::new("mongodb://127.0.0.1:27017", "db_name", "collection") - /// .await?; - /// let ttl = store.ttl(); - /// # Ok(()) }) } - /// ``` - pub fn ttl(&self) -> usize { - self.ttl - } - - /// Set the default ttl value in seconds. - /// ```rust - /// # fn main() -> async_session::Result { async_std::task::block_on(async { - /// # use async_mongodb_session::MongodbSessionStore; - /// let mut store = - /// MongodbSessionStore::new("mongodb://127.0.0.1:27017", "db_name", "collection") - /// .await?; - /// store.set_ttl(300); - /// # Ok(()) }) } - /// ``` - pub fn set_ttl(&mut self, ttl: usize) { - self.ttl = ttl; - } - /// private associated function /// Create an `expire after seconds` index in the provided field. /// Testing is covered by initialize test. @@ -151,25 +121,6 @@ impl MongodbSessionStore { Ok(()) } - /// Create a new index for the `created` property and set the expiry ttl (in secods). - /// The session will expire when the number of seconds in the expireAfterSeconds field has passed - /// since the time specified in its created field. - /// https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-after-a-specified-number-of-seconds - /// ```rust - /// # fn main() -> async_session::Result { async_std::task::block_on(async { - /// # use async_mongodb_session::MongodbSessionStore; - /// let store = - /// MongodbSessionStore::new("mongodb://127.0.0.1:27017", "db_name", "collection") - /// .await?; - /// store.index_on_created(300).await?; - /// # Ok(()) }) } - /// ``` - pub async fn index_on_created(&self, expire_after_seconds: u32) -> Result { - self.create_expire_index("created", expire_after_seconds) - .await?; - Ok(()) - } - /// Create a new index for the `expireAt` property, allowing to expire sessions at a specific clock time. /// If the `expireAt` date field contains a date in the past, mongodb considers the document expired and will be deleted. /// https://docs.mongodb.com/manual/tutorial/expire-data/#expire-documents-at-a-specific-clock-time @@ -220,6 +171,14 @@ impl SessionStore for MongodbSessionStore { Some(v) => v.clone(), None => return Ok(None), }; + // mongodb runs the background task that removes expired documents runs every 60 seconds. + // https://docs.mongodb.com/manual/core/index-ttl/#timing-of-the-delete-operation + // This prevents those documents being returned + if let Some(expiry_at) = doc.get("expireAt").and_then(Bson::as_datetime) { + if expiry_at < &Utc::now() { + return Ok(None); + } + } Ok(Some(bson::from_bson::(bsession)?)) } } diff --git a/tests/test.rs b/tests/test.rs index f1a477f..16379c3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -108,9 +108,7 @@ mod tests { let cookie_value = store.store_session(session).await?.unwrap(); - // mongodb runs the background task that removes expired documents runs every 60 seconds. - // https://docs.mongodb.com/manual/core/index-ttl/#timing-of-the-delete-operation - task::sleep(Duration::from_secs(60)).await; + task::sleep(Duration::from_secs(1)).await; let session_to_recover = store.load_session(cookie_value).await?; assert!(&session_to_recover.is_none());