-
Notifications
You must be signed in to change notification settings - Fork 198
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
Support including Module and Action in each JDBC session with Oracle JDBC #3183
base: 4.10.x
Are you sure you want to change the base?
Conversation
...java/io/micronaut/data/connection/jdbc/operations/DefaultDataSourceConnectionOperations.java
Outdated
Show resolved
Hide resolved
@@ -83,4 +83,29 @@ | |||
*/ | |||
boolean readOnly() default false; | |||
|
|||
/** |
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.
Since there is ConnectableInterceptor
that is creating custom ConnectionDefinition
I thought we could use this annotation for this purpose so users can customize module and action that will be traced. Don't see other way than use this annotation for the repository that needs to be traced. Without it, we cannot obtain details about class/method being executed when connection is established.
...onnection/src/main/java/io/micronaut/data/connection/interceptor/ConnectableInterceptor.java
Outdated
Show resolved
Hide resolved
Maybe we can introduce |
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 don't think this makes sense at the annotation level. You likely just want to configure it once for your whole application. I would refactor this logic to be something that can be configured.
Potentially you can add a strategy interface ConnectionCustomizer
or something that is invoked and implement the Oracle specific logic..
One or more customisers could be registered and ordered with @Ordered
...java/io/micronaut/data/connection/jdbc/operations/DefaultDataSourceConnectionOperations.java
Outdated
Show resolved
Hide resolved
...onnection/src/main/java/io/micronaut/data/connection/interceptor/ConnectableInterceptor.java
Outdated
Show resolved
Hide resolved
I thought users might want to customize module and action with some specific naming in some cases and not just use class name/method name. This is why I thought annotation could be used and also option to enable logging for just some classes. But, I agree it wouldn't be the easiest option to configure and something global might make sense.
or
|
I think we could use |
Still the problem persists, we must somehow populate JDBC client info parameters in
And |
ok does this info need to be set before any SQL is executed? |
I guess so, and it seemed that right place to do it is in |
Seems weird if that is the case, what if you want different actions per query |
according to https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_APPLICATION_INFO.html
So |
Then would do this for the method
since |
...a/io/micronaut/data/connection/jdbc/oracle/OracleClientInfoCustomizerConnectionListener.java
Outdated
Show resolved
Hide resolved
.../main/java/io/micronaut/data/connection/jdbc/oracle/OracleClientInfoCustomizerCondition.java
Outdated
Show resolved
Hide resolved
.../main/java/io/micronaut/data/connection/jdbc/oracle/OracleClientInfoCustomizerCondition.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Graeme Rocher <[email protected]>
Co-authored-by: Graeme Rocher <[email protected]>
Co-authored-by: Graeme Rocher <[email protected]>
Co-authored-by: Graeme Rocher <[email protected]>
…t info connection listener
} catch (SQLException e) { | ||
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", e); | ||
} | ||
|
||
return new DefaultConnectionStatus<>(connection, definition, true); |
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.
Why is this change?
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.
Because you suggested interface methods accept ConnectionStatus
and then creating it here and passing back to openConnection
Will revert 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.
I think it can be done without this change
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.
Yes, using existing internal method for open and introducing new for close.
/** | ||
* Opens a new connection. | ||
* | ||
* @param definition The connection definition | ||
* @return The connection | ||
*/ | ||
protected abstract C openConnection(ConnectionDefinition definition); | ||
protected final C openConnection(ConnectionDefinition definition) { |
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 you introduce something like openConnectionInternal
to avoid changes in the implementations?
protected abstract C openConnection(ConnectionDefinition definition); | ||
protected final C openConnection(ConnectionDefinition definition) { | ||
ConnectionStatus<C> connectionStatus = doOpenConnection(definition); | ||
for (ConnectionListener<C> connectionListener : connectionListeners) { |
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.
Bind listeners to using registerSynchronization
to avoid rechecks if the connection is supported
* @return An instance of {@link ConnectionClientInfoDetails} representing the connection client information, or null if not set. | ||
* @since 4.10 | ||
*/ | ||
@Nullable ConnectionClientInfoDetails connectionClientInfo(); |
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.
Please remove this; let it be fetched by the Oracle listener. Make ConnectionDefinition
AnnotationMetadataProvider
* @param appName The micronaut application name, null if not set | ||
* @return The connection client info or null if not configured to be used | ||
*/ | ||
private static @Nullable ConnectionClientInfoDetails getConnectionClientInfo(AnnotationValue<io.micronaut.data.connection.annotation.ConnectionClientInfo> annotation, |
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.
Move to oracle listener
* | ||
* @return the name of this listener | ||
*/ | ||
String getName(); |
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.
Add a default value to getClass().getSimpleName()
data-connection/src/main/java/io/micronaut/data/connection/annotation/ConnectionClientInfo.java
Outdated
Show resolved
Hide resolved
* | ||
* @since 4.10 | ||
*/ | ||
public @interface ConnectionClientInfoAttribute { |
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.
Make inner annotation of OracleConnectionClientInfo
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 don't think this needs to be renamed, it is not specific to Oracle
this.mongoClient = mongoClient; | ||
} | ||
|
||
@Override | ||
protected ClientSession openConnection(ConnectionDefinition definition) { | ||
return mongoClient.startSession(); | ||
protected ConnectionStatus<ClientSession> doOpenConnection(ConnectionDefinition definition) { |
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.
Please avoid changes in other connection operations
|
||
@Override | ||
public boolean supportsConnection(@NonNull ConnectionStatus<Connection> connectionStatus) { | ||
return connectionSupportedMap.computeIfAbsent(connectionStatus.getConnection(), this::isOracleConnection); |
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 creates a memory leak by collecting all the connections. After turning the connection definition to an annotation provider you should be able to check by simple hasAnnotation
.
Inject the datasource and do unwrap + instanceof to disable the listener permanently.
*/ | ||
@EachBean(DataSource.class) | ||
@Requires(condition = OracleClientInfoCondition.class) | ||
@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.
@EachBean(DataSource.class)
was not creating this bean until added @Context
. Not sure if this can cause some issues, I would prefer if it could work without @Context
but not sure what is missing.
...c/main/java/io/micronaut/data/connection/jdbc/oracle/OracleClientInfoConnectionListener.java
Outdated
Show resolved
Hide resolved
/** | ||
* @return whether connection should set client info | ||
*/ | ||
boolean enabled() default true; |
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.
What is the point of enabled? Just remove the annotation of you don't want to have the values set
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.
If class is annotated and maybe some methods want to be skipped. If you think it makes no sense, I will remove 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.
if it is removed it makes sense for ConnectionClientInfo
to become a repeatable container so you can just do
@ConnectionClientInfo(name="foo", value ="bar")
@ConnectionClientInfo(name="foo2", value ="baz")
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.
Changed, removed enabled and added repeatable.
Quality Gate passedIssues Measures |
public interface OracleXEAuthorRepository extends AuthorRepository { | ||
@Override | ||
@Join(value = "books", type = Join.Type.LEFT_FETCH) | ||
@ConnectionClientInfoAttribute(name = "OCSID.ACTION", value = "QueryAuthorByName") |
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 renamed the annotation to something ahorter
@Retention(RetentionPolicy.RUNTIME) | ||
@Repeatable(ConnClientInfo.class) | ||
@Connectable | ||
public @interface ConnClientInfoAttr { |
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 rename this to just @ClientInfo
(the word connection
is already in the package) and make the repeatable annotation an annotation named List
that is an inner type. See https://github.com/sergiy-naumovych/Professional-Java-for-Web-Applications/blob/master/Chapter%2021/Eclipse/Spring-JPA/source/production/java/com/wrox/site/validation/NotBlank.java for an example
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 repository looks super awkward
@JdbcRepository(dialect = Dialect.ORACLE)
@ClientInfo.List
public interface OracleXEAuthorRepository extends AuthorRepository {
@Override
@Join(value = "books", type = Join.Type.LEFT_FETCH)
@ClientInfo(name = "OCSID.ACTION", value = "QueryAuthorByName")
Author queryByName(String name);
}
Please suggest something new, I have no more ideas.
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.
why is the annotation needed on the type?
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.
Because from type we get module and don't have to annotate each method. Then module will be repository class name and action will be method name. But, it can be added on the method as well, for example if users want to name module/action differently.
I would be ok with @ClientInfo
and @ClientInfoAttr
(or @ClientInfoAttribute
)
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 can't it be:
@JdbcRepository(dialect = Dialect.ORACLE)
@ClientInfo(name = "OCSID.MODULE", value = "SomeModule")
public interface OracleXEAuthorRepository extends AuthorRepository {
@Override
@Join(value = "books", type = Join.Type.LEFT_FETCH)
@ClientInfo(name = "OCSID.ACTION", value = "QueryAuthorByName")
Author queryByName(String name);
}
Quality Gate passedIssues Measures |
Created as draft, not sure if this approach is correct