-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Selecting custom caching strategy #4161
Conversation
97afa88
to
34ef18c
Compare
Thanks for opening this PR and working on this issue. That written: I'm not fond of the chosen approach. I personally do not see why rebuilding the statement isn't an option. Yes it might fail for PostgreSQL in a transaction, but on the other hand there is so many other things that might file and render a transaction unusable that I don't feel that this really matters. If you care about such cases you must implement a transaction retry logic anyway. There are several things to consider with the proposed solution, that I personally not a fan of:
I'm sorry if that sounds like I want to dismiss your work, but I just have different ideas in mind on how to approach that problem. I personally would have preferred to discuss the design of this feature first before posting a PR, as that would likely result in much less frustration on both sides. |
Since this is not a huge feature, I like to start discussion from some point. Real implementations usually have some subtle nuances and you also see full scope, these things are hard to discuss up-front. One important thing, regarding transactions. YMMV, but for us transactions fail only for network issues or breaking-scheme-change issues. Breaking-scheme-change we solve by properly managing migrations in non-breaking change manner, the rest (network issues) we simply propagate to upstream, as technical problems (which might be retried later or not). Before discussing further points, I think that we need to agree on one fundamental point first:
|
I'm open to allow configuring the cache, but I would like to make that option connection specific rather than global. So just That written: I would prefer to have the automatic retry for statements nevertheless as at least I do want to use the statement cache whenever possible as it gives a quite large performance boost. |
Let me think out lout :).
To achieve that, migration process could be implemented like this:
In order to implement caching behavior at runtime, we would wrap r2d2 connection pool into our own and every time we get connection from the pool, we would I probably it's a bit more work on end user side (to wrap connection), but actually that would solve our problem :) So I guess we'll come back to you with updated revision ;) |
That sounds like a good summary, thanks for writing it up. I think it would be also fine to have an additional function that just empties the current query cache. |
dbe25cc
to
95084a6
Compare
Some comments:
Happy reviewing and waiting for your comments :) |
6235344
to
340c893
Compare
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.
Thanks for the update. This is now much closer to what I would expect in terms of public API. I've left a bunch of mostly minor comments. The only major thing I would like to see changed is how this feature is tested. Instead of having the tests "everywhere" I would like to focus them on only this feature inside of the strategy
module.
340c893
to
3d5462d
Compare
3d5462d
to
b783552
Compare
I think I have addressed all your comments. |
diesel/src/pg/connection/mod.rs
Outdated
@@ -124,7 +126,8 @@ pub(super) use self::result::PgResult; | |||
#[allow(missing_debug_implementations)] | |||
#[cfg(feature = "postgres")] | |||
pub struct PgConnection { | |||
statement_cache: StatementCache<Pg, Statement>, | |||
/// pub(crate) for tests | |||
pub(crate) statement_cache: StatementCache<Pg, Statement>, |
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.
That's not required anymore, right?
diesel/src/sqlite/connection/stmt.rs
Outdated
@@ -15,7 +15,7 @@ use std::io::{stderr, Write}; | |||
use std::os::raw as libc; | |||
use std::ptr::{self, NonNull}; | |||
|
|||
pub(super) struct Statement { | |||
pub(crate) struct Statement { |
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.
That shouldn't be required anymore.
diesel/src/sqlite/connection/mod.rs
Outdated
@@ -121,7 +121,8 @@ pub struct SqliteConnection { | |||
// statement_cache needs to be before raw_connection | |||
// otherwise we will get errors about open statements before closing the | |||
// connection itself | |||
statement_cache: StatementCache<Sqlite, Statement>, | |||
// pub(crate) for tests | |||
pub(crate) statement_cache: StatementCache<Sqlite, Statement>, |
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.
That change shouldn't be required anymore?
connection.begin_test_transaction().unwrap(); | ||
|
||
crate::sql_query( | ||
"CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);", |
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.
Can we use CREATE TEMPORARY TABLE
statements here instead? That prevents possible conflicts.
|
||
#[cfg(test)] | ||
#[cfg(feature = "postgres")] | ||
mod tests_pg { |
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.
Would it be possible to add another test that shows that disabling the cache has an effect?
insertable::Insertable, | ||
macros::table, | ||
pg::Pg, | ||
sql_types::{Integer, VarChar}, |
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 don't have such nested imports in diesel otherwise. I would prefer to keep the style consistent.
Thanks for the update. I really like the solution to test this via the instrumentation tooling. |
Bencher
Click to view all benchmark results
Bencher - Continuous Benchmarking View Public Perf Page Docs | Repo | Chat | Help |
Bencher
Click to view all benchmark results
Bencher - Continuous Benchmarking View Public Perf Page Docs | Repo | Chat | Help |
Bencher
Click to view all benchmark results
Bencher - Continuous Benchmarking View Public Perf Page Docs | Repo | Chat | Help |
Hi, @weiznich |
Hello,
Following #4058 discussion (about statement caches), I want to propose a possible solution.
Since rebuilding statement cache on error is not an option (at least for PostgreSQL). The only convenient option that I see, is to give control to end user, to decide how he wants to use statement cache (of course user can implement it's own connection type, but that is not convenient option, as most likely user also needs to implement r2d2 traits as well).
There's the core idea:
StatementCacheStrategy
trait. To implementations are already provided:WithCacheStrategy
(default),WithoutCacheStrategy
.set_cache_strategy
globally per connection type (because traits are generic over DB and Statement).i-implement-a-third-party-backend-and-opt-into-breaking-changes
feature flags).Notable changes:
ConnectionStatementCache
that is implemented by all connections which will use statement cache. This trait has two purposes: 1) exposes functionality for end user to set custom caching strategy, 2)StatementCache
use this, to actually get current caching strategy. Another option was to implement this directly onConnection
, but that would require adding newtype Statement
associated type parameter, and I didn't wanted to deal withMultiConnection
macro.len
fromStatementCache
. It was used by PostgreSQL implementation to generate statement cache names, but this might easily break if user provides it's own implementation for statement cache. Instead,StatementCache
implementation provide acache_counter: Option<u64>
that is incremented every time if statement will be cached, or None otherwise.IntrospectCachingStrategy
for tests only (behindcfg(test)
), that wraps actualStatementCacheStrategy
and records outcome. This is used in tests to check if tests related to statement caching behave correctly. (previouslylen
field fromStatementCache
was utilized, but this is more precise).statement_cache
is public (instead of being behindi-implement-a-third-party-backend-and-opt-into-breaking-changes
).