DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
| ID | IEP-126129 | ||||||||||
| Author | |||||||||||
| Sponsor | Unknown User (nizhikov) | ||||||||||
| Created | 10.09.2024 | ||||||||||
| Status |
|
| Table of Contents |
|---|
When processing data, the user wants to have access to client attributes - an associative array specified on the client application side. Usage scenarios:
CacheInterceptor extracts the user from the attrubutes and saves it along with the data).QuerySqlFunction use the same parameter and it is convenient to take it from the attributes, rather than transfer it separately to each function).The requirements also include:
QuerySqlFunction, CacheInterceptor).Ignite does not have a similar public API:
ServiceCallContext helps solve similar problems, but has several limitations. The main one is that it is only available in calls via the IgniteService API and cannot be used for single SQL queries.UserAttributes - allows to set user attributes, but the attribute values are fixed for the entire lifetime of the connection.A similar context mechanism exists in Oracle [1].
Code running on the Ignite node has access to client attributes through a static method call:
| Code Block | ||
|---|---|---|
| ||
/** SessionContext interface */
public class SessionContext {
/** @return Client attributes set for current thread. */
public static @Nullable Map<String, String> getClientAttributes();
}
/** Example, use it in QuerySqlFunction. */
public static class UserDefinedFunctions {
/** @return Session ID, client attribute. */
@QuerySqlFunction
public static String sessionId() {
Map<String, String> clnAttrs = SessionContext.getClientAttributes();
return clnAttrs == null ? null : clnInfo.getProperty(SESSION_ID);
}
}
|
SessionContext can be further extended to retrieve other non-tabular information, such as SecuritySubject, Transaction, ServiceCallContext, etc.
Ignite provides a few options to set custom attributes on client side: per call (ServiceCallContext) and per connection (UserAttributes). Now there is no opportunity to set attributes on the "application level". Scenario is:
pplicationAttributes. Business logic might depend on such attributes - application language, application name for audit, etc ([2], [4]).It's proposed to provide the opportunity to set attributes for application. Then there will be hierarchy of attributes:
ServiceCallContextApplicationAttributesUserAttributes, SecuritySubjectThe access to the attributes must be provided for functions that are pre-created on Ignite cluster and aren't deployed by specific application. Examples:
CacheInterceptor saves the SecuritySubject along with data and it can be used for fine-grained access control in QuerySqlFunction.Service extracts attribute from ServiceCallContext and, if absent, get default one from ApplicationAttributes.It's proposed to introduce SessionContext API that is available from the functions and provides access to the attributes in order (call, application, connection).
Requirements:
Ignite, IgniteClient, JDBC).QuerySqlFunctionCacheInterceptorServiceComputeTask, ComputeJobAttributes API that aren't part of the IEP:
ClientListenerConnectionContext#attributes - it's actually how client stores UserAttribute in connection context.ComputeTaskSession#setAttribute - this mechanism is available only within Compute API. It allows attributes to be changed during task lifetime, use-case is different - send sync/async notifications between jobs about events happened during task lifetime.Similar mechanisms in other DBMSs:
Ignite#withApplicationAttributes. It returns Ignite instance that is aware of the attributes.GridIoSecurityAwareMessage)| Code Block | ||
|---|---|---|
| ||
// Example of usage.
try (Ignite ign = Ignition.start(ignCfg)) {
Map<String, String> appAttrs = F.asMap("SESSION_ID", "1234");
Ignite app = Ignite.withApplicationAttributes(appAttrs);
...
}
|
IgniteClient#withApplicationAttributes.ClientBitmaskFeature protocol.| Code Block | ||
|---|---|---|
| ||
// Example of usage.
try (IgniteClient cln = Ignition.startClient(clnCfg)) {
Map<String, String> appAttrs = F.asMap("SESSION_ID", "1234");
IgniteClient app = cln.withApplicationAttributes(appAttrs);
...
}
|
The standard jdbc protocol describes the methods Connection#setClientInfo, which allow changing the values of client attributes during the life of the connection [3]. Implementation features:
#setClientInfo is arbitrary. The documentation recommends strictly limiting the set of attributes, but this is not necessary. For example, Oracle does not have such a limitation [5]. The setClientInfo array is completely translated into the getClientAttributes array ApplicationAttributes.setClientInfo are passed along with each JdbcRequest (this requires a new feature in JdbcThinFeature)....
| Code Block | ||
|---|---|---|
| ||
// JDBC connection to Ignite server.
try (Connection conn = DriverManager.getConnection(URL)) {
conn.setClientInfo("SESSION_ID", "1234");
...
}
|
Implementation features:
SessionContext is an entrypoint for accessing attributes on all levels and other session-level data (e.g. SecurutySubject). Ignite provides SessionContextProviderResource to access to SessionContext.
CacheInterceptor or QuerySqlFunction target class for every call (every row or key).SessionContext from Ignite thread.QuerySqlFunction to inject the resourceClientAttributes setting to the current thread. In case of using listeners, it is necessary to reinstall the context in a new thread.withClientAttributes call are sent along with each outgoing TcpClientChannel#send message in the BinaryOutputStream (a new feature is needed in the ClientBitmaskFeature protocol).| Code Block | ||
|---|---|---|
| ||
try (IgniteClient cln = Ignition.startClient(clnCfg))/** SessionContext interface. It's an entrypoint for all attributes. */ public class SessionContext { try (ClientAttributes clnAttrs = cln.withClientAttributes()) {/** @return Attribute by name. */ public @Nullable String clnAttrs.setAttribute("SESSION_ID", "1234"getAttribute(String attrName); /** @return Current ... SecuritySubject. */ public @Nullable SecuritySubject getSecuritySubject(); } } |
The Ignite server node, when receiving client messages, sets ClientAttributes in the ThreadLocal variable, and also handles context transfer to the calls it spawns (in threads and on remote nodes). The same can be done for operations called directly from Ignite:
| Code Block | ||
|---|---|---|
| ||
try (Ignite ign = Ignition.localIgnite()) /** Example, use it in QuerySqlFunction. */ public static class UserDefinedFunctions { /** tryInjection (ClientAttributesperforms clnAttrsonce =per ignquery.withClientAttributes()) { */ @SessionContextProviderResource clnAttrs.setAttribute("SESSION_ID", "1234"); public SessionContextProvider sesCtxProv; /** @return Session ID set with application attributes. */ @QuerySqlFunction public @Nullable String sessionId() { return sesCtxProv...getSessionContext().getAttribute("SESSION_ID"); } } |
QueryStartRequest SQL message protocol.CacheInterceptor), since inserts are not always performed on the request initiator node.GridNearAtomicAbstractUpdateRequest messages about key changes must contain the ClientAttributes id.CacheInterceptor#onGet is not called in SQL)ClientAttributes on the client)// Describe project risks, such as API or binary compatibility issues, major protocol changes, etc.
// Links to discussions on the devlist, if applicable.
// Links to various reference documents, if applicable.
|
https://lists.apache.org/thread/c5j5yykr6tfrnp9zprhg9j69w63r11zx
[4] https://torofimofu.blogspot.com/2014/05/oracle.html
[5] https://docs.oracle.com/database/121/JJDBC/jdbcvers.htm#BABDAEEC
[6] https://stackoverflow.com/search?tab=newest&q=SYS_CONTEXT
[7] https://docs.snowflake.com/en/sql-reference/session-variables#session-variable-functions
[9] https://dev.mysql.com/doc/refman/8.4/en/user-variables.html
| Jira | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|