-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
HHH-19849 Add an SPI that allows attaching session-scoped "extensions" to the session/statelesssession implementors #11093
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
base: main
Are you sure you want to change the base?
Conversation
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.
LGTM, though we definitely need Steve's opinion here.
Perhaps this is over-thinking, but what do you think about something like this instead: /** For now, just a marker */
interface Extension {
}
interface ExtensionStorage {
...
}
interface SharedSessionContractImplementor {
...
ExtensionStorage getExtensionStorage(Class<? extends Extension> extension);
} |
It looks good, but on the other hand I'm not sure Perhaps you were thinking of something specific Envers could leverage? |
Let's avoid stringly-typed |
72bd0e7
to
a2dec7d
Compare
Thanks, that might work 🙂. Just made an adjustment to the pr 🙂 In Search, we have a session and a session holder, we connect the holder to the ORM's session. |
Object storage = extensionStorages.get( extension );; | ||
if ( storage == null ) { | ||
try { | ||
storage = extension.getDeclaredConstructor().newInstance(); |
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 just heard GraalVM cry a little. Do we really need this?
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.
🫣 well... not really, at least not for Search 🙂.
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.
Then let's not! SPI users can figure it out :)
if ( extensionStorages == null ) { | ||
extensionStorages = new HashMap<>(); | ||
} | ||
Object storage = extensionStorages.get( extension );; |
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.
Object storage = extensionStorages.get( extension );; | |
Object storage = extensionStorages.get( extension ); |
@Message("Collection fetched") | ||
void collectionFetched(); | ||
|
||
@Message("Failed to create a storage of type %s: %s") |
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.
@Message("Failed to create a storage of type %s: %s") | |
@Message("Failed to create extension storage of type %s: %s") |
Object storage = extensionStorages.get( extension ); | ||
if ( storage == null ) { | ||
storage = createIfMissing.get(); | ||
extensionStorages.put( extension, storage ); | ||
} |
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.
Perf tip:
Object storage = extensionStorages.get( extension ); | |
if ( storage == null ) { | |
storage = createIfMissing.get(); | |
extensionStorages.put( extension, storage ); | |
} | |
Object storage = extensionStorages.computeIfAbsent( extension, k -> supplier.get() ); |
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.
Really, this performs better?
That's the code I would use by default (because it's obviously cleaner) but I've had people complain that it doesn't perform well due to the extra lambda.
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.
yeah... I didn't want to create unnecessary lambdas, but if you are saying this should perform better 👍🏻 🙂 thanks!
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.
ha 🙂, now @mbellade we need to benchmark it 😁
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.
so I got curious how much that lambda would hurt ...
SimpleTest.computeIfAbsent thrpt 56198.917 ± 254.380 ops/ms
SimpleTest.get thrpt 77517.446 ± 497.622 ops/ms
I'll keep the get for now 🫣 🙂
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'm guessing this was run with the same key every time, such that put
is only invoked once. The instantiation cost of the lambda of course will have an impact, but accessing an hash-map twice (one get
and one put
) should be much worse if the entry doesn't exist yet.
So it really depends on the use-case :)
Edit: if there wasn't a Supplier
but an Object
instead, putIfAbsent
would be the winner regardless of the use case.
…storage" to the session/statelesssession implementors
a2dec7d
to
cc6fc2a
Compare
Here specifically I was thinking about avoiding String keys for a few reasons. But I was thinking also that we've spoken a few times about "true extensions" (for lack of a better phrase) for other projects to expose via Session (Envers reader e.g.). So partially I was thinking about that. So maybe even something like this: interface SharedSessionContractImplementor {
...
<E extends Extension> E getExtensionStorage(Class<E> extensionType);
} which would rely on projects registering this mechanism with ORM. E.g. for Search: class SearchExtension implements Extension {
// access to storage
}
SearchExtension ext = session.getExtension( SearchExtension.class );
ext.storeSomething( ... ); For envers: class EnversExtension implements Extension {
AuditReader getAssociatedAuditReader();
...
}
... Just brainstorming Marko, no need to make changes yet :) I know this is well beyond the scope of the issue here, but I think its worth considering. |
I think Marko already did pretty much what you're suggesting here, give or take a few names 😅 |
Yes and no. I'm actually I am thinking something akin to Gradle's notion of extensions. interface Extension {
}
/** Registered with SessionFactory */
interface ExtensionIntegration<E extends Extension> {
Class<E> getExtensionType();
E createExtension(SharedSessionContractImplementor session, ???);
}
interface SharedSessionContractImplementor {
...
<E extends Extension> E getExtension(Class<E> extensionType);
} |
https://hibernate.atlassian.net/browse/HHH-19849
so that Search wouldn't need to rely on
.getProperties()
and abuse it 😄.I'm not "attached" 🙂 to the method names and if there are any suggestions for better alternatives, happy to change them 🙂
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license
and can be relicensed under the terms of the LGPL v2.1 license in the future at the maintainers' discretion.
For more information on licensing, please check here.