IGNITE-28352 Calcite. User defined sql function miss entries are written under the same tx lock#12936
IGNITE-28352 Calcite. User defined sql function miss entries are written under the same tx lock#12936zstan merged 8 commits intoapache:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses tx-aware query correctness for Calcite user-defined SQL functions (UDFs) by propagating “modified entries” context across query execution threads so nested queries executed inside UDFs can see the proper transactional changes.
Changes:
- Introduces a per-thread “modified entries” holder and wires it through
ExecutionServiceImpl/ExecutionContextto reuse tx-change sets when no explicit user transaction is present. - Updates execution-context construction sites (prod + tests) to match the new constructor signature.
- Adds a new integration test suite entry and a dedicated integration test covering tx-aware UDF behavior.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TxAwareModifiedEntriesHolder.java | New ThreadLocal-based holder for tx-modified entries. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java | Uses the holder to supply tx entries when userTx == null; passes holder into local execution context. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java | Stores/detaches tx entries in the holder around task execution; accepts holder in ctor. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExchangeServiceImpl.java | Updates ExecutionContext construction to new signature (adds null). |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/PlanExecutionTest.java | Updates ExecutionContext construction to new signature (adds null). |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java | Updates ExecutionContext construction to new signature (adds null). |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/RuntimeSortedIndexTest.java | Updates ExecutionContext construction to new signature (adds null). |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java | Updates ExecutionContext construction to new signature (adds null). |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UserDefinedTxAwareFunctionsIntegrationTest.java | New integration test for tx-aware UDF behavior (including nested queries). |
| modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java | Registers the new integration test in the suite. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
Outdated
Show resolved
Hide resolved
...main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
Show resolved
Hide resolved
...src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java
Outdated
Show resolved
Hide resolved
| /** */ | ||
| @Parameterized.Parameter() | ||
| public SqlTransactionMode sqlTxMode; |
There was a problem hiding this comment.
This test class redeclares @Parameterized.Parameter public SqlTransactionMode sqlTxMode, but AbstractBasicIntegrationTransactionalTest already defines the same @Parameterized.Parameter field. JUnit's Parameterized runner will see both (including inherited public fields), which can lead to parameter injection errors or ambiguous parameter indices. Remove the redeclared field and rely on the inherited sqlTxMode while keeping the custom parameters() method to restrict modes.
| /** */ | |
| @Parameterized.Parameter() | |
| public SqlTransactionMode sqlTxMode; |
...a/org/apache/ignite/internal/processors/query/calcite/exec/TxAwareModifiedEntriesHolder.java
Outdated
Show resolved
Hide resolved
...a/org/apache/ignite/internal/processors/query/calcite/exec/TxAwareModifiedEntriesHolder.java
Outdated
Show resolved
Hide resolved
| Commons.parametersMap(msg.parameters()), | ||
| msg.queryTransactionEntries() | ||
| msg.queryTransactionEntries(), | ||
| null |
There was a problem hiding this comment.
The new TxAwareModifiedEntriesHolder parameter is passed as null when creating ExecutionContext for remote fragments. That means ExecutionContext.execute() on remote nodes will not populate the thread-local with msg.queryTransactionEntries(), so nested queries started from UDFs on those nodes won't be able to pick up tx-aware modified entries via mofiedEntriesHolder.retrieve(). Consider passing the node's mofiedEntriesHolder here instead of null so the behavior is consistent between local and remote fragment execution.
| null | |
| modifiedEntriesHolder |
…ten under the same tx lock
| RootQuery<Row> qry, | ||
| MultiStepPlan plan | ||
| MultiStepPlan plan, | ||
| TxAwareModifiedEntriesHolder mofiedEntriesHolder |
There was a problem hiding this comment.
No need to pass this parameter, it's a non static method and always passed as this.modifiedEntriesHolder
Also typo in mofiedEntriesHolder
| * | ||
| * @see TransactionConfiguration#isTxAwareQueriesEnabled() | ||
| */ | ||
| private TxAwareModifiedEntriesHolder modifiedEntriesHolder; |
There was a problem hiding this comment.
Why not use just static ThreadLocal in ExecutionContext? Looks like we don't need this variable outside of execution context.
When execution context created we can check if qryTxEntries == null and get qryTxEntries from thread local.
It's much simplier (just about 5-10 lines of code)
| MultiDcQueryMappingTest.class, | ||
| TxWithExceptionalInterceptorTest.class | ||
| TxWithExceptionalInterceptorTest.class, | ||
| UserDefinedTxAwareFunctionsIntegrationTest.class |
There was a problem hiding this comment.
Comma at the end of line please.
| /** Check tx aware udf execution results. */ | ||
| @Test | ||
| public void testTxAwareUserDefinedFunc() { | ||
| assertTrue(nodeCount() > 1); |
There was a problem hiding this comment.
Override nodeCount() instead?
There was a problem hiding this comment.
it just a defence if someone try to change it in future (for example - startup speedup with only 1 node)
| } | ||
|
|
||
| /** */ | ||
| private static class City { |
There was a problem hiding this comment.
Let's reuse Employer entity from superclass.
|
|
||
| /** */ | ||
| @QuerySqlFunction | ||
| public static List<List<?>> name(int id) { |
|
|
||
| /** */ | ||
| @QuerySqlFunction | ||
| public List<List<?>> customTableFuncInner() { |
There was a problem hiding this comment.
Redundant, can be used customTableFunc
|
|
||
| IgniteCache<Integer, Object> cache = client.cache(DEFAULT_CACHE_NAME); | ||
|
|
||
| /*The pool size should be greater than the maximum number of concurrent queries initiated by UDFs*/ |
There was a problem hiding this comment.
Spaces before/after comment and point at the end.
| client.getOrCreateCache(cacheConfig()); | ||
|
|
||
| IgniteCache<Integer, Object> cache = client.cache(DEFAULT_CACHE_NAME); |
There was a problem hiding this comment.
IgniteCache<Integer, Object> cache = client.getOrCreateCache(cacheConfig());
| client.getOrCreateCache(cacheConfig()); | ||
|
|
||
| List<List<Object>> refResults = new ArrayList<>(); | ||
|
|
||
| IgniteCache<Integer, Object> cache = client.cache(DEFAULT_CACHE_NAME); |
There was a problem hiding this comment.
IgniteCache<Integer, Object> cache = client.getOrCreateCache(cacheConfig());
|



No description provided.