2121import static org .mockito .ArgumentMatchers .any ;
2222import static org .mockito .ArgumentMatchers .anyInt ;
2323import static org .mockito .ArgumentMatchers .anyLong ;
24+ import static org .mockito .ArgumentMatchers .argThat ;
25+ import static org .mockito .Mockito .never ;
2426import static org .mockito .Mockito .times ;
2527
2628import java .util .List ;
2931import java .util .stream .IntStream ;
3032import org .apache .ignite .internal .hlc .HybridTimestampTracker ;
3133import org .apache .ignite .internal .manager .ComponentContext ;
34+ import org .apache .ignite .internal .replicator .ZonePartitionId ;
3235import org .apache .ignite .internal .sql .engine .AsyncSqlCursor ;
3336import org .apache .ignite .internal .sql .engine .InternalSqlRow ;
3437import org .apache .ignite .internal .sql .engine .QueryProcessor ;
3538import org .apache .ignite .internal .sql .engine .SqlProperties ;
39+ import org .apache .ignite .internal .sql .engine .exec .mapping .ColocationGroup ;
3640import org .apache .ignite .internal .sql .engine .framework .DataProvider ;
3741import org .apache .ignite .internal .sql .engine .framework .NoOpTransaction ;
3842import org .apache .ignite .internal .sql .engine .framework .TestBuilders ;
3943import org .apache .ignite .internal .sql .engine .framework .TestCluster ;
4044import org .apache .ignite .internal .sql .engine .framework .TestNode ;
4145import org .apache .ignite .internal .sql .engine .prepare .QueryMetadata ;
4246import org .apache .ignite .internal .sql .engine .prepare .QueryPlan ;
47+ import org .apache .ignite .internal .sql .engine .schema .PartitionCalculator ;
48+ import org .apache .ignite .internal .sql .engine .schema .TableDescriptor ;
4349import org .apache .ignite .internal .sql .engine .util .InjectQueryCheckerFactory ;
4450import org .apache .ignite .internal .sql .engine .util .QueryChecker ;
4551import org .apache .ignite .internal .sql .engine .util .QueryCheckerExtension ;
4652import org .apache .ignite .internal .sql .engine .util .QueryCheckerFactory ;
4753import org .apache .ignite .internal .testframework .BaseIgniteAbstractTest ;
4854import org .apache .ignite .internal .tx .InternalTransaction ;
55+ import org .apache .ignite .internal .type .NativeType ;
56+ import org .apache .ignite .internal .type .NativeTypes ;
4957import org .apache .ignite .lang .CancellationToken ;
5058import org .jetbrains .annotations .Nullable ;
5159import org .junit .jupiter .api .AfterAll ;
5260import org .junit .jupiter .api .BeforeAll ;
5361import org .junit .jupiter .api .Test ;
5462import org .junit .jupiter .api .extension .ExtendWith ;
63+ import org .junit .jupiter .params .ParameterizedTest ;
64+ import org .junit .jupiter .params .provider .ValueSource ;
65+ import org .mockito .ArgumentMatcher ;
5566import org .mockito .Mockito ;
5667
5768/** Transactions enlist count test. */
5869@ ExtendWith (QueryCheckerExtension .class )
5970public class TransactionEnlistTest extends BaseIgniteAbstractTest {
71+ private static final int PARTITIONS_COUNT = 3 ;
6072 private static final String NODE_NAME1 = "N1" ;
6173
6274 @ InjectQueryCheckerFactory
@@ -72,13 +84,45 @@ static void startCluster() {
7284
7385 //noinspection ConcatenationWithEmptyString
7486 CLUSTER .node ("N1" ).initSchema (""
75- + "CREATE ZONE test_zone (partitions 3 ) storage profiles ['Default'];"
76- + "CREATE TABLE t1 (id INT PRIMARY KEY, val INT) ZONE test_zone" );
87+ + "CREATE ZONE test_zone (partitions " + PARTITIONS_COUNT + " ) storage profiles ['Default'];"
88+ + "CREATE TABLE t1 (id INT PRIMARY KEY, val INT) ZONE test_zone; " );
7789
7890 CLUSTER .setAssignmentsProvider ("T1" , (partitionCount , b ) -> IntStream .range (0 , partitionCount )
7991 .mapToObj (i -> List .of ("N1" ))
8092 .collect (Collectors .toList ()));
8193 CLUSTER .setDataProvider ("T1" , TestBuilders .tableScan (DataProvider .fromCollection (List .of ())));
94+ CLUSTER .setUpdatableTable ("T1" , new UpdatableTable () {
95+ @ Override
96+ public TableDescriptor descriptor () {
97+ return null ;
98+ }
99+
100+ @ Override
101+ public <RowT > CompletableFuture <?> insertAll (ExecutionContext <RowT > ectx , List <RowT > rows , ColocationGroup colocationGroup ) {
102+ return nullCompletedFuture ();
103+ }
104+
105+ @ Override
106+ public <RowT > CompletableFuture <Void > insert (@ Nullable InternalTransaction explicitTx , ExecutionContext <RowT > ectx , RowT row ) {
107+ return nullCompletedFuture ();
108+ }
109+
110+ @ Override
111+ public <RowT > CompletableFuture <?> upsertAll (ExecutionContext <RowT > ectx , List <RowT > rows , ColocationGroup colocationGroup ) {
112+ return nullCompletedFuture ();
113+ }
114+
115+ @ Override
116+ public <RowT > CompletableFuture <Boolean > delete (@ Nullable InternalTransaction explicitTx , ExecutionContext <RowT > ectx ,
117+ RowT key ) {
118+ return nullCompletedFuture ();
119+ }
120+
121+ @ Override
122+ public <RowT > CompletableFuture <?> deleteAll (ExecutionContext <RowT > ectx , List <RowT > rows , ColocationGroup colocationGroup ) {
123+ return nullCompletedFuture ();
124+ }
125+ });
82126 }
83127
84128 @ AfterAll
@@ -104,6 +148,55 @@ void testEnlistCall() {
104148 Mockito .verify (spiedTx , times (2 )).enlist (any (), anyInt (), any (), anyLong ());
105149 }
106150
151+ @ ParameterizedTest
152+ @ ValueSource (ints = {1 , 2 , 3 })
153+ void testCommitPartitionChoice (int id ) {
154+ NoOpTransaction tx = NoOpTransaction .readWrite ("t1" , false );
155+
156+ NoOpTransaction spiedTx = Mockito .spy (tx );
157+
158+ assertQuery ("UPDATE t1 /*+ no_index */ SET val = 42 WHERE id = ?" , spiedTx )
159+ .withParam (id )
160+ .check ();
161+
162+ int expectedPartition = expectedPartition (id );
163+ {
164+ ArgumentMatcher <ZonePartitionId > partitionIdMatch = zonePartitionId -> zonePartitionId .partitionId () == expectedPartition ;
165+ // We expect commit partitions to be assigned once for given transaction.
166+ Mockito .verify (spiedTx , times (1 ))
167+ .assignCommitPartition (argThat (partitionIdMatch ));
168+ // Individual partition on the other hand will be enlisted for every source.
169+ // In this particular case -- first time for scan and second for Modify node.
170+ Mockito .verify (spiedTx , times (2 ))
171+ .enlist (argThat (partitionIdMatch ), anyInt (), any (), anyLong ());
172+ }
173+
174+ {
175+ // Due to partition pruning we don't expect any more enlistment.
176+ // We should not try to assign other partition as commit partition as well.
177+ ArgumentMatcher <ZonePartitionId > partitionIdMismatch = zonePartitionId -> zonePartitionId .partitionId () != expectedPartition ;
178+ Mockito .verify (spiedTx , never ())
179+ .assignCommitPartition (argThat (partitionIdMismatch ));
180+ Mockito .verify (spiedTx , never ())
181+ .enlist (argThat (partitionIdMismatch ), anyInt (), any (), anyLong ());
182+ }
183+ }
184+
185+ @ Test
186+ void testNoCommitPartitionAssignment () {
187+ NoOpTransaction tx = NoOpTransaction .readWrite ("t1" , false );
188+ tx .assignCommitPartition (new ZonePartitionId (1 , 1 ));
189+
190+ NoOpTransaction spiedTx = Mockito .spy (tx );
191+
192+ assertQuery ("UPDATE t1 /*+ no_index */ SET val = 42 WHERE id = ?" , spiedTx )
193+ .withParam (1 )
194+ .check ();
195+
196+ // Transaction already has a commit partition, so no assignment is expected during query processing.
197+ Mockito .verify (spiedTx , never ()).assignCommitPartition (any ());
198+ }
199+
107200 private static QueryChecker assertQuery (String qry , InternalTransaction tx ) {
108201 TestNode testNode = CLUSTER .node (NODE_NAME1 );
109202
@@ -149,10 +242,9 @@ public CompletableFuture<AsyncSqlCursor<InternalSqlRow>> queryAsync(
149242 String qry ,
150243 Object ... params
151244 ) {
152- assert params == null || params .length == 0 : "params are not supported" ;
153245 assert !prepareOnly : "Expected that the query will only be prepared, but not executed" ;
154246
155- AsyncSqlCursor <InternalSqlRow > sqlCursor = node .executeQuery (transaction , qry );
247+ AsyncSqlCursor <InternalSqlRow > sqlCursor = node .executeQuery (transaction , qry , params );
156248
157249 return CompletableFuture .completedFuture (sqlCursor );
158250 }
@@ -169,4 +261,10 @@ public CompletableFuture<Void> stopAsync(ComponentContext componentContext) {
169261 return nullCompletedFuture ();
170262 }
171263 }
264+
265+ private static int expectedPartition (int key ) {
266+ var calculator = new PartitionCalculator (PARTITIONS_COUNT , new NativeType [] {NativeTypes .INT32 });
267+ calculator .append (key );
268+ return calculator .partition ();
269+ }
172270}
0 commit comments