You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

IDIEP-126
Author
Sponsor
Created 10.09.2024
Status
DRAFT


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

Code running on the Ignite node has access to client attributes through a static method call:

/** 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.

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.
  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.


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

IgniteClient (Java)

Implementation features:

  1. 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.
  2. It's required to extend ClientTransactions interface with withClientAttributes method, similar to withLabel.
  3. New feature is needed in the ClientBitmaskFeature protocol).


// Extends the interface with new public method.
public interface ClientTransactions {
	...

    /** @param clnAttrs Client attributes. */
    public ClientTransactions withClientAttributes(Map<String, String> clnAttrs);
}

// 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()) {
        ...
    }
}

Ignite 

Same as IgniteClient - attach the attributes to transaction:

// Extends the interface with new public method.
public interface IgniteTransactions {
 	...

    /** @param clnAttrs Client attributes. */
    public IgniteTransactions withClientAttributes(Map<String, String> clnAttrs);
}

// Example of usage.
try (Ignite ign = Ignition.localIgnite()) {
     Map<String, String> clnAttrs = F.asMap("SESSION_ID", "1234");
 
     try (Transaction tx = ign.transactions().withClientAttributes(clnAttrs).txStart()) {
        clnAttrs.setAttribute("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

// Links or report with relevant JIRA tickets.

  • No labels