-
Notifications
You must be signed in to change notification settings - Fork 81
Add pavex_session_redis lib for Redis-based session management #498
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
Add pavex_session_redis lib for Redis-based session management #498
Conversation
|
First of all, thanks for picking this up!
I intend to abstract the underlying library by giving users pre-packaged constructors that go from a configuration type (e.g.
A dedicated configuration type is ideal, but it shouldn't include the connection configuration. As discussed above, the connection config will live elsewhere.
Let's add unit tests. In particular, we should cover:
|
libs/pavex_session/src/id.rs
Outdated
| pub fn as_bytes(&self) -> &[u8; 16] { | ||
| self.0.as_bytes() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to avoid this helper for now, you can get the raw byte representation via .inner().
libs/pavex_session_redis/Cargo.toml
Outdated
| async-trait = { workspace = true } | ||
| pavex = { version = "0.1.80", path = "../pavex" } | ||
| pavex_session = { version = "0.1.80", path = "../pavex_session" } | ||
| redis = { version = "0.31.0", features = ["tokio-comp", "aio", "connection-manager"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's convert it into a workspace dependency, for easier version management.
libs/pavex_session_redis/src/lib.rs
Outdated
| let ttl = match ttl_reply { | ||
| Value::Int(s) if s >= 0 => std::time::Duration::from_secs(s as u64), | ||
| Value::Int(-1) => { | ||
| panic!("Fatal session management error: no TTL set for this session.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't panic.
libs/pavex_session_redis/src/lib.rs
Outdated
| .map_err(LoadError::DeserializationError)?, | ||
| _ => { | ||
| return Err(LoadError::Other(anyhow::anyhow!( | ||
| "Redis GET replied {:?}. Expected bulk string.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may leak the stored data, which might contain sensitive information.
We should limit ourselves to stating which value type we got, rather than logging the value itself.
libs/pavex_session_redis/src/lib.rs
Outdated
| Value::Int(-2) => return Ok(None), | ||
| _ => { | ||
| return Err(LoadError::Other(anyhow::anyhow!( | ||
| "Redis TTL returned {:?}. Expected integer >= -2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as below, regarding privacy.
|
Hi @LukeMathWalker, thanks very much for the feedback above. I believe these commits address everything we've discussed. Worth calling out on this point regarding the dedicated configuration type
Given that the only use case I had in mind here was namespacing, a fully general key_generator seems on reflection like overkill and an unnecessary footgun, so I went ahead and implemented the For the tests, I have mostly copy-pasted from @oliverbarnes' work on #497 since the relevant tests carry over with the appropriate changes to the Happy to squash these commits if you'd prefer a cleaner history when its time to merge. |
|
Hey @joehasson, just a heads-up that I've made some changes to the tests today, based on Luca's feedback. Namely I've moved them to a separate file, and cleaned up a couple of superfluous assertions. I'm curious about your idea to abstract the session storage test suite, specially given I'm now implementing a very similar suite for MySQL on #499. ...though I have to say, coming from Rails/Rspec where sometimes people went a little overboard with test abstractions, that these days I have a lot more tolerance for test duplication =D (tangentially: "footgun" is a great word, thanks - first time I see it) |
Generics unfortunately won't be enough, since the default test runner for Rust is quite limited and requires |
|
Point well taken from both of you regarding test deduplication - seems more effort than it's worth on reflection.
I'll convert this from draft once I have updated and added documentation as mentioned before, unless there's any further comments. Thanks very much! |
381eebb to
6f4fffb
Compare
|
Documentation added as requested and rebased on latest main. Ready for review - thanks for your patience! |
| @@ -0,0 +1,380 @@ | |||
| use anyhow::Context; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add crate-level documentation, the one that will show up on crates.io when you look up this crate.
You can use the one for pavex_session_sqlx as a reference.
| /// Optional namespace prefix for Redis keys. When set, all session keys will be prefixed with this value. | ||
| #[serde(default)] | ||
| pub namespace: Option<String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's include an example, showing the structure of a namespaced key (i.e. that : is used as separator).
| } | ||
| } | ||
|
|
||
| pub struct RedisSessionKit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no longer a need for kits. It's enough to add a couple of attributes to get thing working with the new DI system.
You can use either pavex.dev/docs or the MySQL PR as a reference on this matter.
|
I've resolved the remaining issues in #533, which has now been merged. Thanks for all your work! |
Hey, I'm working on #466 following the patterns used in the postgres-based session store. I have a working implementation and there's still some work to do polish this (mainly tests + documentation) but I'd like to get some pointers around architectural patterns beforehand, any guidance is much appreciated.
RedisSessionStoreconstructor with anewmethod accepting aredis::aio::ConnectionManager. This is analogous to the situation with thePostgresSessionStoreconstructor in the postgres implementation which accepts asqlx::PgPool. This means that the user of these libraries is forced to construct these types and hence to depend directly onredis-rs/sqlx. I'm wondering whether it would be better, at least in the redis case, if the dependency was instead abstracted away? We could instead just require the user to supply connection strings rather than pre-constructed clients.One way to address both of these points might be to have the
RedisSessionStore::newfunction instead accept a struct of the following type, which we would require the user to instantiate:Does this seem reasonable? (If we definitely want to expose redis-rs as a dependency this struct could store a
ConnectionManagerinstead of a connection string).Many thanks for any input on the above questions and any other comments on the PR.