Copilot commented on code in PR #13769:
URL: https://github.com/apache/ignite/pull/13769#discussion_r4477728216


##########
modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java:
##########
@@ -1196,21 +1196,18 @@ else if (!entry.isFiltered())
                     notifyLocalListener(F.asList(evt), trans);
             }
             else {
-                if (!entry.isFiltered()) {
-                    if (trans != null)
-                        entry = transformToEntry(trans, evt);
-
-                    prepareEntry(cctx, nodeId, entry);
-                }
-
-                Object entryOrList = handleEntry(cctx, entry);
-
-                if (entryOrList != null) {
-                    if (log.isDebugEnabled())
-                        log.debug("Send the following event to listener: " + 
entryOrList);
-
-                    ctx.continuous().addNotification(nodeId, routineId, 
entryOrList, topic, sync, true);
+                Object transVal = trans != null && !entry.isFiltered() ? 
transform(trans, evt) : null;
+
+                // The transformer result is converted to a cache object, 
which may register its class. An ATOMIC update
+                // calls this listener under the topology lock, where 
registration is not allowed, so the conversion and
+                // sending are done in the async callback pool, ordered by 
partition.
+                if (trans != null && cctx.atomic() && !asyncCb) {
+                    ctx.pools().asyncCallbackPool().execute(
+                        () -> notifyRemoteListener(cctx, evt, true, transVal),
+                        evt.partitionId());

Review Comment:
   `notifyRemoteListener(...)` is now executed asynchronously but receives the 
original `evt` object created/owned by the update path. If 
`CacheContinuousQueryEvent` (or its `entry()`) contains mutable state or 
references that are only valid on the update thread, this can introduce races 
or use-after-modification bugs. Prefer capturing/creating an immutable snapshot 
of the needed data (e.g., entry + metadata) before scheduling, or 
document/guarantee that `evt` and its contents are immutable/thread-safe to use 
off-thread.



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousWithTransformerUnregisteredClassTest.java:
##########
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.query.continuous;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.event.CacheEntryEvent;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.ContinuousQueryWithTransformer;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Checks that {@link ContinuousQueryWithTransformer} on an ATOMIC cache 
delivers events transformed on a remote node
+ * when the class of the transformer result is not registered yet.
+ */
+public class CacheContinuousWithTransformerUnregisteredClassTest extends 
GridCommonAbstractTest {
+
+    /** */
+    private static final int DFLT_SERVER_NODE_CNT = 2;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+        ccfg.setCacheMode(CacheMode.PARTITIONED);
+        ccfg.setAtomicityMode(CacheAtomicityMode.ATOMIC);
+        ccfg.setBackups(1);
+
+        cfg.setCacheConfiguration(ccfg);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        grid(0).cache(DEFAULT_CACHE_NAME).removeAll();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrids(DFLT_SERVER_NODE_CNT);
+
+        super.beforeTestsStarted();

Review Comment:
   `beforeTestsStarted()` typically calls `super.beforeTestsStarted()` before 
starting grids so the base class can perform any required initialization first. 
Consider swapping the order to call `super.beforeTestsStarted()` and then 
`startGrids(...)`.



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousWithTransformerUnregisteredClassTest.java:
##########
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.query.continuous;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.event.CacheEntryEvent;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.ContinuousQueryWithTransformer;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Checks that {@link ContinuousQueryWithTransformer} on an ATOMIC cache 
delivers events transformed on a remote node
+ * when the class of the transformer result is not registered yet.
+ */
+public class CacheContinuousWithTransformerUnregisteredClassTest extends 
GridCommonAbstractTest {
+
+    /** */
+    private static final int DFLT_SERVER_NODE_CNT = 2;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);

Review Comment:
   Use generics when instantiating `CacheConfiguration` to avoid raw types and 
unchecked warnings (e.g., `CacheConfiguration<Integer, Object>` and `new 
CacheConfiguration<>(...)`).



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousWithTransformerUnregisteredClassTest.java:
##########
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.query.continuous;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.event.CacheEntryEvent;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.ContinuousQueryWithTransformer;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Checks that {@link ContinuousQueryWithTransformer} on an ATOMIC cache 
delivers events transformed on a remote node
+ * when the class of the transformer result is not registered yet.
+ */
+public class CacheContinuousWithTransformerUnregisteredClassTest extends 
GridCommonAbstractTest {
+
+    /** */
+    private static final int DFLT_SERVER_NODE_CNT = 2;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+        ccfg.setCacheMode(CacheMode.PARTITIONED);
+        ccfg.setAtomicityMode(CacheAtomicityMode.ATOMIC);
+        ccfg.setBackups(1);
+
+        cfg.setCacheConfiguration(ccfg);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        grid(0).cache(DEFAULT_CACHE_NAME).removeAll();

Review Comment:
   This override does not call `super.afterTest()`. If the base class performs 
essential cleanup (e.g., resetting static state, checking thread leaks), 
skipping it can make tests flaky. Consider calling `super.afterTest()` (e.g., 
in a finally block after cache cleanup).



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousWithTransformerUnregisteredClassTest.java:
##########
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.query.continuous;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.event.CacheEntryEvent;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.ContinuousQueryWithTransformer;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Checks that {@link ContinuousQueryWithTransformer} on an ATOMIC cache 
delivers events transformed on a remote node
+ * when the class of the transformer result is not registered yet.
+ */
+public class CacheContinuousWithTransformerUnregisteredClassTest extends 
GridCommonAbstractTest {
+
+    /** */
+    private static final int DFLT_SERVER_NODE_CNT = 2;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+        ccfg.setCacheMode(CacheMode.PARTITIONED);
+        ccfg.setAtomicityMode(CacheAtomicityMode.ATOMIC);
+        ccfg.setBackups(1);
+
+        cfg.setCacheConfiguration(ccfg);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        grid(0).cache(DEFAULT_CACHE_NAME).removeAll();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrids(DFLT_SERVER_NODE_CNT);
+
+        super.beforeTestsStarted();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids(true);
+    }
+
+    /** */
+    @Test
+    public void testUnregisteredResultClassOnAtomicCache() throws Exception {
+        List<Object> received = Collections.synchronizedList(new 
ArrayList<>());
+
+        ContinuousQueryWithTransformer<Integer, Object, PersonId> qry = new 
ContinuousQueryWithTransformer<>();
+
+        qry.setRemoteTransformerFactory(FactoryBuilder.factoryOf(new 
ToPersonId()));
+
+        qry.setLocalListener(evts -> {
+            for (Object e : evts)
+                received.add(e);
+        });
+
+        IgniteCache<Integer, Object> cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+        int rmtKey = primaryKey(grid(1).cache(DEFAULT_CACHE_NAME));
+
+        try (QueryCursor<?> ignored = cache.query(qry)) {
+            cache.put(rmtKey, 1);
+            cache.put(rmtKey, 2);
+            cache.put(rmtKey, 3);
+
+            boolean allReceived = GridTestUtils.waitForCondition(() -> 
received.size() == 3, 5_000);
+
+            assertTrue("Received: " + received, allReceived);
+
+            for (Object o : received)
+                assertTrue("Unexpected class: " + o.getClass().getName(), o 
instanceof PersonId);

Review Comment:
   The test currently validates only the number of events and that each is a 
`PersonId`. To better detect regressions in transformer output correctness, 
assert the `PersonId.id` value(s) (e.g., that each received item contains the 
expected key) and, if ordering matters here, assert the expected order as well.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to