Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.


IDIEP-126129
Author
SponsorUnknown User (nizhikov) 
Created 10.09.2024
Status

Status
colour

Grey

Yellow
title

DRAFT

ACTIVE


Table of Contents

Motivation

When processing data, the user wants to have access to client attributes - an associative array specified on the client application side. Usage scenarios:

  1. To run certain business logic that depends on the value of the attribute (for example, the application language [2]).
  2. To audit changes in the database (for example, CacheInterceptor extracts the user from the attributes and saves it along with data).
  3. To optimize the transfer of parameters to processing functions (for example, multiple 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:

  1. Attributes must also be accessible from user functions (QuerySqlFunction, CacheInterceptor).
  2. Setting attributes via protocols: thick client, thin client, jdbc:thin.
  3. Attribute values ​​can change during the life of the connection (a connection from a connection pool can be sequentially used by different applications).

Ignite does not have a similar public API:

  1. 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.
  2. 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].

Description

Accessing attributes

...

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:

  1. Application acquires Ignite connect from connection pool.
  2. Application need specify ApplicationAttributes. Business logic might depend on such attributes - application language, application name for audit, etc ([2], [4]).
  3. Another application re-uses this connect with own attributes values.

It's proposed to provide the opportunity to set attributes for application. Then there will be hierarchy of attributes:

  1. Call level - ServiceCallContext
  2. Application level - ApplicationAttributes
  3. Connection level - UserAttributes, SecuritySubject

The 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:

  1. The attributes ​​are set by application only once for Ignite API entry point (Ignite, IgniteClient, JDBC).
  2. The attributes are available on all participating nodes of the application requests and queries.
  3. The attributes must be accessible in pre-defined functions:
    1. QuerySqlFunction
    2. CacheInterceptor
    3. Service
    4. ComputeTask, ComputeJob

Attributes API that aren't part of the IEP:

  1. ClientListenerConnectionContext#attributes - it's actually how client stores UserAttribute in connection context.
  2. 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:

  1. Application, Client contexts mechanisms exist in Oracle [1] and they are widely used [6].
  2. Custom session variables exist in DBMS like SQLServer [8], Snowflake [7], MySQL [9].

Description

Setting application attributes

Ignite 

  1. Users set attributes with Ignite#withApplicationAttributes. It returns Ignite instance that is aware of the attributes.
  2. Application attributes are propagated with all messages sent within operation on remote nodes (similar to GridIoSecurityAwareMessage)


Code Block
languagejava
/**/ SessionContextExample interfaceof */
public class SessionContextusage.
try (Ignite ign = Ignition.start(ignCfg)) {
    /** @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. */
Map<String, String> appAttrs = F.asMap("SESSION_ID", "1234");

	Ignite app = Ignite.withApplicationAttributes(appAttrs);	

 	...
}

IgniteClient (Java) 

  1. Users set attributes with IgniteClient#withApplicationAttributes.
  2. New feature is needed in the ClientBitmaskFeature protocol.


Code Block
languagejava
// Example of usage.
try (IgniteClient cln = Ignition.startClient(clnCfg))    @QuerySqlFunction
    public static String sessionId() {
        Map<String, String> clnAttrsappAttrs = SessionContextF.getClientAttributes(asMap("SESSION_ID", "1234");
 
        return clnAttrs == null ? null : clnInfo.getProperty(SESSION_ID);
    }	IgniteClient app = cln.withApplicationAttributes(appAttrs);	

 	...
}

SessionContext can be further extended to retrieve other non-tabular information, such as SecuritySubject, Transaction, ServiceCallContext, etc.

Setting attributes

JDBC

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:

  1. The list of attributes that can be set using #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.
  2. The parameters set in setClientInfo are passed along with each JdbcRequest (this requires a new feature in JdbcThinFeature).
  3. The client must reset the set values ​​itself.

...

Code Block
languagejava
// JDBC connection to Ignite server.
try (Connection conn = DriverManager.getConnection(URL)) {
    conn.setClientInfo("SESSION_ID", "1234");
 
    ...
}

IgniteClient (Java)

Implementation features:

Accessing attributes

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.

  1. Injecting the ProviderResource doesn't require new instance of CacheInterceptor or QuerySqlFunction target class for every call (every row or key).
  2. Provider implementation is responsible for extracting SessionContext from Ignite thread.
  3. Note, it proposes to make possible use non-static QuerySqlFunction to inject the resource
  4. The Java implementation of the thin client allows working in a multi-threaded environment, unlike the jdbc connection. Then it is suggested to bind the ClientAttributes setting to transaction.
  5. It's required to extend ClientTransactions interface with withClientAttributes method, similar to withLabel.
  6. New feature is needed in the ClientBitmaskFeature protocol).

Code Block
languagejava
// Extends the interface with new public method.
public interface ClientTransactions {
	...

    ** SessionContext interface. It's an entrypoint for all attributes. */
public class SessionContext {
    /** @param@return clnAttrsAttribute Clientby attributesname. */
    public @Nullable ClientTransactionsString withClientAttributesgetAttribute(Map<String, String> clnAttrsString attrName);
}

// Example of usage.
try (IgniteClient cln = Ignition.startClient(clnCfg)) {
    Map<String, String> clnAttrs = F.asMap("SESSION_ID", "1234");

    try (ClientTransaction tx = cln.transactions().withClientAttributes(clnAttrs).txStart())     /** @return Current SecuritySubject. */
	public @Nullable SecuritySubject getSecuritySubject();
 }
 
/** Example, use it in QuerySqlFunction. */
public static class UserDefinedFunctions {
    /** Injection performs once ...per query. */
    }
}

Ignite 

Same as IgniteClient - attach the attributes to transaction:

Code Block
languagejava
// Extends the interface with new public method.
public interface IgniteTransactions {
 	...

@SessionContextProviderResource
    public SessionContextProvider sesCtxProv;

    /** @param clnAttrs Client@return Session ID set with application attributes. */
    public IgniteTransactions withClientAttributes(Map<String, String> clnAttrs);
}

// Example of usage.
try (Ignite ign = Ignition.localIgnite()) {
@QuerySqlFunction
    public @Nullable String sessionId() {     Map<String, String> clnAttrs = F.asMap("SESSION_ID", "1234");
 
     try (Transaction tx = ign.transactions 
		return sesCtxProv.getSessionContext().withClientAttributes(clnAttrs).txStart()) {
        clnAttrs.setAttribute(getAttribute("SESSION_ID", "1234");
 
        ...
    }
}

Spreading context across cluster nodes

  1. Via the QueryStartRequest SQL message protocol (for jdbc)
  2. Via the transaction protocol for handling inserts (CacheInterceptor), since inserts are not always performed on the request initiator node.

Risks and Assumptions

// Describe project risks, such as API or binary compatibility issues, major protocol changes, etc.

Discussion Links

// Links to discussions on the devlist, if applicable.

Reference Links

// Links to various reference documents, if applicable.

Tickets

   }
}

Risks and Assumptions

  1. No security checks are performed on the application attributes.
  2. Application attributes keys and values are strings, to avoid serialization problems.
  3. User should specify only few application attributes, and they should be small.
  4. UserAttributes currently fill with node attributes (~50 items on node start), and they are not transferred between nodes. This process should be changed.

Discussion Links

https://lists.apache.org/thread/c5j5yykr6tfrnp9zprhg9j69w63r11zx

Reference Links

[1] https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/using-application-contexts-to-retrieve-user-information.html#GUID-51C9D5FA-6787-4F05-82EF-A5968BEDC5A0

[2] https://stackoverflow.com/questions/71067911/use-oracles-dbms-session-set-context-in-entity-framework-core

[3] https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/Connection.html#setClientInfo(java.lang.String,java.lang.String)

[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

[8] https://learn.microsoft.com/en-us/sql/t-sql/functions/session-context-transact-sql?view=sql-server-ver16

[9] https://dev.mysql.com/doc/refman/8.4/en/user-variables.html

Tickets

Jira
serverASF JIRA
columnIdsissuekey,summary,issuetype,assignee,reporter,priority,status,resolution
columnskey,summary,type,assignee,reporter,priority,status,resolution
maximumIssues20
jqlQuerylabels = IEP-129
serverId5aa69414-a9e9-3523-82ec-879b028fb15b
// Links or report with relevant JIRA tickets.