You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2018/08/30 12:24:07 UTC
[01/45] ignite git commit: IGNITE-9326 Fixed deferred serialization
error handling for EntryProcessor result - Fixes #4588.
Repository: ignite
Updated Branches:
refs/heads/ignite-9273 1040589a3 -> e96c06f32
IGNITE-9326 Fixed deferred serialization error handling for EntryProcessor result - Fixes #4588.
Signed-off-by: Alexey Goncharuk <al...@gmail.com>
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/f6234782
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/f6234782
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/f6234782
Branch: refs/heads/ignite-9273
Commit: f623478228f0000d7a88a56c3e280c7f97db858e
Parents: 808d31f
Author: Alexey Goncharuk <al...@gmail.com>
Authored: Thu Aug 30 11:35:53 2018 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Thu Aug 30 11:35:53 2018 +0300
----------------------------------------------------------------------
.../cache/CacheInvokeDirectResult.java | 21 +++++++++++++++-----
.../processors/cache/GridCacheReturn.java | 17 +++++++++++++++-
.../dht/atomic/GridDhtAtomicCache.java | 4 ++++
.../IgniteCacheFailedUpdateResponseTest.java | 2 --
4 files changed, 36 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/f6234782/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheInvokeDirectResult.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheInvokeDirectResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheInvokeDirectResult.java
index 89a0a0ff..3b463af 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheInvokeDirectResult.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheInvokeDirectResult.java
@@ -140,17 +140,28 @@ public class CacheInvokeDirectResult implements Message {
}
}
- if (unprepareRes != null) {
- res = ctx.toCacheObject(unprepareRes);
-
- unprepareRes = null;
- }
+ assert unprepareRes == null : "marshalResult() was not called for the result: " + this;
if (res != null)
res.prepareMarshal(ctx.cacheObjectContext());
}
/**
+ * Converts the entry processor unprepared result to a cache object instance.
+ *
+ * @param ctx Cache context.
+ */
+ public void marshalResult(GridCacheContext ctx) {
+ try {
+ if (unprepareRes != null)
+ res = ctx.toCacheObject(unprepareRes);
+ }
+ finally {
+ unprepareRes = null;
+ }
+ }
+
+ /**
* @param ctx Cache context.
* @param ldr Class loader.
* @throws IgniteCheckedException If failed.
http://git-wip-us.apache.org/repos/asf/ignite/blob/f6234782/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheReturn.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheReturn.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheReturn.java
index 530f5b6..2ae0f7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheReturn.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheReturn.java
@@ -269,7 +269,10 @@ public class GridCacheReturn implements Externalizable, Message {
invokeResCol = new ArrayList<>();
CacheInvokeDirectResult res0 = err == null ?
- CacheInvokeDirectResult.lazyResult(key, res) : new CacheInvokeDirectResult(key, err);
+ cctx.transactional() ?
+ new CacheInvokeDirectResult(key, cctx.toCacheObject(res)) :
+ CacheInvokeDirectResult.lazyResult(key, res) :
+ new CacheInvokeDirectResult(key, err);
invokeResCol.add(res0);
}
@@ -308,6 +311,18 @@ public class GridCacheReturn implements Externalizable, Message {
}
/**
+ * Converts entry processor invokation results to cache object instances.
+ *
+ * @param ctx Cache context.
+ */
+ public void marshalResult(GridCacheContext ctx) {
+ if (invokeRes && invokeResCol != null) {
+ for (CacheInvokeDirectResult directRes : invokeResCol)
+ directRes.marshalResult(ctx);
+ }
+ }
+
+ /**
* @param ctx Cache context.
* @throws IgniteCheckedException If failed.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/f6234782/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index 2e77b52..112b913 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -1787,6 +1787,10 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
top.readUnlock();
}
+ // This call will convert entry processor invocation results to cache object instances.
+ // Must be done outside topology read lock to avoid deadlocks.
+ res.returnValue().marshalResult(ctx);
+
break;
}
catch (UnregisteredClassException ex) {
http://git-wip-us.apache.org/repos/asf/ignite/blob/f6234782/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheFailedUpdateResponseTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheFailedUpdateResponseTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheFailedUpdateResponseTest.java
index bef33e8..ebcff7c 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheFailedUpdateResponseTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheFailedUpdateResponseTest.java
@@ -104,8 +104,6 @@ public class IgniteCacheFailedUpdateResponseTest extends GridCommonAbstractTest
* @throws Exception If failed.
*/
public void testInvokeAtomic() throws Exception {
- fail("https://issues.apache.org/jira/browse/IGNITE-9326");
-
testInvoke(atomicCache);
testInvokeAll(atomicCache);
}
[16/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccVacuumTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccVacuumTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccVacuumTest.java
new file mode 100644
index 0000000..cba6d99
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccVacuumTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.mvcc;
+
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.testframework.GridTestUtils;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/**
+ * Vacuum test.
+ */
+public class CacheMvccVacuumTest extends CacheMvccAbstractTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testStartStopVacuumInMemory() throws Exception {
+ Ignite node0 = startGrid(0);
+ Ignite node1 = startGrid(1);
+
+ ensureVacuum(node0);
+ ensureVacuum(node1);
+
+ stopGrid(0);
+
+ ensureNoVacuum(node0);
+ ensureVacuum(node1);
+
+ stopGrid(1);
+
+ ensureNoVacuum(node0);
+ ensureNoVacuum(node1);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testStartStopVacuumPersistence() throws Exception {
+ persistence = true;
+
+ Ignite node0 = startGrid(0);
+ Ignite node1 = startGrid(1);
+
+ ensureNoVacuum(node0);
+ ensureNoVacuum(node1);
+
+ node1.cluster().active(true);
+
+ ensureVacuum(node0);
+ ensureVacuum(node1);
+
+ node1.cluster().active(false);
+
+ ensureNoVacuum(node0);
+ ensureNoVacuum(node1);
+
+ node1.cluster().active(true);
+
+ ensureVacuum(node0);
+ ensureVacuum(node1);
+
+ stopGrid(0);
+
+ ensureNoVacuum(node0);
+ ensureVacuum(node1);
+
+ stopGrid(1);
+
+ ensureNoVacuum(node0);
+ ensureNoVacuum(node1);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testVacuumNotStartedWithoutMvcc() throws Exception {
+ IgniteConfiguration cfg = getConfiguration("grid1").setMvccEnabled(false);
+
+ Ignite node = startGrid(cfg);
+
+ ensureNoVacuum(node);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testVacuumNotStartedWithoutMvccPersistence() throws Exception {
+ persistence = true;
+
+ IgniteConfiguration cfg = getConfiguration("grid1").setMvccEnabled(false);
+
+ Ignite node = startGrid(cfg);
+
+ ensureNoVacuum(node);
+
+ node.cluster().active(true);
+
+ ensureNoVacuum(node);
+ }
+
+ /**
+ * Ensures vacuum is running on the given node.
+ *
+ * @param node Node.
+ */
+ private void ensureVacuum(Ignite node) {
+ MvccProcessorImpl crd = mvccProcessor(node);
+
+ assertNotNull(crd);
+
+ List<GridWorker> vacuumWorkers = GridTestUtils.getFieldValue(crd, "vacuumWorkers");
+
+ assertNotNull(vacuumWorkers);
+ assertFalse(vacuumWorkers.isEmpty());
+
+ for (GridWorker w : vacuumWorkers) {
+ assertFalse(w.isCancelled());
+ assertFalse(w.isDone());
+ }
+ }
+
+ /**
+ * Ensures vacuum is stopped on the given node.
+ *
+ * @param node Node.
+ */
+ private void ensureNoVacuum(Ignite node) {
+ MvccProcessorImpl crd = mvccProcessor(node);
+
+ if (crd != null)
+ assertNull(GridTestUtils.<List<GridWorker>>getFieldValue(crd, "vacuumWorkers"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/MemoryPolicyInitializationTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/MemoryPolicyInitializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/MemoryPolicyInitializationTest.java
index 774fcc6..ccf1502 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/MemoryPolicyInitializationTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/MemoryPolicyInitializationTest.java
@@ -75,7 +75,7 @@ public class MemoryPolicyInitializationTest extends GridCommonAbstractTest {
Collection<DataRegion> allMemPlcs = ignite.context().cache().context().database().dataRegions();
- assertTrue(allMemPlcs.size() == 2);
+ assertEquals(2, allMemPlcs.size());
verifyDefaultAndSystemMemoryPolicies(allMemPlcs);
}
@@ -91,7 +91,7 @@ public class MemoryPolicyInitializationTest extends GridCommonAbstractTest {
Collection<DataRegion> allMemPlcs = ignite.context().cache().context().database().dataRegions();
- assertTrue(allMemPlcs.size() == 3);
+ assertEquals(3, allMemPlcs.size());
verifyDefaultAndSystemMemoryPolicies(allMemPlcs);
@@ -112,13 +112,13 @@ public class MemoryPolicyInitializationTest extends GridCommonAbstractTest {
Collection<DataRegion> allMemPlcs = dbMgr.dataRegions();
- assertTrue(allMemPlcs.size() == 2);
+ assertEquals(2, allMemPlcs.size());
verifyDefaultAndSystemMemoryPolicies(allMemPlcs);
DataRegion dfltMemPlc = U.field(dbMgr, "dfltDataRegion");
- assertTrue(dfltMemPlc.config().getMaxSize() == USER_DEFAULT_MEM_PLC_SIZE);
+ assertEquals(dfltMemPlc.config().getMaxSize(), USER_DEFAULT_MEM_PLC_SIZE);
}
/**
@@ -136,13 +136,13 @@ public class MemoryPolicyInitializationTest extends GridCommonAbstractTest {
Collection<DataRegion> allMemPlcs = dbMgr.dataRegions();
- assertTrue(allMemPlcs.size() == 3);
+ assertEquals(3, allMemPlcs.size());
verifyDefaultAndSystemMemoryPolicies(allMemPlcs);
DataRegion dfltMemPlc = U.field(dbMgr, "dfltDataRegion");
- assertTrue(dfltMemPlc.config().getMaxSize() == USER_CUSTOM_MEM_PLC_SIZE);
+ assertEquals(dfltMemPlc.config().getMaxSize(), USER_CUSTOM_MEM_PLC_SIZE);
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpPageStoreManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpPageStoreManager.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpPageStoreManager.java
index c61b3c0..0f21952 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpPageStoreManager.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpPageStoreManager.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.StoredCacheData;
+import org.apache.ignite.internal.processors.cache.persistence.AllocatedPageTracker;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.lang.IgniteFuture;
@@ -54,6 +55,12 @@ public class NoOpPageStoreManager implements IgnitePageStoreManager {
}
/** {@inheritDoc} */
+ @Override public void initialize(int cacheId, int partitions, String workingDir,
+ AllocatedPageTracker tracker) throws IgniteCheckedException {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
@Override public void initializeForCache(CacheGroupDescriptor grpDesc,
StoredCacheData cacheData) throws IgniteCheckedException {
// No-op.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
index 3c368f7..487cdbe 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
@@ -28,12 +28,14 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -252,6 +254,53 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
/**
* @throws IgniteCheckedException If failed.
*/
+ public void testFindWithClosure() throws IgniteCheckedException {
+ TestTree tree = createTestTree(true);
+ TreeMap<Long, Long> map = new TreeMap<>();
+
+ long size = CNT * CNT;
+
+ for (long i = 1; i <= size; i++) {
+ tree.put(i);
+ map.put(i, i);
+ }
+
+ checkCursor(tree.find(null, null, new TestTreeFindFilteredClosure(Collections.<Long>emptySet()), null),
+ Collections.<Long>emptyList().iterator());
+
+ checkCursor(tree.find(null, null, new TestTreeFindFilteredClosure(map.keySet()), null),
+ map.values().iterator());
+
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ for (int i = 0; i < 100; i++) {
+ Long val = rnd.nextLong(size) + 1;
+
+ checkCursor(tree.find(null, null, new TestTreeFindFilteredClosure(Collections.singleton(val)), null),
+ Collections.singleton(val).iterator());
+ }
+
+ for (int i = 0; i < 200; i++) {
+ long vals = rnd.nextLong(size) + 1;
+
+ TreeSet<Long> exp = new TreeSet<>();
+
+ for (long k = 0; k < vals; k++)
+ exp.add(rnd.nextLong(size) + 1);
+
+ checkCursor(tree.find(null, null, new TestTreeFindFilteredClosure(exp), null), exp.iterator());
+
+ checkCursor(tree.find(0L, null, new TestTreeFindFilteredClosure(exp), null), exp.iterator());
+
+ checkCursor(tree.find(0L, size, new TestTreeFindFilteredClosure(exp), null), exp.iterator());
+
+ checkCursor(tree.find(null, size, new TestTreeFindFilteredClosure(exp), null), exp.iterator());
+ }
+ }
+
+ /**
+ * @throws IgniteCheckedException If failed.
+ */
public void _testBenchInvoke() throws IgniteCheckedException {
MAX_PER_PAGE = 10;
@@ -605,6 +654,7 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
assertNoLocks();
assertEquals(x, tree.findOne(x).longValue());
+ checkIterate(tree, x, x, x, true);
assertNoLocks();
@@ -619,12 +669,15 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
assertNull(tree.findOne(-1L));
- for (long x = 0; x < cnt; x++)
+ for (long x = 0; x < cnt; x++) {
assertEquals(x, tree.findOne(x).longValue());
+ checkIterate(tree, x, x, x, true);
+ }
assertNoLocks();
assertNull(tree.findOne(cnt));
+ checkIterate(tree, cnt, cnt, null, false);
for (long x = RMV_INC > 0 ? 0 : cnt - 1; x >= 0 && x < cnt; x += RMV_INC) {
X.println(" -- " + x);
@@ -638,6 +691,7 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
assertNoLocks();
assertNull(tree.findOne(x));
+ checkIterate(tree, x, x, null, false);
assertNoLocks();
@@ -654,6 +708,40 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
}
/**
+ * @param tree Tree.
+ * @param lower Lower bound.
+ * @param upper Upper bound.
+ * @param exp Value to find.
+ * @param expFound {@code True} if value should be found.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void checkIterate(TestTree tree, long lower, long upper, Long exp, boolean expFound)
+ throws IgniteCheckedException {
+ TestTreeRowClosure c = new TestTreeRowClosure(exp);
+
+ tree.iterate(lower, upper, c);
+
+ assertEquals(expFound, c.found);
+ }
+
+ /**
+ * @param tree Tree.
+ * @param lower Lower bound.
+ * @param upper Upper bound.
+ * @param c Closure.
+ * @param expFound {@code True} if value should be found.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void checkIterateC(TestTree tree, long lower, long upper, TestTreeRowClosure c, boolean expFound)
+ throws IgniteCheckedException {
+ c.found = false;
+
+ tree.iterate(lower, upper, c);
+
+ assertEquals(expFound, c.found);
+ }
+
+ /**
* @throws IgniteCheckedException If failed.
*/
public void testRandomInvoke_1_30_1() throws IgniteCheckedException {
@@ -2057,7 +2145,209 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
}
/**
- *
+ * @throws Exception If failed.
+ */
+ public void testIterate() throws Exception {
+ MAX_PER_PAGE = 5;
+
+ TestTree tree = createTestTree(true);
+
+ checkIterate(tree, 0L, 100L, null, false);
+
+ for (long idx = 1L; idx <= 10L; ++idx)
+ tree.put(idx);
+
+ for (long idx = 1L; idx <= 10L; ++idx)
+ checkIterate(tree, idx, 100L, idx, true);
+
+ checkIterate(tree, 0L, 100L, 1L, true);
+
+ for (long idx = 1L; idx <= 10L; ++idx)
+ checkIterate(tree, idx, 100L, 10L, true);
+
+ checkIterate(tree, 0L, 100L, 100L, false);
+
+ for (long idx = 1L; idx <= 10L; ++idx)
+ checkIterate(tree, 0L, 100L, idx, true);
+
+ for (long idx = 0L; idx <= 10L; ++idx)
+ checkIterate(tree, idx, 11L, -1L, false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testIterateConcurrentPutRemove() throws Exception {
+ iterateConcurrentPutRemove();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testIterateConcurrentPutRemove_1() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-7265");
+
+ MAX_PER_PAGE = 1;
+
+ iterateConcurrentPutRemove();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testIterateConcurrentPutRemove_5() throws Exception {
+ MAX_PER_PAGE = 5;
+
+ iterateConcurrentPutRemove();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testIteratePutRemove_10() throws Exception {
+ MAX_PER_PAGE = 10;
+
+ iterateConcurrentPutRemove();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ private void iterateConcurrentPutRemove() throws Exception {
+ final TestTree tree = createTestTree(true);
+
+ final int KEYS = 10_000;
+
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ for (int i = 0; i < 10; i++) {
+ for (long idx = 0L; idx < KEYS; ++idx)
+ tree.put(idx);
+
+ final Long findKey;
+
+ if (MAX_PER_PAGE > 0) {
+ switch (i) {
+ case 0:
+ findKey = 1L;
+
+ break;
+
+ case 1:
+ findKey = (long)MAX_PER_PAGE;
+
+ break;
+
+ case 2:
+ findKey = (long)MAX_PER_PAGE - 1;
+
+ break;
+
+ case 3:
+ findKey = (long)MAX_PER_PAGE + 1;
+
+ break;
+
+ case 4:
+ findKey = (long)(KEYS / MAX_PER_PAGE / 2) * MAX_PER_PAGE;
+
+ break;
+
+ case 5:
+ findKey = (long)(KEYS / MAX_PER_PAGE / 2) * MAX_PER_PAGE - 1;
+
+ break;
+
+ case 6:
+ findKey = (long)(KEYS / MAX_PER_PAGE / 2) * MAX_PER_PAGE + 1;
+
+ break;
+
+ case 7:
+ findKey = (long)KEYS - 1;
+
+ break;
+
+ default:
+ findKey = rnd.nextLong(KEYS);
+ }
+ }
+ else
+ findKey = rnd.nextLong(KEYS);
+
+ info("Iteration [iter=" + i + ", key=" + findKey + ']');
+
+ assertEquals(findKey, tree.findOne(findKey));
+ checkIterate(tree, findKey, findKey, findKey, true);
+
+ IgniteInternalFuture getFut = GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ TestTreeRowClosure p = new TestTreeRowClosure(findKey);
+
+ TestTreeRowClosure falseP = new TestTreeRowClosure(-1L);
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ int shift = MAX_PER_PAGE > 0 ? rnd.nextInt(MAX_PER_PAGE * 2) : rnd.nextInt(100);
+
+ checkIterateC(tree, findKey, findKey, p, true);
+
+ checkIterateC(tree, findKey - shift, findKey, p, true);
+
+ checkIterateC(tree, findKey - shift, findKey + shift, p, true);
+
+ checkIterateC(tree, findKey, findKey + shift, p, true);
+
+ checkIterateC(tree, -100L, KEYS + 100L, falseP, false);
+
+ cnt++;
+ }
+
+ info("Done, read count: " + cnt);
+
+ return null;
+ }
+ }, 10, "find");
+
+ asyncRunFut = new GridCompoundFuture<>();
+
+ asyncRunFut.add(getFut);
+
+ asyncRunFut.markInitialized();
+
+ try {
+ U.sleep(100);
+
+ for (int j = 0; j < 20; j++) {
+ for (long idx = 0L; idx < KEYS / 2; ++idx) {
+ long toRmv = rnd.nextLong(KEYS);
+
+ if (toRmv != findKey)
+ tree.remove(toRmv);
+ }
+
+ for (long idx = 0L; idx < KEYS / 2; ++idx) {
+ long put = rnd.nextLong(KEYS);
+
+ tree.put(put);
+ }
+ }
+ }
+ finally {
+ stop.set(true);
+ }
+
+ asyncRunFut.get();
+
+ stop.set(false);
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
*/
public void testConcurrentGrowDegenerateTreeAndConcurrentRemove() throws Exception {
//calculate tree size when split happens
@@ -2264,6 +2554,17 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
last = c.get();
}
+
+ TestTreeFindFirstClosure cl = new TestTreeFindFirstClosure();
+
+ tree.iterate((long)low, (long)high, cl);
+
+ last = cl.val;
+
+ if (last != null) {
+ assertTrue(low + " <= " + last + " <= " + high, last >= low);
+ assertTrue(low + " <= " + last + " <= " + high, last <= high);
+ }
}
return null;
@@ -2401,7 +2702,7 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
}
/** {@inheritDoc} */
- @Override protected Long getRow(BPlusIO<Long> io, long pageAddr, int idx, Object ignore)
+ @Override public Long getRow(BPlusIO<Long> io, long pageAddr, int idx, Object ignore)
throws IgniteCheckedException {
assert io.canGetRow() : io;
@@ -2688,4 +2989,74 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
return PageUtils.getLong(pageAddr, offset(idx));
}
}
+
+ /**
+ *
+ */
+ static class TestTreeRowClosure implements BPlusTree.TreeRowClosure<Long, Long> {
+ /** */
+ private final Long expVal;
+
+ /** */
+ private boolean found;
+
+ /**
+ * @param expVal Value to find or {@code null} to find first.
+ */
+ TestTreeRowClosure(Long expVal) {
+ this.expVal = expVal;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<Long, Long> tree, BPlusIO<Long> io, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ assert !found;
+
+ found = expVal == null || io.getLookupRow(tree, pageAddr, idx).equals(expVal);
+
+ return !found;
+ }
+ }
+
+ /**
+ *
+ */
+ static class TestTreeFindFirstClosure implements BPlusTree.TreeRowClosure<Long, Long> {
+ /** */
+ private Long val;
+
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<Long, Long> tree, BPlusIO<Long> io, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ assert val == null;
+
+ val = io.getLookupRow(tree, pageAddr, idx);
+
+ return false;
+ }
+ }
+
+ /**
+ *
+ */
+ static class TestTreeFindFilteredClosure implements BPlusTree.TreeRowClosure<Long, Long> {
+ /** */
+ private final Set<Long> vals;
+
+ /**
+ * @param vals Values to allow in filter.
+ */
+ TestTreeFindFilteredClosure(Set<Long> vals) {
+ this.vals = vals;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<Long, Long> tree, BPlusIO<Long> io, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ Long val = io.getLookupRow(tree, pageAddr, idx);
+
+ return vals.contains(val);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListImplSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListImplSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListImplSelfTest.java
index 80daff2..17aa9b0 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListImplSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListImplSelfTest.java
@@ -45,6 +45,7 @@ import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetrics
import org.apache.ignite.internal.processors.cache.persistence.evict.NoOpPageEvictionTracker;
import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeListImpl;
import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.CacheVersionIO;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.plugin.extensions.communication.MessageReader;
import org.apache.ignite.plugin.extensions.communication.MessageWriter;
@@ -407,6 +408,20 @@ public class CacheFreeListImplSelfTest extends GridCommonAbstractTest {
}
/** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ int len = key().valueBytesLength(null);
+
+ len += value().valueBytesLength(null) + CacheVersionIO.size(version(), false) + 8;
+
+ return len + (cacheId() != 0 ? 4 : 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
@Override public long link() {
return link;
}
@@ -425,6 +440,46 @@ public class CacheFreeListImplSelfTest extends GridCommonAbstractTest {
@Override public int cacheId() {
return 0;
}
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCoordinatorVersion() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCounter() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int newMvccOperationCounter() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte mvccTxState() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte newMvccTxState() {
+ return 0;
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorMvccSeflTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorMvccSeflTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorMvccSeflTest.java
new file mode 100644
index 0000000..abe6dee
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorMvccSeflTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.datastreamer;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+
+/** */
+public class DataStreamProcessorMvccSeflTest extends DataStreamProcessorSelfTest {
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ IgniteConfiguration igniteConfiguration = super.getConfiguration(igniteInstanceName);
+
+ CacheConfiguration[] cacheConfigurations = igniteConfiguration.getCacheConfiguration();
+
+ assert cacheConfigurations == null || cacheConfigurations.length == 0
+ || (cacheConfigurations.length == 1 && cacheConfigurations[0].getAtomicityMode() == TRANSACTIONAL);
+
+ igniteConfiguration.setMvccEnabled(true);
+
+ return igniteConfiguration;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void testPartitioned() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-8149");
+
+ super.testPartitioned();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void testColocated() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-8149");
+
+ super.testColocated();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void testReplicated() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-8149");
+
+ super.testReplicated();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void testUpdateStore() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-8582");
+
+ super.testUpdateStore();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorSelfTest.java
index 91345fe..7075973 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessorSelfTest.java
@@ -371,11 +371,27 @@ public class DataStreamProcessorSelfTest extends GridCommonAbstractTest {
if (aff.isPrimary(locNode, key) || aff.isBackup(locNode, key)) {
GridCacheEntryEx entry = cache0.entryEx(key);
- entry.unswap();
+ try {
+ // lock non obsolete entry
+ while (true) {
+ entry.lockEntry();
+
+ if (!entry.obsolete())
+ break;
+
+ entry.unlockEntry();
+
+ entry = cache0.entryEx(key);
+ }
- assertNotNull("Missing entry for key: " + key, entry);
- assertEquals(new Integer((key < 100 ? -1 : key)),
- CU.value(entry.rawGet(), cache0.context(), false));
+ entry.unswap();
+
+ assertEquals(new Integer((key < 100 ? -1 : key)),
+ CU.value(entry.rawGet(), cache0.context(), false));
+ }
+ finally {
+ entry.unlockEntry();
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/sql/SqlParserTransactionalKeywordsSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/sql/SqlParserTransactionalKeywordsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/sql/SqlParserTransactionalKeywordsSelfTest.java
new file mode 100644
index 0000000..103bb97
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/sql/SqlParserTransactionalKeywordsSelfTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.sql;
+
+import org.apache.ignite.internal.sql.command.SqlBeginTransactionCommand;
+import org.apache.ignite.internal.sql.command.SqlCommand;
+import org.apache.ignite.internal.sql.command.SqlCommitTransactionCommand;
+import org.apache.ignite.internal.sql.command.SqlRollbackTransactionCommand;
+
+/**
+ * Tests for processing of keywords BEGIN, COMMIT, ROLLBACK, START.
+ */
+public class SqlParserTransactionalKeywordsSelfTest extends SqlParserAbstractSelfTest {
+ /**
+ * Test parsing of different forms of BEGIN/START.
+ */
+ public void testBegin() {
+ assertBegin("begin");
+ assertBegin("BEGIN");
+ assertBegin("BEGIN work");
+ assertBegin("begin Transaction");
+ assertBegin("StarT TransactioN");
+
+ assertParseError(null, "begin index", "Unexpected token: \"INDEX\"");
+ assertParseError(null, "start work", "Unexpected token: \"WORK\" (expected: \"TRANSACTION\")");
+ assertParseError(null, "start", "Unexpected end of command (expected: \"TRANSACTION\")");
+ }
+
+ /**
+ * Test parsing of different forms of COMMIT.
+ */
+ public void testCommit() {
+ assertCommit("commit");
+ assertCommit("COMMIT transaction");
+
+ assertParseError(null, "commit index", "Unexpected token: \"INDEX\"");
+ }
+
+ /**
+ * Test parsing of different forms of ROLLBACK.
+ */
+ public void testRollback() {
+ assertRollback("rollback");
+ assertRollback("ROLLBACK transaction");
+
+ assertParseError(null, "rollback index", "Unexpected token: \"INDEX\"");
+ }
+
+ /**
+ * Test that given SQL is parsed as a BEGIN command.
+ * @param sql command.
+ */
+ private static void assertBegin(String sql) {
+ assertTrue(parse(sql) instanceof SqlBeginTransactionCommand);
+ }
+
+ /**
+ * Test that given SQL is parsed as a BEGIN command.
+ * @param sql command.
+ */
+ private static void assertCommit(String sql) {
+ assertTrue(parse(sql) instanceof SqlCommitTransactionCommand);
+ }
+
+ /**
+ * Test that given SQL is parsed as a BEGIN command.
+ * @param sql command.
+ */
+ private static void assertRollback(String sql) {
+ assertTrue(parse(sql) instanceof SqlRollbackTransactionCommand);
+ }
+
+ /**
+ * Parse single SQL command.
+ * @param sql command.
+ * @return parsed command.
+ */
+ private static SqlCommand parse(String sql) {
+ return new SqlParser(null, sql).nextCommand();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
index 341f7be..09475e6 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
@@ -1583,6 +1583,14 @@ public abstract class GridAbstractTest extends TestCase {
}
/**
+ * @param idx Index of the Ignite instance.
+ * @return Indexed Ignite instance name.
+ */
+ protected String testNodeName(int idx) {
+ return getTestIgniteInstanceName(idx);
+ }
+
+ /**
* Parses test Ignite instance index from test Ignite instance name.
*
* @param testIgniteInstanceName Test Ignite instance name, returned by {@link #getTestIgniteInstanceName(int)}.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite.java
new file mode 100644
index 0000000..b5275b9
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite.java
@@ -0,0 +1,61 @@
+/*
+ * 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.testsuites;
+
+import junit.framework.TestSuite;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccClusterRestartTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccConfigurationValidationTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccIteratorWithConcurrentTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccLocalEntriesWithConcurrentTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccOperationChecksTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedCoordinatorFailoverTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccProcessorTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedCoordinatorFailoverTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccScanQueryWithConcurrentTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccSizeWithConcurrentTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccTransactionsTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccVacuumTest;
+import org.apache.ignite.internal.processors.datastreamer.DataStreamProcessorMvccSeflTest;
+
+/**
+ *
+ */
+public class IgniteCacheMvccTestSuite extends TestSuite {
+ /**
+ * @return Test suite.
+ */
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite("IgniteCache MVCC Test Suite");
+
+ suite.addTestSuite(CacheMvccTransactionsTest.class);
+ suite.addTestSuite(CacheMvccProcessorTest.class);
+ suite.addTestSuite(CacheMvccClusterRestartTest.class);
+ suite.addTestSuite(CacheMvccConfigurationValidationTest.class);
+ suite.addTestSuite(CacheMvccOperationChecksTest.class);
+ suite.addTestSuite(CacheMvccIteratorWithConcurrentTransactionTest.class);
+ suite.addTestSuite(CacheMvccLocalEntriesWithConcurrentTransactionTest.class);
+ suite.addTestSuite(CacheMvccScanQueryWithConcurrentTransactionTest.class);
+ suite.addTestSuite(CacheMvccSizeWithConcurrentTransactionTest.class);
+ suite.addTestSuite(CacheMvccVacuumTest.class);
+ suite.addTestSuite(CacheMvccPartitionedCoordinatorFailoverTest.class);
+ suite.addTestSuite(CacheMvccReplicatedCoordinatorFailoverTest.class);
+ suite.addTestSuite(DataStreamProcessorMvccSeflTest.class);
+
+ return suite;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
----------------------------------------------------------------------
diff --git a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
index 21a0c61..ca144ab 100644
--- a/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
+++ b/modules/dev-utils/src/main/java/org/apache/ignite/development/utils/IgniteWalConverter.java
@@ -29,6 +29,8 @@ import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasInnerI
import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2InnerIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2LeafIO;
+import org.apache.ignite.internal.processors.query.h2.database.io.H2MvccInnerIO;
+import org.apache.ignite.internal.processors.query.h2.database.io.H2MvccLeafIO;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.logger.NullLogger;
import org.jetbrains.annotations.Nullable;
@@ -39,6 +41,7 @@ import org.jetbrains.annotations.Nullable;
public class IgniteWalConverter {
/**
* @param args Args.
+ * @throws Exception If failed.
*/
public static void main(String[] args) throws Exception {
if (args.length < 2)
@@ -47,7 +50,7 @@ public class IgniteWalConverter {
"\t2. Path to dir with wal files.\n" +
"\t3. (Optional) Path to dir with archive wal files.");
- PageIO.registerH2(H2InnerIO.VERSIONS, H2LeafIO.VERSIONS);
+ PageIO.registerH2(H2InnerIO.VERSIONS, H2LeafIO.VERSIONS, H2MvccInnerIO.VERSIONS, H2MvccLeafIO.VERSIONS);
H2ExtrasInnerIO.register();
H2ExtrasLeafIO.register();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
----------------------------------------------------------------------
diff --git a/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java b/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
index b4a8af4..831e674 100644
--- a/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
+++ b/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
@@ -17,8 +17,6 @@
package org.apache.ignite.internal.processors.query.h2.opt;
-import com.vividsolutions.jts.geom.Envelope;
-import com.vividsolutions.jts.geom.Geometry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -28,6 +26,8 @@ import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.query.h2.H2Cursor;
import org.apache.ignite.internal.util.GridCursorIteratorWrapper;
@@ -340,7 +340,12 @@ public class GridH2SpatialIndex extends GridH2IndexBase implements SpatialIndex
long time = System.currentTimeMillis();
- IndexingQueryFilter qryFilter = threadLocalFilter();
+ IndexingQueryFilter qryFilter = null;
+ GridH2QueryContext qctx = GridH2QueryContext.get();
+
+ if (qctx != null) {
+ qryFilter = qctx.filter();
+ }
IndexingQueryCacheFilter qryCacheFilter = qryFilter != null ? qryFilter.forCache(getTable().cacheName()) : null;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
index 4a93aaf..a5f0ca2 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
@@ -65,6 +65,12 @@ public class GridCacheTwoStepQuery {
/** */
private CacheQueryPartitionInfo[] derivedPartitions;
+ /** */
+ private boolean mvccEnabled;
+
+ /** {@code FOR UPDATE} flag. */
+ private boolean forUpdate;
+
/**
* @param originalSql Original query SQL.
* @param tbls Tables in query.
@@ -241,6 +247,8 @@ public class GridCacheTwoStepQuery {
cp.distributedJoins = distributedJoins;
cp.derivedPartitions = derivedPartitions;
cp.local = local;
+ cp.mvccEnabled = mvccEnabled;
+ cp.forUpdate = forUpdate;
for (int i = 0; i < mapQrys.size(); i++)
cp.mapQrys.add(mapQrys.get(i).copy());
@@ -262,6 +270,34 @@ public class GridCacheTwoStepQuery {
return tbls;
}
+ /**
+ * @return Mvcc flag.
+ */
+ public boolean mvccEnabled() {
+ return mvccEnabled;
+ }
+
+ /**
+ * @param mvccEnabled Mvcc flag.
+ */
+ public void mvccEnabled(boolean mvccEnabled) {
+ this.mvccEnabled = mvccEnabled;
+ }
+
+ /**
+ * @return {@code FOR UPDATE} flag.
+ */
+ public boolean forUpdate() {
+ return forUpdate;
+ }
+
+ /**
+ * @param forUpdate {@code FOR UPDATE} flag.
+ */
+ public void forUpdate(boolean forUpdate) {
+ this.forUpdate = forUpdate;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridCacheTwoStepQuery.class, this);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DhtResultSetEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DhtResultSetEnlistFuture.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DhtResultSetEnlistFuture.java
new file mode 100644
index 0000000..1d382f7
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DhtResultSetEnlistFuture.java
@@ -0,0 +1,63 @@
+/*
+ * 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.query.h2;
+
+import java.sql.ResultSet;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxAbstractEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class DhtResultSetEnlistFuture extends GridDhtTxAbstractEnlistFuture implements ResultSetEnlistFuture {
+ /** */
+ private ResultSet rs;
+
+ /**
+ * @param nearNodeId Near node ID.
+ * @param nearLockVer Near lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param threadId Thread ID.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini future id.
+ * @param parts Partitions.
+ * @param tx Transaction.
+ * @param timeout Lock acquisition timeout.
+ * @param cctx Cache context.
+ * @param rs Result set to process.
+ */
+ public DhtResultSetEnlistFuture(UUID nearNodeId, GridCacheVersion nearLockVer,
+ MvccSnapshot mvccSnapshot, long threadId, IgniteUuid nearFutId, int nearMiniId, @Nullable int[] parts,
+ GridDhtTxLocalAdapter tx, long timeout, GridCacheContext<?, ?> cctx, ResultSet rs) {
+ super(nearNodeId, nearLockVer, mvccSnapshot, threadId, nearFutId, nearMiniId, parts, tx, timeout, cctx);
+
+ this.rs = rs;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected UpdateSourceIterator<?> createIterator() {
+ return ResultSetEnlistFuture.createIterator(rs);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
index 5270e7f..6ce43dd 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
@@ -43,6 +43,8 @@ import org.apache.ignite.cache.query.BulkLoadContextCursor;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.bulkload.BulkLoadAckClientParameters;
import org.apache.ignite.internal.processors.bulkload.BulkLoadCacheWriter;
import org.apache.ignite.internal.processors.bulkload.BulkLoadParser;
@@ -51,14 +53,20 @@ import org.apache.ignite.internal.processors.bulkload.BulkLoadStreamerWriter;
import org.apache.ignite.internal.processors.cache.CacheOperationContext;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.StaticMvccQueryTracker;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
import org.apache.ignite.internal.processors.odbc.SqlStateCode;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
import org.apache.ignite.internal.processors.query.GridQueryCacheObjectsIterator;
import org.apache.ignite.internal.processors.query.GridQueryCancel;
import org.apache.ignite.internal.processors.query.GridQueryFieldsResult;
import org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
import org.apache.ignite.internal.processors.query.h2.dml.DmlBatchSender;
import org.apache.ignite.internal.processors.query.h2.dml.DmlDistributedPlanInfo;
import org.apache.ignite.internal.processors.query.h2.dml.DmlUtils;
@@ -67,6 +75,7 @@ import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlan;
import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlanBuilder;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest;
import org.apache.ignite.internal.sql.command.SqlBulkLoadCommand;
import org.apache.ignite.internal.sql.command.SqlCommand;
import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
@@ -86,6 +95,11 @@ import org.h2.command.dml.Merge;
import org.h2.command.dml.Update;
import org.jetbrains.annotations.Nullable;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.checkActive;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccTracker;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.requestSnapshot;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.tx;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.txStart;
import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.DUPLICATE_KEY;
import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.createJdbcSqlException;
import static org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.UPDATE_RESULT_META;
@@ -95,7 +109,7 @@ import static org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.UP
*/
public class DmlStatementsProcessor {
/** Default number of attempts to re-run DELETE and UPDATE queries in case of concurrent modifications of values. */
- private final static int DFLT_DML_RERUN_ATTEMPTS = 4;
+ private static final int DFLT_DML_RERUN_ATTEMPTS = 4;
/** Indexing. */
private IgniteH2Indexing idx;
@@ -168,7 +182,7 @@ public class DmlStatementsProcessor {
UpdateResult r;
try {
- r = executeUpdateStatement(schemaName, cctx, conn, prepared, fieldsQry, loc, filters, cancel);
+ r = executeUpdateStatement(schemaName, plan, fieldsQry, loc, filters, cancel);
}
finally {
cctx.operationContextPerCall(opCtx);
@@ -211,17 +225,16 @@ public class DmlStatementsProcessor {
UpdatePlan plan = getPlanForStatement(schemaName, conn, prepared, fieldsQry, loc, null);
- if (plan.hasRows() && plan.mode() == UpdateMode.INSERT) {
- GridCacheContext<?, ?> cctx = plan.cacheContext();
+ GridCacheContext<?, ?> cctx = plan.cacheContext();
+ // For MVCC case, let's enlist batch elements one by one.
+ if (plan.hasRows() && plan.mode() == UpdateMode.INSERT && !cctx.mvccEnabled()) {
CacheOperationContext opCtx = setKeepBinaryContext(cctx);
try {
List<List<List<?>>> cur = plan.createRows(argss);
- List<UpdateResult> res = processDmlSelectResultBatched(plan, cur, fieldsQry.getPageSize());
-
- return res;
+ return processDmlSelectResultBatched(plan, cur, fieldsQry.getPageSize());
}
finally {
cctx.operationContextPerCall(opCtx);
@@ -344,8 +357,6 @@ public class DmlStatementsProcessor {
else {
UpdateResult res = updateSqlFields(schemaName, c, p, fieldsQry, false, null, cancel);
- ArrayList<QueryCursorImpl<List<?>>> resCurs = new ArrayList<>(1);
-
checkUpdateResult(res);
QueryCursorImpl<List<?>> resCur = (QueryCursorImpl<List<?>>)new QueryCursorImpl(Collections.singletonList
@@ -353,9 +364,7 @@ public class DmlStatementsProcessor {
resCur.fieldsMeta(UPDATE_RESULT_META);
- resCurs.add(resCur);
-
- return resCurs;
+ return Collections.singletonList(resCur);
}
}
@@ -419,7 +428,7 @@ public class DmlStatementsProcessor {
if (!F.isEmpty(plan.selectQuery())) {
GridQueryFieldsResult res = idx.queryLocalSqlFields(idx.schema(cctx.name()),
plan.selectQuery(), F.asList(U.firstNotNull(args, X.EMPTY_OBJECT_ARRAY)),
- null, false, 0, null);
+ null, false, false, 0, null);
it = res.iterator();
}
@@ -467,9 +476,7 @@ public class DmlStatementsProcessor {
* Actually perform SQL DML operation locally.
*
* @param schemaName Schema name.
- * @param cctx Cache context.
- * @param c Connection.
- * @param prepared Prepared statement for DML query.
+ * @param plan Cache context.
* @param fieldsQry Fields query.
* @param loc Local query flag.
* @param filters Cache name and key filter.
@@ -478,12 +485,124 @@ public class DmlStatementsProcessor {
* @throws IgniteCheckedException if failed.
*/
@SuppressWarnings({"ConstantConditions", "unchecked"})
- private UpdateResult executeUpdateStatement(String schemaName, final GridCacheContext cctx, Connection c,
- Prepared prepared, SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters,
+ private UpdateResult executeUpdateStatement(String schemaName, final UpdatePlan plan,
+ SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters,
GridQueryCancel cancel) throws IgniteCheckedException {
- Integer errKeysPos = null;
+ GridCacheContext cctx = plan.cacheContext();
+
+ if (cctx != null && cctx.mvccEnabled()) {
+ assert cctx.transactional();
+
+ DmlDistributedPlanInfo distributedPlan = plan.distributedPlan();
+
+ GridNearTxLocal tx = tx(cctx.kernalContext());
+
+ boolean implicit = (tx == null);
+
+ boolean commit = implicit && (!(fieldsQry instanceof SqlFieldsQueryEx) ||
+ ((SqlFieldsQueryEx)fieldsQry).isAutoCommit());
+
+ if (implicit)
+ tx = txStart(cctx, fieldsQry.getTimeout());
+
+ requestSnapshot(cctx, checkActive(tx));
+
+ try (GridNearTxLocal toCommit = commit ? tx : null) {
+ long timeout;
+
+ if (implicit)
+ timeout = tx.remainingTime();
+ else {
+ long tm1 = tx.remainingTime(), tm2 = fieldsQry.getTimeout();
+
+ timeout = tm1 > 0 && tm2 > 0 ? Math.min(tm1, tm2) : Math.max(tm1, tm2);
+ }
+
+ if (cctx.isReplicated() || distributedPlan == null || ((plan.mode() == UpdateMode.INSERT
+ || plan.mode() == UpdateMode.MERGE) && !plan.isLocalSubquery())) {
+
+ boolean sequential = true;
+
+ UpdateSourceIterator<?> it;
+
+ if (plan.fastResult()) {
+ IgniteBiTuple row = plan.getFastRow(fieldsQry.getArgs());
+
+ EnlistOperation op = UpdatePlan.enlistOperation(plan.mode());
+
+ it = new DmlUpdateSingleEntryIterator<>(op, op.isDeleteOrLock() ? row.getKey() : row);
+ }
+ else if (plan.hasRows())
+ it = new DmlUpdateResultsIterator(UpdatePlan.enlistOperation(plan.mode()), plan, plan.createRows(fieldsQry.getArgs()));
+ else {
+ // TODO IGNITE-8865 if there is no ORDER BY statement it's no use to retain entries order on locking (sequential = false).
+ SqlFieldsQuery newFieldsQry = new SqlFieldsQuery(plan.selectQuery(), fieldsQry.isCollocated())
+ .setArgs(fieldsQry.getArgs())
+ .setDistributedJoins(fieldsQry.isDistributedJoins())
+ .setEnforceJoinOrder(fieldsQry.isEnforceJoinOrder())
+ .setLocal(fieldsQry.isLocal())
+ .setPageSize(fieldsQry.getPageSize())
+ .setTimeout((int)timeout, TimeUnit.MILLISECONDS);
+
+ FieldsQueryCursor<List<?>> cur = idx.querySqlFields(schemaName, newFieldsQry, null,
+ true, true, mvccTracker(cctx, tx), cancel).get(0);
+
+ it = plan.iteratorForTransaction(idx, cur);
+ }
+
+ IgniteInternalFuture<Long> fut = tx.updateAsync(cctx, it,
+ fieldsQry.getPageSize(), timeout, sequential);
+
+ UpdateResult res = new UpdateResult(fut.get(), X.EMPTY_OBJECT_ARRAY);
+
+ if (commit)
+ toCommit.commit();
+
+ return res;
+ }
+
+ int[] ids = U.toIntArray(distributedPlan.getCacheIds());
+
+ int flags = 0;
+
+ if (fieldsQry.isEnforceJoinOrder())
+ flags |= GridH2QueryRequest.FLAG_ENFORCE_JOIN_ORDER;
+
+ if (distributedPlan.isReplicatedOnly())
+ flags |= GridH2QueryRequest.FLAG_REPLICATED;
+
+ int[] parts = fieldsQry.getPartitions();
+
+ IgniteInternalFuture<Long> fut = tx.updateAsync(
+ cctx,
+ ids,
+ parts,
+ schemaName,
+ fieldsQry.getSql(),
+ fieldsQry.getArgs(),
+ flags,
+ fieldsQry.getPageSize(),
+ timeout);
- UpdatePlan plan = getPlanForStatement(schemaName, c, prepared, fieldsQry, loc, errKeysPos);
+ UpdateResult res = new UpdateResult(fut.get(), X.EMPTY_OBJECT_ARRAY);
+
+ if (commit)
+ toCommit.commit();
+
+ return res;
+ }
+ catch (IgniteCheckedException e) {
+ checkSqlException(e);
+
+ U.error(log, "Error during update [localNodeId=" + cctx.localNodeId() + "]", e);
+
+ throw new IgniteSQLException("Failed to run update. " + e.getMessage(), e);
+ }
+ finally {
+ if (commit)
+ cctx.tm().resetContext();
+ }
+ }
UpdateResult fastUpdateRes = plan.processFast(fieldsQry.getArgs());
@@ -514,13 +633,14 @@ public class DmlStatementsProcessor {
.setTimeout(fieldsQry.getTimeout(), TimeUnit.MILLISECONDS);
cur = (QueryCursorImpl<List<?>>)idx.querySqlFields(schemaName, newFieldsQry, null, true, true,
- cancel).get(0);
+ null, cancel).get(0);
}
else if (plan.hasRows())
cur = plan.createRows(fieldsQry.getArgs());
else {
final GridQueryFieldsResult res = idx.queryLocalSqlFields(schemaName, plan.selectQuery(),
- F.asList(fieldsQry.getArgs()), filters, fieldsQry.isEnforceJoinOrder(), fieldsQry.getTimeout(), cancel);
+ F.asList(fieldsQry.getArgs()), filters, fieldsQry.isEnforceJoinOrder(), false, fieldsQry.getTimeout(),
+ cancel);
cur = new QueryCursorImpl<>(new Iterable<List<?>>() {
@Override public Iterator<List<?>> iterator() {
@@ -536,7 +656,17 @@ public class DmlStatementsProcessor {
int pageSize = loc ? 0 : fieldsQry.getPageSize();
- return processDmlSelectResult(cctx, plan, cur, pageSize);
+ return processDmlSelectResult(plan, cur, pageSize);
+ }
+
+ /**
+ * @param e Exception.
+ */
+ private void checkSqlException(IgniteCheckedException e) {
+ IgniteSQLException sqlEx = X.cause(e, IgniteSQLException.class);
+
+ if(sqlEx != null)
+ throw sqlEx;
}
/**
@@ -564,14 +694,13 @@ public class DmlStatementsProcessor {
}
/**
- * @param cctx Cache context.
* @param plan Update plan.
* @param cursor Cursor over select results.
* @param pageSize Page size.
* @return Pair [number of successfully processed items; keys that have failed to be processed]
* @throws IgniteCheckedException if failed.
*/
- private UpdateResult processDmlSelectResult(GridCacheContext cctx, UpdatePlan plan, Iterable<List<?>> cursor,
+ private UpdateResult processDmlSelectResult(UpdatePlan plan, Iterable<List<?>> cursor,
int pageSize) throws IgniteCheckedException {
switch (plan.mode()) {
case MERGE:
@@ -584,7 +713,7 @@ public class DmlStatementsProcessor {
return doUpdate(plan, cursor, pageSize);
case DELETE:
- return doDelete(cctx, cursor, pageSize);
+ return doDelete(plan.cacheContext(), cursor, pageSize);
default:
throw new IgniteSQLException("Unexpected DML operation [mode=" + plan.mode() + ']',
@@ -606,6 +735,8 @@ public class DmlStatementsProcessor {
@SuppressWarnings({"unchecked", "ConstantConditions"})
UpdatePlan getPlanForStatement(String schema, Connection conn, Prepared p, SqlFieldsQuery fieldsQry,
boolean loc, @Nullable Integer errKeysPos) throws IgniteCheckedException {
+ isDmlOnSchemaSupported(schema);
+
H2CachedStatementKey planKey = H2CachedStatementKey.forDmlStatement(schema, p.getSQL(), fieldsQry, loc);
UpdatePlan res = (errKeysPos == null ? planCache.get(planKey) : null);
@@ -973,6 +1104,82 @@ public class DmlStatementsProcessor {
}
/**
+ * @param schema Schema name.
+ * @param conn Connection.
+ * @param stmt Prepared statement.
+ * @param qry Sql fields query
+ * @param filter Backup filter.
+ * @param cancel Query cancel object.
+ * @param local {@code true} if should be executed locally.
+ * @param topVer Topology version.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Iterator upon updated values.
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("unchecked")
+ public UpdateSourceIterator<?> prepareDistributedUpdate(String schema, Connection conn,
+ PreparedStatement stmt, SqlFieldsQuery qry,
+ IndexingQueryFilter filter, GridQueryCancel cancel, boolean local,
+ AffinityTopologyVersion topVer, MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+
+ Prepared prepared = GridSqlQueryParser.prepared(stmt);
+
+ UpdatePlan plan = getPlanForStatement(schema, conn, prepared, qry, local, null);
+
+ GridCacheContext cctx = plan.cacheContext();
+
+ CacheOperationContext opCtx = cctx.operationContextPerCall();
+
+ // Force keepBinary for operation context to avoid binary deserialization inside entry processor
+ if (cctx.binaryMarshaller()) {
+ CacheOperationContext newOpCtx = null;
+
+ if (opCtx == null)
+ newOpCtx = new CacheOperationContext().keepBinary();
+ else if (!opCtx.isKeepBinary())
+ newOpCtx = opCtx.keepBinary();
+
+ if (newOpCtx != null)
+ cctx.operationContextPerCall(newOpCtx);
+ }
+
+ QueryCursorImpl<List<?>> cur;
+
+ // Do a two-step query only if locality flag is not set AND if plan's SELECT corresponds to an actual
+ // sub-query and not some dummy stuff like "select 1, 2, 3;"
+ if (!local && !plan.isLocalSubquery()) {
+ SqlFieldsQuery newFieldsQry = new SqlFieldsQuery(plan.selectQuery(), qry.isCollocated())
+ .setArgs(qry.getArgs())
+ .setDistributedJoins(qry.isDistributedJoins())
+ .setEnforceJoinOrder(qry.isEnforceJoinOrder())
+ .setLocal(qry.isLocal())
+ .setPageSize(qry.getPageSize())
+ .setTimeout(qry.getTimeout(), TimeUnit.MILLISECONDS);
+
+ cur = (QueryCursorImpl<List<?>>)idx.querySqlFields(schema, newFieldsQry, null, true, true,
+ new StaticMvccQueryTracker(cctx, mvccSnapshot), cancel).get(0);
+ }
+ else {
+ final GridQueryFieldsResult res = idx.queryLocalSqlFields(schema, plan.selectQuery(),
+ F.asList(qry.getArgs()), filter, qry.isEnforceJoinOrder(), false, qry.getTimeout(), cancel,
+ new StaticMvccQueryTracker(cctx, mvccSnapshot));
+
+ cur = new QueryCursorImpl<>(new Iterable<List<?>>() {
+ @Override public Iterator<List<?>> iterator() {
+ try {
+ return res.iterator();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+ }, cancel);
+ }
+
+ return plan.iteratorForTransaction(idx, cur);
+ }
+
+ /**
* Runs a DML statement for which we have internal command executor.
*
* @param sql The SQL command text to execute.
@@ -1041,7 +1248,7 @@ public class DmlStatementsProcessor {
}
/** */
- private final static class InsertEntryProcessor implements EntryProcessor<Object, Object, Boolean> {
+ private static final class InsertEntryProcessor implements EntryProcessor<Object, Object, Boolean> {
/** Value to set. */
private final Object val;
@@ -1064,7 +1271,7 @@ public class DmlStatementsProcessor {
/**
* Entry processor invoked by UPDATE and DELETE operations.
*/
- private final static class ModifyingEntryProcessor implements EntryProcessor<Object, Object, Boolean> {
+ private static final class ModifyingEntryProcessor implements EntryProcessor<Object, Object, Boolean> {
/** Value to expect. */
private final Object val;
@@ -1139,6 +1346,17 @@ public class DmlStatementsProcessor {
}
/**
+ * Check if schema supports DDL statement.
+ *
+ * @param schemaName Schema name.
+ */
+ private static void isDmlOnSchemaSupported(String schemaName) {
+ if (F.eq(QueryUtils.SCHEMA_SYS, schemaName))
+ throw new IgniteSQLException("DML statements are not supported on " + schemaName + " schema",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ /**
* Check update result for erroneous keys and throws concurrent update exception if necessary.
*
* @param r Update result.
@@ -1181,4 +1399,82 @@ public class DmlStatementsProcessor {
return plan.processRow(record);
}
}
+
+ /** */
+ private static class DmlUpdateResultsIterator
+ implements UpdateSourceIterator<Object> {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private EnlistOperation op;
+
+ /** */
+ private UpdatePlan plan;
+
+ /** */
+ private Iterator<List<?>> it;
+
+ /** */
+ DmlUpdateResultsIterator(EnlistOperation op, UpdatePlan plan, Iterable<List<?>> rows) {
+ this.op = op;
+ this.plan = plan;
+ this.it = rows.iterator();
+ }
+
+ /** {@inheritDoc} */
+ @Override public EnlistOperation operation() {
+ return op;
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNextX() {
+ return it.hasNext();
+ }
+
+ /** {@inheritDoc} */
+ public Object nextX() throws IgniteCheckedException {
+ return plan.processRowForTx(it.next());
+ }
+ }
+
+ /** */
+ private static class DmlUpdateSingleEntryIterator<T> implements UpdateSourceIterator<T> {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private EnlistOperation op;
+
+ /** */
+ private boolean first = true;
+
+ /** */
+ private T entry;
+
+ /** */
+ DmlUpdateSingleEntryIterator(EnlistOperation op, T entry) {
+ this.op = op;
+ this.entry = entry;
+ }
+
+ /** {@inheritDoc} */
+ @Override public EnlistOperation operation() {
+ return op;
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNextX() {
+ return first;
+ }
+
+ /** {@inheritDoc} */
+ public T nextX() {
+ T res = first ? entry : null;
+
+ first = false;
+
+ return res;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
index e180c9c..425015a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
@@ -17,26 +17,34 @@
package org.apache.ignite.internal.processors.query.h2;
+import java.sql.Connection;
import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
import org.jetbrains.annotations.Nullable;
-import java.sql.Connection;
-
/**
- * Wrapper to store connection and flag is schema set or not.
+ * Wrapper to store connection with currently used schema and statement cache.
*/
-public class H2ConnectionWrapper {
+public class H2ConnectionWrapper implements AutoCloseable {
/** */
- private Connection conn;
+ private static final int STATEMENT_CACHE_SIZE = 256;
+
+ /** */
+ private final Connection conn;
/** */
private volatile String schema;
+ /** */
+ private volatile H2StatementCache statementCache;
+
/**
* @param conn Connection to use.
*/
H2ConnectionWrapper(Connection conn) {
this.conn = conn;
+
+ initStatementCache();
}
/**
@@ -60,8 +68,42 @@ public class H2ConnectionWrapper {
return conn;
}
+ /**
+ * @return Statement cache corresponding to connection.
+ */
+ public H2StatementCache statementCache() {
+ return statementCache;
+ }
+
+ /**
+ * Clears statement cache.
+ */
+ public void clearStatementCache() {
+ initStatementCache();
+ }
+
+ /**
+ * @return Statement cache size.
+ */
+ public int statementCacheSize() {
+ return statementCache == null ? 0 : statementCache.size();
+ }
+
+ /**
+ * Initializes statement cache.
+ */
+ private void initStatementCache() {
+ statementCache = new H2StatementCache(STATEMENT_CACHE_SIZE);
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(H2ConnectionWrapper.class, this);
}
+
+ /** Closes wrapped connection */
+ @Override
+ public void close() {
+ U.closeQuiet(conn);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
index 26cbaee..05df754 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
@@ -17,12 +17,12 @@
package org.apache.ignite.internal.processors.query.h2;
-import org.apache.ignite.IgniteCheckedException;
-
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
/**
* Special field set iterator based on database result set.
@@ -31,20 +31,39 @@ public class H2FieldsIterator extends H2ResultSetIterator<List<?>> {
/** */
private static final long serialVersionUID = 0L;
+ /** */
+ private transient MvccQueryTracker mvccTracker;
+
/**
* @param data Data.
+ * @param mvccTracker Mvcc tracker.
+ * @param forUpdate {@code SELECT FOR UPDATE} flag.
* @throws IgniteCheckedException If failed.
*/
- public H2FieldsIterator(ResultSet data) throws IgniteCheckedException {
- super(data);
+ public H2FieldsIterator(ResultSet data, MvccQueryTracker mvccTracker, boolean forUpdate)
+ throws IgniteCheckedException {
+ super(data, forUpdate);
+
+ this.mvccTracker = mvccTracker;
}
/** {@inheritDoc} */
@Override protected List<?> createRow() {
- ArrayList<Object> res = new ArrayList<>(row.length);
+ List<Object> res = new ArrayList<>(row.length);
Collections.addAll(res, row);
return res;
}
+
+ /** {@inheritDoc} */
+ @Override public void onClose() {
+ try {
+ super.onClose();
+ }
+ finally {
+ if (mvccTracker != null)
+ mvccTracker.onDone();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
index 31add34..770d9d5 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
@@ -34,7 +34,7 @@ public class H2KeyValueIterator<K, V> extends H2ResultSetIterator<IgniteBiTuple<
* @throws IgniteCheckedException If failed.
*/
protected H2KeyValueIterator(ResultSet data) throws IgniteCheckedException {
- super(data);
+ super(data, false);
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
index 8cc396a..e84ca04 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
@@ -69,9 +69,10 @@ public abstract class H2ResultSetIterator<T> extends GridCloseableIteratorAdapte
/**
* @param data Data array.
+ * @param forUpdate Whether is result is one of {@code SELECT FOR UPDATE} query.
* @throws IgniteCheckedException If failed.
*/
- protected H2ResultSetIterator(ResultSet data) throws IgniteCheckedException {
+ protected H2ResultSetIterator(ResultSet data, boolean forUpdate) throws IgniteCheckedException {
this.data = data;
try {
@@ -83,7 +84,9 @@ public abstract class H2ResultSetIterator<T> extends GridCloseableIteratorAdapte
if (data != null) {
try {
- row = new Object[data.getMetaData().getColumnCount()];
+ int colsCnt = data.getMetaData().getColumnCount();
+
+ row = new Object[forUpdate ? colsCnt - 1 : colsCnt];
}
catch (SQLException e) {
throw new IgniteCheckedException(e);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
index 673625f..6426994 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
@@ -17,52 +17,59 @@
package org.apache.ignite.internal.processors.query.h2;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
import java.sql.PreparedStatement;
import java.util.LinkedHashMap;
import java.util.Map;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.jetbrains.annotations.Nullable;
/**
- * Statement cache.
+ * Statement cache. LRU eviction policy is used. Not thread-safe.
*/
-class H2StatementCache extends LinkedHashMap<H2CachedStatementKey, PreparedStatement> {
- /** */
- private int size;
-
+final class H2StatementCache {
/** Last usage. */
private volatile long lastUsage;
+ /** */
+ private final LinkedHashMap<H2CachedStatementKey, PreparedStatement> lruStmtCache;
+
/**
- * @param size Size.
+ * @param size Maximum number of statements this cache can store.
*/
H2StatementCache(int size) {
- super(size, (float)0.75, true);
-
- this.size = size;
- }
-
- /** {@inheritDoc} */
- @Override protected boolean removeEldestEntry(Map.Entry<H2CachedStatementKey, PreparedStatement> eldest) {
- boolean rmv = size() > size;
+ lruStmtCache = new LinkedHashMap<H2CachedStatementKey, PreparedStatement>(size, .75f, true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<H2CachedStatementKey, PreparedStatement> eldest) {
+ if (size() <= size)
+ return false;
- if (rmv) {
- PreparedStatement stmt = eldest.getValue();
+ U.closeQuiet(eldest.getValue());
- U.closeQuiet(stmt);
- }
+ return true;
+ }
+ };
+ }
- return rmv;
+ /**
+ * Caches a statement.
+ *
+ * @param key Key associated with statement.
+ * @param stmt Statement which will be cached.
+ */
+ void put(H2CachedStatementKey key, @NotNull PreparedStatement stmt) {
+ lruStmtCache.put(key, stmt);
}
/**
- * Get statement for given schema and SQL.
- * @param schemaName Schema name.
- * @param sql SQL statement.
- * @return Cached {@link PreparedStatement}, or {@code null} if none found.
+ * Retrieves cached statement.
+ *
+ * @param key Key for a statement.
+ * @return Statement associated with a key.
*/
- @Nullable public PreparedStatement get(String schemaName, String sql) {
- return get(new H2CachedStatementKey(schemaName, sql));
+ @Nullable PreparedStatement get(H2CachedStatementKey key) {
+ return lruStmtCache.get(key);
}
/**
@@ -70,24 +77,31 @@ class H2StatementCache extends LinkedHashMap<H2CachedStatementKey, PreparedState
*
* @return last usage timestamp
*/
- public long lastUsage() {
+ long lastUsage() {
return lastUsage;
}
/**
* Updates the {@link #lastUsage} timestamp by current time.
*/
- public void updateLastUsage() {
+ void updateLastUsage() {
lastUsage = U.currentTimeMillis();
}
/**
* Remove statement for given schema and SQL.
+ *
* @param schemaName Schema name.
* @param sql SQL statement.
- * @return Cached {@link PreparedStatement}, or {@code null} if none found.
*/
- @Nullable public PreparedStatement remove(String schemaName, String sql) {
- return remove(new H2CachedStatementKey(schemaName, sql));
+ void remove(String schemaName, String sql) {
+ lruStmtCache.remove(new H2CachedStatementKey(schemaName, sql));
+ }
+
+ /**
+ * @return Cache size.
+ */
+ int size() {
+ return lruStmtCache.size();
}
}
[05/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/config/mvcc/benchmark-thin-native.properties
----------------------------------------------------------------------
diff --git a/modules/yardstick/config/mvcc/benchmark-thin-native.properties b/modules/yardstick/config/mvcc/benchmark-thin-native.properties
new file mode 100644
index 0000000..3fae9a8
--- /dev/null
+++ b/modules/yardstick/config/mvcc/benchmark-thin-native.properties
@@ -0,0 +1,124 @@
+#
+# 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.
+#
+
+#
+# Configuration to measure mvcc impact on jdbc operations.
+#
+
+now0=`date +'%H%M%S'`
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+JVM_OPTS=${JVM_OPTS}" \
+-Xms8g \
+-Xmx8g \
+-Xloggc:./gc${now0}.log \
+-XX:+PrintGCDetails \
+-verbose:gc \
+-XX:+UseParNewGC \
+-XX:+UseConcMarkSweepGC \
+-XX:+PrintGCDateStamps \
+"
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe,DStatProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Flag which indicates to restart the servers before every benchmark execution.
+RESTART_SERVERS=true
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# The benchmark is applicable only for 2 servers (the second server is started in client mode) and 1 driver.
+SERVER_HOSTS=localhost,localhost
+DRIVER_HOSTS=localhost
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Backups count.
+b=1
+
+# Warmup.
+w=30
+
+# Duration.
+d=300
+
+# Threads count.
+t=4
+
+# Sync mode.
+sm=FULL_SYNC
+
+# Parameters that should be the same across all the benchmarks launches.
+commonParams="-cfg ${SCRIPT_DIR}/../config/ignite-localhost-config.xml -nn ${nodesNum} -b ${b} -w ${w} -d ${d} \
+ -jdbc jdbc:ignite:thin://auto.find/ -t ${t} -sm ${sm} \
+ --clientNodesAfterId 0 \
+ -sn IgniteNode -cl --range 1000000 \
+ --atomic-mode TRANSACTIONAL"
+
+# Run configuration which contains all benchmarks.
+# Note that each benchmark is set to run for 300 seconds (5 min) with warm-up set to 60 seconds (1 minute).
+CONFIGS="\
+${commonParams} -dn JdbcSqlInsertDeleteBenchmark -ds ${ver}sql-insert-delete-batch-1-jdbc-thin-mvcc-off, \
+${commonParams} -dn JdbcSqlInsertDeleteBenchmark -ds ${ver}sql-insert-delete-batch-1-jdbc-thin-mvcc-on --mvcc, \
+ \
+${commonParams} -dn JdbcSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1-jdbc-thin-mvcc-off --sqlRange 1, \
+${commonParams} -dn JdbcSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1-jdbc-thin-mvcc-on --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn JdbcSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1000-jdbc-thin-mvcc-off --sqlRange 1000, \
+${commonParams} -dn JdbcSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1000-jdbc-thin-mvcc-on --sqlRange 1000 --mvcc, \
+ \
+${commonParams} -dn JdbcSqlUpdateBenchmark -ds ${ver}sql-update-batch-1-jdbc-thin-mvcc-off --sqlRange 1, \
+${commonParams} -dn JdbcSqlUpdateBenchmark -ds ${ver}sql-update-batch-1-jdbc-thin-mvcc-on --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn JdbcSqlUpdateBenchmark -ds ${ver}sql-update-batch-1000-jdbc-thin-mvcc-off --sqlRange 1000, \
+${commonParams} -dn JdbcSqlUpdateBenchmark -ds ${ver}sql-update-batch-1000-jdbc-thin-mvcc-on --sqlRange 1000 --mvcc, \
+ \
+ \
+${commonParams} -dn NativeSqlInsertDeleteBenchmark -ds ${ver}sql-insert-delete-batch-1-native-sql-mvcc-off, \
+${commonParams} -dn NativeSqlInsertDeleteBenchmark -ds ${ver}sql-insert-delete-batch-1-native-sql-mvcc-on --mvcc, \
+ \
+${commonParams} -dn NativeSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1-native-sql-mvcc-off --sqlRange 1, \
+${commonParams} -dn NativeSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1-native-sql-mvcc-on --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn NativeSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1000-native-sql-mvcc-off --sqlRange 1000, \
+${commonParams} -dn NativeSqlQueryRangeBenchmark -ds ${ver}sql-select-batch-1000-native-sql-mvcc-on --sqlRange 1000 --mvcc, \
+ \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1-native-sql-mvcc-off --sqlRange 1, \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1-native-sql-mvcc-on --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1000-native-sql-mvcc-off --sqlRange 1000, \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1000-native-sql-mvcc-on --sqlRange 1000 --mvcc, \
+ \
+ \
+${commonParams} -dn NativeJavaApiPutRemoveBenchmark -ds ${ver}sql-update-batch-1-native-sql-mvcc-off, \
+${commonParams} -dn NativeJavaApiPutRemoveBenchmark -ds ${ver}sql-update-batch-1-native-sql-mvcc-on --mvcc \
+"
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/config/upload/benchmark-jdbc-thin-inmemory-mvcc.properties
----------------------------------------------------------------------
diff --git a/modules/yardstick/config/upload/benchmark-jdbc-thin-inmemory-mvcc.properties b/modules/yardstick/config/upload/benchmark-jdbc-thin-inmemory-mvcc.properties
new file mode 100644
index 0000000..6e55b9b
--- /dev/null
+++ b/modules/yardstick/config/upload/benchmark-jdbc-thin-inmemory-mvcc.properties
@@ -0,0 +1,105 @@
+#
+# 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.
+#
+
+#
+# Benchmarks for data upload in inmemory mode (persistence disabled) with and without mvcc.
+#
+
+now0=`date +'%H%M%S'`
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+JVM_OPTS=${JVM_OPTS}" \
+-Xms8g \
+-Xmx8g \
+-Xloggc:./gc${now0}.log \
+-XX:+PrintGCDetails \
+-verbose:gc \
+-XX:+UseParNewGC \
+-XX:+UseConcMarkSweepGC \
+-XX:+PrintGCDateStamps \
+"
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=TotalTimeProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Flag which indicates to restart the servers before every benchmark execution.
+RESTART_SERVERS=true
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# The benchmark is applicable only for 2 servers (the second server is started in client mode) and 1 driver.
+SERVER_HOSTS=localhost,localhost
+DRIVER_HOSTS=localhost
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Backups count.
+b=1
+
+# Warmup.
+w=0
+
+# Threads count.
+t=1
+
+# Sync mode.
+sm=FULL_SYNC
+
+# Parameters that should be the same across all the benchmarks launches.
+commonParams="\
+-cfg ${SCRIPT_DIR}/../config/ignite-localhost-config.xml -nn ${nodesNum} -b ${b} \
+ --warmup ${w} --operations 1 \
+ -jdbc jdbc:ignite:thin://auto.find/ \
+ --threads ${t} --syncMode ${sm} -sn IgniteNode \
+ --upload-rows 1000000 -cl \
+ --clientNodesAfterId 0 \
+ --atomic-mode TRANSACTIONAL \
+"
+
+# Run configuration which contains all benchmarks.
+# Note that each benchmark is set to run only one time, warmup parameter is set to 0 due to custom warmup operation.
+CONFIGS="\
+${commonParams} -dn NativePutBenchmark -ds ${ver}upload-native-put-mvcc-off, \
+${commonParams} -dn NativePutBenchmark -ds ${ver}upload-native-put-mvcc-on --mvcc, \
+ \
+${commonParams} -dn NativeStreamerBenchmark -ds ${ver}upload-native-streamer-mvcc-off --streamer-local-batch-size 1000, \
+${commonParams} -dn NativeStreamerBenchmark -ds ${ver}upload-native-streamer-mvcc-on --streamer-local-batch-size 1000 --mvcc, \
+ \
+${commonParams} -dn CopyBenchmark -ds ${ver}upload-copy-mvcc-off, \
+${commonParams} -dn CopyBenchmark -ds ${ver}upload-copy-mvcc-on --mvcc, \
+ \
+${commonParams} -dn InsertBenchmark -ds ${ver}upload-insert-mvcc-off, \
+${commonParams} -dn InsertBenchmark -ds ${ver}upload-insert-mvcc-on --mvcc, \
+ \
+${commonParams} -dn BatchedInsertBenchmark -ds ${ver}upload-batched-insert-mvcc-off --upload-jdbc-batch-size 1000, \
+${commonParams} -dn BatchedInsertBenchmark -ds ${ver}upload-batched-insert-mvcc-on --upload-jdbc-batch-size 1000 --mvcc \
+"
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
index 72409a0..bbe2ab3 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
@@ -21,7 +21,9 @@ import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParametersDelegate;
import java.util.Collections;
import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -60,6 +62,10 @@ public class IgniteBenchmarkArguments {
private CacheWriteSynchronizationMode syncMode = CacheWriteSynchronizationMode.PRIMARY_SYNC;
/** */
+ @Parameter(names = {"--atomic-mode", "--atomicMode"})
+ @Nullable private CacheAtomicityMode atomicMode = null;
+
+ /** */
@Parameter(names = {"-cl", "--client"}, description = "Client flag")
private boolean clientOnly = false;
@@ -263,6 +269,17 @@ public class IgniteBenchmarkArguments {
private int streamerBufSize = IgniteDataStreamer.DFLT_PER_NODE_BUFFER_SIZE;
/** */
+ @Parameter(names = {"-mvcc", "--mvcc"}, description = "Enable MVCC for cache")
+ private boolean mvcc;
+
+ /**
+ * @return {@code True} if need enable cache mvcc (see {@link CacheConfiguration#isMvccEnabled()}).
+ */
+ public boolean mvccEnabled() {
+ return mvcc;
+ }
+
+ /** */
@Parameter(names = {"-sqlr", "--sqlRange"}, description = "Result set size")
@GridToStringInclude
private int sqlRange = 1;
@@ -277,6 +294,15 @@ public class IgniteBenchmarkArguments {
@GridToStringInclude
public UploadBenchmarkArguments upload = new UploadBenchmarkArguments();
+ /** */
+ @Parameter(names = {"--mvcc-contention-range", "--mvccContentionRange"},
+ description = "Mvcc benchmark specific: " +
+ "Size of range of table keys that should be used in query. " +
+ "Should be less than 'range'. " +
+ "Useful together with 'sqlRange' to control, how often key contentions of sql operations occur.")
+ @GridToStringInclude
+ public long mvccContentionRange = 10_000;
+
/**
* @return {@code True} if need set {@link DataStorageConfiguration}.
*/
@@ -389,6 +415,11 @@ public class IgniteBenchmarkArguments {
return syncMode;
}
+ /** With what cache atomicity mode to create tables. */
+ @Nullable public CacheAtomicityMode atomicMode(){
+ return atomicMode;
+ }
+
/**
* @return Backups.
*/
@@ -688,6 +719,13 @@ public class IgniteBenchmarkArguments {
return clientNodesAfterId;
}
+ /**
+ * @return Mvcc contention range.
+ */
+ public long mvccContentionRange() {
+ return mvccContentionRange;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return GridToStringBuilder.toString(IgniteBenchmarkArguments.class, this);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
index 0c61435..a259cbf 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
@@ -103,6 +103,8 @@ public class IgniteNode implements BenchmarkServer {
CacheConfiguration[] ccfgs = c.getCacheConfiguration();
+ c.setMvccEnabled(args.mvccEnabled());
+
if (ccfgs != null) {
for (CacheConfiguration cc : ccfgs) {
// IgniteNode can not run in CLIENT_ONLY mode,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractJdbcBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractJdbcBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractJdbcBenchmark.java
index 6ab024f..7d5b940 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractJdbcBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractJdbcBenchmark.java
@@ -92,7 +92,7 @@ public abstract class AbstractJdbcBenchmark extends IgniteAbstractBenchmark {
* @throws Exception On error.
*/
protected void setupData() throws Exception {
- fillData(cfg, (IgniteEx)ignite(), args.range());
+ fillData(cfg, (IgniteEx)ignite(), args.range(), args.atomicMode());
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractNativeBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractNativeBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractNativeBenchmark.java
index 5e92514..129e6a4 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractNativeBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/AbstractNativeBenchmark.java
@@ -32,6 +32,6 @@ public abstract class AbstractNativeBenchmark extends IgniteAbstractBenchmark {
@Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
super.setUp(cfg);
- fillData(cfg, (IgniteEx)ignite(), args.range());
+ fillData(cfg, (IgniteEx)ignite(), args.range(), args.atomicMode());
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/JdbcUtils.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/JdbcUtils.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/JdbcUtils.java
index 74b5da5..81d6c17 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/JdbcUtils.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/JdbcUtils.java
@@ -17,6 +17,7 @@
package org.apache.ignite.yardstick.jdbc;
+import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.internal.IgniteEx;
import org.yardstickframework.BenchmarkConfiguration;
@@ -33,11 +34,17 @@ public class JdbcUtils {
* @param ignite Ignite node.
* @param range Data key range.
*/
- static void fillData(BenchmarkConfiguration cfg, IgniteEx ignite, long range) {
+ public static void fillData(BenchmarkConfiguration cfg, IgniteEx ignite, long range, CacheAtomicityMode atomicMode) {
println(cfg, "Create table...");
+ String withExpr = atomicMode != null ? " WITH \"atomicity=" + atomicMode.name() + "\";" : ";";
+
+ String qry = "CREATE TABLE test_long (id long primary key, val long)" + withExpr;
+
+ println(cfg, "Creating table with schema: " + qry);
+
ignite.context().query().querySqlFields(
- new SqlFieldsQuery("CREATE TABLE test_long (id long primary key, val long)"), true);
+ new SqlFieldsQuery(qry), true);
println(cfg, "Populate data...");
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/NativeJavaApiPutRemoveBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/NativeJavaApiPutRemoveBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/NativeJavaApiPutRemoveBenchmark.java
new file mode 100644
index 0000000..c1106d7
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/NativeJavaApiPutRemoveBenchmark.java
@@ -0,0 +1,52 @@
+/*
+ * 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.yardstick.jdbc;
+
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.yardstickframework.BenchmarkConfiguration;
+
+/**
+ * Benchmark that fetches data from cache to compare with SQL SELECT operation.
+ */
+public class NativeJavaApiPutRemoveBenchmark extends AbstractNativeBenchmark {
+ /** Cache for created table. */
+ private IgniteCache<Object, Object> tabCache;
+
+ @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
+ super.setUp(cfg);
+
+ tabCache = ignite().cache("SQL_PUBLIC_TEST_LONG");
+ }
+
+ @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+ long insertKey = ThreadLocalRandom.current().nextLong(args.range()) + 1 + args.range();
+ long insertVal = insertKey + 1;
+
+ try {
+ tabCache.put(insertKey, insertVal);
+ tabCache.remove(insertKey);
+ } catch (IgniteException ign){
+ // Collision occurred, ignoring.
+ }
+
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/AbstractDistributedMvccBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/AbstractDistributedMvccBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/AbstractDistributedMvccBenchmark.java
new file mode 100644
index 0000000..daf50a1
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/AbstractDistributedMvccBenchmark.java
@@ -0,0 +1,101 @@
+/*
+ * 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.yardstick.jdbc.mvcc;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.IgniteCountDownLatch;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.yardstick.IgniteAbstractBenchmark;
+import org.yardstickframework.BenchmarkConfiguration;
+
+import static org.apache.ignite.yardstick.jdbc.JdbcUtils.fillData;
+import static org.yardstickframework.BenchmarkUtils.println;
+
+/**
+ * Base for mvcc benchmarks that are running on multiply hosts.
+ */
+public abstract class AbstractDistributedMvccBenchmark extends IgniteAbstractBenchmark {
+ /** Sql query to create load. */
+ public static final String UPDATE_QRY = "UPDATE test_long SET val = (val + 1) WHERE id BETWEEN ? AND ?";
+
+ /** Timeout in minutest for test data to be loaded. */
+ public static final long DATA_WAIT_TIMEOUT_MIN = 20;
+
+ /** Member id of the host driver is running */
+ protected int memberId;
+ /**
+ * Number of nodes handled by driver.
+ */
+ protected int driversNodesCnt;
+
+ /** {@inheritDoc} */
+ @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
+ super.setUp(cfg);
+
+ memberId = cfg.memberId();
+
+ if (memberId < 0)
+ throw new IllegalStateException("Member id should be initialized with non-negative value");
+
+ // We assume there is no client nodes in the cluster except clients that are yardstick drivers.
+ driversNodesCnt = ignite().cluster().forClients().nodes().size();
+
+ IgniteCountDownLatch dataIsReady = ignite().countDownLatch("fillDataLatch", 1, true, true);
+
+ try {
+ if (memberId == 0) {
+ fillData(cfg, (IgniteEx)ignite(), args.range(), args.atomicMode());
+
+ dataIsReady.countDown();
+ }
+ else {
+ println(cfg, "No need to upload data for memberId=" + memberId + ". Just waiting");
+
+ dataIsReady.await(DATA_WAIT_TIMEOUT_MIN, TimeUnit.MINUTES);
+
+ println(cfg, "Data is ready.");
+ }
+
+ }
+ catch (Throwable th) {
+ dataIsReady.countDownAll();
+
+ throw new RuntimeException("Fill Data failed.", th);
+ }
+
+ // Workaround for "Table TEST_LONG not found" on sql update.
+ execute(new SqlFieldsQuery("SELECT COUNT(*) FROM test_long"));
+ }
+
+ /**
+ * Execute specified query using started driver node.
+ * Returns result using {@link QueryCursor#getAll()}.
+ *
+ * @param qry sql query to execute.
+ */
+ protected List<List<?>> execute(SqlFieldsQuery qry) {
+ return ((IgniteEx)ignite())
+ .context()
+ .query()
+ .querySqlFields(qry, false)
+ .getAll();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccProcessorBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccProcessorBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccProcessorBenchmark.java
new file mode 100644
index 0000000..543e754
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccProcessorBenchmark.java
@@ -0,0 +1,61 @@
+/*
+ * 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.yardstick.jdbc.mvcc;
+
+import java.util.Map;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.yardstick.jdbc.DisjointRangeGenerator;
+import org.yardstickframework.BenchmarkConfiguration;
+
+/**
+ * Benchmark app that creates load on Mvcc Processor.
+ * Should be run in many threads on many hosts against one single server node.
+ */
+public class MvccProcessorBenchmark extends AbstractDistributedMvccBenchmark {
+ /** Generates id, that are disjoint only among threads running current host. */
+ private DisjointRangeGenerator locIdGen;
+
+ /** Offset for current host ids range, to make it disjoint among all the other host id ranges. */
+ private int idOffset;
+
+ /** {@inheritDoc} */
+ @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
+ super.setUp(cfg);
+
+ int locIdRangeWidth = args.range() / driversNodesCnt;
+
+ locIdGen = new DisjointRangeGenerator(cfg.threads(), locIdRangeWidth, args.sqlRange());
+
+ idOffset = locIdRangeWidth * memberId;
+ }
+
+ /**
+ * Performs sql updates on the key sets that are disjoint among all the threads on all the hosts.
+ */
+ @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+ long locStart = locIdGen.nextRangeStartId();
+
+ long start = idOffset + locStart;
+
+ long end = idOffset + locIdGen.endRangeId(locStart);
+
+ execute(new SqlFieldsQuery(UPDATE_QRY).setArgs(start, end));
+
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccUpdateContentionBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccUpdateContentionBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccUpdateContentionBenchmark.java
new file mode 100644
index 0000000..09179db
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/jdbc/mvcc/MvccUpdateContentionBenchmark.java
@@ -0,0 +1,77 @@
+/*
+ * 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.yardstick.jdbc.mvcc;
+
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+
+import static org.yardstickframework.BenchmarkUtils.println;
+
+/**
+ * Benchmark app that eliminates update contention in mvcc mode.
+ * Designed to be ran in many threads on many hosts.
+ */
+public class MvccUpdateContentionBenchmark extends AbstractDistributedMvccBenchmark {
+ /** Expected expception message in mvcc on mode on update fail. */
+ private static final String MVCC_EXC_MSG = "Mvcc version mismatch.";
+
+ /** Expected exception message in mvcc off mode on update fail. */
+ private static final String NO_MVCC_EXC_MSG_PREFIX =
+ "Failed to UPDATE some keys because they had been modified concurrently";
+
+ /** Counter of failed updates. */
+ private final AtomicLong failsCnt = new AtomicLong();
+
+ /** {@inheritDoc} */
+ @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ long start = rnd.nextLong(args.mvccContentionRange() - (args.sqlRange() - 1)) + 1;
+
+ long end = start + (args.sqlRange() - 1);
+
+ try {
+ execute(new SqlFieldsQuery(UPDATE_QRY).setArgs(start, end));
+ }
+ catch (IgniteSQLException exc) {
+ if ((args.mvccEnabled() && !exc.getMessage().equals(MVCC_EXC_MSG)) ||
+ (!args.mvccEnabled() && !exc.getMessage().startsWith(NO_MVCC_EXC_MSG_PREFIX)))
+ throw new RuntimeException("Exception with unexpected message is thrown.", exc);
+
+ failsCnt.incrementAndGet();
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Could not perform update.", e);
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void tearDown() throws Exception {
+ try {
+ super.tearDown();
+ }
+ finally {
+ println("Update contention count : " + failsCnt.get());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractNativeBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractNativeBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractNativeBenchmark.java
index de1ad28..01eb6d4 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractNativeBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractNativeBenchmark.java
@@ -20,6 +20,7 @@ package org.apache.ignite.yardstick.upload;
import java.util.Map;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.yardstick.IgniteAbstractBenchmark;
import org.apache.ignite.yardstick.upload.model.Values10;
import org.yardstickframework.BenchmarkConfiguration;
@@ -58,7 +59,7 @@ public abstract class AbstractNativeBenchmark extends IgniteAbstractBenchmark {
BenchmarkUtils.println(cfg, "Starting custom warmup.");
String warmupCacheName = cacheName + "Warmup";
- try (IgniteCache<Long, Values10> warmupCache = ignite().createCache(warmupCacheName)) {
+ try (IgniteCache<Long, Values10> warmupCache = createCache(warmupCacheName)) {
upload(warmupCacheName, warmupRowsCnt);
}
finally {
@@ -68,7 +69,16 @@ public abstract class AbstractNativeBenchmark extends IgniteAbstractBenchmark {
BenchmarkUtils.println(cfg, "Custom warmup finished.");
// cache for benchmarked action
- cache = ignite().createCache(cacheName);
+ cache = createCache(cacheName);
+ }
+
+ private IgniteCache<Long, Values10> createCache(String name) {
+ CacheConfiguration<Long, Values10> cfg = new CacheConfiguration<>(name);
+
+ if (args.atomicMode() != null)
+ cfg.setAtomicityMode(args.atomicMode());
+
+ return ignite().createCache(cfg);
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractUploadBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractUploadBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractUploadBenchmark.java
index 20dfdd5..6d9c84d 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractUploadBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/AbstractUploadBenchmark.java
@@ -41,7 +41,7 @@ public abstract class AbstractUploadBenchmark extends AbstractJdbcBenchmark {
long warmupRowsCnt;
/** Factory that hides all the test data details. */
- protected QueryFactory queries = new QueryFactory();
+ protected QueryFactory queries;
/** {@inheritDoc} */
@Override public final void setUp(BenchmarkConfiguration cfg) throws Exception {
@@ -93,7 +93,9 @@ public abstract class AbstractUploadBenchmark extends AbstractJdbcBenchmark {
/**
* Creates empty table.
*/
- @Override protected void setupData() throws Exception{
+ @Override protected void setupData() throws Exception {
+ queries = new QueryFactory(args.atomicMode());
+
dropAndCreate();
}
@@ -131,6 +133,9 @@ public abstract class AbstractUploadBenchmark extends AbstractJdbcBenchmark {
*/
private void dropAndCreate() throws SQLException {
executeUpdate(QueryFactory.DROP_TABLE_IF_EXISTS);
+
+ BenchmarkUtils.println(cfg, "Creating table with schema: " + queries.createTable());
+
executeUpdate(queries.createTable());
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/model/QueryFactory.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/model/QueryFactory.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/model/QueryFactory.java
index 3ff4cb4..6c98d1a 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/model/QueryFactory.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/upload/model/QueryFactory.java
@@ -20,6 +20,7 @@ package org.apache.ignite.yardstick.upload.model;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.yardstick.upload.StreamerParams;
import org.jetbrains.annotations.Nullable;
@@ -47,16 +48,21 @@ public class QueryFactory {
/** Number of "values" fields in the test table (any field except primary key). */
private int valFieldsCnt = 10;
- /** Create table with long primary key and number of long and varchar fields */
- private String createTable = newCreateTableQuery();
-
/** Parametrised query to insert new row. */
private String insert = newInsertQuery();
+ /** Atomicity mode of test table's cache. */
+ private CacheAtomicityMode tabAtomicMode;
+
+ /** */
+ public QueryFactory(CacheAtomicityMode tabAtomicMode) {
+ this.tabAtomicMode = tabAtomicMode;
+ }
+
/**
- * See {@link #createTable}.
+ * Create table with long primary key and number of long and varchar fields
*/
- private String newCreateTableQuery() {
+ public String createTable() {
StringBuilder create = new StringBuilder("CREATE TABLE test_upload (id LONG PRIMARY KEY");
for (int vi = 1; vi <= valFieldsCnt; vi++) {
@@ -69,7 +75,12 @@ public class QueryFactory {
}
- create.append(");");
+ create.append(')');
+
+ if (tabAtomicMode != null)
+ create.append(" WITH \"ATOMICITY=").append(tabAtomicMode.name()).append('\"');
+
+ create.append(';');
return create.toString();
}
@@ -87,13 +98,6 @@ public class QueryFactory {
}
/**
- * See {@link #createTable}.
- */
- public String createTable() {
- return createTable;
- }
-
- /**
* See {@link #insert}.
*/
public String insert() {
[25/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
index 8a63a00..125bc35 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
@@ -930,12 +930,13 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/**
* @param upper Upper bound.
+ * @param c Filter closure.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
* @return Cursor.
* @throws IgniteCheckedException If failed.
*/
- private GridCursor<T> findLowerUnbounded(L upper, Object x) throws IgniteCheckedException {
- ForwardCursor cursor = new ForwardCursor(null, upper, x);
+ private GridCursor<T> findLowerUnbounded(L upper, TreeRowClosure<L, T> c, Object x) throws IgniteCheckedException {
+ ForwardCursor cursor = new ForwardCursor(null, upper, c, x);
long firstPageId;
@@ -953,7 +954,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
long pageAddr = readLock(firstPageId, firstPage); // We always merge pages backwards, the first page is never removed.
try {
- cursor.init(pageAddr, io(pageAddr), 0);
+ cursor.init(pageAddr, io(pageAddr), -1);
}
finally {
readUnlock(firstPageId, firstPage, pageAddr);
@@ -981,13 +982,25 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/** {@inheritDoc} */
public final GridCursor<T> find(L lower, L upper, Object x) throws IgniteCheckedException {
+ return find(lower, upper, null, x);
+ }
+
+ /**
+ * @param lower Lower bound inclusive or {@code null} if unbounded.
+ * @param upper Upper bound inclusive or {@code null} if unbounded.
+ * @param c Filter closure.
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ * @return Cursor.
+ * @throws IgniteCheckedException If failed.
+ */
+ public final GridCursor<T> find(L lower, L upper, TreeRowClosure<L, T> c, Object x) throws IgniteCheckedException {
checkDestroyed();
try {
if (lower == null)
- return findLowerUnbounded(upper, x);
+ return findLowerUnbounded(upper, c, x);
- ForwardCursor cursor = new ForwardCursor(lower, upper, x);
+ ForwardCursor cursor = new ForwardCursor(lower, upper, c, x);
cursor.find();
@@ -1004,43 +1017,159 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
}
}
+ /**
+ * @param lower Lower bound inclusive.
+ * @param upper Upper bound inclusive.
+ * @param c Closure applied for all found items, iteration is stopped if closure returns {@code false}.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void iterate(L lower, L upper, TreeRowClosure<L, T> c) throws IgniteCheckedException {
+ checkDestroyed();
+
+ try {
+ ClosureCursor cursor = new ClosureCursor(lower, upper, c);
+
+ cursor.iterate();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteCheckedException("Runtime failure on bounds: [lower=" + lower + ", upper=" + upper + "]", e);
+ }
+ catch (RuntimeException e) {
+ throw new IgniteException("Runtime failure on bounds: [lower=" + lower + ", upper=" + upper + "]", e);
+ }
+ catch (AssertionError e) {
+ throw new AssertionError("Assertion error on bounds: [lower=" + lower + ", upper=" + upper + "]", e);
+ }
+ finally {
+ checkDestroyed();
+ }
+ }
+
+ /**
+ * @param lower Lower bound inclusive.
+ * @param upper Upper bound inclusive.
+ * @param c Closure applied for all found items.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void visit(L lower, L upper, TreeVisitorClosure<L, T> c) throws IgniteCheckedException {
+ checkDestroyed();
+
+ try {
+ new TreeVisitor(lower, upper, c).visit();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteCheckedException("Runtime failure on bounds: [lower=" + lower + ", upper=" + upper + "]", e);
+ }
+ catch (RuntimeException e) {
+ throw new IgniteException("Runtime failure on bounds: [lower=" + lower + ", upper=" + upper + "]", e);
+ }
+ catch (AssertionError e) {
+ throw new AssertionError("Assertion error on bounds: [lower=" + lower + ", upper=" + upper + "]", e);
+ }
+ finally {
+ checkDestroyed();
+ }
+ }
+
/** {@inheritDoc} */
@Override public T findFirst() throws IgniteCheckedException {
+ return findFirst(null);
+ }
+
+ /**
+ * Returns a value mapped to the lowest key, or {@code null} if tree is empty or no entry matches the passed filter.
+ * @param filter Filter closure.
+ * @return Value.
+ * @throws IgniteCheckedException If failed.
+ */
+ public T findFirst(TreeRowClosure<L, T> filter) throws IgniteCheckedException {
checkDestroyed();
try {
- long firstPageId;
-
- long metaPage = acquirePage(metaPageId);
- try {
- firstPageId = getFirstPageId(metaPageId, metaPage, 0);
- }
- finally {
- releasePage(metaPageId, metaPage);
- }
+ for (;;) {
+ long curPageId;
- long page = acquirePage(firstPageId);
+ long metaPage = acquirePage(metaPageId);
- try {
- long pageAddr = readLock(firstPageId, page);
+ try {
+ curPageId = getFirstPageId(metaPageId, metaPage, 0); // Level 0 is always at the bottom.
+ }
+ finally {
+ releasePage(metaPageId, metaPage);
+ }
+ long curPage = acquirePage(curPageId);
try {
- BPlusIO<L> io = io(pageAddr);
+ long curPageAddr = readLock(curPageId, curPage);
- int cnt = io.getCount(pageAddr);
+ if (curPageAddr == 0)
+ continue; // The first page has gone: restart scan.
- if (cnt == 0)
- return null;
+ try {
+ BPlusIO<L> io = io(curPageAddr);
- return getRow(io, pageAddr, 0);
+ assert io.isLeaf();
+
+ for (;;) {
+ int cnt = io.getCount(curPageAddr);
+
+ for (int i = 0; i < cnt; ++i) {
+ if (filter == null || filter.apply(this, io, curPageAddr, i))
+ return getRow(io, curPageAddr, i);
+ }
+
+ long nextPageId = io.getForward(curPageAddr);
+
+ if (nextPageId == 0)
+ return null;
+
+ long nextPage = acquirePage(nextPageId);
+
+ try {
+ long nextPageAddr = readLock(nextPageId, nextPage);
+
+ // In the current implementation the next page can't change when the current page is locked.
+ assert nextPageAddr != 0 : nextPageAddr;
+
+ try {
+ long pa = curPageAddr;
+ curPageAddr = 0; // Set to zero to avoid double unlocking in finalizer.
+
+ readUnlock(curPageId, curPage, pa);
+
+ long p = curPage;
+ curPage = 0; // Set to zero to avoid double release in finalizer.
+
+ releasePage(curPageId, p);
+
+ curPageId = nextPageId;
+ curPage = nextPage;
+ curPageAddr = nextPageAddr;
+
+ nextPage = 0;
+ nextPageAddr = 0;
+ }
+ finally {
+ if (nextPageAddr != 0)
+ readUnlock(nextPageId, nextPage, nextPageAddr);
+ }
+ }
+ finally {
+ if (nextPage != 0)
+ releasePage(nextPageId, nextPage);
+ }
+ }
+ }
+ finally {
+ if (curPageAddr != 0)
+ readUnlock(curPageId, curPage, curPageAddr);
+ }
}
finally {
- readUnlock(firstPageId, page, pageAddr);
+ if (curPage != 0)
+ releasePage(curPageId, curPage);
}
}
- finally {
- releasePage(firstPageId, page);
- }
}
catch (IgniteCheckedException e) {
throw new IgniteCheckedException("Runtime failure on first row lookup", e);
@@ -1056,13 +1185,26 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public T findLast() throws IgniteCheckedException {
+ return findLast(null);
+ }
+
+ /**
+ * Returns a value mapped to the greatest key, or {@code null} if tree is empty or no entry matches the passed filter.
+ * @param c Filter closure.
+ * @return Value.
+ * @throws IgniteCheckedException If failed.
+ */
+ public T findLast(final TreeRowClosure<L, T> c) throws IgniteCheckedException {
checkDestroyed();
try {
- GetOne g = new GetOne(null, null, true);
- doFind(g);
+ if (c == null) {
+ GetOne g = new GetOne(null, null, null, true);
+ doFind(g);
- return (T)g.row;
+ return (T)g.row;
+ } else
+ return new GetLast(c).find();
}
catch (IgniteCheckedException e) {
throw new IgniteCheckedException("Runtime failure on last row lookup", e);
@@ -1078,15 +1220,25 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/**
* @param row Lookup row for exact match.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ * @return Found result or {@code null}
+ * @throws IgniteCheckedException If failed.
+ */
+ public final <R> R findOne(L row, Object x) throws IgniteCheckedException {
+ return findOne(row, null, x);
+ }
+
+ /**
+ * @param row Lookup row for exact match.
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
* @return Found result or {@code null}.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
- public final <R> R findOne(L row, Object x) throws IgniteCheckedException {
+ public final <R> R findOne(L row, TreeRowClosure<L, T> c, Object x) throws IgniteCheckedException {
checkDestroyed();
try {
- GetOne g = new GetOne(row, x, false);
+ GetOne g = new GetOne(row, c, x, false);
doFind(g);
@@ -1110,7 +1262,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
*/
@SuppressWarnings("unchecked")
@Override public final T findOne(L row) throws IgniteCheckedException {
- return findOne(row, null);
+ return findOne(row, null, null);
}
/**
@@ -2388,6 +2540,88 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
}
}
+
+ /**
+ * @param c Get.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void doVisit(TreeVisitor c) throws IgniteCheckedException {
+ for (;;) { // Go down with retries.
+ c.init();
+
+ switch (visitDown(c, c.rootId, 0L, c.rootLvl)) {
+ case RETRY:
+ case RETRY_ROOT:
+ checkInterrupted();
+
+ continue;
+
+ default:
+ return;
+ }
+ }
+ }
+
+ /**
+ * @param v Tree visitor.
+ * @param pageId Page ID.
+ * @param fwdId Expected forward page ID.
+ * @param lvl Level.
+ * @return Result code.
+ * @throws IgniteCheckedException If failed.
+ */
+ private Result visitDown(final TreeVisitor v, final long pageId, final long fwdId, final int lvl)
+ throws IgniteCheckedException {
+ long page = acquirePage(pageId);
+
+ try {
+ for (;;) {
+ v.checkLockRetry();
+
+ // Init args.
+ v.pageId = pageId;
+ v.fwdId = fwdId;
+
+ Result res = read(pageId, page, search, v, lvl, RETRY);
+
+ switch (res) {
+ case GO_DOWN:
+ case GO_DOWN_X:
+ assert v.pageId != pageId;
+ assert v.fwdId != fwdId || fwdId == 0;
+
+ // Go down recursively.
+ res = visitDown(v, v.pageId, v.fwdId, lvl - 1);
+
+ switch (res) {
+ case RETRY:
+ checkInterrupted();
+
+ continue; // The child page got split, need to reread our page.
+
+ default:
+ return res;
+ }
+
+ case NOT_FOUND:
+ assert lvl == 0 : lvl;
+
+ return v.init(pageId, page, fwdId);
+
+ case FOUND:
+ throw new IllegalStateException(); // Must never be called because we always have a shift.
+
+ default:
+ return res;
+ }
+ }
+ }
+ finally {
+ if (v.canRelease(pageId, lvl))
+ releasePage(pageId, page);
+ }
+ }
+
/**
* @param io IO.
* @param pageAddr Page address.
@@ -2602,15 +2836,20 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/** */
Object x;
+ /** */
+ TreeRowClosure<L, T> c;
+
/**
* @param row Row.
+ * @param c Closure filter.
* @param x Implementation specific argument.
* @param findLast Ignore row passed, find last row
*/
- private GetOne(L row, Object x, boolean findLast) {
+ private GetOne(L row, TreeRowClosure<L,T> c, Object x, boolean findLast) {
super(row, findLast);
this.x = x;
+ this.c = c;
}
/** {@inheritDoc} */
@@ -2620,7 +2859,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
if (lvl != 0 && !canGetRowFromInner)
return false;
- row = getRow(io, pageAddr, idx, x);
+ row = c == null || c.apply(BPlusTree.this, io,pageAddr, idx) ? getRow(io, pageAddr, idx, x) : null;
return true;
}
@@ -2631,14 +2870,14 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
*/
private final class GetCursor extends Get {
/** */
- ForwardCursor cursor;
+ AbstractForwardCursor cursor;
/**
* @param lower Lower bound.
* @param shift Shift.
* @param cursor Cursor.
*/
- GetCursor(L lower, int shift, ForwardCursor cursor) {
+ GetCursor(L lower, int shift, AbstractForwardCursor cursor) {
super(lower, false);
assert shift != 0; // Either handle range of equal rows or find a greater row after concurrent merge.
@@ -2664,97 +2903,452 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
}
/**
- * Put operation.
+ * Get a cursor for range.
*/
- public final class Put extends Get {
- /** Mark of NULL value of page id. It means valid value can't be equal this value. */
- private static final long NULL_PAGE_ID = 0L;
-
- /** Mark of NULL value of page. */
- private static final long NULL_PAGE = 0L;
-
- /** Mark of NULL value of page address. */
- private static final long NULL_PAGE_ADDRESS = 0L;
-
- /** Right child page ID for split row. */
- long rightId;
-
- /** Replaced row if any. */
- T oldRow;
-
- /**
- * This page is kept locked after split until insert to the upper level will not be finished. It is needed
- * because split row will be "in flight" and if we'll release tail, remove on split row may fail.
- */
- long tailId;
-
+ private final class TreeVisitor extends Get {
/** */
- long tailPage;
+ long nextPageId;
/** */
- long tailAddr;
+ L upper;
- /**
- * Bottom level for insertion (insert can't go deeper). Will be incremented on split on each level.
- */
- short btmLvl;
+ /** */
+ TreeVisitorClosure<L, T> p;
/** */
- Bool needReplaceInner = FALSE;
+ private boolean dirty;
/** */
- final boolean needOld;
+ private boolean writing;
/**
- * @param row Row.
- * @param needOld {@code True} If need return old value.
+ * @param lower Lower bound.
*/
- private Put(T row, boolean needOld) {
- super(row, false);
+ TreeVisitor(L lower, L upper, TreeVisitorClosure<L, T> p) {
+ super(lower, false);
- this.needOld = needOld;
+ this.shift = -1;
+ this.upper = upper;
+ this.p = p;
}
/** {@inheritDoc} */
- @Override boolean found(BPlusIO<L> io, long pageAddr, int idx, int lvl) {
- if (lvl == 0) // Leaf: need to stop.
- return true;
+ @Override boolean found(BPlusIO<L> io, long pageAddr, int idx, int lvl) throws IgniteCheckedException {
+ throw new IllegalStateException(); // Must never be called because we always have a shift.
+ }
- assert btmLvl == 0; // It can not be insert.
+ /** {@inheritDoc} */
+ @Override boolean notFound(BPlusIO<L> io, long pageAddr, int idx, int lvl) throws IgniteCheckedException {
+ if (lvl != 0)
+ return false;
- // If we can get full row from the inner page, we have to replace it with the new one. On the way down
- // we can not miss inner key even in presence of concurrent operations because of `triangle` invariant +
- // concurrent inner replace handling by retrying from root.
- if (canGetRowFromInner && needReplaceInner == FALSE)
- needReplaceInner = TRUE;
+ if (!(writing = (p.state() & TreeVisitorClosure.CAN_WRITE) != 0))
+ init(pageAddr, io, idx);
- return false;
+ return true;
}
- /** {@inheritDoc} */
- @Override boolean notFound(BPlusIO<L> io, long pageAddr, int idx, int lvl) {
- assert btmLvl >= 0 : btmLvl;
- assert lvl >= btmLvl : lvl;
+ Result init(long pageId, long page, long fwdId) throws IgniteCheckedException {
+ // Init args.
+ this.pageId = pageId;
+ this.fwdId = fwdId;
- return lvl == btmLvl;
- }
+ if (writing) {
+ long pageAddr = writeLock(pageId, page);
- /**
- * @param tailId Tail page ID.
- * @param tailPage Tail page pointer.
- * @param tailPageAddr Tail page address
- */
- private void tail(long tailId, long tailPage, long tailPageAddr) {
- assert (tailId == NULL_PAGE_ID) == (tailPage == NULL_PAGE);
- assert (tailPage == NULL_PAGE) == (tailPageAddr == NULL_PAGE_ADDRESS);
+ if (pageAddr == 0)
+ return RETRY;
- if (this.tailPage != NULL_PAGE)
- writeUnlockAndClose(this.tailId, this.tailPage, this.tailAddr, null);
+ try {
+ BPlusIO<L> io = io(pageAddr);
- this.tailId = tailId;
- this.tailPage = tailPage;
- this.tailAddr = tailPageAddr;
- }
+ // Check triangle invariant.
+ if (io.getForward(pageAddr) != fwdId)
+ return RETRY;
+
+ init(pageAddr, io, -1);
+ } finally {
+ unlock(pageId, page, pageAddr);
+ }
+ }
+
+ return NOT_FOUND;
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param io IO.
+ * @param startIdx Start index.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void init(long pageAddr, BPlusIO<L> io, int startIdx) throws IgniteCheckedException {
+ nextPageId = 0;
+
+ int cnt = io.getCount(pageAddr);
+
+ if (cnt != 0)
+ visit(pageAddr, io, startIdx, cnt);
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param io IO.
+ * @param startIdx Start index.
+ * @param cnt Number of rows in the buffer.
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("unchecked")
+ private void visit(long pageAddr, BPlusIO<L> io, int startIdx, int cnt)
+ throws IgniteCheckedException {
+ assert io.isLeaf() : io;
+ assert cnt != 0 : cnt; // We can not see empty pages (empty tree handled in init).
+ assert startIdx >= -1 : startIdx;
+ assert cnt >= startIdx;
+
+ checkDestroyed();
+
+ nextPageId = io.getForward(pageAddr);
+
+ if (startIdx == -1)
+ startIdx = findLowerBound(pageAddr, io, cnt);
+
+ if (cnt == startIdx)
+ return; // Go to the next page;
+
+ cnt = findUpperBound(pageAddr, io, startIdx, cnt);
+
+ for (int i = startIdx; i < cnt; i++) {
+ int state = p.visit(BPlusTree.this, io, pageAddr, i, wal);
+
+ boolean stop = (state & TreeVisitorClosure.STOP) != 0;
+
+ if (writing)
+ dirty = dirty || (state & TreeVisitorClosure.DIRTY) != 0;
+
+ if (stop) {
+ nextPageId = 0; // The End.
+
+ return;
+ }
+ }
+
+ if (nextPageId != 0) {
+ row = io.getLookupRow(BPlusTree.this, pageAddr, cnt - 1); // Need save last row.
+
+ shift = 1;
+ }
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param io IO.
+ * @param cnt Count.
+ * @return Adjusted to lower bound start index.
+ * @throws IgniteCheckedException If failed.
+ */
+ private int findLowerBound(long pageAddr, BPlusIO<L> io, int cnt) throws IgniteCheckedException {
+ assert io.isLeaf();
+
+ // Compare with the first row on the page.
+ int cmp = compare(0, io, pageAddr, 0, row);
+
+ if (cmp < 0 || (cmp == 0 && shift == 1)) {
+ int idx = findInsertionPoint(0, io, pageAddr, 0, cnt, row, shift);
+
+ assert idx < 0;
+
+ return fix(idx);
+ }
+
+ return 0;
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param io IO.
+ * @param low Start index.
+ * @param cnt Number of rows in the buffer.
+ * @return Corrected number of rows with respect to upper bound.
+ * @throws IgniteCheckedException If failed.
+ */
+ private int findUpperBound(long pageAddr, BPlusIO<L> io, int low, int cnt) throws IgniteCheckedException {
+ assert io.isLeaf();
+
+ // Compare with the last row on the page.
+ int cmp = compare(0, io, pageAddr, cnt - 1, upper);
+
+ if (cmp > 0) {
+ int idx = findInsertionPoint(0, io, pageAddr, low, cnt, upper, 1);
+
+ assert idx < 0;
+
+ cnt = fix(idx);
+
+ nextPageId = 0; // The End.
+ }
+
+ return cnt;
+ }
+
+ /**
+ * @throws IgniteCheckedException If failed.
+ */
+ private void nextPage() throws IgniteCheckedException {
+ for (;;) {
+ if (nextPageId == 0)
+ return;
+
+ long pageId = nextPageId;
+ long page = acquirePage(pageId);
+ try {
+ long pageAddr = lock(pageId, page); // Doing explicit null check.
+
+ // If concurrent merge occurred we have to reinitialize cursor from the last returned row.
+ if (pageAddr == 0L)
+ break;
+
+ try {
+ BPlusIO<L> io = io(pageAddr);
+
+ visit(pageAddr, io, -1, io.getCount(pageAddr));
+ }
+ finally {
+ unlock(pageId, page, pageAddr);
+ }
+ }
+ finally {
+ releasePage(pageId, page);
+ }
+ }
+
+ doVisit(this); // restart from last read row
+ }
+
+ private void unlock(long pageId, long page, long pageAddr) {
+ if (writing) {
+ writeUnlock(pageId, page, pageAddr, dirty);
+
+ dirty = false; // reset dirty flag
+ }
+ else
+ readUnlock(pageId, page, pageAddr);
+ }
+
+ private long lock(long pageId, long page) {
+ if (writing = ((p.state() & TreeVisitorClosure.CAN_WRITE) != 0))
+ return writeLock(pageId, page);
+ else
+ return readLock(pageId, page);
+ }
+
+ /**
+ * @throws IgniteCheckedException If failed.
+ */
+ private void visit() throws IgniteCheckedException {
+ doVisit(this);
+
+ while (nextPageId != 0)
+ nextPage();
+ }
+ }
+
+ /**
+ * Get the last item in the tree which matches the passed filter.
+ */
+ private final class GetLast extends Get {
+ private final TreeRowClosure<L, T> c;
+ private boolean retry = true;
+ private long lastPageId;
+ private T row0;
+
+ /**
+ * @param c Filter closure.
+ */
+ public GetLast(TreeRowClosure<L, T> c) {
+ super(null, true);
+
+ assert c != null;
+
+ this.c = c;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean found(BPlusIO<L> io, long pageAddr, int idx, int lvl) throws IgniteCheckedException {
+ if (lvl != 0)
+ return false;
+
+ for (int i = idx; i >= 0; i--) {
+ if (c.apply(BPlusTree.this, io, pageAddr, i)) {
+ retry = false;
+ row0 = getRow(io, pageAddr, i);
+
+ return true;
+ }
+ }
+
+ if(pageId == rootId)
+ retry = false; // We are at the root page, there are no other leafs.
+
+ if (retry) {
+ findLast = false;
+
+ // Restart from an item before the first item in the leaf (last item on the previous leaf).
+ row0 = getRow(io, pageAddr, 0);
+ shift = -1;
+
+ lastPageId = pageId; // Track leafs to detect a loop over the first leaf in the tree.
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean notFound(BPlusIO<L> io, long pageAddr, int idx, int lvl) throws IgniteCheckedException {
+ if (lvl != 0)
+ return false;
+
+ if(io.getCount(pageAddr) == 0) {
+ // it's an empty tree
+ retry = false;
+
+ return true;
+ }
+
+ if (idx == 0 && lastPageId == pageId) {
+ // not found
+ retry = false;
+ row0 = null;
+
+ return true;
+ }
+ else {
+ for (int i = idx; i >= 0; i--) {
+ if (c.apply(BPlusTree.this, io, pageAddr, i)) {
+ retry = false;
+ row0 = getRow(io, pageAddr, i);
+
+ break;
+ }
+ }
+ }
+
+ if (retry) {
+ // Restart from an item before the first item in the leaf (last item on the previous leaf).
+ row0 = getRow(io, pageAddr, 0);
+
+ lastPageId = pageId; // Track leafs to detect a loop over the first leaf in the tree.
+ }
+
+ return true;
+ }
+
+ /**
+ * @return Last item in the tree.
+ * @throws IgniteCheckedException If failure.
+ */
+ public T find() throws IgniteCheckedException {
+ while (retry) {
+ row = row0;
+
+ doFind(this);
+ }
+
+ return row0;
+ }
+ }
+
+ /**
+ * Put operation.
+ */
+ public final class Put extends Get {
+ /** Mark of NULL value of page id. It means valid value can't be equal this value. */
+ private static final long NULL_PAGE_ID = 0L;
+
+ /** Mark of NULL value of page. */
+ private static final long NULL_PAGE = 0L;
+
+ /** Mark of NULL value of page address. */
+ private static final long NULL_PAGE_ADDRESS = 0L;
+
+ /** Right child page ID for split row. */
+ long rightId;
+
+ /** Replaced row if any. */
+ T oldRow;
+
+ /**
+ * This page is kept locked after split until insert to the upper level will not be finished. It is needed
+ * because split row will be "in flight" and if we'll release tail, remove on split row may fail.
+ */
+ long tailId;
+
+ /** */
+ long tailPage;
+
+ /** */
+ long tailAddr;
+
+ /**
+ * Bottom level for insertion (insert can't go deeper). Will be incremented on split on each level.
+ */
+ short btmLvl;
+
+ /** */
+ Bool needReplaceInner = FALSE;
+
+ /** */
+ final boolean needOld;
+
+ /**
+ * @param row Row.
+ * @param needOld {@code True} If need return old value.
+ */
+ private Put(T row, boolean needOld) {
+ super(row, false);
+
+ this.needOld = needOld;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean found(BPlusIO<L> io, long pageAddr, int idx, int lvl) {
+ if (lvl == 0) // Leaf: need to stop.
+ return true;
+
+ assert btmLvl == 0; // It can not be insert.
+
+ // If we can get full row from the inner page, we have to replace it with the new one. On the way down
+ // we can not miss inner key even in presence of concurrent operations because of `triangle` invariant +
+ // concurrent inner replace handling by retrying from root.
+ if (canGetRowFromInner && needReplaceInner == FALSE)
+ needReplaceInner = TRUE;
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean notFound(BPlusIO<L> io, long pageAddr, int idx, int lvl) {
+ assert btmLvl >= 0 : btmLvl;
+ assert lvl >= btmLvl : lvl;
+
+ return lvl == btmLvl;
+ }
+
+ /**
+ * @param tailId Tail page ID.
+ * @param tailPage Tail page pointer.
+ * @param tailPageAddr Tail page address
+ */
+ private void tail(long tailId, long tailPage, long tailPageAddr) {
+ assert (tailId == NULL_PAGE_ID) == (tailPage == NULL_PAGE);
+ assert (tailPage == NULL_PAGE) == (tailPageAddr == NULL_PAGE_ADDRESS);
+
+ if (this.tailPage != NULL_PAGE)
+ writeUnlockAndClose(this.tailId, this.tailPage, this.tailAddr, null);
+
+ this.tailId = tailId;
+ this.tailPage = tailPage;
+ this.tailAddr = tailPageAddr;
+ }
/** {@inheritDoc} */
@Override public boolean canRelease(long pageId, int lvl) {
@@ -4520,7 +5114,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @return Full detached data row.
* @throws IgniteCheckedException If failed.
*/
- protected final T getRow(BPlusIO<L> io, long pageAddr, int idx) throws IgniteCheckedException {
+ public final T getRow(BPlusIO<L> io, long pageAddr, int idx) throws IgniteCheckedException {
return getRow(io, pageAddr, idx, null);
}
@@ -4534,54 +5128,60 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @return Data row.
* @throws IgniteCheckedException If failed.
*/
- protected abstract T getRow(BPlusIO<L> io, long pageAddr, int idx, Object x) throws IgniteCheckedException;
+ public abstract T getRow(BPlusIO<L> io, long pageAddr, int idx, Object x) throws IgniteCheckedException;
/**
- * Forward cursor.
+ *
*/
@SuppressWarnings("unchecked")
- private final class ForwardCursor implements GridCursor<T> {
+ private abstract class AbstractForwardCursor {
/** */
- private T[] rows = (T[])EMPTY;
-
- /** */
- private int row = -1;
+ long nextPageId;
/** */
- private long nextPageId;
-
- /** */
- private L lowerBound;
+ L lowerBound;
/** */
private int lowerShift = -1; // Initially it is -1 to handle multiple equal rows.
/** */
- private final L upperBound;
-
- /** */
- private final Object x;
+ final L upperBound;
/**
* @param lowerBound Lower bound.
* @param upperBound Upper bound.
*/
- ForwardCursor(L lowerBound, L upperBound) {
+ AbstractForwardCursor(L lowerBound, L upperBound) {
this.lowerBound = lowerBound;
this.upperBound = upperBound;
- this.x = null;
}
/**
- * @param lowerBound Lower bound.
- * @param upperBound Upper bound.
- * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ *
*/
- ForwardCursor(L lowerBound, L upperBound, Object x) {
- this.lowerBound = lowerBound;
- this.upperBound = upperBound;
- this.x = x;
- }
+ abstract void init0();
+
+ /**
+ * @param pageAddr Page address.
+ * @param io IO.
+ * @param startIdx Start index.
+ * @param cnt Number of rows in the buffer.
+ * @return {@code true} If we were able to fetch rows from this page.
+ * @throws IgniteCheckedException If failed.
+ */
+ abstract boolean fillFromBuffer0(long pageAddr, BPlusIO<L> io, int startIdx, int cnt)
+ throws IgniteCheckedException;
+
+ /**
+ * @return {@code True} If we have rows to return after reading the next page.
+ * @throws IgniteCheckedException If failed.
+ */
+ abstract boolean reinitialize0() throws IgniteCheckedException;
+
+ /**
+ * @param readDone {@code True} if traversed all rows.
+ */
+ abstract void onNotFound(boolean readDone);
/**
* @param pageAddr Page address.
@@ -4589,9 +5189,10 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param startIdx Start index.
* @throws IgniteCheckedException If failed.
*/
- private void init(long pageAddr, BPlusIO<L> io, int startIdx) throws IgniteCheckedException {
+ final void init(long pageAddr, BPlusIO<L> io, int startIdx) throws IgniteCheckedException {
nextPageId = 0;
- row = -1;
+
+ init0();
int cnt = io.getCount(pageAddr);
@@ -4599,16 +5200,10 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
if (cnt == 0) {
assert io.getForward(pageAddr) == 0L;
- rows = null;
- }
- else if (!fillFromBuffer(pageAddr, io, startIdx, cnt)) {
- if (rows != EMPTY) {
- assert rows.length > 0; // Otherwise it makes no sense to create an array.
-
- // Fake clear.
- rows[0] = null;
- }
+ onNotFound(true);
}
+ else if (!fillFromBuffer(pageAddr, io, startIdx, cnt))
+ onNotFound(false);
}
/**
@@ -4618,7 +5213,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @return Adjusted to lower bound start index.
* @throws IgniteCheckedException If failed.
*/
- private int findLowerBound(long pageAddr, BPlusIO<L> io, int cnt) throws IgniteCheckedException {
+ final int findLowerBound(long pageAddr, BPlusIO<L> io, int cnt) throws IgniteCheckedException {
assert io.isLeaf();
// Compare with the first row on the page.
@@ -4643,7 +5238,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @return Corrected number of rows with respect to upper bound.
* @throws IgniteCheckedException If failed.
*/
- private int findUpperBound(long pageAddr, BPlusIO<L> io, int low, int cnt) throws IgniteCheckedException {
+ final int findUpperBound(long pageAddr, BPlusIO<L> io, int low, int cnt) throws IgniteCheckedException {
assert io.isLeaf();
// Compare with the last row on the page.
@@ -4675,84 +5270,20 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
throws IgniteCheckedException {
assert io.isLeaf() : io;
assert cnt != 0 : cnt; // We can not see empty pages (empty tree handled in init).
- assert startIdx >= 0 : startIdx;
+ assert startIdx >= 0 || startIdx == -1: startIdx;
assert cnt >= startIdx;
checkDestroyed();
nextPageId = io.getForward(pageAddr);
- if (lowerBound != null && startIdx == 0)
- startIdx = findLowerBound(pageAddr, io, cnt);
-
- if (upperBound != null && cnt != startIdx)
- cnt = findUpperBound(pageAddr, io, startIdx, cnt);
-
- cnt -= startIdx;
-
- if (cnt == 0)
- return false;
-
- if (rows == EMPTY)
- rows = (T[])new Object[cnt];
-
- int foundCnt = 0;
-
- for (int i = 0; i < cnt; i++) {
- T r = getRow(io, pageAddr, startIdx + i, x);
-
- if (r != null)
- rows = GridArrays.set(rows, foundCnt++, r);
- }
-
- if (foundCnt == 0) {
- rows = (T[])EMPTY;
-
- return false;
- }
-
- GridArrays.clearTail(rows, foundCnt);
-
- return true;
- }
-
- /** {@inheritDoc} */
- @SuppressWarnings("SimplifiableIfStatement")
- @Override public boolean next() throws IgniteCheckedException {
- if (rows == null)
- return false;
-
- if (++row < rows.length && rows[row] != null) {
- clearLastRow(); // Allow to GC the last returned row.
-
- return true;
- }
-
- return nextPage();
- }
-
- /**
- * @return Cleared last row.
- */
- private T clearLastRow() {
- if (row == 0)
- return null;
-
- int last = row - 1;
-
- T r = rows[last];
-
- assert r != null;
-
- rows[last] = null;
-
- return r;
+ return fillFromBuffer0(pageAddr, io, startIdx, cnt);
}
/**
* @throws IgniteCheckedException If failed.
*/
- private void find() throws IgniteCheckedException {
+ final void find() throws IgniteCheckedException {
assert lowerBound != null;
doFind(new GetCursor(lowerBound, lowerShift, this));
@@ -4768,21 +5299,20 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
// to the previous lower bound.
find();
- return next();
+ return reinitialize0();
}
/**
+ * @param lastRow Last read row (to be used as new lower bound).
* @return {@code true} If we have rows to return after reading the next page.
* @throws IgniteCheckedException If failed.
*/
- private boolean nextPage() throws IgniteCheckedException {
- updateLowerBound(clearLastRow());
-
- row = 0;
+ final boolean nextPage(L lastRow) throws IgniteCheckedException {
+ updateLowerBound(lastRow);
for (;;) {
if (nextPageId == 0) {
- rows = null;
+ onNotFound(true);
return false; // Done.
}
@@ -4799,7 +5329,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
try {
BPlusIO<L> io = io(pageAddr);
- if (fillFromBuffer(pageAddr, io, 0, io.getCount(pageAddr)))
+ if (fillFromBuffer(pageAddr, io, -1, io.getCount(pageAddr)))
return true;
// Continue fetching forward.
@@ -4820,12 +5350,239 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/**
* @param lower New exact lower bound.
*/
- private void updateLowerBound(T lower) {
+ private void updateLowerBound(L lower) {
if (lower != null) {
lowerShift = 1; // Now we have the full row an need to avoid duplicates.
lowerBound = lower; // Move the lower bound forward for further concurrent merge retries.
}
}
+ }
+
+ /**
+ * Closure cursor.
+ */
+ @SuppressWarnings("unchecked")
+ private final class ClosureCursor extends AbstractForwardCursor {
+ /** */
+ private final TreeRowClosure<L, T> p;
+
+ /** */
+ private L lastRow;
+
+ /**
+ * @param lowerBound Lower bound.
+ * @param upperBound Upper bound.
+ * @param p Row predicate.
+ */
+ ClosureCursor(L lowerBound, L upperBound, TreeRowClosure<L, T> p) {
+ super(lowerBound, upperBound);
+
+ assert lowerBound != null;
+ assert upperBound != null;
+ assert p != null;
+
+ this.p = p;
+ }
+
+ /** {@inheritDoc} */
+ @Override void init0() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean fillFromBuffer0(long pageAddr, BPlusIO<L> io, int startIdx, int cnt)
+ throws IgniteCheckedException {
+ if (startIdx == -1)
+ startIdx = findLowerBound(pageAddr, io, cnt);
+
+ if (cnt == startIdx)
+ return false;
+
+ for (int i = startIdx; i < cnt; i++) {
+ int cmp = compare(0, io, pageAddr, i, upperBound);
+
+ if (cmp > 0) {
+ nextPageId = 0; // The End.
+
+ return false;
+ }
+
+ boolean stop = !p.apply(BPlusTree.this, io, pageAddr, i);
+
+ if (stop) {
+ nextPageId = 0; // The End.
+
+ return true;
+ }
+ }
+
+ if (nextPageId != 0)
+ lastRow = io.getLookupRow(BPlusTree.this, pageAddr, cnt - 1); // Need save last row.
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean reinitialize0() throws IgniteCheckedException {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override void onNotFound(boolean readDone) {
+ nextPageId = 0;
+ }
+
+ /**
+ * @throws IgniteCheckedException If failed.
+ */
+ private void iterate() throws IgniteCheckedException {
+ find();
+
+ if (nextPageId == 0) {
+ return;
+ }
+
+ for (;;) {
+ L lastRow0 = lastRow;
+
+ lastRow = null;
+
+ nextPage(lastRow0);
+
+ if (nextPageId == 0)
+ return;
+ }
+ }
+ }
+
+ /**
+ * Forward cursor.
+ */
+ @SuppressWarnings("unchecked")
+ private final class ForwardCursor extends AbstractForwardCursor implements GridCursor<T> {
+ /** */
+ final Object x;
+
+ /** */
+ private T[] rows = (T[])EMPTY;
+
+ /** */
+ private int row = -1;
+
+ /** */
+ private final TreeRowClosure<L, T> c;
+
+ /**
+ * @param lowerBound Lower bound.
+ * @param upperBound Upper bound.
+ * @param c Filter closure.
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ */
+ ForwardCursor(L lowerBound, L upperBound, TreeRowClosure<L, T> c, Object x) {
+ super(lowerBound, upperBound);
+
+ this.c = c;
+ this.x = x;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean fillFromBuffer0(long pageAddr, BPlusIO<L> io, int startIdx, int cnt) throws IgniteCheckedException {
+ if (startIdx == -1) {
+ if (lowerBound != null)
+ startIdx = findLowerBound(pageAddr, io, cnt);
+ else
+ startIdx = 0;
+ }
+
+ if (upperBound != null && cnt != startIdx)
+ cnt = findUpperBound(pageAddr, io, startIdx, cnt);
+
+ int cnt0 = cnt - startIdx;
+
+ if (cnt0 == 0)
+ return false;
+
+ if (rows == EMPTY)
+ rows = (T[])new Object[cnt0];
+
+ int resCnt = 0;
+
+ for (int idx = startIdx; idx < cnt; idx++) {
+ if (c == null || c.apply(BPlusTree.this, io, pageAddr, idx))
+ rows = GridArrays.set(rows, resCnt++, getRow(io, pageAddr, idx, x));
+ }
+
+ if (resCnt == 0) {
+ rows = (T[])EMPTY;
+
+ return false;
+ }
+
+ GridArrays.clearTail(rows, resCnt);
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override boolean reinitialize0() throws IgniteCheckedException {
+ return next();
+ }
+
+ /** {@inheritDoc} */
+ @Override void onNotFound(boolean readDone) {
+ if (readDone)
+ rows = null;
+ else {
+ if (rows != EMPTY) {
+ assert rows.length > 0; // Otherwise it makes no sense to create an array.
+
+ // Fake clear.
+ rows[0] = null;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override void init0() {
+ row = -1;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("SimplifiableIfStatement")
+ @Override public boolean next() throws IgniteCheckedException {
+ if (rows == null)
+ return false;
+
+ if (++row < rows.length && rows[row] != null) {
+ clearLastRow(); // Allow to GC the last returned row.
+
+ return true;
+ }
+
+ T lastRow = clearLastRow();
+
+ row = 0;
+
+ return nextPage(lastRow);
+ }
+
+ /**
+ * @return Cleared last row.
+ */
+ private T clearLastRow() {
+ if (row == 0)
+ return null;
+
+ int last = row - 1;
+
+ T r = rows[last];
+
+ assert r != null;
+
+ rows[last] = null;
+
+ return r;
+ }
/** {@inheritDoc} */
@Override public T get() {
@@ -4962,6 +5719,36 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
}
/**
+ * A generic visitor-style interface for performing inspection/modification operations on the tree.
+ */
+ public interface TreeVisitorClosure<L, T extends L> {
+ /** */
+ int STOP = 0x01;
+ /** */
+ int CAN_WRITE = STOP << 1;
+ /** */
+ int DIRTY = CAN_WRITE << 1;
+
+ /**
+ * Performs inspection or operation on a specified row.
+ *
+ * @param tree The tree.
+ * @param io Th tree IO object.
+ * @param pageAddr The page address.
+ * @param idx The item index.
+ * @return state bitset.
+ * @throws IgniteCheckedException If failed.
+ */
+ public int visit(BPlusTree<L, T> tree, BPlusIO<L> io, long pageAddr, int idx, IgniteWriteAheadLogManager wal)
+ throws IgniteCheckedException;
+
+ /**
+ * @return state bitset.
+ */
+ public int state();
+ }
+
+ /**
* @return Return number of retries.
*/
protected int getLockRetries() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
index 50b5779..4a12045 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
@@ -81,6 +81,7 @@ public abstract class AbstractDataPageIO<T extends Storable> extends PageIO {
public static final int MIN_DATA_PAGE_OVERHEAD = ITEMS_OFF + ITEM_SIZE + PAYLOAD_LEN_SIZE + LINK_SIZE;
/**
+ * @param type Page type.
* @param ver Page format version.
*/
protected AbstractDataPageIO(int type, int ver) {
@@ -502,6 +503,23 @@ public abstract class AbstractDataPageIO<T extends Storable> extends PageIO {
/**
* @param pageAddr Page address.
+ * @param itemId Item to position on.
+ * @param pageSize Page size.
+ * @param reqLen Required payload length.
+ * @return Offset to start of actual fragment data.
+ */
+ public int getPayloadOffset(final long pageAddr, final int itemId, final int pageSize, int reqLen) {
+ int dataOff = getDataOffset(pageAddr, itemId, pageSize);
+
+ int payloadSize = getPageEntrySize(pageAddr, dataOff, 0);
+
+ assert payloadSize >= reqLen : payloadSize;
+
+ return dataOff + PAYLOAD_LEN_SIZE + (isFragmented(pageAddr, dataOff) ? LINK_SIZE : 0);
+ }
+
+ /**
+ * @param pageAddr Page address.
* @param idx Item index.
* @return Item.
*/
@@ -982,6 +1000,16 @@ public abstract class AbstractDataPageIO<T extends Storable> extends PageIO {
int payloadSize = payload != null ? payload.length :
Math.min(rowSize - written, getFreeSpace(pageAddr));
+ if (row != null) {
+ int remain = rowSize - written - payloadSize;
+ int hdrSize = row.headerSize();
+
+ // We need page header (i.e. MVCC info) is located entirely on the very first page in chain.
+ // So we force moving it to the next page if it could not fit entirely on this page.
+ if (remain > 0 && remain < hdrSize)
+ payloadSize -= hdrSize - remain;
+ }
+
int fullEntrySize = getPageEntrySize(payloadSize, SHOW_PAYLOAD_LEN | SHOW_LINK | SHOW_ITEM);
int dataOff = getDataOffsetForWrite(pageAddr, fullEntrySize, directCnt, indirectCnt, pageSize);
@@ -1227,13 +1255,6 @@ public abstract class AbstractDataPageIO<T extends Storable> extends PageIO {
}
/**
- * @param row Row.
- * @return Row size in page.
- * @throws IgniteCheckedException if failed.
- */
- public abstract int getRowSize(T row) throws IgniteCheckedException;
-
- /**
* Defines closure interface for applying computations to data page items.
*
* @param <T> Closure return type.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/DataPageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/DataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/DataPageIO.java
index 8a04749..87e37f6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/DataPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/DataPageIO.java
@@ -21,16 +21,27 @@ import java.nio.ByteBuffer;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.processors.cache.CacheObject;
-import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.GridStringBuilder;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.EntryPart.CACHE_ID;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.EntryPart.EXPIRE_TIME;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.EntryPart.KEY;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.EntryPart.MVCC_INFO;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.EntryPart.VALUE;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.EntryPart.VERSION;
+
/**
* Data pages IO.
*/
public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
/** */
+ public static final int MVCC_INFO_SIZE = 40;
+
+ /** */
public static final IOVersions<DataPageIO> VERSIONS = new IOVersions<>(
new DataPageIO(1)
);
@@ -43,21 +54,74 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
}
/** {@inheritDoc} */
- @Override
- protected void writeFragmentData(
- final CacheDataRow row,
- final ByteBuffer buf,
- final int rowOff,
- final int payloadSize
- ) throws IgniteCheckedException {
+ @Override protected void writeRowData(long pageAddr, int dataOff, int payloadSize, CacheDataRow row,
+ boolean newRow) throws IgniteCheckedException {
+ long addr = pageAddr + dataOff;
+
+ int cacheIdSize = row.cacheId() != 0 ? 4 : 0;
+ int mvccInfoSize = row.mvccCoordinatorVersion() > 0 ? MVCC_INFO_SIZE : 0;
+
+ if (newRow) {
+ PageUtils.putShort(addr, 0, (short)payloadSize);
+ addr += 2;
+
+ if (mvccInfoSize > 0) {
+ assert MvccUtils.mvccVersionIsValid(row.mvccCoordinatorVersion(), row.mvccCounter(), row.mvccOperationCounter());
+
+ // xid_min.
+ PageUtils.putLong(addr, 0, row.mvccCoordinatorVersion());
+ PageUtils.putLong(addr, 8, row.mvccCounter());
+ PageUtils.putInt(addr, 16, row.mvccOperationCounter() | (row.mvccTxState() << MVCC_HINTS_BIT_OFF));
+
+ assert row.newMvccCoordinatorVersion() == 0
+ || MvccUtils.mvccVersionIsValid(row.newMvccCoordinatorVersion(), row.newMvccCounter(), row.newMvccOperationCounter());
+
+ // xid_max.
+ PageUtils.putLong(addr, 20, row.newMvccCoordinatorVersion());
+ PageUtils.putLong(addr, 28, row.newMvccCounter());
+ PageUtils.putInt(addr, 36, row.newMvccOperationCounter() | (row.newMvccTxState() << MVCC_HINTS_BIT_OFF));
+
+ addr += mvccInfoSize;
+ }
+
+ if (cacheIdSize != 0) {
+ PageUtils.putInt(addr, 0, row.cacheId());
+
+ addr += cacheIdSize;
+ }
+
+ addr += row.key().putValue(addr);
+ }
+ else
+ addr += (2 + mvccInfoSize + cacheIdSize + row.key().valueBytesLength(null));
+
+ addr += row.value().putValue(addr);
+
+ CacheVersionIO.write(addr, row.version(), false);
+ addr += CacheVersionIO.size(row.version(), false);
+
+ PageUtils.putLong(addr, 0, row.expireTime());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeFragmentData(CacheDataRow row, ByteBuffer buf, int rowOff,
+ int payloadSize) throws IgniteCheckedException {
final int keySize = row.key().valueBytesLength(null);
+
final int valSize = row.value().valueBytesLength(null);
- int written = writeFragment(row, buf, rowOff, payloadSize, EntryPart.CACHE_ID, keySize, valSize);
- written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.KEY, keySize, valSize);
- written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.EXPIRE_TIME, keySize, valSize);
- written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.VALUE, keySize, valSize);
- written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.VERSION, keySize, valSize);
+ int written = writeFragment(row, buf, rowOff, payloadSize,
+ MVCC_INFO, keySize, valSize);
+ written += writeFragment(row, buf, rowOff + written, payloadSize - written,
+ CACHE_ID, keySize, valSize);
+ written += writeFragment(row, buf, rowOff + written, payloadSize - written,
+ KEY, keySize, valSize);
+ written += writeFragment(row, buf, rowOff + written, payloadSize - written,
+ EXPIRE_TIME, keySize, valSize);
+ written += writeFragment(row, buf, rowOff + written, payloadSize - written,
+ VALUE, keySize, valSize);
+ written += writeFragment(row, buf, rowOff + written, payloadSize - written,
+ VERSION, keySize, valSize);
assert written == payloadSize;
}
@@ -65,9 +129,13 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
/**
* Try to write fragment data.
*
+ * @param row Row.
+ * @param buf Byte buffer.
* @param rowOff Offset in row data bytes.
* @param payloadSize Data length that should be written in this fragment.
* @param type Type of the part of entry.
+ * @param keySize Key size.
+ * @param valSize Value size.
* @return Actually written data.
* @throws IgniteCheckedException If fail.
*/
@@ -87,35 +155,42 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
final int curLen;
int cacheIdSize = row.cacheId() == 0 ? 0 : 4;
+ int mvccInfoSize = row.mvccCoordinatorVersion() > 0 ? MVCC_INFO_SIZE : 0;
switch (type) {
- case CACHE_ID:
+ case MVCC_INFO:
prevLen = 0;
- curLen = cacheIdSize;
+ curLen = mvccInfoSize;
+
+ break;
+
+ case CACHE_ID:
+ prevLen = mvccInfoSize;
+ curLen = mvccInfoSize + cacheIdSize;
break;
case KEY:
- prevLen = cacheIdSize;
- curLen = cacheIdSize + keySize;
+ prevLen = mvccInfoSize + cacheIdSize;
+ curLen = mvccInfoSize + cacheIdSize + keySize;
break;
case EXPIRE_TIME:
- prevLen = cacheIdSize + keySize;
- curLen = cacheIdSize + keySize + 8;
+ prevLen = mvccInfoSize + cacheIdSize + keySize;
+ curLen = mvccInfoSize + cacheIdSize + keySize + 8;
break;
case VALUE:
- prevLen = cacheIdSize + keySize + 8;
- curLen = cacheIdSize + keySize + valSize + 8;
+ prevLen = mvccInfoSize + cacheIdSize + keySize + 8;
+ curLen = mvccInfoSize + cacheIdSize + keySize + valSize + 8;
break;
case VERSION:
- prevLen = cacheIdSize + keySize + valSize + 8;
- curLen = cacheIdSize + keySize + valSize + CacheVersionIO.size(row.version(), false) + 8;
+ prevLen = mvccInfoSize + cacheIdSize + keySize + valSize + 8;
+ curLen = mvccInfoSize + cacheIdSize + keySize + valSize + CacheVersionIO.size(row.version(), false) + 8;
break;
@@ -128,13 +203,22 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
final int len = Math.min(curLen - rowOff, payloadSize);
- if (type == EntryPart.EXPIRE_TIME)
+ if (type == EXPIRE_TIME)
writeExpireTimeFragment(buf, row.expireTime(), rowOff, len, prevLen);
- else if (type == EntryPart.CACHE_ID)
+ else if (type == CACHE_ID)
writeCacheIdFragment(buf, row.cacheId(), rowOff, len, prevLen);
- else if (type != EntryPart.VERSION) {
+ else if (type == MVCC_INFO)
+ writeMvccInfoFragment(buf,
+ row.mvccCoordinatorVersion(),
+ row.mvccCounter(),
+ row.mvccOperationCounter() | (row.mvccTxState() << MVCC_HINTS_BIT_OFF),
+ row.newMvccCoordinatorVersion(),
+ row.newMvccCounter(),
+ row.newMvccOperationCounter() | (row.newMvccTxState() << MVCC_HINTS_BIT_OFF),
+ len);
+ else if (type != VERSION) {
// Write key or value.
- final CacheObject co = type == EntryPart.KEY ? row.key() : row.value();
+ final CacheObject co = type == KEY ? row.key() : row.value();
co.putValue(buf, rowOff - prevLen, len);
}
@@ -145,6 +229,236 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
}
/**
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @param ver New version.
+ */
+ public void updateVersion(long pageAddr, int dataOff, MvccVersion ver) {
+ long addr = pageAddr + dataOff;
+
+ updateVersion(addr, ver.coordinatorVersion(), ver.counter(), ver.operationCounter());
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param itemId Item ID.
+ * @param pageSize Page size.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ * @param mvccOpCntr Operation counter.
+ */
+ public void updateVersion(long pageAddr, int itemId, int pageSize, long mvccCrd, long mvccCntr, int mvccOpCntr) {
+ int dataOff = getDataOffset(pageAddr, itemId, pageSize);
+
+ long addr = pageAddr + dataOff + (isFragmented(pageAddr, dataOff) ? 10 : 2);
+
+ updateVersion(addr, mvccCrd, mvccCntr, mvccOpCntr);
+ }
+
+ /**
+ * @param addr Address.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ */
+ private void updateVersion(long addr, long mvccCrd, long mvccCntr, int mvccOpCntr) {
+ PageUtils.putLong(addr, 0, mvccCrd);
+ PageUtils.putLong(addr, 8, mvccCntr);
+ PageUtils.putInt(addr, 16, mvccOpCntr);
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @param newVer New version.
+ */
+ public void updateNewVersion(long pageAddr, int dataOff, MvccVersion newVer, byte newTxState) {
+ long addr = pageAddr + dataOff;
+
+ updateNewVersion(addr, newVer.coordinatorVersion(), newVer.counter(),
+ (newVer.operationCounter() & ~MVCC_HINTS_MASK) | ((int)newTxState << MVCC_HINTS_BIT_OFF));
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param itemId Item ID.
+ * @param pageSize Page size.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ * @param mvccOpCntr Operation counter.
+ */
+ public void updateNewVersion(long pageAddr, int itemId, int pageSize, long mvccCrd, long mvccCntr, int mvccOpCntr) {
+ int dataOff = getDataOffset(pageAddr, itemId, pageSize);
+
+ long addr = pageAddr + dataOff + (isFragmented(pageAddr, dataOff) ? 10 : 2);
+
+ updateNewVersion(addr, mvccCrd, mvccCntr, mvccOpCntr);
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param itemId Item ID.
+ * @param pageSize Page size.
+ * @param txState Tx state hint.
+ */
+ public void updateTxState(long pageAddr, int itemId, int pageSize, byte txState) {
+ int dataOff = getDataOffset(pageAddr, itemId, pageSize);
+
+ long addr = pageAddr + dataOff + (isFragmented(pageAddr, dataOff) ? 10 : 2);
+
+ int opCntr = mvccOperationCounter(addr, 0);
+
+ mvccOperationCounter(addr, 0, ((int)txState << MVCC_HINTS_BIT_OFF) | (opCntr & ~MVCC_HINTS_MASK));
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param itemId Item ID.
+ * @param pageSize Page size.
+ * @param txState Tx state hint.
+ */
+ public void updateNewTxState(long pageAddr, int itemId, int pageSize, byte txState) {
+ int dataOff = getDataOffset(pageAddr, itemId, pageSize);
+
+ long addr = pageAddr + dataOff + (isFragmented(pageAddr, dataOff) ? 10 : 2);
+
+ int opCntr = newMvccOperationCounter(addr, 0);
+
+ newMvccOperationCounter(addr, 0, ((int)txState << MVCC_HINTS_BIT_OFF) | (opCntr & ~MVCC_HINTS_MASK));
+ }
+
+ /**
+ * Marks row removed.
+ *
+ * @param addr Address.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ */
+ private void updateNewVersion(long addr, long mvccCrd, long mvccCntr, int mvccOpCntr) {
+ // Skip xid_min.
+ addr += 20;
+
+ PageUtils.putLong(addr, 0, mvccCrd);
+ PageUtils.putLong(addr, 8, mvccCntr);
+ PageUtils.putInt(addr, 16, mvccOpCntr);
+ }
+
+ /**
+ * Returns MVCC coordinator number.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @return MVCC coordinator number.
+ */
+ public long mvccCoordinator(long pageAddr, int dataOff) {
+ long addr = pageAddr + dataOff;
+
+ return PageUtils.getLong(addr, 0);
+ }
+
+ /**
+ * Returns MVCC counter value.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @return MVCC counter value.
+ */
+ public long mvccCounter(long pageAddr, int dataOff) {
+ long addr = pageAddr + dataOff;
+
+ return PageUtils.getLong(addr, 8);
+ }
+
+ /**
+ * Returns MVCC operation counter value.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @return MVCC counter value.
+ */
+ public int mvccOperationCounter(long pageAddr, int dataOff) {
+ long addr = pageAddr + dataOff;
+
+ return PageUtils.getInt(addr, 16);
+ }
+
+ /**
+ * Sets MVCC operation counter value.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @param opCntr MVCC counter value.
+ */
+ public void mvccOperationCounter(long pageAddr, int dataOff, int opCntr) {
+ long addr = pageAddr + dataOff;
+
+ PageUtils.putInt(addr, 16, opCntr);
+ }
+
+ /**
+ * Returns new MVCC coordinator number.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @return New MVCC coordinator number.
+ */
+ public long newMvccCoordinator(long pageAddr, int dataOff) {
+ long addr = pageAddr + dataOff;
+
+ // Skip xid_min.
+ addr += 20;
+
+ return PageUtils.getLong(addr, 0);
+ }
+
+ /**
+ * Returns new MVCC counter value.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @return New MVCC counter value.
+ */
+ public long newMvccCounter(long pageAddr, int dataOff) {
+ long addr = pageAddr + dataOff;
+
+ // Skip xid_min.
+ addr += 20;
+
+ return PageUtils.getLong(addr, 8);
+ }
+
+ /**
+ * Returns MVCC operation counter value.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @return MVCC counter value.
+ */
+ public int newMvccOperationCounter(long pageAddr, int dataOff) {
+ long addr = pageAddr + dataOff;
+
+ // Skip xid_min.
+ addr += 20;
+
+ return PageUtils.getInt(addr, 16);
+ }
+
+ /**
+ * Sets MVCC new operation counter value.
+ *
+ * @param pageAddr Page address.
+ * @param dataOff Data offset.
+ * @param opCntr MVCC operation counter value.
+ */
+ public void newMvccOperationCounter(long pageAddr, int dataOff, int opCntr) {
+ long addr = pageAddr + dataOff;
+
+ // Skip xid_min.
+ addr += 20;
+
+ PageUtils.putInt(addr, 16, opCntr);
+ }
+
+ /**
* @param buf Byte buffer.
* @param ver Version.
* @param rowOff Row offset.
@@ -181,7 +495,6 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
*/
private void writeExpireTimeFragment(ByteBuffer buf, long expireTime, int rowOff, int len, int prevLen) {
int size = 8;
-
if (size <= len)
buf.putLong(expireTime);
else {
@@ -222,77 +535,35 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
}
/**
- *
+ * @param buf Byte buffer.
+ * @param mvccCrd Coordinator version.
+ * @param mvccCntr Counter.
+ * @param mvccOpCntr Operation counter.
+ * @param newMvccCrd New coordinator version.
+ * @param newMvccCntr New counter version.
+ * @param newMvccOpCntr New operation counter.
+ * @param len Length.
*/
- private enum EntryPart {
- /** */
- KEY,
-
- /** */
- VALUE,
-
- /** */
- VERSION,
-
- /** */
- EXPIRE_TIME,
-
- /** */
- CACHE_ID
- }
-
- /** {@inheritDoc} */
- @Override
- protected void writeRowData(
- long pageAddr,
- int dataOff,
- int payloadSize,
- CacheDataRow row,
- boolean newRow
- ) throws IgniteCheckedException {
- long addr = pageAddr + dataOff;
-
- int cacheIdSize = row.cacheId() != 0 ? 4 : 0;
-
- if (newRow) {
- PageUtils.putShort(addr, 0, (short)payloadSize);
- addr += 2;
-
- if (cacheIdSize != 0) {
- PageUtils.putInt(addr, 0, row.cacheId());
-
- addr += cacheIdSize;
- }
-
- addr += row.key().putValue(addr);
- }
- else
- addr += (2 + cacheIdSize + row.key().valueBytesLength(null));
+ private void writeMvccInfoFragment(ByteBuffer buf, long mvccCrd, long mvccCntr, int mvccOpCntr, long newMvccCrd,
+ long newMvccCntr, int newMvccOpCntr, int len) {
+ if (mvccCrd == 0)
+ return;
- addr += row.value().putValue(addr);
+ assert len >= MVCC_INFO_SIZE : "Mvcc info should fit on the one page!";
- CacheVersionIO.write(addr, row.version(), false);
- addr += CacheVersionIO.size(row.version(), false);
+ assert MvccUtils.mvccVersionIsValid(mvccCrd, mvccCntr, mvccOpCntr);
- PageUtils.putLong(addr, 0, row.expireTime());
- }
+ // xid_min.
+ buf.putLong(mvccCrd);
+ buf.putLong(mvccCntr);
+ buf.putInt(mvccOpCntr);
- /** {@inheritDoc} */
- @Override
- protected void writeRowData(
- long pageAddr,
- int dataOff,
- byte[] payload
- ) {
- PageUtils.putShort(pageAddr, dataOff, (short)payload.length);
- dataOff += 2;
-
- PageUtils.putBytes(pageAddr, dataOff, payload);
- }
+ assert newMvccCrd == 0 || MvccUtils.mvccVersionIsValid(newMvccCrd, newMvccCntr, newMvccOpCntr);
- /** {@inheritDoc} */
- @Override public int getRowSize(CacheDataRow row) throws IgniteCheckedException {
- return getRowSize(row, row.cacheId() != 0);
+ // xid_max.
+ buf.putLong(newMvccCrd);
+ buf.putLong(newMvccCntr);
+ buf.putInt(newMvccOpCntr);
}
/** {@inheritDoc} */
@@ -303,18 +574,25 @@ public class DataPageIO extends AbstractDataPageIO<CacheDataRow> {
}
/**
- * @param row Row.
- * @param withCacheId If {@code true} adds cache ID size.
- * @return Entry size on page.
- * @throws IgniteCheckedException If failed.
+ *
*/
- public static int getRowSize(CacheDataRow row, boolean withCacheId) throws IgniteCheckedException {
- KeyCacheObject key = row.key();
- CacheObject val = row.value();
+ enum EntryPart {
+ /** */
+ KEY,
- int keyLen = key.valueBytesLength(null);
- int valLen = val.valueBytesLength(null);
+ /** */
+ VALUE,
+
+ /** */
+ VERSION,
- return keyLen + valLen + CacheVersionIO.size(row.version(), false) + 8 + (withCacheId ? 4 : 0);
+ /** */
+ EXPIRE_TIME,
+
+ /** */
+ CACHE_ID,
+
+ /** */
+ MVCC_INFO
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/IOVersions.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/IOVersions.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/IOVersions.java
index d74d344..9dcad9b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/IOVersions.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/IOVersions.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.processors.cache.persistence.tree.io;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
/**
* Registry for IO versions.
*/
@@ -99,4 +101,9 @@ public final class IOVersions<V extends PageIO> {
return res;
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(IOVersions.class, this);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
index 4534bb5..f167d0c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
@@ -25,6 +25,8 @@ import org.apache.ignite.internal.pagemem.PageIdUtils;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLogInnerIO;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLogLeafIO;
import org.apache.ignite.internal.processors.cache.persistence.IndexStorageImpl;
import org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListMetaIO;
import org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListNodeIO;
@@ -33,10 +35,14 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHan
import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageLockListener;
import org.apache.ignite.internal.processors.cache.tree.CacheIdAwareDataInnerIO;
import org.apache.ignite.internal.processors.cache.tree.CacheIdAwareDataLeafIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccCacheIdAwareDataInnerIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccCacheIdAwareDataLeafIO;
import org.apache.ignite.internal.processors.cache.tree.CacheIdAwarePendingEntryInnerIO;
import org.apache.ignite.internal.processors.cache.tree.CacheIdAwarePendingEntryLeafIO;
import org.apache.ignite.internal.processors.cache.tree.DataInnerIO;
import org.apache.ignite.internal.processors.cache.tree.DataLeafIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataInnerIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataLeafIO;
import org.apache.ignite.internal.processors.cache.tree.PendingEntryInnerIO;
import org.apache.ignite.internal.processors.cache.tree.PendingEntryLeafIO;
import org.apache.ignite.internal.util.GridStringBuilder;
@@ -87,6 +93,12 @@ public abstract class PageIO {
/** */
private static IOVersions<? extends BPlusLeafIO<?>> h2LeafIOs;
+ /** */
+ private static IOVersions<? extends BPlusInnerIO<?>> h2MvccInnerIOs;
+
+ /** */
+ private static IOVersions<? extends BPlusLeafIO<?>> h2MvccLeafIOs;
+
/** Maximum payload size. */
public static final short MAX_PAYLOAD_SIZE = 2048;
@@ -97,6 +109,12 @@ public abstract class PageIO {
private static List<IOVersions<? extends BPlusLeafIO<?>>> h2ExtraLeafIOs = new ArrayList<>(MAX_PAYLOAD_SIZE);
/** */
+ private static List<IOVersions<? extends BPlusInnerIO<?>>> h2ExtraMvccInnerIOs = new ArrayList<>(MAX_PAYLOAD_SIZE);
+
+ /** */
+ private static List<IOVersions<? extends BPlusLeafIO<?>>> h2ExtraMvccLeafIOs = new ArrayList<>(MAX_PAYLOAD_SIZE);
+
+ /** */
public static final int TYPE_OFF = 0;
/** */
@@ -129,6 +147,12 @@ public abstract class PageIO {
/** */
public static final int COMMON_HEADER_END = RESERVED_3_OFF + 8; // 40=type(2)+ver(2)+crc(4)+pageId(8)+rotatedIdPart(1)+reserved(1+2+4+2*8)
+ /** */
+ public static final int MVCC_HINTS_MASK = 0xC0000000;
+
+ /** */
+ public static final int MVCC_HINTS_BIT_OFF = 30;
+
/* All the page types. */
/** */
@@ -200,19 +224,55 @@ public abstract class PageIO {
/** */
public static final short T_DATA_REF_METASTORAGE_LEAF = 23;
+ /** */
+ public static final short T_DATA_REF_MVCC_INNER = 24;
+
+ /** */
+ public static final short T_DATA_REF_MVCC_LEAF = 25;
+
+ /** */
+ public static final short T_CACHE_ID_DATA_REF_MVCC_INNER = 26;
+
+ /** */
+ public static final short T_CACHE_ID_DATA_REF_MVCC_LEAF = 27;
+
+ /** */
+ public static final short T_H2_MVCC_REF_LEAF = 28;
+
+ /** */
+ public static final short T_H2_MVCC_REF_INNER = 29;
+
+ /** */
+ public static final short T_TX_LOG_LEAF = 30;
+
+ /** */
+ public static final short T_TX_LOG_INNER = 31;
+
/** Index for payload == 1. */
- public static final short T_H2_EX_REF_LEAF_START = 10000;
+ public static final short T_H2_EX_REF_LEAF_START = 10_000;
/** */
public static final short T_H2_EX_REF_LEAF_END = T_H2_EX_REF_LEAF_START + MAX_PAYLOAD_SIZE - 1;
/** */
- public static final short T_H2_EX_REF_INNER_START = 20000;
+ public static final short T_H2_EX_REF_INNER_START = 20_000;
/** */
public static final short T_H2_EX_REF_INNER_END = T_H2_EX_REF_INNER_START + MAX_PAYLOAD_SIZE - 1;
/** */
+ public static final short T_H2_EX_REF_MVCC_LEAF_START = 23_000;
+
+ /** */
+ public static final short T_H2_EX_REF_MVCC_LEAF_END = T_H2_EX_REF_MVCC_LEAF_START + MAX_PAYLOAD_SIZE - 1;
+
+ /** */
+ public static final short T_H2_EX_REF_MVCC_INNER_START = 26_000;
+
+ /** */
+ public static final short T_H2_EX_REF_MVCC_INNER_END = T_H2_EX_REF_MVCC_INNER_START + MAX_PAYLOAD_SIZE - 1;
+
+ /** */
private final int ver;
/** */
@@ -363,13 +423,19 @@ public abstract class PageIO {
*
* @param innerIOs Inner IO versions.
* @param leafIOs Leaf IO versions.
+ * @param mvccInnerIOs Inner IO versions with mvcc enabled.
+ * @param mvccLeafIOs Leaf IO versions with mvcc enabled.
*/
public static void registerH2(
IOVersions<? extends BPlusInnerIO<?>> innerIOs,
- IOVersions<? extends BPlusLeafIO<?>> leafIOs
+ IOVersions<? extends BPlusLeafIO<?>> leafIOs,
+ IOVersions<? extends BPlusInnerIO<?>> mvccInnerIOs,
+ IOVersions<? extends BPlusLeafIO<?>> mvccLeafIOs
) {
h2InnerIOs = innerIOs;
h2LeafIOs = leafIOs;
+ h2MvccInnerIOs = mvccInnerIOs;
+ h2MvccLeafIOs = mvccLeafIOs;
}
/**
@@ -377,8 +443,10 @@ public abstract class PageIO {
*
* @param innerExtIOs Extra versions.
*/
- public static void registerH2ExtraInner(IOVersions<? extends BPlusInnerIO<?>> innerExtIOs) {
- h2ExtraInnerIOs.add(innerExtIOs);
+ public static void registerH2ExtraInner(IOVersions<? extends BPlusInnerIO<?>> innerExtIOs, boolean mvcc) {
+ List<IOVersions<? extends BPlusInnerIO<?>>> ios = mvcc ? h2ExtraMvccInnerIOs : h2ExtraInnerIOs;
+
+ ios.add(innerExtIOs);
}
/**
@@ -386,24 +454,30 @@ public abstract class PageIO {
*
* @param leafExtIOs Extra versions.
*/
- public static void registerH2ExtraLeaf(IOVersions<? extends BPlusLeafIO<?>> leafExtIOs) {
- h2ExtraLeafIOs.add(leafExtIOs);
+ public static void registerH2ExtraLeaf(IOVersions<? extends BPlusLeafIO<?>> leafExtIOs, boolean mvcc) {
+ List<IOVersions<? extends BPlusLeafIO<?>>> ios = mvcc ? h2ExtraMvccLeafIOs : h2ExtraLeafIOs;
+
+ ios.add(leafExtIOs);
}
/**
* @param idx Index.
* @return IOVersions for given idx.
*/
- public static IOVersions<? extends BPlusInnerIO<?>> getInnerVersions(int idx) {
- return h2ExtraInnerIOs.get(idx);
+ public static IOVersions<? extends BPlusInnerIO<?>> getInnerVersions(int idx, boolean mvcc) {
+ List<IOVersions<? extends BPlusInnerIO<?>>> ios = mvcc ? h2ExtraMvccInnerIOs : h2ExtraInnerIOs;
+
+ return ios.get(idx);
}
/**
* @param idx Index.
* @return IOVersions for given idx.
*/
- public static IOVersions<? extends BPlusLeafIO<?>> getLeafVersions(int idx) {
- return h2ExtraLeafIOs.get(idx);
+ public static IOVersions<? extends BPlusLeafIO<?>> getLeafVersions(int idx, boolean mvcc) {
+ List<IOVersions<? extends BPlusLeafIO<?>>> ios = mvcc ? h2ExtraMvccLeafIOs : h2ExtraLeafIOs;
+
+ return ios.get(idx);
}
/**
@@ -525,13 +599,18 @@ public abstract class PageIO {
*/
@SuppressWarnings("unchecked")
public static <Q extends BPlusIO<?>> Q getBPlusIO(int type, int ver) throws IgniteCheckedException {
-
if (type >= T_H2_EX_REF_LEAF_START && type <= T_H2_EX_REF_LEAF_END)
return (Q)h2ExtraLeafIOs.get(type - T_H2_EX_REF_LEAF_START).forVersion(ver);
if (type >= T_H2_EX_REF_INNER_START && type <= T_H2_EX_REF_INNER_END)
return (Q)h2ExtraInnerIOs.get(type - T_H2_EX_REF_INNER_START).forVersion(ver);
+ if (type >= T_H2_EX_REF_MVCC_LEAF_START && type <= T_H2_EX_REF_MVCC_LEAF_END)
+ return (Q)h2ExtraMvccLeafIOs.get(type - T_H2_EX_REF_MVCC_LEAF_START).forVersion(ver);
+
+ if (type >= T_H2_EX_REF_MVCC_INNER_START && type <= T_H2_EX_REF_MVCC_INNER_END)
+ return (Q)h2ExtraMvccInnerIOs.get(type - T_H2_EX_REF_MVCC_INNER_START).forVersion(ver);
+
switch (type) {
case T_H2_REF_INNER:
if (h2InnerIOs == null)
@@ -545,6 +624,24 @@ public abstract class PageIO {
return (Q)h2LeafIOs.forVersion(ver);
+ case T_H2_MVCC_REF_INNER:
+ if (h2MvccInnerIOs == null)
+ break;
+
+ return (Q)h2MvccInnerIOs.forVersion(ver);
+
+ case T_H2_MVCC_REF_LEAF:
+ if (h2MvccLeafIOs == null)
+ break;
+
+ return (Q)h2MvccLeafIOs.forVersion(ver);
+
+ case T_TX_LOG_INNER:
+ return (Q)TxLogInnerIO.VERSIONS.forVersion(ver);
+
+ case T_TX_LOG_LEAF:
+ return (Q)TxLogLeafIO.VERSIONS.forVersion(ver);
+
case T_DATA_REF_INNER:
return (Q)DataInnerIO.VERSIONS.forVersion(ver);
@@ -557,6 +654,18 @@ public abstract class PageIO {
case T_CACHE_ID_AWARE_DATA_REF_LEAF:
return (Q)CacheIdAwareDataLeafIO.VERSIONS.forVersion(ver);
+ case T_CACHE_ID_DATA_REF_MVCC_INNER:
+ return (Q) MvccCacheIdAwareDataInnerIO.VERSIONS.forVersion(ver);
+
+ case T_CACHE_ID_DATA_REF_MVCC_LEAF:
+ return (Q) MvccCacheIdAwareDataLeafIO.VERSIONS.forVersion(ver);
+
+ case T_DATA_REF_MVCC_INNER:
+ return (Q)MvccDataInnerIO.VERSIONS.forVersion(ver);
+
+ case T_DATA_REF_MVCC_LEAF:
+ return (Q)MvccDataLeafIO.VERSIONS.forVersion(ver);
+
case T_METASTORE_INNER:
return (Q)IndexStorageImpl.MetaStoreInnerIO.VERSIONS.forVersion(ver);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
index f02e3a0..14489b7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
@@ -113,12 +113,6 @@ public class SimpleDataPageIO extends AbstractDataPageIO<MetastorageDataRow> {
}
/** {@inheritDoc} */
- @Override public int getRowSize(MetastorageDataRow row) throws IgniteCheckedException {
- return 4 + row.value().length;
- }
-
-
- /** {@inheritDoc} */
@Override protected void printPage(long addr, int pageSize, GridStringBuilder sb) throws IgniteCheckedException {
sb.a("SimpleDataPageIO [\n");
printPageLayout(addr, pageSize, sb);
[03/45] ignite git commit: IGNITE-9429 Fixed flaky
GridCacheReplicatedDataStructuresFailoverSelfTest
Posted by ag...@apache.org.
IGNITE-9429 Fixed flaky GridCacheReplicatedDataStructuresFailoverSelfTest
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/2b9fc2eb
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/2b9fc2eb
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/2b9fc2eb
Branch: refs/heads/ignite-9273
Commit: 2b9fc2ebc8b163773ce032b8eca1de17c691ddcc
Parents: 184d6a5
Author: Alexey Goncharuk <al...@gmail.com>
Authored: Thu Aug 30 12:39:22 2018 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Thu Aug 30 12:40:16 2018 +0300
----------------------------------------------------------------------
...CacheAbstractDataStructuresFailoverSelfTest.java | 16 +++++++++-------
...cheReplicatedDataStructuresFailoverSelfTest.java | 8 ++++----
2 files changed, 13 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/2b9fc2eb/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheAbstractDataStructuresFailoverSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheAbstractDataStructuresFailoverSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheAbstractDataStructuresFailoverSelfTest.java
index 9da1161..797e90f 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheAbstractDataStructuresFailoverSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheAbstractDataStructuresFailoverSelfTest.java
@@ -119,11 +119,6 @@ public abstract class GridCacheAbstractDataStructuresFailoverSelfTest extends Ig
}
/** {@inheritDoc} */
- @Override protected void afterTestsStopped() throws Exception {
- // No-op
- }
-
- /** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
startGridsMultiThreaded(gridCount());
@@ -1362,7 +1357,7 @@ public abstract class GridCacheAbstractDataStructuresFailoverSelfTest extends Ig
if (failed.get())
return;
- int idx = nodeIdx.getAndIncrement();
+ int idx = nodeIdx.incrementAndGet();
Thread.currentThread().setName("thread-" + getTestIgniteInstanceName(idx));
@@ -1373,8 +1368,15 @@ public abstract class GridCacheAbstractDataStructuresFailoverSelfTest extends Ig
cb.apply(g);
}
+ catch (IgniteException e) {
+ if (!X.hasCause(e, NodeStoppingException.class) &&
+ !X.hasCause(e, IllegalStateException.class))
+ throw e;
+
+ // OK for this test.
+ }
finally {
- if(circular)
+ if (circular)
stopGrid(G.allGrids().get(0).configuration().getIgniteInstanceName());
else
stopGrid(idx);
http://git-wip-us.apache.org/repos/asf/ignite/blob/2b9fc2eb/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/replicated/GridCacheReplicatedDataStructuresFailoverSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/replicated/GridCacheReplicatedDataStructuresFailoverSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/replicated/GridCacheReplicatedDataStructuresFailoverSelfTest.java
index a228718..cb6e77e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/replicated/GridCacheReplicatedDataStructuresFailoverSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/replicated/GridCacheReplicatedDataStructuresFailoverSelfTest.java
@@ -40,16 +40,16 @@ public class GridCacheReplicatedDataStructuresFailoverSelfTest
}
/**
- * @throws Exception If failed.
+ *
*/
- public void testFairReentrantLockConstantMultipleTopologyChangeNonFailoverSafe() throws Exception {
+ @Override public void testFairReentrantLockConstantMultipleTopologyChangeNonFailoverSafe() {
fail("https://issues.apache.org/jira/browse/IGNITE-6454");
}
/**
- * @throws Exception If failed.
+ *
*/
- public void testReentrantLockConstantMultipleTopologyChangeNonFailoverSafe() throws Exception {
+ @Override public void testReentrantLockConstantMultipleTopologyChangeNonFailoverSafe() {
fail("https://issues.apache.org/jira/browse/IGNITE-6454");
}
}
[20/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
index 716d32b..8bc620f 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.UUID;
import javax.cache.Cache;
import javax.cache.expiry.ExpiryPolicy;
@@ -26,7 +27,10 @@ import javax.cache.processor.EntryProcessorResult;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.eviction.EvictableEntry;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicAbstractUpdateFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
@@ -277,6 +281,12 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
}
/** {@inheritDoc} */
+ @Nullable @Override public List<GridCacheEntryInfo> allVersionsInfo()
+ throws IgniteCheckedException {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean valid(AffinityTopologyVersion topVer) {
return true;
}
@@ -391,7 +401,8 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
Object transformClo,
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
- boolean keepBinary) {
+ boolean keepBinary,
+ MvccSnapshot mvccVer) {
return val;
}
@@ -408,6 +419,7 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean keepBinary,
+ MvccSnapshot mvccVer,
@Nullable ReaderArguments args) throws IgniteCheckedException, GridCacheEntryRemovedException {
assert false;
@@ -425,6 +437,7 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean keepBinary,
+ MvccSnapshot mvccVer,
@Nullable ReaderArguments readerArgs) {
assert false;
@@ -457,9 +470,41 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
UUID subjId,
String taskName,
@Nullable GridCacheVersion dhtVer,
- @Nullable Long updateCntr)
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer
+ )
+ throws IgniteCheckedException, GridCacheEntryRemovedException
+ {
+ rawPut(val, ttl);
+
+ return new GridCacheUpdateTxResult(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheUpdateTxResult mvccSet(@Nullable IgniteInternalTx tx, UUID affNodeId, CacheObject val,
+ long ttl0, AffinityTopologyVersion topVer, @Nullable Long updateCntr, MvccSnapshot mvccVer,
+ GridCacheOperation op, boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException, GridCacheEntryRemovedException {
+ rawPut(val, ttl);
+
+ return new GridCacheUpdateTxResult(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheUpdateTxResult mvccRemove(@Nullable IgniteInternalTx tx, UUID affNodeId,
+ AffinityTopologyVersion topVer, @Nullable Long updateCntr, MvccSnapshot mvccVer, boolean needHistory)
throws IgniteCheckedException, GridCacheEntryRemovedException {
- return new GridCacheUpdateTxResult(true, rawPut(val, ttl), null);
+ obsoleteVer = ver;
+
+ val = null;
+
+ return new GridCacheUpdateTxResult(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheUpdateTxResult mvccLock(GridDhtTxLocalAdapter tx,
+ MvccSnapshot mvccVer) throws GridCacheEntryRemovedException, IgniteCheckedException {
+ return new GridCacheUpdateTxResult(true);
}
/** {@inheritDoc} */
@@ -539,15 +584,14 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
UUID subjId,
String taskName,
@Nullable GridCacheVersion dhtVer,
- @Nullable Long updateCntr
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer
) throws IgniteCheckedException, GridCacheEntryRemovedException {
obsoleteVer = ver;
- CacheObject old = val;
-
val = null;
- return new GridCacheUpdateTxResult(true, old, null);
+ return new GridCacheUpdateTxResult(true);
}
/** @inheritDoc */
@@ -638,6 +682,10 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
@Override public boolean initialValue(
CacheObject val,
GridCacheVersion ver,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState,
long ttl,
long expireTime,
boolean preload,
@@ -892,6 +940,17 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
}
/** {@inheritDoc} */
+ @Override public GridCacheUpdateTxResult mvccUpdateRowsWithPreloadInfo(IgniteInternalTx tx,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ @Nullable Long updateCntr,
+ List<GridCacheEntryInfo> entries,
+ GridCacheOperation op,
+ MvccSnapshot mvccVer) throws IgniteCheckedException, GridCacheEntryRemovedException {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public void touch(AffinityTopologyVersion topVer) {
context().evicts().touch(this, topVer);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
index 4ef1dc3..b1df28e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
@@ -43,6 +43,8 @@ import org.apache.ignite.configuration.NearCacheConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteKernal;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.query.GridQueryCancel;
import org.apache.ignite.internal.processors.query.GridQueryIndexing;
@@ -53,6 +55,7 @@ import org.apache.ignite.internal.processors.query.GridRunningQueryInfo;
import org.apache.ignite.internal.processors.query.QueryField;
import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
import org.apache.ignite.internal.processors.query.SqlClientContext;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.lang.GridCloseableIterator;
@@ -223,6 +226,11 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
*/
private static class FailedIndexing implements GridQueryIndexing {
/** {@inheritDoc} */
+ @Override public void onClientDisconnect() throws IgniteCheckedException {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
@Override public void start(GridKernalContext ctx, GridSpinBusyLock busyLock) throws IgniteCheckedException {
// No-op
}
@@ -240,7 +248,7 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
/** {@inheritDoc} */
@Override public List<FieldsQueryCursor<List<?>>> querySqlFields(String schemaName, SqlFieldsQuery qry,
- SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, GridQueryCancel cancel) {
+ SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, MvccQueryTracker tracker, GridQueryCancel cancel) {
return null;
}
@@ -312,6 +320,14 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
}
/** {@inheritDoc} */
+ @Override public UpdateSourceIterator<?> prepareDistributedUpdate(GridCacheContext<?, ?> cctx, int[] ids, int[] parts,
+ String schema, String qry, Object[] params, int flags, int pageSize, int timeout,
+ AffinityTopologyVersion topVer,
+ MvccSnapshot mvccVer, GridQueryCancel cancel) throws IgniteCheckedException {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean registerType(GridCacheContext cctx,
GridQueryTypeDescriptor desc) throws IgniteCheckedException {
return false;
@@ -319,7 +335,7 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
/** {@inheritDoc} */
@Override public void store(GridCacheContext cctx, GridQueryTypeDescriptor type, CacheDataRow row,
- CacheDataRow prevRow, boolean prevRowAvailable) {
+ CacheDataRow prevRow, boolean prevRowAvailable) throws IgniteCheckedException {
// No-op.
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractBasicCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractBasicCoordinatorFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractBasicCoordinatorFailoverTest.java
new file mode 100644
index 0000000..e51b7d0
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractBasicCoordinatorFailoverTest.java
@@ -0,0 +1,685 @@
+/*
+ * 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.mvcc;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.cluster.ClusterTopologyException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.TestRecordingCommunicationSpi;
+import org.apache.ignite.internal.processors.cache.distributed.TestCacheNodeExcludingFilter;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccSnapshotResponse;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiInClosure;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE;
+
+/**
+ * Base class for Mvcc coordinator failover test.
+ */
+public abstract class CacheMvccAbstractBasicCoordinatorFailoverTest extends CacheMvccAbstractTest {
+ /**
+ * @param concurrency Transaction concurrency.
+ * @param isolation Transaction isolation.
+ * @throws Exception If failed.
+ */
+ @SuppressWarnings("unchecked")
+ protected void coordinatorFailureSimple(
+ final TransactionConcurrency concurrency,
+ final TransactionIsolation isolation,
+ ReadMode readMode,
+ WriteMode writeMode
+ ) throws Exception {
+ testSpi = true;
+
+ startGrids(3);
+
+ client = true;
+
+ final Ignite client = startGrid(3);
+
+ final IgniteCache cache = client.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class));
+
+ final Integer key1 = primaryKey(jcache(1));
+ final Integer key2 = primaryKey(jcache(2));
+
+ TestRecordingCommunicationSpi crdSpi = TestRecordingCommunicationSpi.spi(ignite(0));
+
+ crdSpi.blockMessages(MvccSnapshotResponse.class, client.name());
+
+ IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() {
+ @Override public Object call() throws Exception {
+ try {
+ try (Transaction tx = client.transactions().txStart(concurrency, isolation)) {
+ writeByMode(cache, key1, 1, writeMode, INTEGER_CODEC);
+ writeByMode(cache, key2, 2, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+
+ fail();
+ }
+ catch (ClusterTopologyException e) {
+ info("Expected exception: " + e);
+
+ assertNotNull(e.retryReadyFuture());
+
+ e.retryReadyFuture().get();
+ }
+ catch (CacheException e) {
+ info("Expected exception: " + e);
+ }
+ catch (Throwable e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ return null;
+ }
+ }, "tx-thread");
+
+ crdSpi.waitForBlocked();
+
+ stopGrid(0);
+
+ fut.get();
+
+ assertNull(readByMode(cache, key1, readMode, INTEGER_CODEC));
+ assertNull(readByMode(cache, key2, readMode, INTEGER_CODEC));
+
+ try (Transaction tx = client.transactions().txStart(concurrency, isolation)) {
+ writeByMode(cache, key1, 1, writeMode, INTEGER_CODEC);
+ writeByMode(cache, key2, 2, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+
+ assertEquals(1, readByMode(cache, key1, readMode, INTEGER_CODEC));
+ assertEquals(2, readByMode(cache, key2, readMode, INTEGER_CODEC));
+ }
+
+ /**
+ * @param readDelay {@code True} if delays get requests.
+ * @param readInTx {@code True} to read inside transaction.
+ * @throws Exception If failed.
+ */
+ @SuppressWarnings("unchecked")
+ protected void readInProgressCoordinatorFails(boolean readDelay,
+ final boolean readInTx,
+ final TransactionConcurrency concurrency,
+ final TransactionIsolation isolation,
+ ReadMode readMode,
+ WriteMode writeMode,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC) throws Exception {
+ final int COORD_NODES = 5;
+ final int SRV_NODES = 4;
+
+ if (readDelay)
+ testSpi = true;
+
+ startGrids(COORD_NODES);
+
+ startGridsMultiThreaded(COORD_NODES, SRV_NODES);
+
+ client = true;
+
+ Ignite client = startGrid(COORD_NODES + SRV_NODES);
+
+ final List<String> cacheNames = new ArrayList<>();
+
+ final int KEYS = 100;
+
+ final Map<Integer, Integer> vals = new HashMap<>();
+
+ for (int i = 0; i < KEYS; i++)
+ vals.put(i, 0);
+
+ String[] exclude = new String[COORD_NODES];
+
+ for (int i = 0; i < COORD_NODES; i++)
+ exclude[i] = testNodeName(i);
+
+ for (CacheConfiguration ccfg : cacheConfigurations()) {
+ ccfg.setName("cache-" + cacheNames.size());
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ // First server nodes are 'dedicated' coordinators.
+ ccfg.setNodeFilter(new TestCacheNodeExcludingFilter(exclude));
+
+ cacheNames.add(ccfg.getName());
+
+ IgniteCache cache = client.createCache(ccfg);
+
+ boolean updated = false;
+
+ while (!updated) {
+ try (Transaction tx = client.transactions().txStart(concurrency, isolation)) {
+ tx.timeout(TX_TIMEOUT);
+
+ writeAllByMode(cache, vals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+
+ updated = true;
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ }
+ }
+
+ if (readDelay) {
+ for (int i = COORD_NODES; i < COORD_NODES + SRV_NODES + 1; i++) {
+ TestRecordingCommunicationSpi.spi(ignite(i)).closure(new IgniteBiInClosure<ClusterNode, Message>() {
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (msg instanceof GridNearGetRequest)
+ doSleep(ThreadLocalRandom.current().nextLong(50) + 1);
+ }
+ });
+ }
+ }
+
+ final AtomicBoolean done = new AtomicBoolean();
+
+ try {
+ final AtomicInteger readNodeIdx = new AtomicInteger(0);
+
+ IgniteInternalFuture getFut = GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try {
+ Ignite node = ignite(COORD_NODES + (readNodeIdx.getAndIncrement() % (SRV_NODES + 1)));
+
+ int cnt = 0;
+
+ while (!done.get() && !Thread.currentThread().isInterrupted()) {
+ for (String cacheName : cacheNames) {
+ IgniteCache cache = node.cache(cacheName);
+
+ Map<Integer, Integer> res = null;
+
+ if (readInTx) {
+ try (Transaction tx = node.transactions().txStart(concurrency, isolation)) {
+ tx.timeout(TX_TIMEOUT);
+
+ res = readAllByMode(cache, vals.keySet(), readMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+ catch (Exception e) { // TODO Remove catch clause when IGNITE-8841 implemented.
+ handleTxException(e);
+ }
+ }
+ else
+ res = readAllByMode(cache, vals.keySet(), readMode, INTEGER_CODEC);
+
+ if (readInTx) { // TODO IGNITE-8841
+ assertTrue("res.size=" + (res == null ? 0 : res.size()) + ", res=" + res, res == null || vals.size() == res.size());
+ }
+ else {
+ assertEquals(vals.size(), res.size());
+
+ Integer val0 = null;
+
+ for (Integer val : res.values()) {
+ if (val0 == null)
+ val0 = val;
+ else
+ assertEquals(val0, val);
+ }
+ }
+ }
+
+ cnt++;
+ }
+
+ log.info("Finished [node=" + node.name() + ", readCnt=" + cnt + ']');
+
+ return null;
+ }
+ catch (Throwable e) {
+ error("Unexpected error: " + e, e);
+
+ throw e;
+ }
+ }
+ }, ((SRV_NODES + 1) + 1) * 2, "get-thread");
+
+ IgniteInternalFuture putFut1 = GridTestUtils.runAsync(new Callable() {
+ @Override public Void call() throws Exception {
+ Ignite node = ignite(COORD_NODES);
+
+ List<IgniteCache> caches = new ArrayList<>();
+
+ for (String cacheName : cacheNames)
+ caches.add(node.cache(cacheName));
+
+ Integer val = 1;
+
+ while (!done.get()) {
+ Map<Integer, Integer> vals = new HashMap<>();
+
+ for (int i = 0; i < KEYS; i++)
+ vals.put(i, val);
+
+ for (IgniteCache cache : caches) {
+ try {
+ try (Transaction tx = node.transactions().txStart(concurrency, isolation)) {
+ tx.timeout(TX_TIMEOUT);
+
+ writeAllByMode(cache, vals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ }
+
+ val++;
+ }
+
+ return null;
+ }
+ }, "putAll-thread");
+
+ IgniteInternalFuture putFut2 = GridTestUtils.runAsync(new Callable() {
+ @Override public Void call() throws Exception {
+ Ignite node = ignite(COORD_NODES);
+
+ IgniteCache cache = node.cache(cacheNames.get(0));
+
+ Integer val = 0;
+
+ while (!done.get()) {
+ try {
+ try (Transaction tx = node.transactions().txStart(concurrency, isolation)) {
+ tx.timeout(TX_TIMEOUT);
+
+ writeByMode(cache, Integer.MAX_VALUE, val, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+
+ val++;
+ }
+
+ return null;
+ }
+ }, "put-thread");
+
+ for (int i = 0; i < COORD_NODES && !getFut.isDone(); i++) {
+ U.sleep(3000);
+
+ stopGrid(i);
+
+ awaitPartitionMapExchange();
+ }
+
+ done.set(true);
+
+ getFut.get();
+ putFut1.get();
+ putFut2.get();
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+ finally {
+ done.set(true);
+ }
+ }
+
+ /**
+ * @param concurrency Tx concurrency level.
+ * @param isolation Tx isolation level.
+ * @param cfgC Cache cfg closure.
+ * @param readMode Read mode.
+ * @param writeMode Write mode.
+ * @throws Exception If failed.
+ */
+ protected void txInProgressCoordinatorChangeSimple(
+ final TransactionConcurrency concurrency,
+ final TransactionIsolation isolation,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ ReadMode readMode,
+ WriteMode writeMode) throws Exception {
+ MvccProcessorImpl.coordinatorAssignClosure(new CoordinatorAssignClosure());
+
+ Ignite srv0 = startGrids(4);
+
+ client = true;
+
+ startGrid(4);
+
+ client = false;
+
+ nodeAttr = CRD_ATTR;
+
+ int crdIdx = 5;
+
+ startGrid(crdIdx);
+
+ CacheConfiguration ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setNodeFilter(new CoordinatorNodeFilter());
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ srv0.createCache(ccfg);
+
+ Set<Integer> keys = F.asSet(1, 2, 3);
+
+ for (int i = 0; i < 5; i++) {
+ Ignite node = ignite(i);
+
+ info("Test with node: " + node.name());
+
+ IgniteCache cache = node.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = node.transactions().txStart(concurrency, isolation)) {
+ assertTrue(readAllByMode(cache, keys, readMode, INTEGER_CODEC).isEmpty());
+
+ startGrid(crdIdx + 1);
+
+ stopGrid(crdIdx);
+
+ crdIdx++;
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+
+ checkActiveQueriesCleanup(ignite(crdIdx));
+ }
+ }
+
+ /**
+ * @param fromClient {@code True} if read from client node, otherwise from server node.
+ * @throws Exception If failed.
+ */
+ protected void readInProgressCoordinatorFailsSimple(boolean fromClient,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ ReadMode readMode,
+ WriteMode writeMode) throws Exception {
+ for (boolean readInTx : new boolean[]{false, true}) {
+ for (int i = 1; i <= 3; i++) {
+ readInProgressCoordinatorFailsSimple(fromClient, i, readInTx,cfgC, readMode, writeMode);
+
+ afterTest();
+ }
+ }
+ }
+
+ /**
+ * @param fromClient {@code True} if read from client node, otherwise from server node.
+ * @param crdChangeCnt Number of coordinator changes.
+ * @param readInTx {@code True} to read inside transaction.
+ * @param cfgC Cache configuration closure.
+ * @param readMode Read mode.
+ * @param writeMode Write mode.
+ * @throws Exception If failed.
+ */
+ @SuppressWarnings("unchecked")
+ private void readInProgressCoordinatorFailsSimple(boolean fromClient,
+ int crdChangeCnt,
+ final boolean readInTx,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ ReadMode readMode,
+ WriteMode writeMode) throws Exception {
+ info("readInProgressCoordinatorFailsSimple [fromClient=" + fromClient +
+ ", crdChangeCnt=" + crdChangeCnt +
+ ", readInTx=" + readInTx + ']');
+
+ TransactionConcurrency concurrency = readMode == ReadMode.GET ? OPTIMISTIC : PESSIMISTIC; // TODO IGNITE-7184
+ TransactionIsolation isolation = readMode == ReadMode.GET ? SERIALIZABLE : REPEATABLE_READ; // TODO IGNITE-7184
+
+ testSpi = true;
+
+ client = false;
+
+ final int SRVS = 3;
+ final int COORDS = crdChangeCnt + 1;
+
+ startGrids(SRVS + COORDS);
+
+ client = true;
+
+ assertTrue(startGrid(SRVS + COORDS).configuration().isClientMode());
+
+ final Ignite getNode = fromClient ? ignite(SRVS + COORDS) : ignite(COORDS);
+
+ String[] excludeNodes = new String[COORDS];
+
+ for (int i = 0; i < COORDS; i++)
+ excludeNodes[i] = testNodeName(i);
+
+ CacheConfiguration ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setNodeFilter(new TestCacheNodeExcludingFilter(excludeNodes));
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ final IgniteCache cache = getNode.createCache(ccfg);
+
+ final Set<Integer> keys = new HashSet<>();
+
+ List<Integer> keys1 = primaryKeys(jcache(COORDS), 10);
+ List<Integer> keys2 = primaryKeys(jcache(COORDS + 1), 10);
+
+ keys.addAll(keys1);
+ keys.addAll(keys2);
+
+ Map<Integer, Integer> vals = new HashMap();
+
+ for (Integer key : keys)
+ vals.put(key, -1);
+
+ try (Transaction tx = getNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ writeAllByMode(cache, vals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+
+ final TestRecordingCommunicationSpi getNodeSpi = TestRecordingCommunicationSpi.spi(getNode);
+
+ getNodeSpi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
+ @Override public boolean apply(ClusterNode node, Message msg) {
+ String msgClsName = msg.getClass().getSimpleName();
+
+ return msgClsName.matches("GridNearGetRequest|GridH2QueryRequest|GridCacheQueryRequest");
+ }
+ });
+
+ IgniteInternalFuture getFut = GridTestUtils.runAsync(new Callable() {
+ @Override public Object call() throws Exception {
+ Map<Integer, Integer> res = null;
+
+ if (readInTx) {
+ try (Transaction tx = getNode.transactions().txStart(concurrency, isolation)) {
+ res = readAllByMode(cache, keys, readMode, INTEGER_CODEC);
+
+ tx.rollback();
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ }
+ else
+ res = readAllByMode(cache, keys, readMode, INTEGER_CODEC);
+
+ assertTrue((res != null || readInTx) || (res != null && 20 == res.size()));
+
+ if (res != null) {
+ Integer val = null;
+
+ for (Integer val0 : res.values()) {
+ assertNotNull(val0);
+
+ if (val == null)
+ val = val0;
+ else
+ assertEquals("res=" + res, val, val0);
+ }
+ }
+
+ return null;
+ }
+ }, "get-thread");
+
+ getNodeSpi.waitForBlocked();
+
+ for (int i = 0; i < crdChangeCnt; i++)
+ stopGrid(i);
+
+ for (int i = 0; i < 10; i++) {
+ vals = new HashMap();
+
+ for (Integer key : keys)
+ vals.put(key, i);
+
+ while (true) {
+ try (Transaction tx = getNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ writeAllByMode(cache, vals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+
+ break;
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ }
+ }
+
+ getNodeSpi.stopBlock(true);
+
+ getFut.get();
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @SuppressWarnings("unchecked")
+ protected void checkCoordinatorChangeActiveQueryClientFails_Simple(@Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ ReadMode readMode,
+ WriteMode writeMode) throws Exception {
+ testSpi = true;
+
+ client = false;
+
+ final int SRVS = 3;
+ final int COORDS = 1;
+
+ startGrids(SRVS + COORDS);
+
+ client = true;
+
+ Ignite client = startGrid(SRVS + COORDS);
+
+ CacheConfiguration ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setNodeFilter(new TestCacheNodeExcludingFilter(testNodeName(0)));
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ final IgniteCache cache = client.createCache(ccfg);
+
+ final Map<Integer, Integer> vals = new HashMap();
+
+ for (int i = 0; i < 100; i++)
+ vals.put(i, i);
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ writeAllByMode(cache, vals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+
+ final TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(client);
+
+ clientSpi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
+ @Override public boolean apply(ClusterNode node, Message msg) {
+ String msgClsName = msg.getClass().getSimpleName();
+
+ return msgClsName.matches("GridNearGetRequest|GridH2QueryRequest|GridCacheQueryRequest");
+ }
+ });
+
+ IgniteInternalFuture getFut = GridTestUtils.runAsync(new Callable() {
+ @Override public Object call() throws Exception {
+ Map res = readAllByMode(cache, vals.keySet(), readMode, INTEGER_CODEC);
+
+ assertEquals(vals, res);
+
+ return null;
+ }
+ }, "get-thread");
+
+ clientSpi.waitForBlocked();
+
+ stopGrid(0);
+
+ clientSpi.stopBlock(true);
+
+ getFut.get();
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractCoordinatorFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractCoordinatorFailoverTest.java
new file mode 100644
index 0000000..54e4315
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractCoordinatorFailoverTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.mvcc;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.GET;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SCAN;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.PUT;
+import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE;
+
+/**
+ * Mvcc cache API coordinator failover test.
+ */
+public abstract class CacheMvccAbstractCoordinatorFailoverTest extends CacheMvccAbstractBasicCoordinatorFailoverTest {
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGet_Server_Backups0_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ accountsTxReadAll(2, 0, 0, 64,
+ null, true, GET, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGet_SingleNode_CoordinatorFails() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1,
+ null, true, GET, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_Server_Backups0_CoordinatorFails() throws Exception {
+ accountsTxReadAll(2, 0, 0, 64,
+ null, true, SCAN, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_SingleNode_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ accountsTxReadAll(1, 0, 0, 1,
+ null, true, SCAN, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_Server_Backups0_RestartCoordinator_GetPut() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD , 2, 0, 0, 64,
+ null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_RestartCoordinator_GetPut_Persistence() throws Exception {
+ persistence = true;
+
+ putAllGetAll(RestartMode.RESTART_CRD , 1, 0, 0, 1,
+ null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_Servers_Backups0__PutGet_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ updateNObjectsTest(5, 2, 0, 0, 64, DFLT_TEST_TIME,
+ null, GET, PUT, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_SingleNode__PutGet_CoordinatorFails() throws Exception {
+
+ updateNObjectsTest(7, 1, 0, 0, 1, DFLT_TEST_TIME,
+ null, GET, PUT, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorFailureSimplePessimisticTxPutGet() throws Exception {
+ coordinatorFailureSimple(PESSIMISTIC, REPEATABLE_READ, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorFailureSimpleSerializableTxPutGet() throws Exception {
+ coordinatorFailureSimple(OPTIMISTIC, SERIALIZABLE, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorFailureSimpleOptimisticTxPutGet() throws Exception {
+ coordinatorFailureSimple(OPTIMISTIC, REPEATABLE_READ, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxInProgressCoordinatorChangeSimple_ReadonlyPutGet() throws Exception {
+ txInProgressCoordinatorChangeSimple(OPTIMISTIC, SERIALIZABLE, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testReadInProgressCoordinatorFailsSimple_FromClientPutGet() throws Exception {
+ readInProgressCoordinatorFailsSimple(true, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorChangeActiveQueryClientFails_Simple() throws Exception {
+ checkCoordinatorChangeActiveQueryClientFails_Simple(null, GET, PUT);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractFeatureTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractFeatureTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractFeatureTest.java
new file mode 100644
index 0000000..f5172c8
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractFeatureTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.mvcc;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import javax.cache.Cache;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteInterruptedException;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.util.lang.IgniteClosure2X;
+import org.apache.ignite.internal.util.lang.IgnitePair;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/**
+ * Test that checks
+ */
+public abstract class CacheMvccAbstractFeatureTest extends CacheMvccAbstractTest {
+ /** */
+ private static final String CACHE_NAME = "Person";
+
+ /** */
+ private Ignite node;
+
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGrids(4);
+
+ node = grid(0);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ CacheConfiguration<Integer, Person> ccfg = new CacheConfiguration<>(CACHE_NAME);
+
+ ccfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
+
+ ccfg.setIndexedTypes(Integer.class, Person.class);
+
+ node.createCache(ccfg);
+
+ for (int i = 0; i < 100; i++)
+ cache().put(i, new Person("Name" + i, "LastName" + i));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ node.destroyCache(CACHE_NAME);
+
+ super.afterTest();
+ }
+
+ /**
+ * @param clo Closure to check consistency upon.
+ * @throws Exception if failed.
+ */
+ void doTestConsistency(IgniteClosure2X<CountDownLatch, CountDownLatch, ?> clo) throws Exception {
+ ExecutorService svc = Executors.newFixedThreadPool(2);
+
+ CountDownLatch startLatch = new CountDownLatch(1);
+
+ CountDownLatch endLatch = new CountDownLatch(1);
+
+ try {
+ Future<IgnitePair<?>> fut = svc.submit(new Callable<IgnitePair<?>>() {
+ @Override public IgnitePair<?> call() {
+ try (Transaction ignored = node.transactions().txStart()) {
+ // First result that we'll later check w/respect to REPEATABLE READ semantic.
+ Object res1 = clo.apply(null, null);
+
+ Object res2 = clo.apply(startLatch, endLatch);
+
+ return new IgnitePair<>(res1, res2);
+ }
+ }
+ });
+
+ svc.submit(new Runnable() {
+ @Override public void run() {
+ try {
+ startLatch.await();
+ }
+ catch (InterruptedException e) {
+ throw new IgniteInterruptedException(e);
+ }
+
+ try {
+ modifyData(jdbcTx());
+ }
+ catch (SQLException e) {
+ throw new IgniteException(e);
+ }
+
+ endLatch.countDown();
+ }
+ }).get();
+
+ IgnitePair<?> res2 = fut.get();
+
+ assertEquals(res2.get1(), res2.get2());
+ }
+ finally {
+ svc.shutdown();
+ }
+ }
+
+ /**
+ * @return Whether native or SQL transactions must be used.
+ */
+ boolean jdbcTx() {
+ return false;
+ }
+
+ /**
+ * @param jdbcTx Whether concurrent transaction must be of SQL type.
+ */
+ private void modifyData(boolean jdbcTx) throws SQLException {
+ Set<Integer> keys = new HashSet<>(10);
+
+ for (int i = 0; i < 10; i++) {
+ int idx;
+
+ do {
+ idx = (int) (Math.random() * 100) + 1;
+ }
+ while (!keys.add(idx));
+ }
+
+ if (!jdbcTx) {
+ try (Transaction ignored = node.transactions().txStart()) {
+ for (int idx : keys) {
+ boolean rmv = Math.random() > 0.5;
+
+ if (rmv)
+ cache().remove(idx);
+ else {
+ Person p = cache().get(idx);
+
+ cache().put(idx, new Person(p.fName, p.fName + "Updated"));
+ }
+ }
+ }
+ }
+ else {
+ try (Connection c = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1")) {
+ c.setAutoCommit(false);
+
+ for (int idx : keys) {
+ boolean rmv = Math.random() > 0.5;
+
+ if (rmv) {
+ try (Statement s = c.createStatement()) {
+ s.execute("DELETE FROM \"Person\".PERSON WHERE _key = " + idx);
+ }
+ }
+ else {
+ try (Statement s = c.createStatement()) {
+ s.execute("UPDATE \"Person\".PERSON SET lname = concat(lname, 'Updated')" +
+ "WHERE _key = " + idx);
+ }
+ }
+ }
+
+ try (Statement s = c.createStatement()) {
+ s.execute("COMMIT");
+ }
+ }
+ }
+ }
+
+ /**
+ * @return Cache.
+ */
+ IgniteCache<Integer, Person> cache() {
+ return node.cache(CACHE_NAME);
+ }
+
+ /**
+ *
+ */
+ static class Person implements Serializable {
+ /** */
+ @GridToStringInclude
+ @QuerySqlField(index = true, groups = "full_name")
+ private String fName;
+
+ /** */
+ @GridToStringInclude
+ @QuerySqlField(index = true, groups = "full_name")
+ private String lName;
+
+ /**
+ * @param fName First name.
+ * @param lName Last name.
+ */
+ public Person(String fName, String lName) {
+ this.fName = fName;
+ this.lName = lName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Person.class, this);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ Person person = (Person)o;
+
+ return Objects.equals(fName, person.fName) &&
+ Objects.equals(lName, person.lName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(fName, lName);
+ }
+ }
+
+ /** */
+ final static Comparator<Cache.Entry<Integer, Person>> ENTRY_CMP =
+ new Comparator<Cache.Entry<Integer, Person>>() {
+ @Override public int compare(Cache.Entry<Integer, Person> o1, Cache.Entry<Integer, Person> o2) {
+ return o1.getKey().compareTo(o2.getKey());
+ }
+ };
+
+ /**
+ *
+ */
+ static List<Person> entriesToPersons(List<Cache.Entry<Integer, Person>> entries) {
+ entries.sort(ENTRY_CMP);
+
+ List<Person> res = new ArrayList<>();
+
+ for (Cache.Entry<Integer, Person> e : entries)
+ res.add(e.getValue());
+
+ return res;
+ }
+}
[43/45] ignite git commit: IGNITE-6552 Added ability to set WAL
history size in bytes - Fixes #3559.
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java
new file mode 100644
index 0000000..1cf237c
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java
@@ -0,0 +1,277 @@
+/*
+ * 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.persistence.db.wal;
+
+import java.io.File;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.WALMode;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointHistory;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE;
+
+/**
+ *
+ */
+public abstract class WalDeletionArchiveAbstractTest extends GridCommonAbstractTest {
+ /** */
+ public static final String CACHE_NAME = "SomeCache";
+
+ /**
+ * Start grid with override default configuration via customConfigurator.
+ */
+ private Ignite startGrid(Consumer<DataStorageConfiguration> customConfigurator) throws Exception {
+ IgniteConfiguration configuration = getConfiguration(getTestIgniteInstanceName());
+
+ DataStorageConfiguration dbCfg = new DataStorageConfiguration();
+
+ dbCfg.setWalMode(walMode());
+ dbCfg.setWalSegmentSize(512 * 1024);
+ dbCfg.setCheckpointFrequency(60 * 1000);//too high value for turn off frequency checkpoint.
+ dbCfg.setPageSize(4 * 1024);
+ dbCfg.setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+ .setMaxSize(100 * 1024 * 1024)
+ .setPersistenceEnabled(true));
+
+ customConfigurator.accept(dbCfg);
+
+ configuration.setDataStorageConfiguration(dbCfg);
+
+ Ignite ignite = startGrid(configuration);
+
+ ignite.active(true);
+
+ return ignite;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ stopAllGrids();
+
+ cleanPersistenceDir();
+
+ System.clearProperty(IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+
+ cleanPersistenceDir();
+ }
+
+ /**
+ * @return WAL mode used in test.
+ */
+ abstract protected WALMode walMode();
+
+ /**
+ * History size parameters consistency check. Should be set just one of wal history size or max wal archive size.
+ */
+ public void testGridDoesNotStart_BecauseBothWalHistorySizeAndMaxWalArchiveSizeUsed() throws Exception {
+ //given: wal history size and max wal archive size are both set.
+ IgniteConfiguration configuration = getConfiguration(getTestIgniteInstanceName());
+
+ DataStorageConfiguration dbCfg = new DataStorageConfiguration();
+ dbCfg.setWalHistorySize(12);
+ dbCfg.setMaxWalArchiveSize(9);
+ configuration.setDataStorageConfiguration(dbCfg);
+
+ try {
+ //when: start grid.
+ startGrid(getTestIgniteInstanceName(), configuration);
+ fail("Should be fail because both wal history size and max wal archive size was used");
+ }
+ catch (IgniteException e) {
+ //then: exception is occurrence because should be set just one parameters.
+ assertTrue(findSourceMessage(e).startsWith("Should be used only one of wal history size or max wal archive size"));
+ }
+ }
+
+ /**
+ * find first cause's message
+ */
+ private String findSourceMessage(Throwable ex) {
+ return ex.getCause() == null ? ex.getMessage() : findSourceMessage(ex.getCause());
+ }
+
+ /**
+ * Correct delete archived wal files.
+ */
+ public void testCorrectDeletedArchivedWalFiles() throws Exception {
+ //given: configured grid with setted max wal archive size
+ long maxWalArchiveSize = 2 * 1024 * 1024;
+ Ignite ignite = startGrid(dbCfg -> {
+ dbCfg.setMaxWalArchiveSize(maxWalArchiveSize);
+ });
+
+ GridCacheDatabaseSharedManager dbMgr = gridDatabase(ignite);
+
+ long allowedThresholdWalArchiveSize = maxWalArchiveSize / 2;
+
+ IgniteCache<Integer, Integer> cache = ignite.getOrCreateCache(CACHE_NAME);
+
+ //when: put to cache more than 2 MB
+ for (int i = 0; i < 500; i++)
+ cache.put(i, i);
+
+ forceCheckpoint();
+
+ //then: total archive size less than half of maxWalArchiveSize(by current logic)
+ IgniteWriteAheadLogManager wal = wal(ignite);
+
+ FileDescriptor[] files = (FileDescriptor[])U.findNonPublicMethod(wal.getClass(), "walArchiveFiles").invoke(wal);
+
+ Long totalSize = Stream.of(files)
+ .map(desc -> desc.file().length())
+ .reduce(0L, Long::sum);
+
+ assertTrue(files.length >= 1);
+ assertTrue(totalSize <= allowedThresholdWalArchiveSize);
+ assertFalse(Stream.of(files).anyMatch(desc -> desc.file().getName().endsWith("00001.wal")));
+
+ CheckpointHistory hist = dbMgr.checkpointHistory();
+
+ assertTrue(hist.checkpoints().size() > 0);
+ }
+
+ /**
+ * Checkpoint triggered depends on wal size.
+ */
+ public void testCheckpointStarted_WhenWalHasTooBigSizeWithoutCheckpoint() throws Exception {
+ //given: configured grid with max wal archive size = 1MB, wal segment size = 512KB
+ Ignite ignite = startGrid(dbCfg -> {
+ dbCfg.setMaxWalArchiveSize(1 * 1024 * 1024);// 1 Mbytes
+ });
+
+ GridCacheDatabaseSharedManager dbMgr = gridDatabase(ignite);
+
+ IgniteCache<Integer, Integer> cache = ignite.getOrCreateCache(CACHE_NAME);
+
+ for (int i = 0; i < 500; i++)
+ cache.put(i, i);
+
+ //then: checkpoint triggered by size limit of wall without checkpoint
+ GridCacheDatabaseSharedManager.Checkpointer checkpointer = dbMgr.getCheckpointer();
+
+ String checkpointReason = U.field((Object)U.field(checkpointer, "curCpProgress"), "reason");
+
+ assertEquals("too big size of WAL without checkpoint", checkpointReason);
+ }
+
+ /**
+ * Test for check deprecated removing checkpoint by deprecated walHistorySize parameter
+ *
+ * @deprecated Test old removing process depends on WalHistorySize.
+ */
+ public void testCheckpointHistoryRemovingByWalHistorySize() throws Exception {
+ //given: configured grid with wal history size = 10
+ int walHistorySize = 10;
+
+ Ignite ignite = startGrid(dbCfg -> {
+ dbCfg.setWalHistorySize(walHistorySize);
+ });
+
+ GridCacheDatabaseSharedManager dbMgr = gridDatabase(ignite);
+
+ IgniteCache<Integer, Integer> cache = ignite.getOrCreateCache(CACHE_NAME);
+
+ //when: put to cache and do checkpoint
+ int testNumberOfCheckpoint = walHistorySize * 2;
+
+ for (int i = 0; i < testNumberOfCheckpoint; i++) {
+ cache.put(i, i);
+ //and: wait for checkpoint finished
+ forceCheckpoint();
+ }
+
+ //then: number of checkpoints less or equal than walHistorySize
+ CheckpointHistory hist = dbMgr.checkpointHistory();
+ assertTrue(hist.checkpoints().size() == walHistorySize);
+
+ File[] cpFiles = dbMgr.checkpointDirectory().listFiles();
+
+ assertTrue(cpFiles.length <= (walHistorySize * 2 + 1));// starts & ends + node_start
+ }
+
+ /**
+ * Correct delete checkpoint history from memory depends on IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE. WAL files
+ * doesn't delete because deleting was disabled.
+ */
+ public void testCorrectDeletedCheckpointHistoryButKeepWalFiles() throws Exception {
+ System.setProperty(IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE, "2");
+ //given: configured grid with disabled WAL removing.
+ Ignite ignite = startGrid(dbCfg -> {
+ dbCfg.setMaxWalArchiveSize(Long.MAX_VALUE);
+ });
+
+ GridCacheDatabaseSharedManager dbMgr = gridDatabase(ignite);
+
+ IgniteCache<Integer, Integer> cache = ignite.getOrCreateCache(CACHE_NAME);
+
+ //when: put to cache
+ for (int i = 0; i < 500; i++) {
+ cache.put(i, i);
+
+ if (i % 10 == 0)
+ forceCheckpoint();
+ }
+
+ forceCheckpoint();
+
+ //then: WAL files was not deleted but some of checkpoint history was deleted.
+ IgniteWriteAheadLogManager wal = wal(ignite);
+
+ FileDescriptor[] files = (FileDescriptor[])U.findNonPublicMethod(wal.getClass(), "walArchiveFiles").invoke(wal);
+
+ boolean hasFirstSegment = Stream.of(files)
+ .anyMatch(desc -> desc.file().getName().endsWith("0001.wal"));
+
+ assertTrue(hasFirstSegment);
+
+ CheckpointHistory hist = dbMgr.checkpointHistory();
+
+ assertTrue(hist.checkpoints().size() == 2);
+ }
+
+ /**
+ * Extract GridCacheDatabaseSharedManager.
+ */
+ private GridCacheDatabaseSharedManager gridDatabase(Ignite ignite) {
+ return (GridCacheDatabaseSharedManager)((IgniteEx)ignite).context().cache().context().database();
+ }
+
+ /**
+ * Extract IgniteWriteAheadLogManager.
+ */
+ private IgniteWriteAheadLogManager wal(Ignite ignite) {
+ return ((IgniteEx)ignite).context().cache().context().wal();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveFsyncTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveFsyncTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveFsyncTest.java
new file mode 100644
index 0000000..64a3185
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveFsyncTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.persistence.db.wal;
+
+import org.apache.ignite.configuration.WALMode;
+
+/**
+ *
+ */
+public class WalDeletionArchiveFsyncTest extends WalDeletionArchiveAbstractTest {
+
+ /** {@inheritDoc} */
+ @Override protected WALMode walMode() {
+ return WALMode.FSYNC;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveLogOnlyTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveLogOnlyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveLogOnlyTest.java
new file mode 100644
index 0000000..b1cdf7a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveLogOnlyTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.persistence.db.wal;
+
+import org.apache.ignite.configuration.WALMode;
+
+/**
+ *
+ */
+public class WalDeletionArchiveLogOnlyTest extends WalDeletionArchiveAbstractTest {
+
+ /** {@inheritDoc} */
+ @Override protected WALMode walMode() {
+ return WALMode.LOG_ONLY;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalRecoveryTxLogicalRecordsTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalRecoveryTxLogicalRecordsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalRecoveryTxLogicalRecordsTest.java
index 5abcff4..f84e2b9 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalRecoveryTxLogicalRecordsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalRecoveryTxLogicalRecordsTest.java
@@ -473,50 +473,6 @@ public class WalRecoveryTxLogicalRecordsTest extends GridCommonAbstractTest {
}
/**
- * @throws Exception if failed.
- */
- public void testCheckpointHistory() throws Exception {
- Ignite ignite = startGrid();
-
- ignite.cluster().active(true);
-
- try {
- GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager)((IgniteEx)ignite).context()
- .cache().context().database();
-
- dbMgr.waitForCheckpoint("test");
-
- // This number depends on wal history size.
- int entries = WAL_HIST_SIZE * 2;
-
- IgniteCache<Integer, Integer> cache = ignite.cache(CACHE_NAME);
-
- for (int i = 0; i < entries; i++) {
- // Put to partition 0.
- cache.put(i * PARTS, i * PARTS);
-
- // Put to partition 1.
- cache.put(i * PARTS + 1, i * PARTS + 1);
-
- dbMgr.waitForCheckpoint("test");
- }
-
- CheckpointHistory hist = dbMgr.checkpointHistory();
-
- assertTrue(hist.checkpoints().size() <= WAL_HIST_SIZE);
-
- File cpDir = dbMgr.checkpointDirectory();
-
- File[] cpFiles = cpDir.listFiles();
-
- assertTrue(cpFiles.length <= WAL_HIST_SIZE * 2 + 1); // starts & ends + node_start
- }
- finally {
- stopAllGrids();
- }
- }
-
- /**
* @throws Exception If failed.
*/
public void testWalAfterPreloading() throws Exception {
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/MockWalIteratorFactory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/MockWalIteratorFactory.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/MockWalIteratorFactory.java
index df649fa..b783373 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/MockWalIteratorFactory.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/MockWalIteratorFactory.java
@@ -94,6 +94,7 @@ public class MockWalIteratorFactory {
when(persistentCfg1.getWalSegments()).thenReturn(segments);
when(persistentCfg1.getWalBufferSize()).thenReturn(DataStorageConfiguration.DFLT_WAL_BUFF_SIZE);
when(persistentCfg1.getWalRecordIteratorBufferSize()).thenReturn(DataStorageConfiguration.DFLT_WAL_RECORD_ITERATOR_BUFFER_SIZE);
+ when(persistentCfg1.getWalSegmentSize()).thenReturn(DataStorageConfiguration.DFLT_WAL_SEGMENT_SIZE);
final FileIOFactory fileIOFactory = new DataStorageConfiguration().getFileIOFactory();
when(persistentCfg1.getFileIOFactory()).thenReturn(fileIOFactory);
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpWALManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpWALManager.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpWALManager.java
index 3d62fe1..811a231 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpWALManager.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/NoOpWALManager.java
@@ -25,6 +25,7 @@ import org.apache.ignite.internal.pagemem.wal.WALIterator;
import org.apache.ignite.internal.pagemem.wal.WALPointer;
import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
import org.apache.ignite.lang.IgniteFuture;
/**
@@ -160,4 +161,9 @@ public class NoOpWALManager implements IgniteWriteAheadLogManager {
@Override public long lastCompactedSegment() {
return -1L;
}
+
+ /** {@inheritDoc} */
+ @Override public long maxArchivedSegmentToDelete() {
+ return -1;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
index 2614f1d..5eba5a4 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
@@ -27,23 +27,24 @@ import org.apache.ignite.internal.processors.cache.persistence.IgnitePersistentS
import org.apache.ignite.internal.processors.cache.persistence.IgniteRebalanceScheduleResendPartitionsTest;
import org.apache.ignite.internal.processors.cache.persistence.LocalWacModeNoChangeDuringRebalanceOnNonNodeAssignTest;
import org.apache.ignite.internal.processors.cache.persistence.LocalWalModeChangeDuringRebalancingSelfTest;
-import org.apache.ignite.internal.processors.cache.persistence.baseline.IgniteAbsentEvictionNodeOutOfBaselineTest;
import org.apache.ignite.internal.processors.cache.persistence.baseline.ClientAffinityAssignmentWithBaselineTest;
+import org.apache.ignite.internal.processors.cache.persistence.baseline.IgniteAbsentEvictionNodeOutOfBaselineTest;
import org.apache.ignite.internal.processors.cache.persistence.baseline.IgniteAllBaselineNodesOnlineFullApiSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.baseline.IgniteOfflineBaselineNodeFullApiSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.baseline.IgniteOnlineNodeOutOfBaselineFullApiSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsRebalancingOnNotStableTopologyTest;
+import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsReserveWalSegmentsTest;
+import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsTransactionsHangTest;
import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsWholeClusterRestartTest;
import org.apache.ignite.internal.processors.cache.persistence.db.SlowHistoricalRebalanceSmallHistoryTest;
import org.apache.ignite.internal.processors.cache.persistence.db.checkpoint.IgniteCheckpointDirtyPagesForLowLoadTest;
-import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsUnusedWalSegmentsTest;
import org.apache.ignite.internal.processors.cache.persistence.db.filename.IgniteUidAsConsistentIdMigrationTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteNodeStoppedDuringDisableWALTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWALTailIsReachedDuringIterationOverArchiveTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushBackgroundSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushBackgroundWithMmapBufferSelfTest;
-import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushFsyncSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushFailoverTest;
+import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushFsyncSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushFsyncWithDedicatedWorkerSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushFsyncWithMmapBufferSelfTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalFlushLogOnlySelfTest;
@@ -55,6 +56,8 @@ import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalI
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalRecoverySeveralRestartsTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalSerializerVersionTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.WalCompactionTest;
+import org.apache.ignite.internal.processors.cache.persistence.db.wal.WalDeletionArchiveFsyncTest;
+import org.apache.ignite.internal.processors.cache.persistence.db.wal.WalDeletionArchiveLogOnlyTest;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.crc.IgniteDataIntegrityTests;
import org.apache.ignite.internal.processors.cache.persistence.db.wal.reader.IgniteWalReaderTest;
@@ -146,7 +149,7 @@ public class IgnitePdsTestSuite2 extends TestSuite {
suite.addTestSuite(IgnitePdsExchangeDuringCheckpointTest.class);
- suite.addTestSuite(IgnitePdsUnusedWalSegmentsTest.class);
+ suite.addTestSuite(IgnitePdsReserveWalSegmentsTest.class);
// new style folders with generated consistent ID test
suite.addTestSuite(IgniteUidAsConsistentIdMigrationTest.class);
@@ -155,6 +158,9 @@ public class IgnitePdsTestSuite2 extends TestSuite {
suite.addTestSuite(WalCompactionTest.class);
+ suite.addTestSuite(WalDeletionArchiveFsyncTest.class);
+ suite.addTestSuite(WalDeletionArchiveLogOnlyTest.class);
+
suite.addTestSuite(IgniteCheckpointDirtyPagesForLowLoadTest.class);
suite.addTestSuite(IgnitePdsCorruptedStoreTest.class);
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/DataStorageConfigurationParityTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/DataStorageConfigurationParityTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/DataStorageConfigurationParityTest.cs
index 51f2865..2276c20 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/DataStorageConfigurationParityTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ApiParity/DataStorageConfigurationParityTest.cs
@@ -28,7 +28,8 @@ namespace Apache.Ignite.Core.Tests.ApiParity
/** Properties that are not needed on .NET side. */
private static readonly string[] UnneededProperties =
{
- "FileIOFactory"
+ "FileIOFactory",
+ "isWalHistorySizeParameterUsed"
};
/** Properties that are missing on .NET side. */
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
index 7a3778c..0a010b4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
@@ -164,6 +164,11 @@ namespace Apache.Ignite.Core.Configuration
public const int DefaultConcurrencyLevel = 0;
/// <summary>
+ /// Default value for <see cref="MaxWalArchiveSize"/>.
+ /// </summary>
+ public const long DefaultMaxWalArchiveSize = 1024 * 1024 * 1024;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="DataStorageConfiguration"/> class.
/// </summary>
public DataStorageConfiguration()
@@ -190,6 +195,7 @@ namespace Apache.Ignite.Core.Configuration
SystemRegionMaxSize = DefaultSystemRegionMaxSize;
PageSize = DefaultPageSize;
WalAutoArchiveAfterInactivity = DefaultWalAutoArchiveAfterInactivity;
+ MaxWalArchiveSize = DefaultMaxWalArchiveSize;
}
/// <summary>
@@ -221,6 +227,7 @@ namespace Apache.Ignite.Core.Configuration
CheckpointWriteOrder = (CheckpointWriteOrder)reader.ReadInt();
WriteThrottlingEnabled = reader.ReadBoolean();
WalCompactionEnabled = reader.ReadBoolean();
+ MaxWalArchiveSize = reader.ReadLong();
SystemRegionInitialSize = reader.ReadLong();
SystemRegionMaxSize = reader.ReadLong();
@@ -272,6 +279,7 @@ namespace Apache.Ignite.Core.Configuration
writer.WriteInt((int)CheckpointWriteOrder);
writer.WriteBoolean(WriteThrottlingEnabled);
writer.WriteBoolean(WalCompactionEnabled);
+ writer.WriteLong(MaxWalArchiveSize);
writer.WriteLong(SystemRegionInitialSize);
writer.WriteLong(SystemRegionMaxSize);
@@ -445,6 +453,12 @@ namespace Apache.Ignite.Core.Configuration
public bool WalCompactionEnabled { get; set; }
/// <summary>
+ /// Gets or sets maximum size of wal archive folder, in bytes.
+ /// </summary>
+ [DefaultValue(DefaultMaxWalArchiveSize)]
+ public long MaxWalArchiveSize { get; set; }
+
+ /// <summary>
/// Gets or sets the size of a memory chunk reserved for system needs.
/// </summary>
[DefaultValue(DefaultSystemRegionInitialSize)]
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
index 8db5afa..ebbef67 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
@@ -1855,6 +1855,11 @@
<xs:documentation>If true, system will filter and compress WAL archive in background. Compressed WAL archive gets automatically decompressed on demand.</xs:documentation>
</xs:annotation>
</xs:attribute>
+ <xs:attribute name="maxWalArchiveSize" type="xs:long">
+ <xs:annotation>
+ <xs:documentation>Maximum size of wal archive folder, in bytes.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="pageSize" type="xs:int">
<xs:annotation>
<xs:documentation>Size of the memory page.</xs:documentation>
[19/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractTest.java
new file mode 100644
index 0000000..1abb45f
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractTest.java
@@ -0,0 +1,2291 @@
+/*
+ * 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.mvcc;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+import javax.cache.Cache;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteTransactions;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.cluster.ClusterTopologyException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
+import org.apache.ignite.configuration.WALMode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.TestRecordingCommunicationSpi;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.util.future.GridCompoundIdentityFuture;
+import org.apache.ignite.internal.util.lang.GridAbsPredicate;
+import org.apache.ignite.internal.util.lang.GridInClosure3;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL_SUM;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.DML;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ *
+ */
+public abstract class CacheMvccAbstractTest extends GridCommonAbstractTest {
+ /** */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** */
+ protected static final ObjectCodec<Integer> INTEGER_CODEC = new IntegerCodec();
+
+ /** */
+ protected static final ObjectCodec<MvccTestAccount> ACCOUNT_CODEC = new AccountCodec();
+
+ /** */
+ static final int DFLT_PARTITION_COUNT = RendezvousAffinityFunction.DFLT_PARTITION_COUNT;
+
+ /** */
+ static final String CRD_ATTR = "testCrd";
+
+ /** */
+ static final long DFLT_TEST_TIME = 30_000;
+
+ /** */
+ protected static final int PAGE_SIZE = DataStorageConfiguration.DFLT_PAGE_SIZE;
+
+ /** */
+ protected static final int SRVS = 4;
+
+ /** */
+ protected boolean client;
+
+ /** */
+ protected boolean testSpi;
+
+ /** */
+ protected String nodeAttr;
+
+ /** */
+ protected boolean persistence;
+
+ /** */
+ protected CacheConfiguration ccfg;
+
+ /** */
+ protected CacheConfiguration[] ccfgs;
+
+ /** */
+ protected boolean disableScheduledVacuum;
+
+ /** */
+ protected static final int TX_TIMEOUT = 3000;
+
+ /**
+ * @return Cache mode.
+ */
+ protected abstract CacheMode cacheMode();
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+ cfg.setMvccEnabled(true);
+
+ if (disableScheduledVacuum)
+ cfg.setMvccVacuumTimeInterval(Integer.MAX_VALUE);
+
+ ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+ if (testSpi)
+ cfg.setCommunicationSpi(new TestRecordingCommunicationSpi());
+
+ ((TcpCommunicationSpi)cfg.getCommunicationSpi()).setSharedMemoryPort(-1);
+
+ cfg.setClientMode(client);
+
+ assert (ccfg == null) || (ccfgs == null);
+
+ if (ccfg != null)
+ cfg.setCacheConfiguration(ccfg);
+
+ if (ccfgs != null)
+ cfg.setCacheConfiguration(ccfgs);
+
+ if (nodeAttr != null)
+ cfg.setUserAttributes(F.asMap(nodeAttr, true));
+
+ DataStorageConfiguration storageCfg = new DataStorageConfiguration();
+
+ storageCfg.setWalMode(WALMode.LOG_ONLY);
+ storageCfg.setPageSize(PAGE_SIZE);
+
+ DataRegionConfiguration regionCfg = new DataRegionConfiguration();
+
+ regionCfg.setPersistenceEnabled(persistence);
+ regionCfg.setMaxSize(64L * 1024 * 1024);
+
+ storageCfg.setDefaultDataRegionConfiguration(regionCfg);
+
+ cfg.setDataStorageConfiguration(storageCfg);
+
+ cfg.setConsistentId(gridName);
+
+ cfg.setTransactionConfiguration(new TransactionConfiguration()
+ .setDefaultTxConcurrency(TransactionConcurrency.PESSIMISTIC)
+ .setDefaultTxIsolation(TransactionIsolation.REPEATABLE_READ));
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected long getTestTimeout() {
+ return DFLT_TEST_TIME + 60_000;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ MvccProcessorImpl.coordinatorAssignClosure(null);
+
+ cleanPersistenceDir();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ try {
+ verifyOldVersionsCleaned();
+
+ verifyCoordinatorInternalState();
+ }
+ finally {
+ stopAllGrids();
+ }
+
+ MvccProcessorImpl.coordinatorAssignClosure(null);
+
+ cleanPersistenceDir();
+
+ super.afterTest();
+ }
+
+ /**
+ * @param cfgC Optional closure applied to cache configuration.
+ * @throws Exception If failed.
+ */
+ final void cacheRecreate(@Nullable IgniteInClosure<CacheConfiguration> cfgC) throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ final int PARTS = 64;
+
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, PARTS);
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ IgniteCache<Integer, MvccTestAccount> cache = (IgniteCache)srv0.createCache(ccfg);
+
+ for (int k = 0; k < PARTS * 2; k++) {
+ assertNull(cache.get(k));
+
+ int vals = k % 3 + 1;
+
+ for (int v = 0; v < vals; v++)
+ cache.put(k, new MvccTestAccount(v, 1));
+
+ assertEquals(vals - 1, cache.get(k).val);
+ }
+
+ srv0.destroyCache(cache.getName());
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, PARTS);
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ cache = (IgniteCache)srv0.createCache(ccfg);
+
+ for (int k = 0; k < PARTS * 2; k++) {
+ assertNull(cache.get(k));
+
+ int vals = k % 3 + 2;
+
+ for (int v = 0; v < vals; v++)
+ cache.put(k, new MvccTestAccount(v + 100, 1));
+
+ assertEquals(vals - 1 + 100, cache.get(k).val);
+ }
+
+ srv0.destroyCache(cache.getName());
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, PARTS);
+
+ IgniteCache<Long, Long> cache0 = (IgniteCache)srv0.createCache(ccfg);
+
+ for (long k = 0; k < PARTS * 2; k++) {
+ assertNull(cache0.get(k));
+
+ int vals = (int)(k % 3 + 2);
+
+ for (long v = 0; v < vals; v++)
+ cache0.put(k, v);
+
+ assertEquals((long)(vals - 1), (Object)cache0.get(k));
+ }
+ }
+
+ /**
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param cfgC Optional closure applied to cache configuration.
+ * @param withRmvs If {@code true} then in addition to puts tests also executes removes.
+ * @param readMode Read mode.
+ * @param writeMode Write mode.
+ * @throws Exception If failed.
+ */
+ final void accountsTxReadAll(
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ final boolean withRmvs,
+ final ReadMode readMode,
+ final WriteMode writeMode
+ ) throws Exception {
+ accountsTxReadAll(srvs, clients, cacheBackups, cacheParts, cfgC, withRmvs, readMode, writeMode, DFLT_TEST_TIME, null);
+ }
+
+ /**
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param cfgC Optional closure applied to cache configuration.
+ * @param withRmvs If {@code true} then in addition to puts tests also executes removes.
+ * @param readMode Read mode.
+ * @param writeMode Write mode.
+ * @param testTime Test time.
+ * @throws Exception If failed.
+ */
+ final void accountsTxReadAll(
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ final boolean withRmvs,
+ final ReadMode readMode,
+ final WriteMode writeMode,
+ long testTime,
+ RestartMode restartMode
+ ) throws Exception {
+ final int ACCOUNTS = 20;
+
+ final int ACCOUNT_START_VAL = 1000;
+
+ final int writers = 4;
+
+ final int readers = 4;
+
+ final IgniteInClosure<IgniteCache<Object, Object>> init = new IgniteInClosure<IgniteCache<Object, Object>>() {
+ @Override public void apply(IgniteCache<Object, Object> cache) {
+ final IgniteTransactions txs = cache.unwrap(Ignite.class).transactions();
+
+ if (writeMode == WriteMode.PUT) {
+ Map<Integer, MvccTestAccount> accounts = new HashMap<>();
+
+ for (int i = 0; i < ACCOUNTS; i++)
+ accounts.put(i, new MvccTestAccount(ACCOUNT_START_VAL, 1));
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.putAll(accounts);
+
+ tx.commit();
+ }
+ }
+ else if (writeMode == WriteMode.DML) {
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("insert into MvccTestAccount(_key, val, updateCnt) values " +
+ "(?," + ACCOUNT_START_VAL + ",1)");
+
+ for (int i = 0; i < ACCOUNTS; i++) {
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry.setArgs(i))) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+ }
+ }
+ else
+ assert false : "Unknown write mode";
+ }
+ };
+
+ final RemovedAccountsTracker rmvdTracker = new RemovedAccountsTracker(ACCOUNTS);
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestAccount> cache = randomCache(caches, rnd);
+
+ try {
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ cnt++;
+
+ int i1 = rnd.nextInt(ACCOUNTS), i2 = rnd.nextInt(ACCOUNTS);
+
+ while (i2 == i1)
+ i2 = rnd.nextInt(ACCOUNTS);
+
+ Integer id1 = Math.min(i1, i2);
+ Integer id2 = Math.max(i1, i2);
+
+ TreeSet<Integer> keys = new TreeSet<>();
+
+ keys.add(id1);
+ keys.add(id2);
+
+ Integer cntr1 = null;
+ Integer cntr2 = null;
+
+ Integer rmvd = null;
+ Integer inserted = null;
+
+ MvccTestAccount a1;
+ MvccTestAccount a2;
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ Map<Integer, MvccTestAccount> accounts = null;
+
+ if (writeMode == WriteMode.PUT)
+ accounts = cache.cache.getAll(keys);
+ else if (writeMode == WriteMode.DML)
+ accounts = getAllSql(cache);
+ else
+ assert false : "Unknown write mode";
+
+ a1 = accounts.get(id1);
+ a2 = accounts.get(id2);
+
+ if (!withRmvs) {
+ assertNotNull(a1);
+ assertNotNull(a2);
+
+ cntr1 = a1.updateCnt + 1;
+ cntr2 = a2.updateCnt + 1;
+
+ if (writeMode == WriteMode.PUT) {
+ cache.cache.put(id1, new MvccTestAccount(a1.val + 1, cntr1));
+ cache.cache.put(id2, new MvccTestAccount(a2.val - 1, cntr2));
+ }
+ else if (writeMode == WriteMode.DML) {
+ updateSql(cache, id1, a1.val + 1, cntr1);
+ updateSql(cache, id2, a2.val - 1, cntr2);
+ }
+ else
+ assert false : "Unknown write mode";
+ }
+ else {
+ if (a1 != null || a2 != null) {
+ if (a1 != null && a2 != null) {
+ if (rnd.nextInt(10) == 0) {
+ if (rmvdTracker.size() < ACCOUNTS / 2) {
+ rmvd = rnd.nextBoolean() ? id1 : id2;
+
+ assertTrue(rmvdTracker.markRemoved(rmvd));
+ }
+ }
+
+ if (rmvd != null) {
+ if (writeMode == WriteMode.PUT) {
+ if (rmvd.equals(id1)) {
+ cache.cache.remove(id1);
+ cache.cache.put(id2, new MvccTestAccount(a1.val + a2.val, 1));
+ }
+ else {
+ cache.cache.put(id1, new MvccTestAccount(a1.val + a2.val, 1));
+ cache.cache.remove(id2);
+ }
+ }
+ else if (writeMode == WriteMode.DML) {
+ if (rmvd.equals(id1)) {
+ removeSql(cache, id1);
+ updateSql(cache, id2,a1.val + a2.val, 1);
+ }
+ else {
+ updateSql(cache, id1,a1.val + a2.val, 1);
+ removeSql(cache, id2);
+ }
+ }
+ else
+ assert false : "Unknown write mode";
+ }
+ else {
+ if (writeMode == WriteMode.PUT) {
+ cache.cache.put(id1, new MvccTestAccount(a1.val + 1, 1));
+ cache.cache.put(id2, new MvccTestAccount(a2.val - 1, 1));
+ }
+ else if (writeMode == WriteMode.DML) {
+ updateSql(cache, id1, a1.val + 1, 1);
+ updateSql(cache, id2, a2.val - 1, 1);
+ }
+ else
+ assert false : "Unknown write mode";
+ }
+ }
+ else {
+ if (a1 == null) {
+ inserted = id1;
+
+ if (writeMode == WriteMode.PUT) {
+ cache.cache.put(id1, new MvccTestAccount(100, 1));
+ cache.cache.put(id2, new MvccTestAccount(a2.val - 100, 1));
+ }
+ else if (writeMode == WriteMode.DML) {
+ insertSql(cache, id1, 100, 1);
+ updateSql(cache, id2, a2.val - 100, 1);
+ }
+ else
+ assert false : "Unknown write mode";
+ }
+ else {
+ inserted = id2;
+
+ if (writeMode == WriteMode.PUT) {
+ cache.cache.put(id1, new MvccTestAccount(a1.val - 100, 1));
+ cache.cache.put(id2, new MvccTestAccount(100, 1));
+ }
+ else if (writeMode == WriteMode.DML) {
+ updateSql(cache, id1, a1.val - 100, 1);
+ insertSql(cache, id2, 100, 1);
+ }
+ else
+ assert false : "Unknown write mode";
+ }
+ }
+ }
+ }
+
+ tx.commit();
+
+ // In case of tx success mark inserted.
+ if (inserted != null) {
+ assert withRmvs;
+
+ assertTrue(rmvdTracker.unmarkRemoved(inserted));
+ }
+ }
+ catch (Throwable e) {
+ if (rmvd != null) {
+ assert withRmvs;
+
+ // If tx fails, unmark removed.
+ assertTrue(rmvdTracker.unmarkRemoved(rmvd));
+ }
+
+ throw e;
+ }
+
+ if (!withRmvs) {
+ Map<Integer, MvccTestAccount> accounts = null;
+
+ if (writeMode == WriteMode.PUT)
+ accounts = cache.cache.getAll(keys);
+ else if (writeMode == WriteMode.DML)
+ accounts = getAllSql(cache);
+ else
+ assert false : "Unknown write mode";
+
+ a1 = accounts.get(id1);
+ a2 = accounts.get(id2);
+
+ assertNotNull(a1);
+ assertNotNull(a2);
+
+ assertTrue(a1.updateCnt >= cntr1);
+ assertTrue(a2.updateCnt >= cntr2);
+ }
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Set<Integer> keys = new LinkedHashSet<>();
+
+ Map<Integer, Integer> lastUpdateCntrs = new HashMap<>();
+
+ SqlFieldsQuery sumQry = new SqlFieldsQuery("select sum(val) from MvccTestAccount");
+
+ while (!stop.get()) {
+ while (keys.size() < ACCOUNTS)
+ keys.add(rnd.nextInt(ACCOUNTS));
+
+ TestCache<Integer, MvccTestAccount> cache = randomCache(caches, rnd);
+
+ Map<Integer, MvccTestAccount> accounts = null;
+
+ try {
+ switch (readMode) {
+ case GET: {
+ accounts = cache.cache.getAll(keys);
+
+ break;
+ }
+
+ case SCAN: {
+ accounts = new HashMap<>();
+
+ Iterator<Cache.Entry<Integer, MvccTestAccount>> it = cache.cache.iterator();
+
+ try {
+ for (; it.hasNext(); ) {
+ IgniteCache.Entry<Integer, MvccTestAccount> e = it.next();
+ MvccTestAccount old = accounts.put(e.getKey(), e.getValue());
+
+ assertNull("new=" + e + ", old=" + old, old);
+ }
+ } finally {
+ U.closeQuiet((AutoCloseable) it);
+ }
+
+ break;
+ }
+
+ case SQL: {
+ accounts = new HashMap<>();
+
+ if (rnd.nextBoolean()) {
+ SqlQuery<Integer, MvccTestAccount> qry =
+ new SqlQuery<>(MvccTestAccount.class, "_key >= 0");
+
+ for (IgniteCache.Entry<Integer, MvccTestAccount> e : cache.cache.query(qry).getAll()) {
+ MvccTestAccount old = accounts.put(e.getKey(), e.getValue());
+
+ assertNull(old);
+ }
+ }
+ else {
+ SqlFieldsQuery qry = new SqlFieldsQuery("select _key, val from MvccTestAccount");
+
+ for (List<?> row : cache.cache.query(qry).getAll()) {
+ Integer id = (Integer)row.get(0);
+ Integer val = (Integer)row.get(1);
+
+ MvccTestAccount old = accounts.put(id, new MvccTestAccount(val, 1));
+
+ assertNull(old);
+ }
+ }
+
+ break;
+ }
+
+ case SQL_SUM: {
+ BigDecimal sum;
+
+ if (rnd.nextBoolean()) {
+ List<List<?>> res = cache.cache.query(sumQry).getAll();
+
+ assertEquals(1, res.size());
+
+ sum = (BigDecimal)res.get(0).get(0);
+ }
+ else {
+ Map res = readAllByMode(cache.cache, keys, readMode, ACCOUNT_CODEC);
+
+ sum = (BigDecimal)((Map.Entry)res.entrySet().iterator().next()).getValue();
+ }
+
+ assertEquals(ACCOUNT_START_VAL * ACCOUNTS, sum.intValue());
+
+ break;
+ }
+
+ default: {
+ fail();
+
+ return;
+ }
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ if (accounts != null) {
+ if (!withRmvs)
+ assertEquals(ACCOUNTS, accounts.size());
+
+ int sum = 0;
+
+ for (int i = 0; i < ACCOUNTS; i++) {
+ MvccTestAccount account = accounts.get(i);
+
+ if (account != null) {
+ sum += account.val;
+
+ Integer cntr = lastUpdateCntrs.get(i);
+
+ if (cntr != null)
+ assertTrue(cntr <= account.updateCnt);
+
+ lastUpdateCntrs.put(i, cntr);
+ }
+ else
+ assertTrue(withRmvs);
+ }
+
+ assertEquals(ACCOUNTS * ACCOUNT_START_VAL, sum);
+ }
+ }
+
+ if (idx == 0) {
+ TestCache<Integer, MvccTestAccount> cache = randomCache(caches, rnd);
+
+ Map<Integer, MvccTestAccount> accounts;
+
+ ReadMode readMode0 = readMode == SQL_SUM ? SQL : readMode;
+
+ try {
+ accounts = readAllByMode(cache.cache, keys, readMode0, ACCOUNT_CODEC);;
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ int sum = 0;
+
+ for (int i = 0; i < ACCOUNTS; i++) {
+ MvccTestAccount account = accounts.get(i);
+
+ assertTrue(account != null || withRmvs);
+
+ info("Account [id=" + i + ", val=" + (account != null ? account.val : null) + ']');
+
+ if (account != null)
+ sum += account.val;
+ }
+
+ info("Sum: " + sum);
+ }
+ }
+ };
+
+ readWriteTest(
+ restartMode,
+ srvs,
+ clients,
+ cacheBackups,
+ cacheParts,
+ writers,
+ readers,
+ testTime,
+ cfgC,
+ init,
+ writer,
+ reader);
+ }
+
+ /**
+ * Returns all accounts from cache by means of SQL.
+ *
+ * @param cache Cache.
+ * @return All accounts
+ */
+ private static Map<Integer, MvccTestAccount> getAllSql(TestCache<Integer, MvccTestAccount> cache) {
+ Map<Integer, MvccTestAccount> accounts = new HashMap<>();
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("select _key, val, updateCnt from MvccTestAccount");
+
+ for (List<?> row : cache.cache.query(qry).getAll()) {
+ Integer id = (Integer)row.get(0);
+ Integer val = (Integer)row.get(1);
+ Integer updateCnt = (Integer)row.get(2);
+
+ MvccTestAccount old = accounts.put(id, new MvccTestAccount(val, updateCnt));
+
+ assertNull(old);
+ }
+
+ return accounts;
+ }
+
+ /**
+ * Updates account by means of SQL API.
+ *
+ * @param cache Cache.
+ * @param key Key.
+ * @param val Value.
+ * @param updateCnt Update counter.
+ */
+ private static void updateSql(TestCache<Integer, MvccTestAccount> cache, Integer key, Integer val, Integer updateCnt) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("update MvccTestAccount set val=" + val + ", updateCnt=" +
+ updateCnt + " where _key=" + key);
+
+ cache.cache.query(qry).getAll();
+ }
+
+ /**
+ * Removes account by means of SQL API.
+ *
+ * @param cache Cache.
+ * @param key Key.
+ */
+ private static void removeSql(TestCache<Integer, MvccTestAccount> cache, Integer key) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("delete from MvccTestAccount where _key=" + key);
+
+ cache.cache.query(qry).getAll();
+ }
+
+ /**
+ * Inserts account by means of SQL API.
+ *
+ * @param cache Cache.
+ * @param key Key.
+ * @param val Value.
+ * @param updateCnt Update counter.
+ */
+ private static void insertSql(TestCache<Integer, MvccTestAccount> cache, int key, Integer val, Integer updateCnt) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("insert into MvccTestAccount(_key, val, updateCnt) values " +
+ " (" + key+ ", " + val + ", " + updateCnt + ")");
+
+ cache.cache.query(qry).getAll();
+ }
+
+ /**
+ * @param restartMode Restart mode.
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param readMode Read mode.
+ * @throws Exception If failed.
+ */
+ @SuppressWarnings("unchecked")
+ protected void putAllGetAll(
+ RestartMode restartMode,
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ ReadMode readMode,
+ WriteMode writeMode
+ ) throws Exception {
+ final int RANGE = 20;
+
+ final int writers = 4;
+
+ final int readers = 4;
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int min = idx * RANGE;
+ int max = min + RANGE;
+
+ info("Thread range [min=" + min + ", max=" + max + ']');
+
+ Map<Integer, Integer> map = new HashMap<>();
+
+ int v = idx * 1_000_000;
+
+ boolean first = true;
+
+ while (!stop.get()) {
+ while (map.size() < RANGE)
+ map.put(rnd.nextInt(min, max), v);
+
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+
+ try {
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ if (!first && rnd.nextBoolean()) {
+ Map<Integer, Integer> res = readAllByMode(cache.cache, map.keySet(), readMode, INTEGER_CODEC);
+
+ for (Integer k : map.keySet())
+ assertEquals("res=" + res, v - 1, (Object)res.get(k));
+ }
+
+ writeAllByMode(cache.cache, map, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+
+ first = false;
+ }
+
+ if (rnd.nextBoolean()) {
+ Map<Integer, Integer> res = readAllByMode(cache.cache, map.keySet(), readMode, INTEGER_CODEC);
+
+ for (Integer k : map.keySet())
+ assertEquals("key=" + k, v, (Object)res.get(k));
+ }
+
+ map.clear();
+
+ v++;
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ finally {
+ cache.readUnlock();
+
+ map.clear();
+ }
+ }
+
+ info("Writer done, updates: " + v);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Set<Integer> keys = new LinkedHashSet<>();
+
+ Map<Integer, Integer> readVals = new HashMap<>();
+
+ while (!stop.get()) {
+ int range = rnd.nextInt(0, writers);
+
+ int min = range * RANGE;
+ int max = min + RANGE;
+
+ while (keys.size() < RANGE)
+ keys.add(rnd.nextInt(min, max));
+
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+
+ Map<Integer, Integer> map;
+
+ try {
+ map = readAllByMode(cache.cache, keys, readMode, INTEGER_CODEC);
+ }
+ catch (Exception e) {
+ handleTxException(e);
+
+ continue;
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ assertTrue("Invalid map size: " + map.size() + ", map=" + map, map.isEmpty() || map.size() == RANGE);
+
+ Integer val0 = null;
+
+ for (Map.Entry<Integer, Integer> e: map.entrySet()) {
+ Integer val = e.getValue();
+
+ assertNotNull(val);
+
+ if (val0 == null) {
+ Integer readVal = readVals.get(range);
+
+ if (readVal != null)
+ assertTrue("readVal=" + readVal + ", val=" + val + ", map=" + map,readVal <= val);
+
+ readVals.put(range, val);
+
+ val0 = val;
+ }
+ else {
+ if (!F.eq(val0, val)) {
+ assertEquals("Unexpected value [range=" + range + ", key=" + e.getKey() + ']' +
+ ", map=" + map,
+ val0,
+ val);
+ }
+ }
+ }
+
+ keys.clear();
+ }
+ }
+ };
+
+ readWriteTest(
+ restartMode,
+ srvs,
+ clients,
+ cacheBackups,
+ cacheParts,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ cfgC,
+ null,
+ writer,
+ reader);
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+
+
+
+ /**
+ * @param N Number of object to update in single transaction.
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param time Test time.
+ * @param readMode Read mode.
+ * @throws Exception If failed.
+ */
+ @SuppressWarnings("unchecked")
+ protected void updateNObjectsTest(
+ final int N,
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ long time,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ ReadMode readMode,
+ WriteMode writeMode,
+ RestartMode restartMode
+ )
+ throws Exception
+ {
+ final int TOTAL = 20;
+
+ assert N <= TOTAL;
+
+ info("updateNObjectsTest [n=" + N + ", total=" + TOTAL + ']');
+
+ final int writers = 4;
+
+ final int readers = 4;
+
+ final IgniteInClosure<IgniteCache<Object, Object>> init = new IgniteInClosure<IgniteCache<Object, Object>>() {
+ @Override public void apply(IgniteCache<Object, Object> cache) {
+ final IgniteTransactions txs = cache.unwrap(Ignite.class).transactions();
+
+ Map<Integer, Integer> vals = new HashMap<>();
+
+ for (int i = 0; i < TOTAL; i++)
+ vals.put(i, N);
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ writeAllByMode(cache, vals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ TreeSet<Integer> keys = new TreeSet<>();
+
+ while (keys.size() < N)
+ keys.add(rnd.nextInt(TOTAL));
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ Map<Integer, Integer> curVals = readAllByMode(cache.cache, keys, readMode, INTEGER_CODEC);
+
+ assertEquals(N, curVals.size());
+
+ Map<Integer, Integer> newVals = new TreeMap<>();
+
+ for (Map.Entry<Integer, Integer> e : curVals.entrySet())
+ newVals.put(e.getKey(), e.getValue() + 1);
+
+ writeAllByMode(cache.cache, newVals, writeMode, INTEGER_CODEC);
+
+ tx.commit();
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ cnt++;
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Set<Integer> keys = new LinkedHashSet<>();
+
+ while (!stop.get()) {
+ while (keys.size() < TOTAL)
+ keys.add(rnd.nextInt(TOTAL));
+
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+
+ Map<Integer, Integer> vals = null;
+
+ try {
+ vals = readAllByMode(cache.cache, keys, readMode, INTEGER_CODEC);
+ }
+ catch (Exception e) {
+ handleTxException(e);
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ assertEquals("vals=" + vals, TOTAL, vals.size());
+
+ int sum = 0;
+
+ for (int i = 0; i < TOTAL; i++) {
+ Integer val = vals.get(i);
+
+ assertNotNull(val);
+
+ sum += val;
+ }
+
+ assertEquals(0, sum % N);
+ }
+
+ if (idx == 0) {
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+
+ Map<Integer, Integer> vals;
+
+ try {
+ vals = readAllByMode(cache.cache, keys, readMode, INTEGER_CODEC);
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ int sum = 0;
+
+ for (int i = 0; i < TOTAL; i++) {
+ Integer val = vals.get(i);
+
+ info("Value [id=" + i + ", val=" + val + ']');
+
+ sum += val;
+ }
+
+ info("Sum [sum=" + sum + ", mod=" + sum % N + ']');
+ }
+ }
+ };
+
+ readWriteTest(
+ restartMode,
+ srvs,
+ clients,
+ cacheBackups,
+ cacheParts,
+ writers,
+ readers,
+ time,
+ cfgC,
+ init,
+ writer,
+ reader);
+ }
+
+ /**
+ * @param restartMode Restart mode.
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param time Test time.
+ * @param cfgC Optional closure applied to cache configuration.
+ * @param writers Number of writers.
+ * @param readers Number of readers.
+ * @param init Optional init closure.
+ * @param writer Writers threads closure.
+ * @param reader Readers threads closure.
+ * @throws Exception If failed.
+ */
+ final void readWriteTest(
+ final RestartMode restartMode,
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ final int writers,
+ final int readers,
+ final long time,
+ @Nullable IgniteInClosure<CacheConfiguration> cfgC,
+ IgniteInClosure<IgniteCache<Object, Object>> init,
+ final GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer,
+ final GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader) throws Exception {
+ if (restartMode == RestartMode.RESTART_CRD)
+ MvccProcessorImpl.coordinatorAssignClosure(new CoordinatorAssignClosure());
+
+ Ignite srv0 = startGridsMultiThreaded(srvs);
+
+ if (clients > 0) {
+ client = true;
+
+ startGridsMultiThreaded(srvs, clients);
+
+ client = false;
+ }
+
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(),
+ FULL_SYNC,
+ cacheBackups,
+ cacheParts);
+
+ if (restartMode == RestartMode.RESTART_CRD)
+ ccfg.setNodeFilter(new CoordinatorNodeFilter());
+
+ if (cfgC != null)
+ cfgC.apply(ccfg);
+
+ IgniteCache<Object, Object> cache = srv0.createCache(ccfg);
+
+ int crdIdx = srvs + clients;
+
+ if (restartMode == RestartMode.RESTART_CRD) {
+ nodeAttr = CRD_ATTR;
+
+ startGrid(crdIdx);
+ }
+
+ if (init != null)
+ init.apply(cache);
+
+ final List<TestCache> caches = new ArrayList<>(srvs + clients);
+
+ for (int i = 0; i < srvs + clients; i++) {
+ Ignite node = grid(i);
+
+ caches.add(new TestCache(node.cache(cache.getName())));
+ }
+
+ final long stopTime = U.currentTimeMillis() + time;
+
+ final AtomicBoolean stop = new AtomicBoolean();
+
+ try {
+ final AtomicInteger writerIdx = new AtomicInteger();
+
+ IgniteInternalFuture<?> writeFut = GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try {
+ int idx = writerIdx.getAndIncrement();
+
+ writer.apply(idx, caches, stop);
+ }
+ catch (Throwable e) {
+ if (restartMode != null && X.hasCause(e, ClusterTopologyException.class)) {
+ log.info("Writer error: " + e);
+
+ return null;
+ }
+
+ error("Unexpected error: " + e, e);
+
+ stop.set(true);
+
+ fail("Unexpected error: " + e);
+ }
+
+ return null;
+ }
+ }, writers, "writer");
+
+ final AtomicInteger readerIdx = new AtomicInteger();
+
+ IgniteInternalFuture<?> readFut = GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try {
+ int idx = readerIdx.getAndIncrement();
+
+ reader.apply(idx, caches, stop);
+ }
+ catch (Throwable e) {
+ error("Unexpected error: " + e, e);
+
+ stop.set(true);
+
+ fail("Unexpected error: " + e);
+ }
+
+ return null;
+ }
+ }, readers, "reader");
+
+ while (System.currentTimeMillis() < stopTime && !stop.get()) {
+ Thread.sleep(1000);
+
+ if (restartMode != null) {
+ switch (restartMode) {
+ case RESTART_CRD: {
+ log.info("Start new coordinator: " + (crdIdx + 1));
+
+ startGrid(crdIdx + 1);
+
+ log.info("Stop current coordinator: " + crdIdx);
+
+ stopGrid(crdIdx);
+
+ crdIdx++;
+
+ awaitPartitionMapExchange();
+
+ break;
+ }
+
+ case RESTART_RND_SRV: {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int idx = rnd.nextInt(srvs);
+
+ TestCache cache0 = caches.get(idx);
+
+ cache0.stopLock.writeLock().lock();
+
+ log.info("Stop node: " + idx);
+
+ stopGrid(idx);
+
+ log.info("Start new node: " + idx);
+
+ Ignite srv = startGrid(idx);
+
+ synchronized (caches) {
+ caches.set(idx, new TestCache(srv.cache(DEFAULT_CACHE_NAME)));
+ }
+
+ awaitPartitionMapExchange();
+
+ break;
+ }
+
+ default:
+ fail();
+ }
+ }
+ }
+
+ stop.set(true);
+
+ Exception ex = null;
+
+ try {
+ writeFut.get();
+ }
+ catch (IgniteCheckedException e) {
+ ex = e;
+ }
+
+ try {
+ readFut.get();
+ }
+ catch (IgniteCheckedException e) {
+ if (ex != null)
+ ex.addSuppressed(e);
+ else
+ ex = e;
+ }
+
+ if (ex != null)
+ throw ex;
+ }
+ finally {
+ stop.set(true);
+ }
+ }
+
+ /**
+ * @param cacheMode Cache mode.
+ * @param syncMode Write synchronization mode.
+ * @param backups Number of backups.
+ * @param parts Number of partitions.
+ * @return Cache configuration.
+ */
+ final CacheConfiguration<Object, Object> cacheConfiguration(
+ CacheMode cacheMode,
+ CacheWriteSynchronizationMode syncMode,
+ int backups,
+ int parts) {
+ CacheConfiguration<Object, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
+
+ ccfg.setCacheMode(cacheMode);
+ ccfg.setAtomicityMode(TRANSACTIONAL);
+ ccfg.setWriteSynchronizationMode(syncMode);
+ ccfg.setAffinity(new RendezvousAffinityFunction(false, parts));
+
+ if (cacheMode == PARTITIONED)
+ ccfg.setBackups(backups);
+
+ return ccfg;
+ }
+
+ /**
+ * Handles transaction exception.
+ * @param e Exception.
+ */
+ protected void handleTxException(Exception e) {
+ if (log.isTraceEnabled())
+ log.trace("Exception during tx execution: " + X.getFullStackTrace(e));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ final void verifyCoordinatorInternalState() throws Exception {
+ for (Ignite node : G.allGrids()) {
+ final MvccProcessorImpl crd = mvccProcessor(node);
+
+ if (crd == null)
+ continue;
+
+ crd.stopVacuumWorkers(); // to prevent new futures creation.
+
+ Map activeTxs = GridTestUtils.getFieldValue(crd, "activeTxs");
+ Map<?, Map> cntrFuts = GridTestUtils.getFieldValue(crd, "snapLsnrs");
+ Map ackFuts = GridTestUtils.getFieldValue(crd, "ackFuts");
+ Map activeTrackers = GridTestUtils.getFieldValue(crd, "activeTrackers");
+
+ GridAbsPredicate cond = () -> {
+ log.info("activeTxs=" + activeTxs + ", cntrFuts=" + cntrFuts + ", ackFuts=" + ackFuts +
+ ", activeTrackers=" + activeTrackers);
+
+ boolean empty = true;
+
+ for (Map map : cntrFuts.values())
+ if (!(empty = map.isEmpty()))
+ break;
+
+ return activeTxs.isEmpty() && empty && ackFuts.isEmpty() && activeTrackers.isEmpty();
+ };
+
+ GridTestUtils.waitForCondition(cond, TX_TIMEOUT);
+
+ assertTrue("activeTxs: " + activeTxs, activeTxs.isEmpty());
+
+ boolean empty = true;
+
+ for (Map map : cntrFuts.values())
+ if (!(empty = map.isEmpty())) break;
+
+ assertTrue("cntrFuts: " + cntrFuts, empty);
+ assertTrue("ackFuts: " + ackFuts, ackFuts.isEmpty());
+ assertTrue("activeTrackers: " + activeTrackers, activeTrackers.isEmpty());
+
+ checkActiveQueriesCleanup(node);
+ }
+ }
+
+ /**
+ * Checks if less than 2 versions remain after the vacuum cleanup.
+ *
+ * @throws Exception If failed.
+ */
+ private void verifyOldVersionsCleaned() throws Exception {
+ runVacuumSync();
+
+ // Check versions.
+ boolean cleaned = checkOldVersions(false);
+
+ if (!cleaned) { // Retry on a stable topology with a newer snapshot.
+ awaitPartitionMapExchange();
+
+ runVacuumSync();
+
+ checkOldVersions(true);
+ }
+ }
+
+ /**
+ * Checks if outdated versions were cleaned after the vacuum process.
+ *
+ * @param failIfNotCleaned Fail test if not cleaned.
+ * @return {@code False} if not cleaned.
+ * @throws IgniteCheckedException If failed.
+ */
+ private boolean checkOldVersions(boolean failIfNotCleaned) throws IgniteCheckedException {
+ for (Ignite node : G.allGrids()) {
+ for (IgniteCacheProxy cache : ((IgniteKernal)node).caches()) {
+ GridCacheContext cctx = cache.context();
+
+ if (!cctx.userCache() || !cctx.group().mvccEnabled())
+ continue;
+
+ for (Object e : cache.withKeepBinary()) {
+ IgniteBiTuple entry = (IgniteBiTuple)e;
+
+ KeyCacheObject key = cctx.toCacheKeyObject(entry.getKey());
+
+ List<IgniteBiTuple<Object, MvccVersion>> vers = cctx.offheap().mvccAllVersions(cctx, key)
+ .stream().filter(t -> t.get1() != null).collect(Collectors.toList());
+
+ if (vers.size() > 1) {
+ if (failIfNotCleaned)
+ fail("[key=" + key.value(null, false) + "; vers=" + vers + ']');
+ else
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Runs vacuum on all nodes and waits for its completion.
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ private void runVacuumSync() throws IgniteCheckedException {
+ GridCompoundIdentityFuture<VacuumMetrics> fut = new GridCompoundIdentityFuture<>();
+
+ // Run vacuum manually.
+ for (Ignite node : G.allGrids()) {
+ if (!node.configuration().isClientMode()) {
+ MvccProcessorImpl crd = mvccProcessor(node);
+
+ if (crd == null)
+ continue;
+
+ Throwable vacuumError = crd.vacuumError();
+
+ assertNull(X.getFullStackTrace(vacuumError), vacuumError);
+
+ fut.add(crd.runVacuum());
+ }
+ }
+
+ fut.markInitialized();
+
+ // Wait vacuum finished.
+ fut.get();
+ }
+
+ /**
+ * @param node Ignite node.
+ * @return Mvcc processor.
+ */
+ protected MvccProcessorImpl mvccProcessor(Ignite node) {
+ GridKernalContext ctx = ((IgniteEx)node).context();
+
+ MvccProcessor crd = ctx.coordinators();
+
+ assertNotNull(crd);
+
+ if (crd instanceof NoOpMvccProcessor) {
+ assertFalse(MvccUtils.mvccEnabled(ctx));
+
+ return null;
+ }
+
+ return (MvccProcessorImpl)crd;
+ }
+
+ /**
+ * @param node Node.
+ * @throws Exception If failed.
+ */
+ protected final void checkActiveQueriesCleanup(Ignite node) throws Exception {
+ final MvccProcessorImpl crd = mvccProcessor(node);
+
+ assertTrue("Active queries not cleared: " + node.name(), GridTestUtils.waitForCondition(
+ new GridAbsPredicate() {
+ @Override public boolean apply() {
+ Object activeQueries = GridTestUtils.getFieldValue(crd, "activeQueries");
+
+ synchronized (activeQueries) {
+ Long minQry = GridTestUtils.getFieldValue(activeQueries, "minQry");
+
+ if (minQry != null)
+ log.info("Min query: " + minQry);
+
+ Map<Object, Map> queriesMap = GridTestUtils.getFieldValue(activeQueries, "activeQueries");
+
+ boolean empty = true;
+
+ for (Map.Entry<Object, Map> e : queriesMap.entrySet()) {
+ if (!e.getValue().isEmpty()) {
+ empty = false;
+
+ log.info("Active queries: " + e);
+ }
+ }
+
+ return empty && minQry == null;
+ }
+ }
+ }, 8_000)
+ );
+
+ assertTrue("Previous coordinator queries not empty: " + node.name(), GridTestUtils.waitForCondition(
+ new GridAbsPredicate() {
+ @Override public boolean apply() {
+ Map queries = GridTestUtils.getFieldValue(crd, "prevCrdQueries", "activeQueries");
+ Boolean prevDone = GridTestUtils.getFieldValue(crd, "prevCrdQueries", "prevQueriesDone");
+
+ if (!queries.isEmpty() || !prevDone)
+ log.info("Previous coordinator state [prevDone=" + prevDone + ", queries=" + queries + ']');
+
+ return queries.isEmpty();
+ }
+ }, 8_000)
+ );
+ }
+
+ /**
+ * @return Cache configurations.
+ */
+ protected List<CacheConfiguration<Object, Object>> cacheConfigurations() {
+ List<CacheConfiguration<Object, Object>> ccfgs = new ArrayList<>();
+
+ ccfgs.add(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, RendezvousAffinityFunction.DFLT_PARTITION_COUNT));
+ ccfgs.add(cacheConfiguration(PARTITIONED, FULL_SYNC, 1, RendezvousAffinityFunction.DFLT_PARTITION_COUNT));
+ ccfgs.add(cacheConfiguration(PARTITIONED, FULL_SYNC, 2, RendezvousAffinityFunction.DFLT_PARTITION_COUNT));
+ ccfgs.add(cacheConfiguration(REPLICATED, FULL_SYNC, 0, RendezvousAffinityFunction.DFLT_PARTITION_COUNT));
+
+ return ccfgs;
+ }
+
+ /**
+ * Reads value from cache for the given key using given read mode.
+ *
+ * @param cache Cache.
+ * @param key Key.
+ * @param readMode Read mode.
+ * @param codec Sql object codec.
+ * @return Value.
+ */
+ @SuppressWarnings("unchecked")
+ protected Object readByMode(IgniteCache cache, final Object key, ReadMode readMode, ObjectCodec codec) {
+ assert cache != null && key != null && readMode != null && readMode != SQL_SUM;
+ assert readMode != SQL || codec != null;
+
+ boolean emulateLongQry = ThreadLocalRandom.current().nextBoolean();
+
+ switch (readMode) {
+ case GET:
+ return cache.get(key);
+
+ case SCAN:
+ ScanQuery scanQry = new ScanQuery(new IgniteBiPredicate() {
+ @Override public boolean apply(Object k, Object v) {
+ if (emulateLongQry)
+ doSleep(ThreadLocalRandom.current().nextInt(50));
+
+ return k.equals(key);
+ }
+ });
+
+ List res = cache.query(scanQry).getAll();
+
+ assertTrue(res.size() <= 1);
+
+ return res.isEmpty() ? null : ((IgniteBiTuple)res.get(0)).getValue();
+
+ case SQL:
+ String qry = "SELECT * FROM " + codec.tableName() + " WHERE _key=" + key;
+
+ SqlFieldsQuery sqlFieldsQry = new SqlFieldsQuery(qry);
+
+ if (emulateLongQry)
+ sqlFieldsQry.setLazy(true).setPageSize(1);
+
+ List<List> rows;
+
+ if (emulateLongQry) {
+ FieldsQueryCursor<List> cur = cache.query(sqlFieldsQry);
+
+ rows = new ArrayList<>();
+
+ for (List row : cur) {
+ rows.add(row);
+
+ doSleep(ThreadLocalRandom.current().nextInt(50));
+ }
+ }
+ else
+ rows = cache.query(sqlFieldsQry).getAll();
+
+ assertTrue(rows.size() <= 1);
+
+ return rows.isEmpty() ? null : codec.decode(rows.get(0));
+
+ default:
+ throw new AssertionError("Unsupported read mode: " + readMode);
+ }
+ }
+
+ /**
+ * Writes value into cache using given write mode.
+ *
+ * @param cache Cache.
+ * @param key Key.
+ * @param val Value.
+ * @param writeMode Write mode.
+ * @param codec Sql object codec.
+ */
+ @SuppressWarnings("unchecked")
+ protected void writeByMode(IgniteCache cache, final Object key, Object val, WriteMode writeMode, ObjectCodec codec) {
+ assert writeMode != DML || codec != null;
+ assert cache != null && key != null && writeMode != null && val != null;
+
+ switch (writeMode) {
+ case PUT:
+ cache.put(key, val);
+
+ return;
+
+ case DML:
+ String qry = "MERGE INTO " + codec.tableName() + " (" + codec.columnsNames() + ") VALUES " +
+ '(' + key + ", " + codec.encode(val) + ')';
+
+ List<List> rows = cache.query(new SqlFieldsQuery(qry)).getAll();
+
+ assertTrue(rows.size() <= 1);
+
+ return;
+
+ default:
+ throw new AssertionError("Unsupported write mode: " + writeMode);
+ }
+ }
+
+
+ /**
+ * Reads value from cache for the given key using given read mode.
+ *
+ * @param cache Cache.
+ * @param keys Key.
+ * @param readMode Read mode.
+ * @param codec Value codec.
+ * @return Value.
+ */
+ @SuppressWarnings("unchecked")
+ protected Map readAllByMode(IgniteCache cache, Set keys, ReadMode readMode, ObjectCodec codec) {
+ assert cache != null && keys != null && readMode != null;
+ assert readMode != SQL || codec != null;
+
+ boolean emulateLongQry = ThreadLocalRandom.current().nextBoolean();
+
+ switch (readMode) {
+ case GET:
+ return cache.getAll(keys);
+
+ case SCAN:
+ ScanQuery scanQry = new ScanQuery(new IgniteBiPredicate() {
+ @Override public boolean apply(Object k, Object v) {
+ if (emulateLongQry)
+ doSleep(ThreadLocalRandom.current().nextInt(50));
+
+ return keys.contains(k);
+ }
+ });
+
+
+ Map res = (Map)cache.query(scanQry).getAll()
+ .stream()
+ .collect(Collectors.toMap(v -> ((IgniteBiTuple)v).getKey(), v -> ((IgniteBiTuple)v).getValue()));
+
+ assertTrue("res.size()=" + res.size() + ", keys.size()=" + keys.size(), res.size() <= keys.size());
+
+ return res;
+
+ case SQL:
+ StringBuilder b = new StringBuilder("SELECT " + codec.columnsNames() + " FROM " + codec.tableName() + " WHERE _key IN (");
+
+ boolean first = true;
+
+ for (Object key : keys) {
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+
+ b.append(key);
+ }
+
+ b.append(')');
+
+ String qry = b.toString();
+
+ SqlFieldsQuery sqlFieldsQry = new SqlFieldsQuery(qry);
+
+ if (emulateLongQry)
+ sqlFieldsQry.setLazy(true).setPageSize(1);
+
+ List<List> rows;
+
+ if (emulateLongQry) {
+ FieldsQueryCursor<List> cur = cache.query(sqlFieldsQry);
+
+ rows = new ArrayList<>();
+
+ for (List row : cur) {
+ rows.add(row);
+
+ doSleep(ThreadLocalRandom.current().nextInt(50));
+ }
+ }
+ else
+ rows = cache.query(sqlFieldsQry).getAll();
+
+ if (rows.isEmpty())
+ return Collections.EMPTY_MAP;
+
+ res = new HashMap();
+
+ for (List row : rows)
+ res.put(row.get(0), codec.decode(row));
+
+ return res;
+
+ case SQL_SUM:
+ b = new StringBuilder("SELECT SUM(" + codec.aggregateColumnName() + ") FROM " + codec.tableName() + " WHERE _key IN (");
+
+ first = true;
+
+ for (Object key : keys) {
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+
+ b.append(key);
+ }
+
+ b.append(')');
+
+ qry = b.toString();
+
+ FieldsQueryCursor<List> cur = cache.query(new SqlFieldsQuery(qry));
+
+ rows = cur.getAll();
+
+ if (rows.isEmpty())
+ return Collections.EMPTY_MAP;
+
+ res = new HashMap();
+
+ for (List row : rows)
+ res.put(row.get(0), row.get(0));
+
+ return res;
+
+ default:
+ throw new AssertionError("Unsupported read mode: " + readMode);
+ }
+ }
+
+ /**
+ * Writes all entries using given write mode.
+ *
+ * @param cache Cache.
+ * @param entries Entries to write.
+ * @param writeMode Write mode.
+ * @param codec Entry codec.
+ */
+ @SuppressWarnings("unchecked")
+ protected void writeAllByMode(IgniteCache cache, final Map entries, WriteMode writeMode, ObjectCodec codec) {
+ assert cache != null && entries != null && writeMode != null;
+ assert writeMode != DML || codec != null;
+
+ switch (writeMode) {
+ case PUT:
+ cache.putAll(entries);
+
+ return;
+
+ case DML:
+ StringBuilder b = new StringBuilder("MERGE INTO " + codec.tableName() + " (" + codec.columnsNames() + ") VALUES ");
+
+ boolean first = true;
+
+ for (Object entry : entries.entrySet()) {
+ Map.Entry e = (Map.Entry)entry;
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+
+ b.append('(')
+ .append(e.getKey())
+ .append(", ")
+ .append(codec.encode(e.getValue()))
+ .append(')');
+ }
+
+ String qry = b.toString();
+
+ cache.query(new SqlFieldsQuery(qry)).getAll();
+
+ return;
+
+ default:
+ throw new AssertionError("Unsupported write mode: " + writeMode);
+ }
+ }
+
+ /**
+ * Object codec for SQL queries.
+ *
+ * @param <T> Type.
+ */
+ private interface ObjectCodec<T> {
+ /**
+ * Decodes object from SQL request result.
+ *
+ * @param row SQL request result.
+ * @return Decoded object.
+ */
+ T decode(List<?> row);
+
+ /**
+ * Encodes object into SQL string for INSERT clause.
+ *
+ * @param obj Object.
+ * @return Sql string.
+ */
+ String encode(T obj);
+
+ /**
+ * @return Table name.
+ */
+ String tableName();
+
+ /**
+ * @return Columns names.
+ */
+ String columnsNames();
+
+ /**
+ * @return Column for aggregate functions.
+ */
+ String aggregateColumnName();
+ }
+
+ /**
+ * Codec for {@code Integer} table.
+ */
+ private static class IntegerCodec implements ObjectCodec<Integer> {
+ /** {@inheritDoc} */
+ @Override public Integer decode(List<?> row) {
+ return (Integer)row.get(1);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String encode(Integer obj) {
+ return String.valueOf(obj);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String tableName() {
+ return "Integer";
+ }
+
+ /** {@inheritDoc} */
+ @Override public String columnsNames() {
+ return "_key, _val";
+ }
+
+ /** {@inheritDoc} */
+ @Override public String aggregateColumnName() {
+ return "_val";
+ }
+ }
+
+ /**
+ * Codec for {@code MvccTestAccount} table.
+ */
+ private static class AccountCodec implements ObjectCodec<MvccTestAccount> {
+ /** {@inheritDoc} */
+ @Override public MvccTestAccount decode(List<?> row) {
+ Integer val = (Integer)row.get(1);
+ Integer updateCnt = (Integer)row.get(2);
+
+ return new MvccTestAccount(val, updateCnt);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String encode(MvccTestAccount obj) {
+ return String.valueOf(obj.val) + ", " + String.valueOf(obj.updateCnt);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String tableName() {
+ return "MvccTestAccount";
+ }
+
+ /** {@inheritDoc} */
+ @Override public String columnsNames() {
+ return "_key, val, updateCnt";
+ }
+
+ /** {@inheritDoc} */
+ @Override public String aggregateColumnName() {
+ return "val";
+ }
+ }
+
+
+ /**
+ * @param caches Caches.
+ * @param rnd Random.
+ * @return Random cache.
+ */
+ static <K, V> TestCache<K, V> randomCache(
+ List<TestCache> caches,
+ ThreadLocalRandom rnd) {
+ synchronized (caches) {
+ if (caches.size() == 1) {
+ TestCache cache = caches.get(0);
+
+ assertTrue(cache.readLock());
+
+ return cache;
+ }
+
+ for (;;) {
+ int idx = rnd.nextInt(caches.size());
+
+ TestCache testCache = caches.get(idx);
+
+ if (testCache.readLock())
+ return testCache;
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ static class MvccTestAccount {
+ /** */
+ @QuerySqlField(index = false)
+ final int val;
+
+ /** */
+ @QuerySqlField
+ final int updateCnt;
+
+ /**
+ * @param val Value.
+ * @param updateCnt Updates counter.
+ */
+ MvccTestAccount(int val, int updateCnt) {
+ assert updateCnt > 0;
+
+ this.val = val;
+ this.updateCnt = updateCnt;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "MvccTestAccount{" +
+ "val=" + val +
+ ", updateCnt=" + updateCnt +
+ '}';
+ }
+ }
+
+ /**
+ *
+ */
+ enum ReadMode {
+ /** */
+ GET,
+
+ /** */
+ SCAN,
+
+ /** */
+ SQL,
+
+ /** */
+ SQL_SUM
+ }
+
+ /**
+ *
+ */
+ enum WriteMode {
+ /** */
+ DML,
+
+ /** */
+ PUT
+ }
+
+ /**
+ *
+ */
+ enum RestartMode {
+ /**
+ * Dedicated coordinator node is restarted during test.
+ */
+ RESTART_CRD,
+
+ /** */
+ RESTART_RND_SRV
+ }
+
+ /**
+ *
+ */
+ static class CoordinatorNodeFilter implements IgnitePredicate<ClusterNode> {
+ /** {@inheritDoc} */
+ @Override public boolean apply(ClusterNode node) {
+ return node.attribute(CRD_ATTR) == null;
+ }
+ }
+
+ /**
+ *
+ */
+ static class CoordinatorAssignClosure implements IgniteClosure<Collection<ClusterNode>, ClusterNode> {
+ /** {@inheritDoc} */
+ @Override public ClusterNode apply(Collection<ClusterNode> clusterNodes) {
+ for (ClusterNode node : clusterNodes) {
+ if (node.attribute(CRD_ATTR) != null) {
+ assert !node.isClient();
+
+ return node;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ *
+ */
+ static class TestCache<K, V> {
+ /** */
+ final IgniteCache<K, V> cache;
+
+ /** Locks node to avoid node restart while test operation is in progress. */
+ final ReadWriteLock stopLock = new ReentrantReadWriteLock();
+
+ /**
+ * @param cache Cache.
+ */
+ TestCache(IgniteCache cache) {
+ this.cache = cache;
+ }
+
+ /**
+ * @return {@code True} if locked.
+ */
+ boolean readLock() {
+ return stopLock.readLock().tryLock();
+ }
+
+ /**
+ *
+ */
+ void readUnlock() {
+ stopLock.readLock().unlock();
+ }
+ }
+
+ /**
+ *
+ */
+ static class InitIndexing implements IgniteInClosure<CacheConfiguration> {
+ /** */
+ private final Class[] idxTypes;
+
+ /**
+ * @param idxTypes Indexed types.
+ */
+ InitIndexing(Class<?>... idxTypes) {
+ this.idxTypes = idxTypes;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void apply(CacheConfiguration cfg) {
+ cfg.setIndexedTypes(idxTypes);
+ }
+ }
+
+ /**
+ * Removed accounts tracker.
+ */
+ private static class RemovedAccountsTracker {
+ /** */
+ private final Map<Integer, Integer> rmvdKeys;
+
+ /**
+ * @param size Size.
+ */
+ RemovedAccountsTracker(int size) {
+ this.rmvdKeys = new HashMap<>(size);
+
+ for (int i = 0; i < size; i++)
+ rmvdKeys.put(i, 0);
+ }
+
+ /**
+ * @return Size.
+ */
+ public synchronized int size() {
+ int size = 0;
+
+ for (int i = 0; i < rmvdKeys.size(); i++) {
+ if (rmvdKeys.get(i) > 0)
+ size++;
+ }
+
+ return size;
+ }
+
+ /**
+ * @param id Id.
+ * @return {@code True} if success.
+ */
+ synchronized boolean markRemoved(Integer id) {
+ Integer rmvdCntr = rmvdKeys.get(id);
+
+ Integer newCntr = rmvdCntr + 1;
+
+ rmvdKeys.put(id, newCntr);
+
+ return newCntr >= 0;
+ }
+
+ /**
+ * @param id Id.
+ * @return {@code True} if success.
+ */
+ synchronized boolean unmarkRemoved(Integer id) {
+ Integer rmvdCntr = rmvdKeys.get(id);
+
+ Integer newCntr = rmvdCntr - 1;
+
+ rmvdKeys.put(id, newCntr);
+
+ return newCntr >= 0;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccClusterRestartTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccClusterRestartTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccClusterRestartTest.java
new file mode 100644
index 0000000..f9ab7bc
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccClusterRestartTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.mvcc;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.WALMode;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ *
+ */
+public class CacheMvccClusterRestartTest extends GridCommonAbstractTest {
+ /** */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+ cfg.setConsistentId(gridName);
+
+ cfg.setMvccEnabled(true);
+
+ ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+ DataStorageConfiguration storageCfg = new DataStorageConfiguration();
+
+ storageCfg.setWalMode(WALMode.LOG_ONLY);
+ storageCfg.setPageSize(1024);
+
+ DataRegionConfiguration regionCfg = new DataRegionConfiguration();
+
+ regionCfg.setPersistenceEnabled(true);
+ regionCfg.setMaxSize(100 * 1024 * 1024);
+
+ storageCfg.setDefaultDataRegionConfiguration(regionCfg);
+
+ cfg.setDataStorageConfiguration(storageCfg);
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ cleanPersistenceDir();
+
+ super.afterTestsStopped();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ cleanPersistenceDir();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ cleanPersistenceDir();
+
+ stopAllGrids();
+
+ super.afterTest();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRestart1() throws Exception {
+ restart1(3, 3);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRestart2() throws Exception {
+ restart1(1, 3);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRestart3() throws Exception {
+ restart1(3, 1);
+ }
+
+ /**
+ * @param srvBefore Number of servers before restart.
+ * @param srvAfter Number of servers after restart.
+ * @throws Exception If failed.
+ */
+ private void restart1(int srvBefore, int srvAfter) throws Exception {
+ Ignite srv0 = startGridsMultiThreaded(srvBefore);
+
+ srv0.active(true);
+
+ IgniteCache<Object, Object> cache = srv0.createCache(cacheConfiguration());
+
+ Set<Integer> keys = new HashSet<>(primaryKeys(cache, 1, 0));
+
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (Integer k : keys)
+ cache.put(k, k);
+
+ tx.commit();
+ }
+
+ stopAllGrids();
+
+ srv0 = startGridsMultiThreaded(srvAfter);
+
+ srv0.active(true);
+
+ cache = srv0.cache(DEFAULT_CACHE_NAME);
+
+ Map<Object, Object> res = cache.getAll(keys);
+
+ assertEquals(keys.size(), res.size());
+
+ for (Integer k : keys)
+ assertEquals(k, cache.get(k));
+
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (Integer k : keys)
+ cache.put(k, k + 1);
+
+ tx.commit();
+ }
+
+ for (Integer k : keys)
+ assertEquals(k + 1, cache.get(k));
+ }
+
+ /**
+ * @return Cache configuration.
+ */
+ private CacheConfiguration<Object, Object> cacheConfiguration() {
+ CacheConfiguration<Object, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
+
+ ccfg.setWriteSynchronizationMode(FULL_SYNC);
+ ccfg.setAtomicityMode(TRANSACTIONAL);
+ ccfg.setBackups(2);
+
+ return ccfg;
+ }
+}
[26/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogInnerIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogInnerIO.java
new file mode 100644
index 0000000..95c10ce
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogInnerIO.java
@@ -0,0 +1,105 @@
+/*
+ * 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.mvcc.txlog;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInnerIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+
+/** */
+public class TxLogInnerIO extends BPlusInnerIO<TxKey> implements TxLogIO {
+ /** */
+ public static final IOVersions<TxLogInnerIO> VERSIONS = new IOVersions<>(new TxLogInnerIO(1));
+
+ /**
+ * @param ver Page format version.
+ */
+ protected TxLogInnerIO(int ver) {
+ super(T_TX_LOG_INNER, ver, true, 8 + 8 + 1);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void storeByOffset(long pageAddr, int off, TxKey row) {
+ TxRow row0 = (TxRow)row;
+
+ setMajor(pageAddr, off, row0.major());
+ setMinor(pageAddr, off, row0.minor());
+ setState(pageAddr, off, row0.state());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<TxKey> srcIo, long srcPageAddr, int srcIdx) {
+ TxLogIO srcIo0 = (TxLogIO)srcIo;
+
+ int srcOff = srcIo.offset(srcIdx);
+ int dstOff = offset(dstIdx);
+
+ setMajor(dstPageAddr, dstOff, srcIo0.getMajor(srcPageAddr, srcOff));
+ setMinor(dstPageAddr, dstOff, srcIo0.getMinor(srcPageAddr, srcOff));
+ setState(dstPageAddr, dstOff, srcIo0.getState(srcPageAddr, srcOff));
+ }
+
+ /** {@inheritDoc} */
+ @Override public TxKey getLookupRow(BPlusTree<TxKey, ?> tree, long pageAddr, int idx) {
+ int off = offset(idx);
+
+ return new TxRow(
+ getMajor(pageAddr, off),
+ getMinor(pageAddr, off),
+ getState(pageAddr, off));
+ }
+
+ /** {@inheritDoc} */
+ @Override public int compare(long pageAddr, int off, TxKey row) {
+ int cmp = Long.compare(PageUtils.getLong(pageAddr, off), row.major());
+
+ return cmp != 0 ? cmp : Long.compare(getMinor(pageAddr, off), row.minor());
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMajor(long pageAddr, int off) {
+ return PageUtils.getLong(pageAddr, off);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMajor(long pageAddr, int off, long major) {
+ PageUtils.putLong(pageAddr, off, major);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMinor(long pageAddr, int off) {
+ return PageUtils.getLong(pageAddr, off + 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMinor(long pageAddr, int off, long minor) {
+ PageUtils.putLong(pageAddr, off + 8, minor);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte getState(long pageAddr, int off) {
+ return PageUtils.getByte(pageAddr, off + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setState(long pageAddr, int off, byte state) {
+ PageUtils.putByte(pageAddr, off + 16, state);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogLeafIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogLeafIO.java
new file mode 100644
index 0000000..e037fbe
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogLeafIO.java
@@ -0,0 +1,105 @@
+/*
+ * 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.mvcc.txlog;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+
+/** */
+public class TxLogLeafIO extends BPlusLeafIO<TxKey> implements TxLogIO {
+ /** */
+ public static final IOVersions<TxLogLeafIO> VERSIONS = new IOVersions<>(new TxLogLeafIO(1));
+
+ /**
+ * @param ver Page format version.
+ */
+ protected TxLogLeafIO(int ver) {
+ super(T_TX_LOG_LEAF, ver, 17);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void storeByOffset(long pageAddr, int off, TxKey row) {
+ TxRow row0 = (TxRow)row;
+
+ setMajor(pageAddr, off, row0.major());
+ setMinor(pageAddr, off, row0.minor());
+ setState(pageAddr, off, row0.state());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<TxKey> srcIo, long srcPageAddr, int srcIdx) {
+ TxLogIO srcIo0 = (TxLogIO)srcIo;
+
+ int srcOff = srcIo.offset(srcIdx);
+ int dstOff = offset(dstIdx);
+
+ setMajor(dstPageAddr, dstOff, srcIo0.getMajor(srcPageAddr, srcOff));
+ setMinor(dstPageAddr, dstOff, srcIo0.getMinor(srcPageAddr, srcOff));
+ setState(dstPageAddr, dstOff, srcIo0.getState(srcPageAddr, srcOff));
+ }
+
+ /** {@inheritDoc} */
+ @Override public TxKey getLookupRow(BPlusTree<TxKey, ?> tree, long pageAddr, int idx) {
+ int off = offset(idx);
+
+ return new TxRow(
+ getMajor(pageAddr, off),
+ getMinor(pageAddr, off),
+ getState(pageAddr, off));
+ }
+
+ /** {@inheritDoc} */
+ @Override public int compare(long pageAddr, int off, TxKey row) {
+ int cmp = Long.compare(getMajor(pageAddr, off), row.major());
+
+ return cmp != 0 ? cmp : Long.compare(getMinor(pageAddr, off), row.minor());
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMajor(long pageAddr, int off) {
+ return PageUtils.getLong(pageAddr, off);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMajor(long pageAddr, int off, long major) {
+ PageUtils.putLong(pageAddr, off, major);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMinor(long pageAddr, int off) {
+ return PageUtils.getLong(pageAddr, off + 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMinor(long pageAddr, int off, long minor) {
+ PageUtils.putLong(pageAddr, off + 8, minor);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte getState(long pageAddr, int off) {
+ return PageUtils.getByte(pageAddr, off + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setState(long pageAddr, int off, byte state) {
+ PageUtils.putByte(pageAddr, off + 16, state);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogTree.java
new file mode 100644
index 0000000..60fbc84
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogTree.java
@@ -0,0 +1,62 @@
+/*
+ * 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.mvcc.txlog;
+
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
+import org.apache.ignite.internal.processors.failure.FailureProcessor;
+
+/**
+ *
+ */
+public class TxLogTree extends BPlusTree<TxKey, TxRow> {
+ /**
+ * @param pageMem Page memory.
+ * @param wal Write ahead log manager
+ * @param metaPageId Tree metapage id.
+ * @param reuseList Reuse list
+ * @param failureProcessor Failure processor.
+ * @param initNew {@code True} if new tree should be created.
+ * @throws IgniteCheckedException If fails.
+ */
+ public TxLogTree(PageMemory pageMem,
+ IgniteWriteAheadLogManager wal, long metaPageId,
+ ReuseList reuseList, FailureProcessor failureProcessor,
+ boolean initNew) throws IgniteCheckedException {
+ super(TxLog.TX_LOG_CACHE_NAME, TxLog.TX_LOG_CACHE_ID, pageMem, wal, new AtomicLong(), metaPageId,
+ reuseList, TxLogInnerIO.VERSIONS, TxLogLeafIO.VERSIONS, failureProcessor);
+
+ initTree(initNew);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected int compare(BPlusIO<TxKey> io, long pageAddr, int idx, TxKey row) {
+ return ((TxLogIO)io).compare(pageAddr, io.offset(idx), row);
+ }
+
+ /** {@inheritDoc} */
+ @Override public TxRow getRow(BPlusIO<TxKey> io, long pageAddr,
+ int idx, Object ignored) throws IgniteCheckedException {
+ return (TxRow) io.getLookupRow(this, pageAddr, idx);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxRow.java
new file mode 100644
index 0000000..0d161c8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxRow.java
@@ -0,0 +1,44 @@
+/*
+ * 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.mvcc.txlog;
+
+/**
+ *
+ */
+public class TxRow extends TxKey {
+ /** */
+ private byte state;
+
+ /**
+ * @param major Major version.
+ * @param minor Minor version.
+ * @param state Transaction state.
+ */
+ TxRow(long major, long minor, byte state) {
+ super(major, minor);
+
+ this.state = state;
+ }
+
+ /**
+ * @return Transaction state.
+ */
+ public byte state() {
+ return state;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxState.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxState.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxState.java
new file mode 100644
index 0000000..65a1f25
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxState.java
@@ -0,0 +1,37 @@
+/*
+ * 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.mvcc.txlog;
+
+/**
+ *
+ */
+public final class TxState {
+ /** */
+ public static final byte NA = 0x0;
+ /** */
+ public static final byte PREPARED = 0x1;
+ /** */
+ public static final byte ABORTED = 0x2;
+ /** */
+ public static final byte COMMITTED = 0x3;
+
+ /**
+ * Private constructor.
+ */
+ private TxState() {}
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
index 44f0a3f..64517ac 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
@@ -19,12 +19,13 @@ package org.apache.ignite.internal.processors.cache.persistence;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
/**
* Cache data row.
*/
-public interface CacheDataRow extends CacheSearchRow, Storable {
+public interface CacheDataRow extends MvccUpdateVersionAware, CacheSearchRow, Storable {
/**
* @return Cache value.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
index 9f2e031..b8245df 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.IncompleteCacheObject;
import org.apache.ignite.internal.processors.cache.IncompleteObject;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.CacheVersionIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPagePayload;
@@ -42,6 +43,10 @@ import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.pagemem.PageIdUtils.itemId;
import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.RowData.LINK_WITH_HEADER;
/**
* Cache data row adapter.
@@ -60,6 +65,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
protected CacheObject val;
/** */
+ @GridToStringInclude
protected long expireTime = -1;
/** */
@@ -153,6 +159,8 @@ public class CacheDataRowAdapter implements CacheDataRow {
nextLink = data.nextLink();
+ int hdrLen = 0;
+
if (first) {
if (nextLink == 0) {
// Fast path for a single page row.
@@ -162,12 +170,21 @@ public class CacheDataRowAdapter implements CacheDataRow {
}
first = false;
+
+ // Assume that row header is always located entirely on the very first page.
+ hdrLen = readHeader(pageAddr, data.offset());
+
+ if (rowData == LINK_WITH_HEADER)
+ return;
}
ByteBuffer buf = pageMem.pageBuffer(pageAddr);
- buf.position(data.offset());
- buf.limit(data.offset() + data.payloadSize());
+ int off = data.offset() + hdrLen;
+ int payloadSize = data.payloadSize() - hdrLen;
+
+ buf.position(off);
+ buf.limit(off + payloadSize);
boolean keyOnly = rowData == RowData.KEY_ONLY;
@@ -190,6 +207,18 @@ public class CacheDataRowAdapter implements CacheDataRow {
}
/**
+ * Reads row header (i.e. MVCC info) which should be located on the very first page od data.
+ *
+ * @param addr Address.
+ * @param off Offset
+ * @return Number of bytes read.
+ */
+ protected int readHeader(long addr, int off) {
+ // No-op.
+ return 0;
+ }
+
+ /**
* @param sharedCtx Cache shared context.
* @param coctx Cache object context.
* @param buf Buffer.
@@ -199,7 +228,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @throws IgniteCheckedException If failed.
* @return Read object.
*/
- private IncompleteObject<?> readFragment(
+ protected IncompleteObject<?> readFragment(
GridCacheSharedContext<?, ?> sharedCtx,
CacheObjectContext coctx,
ByteBuffer buf,
@@ -268,7 +297,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @param readCacheId {@code true} If need to read cache ID.
* @throws IgniteCheckedException If failed.
*/
- private void readFullRow(
+ protected void readFullRow(
GridCacheSharedContext<?, ?> sharedCtx,
CacheObjectContext coctx,
long addr,
@@ -277,6 +306,11 @@ public class CacheDataRowAdapter implements CacheDataRow {
throws IgniteCheckedException {
int off = 0;
+ off += readHeader(addr, off);
+
+ if (rowData == LINK_WITH_HEADER)
+ return;
+
if (readCacheId) {
cacheId = PageUtils.getInt(addr, off);
@@ -326,7 +360,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @param buf Buffer.
* @param incomplete Incomplete.
*/
- private IncompleteObject<?> readIncompleteCacheId(
+ protected IncompleteObject<?> readIncompleteCacheId(
ByteBuffer buf,
IncompleteObject<?> incomplete
) {
@@ -371,7 +405,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @return Incomplete object.
* @throws IgniteCheckedException If failed.
*/
- private IncompleteCacheObject readIncompleteKey(
+ protected IncompleteCacheObject readIncompleteKey(
CacheObjectContext coctx,
ByteBuffer buf,
IncompleteCacheObject incomplete
@@ -396,7 +430,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @return Incomplete object.
* @throws IgniteCheckedException If failed.
*/
- private IncompleteCacheObject readIncompleteValue(
+ protected IncompleteCacheObject readIncompleteValue(
CacheObjectContext coctx,
ByteBuffer buf,
IncompleteCacheObject incomplete
@@ -419,7 +453,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @param incomplete Incomplete object.
* @return Incomplete object.
*/
- private IncompleteObject<?> readIncompleteExpireTime(
+ protected IncompleteObject<?> readIncompleteExpireTime(
ByteBuffer buf,
IncompleteObject<?> incomplete
) {
@@ -463,7 +497,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
* @return Incomplete object.
* @throws IgniteCheckedException If failed.
*/
- private IncompleteObject<?> readIncompleteVersion(
+ protected IncompleteObject<?> readIncompleteVersion(
ByteBuffer buf,
IncompleteObject<?> incomplete
) throws IgniteCheckedException {
@@ -573,6 +607,60 @@ public class CacheDataRowAdapter implements CacheDataRow {
throw new UnsupportedOperationException();
}
+ /** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ int len = key().valueBytesLength(null);
+
+ len += value().valueBytesLength(null) + CacheVersionIO.size(version(), false) + 8;
+
+ return len + (cacheId() != 0 ? 4 : 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return MVCC_CRD_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return MVCC_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return MVCC_OP_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte mvccTxState() {
+ return TxState.NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCoordinatorVersion() {
+ return MVCC_CRD_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCounter() {
+ return MVCC_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int newMvccOperationCounter() {
+ return MVCC_OP_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte newMvccTxState() {
+ return TxState.NA;
+ }
+
/**
*
*/
@@ -584,7 +672,13 @@ public class CacheDataRowAdapter implements CacheDataRow {
KEY_ONLY,
/** */
- NO_KEY
+ NO_KEY,
+
+ /** */
+ LINK_ONLY,
+
+ /** */
+ LINK_WITH_HEADER
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheSearchRow.java
index 1637eb0..c3cfb83 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheSearchRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheSearchRow.java
@@ -18,11 +18,12 @@
package org.apache.ignite.internal.processors.cache.persistence;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
/**
*
*/
-public interface CacheSearchRow {
+public interface CacheSearchRow extends MvccVersionAware {
/**
* @return Cache key.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DatabaseLifecycleListener.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DatabaseLifecycleListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DatabaseLifecycleListener.java
new file mode 100644
index 0000000..f96cdd9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DatabaseLifecycleListener.java
@@ -0,0 +1,54 @@
+/*
+ * 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.persistence;
+
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ *
+ */
+public interface DatabaseLifecycleListener {
+
+ /**
+ * @param mgr Database shared manager.
+ *
+ */
+ void onInitDataRegions(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException;
+
+ /**
+ * @param mgr Page store manager.
+ *
+ */
+ void beforeMemoryRestore(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException;
+
+ /**
+ * @param mgr Database shared manager.
+ *
+ */
+ void afterMemoryRestore(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException;
+
+ /**
+ * @param mgr Database shared manager.
+ */
+ void afterInitialise(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException;
+
+ /**
+ * @param mgr Database shared manager.
+ */
+ void beforeStop(IgniteCacheDatabaseSharedManager mgr);
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index bd8a62f..7d91645 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -111,6 +111,7 @@ import org.apache.ignite.internal.processors.cache.StoredCacheData;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog;
import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointEntry;
import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointEntryType;
import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointHistory;
@@ -409,8 +410,8 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
}
/** {@inheritDoc} */
- @Override protected void initDataRegions(DataStorageConfiguration memCfg) throws IgniteCheckedException {
- super.initDataRegions(memCfg);
+ @Override protected void initDataRegions0(DataStorageConfiguration memCfg) throws IgniteCheckedException {
+ super.initDataRegions0(memCfg);
addDataRegion(
memCfg,
@@ -812,6 +813,10 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
checkpointReadLock();
try {
+ for (DatabaseLifecycleListener lsnr : getDatabaseListeners(cctx.kernalContext())) {
+ lsnr.beforeMemoryRestore(this);
+ }
+
if (!F.isEmpty(cachesToStart)) {
for (DynamicCacheDescriptor desc : cachesToStart) {
if (CU.affinityNode(cctx.localNode(), desc.cacheConfiguration().getNodeFilter()))
@@ -851,6 +856,11 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
metaStorage.init(this);
notifyMetastorageReadyForReadWrite();
+
+ for (DatabaseLifecycleListener lsnr : getDatabaseListeners(cctx.kernalContext())) {
+ lsnr.afterMemoryRestore(this);
+ }
+
}
catch (IgniteCheckedException e) {
if (X.hasCause(e, StorageException.class, IOException.class))
@@ -1351,7 +1361,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
if (!cctx.pageStore().hasIndexStore(cacheCtx.groupId()) && cacheCtx.affinityNode()) {
IgniteInternalFuture<?> rebuildFut = cctx.kernalContext().query()
- .rebuildIndexesFromHash(Collections.singletonList(cacheCtx.cacheId()));
+ .rebuildIndexesFromHash(Collections.singleton(cacheCtx.cacheId()));
assert usrFut != null : "Missing user future for cache: " + cacheCtx.name();
@@ -2088,6 +2098,10 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
* @throws IgniteCheckedException if no DataRegion is configured for a name obtained from cache descriptor.
*/
private PageMemoryEx getPageMemoryForCacheGroup(int grpId) throws IgniteCheckedException {
+ // TODO IGNITE-7792 add generic mapping.
+ if (grpId == TxLog.TX_LOG_CACHE_ID)
+ return (PageMemoryEx)dataRegion(TxLog.TX_LOG_CACHE_NAME).pageMemory();
+
// TODO IGNITE-5075: cache descriptor can be removed.
GridCacheSharedContext sharedCtx = context();
@@ -2391,7 +2405,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
}
}
else
- updateState(part, (int)io.getPartitionState(pageAddr));
+ changed = updateState(part, (int)io.getPartitionState(pageAddr));
}
finally {
pageMem.writeUnlock(grpId, partMetaId, partMetaPage, null, changed);
@@ -3852,20 +3866,21 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
PageMemoryEx pageMem;
- if (grpId != MetaStorage.METASTORAGE_CACHE_ID) {
+ // TODO IGNITE-7792 add generic mapping.
+ if (grpId == MetaStorage.METASTORAGE_CACHE_ID)
+ pageMem = (PageMemoryEx)metaStorage.pageMemory();
+ else if (grpId == TxLog.TX_LOG_CACHE_ID)
+ pageMem = (PageMemoryEx)dataRegion(TxLog.TX_LOG_CACHE_NAME).pageMemory();
+ else {
CacheGroupContext grp = context().cache().cacheGroup(grpId);
- if (grp == null)
- continue;
+ DataRegion region = grp != null ?grp .dataRegion() : null;
- if (!grp.dataRegion().config().isPersistenceEnabled())
+ if (region == null || !region.config().isPersistenceEnabled())
continue;
- pageMem = (PageMemoryEx)grp.dataRegion().pageMemory();
+ pageMem = (PageMemoryEx)region.pageMemory();
}
- else
- pageMem = (PageMemoryEx)metaStorage.pageMemory();
-
Integer tag = pageMem.getForCheckpoint(
fullId, tmpWriteBuf, persStoreMetrics.metricsEnabled() ? tracker : null);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
index 826f619..4c45352 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
@@ -17,9 +17,11 @@
package org.apache.ignite.internal.processors.cache.persistence;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -57,6 +59,8 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalP
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteHistoricalIterator;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeListImpl;
import org.apache.ignite.internal.processors.cache.persistence.migration.UpgradePendingTreeToPerPartitionTask;
import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryEx;
@@ -76,8 +80,11 @@ import org.apache.ignite.internal.processors.cache.tree.CacheDataRowStore;
import org.apache.ignite.internal.processors.cache.tree.CacheDataTree;
import org.apache.ignite.internal.processors.cache.tree.PendingEntriesTree;
import org.apache.ignite.internal.processors.cache.tree.PendingRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateResult;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.lang.IgniteInClosure2X;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -735,11 +742,14 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
- @Override @Nullable protected WALHistoricalIterator historicalIterator(
+ @Override @Nullable protected IgniteHistoricalIterator historicalIterator(
CachePartitionPartialCountersMap partCntrs, Set<Integer> missing) throws IgniteCheckedException {
if (partCntrs == null || partCntrs.isEmpty())
return null;
+ if (grp.mvccEnabled()) // TODO IGNITE-7384
+ return super.historicalIterator(partCntrs, missing);
+
GridCacheDatabaseSharedManager database = (GridCacheDatabaseSharedManager)grp.shared().database();
FileWALPointer minPtr = null;
@@ -1093,6 +1103,16 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
@Override public long link() {
return 0;
}
@@ -1111,6 +1131,46 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
@Override public int cacheId() {
return entry.cacheId();
}
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCoordinatorVersion() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCounter() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public int newMvccOperationCounter() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte mvccTxState() {
+ return 0; // TODO IGNITE-7384
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte newMvccTxState() {
+ return 0; // TODO IGNITE-7384
+ }
}
/**
@@ -1505,6 +1565,30 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public long nextMvccUpdateCounter() {
+ try {
+ CacheDataStore delegate0 = init0(true);
+
+ return delegate0 == null ? 0 : delegate0.nextMvccUpdateCounter();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccUpdateCounter() {
+ try {
+ CacheDataStore delegate0 = init0(true);
+
+ return delegate0 == null ? 0 : delegate0.mvccUpdateCounter();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
@Override public void init(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes) {
throw new IllegalStateException("Should be never called.");
}
@@ -1589,6 +1673,126 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public boolean mvccInitialValue(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer)
+ throws IgniteCheckedException
+ {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccInitialValue(cctx, key, val, ver, expireTime, mvccVer, newMvccVer);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean mvccInitialValueIfAbsent(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte txState,
+ byte newTxState)
+ throws IgniteCheckedException
+ {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccInitialValueIfAbsent(cctx, key, val, ver, expireTime, mvccVer, newMvccVer,
+ txState, newTxState);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean mvccUpdateRowWithPreloadInfo(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState) throws IgniteCheckedException {
+
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccUpdateRowWithPreloadInfo(cctx,
+ key,
+ val,
+ ver,
+ expireTime,
+ mvccVer,
+ newMvccVer,
+ mvccTxState,
+ newMvccTxState);
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccUpdate(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccVer,
+ boolean primary,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccUpdate(
+ cctx, key, val, ver, expireTime, mvccVer, primary, needHistory, noCreate);
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccRemove(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ MvccSnapshot mvccVer,
+ boolean primary,
+ boolean needHistory) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccRemove(cctx, key, mvccVer, primary, needHistory);
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccLock(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccLock(cctx, key, mvccSnapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridLongList mvccUpdateNative(GridCacheContext cctx, boolean primary, KeyCacheObject key, CacheObject val, GridCacheVersion ver, long expireTime, MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccUpdateNative(cctx, primary, key, val, ver, expireTime, mvccSnapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridLongList mvccRemoveNative(GridCacheContext cctx, boolean primary, KeyCacheObject key, MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.mvccRemoveNative(cctx, primary, key, mvccSnapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void mvccRemoveAll(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ delegate.mvccRemoveAll(cctx, key);
+ }
+
+ /** {@inheritDoc} */
@Override public CacheDataRow createRow(
GridCacheContext cctx,
KeyCacheObject key,
@@ -1604,6 +1808,21 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public int cleanup(GridCacheContext cctx,
+ @Nullable List<MvccLinkAwareSearchRow> cleanupRows) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ return delegate.cleanup(cctx, cleanupRows);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateTxState(GridCacheContext cctx, CacheSearchRow row) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(false);
+
+ delegate.updateTxState(cctx, row);
+ }
+
+ /** {@inheritDoc} */
@Override public void invoke(GridCacheContext cctx, KeyCacheObject key, OffheapInvokeClosure c)
throws IgniteCheckedException {
assert ctx.database().checkpointLockIsHeldByThread();
@@ -1634,6 +1853,40 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public CacheDataRow mvccFind(GridCacheContext cctx, KeyCacheObject key, MvccSnapshot snapshot)
+ throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.mvccFind(cctx, key, snapshot);
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<IgniteBiTuple<Object, MvccVersion>> mvccFindAllVersions(GridCacheContext cctx, KeyCacheObject key)
+ throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.mvccFindAllVersions(cctx, key);
+
+ return Collections.emptyList();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCursor<CacheDataRow> mvccAllVersionsCursor(GridCacheContext cctx,
+ KeyCacheObject key, Object x) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.mvccAllVersionsCursor(cctx, key, x);
+
+ return EMPTY_CURSOR;
+ }
+
+
+ /** {@inheritDoc} */
@Override public GridCursor<? extends CacheDataRow> cursor() throws IgniteCheckedException {
CacheDataStore delegate = init0(true);
@@ -1644,6 +1897,27 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(Object x) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.cursor(x);
+
+ return EMPTY_CURSOR;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(MvccSnapshot mvccSnapshot)
+ throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.cursor(mvccSnapshot);
+
+ return EMPTY_CURSOR;
+ }
+
+ /** {@inheritDoc} */
@Override public GridCursor<? extends CacheDataRow> cursor(
int cacheId,
KeyCacheObject lower,
@@ -1671,6 +1945,21 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId,
+ KeyCacheObject lower,
+ KeyCacheObject upper,
+ Object x,
+ MvccSnapshot mvccSnapshot)
+ throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.cursor(cacheId, lower, upper, x, mvccSnapshot);
+
+ return EMPTY_CURSOR;
+ }
+
+ /** {@inheritDoc} */
@Override public void destroy() throws IgniteCheckedException {
// No need to destroy delegate.
}
@@ -1686,6 +1975,17 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
}
/** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ CacheDataStore delegate = init0(true);
+
+ if (delegate != null)
+ return delegate.cursor(cacheId, mvccSnapshot);
+
+ return EMPTY_CURSOR;
+ }
+
+ /** {@inheritDoc} */
@Override public void clear(int cacheId) throws IgniteCheckedException {
CacheDataStore delegate0 = init0(true);
@@ -1855,7 +2155,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
/**
*
*/
- private static final GridCursor<CacheDataRow> EMPTY_CURSOR = new GridCursor<CacheDataRow>() {
+ public static final GridCursor<CacheDataRow> EMPTY_CURSOR = new GridCursor<CacheDataRow>() {
/** {@inheritDoc} */
@Override public boolean next() {
return false;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
index f17e527..3ade265 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
@@ -223,6 +223,16 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
if (dataRegionsInitialized)
return;
+ initDataRegions0(memCfg);
+
+ dataRegionsInitialized = true;
+ }
+
+ /**
+ * @param memCfg Database config.
+ * @throws IgniteCheckedException If failed to initialize swap path.
+ */
+ protected void initDataRegions0(DataStorageConfiguration memCfg) throws IgniteCheckedException {
DataRegionConfiguration[] dataRegionCfgs = memCfg.getDataRegionConfigurations();
int dataRegions = dataRegionCfgs == null ? 0 : dataRegionCfgs.length;
@@ -251,8 +261,17 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
CU.isPersistenceEnabled(memCfg)
);
+ for (DatabaseLifecycleListener lsnr : getDatabaseListeners(cctx.kernalContext())) {
+ lsnr.onInitDataRegions(this);
+ }
+ }
- dataRegionsInitialized = true;
+ /**
+ * @param kctx Kernal context.
+ * @return Database lifecycle listeners.
+ */
+ protected List<DatabaseLifecycleListener> getDatabaseListeners(GridKernalContext kctx) {
+ return kctx.internalSubscriptionProcessor().getDatabaseListeners();
}
/**
@@ -260,7 +279,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
* @param dataRegionCfg Data region config.
* @throws IgniteCheckedException If failed to initialize swap path.
*/
- protected void addDataRegion(
+ public void addDataRegion(
DataStorageConfiguration dataStorageCfg,
DataRegionConfiguration dataRegionCfg,
boolean trackable
@@ -984,6 +1003,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
final DataRegionMetricsImpl memMetrics
) {
return new DirectMemoryProvider() {
+ /** */
private final DirectMemoryProvider memProvider = memoryProvider0;
@Override public void initialize(long[] chunkSizes) {
@@ -1039,10 +1059,18 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
startMemoryPolicies();
initPageMemoryDataStructures(memCfg);
+
+ for (DatabaseLifecycleListener lsnr : getDatabaseListeners(kctx)) {
+ lsnr.afterInitialise(this);
+ }
}
/** {@inheritDoc} */
@Override public void onDeActivate(GridKernalContext kctx) {
+ for (DatabaseLifecycleListener lsnr : getDatabaseListeners(cctx.kernalContext())) {
+ lsnr.beforeStop(this);
+ }
+
if (dataRegionMap != null) {
for (DataRegion memPlc : dataRegionMap.values()) {
memPlc.pageMemory().stop();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
index a4266fa..6248765 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
@@ -220,7 +220,7 @@ public class IndexStorageImpl implements IndexStorage {
}
/** {@inheritDoc} */
- @Override protected IndexItem getRow(final BPlusIO<IndexItem> io, final long pageAddr,
+ @Override public IndexItem getRow(final BPlusIO<IndexItem> io, final long pageAddr,
final int idx, Object ignore) throws IgniteCheckedException {
return readRow(pageAddr, ((IndexIO)io).getOffset(pageAddr, idx));
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
index ee7c255..6900b7e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
@@ -23,6 +23,7 @@ import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.CacheObjectContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList;
+import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
/**
@@ -100,6 +101,8 @@ public class RowStore {
try {
freeList.insertDataRow(row);
+
+ assert row.link() != 0L;
}
finally {
ctx.database().checkpointReadUnlock();
@@ -110,8 +113,8 @@ public class RowStore {
/**
* @param link Row link.
* @param row New row data.
- * @throws IgniteCheckedException If failed.
* @return {@code True} if was able to update row.
+ * @throws IgniteCheckedException If failed.
*/
public boolean updateRow(long link, CacheDataRow row) throws IgniteCheckedException {
assert !persistenceEnabled || ctx.database().checkpointLockIsHeldByThread();
@@ -123,6 +126,29 @@ public class RowStore {
}
/**
+ * Run page handler operation over the row.
+ *
+ * @param link Row link.
+ * @param pageHnd Page handler.
+ * @param arg Page handler argument.
+ * @throws IgniteCheckedException If failed.
+ */
+ public <S, R> void updateDataRow(long link, PageHandler<S, R> pageHnd, S arg) throws IgniteCheckedException {
+ if (!persistenceEnabled)
+ freeList.updateDataRow(link, pageHnd, arg);
+ else {
+ ctx.database().checkpointReadLock();
+
+ try {
+ freeList.updateDataRow(link, pageHnd, arg);
+ }
+ finally {
+ ctx.database().checkpointReadUnlock();
+ }
+ }
+ }
+
+ /**
* @return Free list.
*/
public FreeList freeList() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
index ae200df..133f0a1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.processors.cache.persistence;
+import org.apache.ignite.IgniteCheckedException;
+
/**
* Simple interface for data, store in some RowStore.
*/
@@ -35,4 +37,16 @@ public interface Storable {
* @return Partition.
*/
public int partition();
+
+ /**
+ * @return Row size in page.
+ * @throws IgniteCheckedException If failed.
+ */
+ public int size() throws IgniteCheckedException;
+
+ /**
+ * @return Row header size in page. Header is indivisible part of row
+ * which is entirely available on the very first page followed by the row link.
+ */
+ public int headerSize();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreManager.java
index 933d28e..aa09241 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreManager.java
@@ -279,6 +279,23 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
}
/** {@inheritDoc} */
+ @Override public void initialize(int cacheId, int partitions, String workingDir, AllocatedPageTracker tracker)
+ throws IgniteCheckedException {
+ if (!idxCacheStores.containsKey(cacheId)) {
+ CacheStoreHolder holder = initDir(
+ new File(storeWorkDir, workingDir),
+ cacheId,
+ partitions,
+ tracker
+ );
+
+ CacheStoreHolder old = idxCacheStores.put(cacheId, holder);
+
+ assert old == null : "Non-null old store holder for cacheId: " + cacheId;
+ }
+ }
+
+ /** {@inheritDoc} */
@Override public void initializeForCache(CacheGroupDescriptor grpDesc, StoredCacheData cacheData) throws IgniteCheckedException {
int grpId = grpDesc.groupId();
@@ -298,9 +315,9 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
if (!idxCacheStores.containsKey(grpId)) {
CacheStoreHolder holder = initDir(
new File(storeWorkDir, META_STORAGE_NAME),
- grpId,
- 1,
- AllocatedPageTracker.NO_OP);
+ grpId,
+ 1,
+ AllocatedPageTracker.NO_OP );
CacheStoreHolder old = idxCacheStores.put(grpId, holder);
@@ -502,7 +519,7 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
* @param cacheWorkDir Work directory.
* @param grpId Group ID.
* @param partitions Number of partitions.
- * @param allocatedTracker Metrics updater
+ * @param allocatedTracker Metrics updater.
* @return Cache store holder.
* @throws IgniteCheckedException If failed.
*/
@@ -1012,7 +1029,7 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
/**
*
*/
- public CacheStoreHolder(FilePageStore idxStore, FilePageStore[] partStores) {
+ CacheStoreHolder(FilePageStore idxStore, FilePageStore[] partStores) {
this.idxStore = idxStore;
this.partStores = partStores;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
index bcedd8c..4e1f783 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
@@ -100,7 +100,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
throws IgniteCheckedException {
AbstractDataPageIO<T> io = (AbstractDataPageIO<T>)iox;
- int rowSize = io.getRowSize(row);
+ int rowSize = row.size();
boolean updated = io.updateRow(pageAddr, itemId, pageSize(), null, row, rowSize);
@@ -146,7 +146,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
throws IgniteCheckedException {
AbstractDataPageIO<T> io = (AbstractDataPageIO<T>)iox;
- int rowSize = io.getRowSize(row);
+ int rowSize = row.size();
int oldFreeSpace = io.getFreeSpace(pageAddr);
assert oldFreeSpace > 0 : oldFreeSpace;
@@ -466,7 +466,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
/** {@inheritDoc} */
@Override public void insertDataRow(T row) throws IgniteCheckedException {
- int rowSize = ioVersions().latest().getRowSize(row);
+ int rowSize = row.size();
int written = 0;
@@ -546,6 +546,20 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
}
/** {@inheritDoc} */
+ @Override public <S, R> R updateDataRow(long link, PageHandler<S, R> pageHnd, S arg) throws IgniteCheckedException {
+ assert link != 0;
+
+ long pageId = PageIdUtils.pageId(link);
+ int itemId = PageIdUtils.itemId(link);
+
+ R updRes = write(pageId, pageHnd, arg, itemId, null);
+
+ assert updRes != null; // Can't fail here.
+
+ return updRes;
+ }
+
+ /** {@inheritDoc} */
@Override public void removeDataRowByLink(long link) throws IgniteCheckedException {
assert link != 0;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java
index bdca21c..e73124e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.processors.cache.persistence.Storable;
+import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
/**
*/
@@ -40,6 +41,17 @@ public interface FreeList<T extends Storable> {
/**
* @param link Row link.
+ * @param pageHnd Page handler.
+ * @param arg Handler argument.
+ * @param <S> Argument type.
+ * @param <R> Result type.
+ * @return Result.
+ * @throws IgniteCheckedException If failed.
+ */
+ public <S, R> R updateDataRow(long link, PageHandler<S, R> pageHnd, S arg) throws IgniteCheckedException;
+
+ /**
+ * @param link Row link.
* @throws IgniteCheckedException If failed.
*/
public void removeDataRowByLink(long link) throws IgniteCheckedException;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
index 271efdf..95d8e81 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.cache.persistence.metastorage;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.persistence.Storable;
/**
@@ -65,6 +66,16 @@ public class MetastorageDataRow implements MetastorageSearchRow, Storable {
}
/** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ return 4 + value().length;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
@Override
public void link(long link) {
this.link = link;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageTree.java
index 19a145f..00db5cd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageTree.java
@@ -79,7 +79,7 @@ public class MetastorageTree extends BPlusTree<MetastorageSearchRow, Metastorage
}
/** {@inheritDoc} */
- @Override protected MetastorageDataRow getRow(BPlusIO<MetastorageSearchRow> io, long pageAddr, int idx,
+ @Override public MetastorageDataRow getRow(BPlusIO<MetastorageSearchRow> io, long pageAddr, int idx,
Object x) throws IgniteCheckedException {
long link = ((DataLinkIO)io).getLink(pageAddr, idx);
String key = ((DataLinkIO)io).getKey(pageAddr, idx);
[02/45] ignite git commit: IGNITE-9421: ML Examples:
LogisticRegressionSGDTrainerExample example result not correct
Posted by ag...@apache.org.
IGNITE-9421: ML Examples: LogisticRegressionSGDTrainerExample
example result not correct
this closes #4646
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/184d6a56
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/184d6a56
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/184d6a56
Branch: refs/heads/ignite-9273
Commit: 184d6a56335f58300907e30f93fa832371a80342
Parents: f623478
Author: zaleslaw <za...@gmail.com>
Authored: Thu Aug 30 11:56:39 2018 +0300
Committer: Yury Babak <yb...@gridgain.com>
Committed: Thu Aug 30 11:56:39 2018 +0300
----------------------------------------------------------------------
.../logistic/binary/LogisticRegressionSGDTrainerExample.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/184d6a56/examples/src/main/java/org/apache/ignite/examples/ml/regression/logistic/binary/LogisticRegressionSGDTrainerExample.java
----------------------------------------------------------------------
diff --git a/examples/src/main/java/org/apache/ignite/examples/ml/regression/logistic/binary/LogisticRegressionSGDTrainerExample.java b/examples/src/main/java/org/apache/ignite/examples/ml/regression/logistic/binary/LogisticRegressionSGDTrainerExample.java
index 99b5677..7e1c773 100644
--- a/examples/src/main/java/org/apache/ignite/examples/ml/regression/logistic/binary/LogisticRegressionSGDTrainerExample.java
+++ b/examples/src/main/java/org/apache/ignite/examples/ml/regression/logistic/binary/LogisticRegressionSGDTrainerExample.java
@@ -75,7 +75,7 @@ public class LogisticRegressionSGDTrainerExample {
dataCache,
(k, v) -> VectorUtils.of(Arrays.copyOfRange(v, 1, v.length)),
(k, v) -> v[0]
- ).withRawLabels(true);
+ );
System.out.println(">>> Logistic regression model: " + mdl);
[35/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxAbstractEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxAbstractEnlistFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxAbstractEnlistFuture.java
new file mode 100644
index 0000000..bb11df5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxAbstractEnlistFuture.java
@@ -0,0 +1,1139 @@
+/*
+ * 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.distributed.dht;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.pagemem.wal.WALPointer;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
+import org.apache.ignite.internal.processors.cache.GridCacheFutureAdapter;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo;
+import org.apache.ignite.internal.processors.cache.GridCacheUpdateTxResult;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxAbstractEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxSelectForUpdateFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Abstract future processing transaction enlisting and locking
+ * of entries produced with DML and SELECT FOR UPDATE queries.
+ */
+public abstract class GridDhtTxAbstractEnlistFuture extends GridCacheFutureAdapter<Long>
+ implements DhtLockFuture<Long> {
+ /** Done field updater. */
+ private static final AtomicIntegerFieldUpdater<GridDhtTxAbstractEnlistFuture> DONE_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(GridDhtTxAbstractEnlistFuture.class, "done");
+
+ /** SkipCntr field updater. */
+ private static final AtomicIntegerFieldUpdater<GridDhtTxAbstractEnlistFuture> SKIP_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(GridDhtTxAbstractEnlistFuture.class, "skipCntr");
+
+ /** Marker object. */
+ private static final Object FINISHED = new Object();
+
+ /** */
+ private static final int BATCH_SIZE = 1024;
+
+ /** In-flight batches per node limit. */
+ private static final int BATCHES_PER_NODE = 5;
+
+ /** */
+ private static final int FIRST_BATCH_ID = 0;
+
+ /** Future ID. */
+ protected final IgniteUuid futId;
+
+ /** Cache registry. */
+ @GridToStringExclude
+ protected final GridCacheContext<?, ?> cctx;
+
+ /** Logger. */
+ @GridToStringExclude
+ protected final IgniteLogger log;
+
+ /** Thread. */
+ protected final long threadId;
+
+ /** Future ID. */
+ protected final IgniteUuid nearFutId;
+
+ /** Future ID. */
+ protected final int nearMiniId;
+
+ /** Partitions. */
+ protected final int[] parts;
+
+ /** Transaction. */
+ protected final GridDhtTxLocalAdapter tx;
+
+ /** Lock version. */
+ protected final GridCacheVersion lockVer;
+
+ /** */
+ protected final MvccSnapshot mvccSnapshot;
+
+ /** Processed entries count. */
+ protected long cnt;
+
+ /** Near node ID. */
+ protected final UUID nearNodeId;
+
+ /** Near lock version. */
+ protected final GridCacheVersion nearLockVer;
+
+ /** Timeout object. */
+ @GridToStringExclude
+ protected LockTimeoutObject timeoutObj;
+
+ /** Lock timeout. */
+ protected final long timeout;
+
+ /** Query iterator */
+ private UpdateSourceIterator<?> it;
+
+ /** Row extracted from iterator but not yet used. */
+ private Object peek;
+
+ /** */
+ @SuppressWarnings({"FieldCanBeLocal"})
+ @GridToStringExclude
+ private volatile int skipCntr;
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ private volatile int done;
+
+ /** */
+ @GridToStringExclude
+ private int batchIdCntr;
+
+ /** Batches for sending to remote nodes. */
+ private Map<UUID, Batch> batches;
+
+ /** Batches already sent to remotes, but their acks are not received yet. */
+ private ConcurrentMap<UUID, ConcurrentMap<Integer, Batch>> pending;
+
+ /** */
+ private WALPointer walPtr;
+
+ /** Do not send DHT requests to near node. */
+ protected boolean skipNearNodeUpdates;
+
+ /** There are keys belonging to backup partitions on near node. */
+ protected boolean hasNearNodeUpdates;
+
+ /** Moving partitions. */
+ private Map<Integer, Boolean> movingParts;
+
+ /** Update counters to be sent to the near node in case it is a backup node also. */
+ protected GridLongList nearUpdCntrs;
+
+ /**
+ * @param nearNodeId Near node ID.
+ * @param nearLockVer Near lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param threadId Thread ID.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini future id.
+ * @param parts Partitions.
+ * @param tx Transaction.
+ * @param timeout Lock acquisition timeout.
+ * @param cctx Cache context.
+ */
+ protected GridDhtTxAbstractEnlistFuture(UUID nearNodeId,
+ GridCacheVersion nearLockVer,
+ MvccSnapshot mvccSnapshot,
+ long threadId,
+ IgniteUuid nearFutId,
+ int nearMiniId,
+ @Nullable int[] parts,
+ GridDhtTxLocalAdapter tx,
+ long timeout,
+ GridCacheContext<?, ?> cctx) {
+ assert tx != null;
+ assert timeout >= 0;
+ assert nearNodeId != null;
+ assert nearLockVer != null;
+ assert threadId == tx.threadId();
+
+ this.threadId = threadId;
+ this.cctx = cctx;
+ this.nearNodeId = nearNodeId;
+ this.nearLockVer = nearLockVer;
+ this.nearFutId = nearFutId;
+ this.nearMiniId = nearMiniId;
+ this.mvccSnapshot = mvccSnapshot;
+ this.timeout = timeout;
+ this.tx = tx;
+ this.parts = parts;
+
+ lockVer = tx.xidVersion();
+
+ futId = IgniteUuid.randomUuid();
+
+ log = cctx.logger(GridDhtTxAbstractEnlistFuture.class);
+ }
+
+ /**
+ * @return iterator.
+ * @throws IgniteCheckedException If failed.
+ */
+ protected abstract UpdateSourceIterator<?> createIterator() throws IgniteCheckedException;
+
+ /**
+ *
+ */
+ public void init() {
+ if (timeout < 0) {
+ // Time is out.
+ onDone(timeoutException());
+
+ return;
+ }
+ else if (timeout > 0)
+ timeoutObj = new LockTimeoutObject();
+
+ while(true) {
+ IgniteInternalFuture<?> fut = tx.lockFut;
+
+ if (fut == GridDhtTxLocalAdapter.ROLLBACK_FUT) {
+ onDone(tx.timedOut() ? tx.timeoutException() : tx.rollbackException());
+
+ return;
+ }
+ else if (fut != null) {
+ // Wait for previous future.
+ assert fut instanceof GridNearTxAbstractEnlistFuture
+ || fut instanceof GridDhtTxAbstractEnlistFuture
+ || fut instanceof CompoundLockFuture
+ || fut instanceof GridNearTxSelectForUpdateFuture : fut;
+
+ // Terminate this future if parent future is terminated by rollback.
+ if (!fut.isDone()) {
+ fut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ if (fut.error() != null)
+ onDone(fut.error());
+ }
+ });
+ }
+ else if (fut.error() != null)
+ onDone(fut.error());
+
+ break;
+ }
+ else if (tx.updateLockFuture(null, this))
+ break;
+ }
+
+ boolean added = cctx.mvcc().addFuture(this, futId);
+
+ assert added;
+
+ if (isDone()) {
+ cctx.mvcc().removeFuture(futId);
+
+ return;
+ }
+
+ if (timeoutObj != null)
+ cctx.time().addTimeoutObject(timeoutObj);
+
+ try {
+ checkPartitions(parts);
+
+ UpdateSourceIterator<?> it = createIterator();
+
+ if (!it.hasNext()) {
+ U.close(it, log);
+
+ onDone(0L);
+
+ return;
+ }
+
+ tx.addActiveCache(cctx, false);
+
+ this.it = it;
+ }
+ catch (Throwable e) {
+ onDone(e);
+
+ if (e instanceof Error)
+ throw (Error)e;
+
+ return;
+ }
+
+ continueLoop(false);
+ }
+
+ /**
+ * Clears lock future.
+ */
+ protected void clearLockFuture() {
+ tx.clearLockFuture(this);
+ }
+
+ /**
+ * Iterates over iterator, applies changes locally and sends it on backups.
+ *
+ * @param ignoreCntr {@code True} if need to ignore skip counter.
+ */
+ private void continueLoop(boolean ignoreCntr) {
+ if (isDone() || (!ignoreCntr && (SKIP_UPD.getAndIncrement(this) != 0)))
+ return;
+
+ GridDhtCacheAdapter cache = cctx.dhtCache();
+ EnlistOperation op = it.operation();
+ AffinityTopologyVersion topVer = tx.topologyVersionSnapshot();
+
+ try {
+ while (true) {
+ while (hasNext0()) {
+ Object cur = next0();
+
+ KeyCacheObject key = cctx.toCacheKeyObject(op.isDeleteOrLock() ? cur : ((IgniteBiTuple)cur).getKey());
+
+ if (!ensureFreeSlot(key)) {
+ // Can't advance further at the moment.
+ peek = cur;
+
+ it.beforeDetach();
+
+ break;
+ }
+
+ GridDhtCacheEntry entry = cache.entryExx(key);
+
+ if (log.isDebugEnabled())
+ log.debug("Adding entry: " + entry);
+
+ assert !entry.detached();
+
+ CacheObject val = op.isDeleteOrLock() ? null : cctx.toCacheObject(((IgniteBiTuple)cur).getValue());
+
+ tx.markQueryEnlisted(mvccSnapshot);
+
+ GridCacheUpdateTxResult res;
+
+ while (true) {
+ cctx.shared().database().checkpointReadLock();
+
+ try {
+ switch (op) {
+ case DELETE:
+ res = entry.mvccRemove(
+ tx,
+ cctx.localNodeId(),
+ topVer,
+ null,
+ mvccSnapshot,
+ isMoving(key.partition()));
+
+ break;
+
+ case INSERT:
+ case UPSERT:
+ case UPDATE:
+ res = entry.mvccSet(
+ tx,
+ cctx.localNodeId(),
+ val,
+ 0,
+ topVer,
+ null,
+ mvccSnapshot,
+ op.cacheOperation(),
+ isMoving(key.partition()),
+ op.noCreate());
+
+ break;
+
+ case LOCK:
+ res = entry.mvccLock(
+ tx,
+ mvccSnapshot);
+
+ break;
+
+ default:
+ throw new IgniteSQLException("Cannot acquire lock for operation [op= " + op + "]" +
+ "Operation is unsupported at the moment ", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ break;
+ }
+ catch (GridCacheEntryRemovedException ignored) {
+ entry = cache.entryExx(entry.key(), topVer);
+ }
+ finally {
+ cctx.shared().database().checkpointReadUnlock();
+ }
+ }
+
+ IgniteInternalFuture<GridCacheUpdateTxResult> updateFut = res.updateFuture();
+
+ if (updateFut != null) {
+ if (updateFut.isDone())
+ res = updateFut.get();
+ else {
+ CacheObject val0 = val;
+ GridDhtCacheEntry entry0 = entry;
+
+ it.beforeDetach();
+
+ updateFut.listen(new CI1<IgniteInternalFuture<GridCacheUpdateTxResult>>() {
+ @Override public void apply(IgniteInternalFuture<GridCacheUpdateTxResult> fut) {
+ try {
+ processEntry(entry0, op, fut.get(), val0);
+
+ continueLoop(true);
+ }
+ catch (Throwable e) {
+ onDone(e);
+ }
+ }
+ });
+
+ // Can't move further. Exit loop without decrementing the counter.
+ return;
+ }
+ }
+
+ processEntry(entry, op, res, val);
+ }
+
+ if (!hasNext0()) {
+ if (walPtr != null && !cctx.tm().logTxRecords()) {
+ cctx.shared().wal().flush(walPtr, true);
+
+ walPtr = null; // Avoid additional flushing.
+ }
+
+ if (!F.isEmpty(batches)) {
+ // Flush incomplete batches.
+ // Need to skip batches for nodes where first request (contains tx info) is still in-flight.
+ // Otherwise, the regular enlist request (without tx info) may beat it to the primary node.
+ Iterator<Map.Entry<UUID, Batch>> it = batches.entrySet().iterator();
+
+ while (it.hasNext()) {
+ Map.Entry<UUID, Batch> e = it.next();
+
+ ConcurrentMap<Integer, Batch> pending0 =
+ pending == null ? null : pending.get(e.getKey());
+
+ if (pending0 == null || !pending0.containsKey(FIRST_BATCH_ID)) {
+ it.remove();
+
+ sendBatch(e.getValue());
+ }
+ }
+ }
+
+ if (noPendingRequests()) {
+ onDone(cnt);
+
+ return;
+ }
+ }
+
+ if (SKIP_UPD.decrementAndGet(this) == 0)
+ break;
+
+ skipCntr = 1;
+ }
+ }
+ catch (Throwable e) {
+ onDone(e);
+
+ if (e instanceof Error)
+ throw (Error)e;
+ }
+ }
+
+ /** */
+ private Object next0() {
+ if (!hasNext0())
+ throw new NoSuchElementException();
+
+ Object cur;
+
+ if ((cur = peek) != null)
+ peek = null;
+ else
+ cur = it.next();
+
+ return cur;
+ }
+
+ /** */
+ private boolean hasNext0() {
+ if (peek == null && !it.hasNext())
+ peek = FINISHED;
+
+ return peek != FINISHED;
+ }
+
+ /**
+ * @return {@code True} if in-flight batches map is empty.
+ */
+ private boolean noPendingRequests() {
+ if (F.isEmpty(pending))
+ return true;
+
+ for (ConcurrentMap<Integer, Batch> e : pending.values()) {
+ if (!e.isEmpty())
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param entry Cache entry.
+ * @param op Operation.
+ * @param updRes Update result.
+ * @param val New value.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void processEntry(GridDhtCacheEntry entry, EnlistOperation op,
+ GridCacheUpdateTxResult updRes, CacheObject val) throws IgniteCheckedException {
+ checkCompleted();
+
+ assert updRes != null && updRes.updateFuture() == null;
+
+ WALPointer ptr0 = updRes.loggedPointer();
+
+ if (ptr0 != null)
+ walPtr = ptr0;
+
+ if (!updRes.success())
+ return;
+
+ cnt++;
+
+ if (op != EnlistOperation.LOCK)
+ addToBatch(entry.key(), val, updRes.mvccHistory(), updRes.updateCounter(), entry.context().cacheId());
+ }
+
+ /**
+ * Adds row to batch.
+ * <b>IMPORTANT:</b> This method should be called from the critical section in {@link this.sendNextBatches()}
+ *
+ * @param key Key.
+ * @param val Value.
+ * @param hist History rows.
+ * @param updCntr Update counter.
+ */
+ private void addToBatch(KeyCacheObject key, CacheObject val, List<MvccLinkAwareSearchRow> hist, long updCntr,
+ int cacheId) throws IgniteCheckedException {
+ List<ClusterNode> backups = backupNodes(key);
+
+ int part = cctx.affinity().partition(key);
+
+ tx.addPartitionCountersMapping(cacheId, part);
+
+ if (F.isEmpty(backups))
+ return;
+
+ CacheEntryInfoCollection hist0 = null;
+
+ for (ClusterNode node : backups) {
+ assert !node.isLocal();
+
+ boolean moving = isMoving(node, part);
+
+ if (skipNearNodeUpdates && node.id().equals(nearNodeId) && !moving) {
+ updateMappings(node);
+
+ if (newRemoteTx(node))
+ tx.addLockTransactionNode(node);
+
+ hasNearNodeUpdates = true;
+
+ if (nearUpdCntrs == null)
+ nearUpdCntrs = new GridLongList();
+
+ nearUpdCntrs.add(updCntr);
+
+ continue;
+ }
+
+ Batch batch = null;
+
+ if (batches == null)
+ batches = new HashMap<>();
+ else
+ batch = batches.get(node.id());
+
+ if (batch == null)
+ batches.put(node.id(), batch = new Batch(node));
+
+ if (moving && hist0 == null) {
+ assert !F.isEmpty(hist);
+
+ hist0 = fetchHistoryInfo(key, hist);
+ }
+
+ batch.add(key, moving ? hist0 : val, updCntr);
+
+ if (batch.size() == BATCH_SIZE) {
+ assert batches != null;
+
+ batches.remove(node.id());
+
+ sendBatch(batch);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param key Key.
+ * @param hist History rows.
+ * @return History entries.
+ * @throws IgniteCheckedException, if failed.
+ */
+ private CacheEntryInfoCollection fetchHistoryInfo(KeyCacheObject key, List<MvccLinkAwareSearchRow> hist)
+ throws IgniteCheckedException {
+ List<GridCacheEntryInfo> res = new ArrayList<>();
+
+ for (int i = 0; i < hist.size(); i++) {
+ MvccLinkAwareSearchRow row0 = hist.get(i);
+
+ MvccDataRow row = new MvccDataRow(cctx.group(),
+ row0.hash(),
+ row0.link(),
+ key.partition(),
+ CacheDataRowAdapter.RowData.NO_KEY,
+ row0.mvccCoordinatorVersion(),
+ row0.mvccCounter(),
+ row0.mvccOperationCounter());
+
+ GridCacheMvccEntryInfo entry = new GridCacheMvccEntryInfo();
+
+ entry.version(row.version());
+ entry.mvccVersion(row);
+ entry.newMvccVersion(row);
+ entry.value(row.value());
+ entry.expireTime(row.expireTime());
+
+ if (MvccUtils.compare(mvccSnapshot, row.mvccCoordinatorVersion(), row.mvccCounter()) != 0) {
+ entry.mvccTxState(row.mvccTxState() != TxState.NA ? row.mvccTxState() :
+ MvccUtils.state(cctx, row.mvccCoordinatorVersion(), row.mvccCounter(), row.mvccOperationCounter()));
+ }
+
+ if (MvccUtils.compare(mvccSnapshot, row.newMvccCoordinatorVersion(), row.newMvccCounter()) != 0) {
+ entry.newMvccTxState(row.newMvccTxState() != TxState.NA ? row.newMvccTxState() :
+ MvccUtils.state(cctx, row.newMvccCoordinatorVersion(), row.newMvccCounter(),
+ row.newMvccOperationCounter()));
+ }
+
+ res.add(entry);
+ }
+
+ return new CacheEntryInfoCollection(res);
+ }
+
+ /** */
+ private boolean newRemoteTx(ClusterNode node) {
+ Set<ClusterNode> nodes = tx.lockTransactionNodes();
+
+ return nodes == null || !nodes.contains(node);
+ }
+
+ /**
+ * Checks if there free space in batches or free slot in in-flight batches is available for the given key.
+ *
+ * @param key Key.
+ * @return {@code True} if there is possible to add this key to batch or send ready batch.
+ */
+ @SuppressWarnings("ForLoopReplaceableByForEach")
+ private boolean ensureFreeSlot(KeyCacheObject key) {
+ if (F.isEmpty(batches) || F.isEmpty(pending))
+ return true;
+
+ // Check possibility of adding to batch and sending.
+ for (ClusterNode node : backupNodes(key)) {
+ if (skipNearNodeUpdates && node.id().equals(nearNodeId) && !isMoving(node, key.partition()))
+ continue;
+
+ Batch batch = batches.get(node.id());
+
+ // We can add key if batch is not full.
+ if (batch == null || batch.size() < BATCH_SIZE - 1)
+ continue;
+
+ ConcurrentMap<Integer, Batch> pending0 = pending.get(node.id());
+
+ assert pending0 == null || pending0.size() <= BATCHES_PER_NODE;
+
+ if (pending0 != null && (pending0.containsKey(FIRST_BATCH_ID) || pending0.size() == BATCHES_PER_NODE))
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send batch request to remote data node.
+ *
+ * @param batch Batch.
+ */
+ private void sendBatch(Batch batch) throws IgniteCheckedException {
+ assert batch != null && !batch.node().isLocal();
+
+ ClusterNode node = batch.node();
+
+ updateMappings(node);
+
+ GridDhtTxQueryEnlistRequest req;
+
+ if (newRemoteTx(node)) {
+ tx.addLockTransactionNode(node);
+
+ // If this is a first request to this node, send full info.
+ req = new GridDhtTxQueryFirstEnlistRequest(cctx.cacheId(),
+ futId,
+ cctx.localNodeId(),
+ tx.topologyVersionSnapshot(),
+ lockVer,
+ mvccSnapshot,
+ tx.remainingTime(),
+ tx.taskNameHash(),
+ nearNodeId,
+ nearLockVer,
+ it.operation(),
+ FIRST_BATCH_ID,
+ batch.keys(),
+ batch.values(),
+ batch.updateCounters());
+ }
+ else {
+ // Send only keys, values, LockVersion and batchId if this is not a first request to this backup.
+ req = new GridDhtTxQueryEnlistRequest(cctx.cacheId(),
+ futId,
+ lockVer,
+ it.operation(),
+ ++batchIdCntr,
+ mvccSnapshot.operationCounter(),
+ batch.keys(),
+ batch.values(),
+ batch.updateCounters());
+ }
+
+ ConcurrentMap<Integer, Batch> pending0 = null;
+
+ if (pending == null)
+ pending = new ConcurrentHashMap<>();
+ else
+ pending0 = pending.get(node.id());
+
+ if (pending0 == null)
+ pending.put(node.id(), pending0 = new ConcurrentHashMap<>());
+
+ Batch prev = pending0.put(req.batchId(), batch);
+
+ assert prev == null;
+
+ cctx.io().send(node, req, cctx.ioPolicy());
+ }
+
+ /** */
+ private synchronized void updateMappings(ClusterNode node) throws IgniteCheckedException {
+ checkCompleted();
+
+ Map<UUID, GridDistributedTxMapping> m = tx.dhtMap;
+
+ GridDistributedTxMapping mapping = m.get(node.id());
+
+ if (mapping == null)
+ m.put(node.id(), mapping = new GridDistributedTxMapping(node));
+
+ mapping.markQueryUpdate();
+ }
+
+ /**
+ * @param key Key.
+ * @return Backup nodes for the given key.
+ */
+ @NotNull private List<ClusterNode> backupNodes(KeyCacheObject key) {
+ List<ClusterNode> dhtNodes = cctx.affinity().nodesByKey(key, tx.topologyVersion());
+
+ assert !dhtNodes.isEmpty() && dhtNodes.get(0).id().equals(cctx.localNodeId()) :
+ "localNode = " + cctx.localNodeId() + ", dhtNodes = " + dhtNodes;
+
+ if (dhtNodes.size() == 1)
+ return Collections.emptyList();
+
+ return dhtNodes.subList(1, dhtNodes.size());
+ }
+
+ /**
+ * Checks whether all the necessary partitions are in {@link GridDhtPartitionState#OWNING} state.
+ *
+ * @param parts Partitions.
+ * @throws ClusterTopologyCheckedException If failed.
+ */
+ @SuppressWarnings("ForLoopReplaceableByForEach")
+ private void checkPartitions(@Nullable int[] parts) throws ClusterTopologyCheckedException {
+ if (cctx.isLocal() || !cctx.rebalanceEnabled())
+ return;
+
+ if (parts == null)
+ parts = U.toIntArray(
+ cctx.affinity()
+ .primaryPartitions(cctx.localNodeId(), tx.topologyVersionSnapshot()));
+
+ GridDhtPartitionTopology top = cctx.topology();
+
+ try {
+ top.readLock();
+
+ for (int i = 0; i < parts.length; i++) {
+ GridDhtLocalPartition p = top.localPartition(parts[i]);
+
+ if (p == null || p.state() != GridDhtPartitionState.OWNING)
+ throw new ClusterTopologyCheckedException("Cannot run update query. " +
+ "Node must own all the necessary partitions."); // TODO IGNITE-7185 Send retry instead.
+ }
+ }
+ finally {
+ top.readUnlock();
+ }
+ }
+
+ /**
+ * @param part Partition.
+ * @return {@code true} if the given partition is rebalancing to any backup node.
+ */
+ private boolean isMoving(int part) {
+ if (movingParts == null)
+ movingParts = new HashMap<>();
+
+ Boolean res = movingParts.get(part);
+
+ if (res != null)
+ return res;
+
+ List<ClusterNode> dhtNodes = cctx.affinity().nodesByPartition(part, tx.topologyVersion());
+
+ for (int i = 1; i < dhtNodes.size(); i++) {
+ ClusterNode node = dhtNodes.get(i);
+ if (isMoving(node, part)) {
+ movingParts.put(part, Boolean.TRUE);
+
+ return true;
+ }
+ }
+
+ movingParts.put(part, Boolean.FALSE);
+
+ return false;
+ }
+
+ /**
+ * @param node Cluster node.
+ * @param part Partition.
+ * @return {@code true} if the given partition is rebalancing to the given node.
+ */
+ private boolean isMoving(ClusterNode node, int part) {
+ GridDhtPartitionState partState = cctx.topology().partitionState(node.id(), part);
+
+ return partState != GridDhtPartitionState.OWNING && partState != GridDhtPartitionState.EVICTED;
+ }
+
+ /** */
+ private void checkCompleted() throws IgniteCheckedException {
+ if (isDone())
+ throw new IgniteCheckedException("Future is done.");
+ }
+
+ /**
+ * Callback on backup response.
+ *
+ * @param nodeId Backup node.
+ * @param res Response.
+ */
+ public void onResult(UUID nodeId, GridDhtTxQueryEnlistResponse res) {
+ if (res.error() != null) {
+ onDone(new IgniteCheckedException("Failed to update backup node: [localNodeId=" + cctx.localNodeId() +
+ ", remoteNodeId=" + nodeId + ']', res.error()));
+
+ return;
+ }
+
+ assert pending != null;
+
+ ConcurrentMap<Integer, Batch> pending0 = pending.get(nodeId);
+
+ assert pending0 != null;
+
+ Batch rmv = pending0.remove(res.batchId());
+
+ assert rmv != null;
+
+ continueLoop(false);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean trackable() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void markNotTrackable() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onNodeLeft(UUID nodeId) {
+ boolean backupLeft = false;
+
+ Set<ClusterNode> nodes = tx.lockTransactionNodes();
+
+ if (!F.isEmpty(nodes)) {
+ for (ClusterNode node : nodes) {
+ if (node.id().equals(nodeId)) {
+ backupLeft = true;
+
+ break;
+ }
+ }
+ }
+
+ return (backupLeft || nearNodeId.equals(nodeId)) && onDone(
+ new ClusterTopologyCheckedException((backupLeft ? "Backup" : "Requesting") +
+ " node left the grid [nodeId=" + nodeId + ']'));
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onDone(@Nullable Long res, @Nullable Throwable err) {
+ assert res != null || err != null;
+
+ if (!DONE_UPD.compareAndSet(this, 0, 1))
+ return false;
+
+ if (err == null)
+ clearLockFuture();
+
+ // To prevent new remote transactions creation
+ // after future is cancelled by rollback.
+ synchronized (this) {
+ boolean done = super.onDone(res, err);
+
+ assert done;
+
+ if (log.isDebugEnabled())
+ log.debug("Completing future: " + this);
+
+ // Clean up.
+ cctx.mvcc().removeFuture(futId);
+
+ if (timeoutObj != null)
+ cctx.time().removeTimeoutObject(timeoutObj);
+
+ U.close(it, log);
+
+ return true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onError(Throwable error) {
+ onDone(error);
+ }
+
+ /**
+ * @return Timeout exception.
+ */
+ @NotNull protected IgniteTxTimeoutCheckedException timeoutException() {
+ return new IgniteTxTimeoutCheckedException("Failed to acquire lock within provided timeout for " +
+ "transaction [timeout=" + timeout + ", tx=" + tx + ']');
+ }
+
+ /**
+ * A batch of rows
+ */
+ private static class Batch {
+ /** Node ID. */
+ @GridToStringExclude
+ private final ClusterNode node;
+
+ /** */
+ private List<KeyCacheObject> keys;
+
+ /**
+ * Values collection.
+ * Items can be either {@link CacheObject} or preload entries collection {@link CacheEntryInfoCollection}.
+ */
+ private List<Message> vals;
+
+ /** Update counters. */
+ private GridLongList updCntrs;
+
+ /**
+ * @param node Cluster node.
+ */
+ private Batch(ClusterNode node) {
+ this.node = node;
+ }
+
+ /**
+ * @return Node.
+ */
+ public ClusterNode node() {
+ return node;
+ }
+
+ /**
+ * Adds a row to batch.
+ *
+ * @param key Key.
+ * @param val Value or preload entries collection.
+ */
+ public void add(KeyCacheObject key, Message val, long updCntr) {
+ assert val == null || val instanceof CacheObject || val instanceof CacheEntryInfoCollection;
+ assert updCntr > 0;
+
+ if (keys == null)
+ keys = new ArrayList<>();
+
+ keys.add(key);
+
+ if (val != null) {
+ if (vals == null)
+ vals = new ArrayList<>();
+
+ vals.add(val);
+ }
+
+ if (updCntrs == null)
+ updCntrs = new GridLongList();
+
+ updCntrs.add(updCntr);
+
+ assert (vals == null) || keys.size() == vals.size();
+ }
+
+ /**
+ * @return number of rows.
+ */
+ public int size() {
+ return keys == null ? 0 : keys.size();
+ }
+
+ /**
+ * @return Collection of row keys.
+ */
+ public List<KeyCacheObject> keys() {
+ return keys;
+ }
+
+ /**
+ * @return Collection of row values.
+ */
+ public List<Message> values() {
+ return vals;
+ }
+
+ /**
+ * @return Update counters.
+ */
+ public GridLongList updateCounters() {
+ return updCntrs;
+ }
+ }
+
+ /**
+ * Lock request timeout object.
+ */
+ protected class LockTimeoutObject extends GridTimeoutObjectAdapter {
+ /**
+ * Default constructor.
+ */
+ LockTimeoutObject() {
+ super(timeout);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onTimeout() {
+ if (log.isDebugEnabled())
+ log.debug("Timed out waiting for lock response: " + this);
+
+ onDone(timeoutException());
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(LockTimeoutObject.class, this);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
index 2f36053..c0a3845 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
@@ -26,19 +26,23 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.internal.InvalidEnvironmentException;
import org.apache.ignite.internal.IgniteDiagnosticAware;
import org.apache.ignite.internal.IgniteDiagnosticPrepareContext;
import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.InvalidEnvironmentException;
import org.apache.ignite.internal.NodeStoppingException;
import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.processors.cache.GridCacheCompoundIdentityFuture;
import org.apache.ignite.internal.processors.cache.GridCacheFuture;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -284,6 +288,8 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
public void finish(boolean commit) {
boolean sync;
+ assert !tx.queryEnlisted() || tx.mvccSnapshot() != null;
+
if (!F.isEmpty(dhtMap) || !F.isEmpty(nearMap))
sync = finish(commit, dhtMap, nearMap);
else if (!commit && !F.isEmpty(tx.lockTransactionNodes()))
@@ -292,6 +298,22 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
// No backup or near nodes to send commit message to (just complete then).
sync = false;
+ GridLongList waitTxs = tx.mvccWaitTransactions();
+
+ if (waitTxs != null) {
+ MvccSnapshot snapshot = tx.mvccSnapshot();
+
+ assert snapshot != null;
+
+ MvccCoordinator crd = cctx.coordinators().currentCoordinator();
+
+ if (crd != null && crd.coordinatorVersion() == snapshot.coordinatorVersion()) {
+ add((IgniteInternalFuture)cctx.coordinators().waitTxsFuture(crd.nodeId(), waitTxs));
+
+ sync = true;
+ }
+ }
+
markInitialized();
if (!sync)
@@ -310,7 +332,7 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
boolean sync = tx.syncMode() == FULL_SYNC;
- if (tx.explicitLock())
+ if (tx.explicitLock() || tx.queryEnlisted())
sync = true;
boolean res = false;
@@ -348,7 +370,8 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
tx.taskNameHash(),
tx.activeCachesDeploymentEnabled(),
false,
- false);
+ false,
+ tx.mvccSnapshot());
try {
cctx.io().send(n, req, tx.ioPolicy());
@@ -396,15 +419,23 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
if (tx.onePhaseCommit())
return false;
+ assert !commit || !tx.txState().mvccEnabled(cctx) || tx.mvccSnapshot() != null || F.isEmpty(tx.writeEntries());
+
boolean sync = tx.syncMode() == FULL_SYNC;
- if (tx.explicitLock())
+ if (tx.explicitLock() || tx.queryEnlisted())
sync = true;
boolean res = false;
int miniId = 0;
+ // Do not need process active transactions on backups.
+ MvccSnapshot mvccSnapshot = tx.mvccSnapshot();
+
+ if (mvccSnapshot != null)
+ mvccSnapshot = mvccSnapshot.withoutActiveTransactions();
+
// Create mini futures.
for (GridDistributedTxMapping dhtMapping : dhtMap.values()) {
ClusterNode n = dhtMapping.primary();
@@ -413,7 +444,7 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
GridDistributedTxMapping nearMapping = nearMap.get(n.id());
- if (dhtMapping.empty() && nearMapping != null && nearMapping.empty())
+ if (!dhtMapping.queryUpdate() && dhtMapping.empty() && nearMapping != null && nearMapping.empty())
// Nothing to send.
continue;
@@ -426,6 +457,11 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
for (IgniteTxEntry e : dhtMapping.entries())
updCntrs.add(e.updateCounter());
+ Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrsMap = null;
+
+ if (dhtMapping.queryUpdate() && commit)
+ updCntrsMap = tx.updateCountersForNode(n);
+
GridDhtTxFinishRequest req = new GridDhtTxFinishRequest(
tx.nearNodeId(),
futId,
@@ -451,7 +487,9 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
tx.activeCachesDeploymentEnabled(),
updCntrs,
false,
- false);
+ false,
+ mvccSnapshot,
+ updCntrsMap);
req.writeVersion(tx.writeVersion() != null ? tx.writeVersion() : tx.xidVersion());
@@ -520,7 +558,8 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
tx.taskNameHash(),
tx.activeCachesDeploymentEnabled(),
false,
- false);
+ false,
+ mvccSnapshot);
req.writeVersion(tx.writeVersion());
@@ -565,22 +604,35 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
if (!isDone()) {
for (IgniteInternalFuture fut : futures()) {
if (!fut.isDone()) {
- MiniFuture f = (MiniFuture)fut;
-
- if (!f.node().isLocal()) {
- GridCacheVersion dhtVer = tx.xidVersion();
- GridCacheVersion nearVer = tx.nearXidVersion();
+ if (MiniFuture.class.isInstance(fut)) {
+ MiniFuture f = (MiniFuture)fut;
+
+ if (!f.node().isLocal()) {
+ GridCacheVersion dhtVer = tx.xidVersion();
+ GridCacheVersion nearVer = tx.nearXidVersion();
+
+ ctx.remoteTxInfo(f.node().id(), dhtVer, nearVer, "GridDhtTxFinishFuture " +
+ "waiting for response [node=" + f.node().id() +
+ ", topVer=" + tx.topologyVersion() +
+ ", dhtVer=" + dhtVer +
+ ", nearVer=" + nearVer +
+ ", futId=" + futId +
+ ", miniId=" + f.futId +
+ ", tx=" + tx + ']');
+
+ return;
+ }
+ }
+ else if (fut instanceof MvccFuture) {
+ MvccFuture f = (MvccFuture)fut;
- ctx.remoteTxInfo(f.node().id(), dhtVer, nearVer, "GridDhtTxFinishFuture " +
- "waiting for response [node=" + f.node().id() +
- ", topVer=" + tx.topologyVersion() +
- ", dhtVer=" + dhtVer +
- ", nearVer=" + nearVer +
- ", futId=" + futId +
- ", miniId=" + f.futId +
- ", tx=" + tx + ']');
+ if (!cctx.localNodeId().equals(f.coordinatorNodeId())) {
+ ctx.basicInfo(f.coordinatorNodeId(), "GridDhtTxFinishFuture " +
+ "waiting for mvcc coordinator reply [mvccCrdNode=" + f.coordinatorNodeId() +
+ ", loc=" + f.coordinatorNodeId().equals(cctx.localNodeId()) + ']');
- return;
+ return;
+ }
}
}
}
@@ -592,9 +644,20 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
Collection<String> futs = F.viewReadOnly(futures(), new C1<IgniteInternalFuture<?>, String>() {
@SuppressWarnings("unchecked")
@Override public String apply(IgniteInternalFuture<?> f) {
- return "[node=" + ((MiniFuture)f).node().id() +
- ", loc=" + ((MiniFuture)f).node().isLocal() +
- ", done=" + f.isDone() + "]";
+ if (f.getClass() == MiniFuture.class) {
+ return "[node=" + ((MiniFuture)f).node().id() +
+ ", loc=" + ((MiniFuture)f).node().isLocal() +
+ ", done=" + f.isDone() + "]";
+ }
+ else if (f instanceof MvccFuture) {
+ MvccFuture crdFut = (MvccFuture)f;
+
+ return "[mvccCrdNode=" + crdFut.coordinatorNodeId() +
+ ", loc=" + crdFut.coordinatorNodeId().equals(cctx.localNodeId()) +
+ ", done=" + f.isDone() + "]";
+ }
+ else
+ return f.toString();
}
});
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
index 823b5fe..2696c0e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
@@ -20,11 +20,14 @@ package org.apache.ignite.internal.processors.cache.distributed.dht;
import java.io.Externalizable;
import java.nio.ByteBuffer;
import java.util.Collection;
+import java.util.Map;
import java.util.UUID;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.GridDirectMap;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxFinishRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -67,6 +70,12 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
/** One phase commit write version. */
private GridCacheVersion writeVer;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
+ /** Map of update counters made by this tx. Mapping: cacheId -> partId -> updCntr. */
+ @GridDirectMap(keyType = Integer.class, valueType = GridDhtPartitionsUpdateCountersMap.class)
+ private Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrs;
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -122,7 +131,8 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
int taskNameHash,
boolean addDepInfo,
boolean retVal,
- boolean waitRemoteTxs
+ boolean waitRemoteTxs,
+ MvccSnapshot mvccSnapshot
) {
super(
xidVer,
@@ -151,6 +161,7 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
this.nearNodeId = nearNodeId;
this.isolation = isolation;
this.miniId = miniId;
+ this.mvccSnapshot = mvccSnapshot;
needReturnValue(retVal);
waitRemoteTransactions(waitRemoteTxs);
@@ -207,7 +218,9 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
boolean addDepInfo,
Collection<Long> updateIdxs,
boolean retVal,
- boolean waitRemoteTxs
+ boolean waitRemoteTxs,
+ MvccSnapshot mvccSnapshot,
+ Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrs
) {
this(nearNodeId,
futId,
@@ -232,7 +245,8 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
taskNameHash,
addDepInfo,
retVal,
- waitRemoteTxs);
+ waitRemoteTxs,
+ mvccSnapshot);
if (updateIdxs != null && !updateIdxs.isEmpty()) {
partUpdateCnt = new GridLongList(updateIdxs.size());
@@ -240,6 +254,15 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
for (Long idx : updateIdxs)
partUpdateCnt.add(idx);
}
+
+ this.updCntrs = updCntrs;
+ }
+
+ /**
+ * @return Counter.
+ */
+ public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
}
/**
@@ -339,6 +362,12 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
public void needReturnValue(boolean retVal) {
setFlag(retVal, NEED_RETURN_VALUE_FLAG_MASK);
}
+ /**
+ * @return Partition update counters map.
+ */
+ public Map<Integer, GridDhtPartitionsUpdateCountersMap> updateCountersMap() {
+ return updCntrs;
+ }
/** {@inheritDoc} */
@Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
@@ -368,24 +397,36 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
writer.incrementState();
case 23:
- if (!writer.writeUuid("nearNodeId", nearNodeId))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 24:
- if (!writer.writeMessage("partUpdateCnt", partUpdateCnt))
+ if (!writer.writeUuid("nearNodeId", nearNodeId))
return false;
writer.incrementState();
case 25:
- if (!writer.writeCollection("pendingVers", pendingVers, MessageCollectionItemType.MSG))
+ if (!writer.writeMessage("partUpdateCnt", partUpdateCnt))
return false;
writer.incrementState();
case 26:
+ if (!writer.writeCollection("pendingVers", pendingVers, MessageCollectionItemType.MSG))
+ return false;
+
+ writer.incrementState();
+
+ case 27:
+ if (!writer.writeMap("updCntrs", updCntrs, MessageCollectionItemType.INT, MessageCollectionItemType.MSG))
+ return false;
+
+ writer.incrementState();
+
+ case 28:
if (!writer.writeMessage("writeVer", writeVer))
return false;
@@ -428,7 +469,7 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
reader.incrementState();
case 23:
- nearNodeId = reader.readUuid("nearNodeId");
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -436,7 +477,7 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
reader.incrementState();
case 24:
- partUpdateCnt = reader.readMessage("partUpdateCnt");
+ nearNodeId = reader.readUuid("nearNodeId");
if (!reader.isLastRead())
return false;
@@ -444,7 +485,7 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
reader.incrementState();
case 25:
- pendingVers = reader.readCollection("pendingVers", MessageCollectionItemType.MSG);
+ partUpdateCnt = reader.readMessage("partUpdateCnt");
if (!reader.isLastRead())
return false;
@@ -452,6 +493,22 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
reader.incrementState();
case 26:
+ pendingVers = reader.readCollection("pendingVers", MessageCollectionItemType.MSG);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 27:
+ updCntrs = reader.readMap("updCntrs", MessageCollectionItemType.INT, MessageCollectionItemType.MSG, false);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 28:
writeVer = reader.readMessage("writeVer");
if (!reader.isLastRead())
@@ -471,7 +528,7 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 27;
+ return 29;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocal.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocal.java
index 2e19df2..a091d44 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocal.java
@@ -160,7 +160,6 @@ public class GridDhtTxLocal extends GridDhtTxLocalAdapter implements GridCacheMa
assert nearNodeId != null;
assert nearFutId != null;
- assert nearMiniId != 0;
assert nearXidVer != null;
this.nearNodeId = nearNodeId;
@@ -424,10 +423,10 @@ public class GridDhtTxLocal extends GridDhtTxLocalAdapter implements GridCacheMa
final IgniteInternalFuture<?> lockFut = tryRollbackAsync();
if (lockFut != null) {
- if (lockFut instanceof GridDhtLockFuture)
- ((GridDhtLockFuture)lockFut).onError(rollbackException());
- else {
- /**
+ if (lockFut instanceof DhtLockFuture)
+ ((DhtLockFuture<?>)lockFut).onError(rollbackException());
+ else if (!lockFut.isDone()) {
+ /*
* Prevents race with {@link GridDhtTransactionalCacheAdapter#lockAllAsync
* (GridCacheContext, ClusterNode, GridNearLockRequest, CacheEntryPredicate[])}
*/
@@ -546,6 +545,8 @@ public class GridDhtTxLocal extends GridDhtTxLocalAdapter implements GridCacheMa
public IgniteInternalFuture<IgniteInternalTx> rollbackDhtLocalAsync() {
final GridDhtTxFinishFuture fut = new GridDhtTxFinishFuture<>(cctx, this, false);
+ rollbackFuture(fut);
+
cctx.mvcc().addFuture(fut, fut.futureId());
GridDhtTxPrepareFuture prepFut = this.prepFut;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocalAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocalAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocalAdapter.java
index 604fe06..11b46a0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocalAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxLocalAdapter.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cluster.ClusterNode;
@@ -59,7 +60,6 @@ import org.apache.ignite.transactions.TransactionConcurrency;
import org.apache.ignite.transactions.TransactionIsolation;
import org.apache.ignite.transactions.TransactionState;
import org.jetbrains.annotations.Nullable;
-import java.util.concurrent.ConcurrentHashMap;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.NOOP;
import static org.apache.ignite.transactions.TransactionState.COMMITTED;
@@ -78,7 +78,7 @@ public abstract class GridDhtTxLocalAdapter extends IgniteTxLocalAdapter {
private static final long serialVersionUID = 0L;
/** Asynchronous rollback marker for lock futures. */
- protected static final IgniteInternalFuture<Boolean> ROLLBACK_FUT = new GridFutureAdapter<>();
+ public static final IgniteInternalFuture<Boolean> ROLLBACK_FUT = new GridFutureAdapter<>();
/** Lock future updater. */
private static final AtomicReferenceFieldUpdater<GridDhtTxLocalAdapter, IgniteInternalFuture> LOCK_FUT_UPD =
@@ -108,7 +108,7 @@ public abstract class GridDhtTxLocalAdapter extends IgniteTxLocalAdapter {
/** Enlist or lock future what is currently in progress. */
@SuppressWarnings("UnusedDeclaration")
@GridToStringExclude
- protected volatile IgniteInternalFuture<Boolean> lockFut;
+ protected volatile IgniteInternalFuture<?> lockFut;
/**
* Empty constructor required for {@link Externalizable}.
@@ -853,11 +853,17 @@ public abstract class GridDhtTxLocalAdapter extends IgniteTxLocalAdapter {
}
/**
+ * @return Lock future.
+ */
+ public IgniteInternalFuture<?> lockFuture() {
+ return lockFut;
+ }
+
+ /**
* Atomically updates lock future.
*
* @param oldFut Old future.
* @param newFut New future.
- *
* @return {@code true} If future was changed.
*/
public boolean updateLockFuture(IgniteInternalFuture<?> oldFut, IgniteInternalFuture<?> newFut) {
@@ -870,20 +876,21 @@ public abstract class GridDhtTxLocalAdapter extends IgniteTxLocalAdapter {
* @param cond Clear lock condition.
*/
public void clearLockFuture(@Nullable IgniteInternalFuture cond) {
- IgniteInternalFuture f = lockFut;
+ while (true) {
+ IgniteInternalFuture f = lockFut;
- if (cond != null && f != cond)
- return;
-
- lockFut = null;
+ if (f == null
+ || f == ROLLBACK_FUT
+ || (cond != null && f != cond)
+ || updateLockFuture(f, null))
+ return;
+ }
}
/**
- *
* @param f Future to finish.
* @param err Error.
* @param clearLockFut {@code True} if need to clear lock future.
- *
* @return Finished future.
*/
public <T> GridFutureAdapter<T> finishFuture(GridFutureAdapter<T> f, Throwable err, boolean clearLockFut) {
@@ -901,16 +908,13 @@ public abstract class GridDhtTxLocalAdapter extends IgniteTxLocalAdapter {
* @return Current lock future or null if it's safe to roll back.
*/
public @Nullable IgniteInternalFuture<?> tryRollbackAsync() {
- IgniteInternalFuture<Boolean> fut;
-
- while(true) {
- fut = lockFut;
-
- if (fut != null)
- return fut == ROLLBACK_FUT ? null : fut;
+ while (true) {
+ final IgniteInternalFuture fut = lockFut;
- if (updateLockFuture(null, ROLLBACK_FUT))
+ if (fut == ROLLBACK_FUT)
return null;
+ else if (updateLockFuture(fut, ROLLBACK_FUT))
+ return fut;
}
}
@@ -924,8 +928,7 @@ public abstract class GridDhtTxLocalAdapter extends IgniteTxLocalAdapter {
if (commitOnPrepare()) {
return finishFuture().chain(new CX1<IgniteInternalFuture<IgniteInternalTx>, GridNearTxPrepareResponse>() {
@Override public GridNearTxPrepareResponse applyx(IgniteInternalFuture<IgniteInternalTx> finishFut)
- throws IgniteCheckedException
- {
+ throws IgniteCheckedException {
return prepFut.get();
}
});
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
index d8e204a..8ac5419 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
@@ -51,16 +51,21 @@ import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
-import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
import org.apache.ignite.internal.processors.cache.GridCacheOperation;
import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheEntry;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxPrepareRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxPrepareResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
@@ -85,6 +90,7 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteFutureCancelledException;
+import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.lang.IgniteReducer;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.thread.IgniteThread;
@@ -256,6 +262,11 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
}
/** {@inheritDoc} */
+ @Nullable @Override public IgniteLogger logger() {
+ return log;
+ }
+
+ /** {@inheritDoc} */
@Override public IgniteUuid futureId() {
return futId;
}
@@ -394,7 +405,8 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
entryProc,
tx.resolveTaskName(),
null,
- keepBinary);
+ keepBinary,
+ null); // TODO IGNITE-7371
if (retVal || txEntry.op() == TRANSFORM) {
if (!F.isEmpty(txEntry.entryProcessors())) {
@@ -507,7 +519,8 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
/*transformClo*/null,
/*taskName*/null,
/*expiryPlc*/null,
- /*keepBinary*/true);
+ /*keepBinary*/true,
+ null); // TODO IGNITE-7371
}
if (oldVal != null)
@@ -886,6 +899,8 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
tx.onePhaseCommit(),
tx.activeCachesDeploymentEnabled());
+ res.mvccSnapshot(tx.mvccSnapshot());
+
if (prepErr == null) {
if (tx.needReturnValue() || tx.nearOnOriginatingNode() || tx.hasInterceptor())
addDhtValues(res);
@@ -1025,7 +1040,7 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
public void prepare(GridNearTxPrepareRequest req) {
assert req != null;
- if (tx.empty()) {
+ if (tx.empty() && !req.queryUpdate()) {
tx.setRollbackOnly();
onDone((GridNearTxPrepareResponse)null);
@@ -1205,6 +1220,8 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
*
*/
private void prepare0() {
+ boolean skipInit = false;
+
try {
if (tx.serializable() && tx.optimistic()) {
IgniteCheckedException err0;
@@ -1239,6 +1256,29 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
}
}
+ IgniteInternalFuture<MvccSnapshot> waitCrdCntrFut = null;
+
+ if (req.requestMvccCounter()) {
+ assert last;
+
+ assert tx.txState().mvccEnabled(cctx);
+
+ try {
+ // Request snapshot locally only because
+ // Mvcc Coordinator is expected to be local.
+ MvccSnapshot snapshot = cctx.coordinators().tryRequestSnapshotLocal(tx);
+
+ assert snapshot != null : tx.topologyVersion();
+
+ tx.mvccSnapshot(snapshot);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ onDone(e);
+
+ return;
+ }
+ }
+
onEntriesLocked();
// We are holding transaction-level locks for entries here, so we can get next write version.
@@ -1258,11 +1298,37 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
if (isDone())
return;
- if (last)
- sendPrepareRequests();
+ if (last) {
+ if (waitCrdCntrFut != null) {
+ skipInit = true;
+
+ waitCrdCntrFut.listen(new IgniteInClosure<IgniteInternalFuture<MvccSnapshot>>() {
+ @Override public void apply(IgniteInternalFuture<MvccSnapshot> fut) {
+ try {
+ fut.get();
+
+ sendPrepareRequests();
+
+ markInitialized();
+ }
+ catch (Throwable e) {
+ U.error(log, "Failed to get mvcc version for tx [txId=" + tx.nearXidVersion() +
+ ", err=" + e + ']', e);
+
+ GridNearTxPrepareResponse res = createPrepareResponse(e);
+
+ onDone(res, res.error());
+ }
+ }
+ });
+ }
+ else
+ sendPrepareRequests();
+ }
}
finally {
- markInitialized();
+ if (!skipInit)
+ markInitialized();
}
}
@@ -1280,15 +1346,23 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
}
}
+ assert !tx.txState().mvccEnabled(cctx) || !tx.onePhaseCommit() || tx.mvccSnapshot() != null;
+
int miniId = 0;
assert tx.transactionNodes() != null;
final long timeout = timeoutObj != null ? timeoutObj.timeout : 0;
+ // Do not need process active transactions on backups.
+ MvccSnapshot mvccSnapshot = tx.mvccSnapshot();
+
+ if (mvccSnapshot != null)
+ mvccSnapshot = mvccSnapshot.withoutActiveTransactions();
+
// Create mini futures.
for (GridDistributedTxMapping dhtMapping : tx.dhtMap().values()) {
- assert !dhtMapping.empty();
+ assert !dhtMapping.empty() || dhtMapping.queryUpdate();
ClusterNode n = dhtMapping.primary();
@@ -1300,7 +1374,7 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
Collection<IgniteTxEntry> dhtWrites = dhtMapping.writes();
- if (F.isEmpty(dhtWrites) && F.isEmpty(nearWrites))
+ if (!dhtMapping.queryUpdate() && F.isEmpty(dhtWrites) && F.isEmpty(nearWrites))
continue;
MiniFuture fut = new MiniFuture(n.id(), ++miniId, dhtMapping, nearMapping);
@@ -1325,7 +1399,10 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
tx.taskNameHash(),
tx.activeCachesDeploymentEnabled(),
tx.storeWriteThrough(),
- retVal);
+ retVal,
+ mvccSnapshot);
+
+ req.queryUpdate(dhtMapping.queryUpdate());
int idx = 0;
@@ -1428,7 +1505,8 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
tx.taskNameHash(),
tx.activeCachesDeploymentEnabled(),
tx.storeWriteThrough(),
- retVal);
+ retVal,
+ mvccSnapshot);
for (IgniteTxEntry entry : nearMapping.entries()) {
if (CU.writes().apply(entry)) {
@@ -1799,7 +1877,7 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
}
}
- if (dhtMapping.empty()) {
+ if (!dhtMapping.queryUpdate() && dhtMapping.empty()) {
dhtMap.remove(nodeId);
if (log.isDebugEnabled())
@@ -1830,6 +1908,10 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
try {
if (entry.initialValue(info.value(),
info.version(),
+ cacheCtx.mvccEnabled() ? ((MvccVersionAware)info).mvccVersion() : null,
+ cacheCtx.mvccEnabled() ? ((MvccUpdateVersionAware)info).newMvccVersion() : null,
+ cacheCtx.mvccEnabled() ? ((MvccVersionAware)info).mvccTxState() : TxState.NA,
+ cacheCtx.mvccEnabled() ? ((MvccUpdateVersionAware)info).newMvccTxState() : TxState.NA,
info.ttl(),
info.expireTime(),
true,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareRequest.java
index 88da7b0..a71fb6c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareRequest.java
@@ -34,6 +34,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxPrepareRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -103,6 +104,9 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
@GridDirectTransient
private List<IgniteTxKey> nearWritesCacheMissed;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/** {@code True} if remote tx should skip adding itself to completed versions map on finish. */
private boolean skipCompletedVers;
@@ -144,7 +148,8 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
int taskNameHash,
boolean addDepInfo,
boolean storeWriteThrough,
- boolean retVal) {
+ boolean retVal,
+ MvccSnapshot mvccInfo) {
super(tx,
timeout,
null,
@@ -165,6 +170,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
this.nearXidVer = nearXidVer;
this.subjId = subjId;
this.taskNameHash = taskNameHash;
+ this.mvccSnapshot = mvccInfo;
storeWriteThrough(storeWriteThrough);
needReturnValue(retVal);
@@ -177,6 +183,13 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
}
/**
+ * @return Mvcc info.
+ */
+ public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
* @return Near cache writes for which cache was not found (possible if client near cache was closed).
*/
@Nullable public List<IgniteTxKey> nearWritesCacheMissed() {
@@ -414,60 +427,66 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
writer.incrementState();
case 23:
- if (!writer.writeUuid("nearNodeId", nearNodeId))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 24:
- if (!writer.writeCollection("nearWrites", nearWrites, MessageCollectionItemType.MSG))
+ if (!writer.writeUuid("nearNodeId", nearNodeId))
return false;
writer.incrementState();
case 25:
- if (!writer.writeMessage("nearXidVer", nearXidVer))
+ if (!writer.writeCollection("nearWrites", nearWrites, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 26:
- if (!writer.writeCollection("ownedKeys", ownedKeys, MessageCollectionItemType.MSG))
+ if (!writer.writeMessage("nearXidVer", nearXidVer))
return false;
writer.incrementState();
case 27:
- if (!writer.writeCollection("ownedVals", ownedVals, MessageCollectionItemType.MSG))
+ if (!writer.writeCollection("ownedKeys", ownedKeys, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 28:
- if (!writer.writeBitSet("preloadKeys", preloadKeys))
+ if (!writer.writeCollection("ownedVals", ownedVals, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 29:
- if (!writer.writeBoolean("skipCompletedVers", skipCompletedVers))
+ if (!writer.writeBitSet("preloadKeys", preloadKeys))
return false;
writer.incrementState();
case 30:
- if (!writer.writeUuid("subjId", subjId))
+ if (!writer.writeBoolean("skipCompletedVers", skipCompletedVers))
return false;
writer.incrementState();
case 31:
- if (!writer.writeInt("taskNameHash", taskNameHash))
+ if (!writer.writeUuid("subjId", subjId))
return false;
writer.incrementState();
case 32:
+ if (!writer.writeInt("taskNameHash", taskNameHash))
+ return false;
+
+ writer.incrementState();
+
+ case 33:
if (!writer.writeMessage("topVer", topVer))
return false;
@@ -514,7 +533,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 23:
- nearNodeId = reader.readUuid("nearNodeId");
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -522,7 +541,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 24:
- nearWrites = reader.readCollection("nearWrites", MessageCollectionItemType.MSG);
+ nearNodeId = reader.readUuid("nearNodeId");
if (!reader.isLastRead())
return false;
@@ -530,7 +549,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 25:
- nearXidVer = reader.readMessage("nearXidVer");
+ nearWrites = reader.readCollection("nearWrites", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -538,7 +557,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 26:
- ownedKeys = reader.readCollection("ownedKeys", MessageCollectionItemType.MSG);
+ nearXidVer = reader.readMessage("nearXidVer");
if (!reader.isLastRead())
return false;
@@ -546,7 +565,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 27:
- ownedVals = reader.readCollection("ownedVals", MessageCollectionItemType.MSG);
+ ownedKeys = reader.readCollection("ownedKeys", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -554,7 +573,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 28:
- preloadKeys = reader.readBitSet("preloadKeys");
+ ownedVals = reader.readCollection("ownedVals", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -562,7 +581,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 29:
- skipCompletedVers = reader.readBoolean("skipCompletedVers");
+ preloadKeys = reader.readBitSet("preloadKeys");
if (!reader.isLastRead())
return false;
@@ -570,7 +589,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 30:
- subjId = reader.readUuid("subjId");
+ skipCompletedVers = reader.readBoolean("skipCompletedVers");
if (!reader.isLastRead())
return false;
@@ -578,7 +597,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 31:
- taskNameHash = reader.readInt("taskNameHash");
+ subjId = reader.readUuid("subjId");
if (!reader.isLastRead())
return false;
@@ -586,6 +605,14 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
reader.incrementState();
case 32:
+ taskNameHash = reader.readInt("taskNameHash");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 33:
topVer = reader.readMessage("topVer");
if (!reader.isLastRead())
@@ -605,7 +632,7 @@ public class GridDhtTxPrepareRequest extends GridDistributedTxPrepareRequest {
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 33;
+ return 34;
}
/** {@inheritDoc} */
[15/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 6b7eddf..9b71f37 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -69,6 +69,12 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxSelectForUpdateFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.TxTopologyVersionFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.processors.cache.query.CacheQueryPartitionInfo;
@@ -90,16 +96,20 @@ import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.GridRunningQueryInfo;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.processors.query.QueryField;
import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.processors.query.SqlClientContext;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasInnerIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2InnerIO;
import org.apache.ignite.internal.processors.query.h2.database.io.H2LeafIO;
+import org.apache.ignite.internal.processors.query.h2.database.io.H2MvccInnerIO;
+import org.apache.ignite.internal.processors.query.h2.database.io.H2MvccLeafIO;
import org.apache.ignite.internal.processors.query.h2.ddl.DdlStatementsProcessor;
import org.apache.ignite.internal.processors.query.h2.dml.DmlUtils;
import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlan;
@@ -110,10 +120,13 @@ import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAlias;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAst;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuery;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlStatement;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlTable;
import org.apache.ignite.internal.processors.query.h2.sys.SqlSystemTableEngine;
import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewBaselineNodes;
import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewNodeAttributes;
@@ -121,6 +134,7 @@ import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemViewNode
import org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor;
import org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor;
import org.apache.ignite.internal.processors.query.h2.twostep.MapQueryLazyWorker;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest;
import org.apache.ignite.internal.processors.query.h2.sys.view.SqlSystemView;
import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
@@ -130,14 +144,17 @@ import org.apache.ignite.internal.sql.SqlParseException;
import org.apache.ignite.internal.sql.SqlParser;
import org.apache.ignite.internal.sql.SqlStrictParseException;
import org.apache.ignite.internal.sql.command.SqlAlterTableCommand;
-import org.apache.ignite.internal.sql.command.SqlBulkLoadCommand;
import org.apache.ignite.internal.sql.command.SqlAlterUserCommand;
+import org.apache.ignite.internal.sql.command.SqlBeginTransactionCommand;
+import org.apache.ignite.internal.sql.command.SqlBulkLoadCommand;
import org.apache.ignite.internal.sql.command.SqlCommand;
+import org.apache.ignite.internal.sql.command.SqlCommitTransactionCommand;
import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
import org.apache.ignite.internal.sql.command.SqlCreateUserCommand;
import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
-import org.apache.ignite.internal.sql.command.SqlSetStreamingCommand;
import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
+import org.apache.ignite.internal.sql.command.SqlRollbackTransactionCommand;
+import org.apache.ignite.internal.sql.command.SqlSetStreamingCommand;
import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap;
import org.apache.ignite.internal.util.GridEmptyCloseableIterator;
import org.apache.ignite.internal.util.GridSpinBusyLock;
@@ -153,6 +170,7 @@ import org.apache.ignite.lang.IgniteBiClosure;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.marshaller.jdk.JdkMarshaller;
import org.apache.ignite.plugin.extensions.communication.Message;
@@ -180,12 +198,17 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_H2_INDEXING_CACHE_
import static org.apache.ignite.IgniteSystemProperties.IGNITE_H2_INDEXING_CACHE_THREAD_USAGE_TIMEOUT;
import static org.apache.ignite.IgniteSystemProperties.getInteger;
import static org.apache.ignite.IgniteSystemProperties.getString;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.checkActive;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccEnabled;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.tx;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.txStart;
import static org.apache.ignite.internal.processors.cache.query.GridCacheQueryType.SQL;
import static org.apache.ignite.internal.processors.cache.query.GridCacheQueryType.SQL_FIELDS;
import static org.apache.ignite.internal.processors.cache.query.GridCacheQueryType.TEXT;
import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME;
import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME;
import static org.apache.ignite.internal.processors.query.QueryUtils.VER_FIELD_NAME;
+import static org.apache.ignite.internal.processors.query.h2.PreparedStatementEx.INVOLVED_CACHES;
import static org.apache.ignite.internal.processors.query.h2.opt.DistributedJoinMode.OFF;
import static org.apache.ignite.internal.processors.query.h2.opt.DistributedJoinMode.distributedJoinMode;
import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.LOCAL;
@@ -203,13 +226,14 @@ import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType
public class IgniteH2Indexing implements GridQueryIndexing {
/** A pattern for commands having internal implementation in Ignite. */
public static final Pattern INTERNAL_CMD_RE = Pattern.compile(
- "^(create|drop)\\s+index|^alter\\s+table|^copy|^set|^(create|alter|drop)\\s+user", Pattern.CASE_INSENSITIVE);
+ "^(create|drop)\\s+index|^alter\\s+table|^copy|^set|^begin|^commit|^rollback|^(create|alter|drop)\\s+user",
+ Pattern.CASE_INSENSITIVE);
/*
* Register IO for indexes.
*/
static {
- PageIO.registerH2(H2InnerIO.VERSIONS, H2LeafIO.VERSIONS);
+ PageIO.registerH2(H2InnerIO.VERSIONS, H2LeafIO.VERSIONS, H2MvccInnerIO.VERSIONS, H2MvccLeafIO.VERSIONS);
H2ExtrasInnerIO.register();
H2ExtrasLeafIO.register();
@@ -235,19 +259,16 @@ public class IgniteH2Indexing implements GridQueryIndexing {
singletonList(new H2SqlFieldMetadata(null, null, "UPDATED", Long.class.getName(), -1, -1));
/** */
- private static final int PREPARED_STMT_CACHE_SIZE = 256;
-
- /** */
private static final int TWO_STEP_QRY_CACHE_SIZE = 1024;
- /** The period of clean up the {@link #stmtCache}. */
+ /** The period of clean up the statement cache. */
private final Long CLEANUP_STMT_CACHE_PERIOD = Long.getLong(IGNITE_H2_INDEXING_CACHE_CLEANUP_PERIOD, 10_000);
/** The period of clean up the {@link #conns}. */
@SuppressWarnings("FieldCanBeLocal")
private final Long CLEANUP_CONNECTIONS_PERIOD = 2000L;
- /** The timeout to remove entry from the {@link #stmtCache} if the thread doesn't perform any queries. */
+ /** The timeout to remove entry from the statement cache if the thread doesn't perform any queries. */
private final Long STATEMENT_CACHE_THREAD_USAGE_TIMEOUT =
Long.getLong(IGNITE_H2_INDEXING_CACHE_THREAD_USAGE_TIMEOUT, 600 * 1000);
@@ -274,7 +295,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
private String dbUrl = "jdbc:h2:mem:";
/** */
- private final ConcurrentMap<Thread, Connection> conns = new ConcurrentHashMap<>();
+ // TODO https://issues.apache.org/jira/browse/IGNITE-9062
+ private final ConcurrentMap<Thread, H2ConnectionWrapper> conns = new ConcurrentHashMap<>();
/** */
private GridMapQueryExecutor mapQryExec;
@@ -301,44 +323,39 @@ public class IgniteH2Indexing implements GridQueryIndexing {
private final H2RowCacheRegistry rowCache = new H2RowCacheRegistry();
/** */
- private final ThreadLocal<H2ConnectionWrapper> connCache = new ThreadLocal<H2ConnectionWrapper>() {
- @Override public H2ConnectionWrapper get() {
- H2ConnectionWrapper c = super.get();
+ // TODO https://issues.apache.org/jira/browse/IGNITE-9062
+ private final ThreadLocalObjectPool<H2ConnectionWrapper> connectionPool = new ThreadLocalObjectPool<>(IgniteH2Indexing.this::newConnectionWrapper, 5);
+
+ /** */
+ // TODO https://issues.apache.org/jira/browse/IGNITE-9062
+ private final ThreadLocal<ThreadLocalObjectPool.Reusable<H2ConnectionWrapper>> connCache = new ThreadLocal<ThreadLocalObjectPool.Reusable<H2ConnectionWrapper>>() {
+ @Override public ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> get() {
+ ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> reusable = super.get();
boolean reconnect = true;
try {
- reconnect = c == null || c.connection().isClosed();
+ reconnect = reusable == null || reusable.object().connection().isClosed();
}
catch (SQLException e) {
U.warn(log, "Failed to check connection status.", e);
}
if (reconnect) {
- c = initialValue();
+ reusable = initialValue();
- set(c);
-
- // Reset statement cache when new connection is created.
- stmtCache.remove(Thread.currentThread());
+ set(reusable);
}
- return c;
+ return reusable;
}
- @Override protected H2ConnectionWrapper initialValue() {
- Connection c;
+ @Override protected ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> initialValue() {
+ ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> reusableConnection = connectionPool.borrow();
- try {
- c = DriverManager.getConnection(dbUrl);
- }
- catch (SQLException e) {
- throw new IgniteSQLException("Failed to initialize DB connection: " + dbUrl, e);
- }
+ conns.put(Thread.currentThread(), reusableConnection.object());
- conns.put(Thread.currentThread(), c);
-
- return new H2ConnectionWrapper(c);
+ return reusableConnection;
}
};
@@ -357,9 +374,6 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/** */
private final ConcurrentMap<QueryTable, GridH2Table> dataTables = new ConcurrentHashMap<>();
- /** Statement cache. */
- private final ConcurrentHashMap<Thread, H2StatementCache> stmtCache = new ConcurrentHashMap<>();
-
/** */
private volatile GridBoundedConcurrentLinkedHashMap<H2TwoStepCachedQueryKey, H2TwoStepCachedQuery> twoStepCache =
new GridBoundedConcurrentLinkedHashMap<>(TWO_STEP_QRY_CACHE_SIZE);
@@ -419,6 +433,15 @@ public class IgniteH2Indexing implements GridQueryIndexing {
return sysConn;
}
+ /** */
+ private H2ConnectionWrapper newConnectionWrapper() {
+ try {
+ return new H2ConnectionWrapper(DriverManager.getConnection(dbUrl));
+ } catch (SQLException e) {
+ throw new IgniteSQLException("Failed to initialize DB connection: " + dbUrl, e);
+ }
+ }
+
/**
* @param c Connection.
* @param sql SQL.
@@ -443,7 +466,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @throws SQLException If failed.
*/
@SuppressWarnings("ConstantConditions")
- @NotNull private PreparedStatement prepareStatement(Connection c, String sql, boolean useStmtCache)
+ @NotNull public PreparedStatement prepareStatement(Connection c, String sql, boolean useStmtCache)
throws SQLException {
return prepareStatement(c, sql, useStmtCache, false);
}
@@ -468,7 +491,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
PreparedStatement stmt = cache.get(key);
- if (stmt != null && !stmt.isClosed() && !((JdbcStatement)stmt).isCancelled() &&
+ if (stmt != null && !stmt.isClosed() && !stmt.unwrap(JdbcStatement.class).isCancelled() &&
!GridSqlQueryParser.prepared(stmt).needRecompile()) {
assert stmt.getConnection() == c;
@@ -478,9 +501,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (cachedOnly)
return null;
- stmt = prepare0(c, sql);
-
- cache.put(key, stmt);
+ cache.put(key, stmt = PreparedStatementExImpl.wrap(prepare0(c, sql)));
return stmt;
}
@@ -503,36 +524,25 @@ public class IgniteH2Indexing implements GridQueryIndexing {
GridH2Table.insertHack(true);
try {
- return c.prepareStatement(sql);
+ return c.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
finally {
GridH2Table.insertHack(false);
}
}
else
- return c.prepareStatement(sql);
+ return c.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
}
/**
* @return {@link H2StatementCache} associated with current thread.
*/
@NotNull private H2StatementCache getStatementsCacheForCurrentThread() {
- Thread curThread = Thread.currentThread();
-
- H2StatementCache cache = stmtCache.get(curThread);
-
- if (cache == null) {
- H2StatementCache cache0 = new H2StatementCache(PREPARED_STMT_CACHE_SIZE);
-
- cache = stmtCache.putIfAbsent(curThread, cache0);
+ H2StatementCache statementCache = connCache.get().object().statementCache();
- if (cache == null)
- cache = cache0;
- }
-
- cache.updateLastUsage();
+ statementCache.updateLastUsage();
- return cache;
+ return statementCache;
}
/** {@inheritDoc} */
@@ -550,7 +560,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @throws IgniteCheckedException In case of error.
*/
private Connection connectionForThread(@Nullable String schema) throws IgniteCheckedException {
- H2ConnectionWrapper c = connCache.get();
+ H2ConnectionWrapper c = connCache.get().object();
if (c == null)
throw new IgniteCheckedException("Failed to get DB connection for thread (check log for details).");
@@ -724,7 +734,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* Handles SQL exception.
*/
private void onSqlException() {
- Connection conn = connCache.get().connection();
+ Connection conn = connCache.get().object().connection();
connCache.set(null);
@@ -737,8 +747,12 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/** {@inheritDoc} */
- @Override public void store(GridCacheContext cctx, GridQueryTypeDescriptor type, CacheDataRow row,
- @Nullable CacheDataRow prevRow, boolean prevRowAvailable) throws IgniteCheckedException {
+ @Override public void store(GridCacheContext cctx,
+ GridQueryTypeDescriptor type,
+ CacheDataRow row,
+ @Nullable CacheDataRow prevRow,
+ boolean prevRowAvailable) throws IgniteCheckedException
+ {
String cacheName = cctx.name();
H2TableDescriptor tbl = tableDescriptor(schema(cacheName), cacheName, type.name());
@@ -746,7 +760,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (tbl == null)
return; // Type was rejected.
- tbl.table().update(row, prevRow, prevRowAvailable);
+ tbl.table().update(row, prevRow, prevRowAvailable);
if (tbl.luceneIndex() != null) {
long expireTime = row.expireTime();
@@ -1024,76 +1038,230 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @param params Query parameters.
* @param filter Cache name and key filter.
* @param enforceJoinOrder Enforce join order of tables in the query.
+ * @param startTx Start transaction flag.
* @param timeout Query timeout in milliseconds.
* @param cancel Query cancel.
* @return Query result.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
- GridQueryFieldsResult queryLocalSqlFields(final String schemaName, final String qry,
+ public GridQueryFieldsResult queryLocalSqlFields(String schemaName, String qry, @Nullable Collection<Object> params,
+ IndexingQueryFilter filter, boolean enforceJoinOrder, boolean startTx, int timeout,
+ GridQueryCancel cancel) throws IgniteCheckedException {
+ return queryLocalSqlFields(schemaName, qry, params, filter, enforceJoinOrder, startTx, timeout, cancel, null);
+ }
+
+ /**
+ * Queries individual fields (generally used by JDBC drivers).
+ *
+ * @param schemaName Schema name.
+ * @param qry Query.
+ * @param params Query parameters.
+ * @param filter Cache name and key filter.
+ * @param enforceJoinOrder Enforce join order of tables in the query.
+ * @param startTx Start transaction flag.
+ * @param timeout Query timeout in milliseconds.
+ * @param cancel Query cancel.
+ * @param mvccTracker Query tracker.
+ * @return Query result.
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("unchecked")
+ GridQueryFieldsResult queryLocalSqlFields(final String schemaName, String qry,
@Nullable final Collection<Object> params, final IndexingQueryFilter filter, boolean enforceJoinOrder,
- final int timeout, final GridQueryCancel cancel) throws IgniteCheckedException {
- final Connection conn = connectionForSchema(schemaName);
+ boolean startTx, int timeout, final GridQueryCancel cancel,
+ MvccQueryTracker mvccTracker) throws IgniteCheckedException {
- H2Utils.setupConnection(conn, false, enforceJoinOrder);
+ GridNearTxLocal tx = null; boolean mvccEnabled = mvccEnabled(kernalContext());
- final PreparedStatement stmt = preparedStatementWithParams(conn, qry, params, true);
+ assert mvccEnabled || mvccTracker == null;
- if (GridSqlQueryParser.checkMultipleStatements(stmt))
- throw new IgniteSQLException("Multiple statements queries are not supported for local queries");
+ try {
+ final Connection conn = connectionForSchema(schemaName);
- Prepared p = GridSqlQueryParser.prepared(stmt);
+ H2Utils.setupConnection(conn, false, enforceJoinOrder);
- if (DmlStatementsProcessor.isDmlStatement(p)) {
- SqlFieldsQuery fldsQry = new SqlFieldsQuery(qry);
+ PreparedStatement stmt = preparedStatementWithParams(conn, qry, params, true);
- if (params != null)
- fldsQry.setArgs(params.toArray());
+ if (GridSqlQueryParser.checkMultipleStatements(stmt))
+ throw new IgniteSQLException("Multiple statements queries are not supported for local queries");
- fldsQry.setEnforceJoinOrder(enforceJoinOrder);
- fldsQry.setTimeout(timeout, TimeUnit.MILLISECONDS);
+ Prepared p = GridSqlQueryParser.prepared(stmt);
- return dmlProc.updateSqlFieldsLocal(schemaName, conn, p, fldsQry, filter, cancel);
- }
- else if (DdlStatementsProcessor.isDdlStatement(p))
- throw new IgniteSQLException("DDL statements are supported for the whole cluster only",
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ if (DmlStatementsProcessor.isDmlStatement(p)) {
+ SqlFieldsQuery fldsQry = new SqlFieldsQuery(qry);
- List<GridQueryFieldMetadata> meta;
+ if (params != null)
+ fldsQry.setArgs(params.toArray());
- try {
- meta = H2Utils.meta(stmt.getMetaData());
- }
- catch (SQLException e) {
- throw new IgniteCheckedException("Cannot prepare query metadata", e);
- }
+ fldsQry.setEnforceJoinOrder(enforceJoinOrder);
+ fldsQry.setTimeout(timeout, TimeUnit.MILLISECONDS);
- final GridH2QueryContext ctx = new GridH2QueryContext(nodeId, nodeId, 0, LOCAL)
- .filter(filter).distributedJoinMode(OFF);
+ return dmlProc.updateSqlFieldsLocal(schemaName, conn, p, fldsQry, filter, cancel);
+ }
+ else if (DdlStatementsProcessor.isDdlStatement(p)) {
+ throw new IgniteSQLException("DDL statements are supported for the whole cluster only.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
- return new GridQueryFieldsResultAdapter(meta, null) {
- @Override public GridCloseableIterator<List<?>> iterator() throws IgniteCheckedException {
- assert GridH2QueryContext.get() == null;
+ final GridH2QueryContext ctx = new GridH2QueryContext(nodeId, nodeId, 0, LOCAL)
+ .filter(filter).distributedJoinMode(OFF);
- GridH2QueryContext.set(ctx);
+ boolean forUpdate = GridSqlQueryParser.isForUpdateQuery(p);
- GridRunningQueryInfo run = new GridRunningQueryInfo(qryIdGen.incrementAndGet(), qry, SQL_FIELDS,
- schemaName, U.currentTimeMillis(), cancel, true);
+ if (forUpdate && !mvccEnabled)
+ throw new IgniteSQLException("SELECT FOR UPDATE query requires transactional " +
+ "cache with MVCC enabled.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
- runs.putIfAbsent(run.id(), run);
+ GridNearTxSelectForUpdateFuture sfuFut = null;
- try {
- ResultSet rs = executeSqlQueryWithTimer(stmt, conn, qry, params, timeout, cancel);
+ if (mvccEnabled) {
+ if (mvccTracker == null)
+ mvccTracker = mvccTracker(stmt, startTx);
+
+ if (mvccTracker != null) {
+ ctx.mvccSnapshot(mvccTracker.snapshot());
- return new H2FieldsIterator(rs);
+ if ((tx = checkActive(tx(this.ctx))) != null) {
+ int tm1 = (int)tx.remainingTime(), tm2 = timeout;
+
+ timeout = tm1 > 0 && tm2 > 0 ? Math.min(tm1, tm2) : Math.max(tm1, tm2);
+ }
}
- finally {
- GridH2QueryContext.clearThreadLocal();
- runs.remove(run.id());
+ if (forUpdate) {
+ if (mvccTracker == null)
+ throw new IgniteSQLException("SELECT FOR UPDATE query requires transactional " +
+ "cache with MVCC enabled.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ GridSqlStatement stmt0 = new GridSqlQueryParser(false).parse(p);
+
+ qry = GridSqlQueryParser.rewriteQueryForUpdateIfNeeded(stmt0, forUpdate = tx != null);
+
+ stmt = preparedStatementWithParams(conn, qry, params, true);
+
+ if (forUpdate) {
+ GridCacheContext cctx = mvccTracker.context();
+
+ try {
+ if (tx.topologyVersionSnapshot() == null)
+ new TxTopologyVersionFuture(tx, cctx).get();
+ }
+ catch (Exception e) {
+ throw new IgniteSQLException("Failed to lock topology for SELECT FOR UPDATE query.", e);
+ }
+
+ sfuFut = new GridNearTxSelectForUpdateFuture(cctx, tx, timeout);
+
+ sfuFut.initLocal();
+ }
}
}
- };
+
+ List<GridQueryFieldMetadata> meta;
+
+ try {
+ meta = H2Utils.meta(stmt.getMetaData());
+
+ if (forUpdate) {
+ assert meta.size() >= 1;
+
+ meta = meta.subList(0, meta.size() - 1);
+ }
+ }
+ catch (SQLException e) {
+ throw new IgniteCheckedException("Cannot prepare query metadata", e);
+ }
+
+ GridNearTxLocal tx0 = tx;
+ MvccQueryTracker mvccTracker0 = mvccTracker;
+ GridNearTxSelectForUpdateFuture sfuFut0 = sfuFut;
+ PreparedStatement stmt0 = stmt;
+ String qry0 = qry;
+ int timeout0 = timeout;
+
+ return new GridQueryFieldsResultAdapter(meta, null) {
+ @Override public GridCloseableIterator<List<?>> iterator() throws IgniteCheckedException {
+ assert GridH2QueryContext.get() == null;
+
+ GridH2QueryContext.set(ctx);
+
+ GridRunningQueryInfo run = new GridRunningQueryInfo(qryIdGen.incrementAndGet(), qry0,
+ SQL_FIELDS, schemaName, U.currentTimeMillis(), cancel, true);
+
+ runs.putIfAbsent(run.id(), run);
+
+ try {
+ ResultSet rs = executeSqlQueryWithTimer(stmt0, conn, qry0, params, timeout0, cancel);
+
+ if (sfuFut0 != null) {
+ assert tx0.mvccSnapshot() != null;
+
+ ResultSetEnlistFuture enlistFut = ResultSetEnlistFuture.future(
+ IgniteH2Indexing.this.ctx.localNodeId(),
+ tx0.nearXidVersion(),
+ tx0.mvccSnapshot(),
+ tx0.threadId(),
+ IgniteUuid.randomUuid(),
+ -1,
+ null,
+ tx0,
+ timeout0,
+ sfuFut0.cache(),
+ rs
+ );
+
+ enlistFut.listen(new IgniteInClosure<IgniteInternalFuture<Long>>() {
+ @Override public void apply(IgniteInternalFuture<Long> fut) {
+ if (fut.error() != null)
+ sfuFut0.onResult(IgniteH2Indexing.this.ctx.localNodeId(), 0L, false, fut.error());
+ else
+ sfuFut0.onResult(IgniteH2Indexing.this.ctx.localNodeId(), fut.result(), false, null);
+ }
+ });
+
+ enlistFut.init();
+
+ try {
+ sfuFut0.get();
+
+ rs.beforeFirst();
+ }
+ catch (Exception e) {
+ U.closeQuiet(rs);
+
+ throw new IgniteSQLException("Failed to obtain locks on result of SELECT FOR UPDATE.",
+ e);
+ }
+ }
+
+ return new H2FieldsIterator(rs, mvccTracker0, sfuFut0 != null);
+ }
+ catch (IgniteCheckedException | RuntimeException | Error e) {
+ try {
+ if (mvccTracker0 != null)
+ mvccTracker0.onDone();
+ }
+ catch (Exception e0) {
+ e.addSuppressed(e0);
+ }
+
+ throw e;
+ }
+ finally {
+ GridH2QueryContext.clearThreadLocal();
+
+ runs.remove(run.id());
+ }
+ }
+ };
+ }
+ catch (IgniteCheckedException | RuntimeException | Error e) {
+ if (mvccEnabled && (tx != null || (tx = tx(ctx)) != null))
+ tx.setRollbackOnly();
+
+ throw e;
+ }
}
/** {@inheritDoc} */
@@ -1171,7 +1339,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @return Prepared statement with set parameters.
* @throws IgniteCheckedException If failed.
*/
- private PreparedStatement preparedStatementWithParams(Connection conn, String sql, Collection<Object> params,
+ public PreparedStatement preparedStatementWithParams(Connection conn, String sql, Collection<Object> params,
boolean useStmtCache) throws IgniteCheckedException {
final PreparedStatement stmt;
@@ -1288,7 +1456,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @return Result.
* @throws IgniteCheckedException If failed.
*/
- private ResultSet executeSqlQueryWithTimer(PreparedStatement stmt, Connection conn, String sql,
+ public ResultSet executeSqlQueryWithTimer(PreparedStatement stmt, Connection conn, String sql,
@Nullable Collection<Object> params, int timeoutMillis, @Nullable GridQueryCancel cancel)
throws IgniteCheckedException {
long start = U.currentTimeMillis();
@@ -1344,10 +1512,12 @@ public class IgniteH2Indexing implements GridQueryIndexing {
@Override public FieldsQueryCursor<List<?>> queryLocalSqlFields(String schemaName, SqlFieldsQuery qry,
final boolean keepBinary, IndexingQueryFilter filter, GridQueryCancel cancel) throws IgniteCheckedException {
String sql = qry.getSql();
- Object[] args = qry.getArgs();
+ List<Object> params = F.asList(qry.getArgs());
+ boolean enforceJoinOrder = qry.isEnforceJoinOrder(), startTx = autoStartTx(qry);
+ int timeout = qry.getTimeout();
- final GridQueryFieldsResult res = queryLocalSqlFields(schemaName, sql, F.asList(args), filter,
- qry.isEnforceJoinOrder(), qry.getTimeout(), cancel);
+ final GridQueryFieldsResult res = queryLocalSqlFields(schemaName, sql, params, filter,
+ enforceJoinOrder, startTx, timeout, cancel);
QueryCursorImpl<List<?>> cursor = new QueryCursorImpl<>(new Iterable<List<?>>() {
@SuppressWarnings("NullableProblems")
@@ -1439,8 +1609,17 @@ public class IgniteH2Indexing implements GridQueryIndexing {
H2Utils.setupConnection(conn, false, false);
- GridH2QueryContext.set(new GridH2QueryContext(nodeId, nodeId, 0, LOCAL).filter(filter)
- .distributedJoinMode(OFF));
+ GridH2QueryContext qctx = new GridH2QueryContext(nodeId, nodeId, 0, LOCAL).filter(filter)
+ .distributedJoinMode(OFF);
+
+ PreparedStatement stmt = preparedStatementWithParams(conn, sql, params, true);
+
+ MvccQueryTracker mvccTracker = mvccTracker(stmt, false);
+
+ if (mvccTracker != null)
+ qctx.mvccSnapshot(mvccTracker.snapshot());
+
+ GridH2QueryContext.set(qctx);
GridRunningQueryInfo run = new GridRunningQueryInfo(qryIdGen.incrementAndGet(), qry, SQL, schemaName,
U.currentTimeMillis(), null, true);
@@ -1448,27 +1627,88 @@ public class IgniteH2Indexing implements GridQueryIndexing {
runs.put(run.id(), run);
try {
- ResultSet rs = executeSqlQueryWithTimer(conn, sql, params, true, 0, cancel);
+ ResultSet rs = executeSqlQueryWithTimer(stmt, conn, sql, params, 0, cancel);
return new H2KeyValueIterator(rs);
}
finally {
GridH2QueryContext.clearThreadLocal();
+ if (mvccTracker != null)
+ mvccTracker.onDone();
+
runs.remove(run.id());
}
}
/**
+ * Initialises MVCC filter and returns MVCC query tracker if needed.
+ * @param stmt Prepared statement.
+ * @param startTx Start transaction flag.
+ * @return MVCC query tracker or {@code null} if MVCC is disabled for involved caches.
+ */
+ private MvccQueryTracker mvccTracker(PreparedStatement stmt, boolean startTx) throws IgniteCheckedException {
+ List<GridCacheContext> caches;
+ try {
+ if (stmt.isWrapperFor(PreparedStatementEx.class)) {
+ PreparedStatementEx stmtEx = stmt.unwrap(PreparedStatementEx.class);
+
+ caches = stmtEx.meta(INVOLVED_CACHES);
+
+ if (caches == null)
+ stmtEx.putMeta(INVOLVED_CACHES, caches = parseInvolvedCaches(stmt));
+ }
+ else
+ caches = parseInvolvedCaches(stmt);
+ } catch (SQLException e) {
+ throw new IgniteSQLException(e);
+ }
+
+ GridCacheContext firstCctx = null;
+ boolean mvccEnabled = false;
+
+ for (GridCacheContext cctx : caches) {
+ if (firstCctx == null) {
+ firstCctx = cctx;
+ mvccEnabled = firstCctx.mvccEnabled();
+ }
+ else if (mvccEnabled != cctx.mvccEnabled())
+ throw new IllegalStateException("Using caches with different mvcc settings in same query is forbidden.");
+ }
+
+ return mvccEnabled ? MvccUtils.mvccTracker(firstCctx, startTx) : null;
+ }
+
+ /** */
+ private static List<GridCacheContext> parseInvolvedCaches(PreparedStatement stmt) {
+ GridSqlQueryParser parser = new GridSqlQueryParser(false);
+
+ parser.parse(GridSqlQueryParser.prepared(stmt));
+
+ List<GridCacheContext> involvedCaches = new ArrayList<>();
+
+ for (Object o : parser.objectsMap().values()) {
+ if (o instanceof GridSqlAlias)
+ o = GridSqlAlias.unwrap((GridSqlAst) o);
+ if (o instanceof GridSqlTable && ((GridSqlTable) o).dataTable() != null)
+ involvedCaches.add(((GridSqlTable) o).dataTable().cache());
+ }
+
+ return involvedCaches;
+ }
+
+ /**
* @param schemaName Schema name.
* @param qry Query.
* @param keepCacheObj Flag to keep cache object.
* @param enforceJoinOrder Enforce join order of tables.
+ * @param startTx Start transaction flag.
* @param timeoutMillis Query timeout.
* @param cancel Cancel object.
* @param params Query parameters.
* @param parts Partitions.
* @param lazy Lazy query execution flag.
+ * @param mvccTracker Query tracker.
* @return Iterable result.
*/
private Iterable<List<?>> runQueryTwoStep(
@@ -1476,19 +1716,33 @@ public class IgniteH2Indexing implements GridQueryIndexing {
final GridCacheTwoStepQuery qry,
final boolean keepCacheObj,
final boolean enforceJoinOrder,
+ boolean startTx,
final int timeoutMillis,
final GridQueryCancel cancel,
final Object[] params,
final int[] parts,
- final boolean lazy
- ) {
- return new Iterable<List<?>>() {
- @SuppressWarnings("NullableProblems")
- @Override public Iterator<List<?>> iterator() {
- return rdcQryExec.query(schemaName, qry, keepCacheObj, enforceJoinOrder, timeoutMillis, cancel, params,
- parts, lazy);
- }
- };
+ final boolean lazy,
+ MvccQueryTracker mvccTracker) {
+ assert !qry.mvccEnabled() || !F.isEmpty(qry.cacheIds());
+
+ try {
+ final MvccQueryTracker tracker = mvccTracker == null && qry.mvccEnabled() ?
+ MvccUtils.mvccTracker(ctx.cache().context().cacheContext(qry.cacheIds().get(0)), startTx) : mvccTracker;
+
+ if (qry.forUpdate())
+ qry.forUpdate(checkActive(tx(ctx)) != null);
+
+ return new Iterable<List<?>>() {
+ @SuppressWarnings("NullableProblems")
+ @Override public Iterator<List<?>> iterator() {
+ return rdcQryExec.query(schemaName, qry, keepCacheObj, enforceJoinOrder, timeoutMillis,
+ cancel, params, parts, lazy, tracker);
+ }
+ };
+ }
+ catch (IgniteCheckedException e) {
+ throw new CacheException(e);
+ }
}
/**
@@ -1545,7 +1799,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
fqry.setTimeout(qry.getTimeout(), TimeUnit.MILLISECONDS);
final QueryCursor<List<?>> res =
- querySqlFields(schemaName, fqry, null, keepBinary, true, null).get(0);
+ querySqlFields(schemaName, fqry, null, keepBinary, true, null, null).get(0);
final Iterable<Cache.Entry<K, V>> converted = new Iterable<Cache.Entry<K, V>>() {
@Override public Iterator<Cache.Entry<K, V>> iterator() {
@@ -1581,21 +1835,22 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* Try executing query using native facilities.
*
* @param schemaName Schema name.
- * @param sql Query.
+ * @param qry Query.
* @param cliCtx Client context, or {@code null} if not applicable.
* @return Result or {@code null} if cannot parse/process this query.
*/
- private List<FieldsQueryCursor<List<?>>> tryQueryDistributedSqlFieldsNative(String schemaName, String sql,
+ @SuppressWarnings({"ConstantConditions", "StatementWithEmptyBody"})
+ private List<FieldsQueryCursor<List<?>>> tryQueryDistributedSqlFieldsNative(String schemaName, SqlFieldsQuery qry,
@Nullable SqlClientContext cliCtx) {
// Heuristic check for fast return.
- if (!INTERNAL_CMD_RE.matcher(sql.trim()).find())
+ if (!INTERNAL_CMD_RE.matcher(qry.getSql().trim()).find())
return null;
// Parse.
SqlCommand cmd;
try {
- SqlParser parser = new SqlParser(schemaName, sql);
+ SqlParser parser = new SqlParser(schemaName, qry.getSql());
cmd = parser.nextCommand();
@@ -1603,16 +1858,16 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (parser.nextCommand() != null)
return null;
- // Currently supported commands are:
- // CREATE/DROP INDEX
- // COPY
- // ALTER TABLE
- // SET STREAMING
- // CREATE/ALTER/DROP USER
- if (!(cmd instanceof SqlCreateIndexCommand || cmd instanceof SqlDropIndexCommand
- || cmd instanceof SqlAlterTableCommand || cmd instanceof SqlBulkLoadCommand
+ if (!(cmd instanceof SqlCreateIndexCommand
+ || cmd instanceof SqlDropIndexCommand
+ || cmd instanceof SqlBeginTransactionCommand
+ || cmd instanceof SqlCommitTransactionCommand
+ || cmd instanceof SqlRollbackTransactionCommand
+ || cmd instanceof SqlBulkLoadCommand
+ || cmd instanceof SqlAlterTableCommand
|| cmd instanceof SqlSetStreamingCommand
- || cmd instanceof SqlCreateUserCommand || cmd instanceof SqlAlterUserCommand
+ || cmd instanceof SqlCreateUserCommand
+ || cmd instanceof SqlAlterUserCommand
|| cmd instanceof SqlDropUserCommand))
return null;
}
@@ -1622,7 +1877,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
catch (Exception e) {
// Cannot parse, return.
if (log.isDebugEnabled())
- log.debug("Failed to parse SQL with native parser [qry=" + sql + ", err=" + e + ']');
+ log.debug("Failed to parse SQL with native parser [qry=" + qry.getSql() + ", err=" + e + ']');
if (!IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_SQL_PARSER_DISABLE_H2_FALLBACK))
return null;
@@ -1632,36 +1887,40 @@ public class IgniteH2Indexing implements GridQueryIndexing {
if (e instanceof SqlParseException)
code = ((SqlParseException)e).code();
- throw new IgniteSQLException("Failed to parse DDL statement: " + sql + ": " + e.getMessage(),
+ throw new IgniteSQLException("Failed to parse DDL statement: " + qry.getSql() + ": " + e.getMessage(),
code, e);
}
// Execute.
- if (cmd instanceof SqlBulkLoadCommand) {
- FieldsQueryCursor<List<?>> cursor = dmlProc.runNativeDmlStatement(sql, cmd);
-
- return Collections.singletonList(cursor);
- }
- else if (cmd instanceof SqlSetStreamingCommand) {
- if (cliCtx == null)
- throw new IgniteSQLException("SET STREAMING command can only be executed from JDBC or ODBC driver.");
-
- SqlSetStreamingCommand setCmd = (SqlSetStreamingCommand)cmd;
-
- boolean on = setCmd.isTurnOn();
-
- if (on)
- cliCtx.enableStreaming(setCmd.allowOverwrite(), setCmd.flushFrequency(),
- setCmd.perNodeBufferSize(), setCmd.perNodeParallelOperations(), setCmd.isOrdered());
+ try {
+ if (cmd instanceof SqlCreateIndexCommand
+ || cmd instanceof SqlDropIndexCommand
+ || cmd instanceof SqlAlterTableCommand
+ || cmd instanceof SqlCreateUserCommand
+ || cmd instanceof SqlAlterUserCommand
+ || cmd instanceof SqlDropUserCommand)
+ return Collections.singletonList(ddlProc.runDdlStatement(qry.getSql(), cmd));
+ else if (cmd instanceof SqlBulkLoadCommand)
+ return Collections.singletonList(dmlProc.runNativeDmlStatement(qry.getSql(), cmd));
+ else if (cmd instanceof SqlSetStreamingCommand) {
+ if (cliCtx == null)
+ throw new IgniteSQLException("SET STREAMING command can only be executed from JDBC or ODBC driver.");
+
+ SqlSetStreamingCommand setCmd = (SqlSetStreamingCommand)cmd;
+
+ if (setCmd.isTurnOn())
+ cliCtx.enableStreaming(setCmd.allowOverwrite(), setCmd.flushFrequency(),
+ setCmd.perNodeBufferSize(), setCmd.perNodeParallelOperations(), setCmd.isOrdered());
+ else
+ cliCtx.disableStreaming();
+ }
else
- cliCtx.disableStreaming();
+ processTxCommand(cmd, qry);
return Collections.singletonList(H2Utils.zeroCursor());
}
- else {
- FieldsQueryCursor<List<?>> cursor = ddlProc.runDdlStatement(sql, cmd);
-
- return Collections.singletonList(cursor);
+ catch (IgniteCheckedException e) {
+ throw new IgniteSQLException("Failed to execute DDL statement [stmt=" + qry.getSql() + ']', e);
}
}
@@ -1679,88 +1938,207 @@ public class IgniteH2Indexing implements GridQueryIndexing {
IgniteQueryErrorCode.STMT_TYPE_MISMATCH);
}
+ /**
+ * Process transactional command.
+ * @param cmd Command.
+ * @param qry Query.
+ * @throws IgniteCheckedException if failed.
+ */
+ private void processTxCommand(SqlCommand cmd, SqlFieldsQuery qry) throws IgniteCheckedException {
+ if (!mvccEnabled(ctx))
+ throw new IgniteSQLException("MVCC must be enabled in order to invoke transactional operation: " +
+ qry.getSql(), IgniteQueryErrorCode.MVCC_DISABLED);
+
+ NestedTxMode nestedTxMode = qry instanceof SqlFieldsQueryEx ? ((SqlFieldsQueryEx)qry).getNestedTxMode() :
+ NestedTxMode.DEFAULT;
+
+ GridNearTxLocal tx = tx(ctx);
+
+ if (cmd instanceof SqlBeginTransactionCommand) {
+ if (tx != null) {
+ if (nestedTxMode == null)
+ nestedTxMode = NestedTxMode.DEFAULT;
+
+ switch (nestedTxMode) {
+ case COMMIT:
+ doCommit(tx);
+
+ txStart(ctx, qry.getTimeout());
+
+ break;
+
+ case IGNORE:
+ log.warning("Transaction has already been started, ignoring BEGIN command.");
+
+ break;
+
+ case ERROR:
+ throw new IgniteSQLException("Transaction has already been started.",
+ IgniteQueryErrorCode.TRANSACTION_EXISTS);
+
+ default:
+ throw new IgniteSQLException("Unexpected nested transaction handling mode: " +
+ nestedTxMode.name());
+ }
+ }
+ else
+ txStart(ctx, qry.getTimeout());
+ }
+ else if (cmd instanceof SqlCommitTransactionCommand) {
+ // Do nothing if there's no transaction.
+ if (tx != null)
+ doCommit(tx);
+ }
+ else {
+ assert cmd instanceof SqlRollbackTransactionCommand;
+
+ // Do nothing if there's no transaction.
+ if (tx != null)
+ doRollback(tx);
+ }
+ }
+
+ /**
+ * Commit and properly close transaction.
+ * @param tx Transaction.
+ * @throws IgniteCheckedException if failed.
+ */
+ @SuppressWarnings("ThrowFromFinallyBlock")
+ private void doCommit(@NotNull GridNearTxLocal tx) throws IgniteCheckedException {
+ try {
+ if (!tx.isRollbackOnly())
+ tx.commit();
+ }
+ finally {
+ closeTx(tx);
+ }
+ }
+
+ /**
+ * Rollback and properly close transaction.
+ * @param tx Transaction.
+ * @throws IgniteCheckedException if failed.
+ */
+ @SuppressWarnings("ThrowFromFinallyBlock")
+ private void doRollback(@NotNull GridNearTxLocal tx) throws IgniteCheckedException {
+ try {
+ tx.rollback();
+ }
+ finally {
+ closeTx(tx);
+ }
+ }
+
+ /**
+ * Properly close transaction.
+ * @param tx Transaction.
+ * @throws IgniteCheckedException if failed.
+ */
+ private void closeTx(@NotNull GridNearTxLocal tx) throws IgniteCheckedException {
+ try {
+ tx.close();
+ }
+ finally {
+ ctx.cache().context().tm().resetContext();
+ }
+ }
/** {@inheritDoc} */
@SuppressWarnings({"StringEquality", "unchecked"})
@Override public List<FieldsQueryCursor<List<?>>> querySqlFields(String schemaName, SqlFieldsQuery qry,
- @Nullable SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, GridQueryCancel cancel) {
- List<FieldsQueryCursor<List<?>>> res = tryQueryDistributedSqlFieldsNative(schemaName, qry.getSql(), cliCtx);
+ @Nullable SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, MvccQueryTracker tracker,
+ GridQueryCancel cancel) {
+ boolean mvccEnabled = mvccEnabled(ctx), startTx = autoStartTx(qry);
- if (res != null)
- return res;
+ try {
+ List<FieldsQueryCursor<List<?>>> res = tryQueryDistributedSqlFieldsNative(schemaName, qry, cliCtx);
- {
- // First, let's check if we already have a two-step query for this statement...
- H2TwoStepCachedQueryKey cachedQryKey = new H2TwoStepCachedQueryKey(schemaName, qry.getSql(),
- qry.isCollocated(), qry.isDistributedJoins(), qry.isEnforceJoinOrder(), qry.isLocal());
+ if (res != null)
+ return res;
- H2TwoStepCachedQuery cachedQry;
+ {
+ // First, let's check if we already have a two-step query for this statement...
+ H2TwoStepCachedQueryKey cachedQryKey = new H2TwoStepCachedQueryKey(schemaName, qry.getSql(),
+ qry.isCollocated(), qry.isDistributedJoins(), qry.isEnforceJoinOrder(), qry.isLocal());
- if ((cachedQry = twoStepCache.get(cachedQryKey)) != null) {
- checkQueryType(qry, true);
+ H2TwoStepCachedQuery cachedQry;
- GridCacheTwoStepQuery twoStepQry = cachedQry.query().copy();
+ if ((cachedQry = twoStepCache.get(cachedQryKey)) != null) {
+ checkQueryType(qry, true);
- List<GridQueryFieldMetadata> meta = cachedQry.meta();
+ GridCacheTwoStepQuery twoStepQry = cachedQry.query().copy();
- res = Collections.singletonList(doRunDistributedQuery(schemaName, qry, twoStepQry, meta, keepBinary,
- cancel));
+ List<GridQueryFieldMetadata> meta = cachedQry.meta();
- if (!twoStepQry.explain())
- twoStepCache.putIfAbsent(cachedQryKey, new H2TwoStepCachedQuery(meta, twoStepQry.copy()));
+ res = Collections.singletonList(doRunDistributedQuery(schemaName, qry, twoStepQry, meta, keepBinary,
+ startTx, tracker, cancel));
- return res;
+ if (!twoStepQry.explain())
+ twoStepCache.putIfAbsent(cachedQryKey, new H2TwoStepCachedQuery(meta, twoStepQry.copy()));
+
+ return res;
+ }
}
- }
- {
- // Second, let's check if we already have a parsed statement...
- PreparedStatement cachedStmt;
+ {
+ // Second, let's check if we already have a parsed statement...
+ PreparedStatement cachedStmt;
- if ((cachedStmt = cachedStatement(connectionForSchema(schemaName), qry.getSql())) != null) {
- Prepared prepared = GridSqlQueryParser.prepared(cachedStmt);
+ if ((cachedStmt = cachedStatement(connectionForSchema(schemaName), qry.getSql())) != null) {
+ Prepared prepared = GridSqlQueryParser.prepared(cachedStmt);
- // We may use this cached statement only for local queries and non queries.
- if (qry.isLocal() || !prepared.isQuery())
- return (List<FieldsQueryCursor<List<?>>>)doRunPrepared(schemaName, prepared, qry, null,
- null, keepBinary, cancel);
+ // We may use this cached statement only for local queries and non queries.
+ if (qry.isLocal() || !prepared.isQuery())
+ return (List<FieldsQueryCursor<List<?>>>)doRunPrepared(schemaName, prepared, qry, null, null,
+ keepBinary, startTx, tracker, cancel);
+ }
}
- }
- res = new ArrayList<>(1);
+ res = new ArrayList<>(1);
- int firstArg = 0;
+ int firstArg = 0;
- String remainingSql = qry.getSql();
+ String remainingSql = qry.getSql();
- while (remainingSql != null) {
- ParsingResult parseRes = parseAndSplit(schemaName,
- remainingSql != qry.getSql() ? cloneFieldsQuery(qry).setSql(remainingSql) : qry, firstArg);
+ while (remainingSql != null) {
+ ParsingResult parseRes = parseAndSplit(schemaName,
+ remainingSql != qry.getSql() ? cloneFieldsQuery(qry).setSql(remainingSql) : qry, firstArg);
- // Let's avoid second reflection getter call by returning Prepared object too
- Prepared prepared = parseRes.prepared();
+ // Let's avoid second reflection getter call by returning Prepared object too
+ Prepared prepared = parseRes.prepared();
- GridCacheTwoStepQuery twoStepQry = parseRes.twoStepQuery();
+ GridCacheTwoStepQuery twoStepQry = parseRes.twoStepQuery();
- List<GridQueryFieldMetadata> meta = parseRes.meta();
+ List<GridQueryFieldMetadata> meta = parseRes.meta();
- SqlFieldsQuery newQry = parseRes.newQuery();
+ SqlFieldsQuery newQry = parseRes.newQuery();
- remainingSql = parseRes.remainingSql();
+ remainingSql = parseRes.remainingSql();
- if (remainingSql != null && failOnMultipleStmts)
- throw new IgniteSQLException("Multiple statements queries are not supported");
+ if (remainingSql != null && failOnMultipleStmts)
+ throw new IgniteSQLException("Multiple statements queries are not supported");
- firstArg += prepared.getParameters().size();
+ firstArg += prepared.getParameters().size();
- res.addAll(doRunPrepared(schemaName, prepared, newQry, twoStepQry, meta, keepBinary, cancel));
+ res.addAll(doRunPrepared(schemaName, prepared, newQry, twoStepQry, meta, keepBinary, startTx, tracker,
+ cancel));
- if (parseRes.twoStepQuery() != null && parseRes.twoStepQueryKey() != null &&
+ if (parseRes.twoStepQuery() != null && parseRes.twoStepQueryKey() != null &&
!parseRes.twoStepQuery().explain())
- twoStepCache.putIfAbsent(parseRes.twoStepQueryKey(), new H2TwoStepCachedQuery(meta, twoStepQry.copy()));
+ twoStepCache.putIfAbsent(parseRes.twoStepQueryKey(), new H2TwoStepCachedQuery(meta,
+ twoStepQry.copy()));
+ }
+
+ return res;
}
+ catch (RuntimeException | Error e) {
+ GridNearTxLocal tx;
- return res;
+ if (mvccEnabled && (tx = tx(ctx)) != null)
+ tx.setRollbackOnly();
+
+ throw e;
+ }
}
/**
@@ -1771,12 +2149,15 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @param twoStepQry Two-step query if this query must be executed in a distributed way.
* @param meta Metadata for {@code twoStepQry}.
* @param keepBinary Whether binary objects must not be deserialized automatically.
- * @param cancel Query cancel state holder. @return Query result.
+ * @param startTx Start transactionq flag.
+ * @param tracker MVCC tracker.
+ * @param cancel Query cancel state holder.
+ * @return Query result.
*/
@SuppressWarnings("unchecked")
private List<? extends FieldsQueryCursor<List<?>>> doRunPrepared(String schemaName, Prepared prepared,
- SqlFieldsQuery qry, GridCacheTwoStepQuery twoStepQry,
- List<GridQueryFieldMetadata> meta, boolean keepBinary, GridQueryCancel cancel) {
+ SqlFieldsQuery qry, GridCacheTwoStepQuery twoStepQry, List<GridQueryFieldMetadata> meta, boolean keepBinary,
+ boolean startTx, MvccQueryTracker tracker, GridQueryCancel cancel) {
String sqlQry = qry.getSql();
boolean loc = qry.isLocal();
@@ -1842,7 +2223,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
checkQueryType(qry, true);
return Collections.singletonList(doRunDistributedQuery(schemaName, qry, twoStepQry, meta, keepBinary,
- cancel));
+ startTx, tracker, cancel));
}
// We've encountered a local query, let's just run it.
@@ -1879,7 +2260,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
PreparedStatement stmt = prepareStatementAndCaches(c, qry.getSql());
if (loc && GridSqlQueryParser.checkMultipleStatements(stmt))
- throw new IgniteSQLException("Multiple statements queries are not supported for local queries");
+ throw new IgniteSQLException("Multiple statements queries are not supported for local queries.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
GridSqlQueryParser.PreparedWithRemaining prep = GridSqlQueryParser.preparedWithRemaining(stmt);
@@ -1980,7 +2362,9 @@ public class IgniteH2Indexing implements GridQueryIndexing {
.distributedJoinMode(distributedJoinMode(qry.isLocal(), qry.isDistributedJoins())));
try {
- return new ParsingResult(prepared, newQry, remainingSql, split(prepared, newQry),
+ GridCacheTwoStepQuery twoStepQry = split(prepared, newQry);
+
+ return new ParsingResult(prepared, newQry, remainingSql, twoStepQry,
cachedQryKey, H2Utils.meta(stmt.getMetaData()));
}
catch (IgniteCheckedException e) {
@@ -2041,18 +2425,77 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
+ * @param qry Sql fields query.autoStartTx(qry)
+ * @return {@code True} if need to start transaction.
+ */
+ public boolean autoStartTx(SqlFieldsQuery qry) {
+ if (!mvccEnabled(ctx))
+ return false;
+
+ return qry instanceof SqlFieldsQueryEx && !((SqlFieldsQueryEx)qry).isAutoCommit() && tx(ctx) == null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public UpdateSourceIterator<?> prepareDistributedUpdate(GridCacheContext<?, ?> cctx, int[] ids,
+ int[] parts, String schema, String qry, Object[] params, int flags,
+ int pageSize, int timeout, AffinityTopologyVersion topVer,
+ MvccSnapshot mvccSnapshot, GridQueryCancel cancel) throws IgniteCheckedException {
+
+ SqlFieldsQuery fldsQry = new SqlFieldsQuery(qry);
+
+ if (params != null)
+ fldsQry.setArgs(params);
+
+ fldsQry.setEnforceJoinOrder(isFlagSet(flags, GridH2QueryRequest.FLAG_ENFORCE_JOIN_ORDER));
+ fldsQry.setTimeout(timeout, TimeUnit.MILLISECONDS);
+ fldsQry.setPageSize(pageSize);
+ fldsQry.setLocal(true);
+
+ boolean loc = true;
+
+ final boolean replicated = isFlagSet(flags, GridH2QueryRequest.FLAG_REPLICATED);
+
+ GridCacheContext<?, ?> cctx0;
+
+ if (!replicated
+ && !F.isEmpty(ids)
+ && (cctx0 = CU.firstPartitioned(cctx.shared(), ids)) != null
+ && cctx0.config().getQueryParallelism() > 1) {
+ fldsQry.setDistributedJoins(true);
+
+ loc = false;
+ }
+
+ Connection conn = connectionForSchema(schema);
+
+ H2Utils.setupConnection(conn, false, fldsQry.isEnforceJoinOrder());
+
+ PreparedStatement stmt = preparedStatementWithParams(conn, fldsQry.getSql(),
+ F.asList(fldsQry.getArgs()), true);
+
+ return dmlProc.prepareDistributedUpdate(schema, conn, stmt, fldsQry, backupFilter(topVer, parts), cancel, loc,
+ topVer, mvccSnapshot);
+ }
+
+ private boolean isFlagSet(int flags, int flag) {
+ return (flags & flag) == flag;
+ }
+
+ /**
* Run distributed query on detected set of partitions.
* @param schemaName Schema name.
* @param qry Original query.
* @param twoStepQry Two-step query.
* @param meta Metadata to set to cursor.
* @param keepBinary Keep binary flag.
+ * @param startTx Start transaction flag.
+ * @param mvccTracker Query tracker.
* @param cancel Cancel handler.
* @return Cursor representing distributed query result.
*/
private FieldsQueryCursor<List<?>> doRunDistributedQuery(String schemaName, SqlFieldsQuery qry,
GridCacheTwoStepQuery twoStepQry, List<GridQueryFieldMetadata> meta, boolean keepBinary,
- GridQueryCancel cancel) {
+ boolean startTx, MvccQueryTracker mvccTracker, GridQueryCancel cancel) {
if (log.isDebugEnabled())
log.debug("Parsed query: `" + qry.getSql() + "` into two step query: " + twoStepQry);
@@ -2074,8 +2517,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
QueryCursorImpl<List<?>> cursor = new QueryCursorImpl<>(
- runQueryTwoStep(schemaName, twoStepQry, keepBinary, qry.isEnforceJoinOrder(), qry.getTimeout(), cancel,
- qry.getArgs(), partitions, qry.isLazy()), cancel);
+ runQueryTwoStep(schemaName, twoStepQry, keepBinary, qry.isEnforceJoinOrder(), startTx, qry.getTimeout(),
+ cancel, qry.getArgs(), partitions, qry.isLazy(), mvccTracker), cancel);
cursor.fieldsMeta(meta);
@@ -2141,10 +2584,11 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
- * @param cacheIds Caches identifiers.
+ * @param cacheIds Cache IDs.
+ * @param twoStepQry Query.
* @throws IllegalStateException if segmented indices used with non-segmented indices.
*/
- private void checkCacheIndexSegmentation(Collection<Integer> cacheIds) {
+ private void processCaches(List<Integer> cacheIds, GridCacheTwoStepQuery twoStepQry) {
if (cacheIds.isEmpty())
return; // Nothing to check
@@ -2152,11 +2596,21 @@ public class IgniteH2Indexing implements GridQueryIndexing {
int expectedParallelism = 0;
- for (Integer cacheId : cacheIds) {
+ boolean mvccEnabled = false;
+
+ for (int i = 0; i < cacheIds.size(); i++) {
+ Integer cacheId = cacheIds.get(i);
+
GridCacheContext cctx = sharedCtx.cacheContext(cacheId);
assert cctx != null;
+ if (i == 0)
+ mvccEnabled = cctx.mvccEnabled();
+ else if (cctx.mvccEnabled() != mvccEnabled)
+ throw new IllegalStateException("Using caches with different mvcc settings in same query is " +
+ "forbidden.");
+
if (!cctx.isPartitioned())
continue;
@@ -2167,6 +2621,18 @@ public class IgniteH2Indexing implements GridQueryIndexing {
"forbidden.");
}
}
+
+ twoStepQry.mvccEnabled(mvccEnabled);
+
+ if (twoStepQry.forUpdate()) {
+ if (cacheIds.size() != 1)
+ throw new IgniteSQLException("SELECT FOR UPDATE is supported only for queries " +
+ "that involve single transactional cache.");
+
+ if (!mvccEnabled)
+ throw new IgniteSQLException("SELECT FOR UPDATE query requires transactional cache " +
+ "with MVCC enabled.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
}
/**
@@ -2449,10 +2915,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
/**
* Gets collection of table for given schema name.
*
- * @param cacheName Schema name.
+ * @param cacheName Cache name.
* @return Collection of table descriptors.
*/
- private Collection<H2TableDescriptor> tables(String cacheName) {
+ Collection<H2TableDescriptor> tables(String cacheName) {
H2Schema s = schemas.get(schema(cacheName));
if (s == null)
@@ -2481,28 +2947,32 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
- * Called periodically by {@link GridTimeoutProcessor} to clean up the {@link #stmtCache}.
+ * Called periodically by {@link GridTimeoutProcessor} to clean up the statement cache.
*/
private void cleanupStatementCache() {
- long cur = U.currentTimeMillis();
+ long now = U.currentTimeMillis();
- for (Iterator<Map.Entry<Thread, H2StatementCache>> it = stmtCache.entrySet().iterator(); it.hasNext(); ) {
- Map.Entry<Thread, H2StatementCache> entry = it.next();
+ for (Iterator<Map.Entry<Thread, H2ConnectionWrapper>> it = conns.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry<Thread, H2ConnectionWrapper> entry = it.next();
Thread t = entry.getKey();
- if (t.getState() == Thread.State.TERMINATED
- || cur - entry.getValue().lastUsage() > STATEMENT_CACHE_THREAD_USAGE_TIMEOUT)
+ if (t.getState() == Thread.State.TERMINATED) {
+ U.close(entry.getValue(), log);
+
it.remove();
+ }
+ else if (now - entry.getValue().statementCache().lastUsage() > STATEMENT_CACHE_THREAD_USAGE_TIMEOUT)
+ entry.getValue().clearStatementCache();
}
}
/**
- * Called periodically by {@link GridTimeoutProcessor} to clean up the {@link #stmtCache}.
+ * Called periodically by {@link GridTimeoutProcessor} to clean up the {@link #conns}.
*/
private void cleanupConnections() {
- for (Iterator<Map.Entry<Thread, Connection>> it = conns.entrySet().iterator(); it.hasNext(); ) {
- Map.Entry<Thread, Connection> entry = it.next();
+ for (Iterator<Map.Entry<Thread, H2ConnectionWrapper>> it = conns.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry<Thread, H2ConnectionWrapper> entry = it.next();
Thread t = entry.getKey();
@@ -2515,6 +2985,24 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/**
+ * Removes from cache and returns associated with current thread connection.
+ * @return Connection associated with current thread.
+ */
+ public ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> detach() {
+ Thread key = Thread.currentThread();
+
+ ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> reusableConnection = connCache.get();
+
+ H2ConnectionWrapper connection = conns.remove(key);
+
+ connCache.remove();
+
+ assert reusableConnection.object().connection() == connection.connection();
+
+ return reusableConnection;
+ }
+
+ /**
* Rebuild indexes from hash index.
*
* @param cacheName Cache name.
@@ -2529,7 +3017,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
SchemaIndexCacheVisitor visitor = new SchemaIndexCacheVisitorImpl(cctx);
- visitor.visit(new RebuldIndexFromHashClosure(qryMgr));
+ visitor.visit(new RebuildIndexFromHashClosure(qryMgr, cctx.mvccEnabled()));
for (H2TableDescriptor tblDesc : tables(cacheName))
tblDesc.table().markRebuildFromHashInProgress(false);
@@ -2879,7 +3367,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
mapQryExec.cancelLazyWorkers();
- for (Connection c : conns.values())
+ for (H2ConnectionWrapper c : conns.values())
U.close(c, log);
conns.clear();
@@ -2915,6 +3403,17 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
/** {@inheritDoc} */
+ @Override public void onClientDisconnect() throws IgniteCheckedException {
+ if (!mvccEnabled(ctx))
+ return;
+
+ GridNearTxLocal tx = tx(ctx);
+
+ if (tx != null)
+ doRollback(tx);
+ }
+
+ /** {@inheritDoc} */
@Override public void registerCache(String cacheName, String schemaName, GridCacheContext<?, ?> cctx)
throws IgniteCheckedException {
rowCache.onCacheRegistered(cctx);
@@ -2978,7 +3477,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
}
}
- stmtCache.clear();
+ conns.values().forEach(H2ConnectionWrapper::clearStatementCache);
for (H2TableDescriptor tbl : rmvTbls) {
for (Index idx : tbl.table().getIndexes())
@@ -3138,14 +3637,14 @@ public class IgniteH2Indexing implements GridQueryIndexing {
@Override public void cancelAllQueries() {
mapQryExec.cancelLazyWorkers();
- for (Connection c : conns.values())
+ for (H2ConnectionWrapper c : conns.values())
U.close(c, log);
}
/**
* @return Per-thread connections.
*/
- public Map<Thread, Connection> perThreadConnections() {
+ public Map<Thread, ?> perThreadConnections() {
return conns;
}
@@ -3182,7 +3681,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
//Prohibit usage indices with different numbers of segments in same query.
List<Integer> cacheIds = new ArrayList<>(caches0);
- checkCacheIndexSegmentation(cacheIds);
+ processCaches(cacheIds, twoStepQry);
return cacheIds;
}
@@ -3208,22 +3707,4 @@ public class IgniteH2Indexing implements GridQueryIndexing {
private interface ClIter<X> extends AutoCloseable, Iterator<X> {
// No-op.
}
-
- /** */
- private static class RebuldIndexFromHashClosure implements SchemaIndexCacheVisitorClosure {
- /** */
- private final GridCacheQueryManager qryMgr;
-
- /**
- * @param qryMgr Query manager.
- */
- RebuldIndexFromHashClosure(GridCacheQueryManager qryMgr) {
- this.qryMgr = qryMgr;
- }
-
- /** {@inheritDoc} */
- @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
- qryMgr.store(row, null, false);
- }
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/NearResultSetEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/NearResultSetEnlistFuture.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/NearResultSetEnlistFuture.java
new file mode 100644
index 0000000..1856430
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/NearResultSetEnlistFuture.java
@@ -0,0 +1,62 @@
+/*
+ * 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.query.h2;
+
+import java.sql.ResultSet;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class NearResultSetEnlistFuture extends GridNearTxQueryResultsEnlistFuture implements ResultSetEnlistFuture {
+ /** */
+ private static final long serialVersionUID = 877907044489718378L;
+
+ /**
+ * @param nearNodeId Near node ID.
+ * @param nearLockVer Near lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param threadId Thread ID.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini future id.
+ * @param parts Partitions.
+ * @param tx Transaction.
+ * @param timeout Lock acquisition timeout.
+ * @param cctx Cache context.
+ * @param rs Result set to process.
+ */
+ public NearResultSetEnlistFuture(UUID nearNodeId, GridCacheVersion nearLockVer, MvccSnapshot mvccSnapshot,
+ long threadId, IgniteUuid nearFutId, int nearMiniId, @Nullable int[] parts, GridDhtTxLocalAdapter tx,
+ long timeout, GridCacheContext<?, ?> cctx, ResultSet rs) {
+ super(cctx, (GridNearTxLocal)tx, timeout, ResultSetEnlistFuture.createIterator(rs), 0, true);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override public void onError(Throwable error) {
+ onDone(error);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementEx.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementEx.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementEx.java
new file mode 100644
index 0000000..d254971
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementEx.java
@@ -0,0 +1,45 @@
+/*
+ * 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.query.h2;
+
+import java.sql.PreparedStatement;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public interface PreparedStatementEx extends PreparedStatement {
+ /** */
+ static final AtomicInteger metaIdGenerator = new AtomicInteger();
+
+ /** */
+ static final int INVOLVED_CACHES = metaIdGenerator.getAndIncrement();
+
+ /**
+ * @param id Metadata key.
+ * @return Attached metadata.
+ */
+ @Nullable <T> T meta(int id);
+
+ /**
+ * @param id Metadata key.
+ * @param metaObj Metadata object.
+ */
+ void putMeta(int id, Object metaObj);
+}
[42/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
IGNITE-4191: MVCC and transactional SQL support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov, Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov, Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mashenkov, and many others. Special thanks for design ideas and review to Alexey Goncharuk and Sergi Vladykin. This closes #3220.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/00bb4d4d
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/00bb4d4d
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/00bb4d4d
Branch: refs/heads/ignite-9273
Commit: 00bb4d4def3391bfe22e0a6042ab18bd95ef9ecf
Parents: 4ea23b0
Author: Igor Seliverstov <gv...@gmail.com>
Authored: Thu Aug 30 14:52:49 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Thu Aug 30 14:52:55 2018 +0300
----------------------------------------------------------------------
.../benchmarks/jmh/tree/BPlusTreeBenchmark.java | 2 +-
.../internal/jdbc2/JdbcStreamingSelfTest.java | 3 +-
.../jdbc/suite/IgniteJdbcDriverTestSuite.java | 14 +
.../jdbc/thin/JdbcThinAbstractSelfTest.java | 4 +-
.../JdbcThinConnectionMvccEnabledSelfTest.java | 378 ++
.../jdbc/thin/JdbcThinConnectionSelfTest.java | 188 +-
.../thin/JdbcThinStreamingAbstractSelfTest.java | 19 +-
.../JdbcThinStreamingNotOrderedSelfTest.java | 2 +-
.../thin/JdbcThinStreamingOrderedSelfTest.java | 2 +-
...ThinTransactionsAbstractComplexSelfTest.java | 1058 +++++
...sactionsClientAutoCommitComplexSelfTest.java | 33 +
...ctionsClientNoAutoCommitComplexSelfTest.java | 33 +
.../jdbc/thin/JdbcThinTransactionsSelfTest.java | 447 +++
...sactionsServerAutoCommitComplexSelfTest.java | 33 +
...ctionsServerNoAutoCommitComplexSelfTest.java | 33 +
.../ignite/codegen/MessageCodeGenerator.java | 6 +-
.../configuration/CacheConfiguration.java | 1 +
.../configuration/IgniteConfiguration.java | 84 +-
.../apache/ignite/internal/GridComponent.java | 5 +-
.../ignite/internal/GridKernalContext.java | 6 +
.../ignite/internal/GridKernalContextImpl.java | 14 +-
.../org/apache/ignite/internal/GridTopic.java | 5 +-
.../apache/ignite/internal/IgniteKernal.java | 22 +-
.../ignite/internal/IgniteNodeAttributes.java | 3 +
.../org/apache/ignite/internal/IgnitionEx.java | 8 +-
.../jdbc/thin/ConnectionProperties.java | 10 +
.../jdbc/thin/ConnectionPropertiesImpl.java | 37 +-
.../internal/jdbc/thin/JdbcThinConnection.java | 33 +-
.../jdbc/thin/JdbcThinDatabaseMetadata.java | 9 +-
.../internal/jdbc/thin/JdbcThinStatement.java | 7 +-
.../internal/jdbc/thin/JdbcThinTcpIo.java | 10 +-
.../managers/communication/GridIoManager.java | 16 +
.../communication/GridIoMessageFactory.java | 134 +-
.../internal/managers/discovery/DiscoCache.java | 17 +-
.../discovery/GridDiscoveryManager.java | 21 +
.../pagemem/store/IgnitePageStoreManager.java | 13 +
.../internal/pagemem/wal/record/WALRecord.java | 11 +-
.../delta/DataPageMvccMarkUpdatedRecord.java | 103 +
.../DataPageMvccUpdateNewTxStateHintRecord.java | 79 +
.../DataPageMvccUpdateTxStateHintRecord.java | 79 +
.../processors/affinity/AffinityAssignment.java | 9 +-
.../affinity/GridAffinityAssignment.java | 15 +-
.../affinity/GridAffinityAssignmentCache.java | 21 +-
.../affinity/GridAffinityProcessor.java | 2 +-
.../processors/affinity/GridAffinityUtils.java | 2 +-
.../affinity/HistoryAffinityAssignment.java | 12 +-
.../cache/CacheAffinitySharedManager.java | 33 +-
.../cache/CacheEntryInfoCollection.java | 12 +
.../processors/cache/CacheGroupContext.java | 34 +
.../processors/cache/CacheOperationContext.java | 2 +-
.../processors/cache/ClusterCachesInfo.java | 17 +-
.../processors/cache/ExchangeContext.java | 43 +-
.../cache/GatewayProtectedCacheProxy.java | 3 +
.../processors/cache/GridCacheAdapter.java | 65 +-
.../cache/GridCacheAffinityManager.java | 5 +
.../processors/cache/GridCacheContext.java | 7 +
.../processors/cache/GridCacheEntryEx.java | 135 +-
.../processors/cache/GridCacheEntryInfo.java | 6 +-
.../processors/cache/GridCacheMapEntry.java | 1149 +++++-
.../cache/GridCacheMvccEntryInfo.java | 257 ++
.../processors/cache/GridCacheOperation.java | 2 +-
.../GridCachePartitionExchangeManager.java | 76 +-
.../processors/cache/GridCacheProcessor.java | 28 +-
.../cache/GridCacheSharedContext.java | 11 +-
.../cache/GridCacheUpdateTxResult.java | 98 +-
.../processors/cache/GridCacheUtils.java | 113 +-
.../cache/IgniteCacheOffheapManager.java | 481 ++-
.../cache/IgniteCacheOffheapManagerImpl.java | 1679 +++++++-
.../processors/cache/IgniteCacheProxyImpl.java | 4 +
.../distributed/GridCacheTxFinishSync.java | 2 +-
.../distributed/GridCacheTxRecoveryFuture.java | 11 +
.../distributed/GridDistributedTxMapping.java | 17 +
.../GridDistributedTxPrepareRequest.java | 22 +
.../GridDistributedTxRemoteAdapter.java | 75 +-
.../distributed/dht/CompoundLockFuture.java | 112 +
.../cache/distributed/dht/DhtLockFuture.java | 30 +
.../cache/distributed/dht/ExceptionAware.java | 30 +
.../dht/GridClientPartitionTopology.java | 7 +
.../distributed/dht/GridDhtCacheAdapter.java | 62 +-
.../distributed/dht/GridDhtCacheEntry.java | 10 +-
.../cache/distributed/dht/GridDhtGetFuture.java | 15 +-
.../distributed/dht/GridDhtGetSingleFuture.java | 15 +-
.../distributed/dht/GridDhtLocalPartition.java | 23 +
.../distributed/dht/GridDhtLockFuture.java | 15 +-
.../dht/GridDhtPartitionTopology.java | 4 +
.../dht/GridDhtPartitionTopologyImpl.java | 11 +
.../dht/GridDhtPartitionsStateValidator.java | 11 +-
.../dht/GridDhtPartitionsUpdateCountersMap.java | 119 +
.../dht/GridDhtTransactionalCacheAdapter.java | 465 ++-
.../dht/GridDhtTxAbstractEnlistFuture.java | 1139 ++++++
.../distributed/dht/GridDhtTxFinishFuture.java | 111 +-
.../distributed/dht/GridDhtTxFinishRequest.java | 77 +-
.../cache/distributed/dht/GridDhtTxLocal.java | 11 +-
.../distributed/dht/GridDhtTxLocalAdapter.java | 45 +-
.../distributed/dht/GridDhtTxPrepareFuture.java | 106 +-
.../dht/GridDhtTxPrepareRequest.java | 67 +-
.../dht/GridDhtTxQueryEnlistFuture.java | 140 +
.../dht/GridDhtTxQueryEnlistRequest.java | 404 ++
.../dht/GridDhtTxQueryEnlistResponse.java | 205 +
.../dht/GridDhtTxQueryFirstEnlistRequest.java | 370 ++
.../dht/GridDhtTxQueryResultsEnlistFuture.java | 130 +
.../cache/distributed/dht/GridDhtTxRemote.java | 144 +
.../dht/GridPartitionedGetFuture.java | 90 +-
.../dht/GridPartitionedSingleGetFuture.java | 22 +-
.../dht/NearTxQueryEnlistResultHandler.java | 169 +
.../dht/atomic/GridDhtAtomicCache.java | 21 +-
.../dht/colocated/GridDhtColocatedCache.java | 30 +-
.../dht/preloader/GridDhtForceKeysFuture.java | 7 +
.../dht/preloader/GridDhtPartitionDemander.java | 7 +
.../dht/preloader/GridDhtPartitionSupplier.java | 38 +-
.../GridDhtPartitionsExchangeFuture.java | 48 +-
.../GridDhtPartitionsSingleMessage.java | 76 +-
.../near/AckCoordinatorOnRollback.java | 54 +
.../distributed/near/GridNearGetFuture.java | 18 +-
.../distributed/near/GridNearGetRequest.java | 49 +-
...arOptimisticSerializableTxPrepareFuture.java | 75 +-
.../near/GridNearOptimisticTxPrepareFuture.java | 68 +-
...ridNearOptimisticTxPrepareFutureAdapter.java | 133 +-
.../GridNearPessimisticTxPrepareFuture.java | 194 +-
.../near/GridNearSingleGetRequest.java | 41 +-
.../near/GridNearTxAbstractEnlistFuture.java | 499 +++
.../near/GridNearTxFastFinishFuture.java | 13 +-
.../near/GridNearTxFinishAndAckFuture.java | 144 +
.../near/GridNearTxFinishFuture.java | 66 +-
.../near/GridNearTxFinishRequest.java | 30 +-
.../cache/distributed/near/GridNearTxLocal.java | 407 +-
.../near/GridNearTxPrepareFutureAdapter.java | 12 +-
.../near/GridNearTxPrepareRequest.java | 17 +
.../near/GridNearTxPrepareResponse.java | 50 +-
.../near/GridNearTxQueryEnlistFuture.java | 382 ++
.../near/GridNearTxQueryEnlistRequest.java | 616 +++
.../near/GridNearTxQueryEnlistResponse.java | 298 ++
.../GridNearTxQueryResultsEnlistFuture.java | 670 ++++
.../GridNearTxQueryResultsEnlistRequest.java | 578 +++
.../GridNearTxQueryResultsEnlistResponse.java | 202 +
.../distributed/near/GridNearTxRemote.java | 9 +-
.../near/GridNearTxSelectForUpdateFuture.java | 462 +++
.../distributed/near/NearTxFinishFuture.java | 21 +-
.../near/TxTopologyVersionFuture.java | 155 +
.../processors/cache/dr/GridCacheDrManager.java | 31 +
.../cache/dr/GridOsCacheDrManager.java | 13 +
.../local/atomic/GridLocalAtomicCache.java | 15 +-
.../processors/cache/mvcc/MvccCoordinator.java | 104 +
.../cache/mvcc/MvccDiscoveryData.java | 52 +
.../cache/mvcc/MvccEmptyLongList.java | 53 +
.../processors/cache/mvcc/MvccFuture.java | 66 +
.../processors/cache/mvcc/MvccLongList.java | 29 +
.../mvcc/MvccPreviousCoordinatorQueries.java | 222 ++
.../processors/cache/mvcc/MvccProcessor.java | 250 ++
.../cache/mvcc/MvccProcessorImpl.java | 2187 +++++++++++
.../processors/cache/mvcc/MvccQueryTracker.java | 102 +
.../cache/mvcc/MvccQueryTrackerImpl.java | 348 ++
.../processors/cache/mvcc/MvccSnapshot.java | 48 +
.../cache/mvcc/MvccSnapshotFuture.java | 43 +
.../mvcc/MvccSnapshotResponseListener.java | 35 +
.../cache/mvcc/MvccSnapshotWithoutTxs.java | 204 +
.../cache/mvcc/MvccUpdateVersionAware.java | 76 +
.../processors/cache/mvcc/MvccUtils.java | 882 +++++
.../processors/cache/mvcc/MvccVersion.java | 48 +
.../processors/cache/mvcc/MvccVersionAware.java | 76 +
.../processors/cache/mvcc/MvccVersionImpl.java | 193 +
.../cache/mvcc/NoOpMvccProcessor.java | 215 +
.../cache/mvcc/StaticMvccQueryTracker.java | 96 +
.../processors/cache/mvcc/VacuumMetrics.java | 102 +
.../cache/mvcc/VacuumMetricsReducer.java | 49 +
.../processors/cache/mvcc/VacuumTask.java | 63 +
.../cache/mvcc/msg/MvccAckRequestQueryCntr.java | 130 +
.../cache/mvcc/msg/MvccAckRequestQueryId.java | 130 +
.../cache/mvcc/msg/MvccAckRequestTx.java | 210 +
.../mvcc/msg/MvccAckRequestTxAndQueryCntr.java | 123 +
.../mvcc/msg/MvccAckRequestTxAndQueryId.java | 122 +
.../mvcc/msg/MvccActiveQueriesMessage.java | 132 +
.../cache/mvcc/msg/MvccFutureResponse.java | 130 +
.../processors/cache/mvcc/msg/MvccMessage.java | 35 +
.../mvcc/msg/MvccQuerySnapshotRequest.java | 130 +
.../cache/mvcc/msg/MvccSnapshotResponse.java | 320 ++
.../cache/mvcc/msg/MvccTxSnapshotRequest.java | 128 +
.../cache/mvcc/msg/MvccWaitTxsRequest.java | 159 +
.../processors/cache/mvcc/txlog/TxKey.java | 76 +
.../processors/cache/mvcc/txlog/TxLog.java | 584 +++
.../processors/cache/mvcc/txlog/TxLogIO.java | 73 +
.../cache/mvcc/txlog/TxLogInnerIO.java | 105 +
.../cache/mvcc/txlog/TxLogLeafIO.java | 105 +
.../processors/cache/mvcc/txlog/TxLogTree.java | 62 +
.../processors/cache/mvcc/txlog/TxRow.java | 44 +
.../processors/cache/mvcc/txlog/TxState.java | 37 +
.../cache/persistence/CacheDataRow.java | 3 +-
.../cache/persistence/CacheDataRowAdapter.java | 114 +-
.../cache/persistence/CacheSearchRow.java | 3 +-
.../persistence/DatabaseLifecycleListener.java | 54 +
.../GridCacheDatabaseSharedManager.java | 39 +-
.../persistence/GridCacheOffheapManager.java | 304 +-
.../IgniteCacheDatabaseSharedManager.java | 32 +-
.../cache/persistence/IndexStorageImpl.java | 2 +-
.../processors/cache/persistence/RowStore.java | 28 +-
.../processors/cache/persistence/Storable.java | 14 +
.../persistence/file/FilePageStoreManager.java | 27 +-
.../persistence/freelist/AbstractFreeList.java | 20 +-
.../cache/persistence/freelist/FreeList.java | 12 +
.../metastorage/MetastorageDataRow.java | 11 +
.../metastorage/MetastorageTree.java | 2 +-
.../cache/persistence/tree/BPlusTree.java | 1219 +++++-
.../persistence/tree/io/AbstractDataPageIO.java | 35 +-
.../cache/persistence/tree/io/DataPageIO.java | 484 ++-
.../cache/persistence/tree/io/IOVersions.java | 7 +
.../cache/persistence/tree/io/PageIO.java | 133 +-
.../persistence/tree/io/SimpleDataPageIO.java | 6 -
.../wal/reader/StandaloneGridKernalContext.java | 6 +
.../persistence/wal/record/RecordTypes.java | 3 +
.../wal/serializer/RecordDataV1Serializer.java | 82 +
.../query/GridCacheDistributedQueryManager.java | 14 +-
.../cache/query/GridCacheQueryAdapter.java | 132 +-
.../cache/query/GridCacheQueryManager.java | 18 +-
.../cache/query/GridCacheQueryRequest.java | 59 +-
.../cache/query/IgniteQueryErrorCode.java | 20 +
.../cache/query/SqlFieldsQueryEx.java | 39 +
.../continuous/CacheContinuousQueryManager.java | 10 +-
.../cache/transactions/IgniteInternalTx.java | 16 +
.../transactions/IgniteTransactionsImpl.java | 4 +-
.../cache/transactions/IgniteTxAdapter.java | 149 +-
.../cache/transactions/IgniteTxHandler.java | 26 +-
.../IgniteTxImplicitSingleStateImpl.java | 16 +-
.../transactions/IgniteTxLocalAdapter.java | 311 +-
.../cache/transactions/IgniteTxManager.java | 13 +
.../cache/transactions/IgniteTxRemoteEx.java | 12 +
.../IgniteTxRemoteSingleStateImpl.java | 5 +
.../IgniteTxRemoteStateAdapter.java | 21 +-
.../transactions/IgniteTxRemoteStateImpl.java | 10 +
.../cache/transactions/IgniteTxState.java | 16 +-
.../cache/transactions/IgniteTxStateImpl.java | 26 +-
.../cache/tree/AbstractDataInnerIO.java | 112 +-
.../cache/tree/AbstractDataLeafIO.java | 125 +-
.../cache/tree/CacheDataRowStore.java | 35 +-
.../processors/cache/tree/CacheDataTree.java | 82 +-
.../cache/tree/CacheIdAwareDataInnerIO.java | 8 +-
.../cache/tree/CacheIdAwareDataLeafIO.java | 8 +-
.../processors/cache/tree/DataInnerIO.java | 11 -
.../processors/cache/tree/DataLeafIO.java | 11 -
.../internal/processors/cache/tree/DataRow.java | 28 +-
.../cache/tree/PendingEntriesTree.java | 2 +-
.../processors/cache/tree/PendingRow.java | 3 +-
.../processors/cache/tree/RowLinkIO.java | 67 +-
.../processors/cache/tree/SearchRow.java | 31 +
.../mvcc/data/MvccCacheIdAwareDataInnerIO.java | 68 +
.../mvcc/data/MvccCacheIdAwareDataLeafIO.java | 89 +
.../cache/tree/mvcc/data/MvccDataInnerIO.java | 71 +
.../cache/tree/mvcc/data/MvccDataLeafIO.java | 89 +
.../cache/tree/mvcc/data/MvccDataRow.java | 263 ++
.../cache/tree/mvcc/data/MvccUpdateDataRow.java | 485 +++
.../tree/mvcc/data/MvccUpdateDataRowNative.java | 240 ++
.../cache/tree/mvcc/data/MvccUpdateResult.java | 43 +
.../cache/tree/mvcc/data/ResultType.java | 34 +
.../mvcc/search/MvccFirstRowTreeClosure.java | 76 +
.../mvcc/search/MvccLinkAwareSearchRow.java | 48 +
.../tree/mvcc/search/MvccMaxSearchRow.java | 55 +
.../tree/mvcc/search/MvccMinSearchRow.java | 55 +
.../cache/tree/mvcc/search/MvccSearchRow.java | 70 +
.../tree/mvcc/search/MvccSnapshotSearchRow.java | 113 +
.../cache/tree/mvcc/search/MvccTreeClosure.java | 29 +
.../internal/processors/odbc/SqlStateCode.java | 3 +
.../odbc/jdbc/JdbcBatchExecuteRequest.java | 27 +-
.../odbc/jdbc/JdbcConnectionContext.java | 47 +-
.../jdbc/JdbcOrderedBatchExecuteRequest.java | 5 +-
.../odbc/jdbc/JdbcQueryExecuteRequest.java | 27 +-
.../odbc/jdbc/JdbcRequestHandler.java | 65 +-
.../odbc/jdbc/JdbcRequestHandlerWorker.java | 131 +
.../odbc/odbc/OdbcConnectionContext.java | 21 +-
.../processors/odbc/odbc/OdbcMessageParser.java | 16 +-
.../odbc/odbc/OdbcQueryExecuteBatchRequest.java | 15 +-
.../odbc/odbc/OdbcQueryExecuteRequest.java | 14 +-
.../odbc/odbc/OdbcRequestHandler.java | 73 +-
.../odbc/odbc/OdbcRequestHandlerWorker.java | 131 +
.../processors/query/EnlistOperation.java | 91 +
.../processors/query/GridQueryIndexing.java | 41 +-
.../processors/query/GridQueryProcessor.java | 52 +-
.../internal/processors/query/NestedTxMode.java | 60 +
.../processors/query/UpdateSourceIterator.java | 89 +
.../messages/GridQueryNextPageResponse.java | 32 +-
.../GridInternalSubscriptionProcessor.java | 18 +
.../apache/ignite/internal/sql/SqlKeyword.java | 20 +
.../apache/ignite/internal/sql/SqlParser.java | 85 +-
.../ignite/internal/sql/SqlParserUtils.java | 14 +
.../sql/command/SqlBeginTransactionCommand.java | 46 +
.../command/SqlCommitTransactionCommand.java | 46 +
.../command/SqlRollbackTransactionCommand.java | 46 +
.../IgniteTxMvccVersionCheckedException.java | 47 +
.../util/future/GridCompoundFuture.java | 4 +-
.../internal/TestRecordingCommunicationSpi.java | 26 +-
.../processors/cache/GridCacheTestEntryEx.java | 73 +-
...IgniteClientCacheInitializationFailTest.java | 20 +-
...vccAbstractBasicCoordinatorFailoverTest.java | 685 ++++
...acheMvccAbstractCoordinatorFailoverTest.java | 148 +
.../mvcc/CacheMvccAbstractFeatureTest.java | 294 ++
.../cache/mvcc/CacheMvccAbstractTest.java | 2291 +++++++++++
.../cache/mvcc/CacheMvccClusterRestartTest.java | 178 +
.../CacheMvccConfigurationValidationTest.java | 191 +
...ccIteratorWithConcurrentTransactionTest.java | 64 +
...calEntriesWithConcurrentTransactionTest.java | 65 +
.../mvcc/CacheMvccOperationChecksTest.java | 201 +
...eMvccPartitionedCoordinatorFailoverTest.java | 144 +
.../cache/mvcc/CacheMvccProcessorTest.java | 97 +
...heMvccReplicatedCoordinatorFailoverTest.java | 30 +
...cScanQueryWithConcurrentTransactionTest.java | 76 +
...heMvccSizeWithConcurrentTransactionTest.java | 52 +
.../cache/mvcc/CacheMvccTransactionsTest.java | 3674 ++++++++++++++++++
.../cache/mvcc/CacheMvccVacuumTest.java | 158 +
.../MemoryPolicyInitializationTest.java | 12 +-
.../pagemem/NoOpPageStoreManager.java | 7 +
.../processors/database/BPlusTreeSelfTest.java | 377 +-
.../database/CacheFreeListImplSelfTest.java | 55 +
.../DataStreamProcessorMvccSeflTest.java | 68 +
.../DataStreamProcessorSelfTest.java | 24 +-
.../SqlParserTransactionalKeywordsSelfTest.java | 96 +
.../testframework/junits/GridAbstractTest.java | 8 +
.../testsuites/IgniteCacheMvccTestSuite.java | 61 +
.../development/utils/IgniteWalConverter.java | 5 +-
.../query/h2/opt/GridH2SpatialIndex.java | 11 +-
.../cache/query/GridCacheTwoStepQuery.java | 36 +
.../query/h2/DhtResultSetEnlistFuture.java | 63 +
.../query/h2/DmlStatementsProcessor.java | 352 +-
.../query/h2/H2ConnectionWrapper.java | 52 +-
.../processors/query/h2/H2FieldsIterator.java | 29 +-
.../processors/query/h2/H2KeyValueIterator.java | 2 +-
.../query/h2/H2ResultSetIterator.java | 7 +-
.../processors/query/h2/H2StatementCache.java | 78 +-
.../processors/query/h2/IgniteH2Indexing.java | 983 +++--
.../query/h2/NearResultSetEnlistFuture.java | 62 +
.../query/h2/PreparedStatementEx.java | 45 +
.../query/h2/PreparedStatementExImpl.java | 648 +++
.../query/h2/RebuildIndexFromHashClosure.java | 47 +
.../query/h2/ResultSetEnlistFuture.java | 136 +
.../query/h2/ThreadLocalObjectPool.java | 103 +
.../query/h2/database/H2PkHashIndex.java | 108 +-
.../query/h2/database/H2RowFactory.java | 25 +-
.../processors/query/h2/database/H2Tree.java | 112 +-
.../query/h2/database/H2TreeFilterClosure.java | 109 +
.../query/h2/database/H2TreeIndex.java | 160 +-
.../h2/database/io/AbstractH2ExtrasInnerIO.java | 164 +
.../h2/database/io/AbstractH2ExtrasLeafIO.java | 161 +
.../query/h2/database/io/AbstractH2InnerIO.java | 74 +
.../query/h2/database/io/AbstractH2LeafIO.java | 76 +
.../query/h2/database/io/H2ExtrasInnerIO.java | 111 +-
.../query/h2/database/io/H2ExtrasLeafIO.java | 107 +-
.../query/h2/database/io/H2IOUtils.java | 94 +
.../query/h2/database/io/H2InnerIO.java | 41 +-
.../query/h2/database/io/H2LeafIO.java | 41 +-
.../h2/database/io/H2MvccExtrasInnerIO.java | 55 +
.../h2/database/io/H2MvccExtrasLeafIO.java | 54 +
.../query/h2/database/io/H2MvccInnerIO.java | 58 +
.../query/h2/database/io/H2MvccLeafIO.java | 58 +
.../query/h2/database/io/H2RowLinkIO.java | 34 +
.../query/h2/ddl/DdlStatementsProcessor.java | 27 +
.../processors/query/h2/dml/DmlAstUtils.java | 12 +-
.../processors/query/h2/dml/FastUpdate.java | 17 +
.../processors/query/h2/dml/UpdatePlan.java | 233 ++
.../query/h2/dml/UpdatePlanBuilder.java | 118 +-
.../query/h2/opt/GridH2IndexBase.java | 36 +-
.../query/h2/opt/GridH2KeyRowOnheap.java | 11 +
.../query/h2/opt/GridH2KeyValueRowOnheap.java | 21 +-
.../query/h2/opt/GridH2MetaTable.java | 5 +
.../query/h2/opt/GridH2PlainRowFactory.java | 17 +-
.../query/h2/opt/GridH2QueryContext.java | 27 +-
.../processors/query/h2/opt/GridH2Row.java | 50 +-
.../query/h2/opt/GridH2RowDescriptor.java | 9 +-
.../query/h2/opt/GridH2SearchRow.java | 32 +
.../query/h2/opt/GridH2SearchRowAdapter.java | 27 +-
.../processors/query/h2/opt/GridH2Table.java | 8 +-
.../query/h2/sql/GridSqlQueryParser.java | 164 +-
.../query/h2/sql/GridSqlQuerySplitter.java | 18 +-
.../processors/query/h2/sql/GridSqlSelect.java | 20 +
.../query/h2/twostep/GridMapQueryExecutor.java | 334 +-
.../h2/twostep/GridMergeIndexIterator.java | 17 +-
.../h2/twostep/GridReduceQueryExecutor.java | 183 +-
.../query/h2/twostep/GridResultPage.java | 22 +-
.../query/h2/twostep/MapQueryResults.java | 18 +-
.../query/h2/twostep/ReduceQueryRun.java | 35 +-
.../h2/twostep/msg/GridH2QueryRequest.java | 115 +-
.../msg/GridH2SelectForUpdateTxDetails.java | 299 ++
.../twostep/msg/GridH2ValueMessageFactory.java | 12 +-
.../cache/DdlTransactionSelfTest.java | 335 ++
.../IgniteCacheQueryH2IndexingLeakTest.java | 10 +-
.../cache/index/AbstractSchemaSelfTest.java | 102 +-
...ynamicColumnsAbstractConcurrentSelfTest.java | 13 +-
.../index/DynamicIndexAbstractSelfTest.java | 7 +
.../cache/index/H2ConnectionLeaksSelfTest.java | 8 +-
.../index/H2DynamicIndexAbstractSelfTest.java | 49 -
.../cache/index/H2DynamicTableSelfTest.java | 16 +-
...sactionsComandsWithMvccDisabledSelfTest.java | 83 +
.../cache/index/SqlTransactionsSelfTest.java | 421 ++
...eMvccAbstractSqlCoordinatorFailoverTest.java | 138 +
.../mvcc/CacheMvccBackupsAbstractTest.java | 808 ++++
.../cache/mvcc/CacheMvccBulkLoadTest.java | 123 +
.../cache/mvcc/CacheMvccDmlSimpleTest.java | 200 +
...eratorWithConcurrentJdbcTransactionTest.java | 28 +
...ntriesWithConcurrentJdbcTransactionTest.java | 29 +
.../mvcc/CacheMvccPartitionedBackupsTest.java | 30 +
...MvccPartitionedSelectForUpdateQueryTest.java | 44 +
...ccPartitionedSqlCoordinatorFailoverTest.java | 143 +
.../CacheMvccPartitionedSqlQueriesTest.java | 30 +
.../CacheMvccPartitionedSqlTxQueriesTest.java | 30 +
...cPartitionedSqlTxQueriesWithReducerTest.java | 30 +
.../mvcc/CacheMvccReplicatedBackupsTest.java | 30 +
...eMvccReplicatedSelectForUpdateQueryTest.java | 30 +
...vccReplicatedSqlCoordinatorFailoverTest.java | 30 +
.../mvcc/CacheMvccReplicatedSqlQueriesTest.java | 30 +
.../CacheMvccReplicatedSqlTxQueriesTest.java | 242 ++
...ccReplicatedSqlTxQueriesWithReducerTest.java | 30 +
...nQueryWithConcurrentJdbcTransactionTest.java | 29 +
...cheMvccSelectForUpdateQueryAbstractTest.java | 374 ++
...ccSizeWithConcurrentJdbcTransactionTest.java | 28 +
.../mvcc/CacheMvccSqlQueriesAbstractTest.java | 1611 ++++++++
.../mvcc/CacheMvccSqlTxQueriesAbstractTest.java | 1832 +++++++++
...MvccSqlTxQueriesWithReducerAbstractTest.java | 829 ++++
.../mvcc/CacheMvccStreamingInsertTest.java | 122 +
.../query/h2/GridIndexRebuildSelfTest.java | 350 ++
.../h2/GridIndexingSpiAbstractSelfTest.java | 4 +-
.../query/h2/H2StatementCacheSelfTest.java | 83 +
.../query/h2/PreparedStatementExSelfTest.java | 61 +
.../query/h2/ThreadLocalObjectPoolSelfTest.java | 113 +
.../query/h2/sql/GridQueryParsingTest.java | 1 +
.../testsuites/IgniteCacheMvccSqlTestSuite.java | 73 +
.../IgniteCacheQuerySelfTestSuite.java | 20 +
.../processors/cache/mvcc/mvcc_person.csv | 2 +
.../cache/mvcc/mvcc_person_broken.csv | 2 +
.../processors/cache/jta/CacheJtaManager.java | 1 +
modules/platforms/cpp/odbc-test/Makefile.am | 4 +-
.../odbc-test/config/queries-transaction-32.xml | 50 +
.../odbc-test/config/queries-transaction.xml | 32 +
.../cpp/odbc-test/project/vs/odbc-test.vcxproj | 4 +
.../project/vs/odbc-test.vcxproj.filters | 12 +
.../cpp/odbc-test/src/sql_get_info_test.cpp | 8 +-
.../cpp/odbc-test/src/transaction_test.cpp | 738 ++++
modules/platforms/cpp/odbc/Makefile.am | 1 +
.../include/ignite/odbc/config/configuration.h | 32 +
.../odbc/config/connection_string_parser.h | 3 +
.../cpp/odbc/include/ignite/odbc/connection.h | 21 +-
.../ignite/odbc/diagnostic/diagnosable.h | 7 +
.../odbc/diagnostic/diagnosable_adapter.h | 11 +-
.../cpp/odbc/include/ignite/odbc/environment.h | 15 +
.../cpp/odbc/include/ignite/odbc/message.h | 13 +-
.../odbc/include/ignite/odbc/nested_tx_mode.h | 81 +
.../odbc/include/ignite/odbc/protocol_version.h | 2 +-
.../odbc/system/ui/dsn_configuration_window.h | 8 +
.../src/system/ui/dsn_configuration_window.cpp | 56 +-
.../platforms/cpp/odbc/project/vs/odbc.vcxproj | 2 +
.../cpp/odbc/project/vs/odbc.vcxproj.filters | 6 +
.../cpp/odbc/src/config/configuration.cpp | 29 +-
.../cpp/odbc/src/config/connection_info.cpp | 8 +-
.../src/config/connection_string_parser.cpp | 19 +
modules/platforms/cpp/odbc/src/connection.cpp | 96 +-
.../odbc/src/diagnostic/diagnosable_adapter.cpp | 5 +
modules/platforms/cpp/odbc/src/dsn_config.cpp | 5 +
modules/platforms/cpp/odbc/src/environment.cpp | 53 +-
modules/platforms/cpp/odbc/src/message.cpp | 18 +-
.../platforms/cpp/odbc/src/nested_tx_mode.cpp | 80 +
modules/platforms/cpp/odbc/src/odbc.cpp | 2 +
.../cpp/odbc/src/query/batch_query.cpp | 4 +-
.../platforms/cpp/odbc/src/query/data_query.cpp | 2 +-
.../config/mvcc/benchmark-mvcc-messages.sh | 98 +
.../config/mvcc/benchmark-mvcc-processor.sh | 95 +
.../mvcc/benchmark-mvcc-updates-contention.sh | 96 +
.../mvcc/benchmark-thin-native.properties | 124 +
...benchmark-jdbc-thin-inmemory-mvcc.properties | 105 +
.../yardstick/IgniteBenchmarkArguments.java | 38 +
.../org/apache/ignite/yardstick/IgniteNode.java | 2 +
.../yardstick/jdbc/AbstractJdbcBenchmark.java | 2 +-
.../yardstick/jdbc/AbstractNativeBenchmark.java | 2 +-
.../apache/ignite/yardstick/jdbc/JdbcUtils.java | 11 +-
.../jdbc/NativeJavaApiPutRemoveBenchmark.java | 52 +
.../mvcc/AbstractDistributedMvccBenchmark.java | 101 +
.../jdbc/mvcc/MvccProcessorBenchmark.java | 61 +
.../mvcc/MvccUpdateContentionBenchmark.java | 77 +
.../upload/AbstractNativeBenchmark.java | 14 +-
.../upload/AbstractUploadBenchmark.java | 9 +-
.../yardstick/upload/model/QueryFactory.java | 30 +-
475 files changed, 56322 insertions(+), 2679 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
index 7ed84cb..e80e13d 100644
--- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
+++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/BPlusTreeBenchmark.java
@@ -192,7 +192,7 @@ public class BPlusTreeBenchmark extends JmhAbstractBenchmark {
}
/** {@inheritDoc} */
- @Override protected Long getRow(BPlusIO<Long> io, long pageAddr, int idx, Object ignore)
+ @Override public Long getRow(BPlusIO<Long> io, long pageAddr, int idx, Object ignore)
throws IgniteCheckedException {
assert io.canGetRow() : io;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStreamingSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStreamingSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStreamingSelfTest.java
index 9e11efc..bc545ac 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStreamingSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStreamingSelfTest.java
@@ -34,6 +34,7 @@ import org.apache.ignite.configuration.ConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.jdbc.thin.JdbcThinAbstractSelfTest;
import org.apache.ignite.lang.IgniteCallable;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
@@ -47,7 +48,7 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
/**
* Data streaming test.
*/
-public class JdbcStreamingSelfTest extends GridCommonAbstractTest {
+public class JdbcStreamingSelfTest extends JdbcThinAbstractSelfTest {
/** JDBC URL. */
private static final String BASE_URL = CFG_URL_PREFIX +
"cache=default@modules/clients/src/test/config/jdbc-config.xml";
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index 889a551..275040f 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -48,6 +48,7 @@ import org.apache.ignite.jdbc.thin.JdbcThinComplexDmlDdlSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinComplexDmlDdlSkipReducerOnUpdateSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinComplexQuerySelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinConnectionMultipleAddressesTest;
+import org.apache.ignite.jdbc.thin.JdbcThinConnectionMvccEnabledSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinConnectionSSLTest;
import org.apache.ignite.jdbc.thin.JdbcThinConnectionSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinDataSourceSelfTest;
@@ -76,6 +77,11 @@ import org.apache.ignite.jdbc.thin.JdbcThinStatementSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinStreamingNotOrderedSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinStreamingOrderedSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinTcpIoTest;
+import org.apache.ignite.jdbc.thin.JdbcThinTransactionsClientAutoCommitComplexSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinTransactionsClientNoAutoCommitComplexSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinTransactionsSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinTransactionsServerAutoCommitComplexSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinTransactionsServerNoAutoCommitComplexSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinUpdateStatementSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinUpdateStatementSkipReducerOnUpdateSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinWalModeChangeSelfTest;
@@ -146,6 +152,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
// New thin JDBC
suite.addTest(new TestSuite(JdbcThinConnectionSelfTest.class));
+ suite.addTest(new TestSuite(JdbcThinConnectionMvccEnabledSelfTest.class));
suite.addTest(new TestSuite(JdbcThinConnectionMultipleAddressesTest.class));
suite.addTest(new TestSuite(JdbcThinTcpIoTest.class));
suite.addTest(new TestSuite(JdbcThinConnectionSSLTest.class));
@@ -197,6 +204,13 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
suite.addTest(new TestSuite(JdbcThinComplexDmlDdlSkipReducerOnUpdateSelfTest.class));
suite.addTest(new TestSuite(JdbcThinComplexDmlDdlCustomSchemaSelfTest.class));
+ // Transactions
+ suite.addTest(new TestSuite(JdbcThinTransactionsSelfTest.class));
+ suite.addTest(new TestSuite(JdbcThinTransactionsClientAutoCommitComplexSelfTest.class));
+ suite.addTest(new TestSuite(JdbcThinTransactionsServerAutoCommitComplexSelfTest.class));
+ suite.addTest(new TestSuite(JdbcThinTransactionsClientNoAutoCommitComplexSelfTest.class));
+ suite.addTest(new TestSuite(JdbcThinTransactionsServerNoAutoCommitComplexSelfTest.class));
+
suite.addTest(new TestSuite(JdbcThinLocalQueriesSelfTest.class));
// Various commands.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
index 2ba36c3..6d5f59a 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinAbstractSelfTest.java
@@ -112,7 +112,7 @@ public class JdbcThinAbstractSelfTest extends GridCommonAbstractTest {
* @param params Connection parameters.
* @return Thin JDBC connection to specified node.
*/
- static Connection connect(IgniteEx node, String params) throws SQLException {
+ protected Connection connect(IgniteEx node, String params) throws SQLException {
Collection<GridPortRecord> recs = node.context().ports().records();
GridPortRecord cliLsnrRec = null;
@@ -141,7 +141,7 @@ public class JdbcThinAbstractSelfTest extends GridCommonAbstractTest {
* @return Result set.
* @throws RuntimeException if failed.
*/
- static List<List<?>> execute(Connection conn, String sql, Object... args) throws SQLException {
+ protected List<List<?>> execute(Connection conn, String sql, Object... args) throws SQLException {
try (PreparedStatement s = conn.prepareStatement(sql)) {
for (int i = 0; i < args.length; i++)
s.setObject(i + 1, args[i]);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMvccEnabledSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMvccEnabledSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMvccEnabledSelfTest.java
new file mode 100644
index 0000000..051d1d2
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionMvccEnabledSelfTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.jdbc.thin;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Savepoint;
+import java.util.concurrent.Callable;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridStringLogger;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.jetbrains.annotations.NotNull;
+
+import static java.sql.Connection.TRANSACTION_NONE;
+import static java.sql.Connection.TRANSACTION_READ_COMMITTED;
+import static java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
+import static java.sql.Connection.TRANSACTION_REPEATABLE_READ;
+import static java.sql.Connection.TRANSACTION_SERIALIZABLE;
+
+/**
+ * Connection test.
+ */
+@SuppressWarnings("ThrowableNotThrown")
+public class JdbcThinConnectionMvccEnabledSelfTest extends JdbcThinAbstractSelfTest {
+ /** IP finder. */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** */
+ private static final String URL = "jdbc:ignite:thin://127.0.0.1";
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("deprecation")
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ cfg.setCacheConfiguration(cacheConfiguration(DEFAULT_CACHE_NAME));
+
+ TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+ disco.setIpFinder(IP_FINDER);
+
+ cfg.setDiscoverySpi(disco);
+
+ cfg.setMarshaller(new BinaryMarshaller());
+
+ cfg.setGridLogger(new GridStringLogger());
+
+ cfg.setMvccEnabled(true);
+
+ return cfg;
+ }
+
+ /**
+ * @param name Cache name.
+ * @return Cache configuration.
+ * @throws Exception In case of error.
+ */
+ private CacheConfiguration cacheConfiguration(@NotNull String name) throws Exception {
+ CacheConfiguration cfg = defaultCacheConfiguration();
+
+ cfg.setName(name);
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGridsMultiThreaded(2);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+ }
+
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMetadataDefaults() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ DatabaseMetaData meta = conn.getMetaData();
+
+ assertEquals(TRANSACTION_REPEATABLE_READ, meta.getDefaultTransactionIsolation());
+ assertTrue(meta.supportsTransactions());
+
+ assertFalse(meta.supportsTransactionIsolationLevel(TRANSACTION_NONE));
+ assertFalse(meta.supportsTransactionIsolationLevel(TRANSACTION_READ_UNCOMMITTED));
+ assertFalse(meta.supportsTransactionIsolationLevel(TRANSACTION_READ_COMMITTED));
+ assertTrue(meta.supportsTransactionIsolationLevel(TRANSACTION_REPEATABLE_READ));
+ assertFalse(meta.supportsTransactionIsolationLevel(TRANSACTION_SERIALIZABLE));
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testGetSetAutoCommit() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ assertTrue(conn.getMetaData().supportsTransactions());
+
+ assertTrue(conn.getAutoCommit());
+
+ conn.setAutoCommit(false);
+
+ assertFalse(conn.getAutoCommit());
+
+ conn.setAutoCommit(true);
+
+ assertTrue(conn.getAutoCommit());
+
+ conn.close();
+
+ // Exception when called on closed connection
+ checkConnectionClosed(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.setAutoCommit(true);
+ }
+ });
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCommit() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ assertTrue(conn.getMetaData().supportsTransactions());
+
+ // Should not be called in auto-commit mode
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.commit();
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Transaction cannot be committed explicitly in auto-commit mode"
+ );
+
+ conn.setAutoCommit(false);
+
+ conn.commit();
+
+ conn.close();
+
+ // Exception when called on closed connection
+ checkConnectionClosed(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.commit();
+ }
+ });
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRollback() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ assertTrue(conn.getMetaData().supportsTransactions());
+
+ // Should not be called in auto-commit mode
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.rollback();
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Transaction cannot be rolled back explicitly in auto-commit mode."
+ );
+
+ conn.setAutoCommit(false);
+
+ conn.rollback();
+
+ conn.close();
+
+ // Exception when called on closed connection
+ checkConnectionClosed(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.rollback();
+ }
+ });
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSetSavepoint() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ assert !conn.getMetaData().supportsSavepoints();
+
+ // Disallowed in auto-commit mode
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setSavepoint();
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Savepoint cannot be set in auto-commit mode"
+ );
+
+ conn.setAutoCommit(false);
+
+ // Unsupported
+ checkNotSupported(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.setSavepoint();
+ }
+ });
+
+ conn.close();
+
+ checkConnectionClosed(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.setSavepoint();
+ }
+ });
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSetSavepointName() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ assert !conn.getMetaData().supportsSavepoints();
+
+ // Invalid arg
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setSavepoint(null);
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Savepoint name cannot be null"
+ );
+
+ final String name = "savepoint";
+
+ // Disallowed in auto-commit mode
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setSavepoint(name);
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Savepoint cannot be set in auto-commit mode"
+ );
+
+ conn.setAutoCommit(false);
+
+ // Unsupported
+ checkNotSupported(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.setSavepoint(name);
+ }
+ });
+
+ conn.close();
+
+ checkConnectionClosed(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.setSavepoint(name);
+ }
+ });
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRollbackSavePoint() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ assert !conn.getMetaData().supportsSavepoints();
+
+ // Invalid arg
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.rollback(null);
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Invalid savepoint"
+ );
+
+ final Savepoint savepoint = getFakeSavepoint();
+
+ // Disallowed in auto-commit mode
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.rollback(savepoint);
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Auto-commit mode"
+ );
+
+ conn.setAutoCommit(false);
+
+ // Unsupported
+ checkNotSupported(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.rollback(savepoint);
+ }
+ });
+
+ conn.close();
+
+ checkConnectionClosed(new RunnableX() {
+ @Override public void run() throws Exception {
+ conn.rollback(savepoint);
+ }
+ });
+ }
+ }
+
+ /**
+ * @return Savepoint.
+ */
+ private Savepoint getFakeSavepoint() {
+ return new Savepoint() {
+ @Override public int getSavepointId() throws SQLException {
+ return 100;
+ }
+
+ @Override public String getSavepointName() {
+ return "savepoint";
+ }
+ };
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
index 38bcab2..80397e6 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
@@ -17,6 +17,9 @@
package org.apache.ignite.jdbc.thin;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
@@ -38,12 +41,16 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.jdbc.thin.ConnectionProperties;
+import org.apache.ignite.internal.jdbc.thin.ConnectionPropertiesImpl;
import org.apache.ignite.internal.jdbc.thin.JdbcThinConnection;
import org.apache.ignite.internal.jdbc.thin.JdbcThinTcpIo;
+import org.apache.ignite.internal.util.HostAndPortRange;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridStringLogger;
import org.apache.ignite.testframework.GridTestUtils;
import org.jetbrains.annotations.NotNull;
@@ -58,6 +65,7 @@ import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
import static java.sql.Statement.NO_GENERATED_KEYS;
import static java.sql.Statement.RETURN_GENERATED_KEYS;
+import static org.apache.ignite.configuration.ClientConnectorConfiguration.DFLT_PORT;
/**
* Connection test.
@@ -93,6 +101,8 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
cfg.setMarshaller(new BinaryMarshaller());
+ cfg.setGridLogger(new GridStringLogger());
+
return cfg;
}
@@ -970,11 +980,18 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
try (Connection conn = DriverManager.getConnection(URL)) {
assertTrue(conn.getAutoCommit());
- conn.setAutoCommit(false);
-
- assertFalse(conn.getAutoCommit());
+ // Cannot disable autocommit when MVCC is disabled.
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setAutoCommit(false);
- conn.setAutoCommit(true);
+ return null;
+ }
+ },
+ SQLException.class,
+ "MVCC must be enabled in order to invoke transactional operation: COMMIT"
+ );
assertTrue(conn.getAutoCommit());
@@ -994,8 +1011,6 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
*/
public void testCommit() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
- assert !conn.getMetaData().supportsTransactions();
-
// Should not be called in auto-commit mode
GridTestUtils.assertThrows(log,
new Callable<Object>() {
@@ -1009,9 +1024,33 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
"Transaction cannot be committed explicitly in auto-commit mode"
);
- conn.setAutoCommit(false);
+ // Cannot disable autocommit when MVCC is disabled.
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setAutoCommit(false);
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "MVCC must be enabled in order to invoke transactional operation: COMMIT"
+ );
+
+ assertTrue(conn.getAutoCommit());
- conn.commit();
+ // Should not be called in auto-commit mode
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.commit();
+
+ return null;
+ }
+ },
+ SQLException.class,
+ "Transaction cannot be committed explicitly in auto-commit mode."
+ );
conn.close();
@@ -1029,8 +1068,6 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
*/
public void testRollback() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
- assert !conn.getMetaData().supportsTransactions();
-
// Should not be called in auto-commit mode
GridTestUtils.assertThrows(log,
new Callable<Object>() {
@@ -1041,12 +1078,23 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
}
},
SQLException.class,
- "Transaction cannot rollback in auto-commit mode"
+ "Transaction cannot be rolled back explicitly in auto-commit mode."
);
- conn.setAutoCommit(false);
+ // Cannot disable autocommit when MVCC is disabled.
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setAutoCommit(false);
- conn.rollback();
+ return null;
+ }
+ },
+ SQLException.class,
+ "MVCC must be enabled in order to invoke transactional operation: COMMIT"
+ );
+
+ assertTrue(conn.getAutoCommit());
conn.close();
@@ -1138,8 +1186,6 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
*/
public void testGetSetTransactionIsolation() throws Exception {
try (Connection conn = DriverManager.getConnection(URL)) {
- assert !conn.getMetaData().supportsTransactions();
-
// Invalid parameter value
GridTestUtils.assertThrows(log,
new Callable<Object>() {
@@ -1346,14 +1392,20 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
"Savepoint cannot be set in auto-commit mode"
);
- conn.setAutoCommit(false);
+ // Cannot disable autocommit when MVCC is disabled.
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setAutoCommit(false);
- // Unsupported
- checkNotSupported(new RunnableX() {
- @Override public void run() throws Exception {
- conn.setSavepoint();
- }
- });
+ return null;
+ }
+ },
+ SQLException.class,
+ "MVCC must be enabled in order to invoke transactional operation: COMMIT"
+ );
+
+ assertTrue(conn.getAutoCommit());
conn.close();
@@ -1400,14 +1452,20 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
"Savepoint cannot be set in auto-commit mode"
);
- conn.setAutoCommit(false);
+ // Cannot disable autocommit when MVCC is disabled.
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setAutoCommit(false);
- // Unsupported
- checkNotSupported(new RunnableX() {
- @Override public void run() throws Exception {
- conn.setSavepoint(name);
- }
- });
+ return null;
+ }
+ },
+ SQLException.class,
+ "MVCC must be enabled in order to invoke transactional operation: COMMIT"
+ );
+
+ assertTrue(conn.getAutoCommit());
conn.close();
@@ -1454,14 +1512,20 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
"Auto-commit mode"
);
- conn.setAutoCommit(false);
+ // Cannot disable autocommit when MVCC is disabled.
+ GridTestUtils.assertThrows(log,
+ new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ conn.setAutoCommit(false);
- // Unsupported
- checkNotSupported(new RunnableX() {
- @Override public void run() throws Exception {
- conn.rollback(savepoint);
- }
- });
+ return null;
+ }
+ },
+ SQLException.class,
+ "MVCC must be enabled in order to invoke transactional operation: COMMIT"
+ );
+
+ assertTrue(conn.getAutoCommit());
conn.close();
@@ -1898,6 +1962,58 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
}
/**
+ * Test that attempting to supply invalid nested TX mode to driver fails on the client.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testInvalidNestedTxMode() {
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ DriverManager.getConnection(URL + "/?nestedTransactionsMode=invalid");
+
+ return null;
+ }
+ }, SQLException.class, "Invalid nested transactions handling mode");
+ }
+
+ /**
+ * Test that attempting to send unexpected name of nested TX mode to server on handshake yields an error.
+ * We have to do this without explicit {@link Connection} as long as there's no other way to bypass validation and
+ * supply a malformed {@link ConnectionProperties} to {@link JdbcThinTcpIo}.
+ */
+ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "ThrowFromFinallyBlock"})
+ public void testInvalidNestedTxModeOnServerSide() throws SQLException, NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
+ ConnectionPropertiesImpl connProps = new ConnectionPropertiesImpl();
+
+ connProps.setAddresses(new HostAndPortRange[]{new HostAndPortRange("127.0.0.1", DFLT_PORT, DFLT_PORT)});
+
+ connProps.nestedTxMode("invalid");
+
+ Constructor ctor = JdbcThinTcpIo.class.getDeclaredConstructor(ConnectionProperties.class);
+
+ boolean acc = ctor.isAccessible();
+
+ ctor.setAccessible(true);
+
+ final JdbcThinTcpIo io = (JdbcThinTcpIo)ctor.newInstance(connProps);
+
+ try {
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ io.start();
+
+ return null;
+ }
+ }, SQLException.class, "err=Invalid nested transactions handling mode: invalid");
+ }
+ finally {
+ io.close();
+
+ ctor.setAccessible(acc);
+ }
+ }
+
+ /**
*/
public void testSslClientAndPlainServer() {
GridTestUtils.assertThrows(log, new Callable<Object>() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingAbstractSelfTest.java
index 7004635..c83977c 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingAbstractSelfTest.java
@@ -32,6 +32,7 @@ import org.apache.ignite.cache.CachePeekMode;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.internal.jdbc2.JdbcStreamingSelfTest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
import org.apache.ignite.internal.processors.query.GridQueryCancel;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.query.SqlClientContext;
@@ -77,9 +78,19 @@ public abstract class JdbcThinStreamingAbstractSelfTest extends JdbcStreamingSel
super.afterTest();
}
- /** {@inheritDoc} */
+ /** {@inheritDoc} */
+ @Override protected Connection createStreamedConnection(boolean allowOverwrite, long flushFreq) throws Exception {
+ Connection c = connect(grid(0), null);
+
+ execute(c, "SET STREAMING 1 BATCH_SIZE " + batchSize + " ALLOW_OVERWRITE " + (allowOverwrite ? 1 : 0) +
+ " PER_NODE_BUFFER_SIZE 1000 FLUSH_FREQUENCY " + flushFreq);
+
+ return c;
+ }
+
+ /** {@inheritDoc} */
@Override protected Connection createOrdinaryConnection() throws SQLException {
- return JdbcThinAbstractSelfTest.connect(grid(0), null);
+ return connect(grid(0), null);
}
/**
@@ -495,11 +506,11 @@ public abstract class JdbcThinStreamingAbstractSelfTest extends JdbcStreamingSel
/** {@inheritDoc} */
@Override public List<FieldsQueryCursor<List<?>>> querySqlFields(String schemaName, SqlFieldsQuery qry,
- @Nullable SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts,
+ @Nullable SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, MvccQueryTracker tracker,
GridQueryCancel cancel) {
IndexingWithContext.cliCtx = cliCtx;
- return super.querySqlFields(schemaName, qry, cliCtx, keepBinary, failOnMultipleStmts, cancel);
+ return super.querySqlFields(schemaName, qry, cliCtx, keepBinary, failOnMultipleStmts, tracker, cancel);
}
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingNotOrderedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingNotOrderedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingNotOrderedSelfTest.java
index b91258f..b0788e7 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingNotOrderedSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingNotOrderedSelfTest.java
@@ -25,7 +25,7 @@ import java.sql.Connection;
public class JdbcThinStreamingNotOrderedSelfTest extends JdbcThinStreamingAbstractSelfTest {
/** {@inheritDoc} */
@Override protected Connection createStreamedConnection(boolean allowOverwrite, long flushFreq) throws Exception {
- Connection c = JdbcThinAbstractSelfTest.connect(grid(0), null);
+ Connection c = connect(grid(0), null);
execute(c, "SET STREAMING 1 BATCH_SIZE " + batchSize
+ " ALLOW_OVERWRITE " + (allowOverwrite ? 1 : 0)
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingOrderedSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingOrderedSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingOrderedSelfTest.java
index b615f8c..c116a7d 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingOrderedSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinStreamingOrderedSelfTest.java
@@ -25,7 +25,7 @@ import java.sql.Connection;
public class JdbcThinStreamingOrderedSelfTest extends JdbcThinStreamingAbstractSelfTest {
/** {@inheritDoc} */
@Override protected Connection createStreamedConnection(boolean allowOverwrite, long flushFreq) throws Exception {
- Connection c = JdbcThinAbstractSelfTest.connect(grid(0), null);
+ Connection c = connect(grid(0), null);
execute(c, "SET STREAMING 1 BATCH_SIZE " + batchSize
+ " ALLOW_OVERWRITE " + (allowOverwrite ? 1 : 0)
[23/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
index e6c656e..b86273f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
@@ -20,9 +20,11 @@ package org.apache.ignite.internal.processors.cache.transactions;
import java.io.Externalizable;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import javax.cache.expiry.Duration;
@@ -30,12 +32,12 @@ import javax.cache.expiry.ExpiryPolicy;
import javax.cache.processor.EntryProcessor;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.failure.FailureContext;
import org.apache.ignite.failure.FailureType;
-import org.apache.ignite.internal.InvalidEnvironmentException;
import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.InvalidEnvironmentException;
import org.apache.ignite.internal.NodeStoppingException;
-import org.apache.ignite.internal.processors.cache.persistence.StorageException;
import org.apache.ignite.internal.pagemem.wal.WALPointer;
import org.apache.ignite.internal.pagemem.wal.record.DataEntry;
import org.apache.ignite.internal.pagemem.wal.record.DataRecord;
@@ -56,7 +58,11 @@ import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.GridCacheUpdateTxResult;
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsUpdateCountersMap;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.persistence.StorageException;
import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionConflictContext;
@@ -64,6 +70,7 @@ import org.apache.ignite.internal.processors.dr.GridDrType;
import org.apache.ignite.internal.transactions.IgniteTxHeuristicCheckedException;
import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
import org.apache.ignite.internal.util.lang.GridClosureException;
import org.apache.ignite.internal.util.lang.GridTuple;
@@ -152,6 +159,15 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
/** */
protected CacheWriteSynchronizationMode syncMode;
+ /** */
+ private GridLongList mvccWaitTxs;
+
+ /** Update counters map */
+ private Map<Integer, Map<Integer, Long>> updCntrs;
+
+ /** */
+ private volatile boolean qryEnlisted;
+
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -212,6 +228,10 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
txState = implicitSingle ? new IgniteTxImplicitSingleStateImpl() : new IgniteTxStateImpl();
}
+ public GridLongList mvccWaitTransactions() {
+ return mvccWaitTxs;
+ }
+
/**
* @return Transaction write synchronization mode.
*/
@@ -283,10 +303,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
return depEnabled;
}
- /**
- * @param depEnabled Flag indicating whether deployment is enabled for caches from this transaction or not.
- */
- public void activeCachesDeploymentEnabled(boolean depEnabled) {
+ /** {@inheritDoc} */
+ @Override public void activeCachesDeploymentEnabled(boolean depEnabled) {
this.depEnabled = depEnabled;
}
@@ -353,6 +371,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
* @param ret Result.
*/
public void implicitSingleResult(GridCacheReturn ret) {
+ assert ret != null;
+
if (ret.invokeResult())
implicitRes.mergeEntryProcessResults(ret);
else
@@ -476,7 +496,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
/** {@inheritDoc} */
@SuppressWarnings({"CatchGenericClass"})
- @Override public void userCommit() throws IgniteCheckedException {
+ @Override public final void userCommit() throws IgniteCheckedException {
TransactionState state = state();
if (state != COMMITTING) {
@@ -492,7 +512,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
Collection<IgniteTxEntry> commitEntries = (near() || cctx.snapshot().needTxReadLogging()) ? allEntries() : writeEntries();
- boolean empty = F.isEmpty(commitEntries);
+ boolean empty = F.isEmpty(commitEntries) && !queryEnlisted();
// Register this transaction as completed prior to write-phase to
// ensure proper lock ordering for removed entries.
@@ -502,10 +522,14 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
cctx.tm().addCommittedTx(this);
if (!empty) {
+ assert mvccWaitTxs == null;
+
batchStoreCommit(writeEntries());
WALPointer ptr = null;
+ Exception err = null;
+
cctx.database().checkpointReadLock();
try {
@@ -673,10 +697,16 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
CU.subjectId(this, cctx),
resolveTaskName(),
dhtVer,
- null);
+ null,
+ mvccSnapshot());
+
+ if (updRes.success()) {
+ txEntry.updateCounter(updRes.updateCounter());
- if (updRes.success())
- txEntry.updateCounter(updRes.updatePartitionCounter());
+ GridLongList waitTxs = updRes.mvccWaitTransactions();
+
+ updateWaitTxs(waitTxs);
+ }
if (updRes.loggedPointer() != null)
ptr = updRes.loggedPointer();
@@ -687,27 +717,28 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
final GridCacheVersion dhtVer0 = dhtVer;
updateNearEntrySafely(cacheCtx, txEntry.key(), entry -> entry.innerSet(
- null,
- eventNodeId(),
- nodeId,
- val0,
- false,
- false,
- txEntry.ttl(),
- false,
- metrics0,
- txEntry.keepBinary(),
- txEntry.hasOldValue(),
- txEntry.oldValue(),
- topVer,
- CU.empty0(),
- DR_NONE,
- txEntry.conflictExpireTime(),
- null,
- CU.subjectId(this, cctx),
- resolveTaskName(),
- dhtVer0,
- null)
+ null,
+ eventNodeId(),
+ nodeId,
+ val0,
+ false,
+ false,
+ txEntry.ttl(),
+ false,
+ metrics0,
+ txEntry.keepBinary(),
+ txEntry.hasOldValue(),
+ txEntry.oldValue(),
+ topVer,
+ CU.empty0(),
+ DR_NONE,
+ txEntry.conflictExpireTime(),
+ null,
+ CU.subjectId(this, cctx),
+ resolveTaskName(),
+ dhtVer0,
+ null,
+ mvccSnapshot())
);
}
}
@@ -724,15 +755,21 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
txEntry.oldValue(),
topVer,
null,
- cached.detached() ? DR_NONE : drType,
+ cached.detached() ? DR_NONE : drType,
cached.isNear() ? null : explicitVer,
CU.subjectId(this, cctx),
resolveTaskName(),
dhtVer,
- null);
+ null,
+ mvccSnapshot());
+
+ if (updRes.success()) {
+ txEntry.updateCounter(updRes.updateCounter());
+
+ GridLongList waitTxs = updRes.mvccWaitTransactions();
- if (updRes.success())
- txEntry.updateCounter(updRes.updatePartitionCounter());
+ updateWaitTxs(waitTxs);
+ }
if (updRes.loggedPointer() != null)
ptr = updRes.loggedPointer();
@@ -742,23 +779,24 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
final GridCacheVersion dhtVer0 = dhtVer;
updateNearEntrySafely(cacheCtx, txEntry.key(), entry -> entry.innerRemove(
- null,
- eventNodeId(),
- nodeId,
- false,
- false,
- metrics0,
- txEntry.keepBinary(),
- txEntry.hasOldValue(),
- txEntry.oldValue(),
- topVer,
- CU.empty0(),
- DR_NONE,
- null,
- CU.subjectId(this, cctx),
- resolveTaskName(),
- dhtVer0,
- null)
+ null,
+ eventNodeId(),
+ nodeId,
+ false,
+ false,
+ metrics0,
+ txEntry.keepBinary(),
+ txEntry.hasOldValue(),
+ txEntry.oldValue(),
+ topVer,
+ CU.empty0(),
+ DR_NONE,
+ null,
+ CU.subjectId(this, cctx),
+ resolveTaskName(),
+ dhtVer0,
+ null,
+ mvccSnapshot())
);
}
}
@@ -798,7 +836,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
log.debug("Ignoring READ entry when committing: " + txEntry);
}
else {
- assert ownsLock(txEntry.cached()):
+ assert ownsLock(txEntry.cached()) :
"Transaction does not own lock for group lock entry during commit [tx=" +
this + ", txEntry=" + txEntry + ']';
@@ -839,7 +877,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
boolean isNodeStopping = X.hasCause(ex, NodeStoppingException.class);
boolean hasInvalidEnvironmentIssue = X.hasCause(ex, InvalidEnvironmentException.class);
- IgniteCheckedException err = new IgniteTxHeuristicCheckedException("Failed to locally write to cache " +
+ IgniteCheckedException err0 = new IgniteTxHeuristicCheckedException("Failed to locally write to cache " +
"(all transaction entries will be invalidated, however there was a window when " +
"entries for this transaction were visible to others): " + this, ex);
@@ -852,9 +890,9 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
", err=" + ex + ']');
}
else
- U.error(log, "Commit failed.", err);
+ U.error(log, "Commit failed.", err0);
- COMMIT_ERR_UPD.compareAndSet(this, null, err);
+ COMMIT_ERR_UPD.compareAndSet(this, null, err0);
state(UNKNOWN);
@@ -876,20 +914,26 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
if (ex instanceof Error)
throw ex;
- throw err;
+ throw err0;
}
}
+ updateLocalPartitionCounters();
+
if (ptr != null && !cctx.tm().logTxRecords())
cctx.wal().flush(ptr, false);
}
catch (StorageException e) {
+ err = e;
+
throw new IgniteCheckedException("Failed to log transaction record " +
"(transaction will be rolled back): " + this, e);
}
finally {
cctx.database().checkpointReadUnlock();
+ notifyDrManager(state() == COMMITTING && err == null);
+
cctx.tm().resetContext();
}
}
@@ -910,6 +954,18 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
}
/**
+ * @param waitTxs Tx ids to wait for.
+ */
+ private void updateWaitTxs(@Nullable GridLongList waitTxs) {
+ if (waitTxs != null) {
+ if (this.mvccWaitTxs == null)
+ this.mvccWaitTxs = waitTxs;
+ else
+ this.mvccWaitTxs.addAll(waitTxs);
+ }
+ }
+
+ /**
* Safely performs {@code updateClojure} operation on near cache entry with given {@code entryKey}.
* In case of {@link GridCacheEntryRemovedException} operation will be retried.
*
@@ -943,6 +999,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
}
}
+
/**
* Commits transaction to transaction manager. Used for one-phase commit transactions only.
*
@@ -1010,6 +1067,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
@Override public void userRollback(boolean clearThreadMap) throws IgniteCheckedException {
TransactionState state = state();
+ notifyDrManager(false);
+
if (state != ROLLING_BACK && state != ROLLED_BACK) {
setRollbackOnly();
@@ -1077,8 +1136,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
* @param read {@code True} if read.
* @param accessTtl TTL for read operation.
* @param filter Filter to check entries.
- * @throws IgniteCheckedException If error.
* @param computeInvoke If {@code true} computes return value for invoke operation.
+ * @throws IgniteCheckedException If error.
*/
@SuppressWarnings("unchecked")
protected final void postLockWrite(
@@ -1140,7 +1199,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
null,
resolveTaskName(),
null,
- txEntry.keepBinary());
+ txEntry.keepBinary(),
+ null); // TODO IGNITE-7371
}
}
else {
@@ -1252,15 +1312,13 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
res = entryProcessor.process(invokeEntry, t.get2());
- val0 = invokeEntry.value();
+ val0 = invokeEntry.getValue(txEntry.keepBinary());
key0 = invokeEntry.key();
}
- val0 = ctx.toCacheObject(val0);
-
- if (val0 != null)
- ctx.validateKeyAndValue(txEntry.key(), (CacheObject)val0);
+ if (val0 != null) // no validation for remove case
+ ctx.validateKeyAndValue(txEntry.key(), ctx.toCacheObject(val0));
if (res != null)
ret.addEntryProcessResult(ctx, txEntry.key(), key0, res, null, txEntry.keepBinary());
@@ -1282,14 +1340,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
return !txState.init(txSize) || cctx.tm().onStarted(this);
}
- /**
- * Adds cache to the list of active caches in transaction.
- *
- * @param cacheCtx Cache context to add.
- * @throws IgniteCheckedException If caches already enlisted in this transaction are not compatible with given
- * cache (e.g. they have different stores).
- */
- protected final void addActiveCache(GridCacheContext cacheCtx, boolean recovery) throws IgniteCheckedException {
+ /** {@inheritDoc} */
+ @Override public final void addActiveCache(GridCacheContext cacheCtx, boolean recovery) throws IgniteCheckedException {
txState.addActiveCache(cacheCtx, recovery, this);
}
@@ -1350,7 +1402,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
* @param skipStore Skip store flag.
* @return Transaction entry.
*/
- protected final IgniteTxEntry addEntry(GridCacheOperation op,
+ public final IgniteTxEntry addEntry(GridCacheOperation op,
@Nullable CacheObject val,
@Nullable EntryProcessor entryProcessor,
Object[] invokeArgs,
@@ -1503,6 +1555,13 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
}
}
+ /**
+ * @return Map of affected partitions: cacheId -> partId.
+ */
+ public Map<Integer, Set<Integer>> partsMap() {
+ return null;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return GridToStringBuilder.toString(IgniteTxLocalAdapter.class, this, "super", super.toString(),
@@ -1585,6 +1644,110 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
}
/**
+ * @return Partition counters map for the given backup node.
+ */
+ public Map<Integer, GridDhtPartitionsUpdateCountersMap> updateCountersForNode(ClusterNode node) {
+ if (F.isEmpty(updCntrs))
+ return null;
+
+ Map<Integer, GridDhtPartitionsUpdateCountersMap> res = new HashMap<>();
+
+ for (Map.Entry<Integer, Map<Integer, Long>> entry : updCntrs.entrySet()) {
+ Map<Integer, Long> partsCntrs = entry.getValue();
+
+ assert !F.isEmpty(partsCntrs);
+
+ GridCacheContext ctx0 = cctx.cacheContext(entry.getKey());
+
+ GridDhtPartitionsUpdateCountersMap resBackupCntrs = new GridDhtPartitionsUpdateCountersMap();
+
+ for (Map.Entry<Integer, Long> e : partsCntrs.entrySet()) {
+ Long cntr = partsCntrs.get(e.getKey());
+
+ if (ctx0.affinity().backupByPartition(node, e.getKey(), topologyVersionSnapshot())) {
+ assert cntr != null && cntr > 0 : cntr;
+
+ resBackupCntrs.updateCounters().put(e.getKey(), cntr);
+ }
+ }
+
+ if (!resBackupCntrs.updateCounters().isEmpty())
+ res.put(entry.getKey(), resBackupCntrs);
+ }
+
+ return res;
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param part Partition id.
+ */
+ @SuppressWarnings("Java8MapApi")
+ public void addPartitionCountersMapping(Integer cacheId, Integer part) {
+ if (updCntrs == null)
+ updCntrs = new ConcurrentHashMap<>();
+
+ Map<Integer, Long> partUpdCntrs = updCntrs.get(cacheId);
+
+ if (partUpdCntrs == null)
+ updCntrs.put(cacheId, partUpdCntrs = new ConcurrentHashMap<>());
+
+ partUpdCntrs.put(part, 0L);
+ }
+
+ /**
+ * Merges mvcc update counters to the partition update counters. For mvcc transactions we update partitions
+ * counters only on commit phase.
+ */
+ private void updateLocalPartitionCounters() {
+ if (F.isEmpty(updCntrs))
+ return;
+
+ for (Map.Entry<Integer, Map<Integer, Long>> entry : updCntrs.entrySet()) {
+ Map<Integer, Long> partsCntrs = entry.getValue();
+
+ assert !F.isEmpty(partsCntrs);
+
+ GridCacheContext ctx0 = cctx.cacheContext(entry.getKey());
+
+ for (Map.Entry<Integer, Long> e : partsCntrs.entrySet()) {
+ GridDhtLocalPartition dhtPart = ctx0.topology().localPartition(e.getKey());
+
+ assert dhtPart != null;
+
+ long cntr = dhtPart.mvccUpdateCounter();
+
+ dhtPart.updateCounter(cntr);
+
+ Long prev = partsCntrs.put(e.getKey(), cntr);
+
+ assert prev == 0L : prev;
+ }
+ }
+ }
+
+ /**
+ * @return {@code True} if there are entries, enlisted by query.
+ */
+ public boolean queryEnlisted() {
+ return qryEnlisted;
+ }
+
+ /**
+ * @param ver Mvcc version.
+ */
+ public void markQueryEnlisted(MvccSnapshot ver) {
+ if (!qryEnlisted) {
+ if (mvccSnapshot == null)
+ mvccSnapshot = ver;
+
+ cctx.coordinators().registerLocalTransaction(ver.coordinatorVersion(), ver.counter());
+
+ qryEnlisted = true;
+ }
+ }
+
+ /**
* Post-lock closure alias.
*
* @param <T> Return type.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
index ffaaf43..d6e9d7a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
@@ -313,6 +313,16 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
}
/**
+ * Rollback all active transactions with acquired Mvcc snapshot.
+ */
+ public void rollbackMvccTxOnCoordinatorChange() {
+ for (IgniteInternalTx tx : activeTransactions()) {
+ if (tx.mvccSnapshot() != null)
+ ((GridNearTxLocal)tx).rollbackNearTxLocalAsync(false, false);
+ }
+ }
+
+ /**
* @param cacheId Cache ID.
* @param txMap Transactions map.
*/
@@ -456,6 +466,7 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
* @param concurrency Concurrency.
* @param isolation Isolation.
* @param timeout transaction timeout.
+ * @param sql Whether this transaction is being started via SQL API or not, or {@code null} if unknown.
* @param txSize Expected transaction size.
* @param lb Label.
* @return New transaction.
@@ -468,6 +479,7 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
TransactionIsolation isolation,
long timeout,
boolean storeEnabled,
+ Boolean sql,
int txSize,
@Nullable String lb
) {
@@ -487,6 +499,7 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
isolation,
timeout,
storeEnabled,
+ sql,
txSize,
subjId,
taskNameHash,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteEx.java
index 87cc7cc..1e0645f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteEx.java
@@ -18,7 +18,9 @@
package org.apache.ignite.internal.processors.cache.transactions;
import java.util.Collection;
+import java.util.Map;
import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsUpdateCountersMap;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
/**
@@ -50,4 +52,14 @@ public interface IgniteTxRemoteEx extends IgniteInternalTx {
* @param cntrs Partition update indexes.
*/
public void setPartitionUpdateCounters(long[] cntrs);
+
+ /**
+ * @param updCntrsMap Partition update counters map: cacheId -> partId -> updateCntr.
+ */
+ public void updateCountersMap(Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrsMap);
+
+ /**
+ * @return Partition update counters map: cacheId -> partId -> updateCntr.
+ */
+ public Map<Integer, GridDhtPartitionsUpdateCountersMap> updateCountersMap();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteSingleStateImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteSingleStateImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteSingleStateImpl.java
index b61a99c..4d0388b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteSingleStateImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteSingleStateImpl.java
@@ -142,4 +142,9 @@ public class IgniteTxRemoteSingleStateImpl extends IgniteTxRemoteStateAdapter {
return null;
}
+
+ /** {@inheritDoc} */
+ @Override public boolean mvccEnabled(GridCacheSharedContext cctx) {
+ return entry != null && entry.context().mvccEnabled();
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateAdapter.java
index bcb900c..c1d973e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateAdapter.java
@@ -22,6 +22,7 @@ import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture;
+import org.apache.ignite.internal.util.GridIntList;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.jetbrains.annotations.Nullable;
@@ -31,6 +32,9 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_ASYNC;
*
*/
public abstract class IgniteTxRemoteStateAdapter implements IgniteTxRemoteState {
+ /** Active cache IDs. */
+ private GridIntList activeCacheIds = new GridIntList();
+
/** {@inheritDoc} */
@Override public boolean implicitSingle() {
return false;
@@ -38,9 +42,12 @@ public abstract class IgniteTxRemoteStateAdapter implements IgniteTxRemoteState
/** {@inheritDoc} */
@Nullable @Override public Integer firstCacheId() {
- assert false;
+ return activeCacheIds.isEmpty() ? null : activeCacheIds.get(0);
+ }
- return null;
+ /** {@inheritDoc} */
+ @Nullable @Override public GridIntList cacheIds() {
+ return activeCacheIds;
}
/** {@inheritDoc} */
@@ -67,9 +74,15 @@ public abstract class IgniteTxRemoteStateAdapter implements IgniteTxRemoteState
}
/** {@inheritDoc} */
- @Override public void addActiveCache(GridCacheContext cacheCtx, boolean recovery, IgniteTxLocalAdapter tx)
+ @Override public void addActiveCache(GridCacheContext cctx, boolean recovery, IgniteTxAdapter tx)
throws IgniteCheckedException {
- assert false;
+ assert !tx.local();
+
+ int cacheId = cctx.cacheId();
+
+ // Check if we can enlist new cache to transaction.
+ if (!activeCacheIds.contains(cacheId))
+ activeCacheIds.add(cacheId);
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateImpl.java
index 1326491..1b6c656 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxRemoteStateImpl.java
@@ -209,4 +209,14 @@ public class IgniteTxRemoteStateImpl extends IgniteTxRemoteStateAdapter {
return null;
}
+
+ /** {@inheritDoc} */
+ @Override public boolean mvccEnabled(GridCacheSharedContext cctx) {
+ for (IgniteTxEntry e : writeMap.values()) {
+ if (e.context().mvccEnabled())
+ return true;
+ }
+
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxState.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxState.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxState.java
index 1fe0d2a..e42fe7f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxState.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxState.java
@@ -26,6 +26,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture;
import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
+import org.apache.ignite.internal.util.GridIntList;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.jetbrains.annotations.Nullable;
@@ -45,6 +46,13 @@ public interface IgniteTxState {
@Nullable public Integer firstCacheId();
/**
+ * Gets caches ids affected with current tx.
+ *
+ * @return tx cache ids.
+ */
+ @Nullable public GridIntList cacheIds();
+
+ /**
* Unwind evicts for caches involved in this transaction.
* @param cctx Grid cache shared context.
*/
@@ -83,7 +91,7 @@ public interface IgniteTxState {
* @param tx Transaction.
* @throws IgniteCheckedException If cache check failed.
*/
- public void addActiveCache(GridCacheContext cacheCtx, boolean recovery, IgniteTxLocalAdapter tx)
+ public void addActiveCache(GridCacheContext cacheCtx, boolean recovery, IgniteTxAdapter tx)
throws IgniteCheckedException;
/**
@@ -180,4 +188,10 @@ public interface IgniteTxState {
* @return {@code True} if transaction is empty.
*/
public boolean empty();
+
+ /**
+ * @param cctx Context.
+ * @return {@code True} if MVCC mode is enabled for transaction.
+ */
+ public boolean mvccEnabled(GridCacheSharedContext cctx);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxStateImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxStateImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxStateImpl.java
index 4f14b5c..0315f8a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxStateImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxStateImpl.java
@@ -29,8 +29,6 @@ import org.apache.ignite.cache.CacheInterceptor;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
import org.apache.ignite.internal.processors.cache.CacheStoppedException;
-import org.apache.ignite.internal.managers.discovery.DiscoCache;
-import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -73,6 +71,10 @@ public class IgniteTxStateImpl extends IgniteTxLocalStateAdapter {
@GridToStringInclude
protected Boolean recovery;
+ /** */
+ @GridToStringInclude
+ protected Boolean mvccEnabled;
+
/** {@inheritDoc} */
@Override public boolean implicitSingle() {
return false;
@@ -84,6 +86,11 @@ public class IgniteTxStateImpl extends IgniteTxLocalStateAdapter {
}
/** {@inheritDoc} */
+ @Nullable @Override public GridIntList cacheIds() {
+ return activeCacheIds;
+ }
+
+ /** {@inheritDoc} */
@Override public void unwindEvicts(GridCacheSharedContext cctx) {
for (int i = 0; i < activeCacheIds.size(); i++) {
int cacheId = activeCacheIds.get(i);
@@ -203,8 +210,10 @@ public class IgniteTxStateImpl extends IgniteTxLocalStateAdapter {
}
/** {@inheritDoc} */
- @Override public void addActiveCache(GridCacheContext cacheCtx, boolean recovery, IgniteTxLocalAdapter tx)
+ @Override public void addActiveCache(GridCacheContext cacheCtx, boolean recovery, IgniteTxAdapter tx)
throws IgniteCheckedException {
+ assert tx.local();
+
GridCacheSharedContext cctx = cacheCtx.shared();
int cacheId = cacheCtx.cacheId();
@@ -215,6 +224,12 @@ public class IgniteTxStateImpl extends IgniteTxLocalStateAdapter {
this.recovery = recovery;
+ if (this.mvccEnabled != null && this.mvccEnabled != cacheCtx.mvccEnabled())
+ throw new IgniteCheckedException("Failed to enlist new cache to existing transaction " +
+ "(caches with different mvcc settings can't be enlisted in one transaction).");
+
+ this.mvccEnabled = cacheCtx.mvccEnabled();
+
// Check if we can enlist new cache to transaction.
if (!activeCacheIds.contains(cacheId)) {
String err = cctx.verifyTxCompatibility(tx, activeCacheIds, cacheCtx);
@@ -462,6 +477,11 @@ public class IgniteTxStateImpl extends IgniteTxLocalStateAdapter {
}
/** {@inheritDoc} */
+ @Override public boolean mvccEnabled(GridCacheSharedContext cctx) {
+ return Boolean.TRUE == mvccEnabled;
+ }
+
+ /** {@inheritDoc} */
public String toString() {
return S.toString(IgniteTxStateImpl.class, this, "txMap", allEntriesCopy());
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataInnerIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataInnerIO.java
index d5b28ef..6594451 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataInnerIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataInnerIO.java
@@ -17,7 +17,9 @@
package org.apache.ignite.internal.processors.cache.tree;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
@@ -26,6 +28,8 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInne
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.lang.IgniteInClosure;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVersionIsValid;
+
/**
*
*/
@@ -36,61 +40,126 @@ public abstract class AbstractDataInnerIO extends BPlusInnerIO<CacheSearchRow> i
* @param canGetRow If we can get full row from this page.
* @param itemSize Single item size on page.
*/
- AbstractDataInnerIO(int type, int ver, boolean canGetRow, int itemSize) {
+ protected AbstractDataInnerIO(int type, int ver, boolean canGetRow, int itemSize) {
super(type, ver, canGetRow, itemSize);
}
/** {@inheritDoc} */
- @Override public void storeByOffset(long pageAddr, int off, CacheSearchRow row) {
+ @Override public final void storeByOffset(long pageAddr, int off, CacheSearchRow row) {
assert row.link() != 0;
PageUtils.putLong(pageAddr, off, row.link());
- PageUtils.putInt(pageAddr, off + 8, row.hash());
+ off += 8;
+
+ PageUtils.putInt(pageAddr, off, row.hash());
+ off += 4;
if (storeCacheId()) {
assert row.cacheId() != CU.UNDEFINED_CACHE_ID : row;
- PageUtils.putInt(pageAddr, off + 12, row.cacheId());
+ PageUtils.putInt(pageAddr, off, row.cacheId());
+ off += 4;
+ }
+
+ if (storeMvccVersion()) {
+ long mvccCrd = row.mvccCoordinatorVersion();
+ long mvccCntr = row.mvccCounter();
+ int opCntr = row.mvccOperationCounter();
+
+ assert mvccVersionIsValid(mvccCrd, mvccCntr, opCntr);
+
+ PageUtils.putLong(pageAddr, off, mvccCrd);
+ off += 8;
+
+ PageUtils.putLong(pageAddr, off, mvccCntr);
+ off += 8;
+
+ PageUtils.putInt(pageAddr, off, opCntr);
}
}
/** {@inheritDoc} */
- @Override public CacheSearchRow getLookupRow(BPlusTree<CacheSearchRow, ?> tree, long pageAddr, int idx) {
- int cacheId = getCacheId(pageAddr, idx);
- int hash = getHash(pageAddr, idx);
+ @Override public final CacheSearchRow getLookupRow(BPlusTree<CacheSearchRow, ?> tree, long pageAddr, int idx)
+ throws IgniteCheckedException {
long link = getLink(pageAddr, idx);
+ int hash = getHash(pageAddr, idx);
+
+ int cacheId = storeCacheId() ? getCacheId(pageAddr, idx) : CU.UNDEFINED_CACHE_ID;
+
+ if (storeMvccVersion()) {
+ long mvccCrd = getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = getMvccCounter(pageAddr, idx);
+ int opCntr = getMvccOperationCounter(pageAddr, idx);
+
+ assert mvccVersionIsValid(mvccCrd, mvccCntr, opCntr);
+
+ return ((CacheDataTree)tree).rowStore().mvccRow(cacheId,
+ hash,
+ link,
+ CacheDataRowAdapter.RowData.KEY_ONLY,
+ mvccCrd,
+ mvccCntr,
+ opCntr);
+ }
return ((CacheDataTree)tree).rowStore().keySearchRow(cacheId, hash, link);
}
/** {@inheritDoc} */
- @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<CacheSearchRow> srcIo, long srcPageAddr,
- int srcIdx) {
- int hash = ((RowLinkIO)srcIo).getHash(srcPageAddr, srcIdx);
- long link = ((RowLinkIO)srcIo).getLink(srcPageAddr, srcIdx);
+ @Override public final void store(long dstPageAddr,
+ int dstIdx,
+ BPlusIO<CacheSearchRow> srcIo,
+ long srcPageAddr,
+ int srcIdx)
+ {
+ RowLinkIO rowIo = ((RowLinkIO)srcIo);
+
+ long link =rowIo.getLink(srcPageAddr, srcIdx);
+ int hash = rowIo.getHash(srcPageAddr, srcIdx);
+
int off = offset(dstIdx);
PageUtils.putLong(dstPageAddr, off, link);
- PageUtils.putInt(dstPageAddr, off + 8, hash);
+ off += 8;
+
+ PageUtils.putInt(dstPageAddr, off, hash);
+ off += 4;
if (storeCacheId()) {
- int cacheId = ((RowLinkIO)srcIo).getCacheId(srcPageAddr, srcIdx);
+ int cacheId = rowIo.getCacheId(srcPageAddr, srcIdx);
assert cacheId != CU.UNDEFINED_CACHE_ID;
- PageUtils.putInt(dstPageAddr, off + 12, cacheId);
+ PageUtils.putInt(dstPageAddr, off, cacheId);
+ off += 4;
+ }
+
+ if (storeMvccVersion()) {
+ long mvccCrd = rowIo.getMvccCoordinatorVersion(srcPageAddr, srcIdx);
+ long mvccCntr = rowIo.getMvccCounter(srcPageAddr, srcIdx);
+ int opCntr = rowIo.getMvccOperationCounter(srcPageAddr, srcIdx);
+
+ assert MvccUtils.mvccVersionIsValid(mvccCrd, mvccCntr, opCntr);
+
+ PageUtils.putLong(dstPageAddr, off, mvccCrd);
+ off += 8;
+
+ PageUtils.putLong(dstPageAddr, off, mvccCntr);
+ off += 8;
+
+ PageUtils.putInt(dstPageAddr, off, opCntr);
}
}
/** {@inheritDoc} */
- @Override public long getLink(long pageAddr, int idx) {
+ @Override public final long getLink(long pageAddr, int idx) {
assert idx < getCount(pageAddr) : idx;
return PageUtils.getLong(pageAddr, offset(idx));
}
/** {@inheritDoc} */
- @Override public int getHash(long pageAddr, int idx) {
+ @Override public final int getHash(long pageAddr, int idx) {
return PageUtils.getInt(pageAddr, offset(idx) + 8);
}
@@ -105,5 +174,14 @@ public abstract class AbstractDataInnerIO extends BPlusInnerIO<CacheSearchRow> i
/**
* @return {@code True} if cache ID has to be stored.
*/
- protected abstract boolean storeCacheId();
+ protected boolean storeCacheId() {
+ return false;
+ }
+
+ /**
+ * @return {@code True} if mvcc version has to be stored.
+ */
+ protected boolean storeMvccVersion() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataLeafIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataLeafIO.java
index e9a3a9b..f0e5c90e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataLeafIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/AbstractDataLeafIO.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.cache.tree;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
@@ -26,6 +27,8 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeaf
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.lang.IgniteInClosure;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVersionIsValid;
+
/**
*
*/
@@ -35,7 +38,7 @@ public abstract class AbstractDataLeafIO extends BPlusLeafIO<CacheSearchRow> imp
* @param ver Page format version.
* @param itemSize Single item size on page.
*/
- AbstractDataLeafIO(int type, int ver, int itemSize) {
+ public AbstractDataLeafIO(int type, int ver, int itemSize) {
super(type, ver, itemSize);
}
@@ -44,52 +47,133 @@ public abstract class AbstractDataLeafIO extends BPlusLeafIO<CacheSearchRow> imp
assert row.link() != 0;
PageUtils.putLong(pageAddr, off, row.link());
- PageUtils.putInt(pageAddr, off + 8, row.hash());
+ off += 8;
+
+ PageUtils.putInt(pageAddr, off, row.hash());
+ off += 4;
if (storeCacheId()) {
assert row.cacheId() != CU.UNDEFINED_CACHE_ID;
- PageUtils.putInt(pageAddr, off + 12, row.cacheId());
+ PageUtils.putInt(pageAddr, off, row.cacheId());
+ off += 4;
+ }
+
+ if (storeMvccVersion()) {
+ long mvccCrdVer = row.mvccCoordinatorVersion();
+ long mvccCntr = row.mvccCounter();
+ int mvccOpCntr = row.mvccOperationCounter();
+
+ assert mvccVersionIsValid(mvccCrdVer, mvccCntr, mvccOpCntr);
+
+ PageUtils.putLong(pageAddr, off, mvccCrdVer);
+ off += 8;
+
+ PageUtils.putLong(pageAddr, off, mvccCntr);
+ off += 8;
+
+ PageUtils.putInt(pageAddr, off, mvccOpCntr);
+ off += 4;
+
+ // Lock version the same as mvcc version, a new row is
+ // always locked by Tx, in scope of which it was created.
+ PageUtils.putLong(pageAddr, off, mvccCrdVer);
+ off += 8;
+
+ PageUtils.putLong(pageAddr, off, mvccCntr);
}
}
/** {@inheritDoc} */
@Override public void store(long dstPageAddr, int dstIdx, BPlusIO<CacheSearchRow> srcIo, long srcPageAddr,
- int srcIdx) {
- int hash = ((RowLinkIO)srcIo).getHash(srcPageAddr, srcIdx);
- long link = ((RowLinkIO)srcIo).getLink(srcPageAddr, srcIdx);
+ int srcIdx) {
+ RowLinkIO rowIo = (RowLinkIO) srcIo;
+
+ long link = rowIo.getLink(srcPageAddr, srcIdx);
+ int hash = rowIo.getHash(srcPageAddr, srcIdx);
+
int off = offset(dstIdx);
PageUtils.putLong(dstPageAddr, off, link);
- PageUtils.putInt(dstPageAddr, off + 8, hash);
+ off += 8;
+
+ PageUtils.putInt(dstPageAddr, off, hash);
+ off += 4;
if (storeCacheId()) {
- int cacheId = ((RowLinkIO)srcIo).getCacheId(srcPageAddr, srcIdx);
+ int cacheId = rowIo.getCacheId(srcPageAddr, srcIdx);
assert cacheId != CU.UNDEFINED_CACHE_ID;
- PageUtils.putInt(dstPageAddr, off + 12, cacheId);
+ PageUtils.putInt(dstPageAddr, off, cacheId);
+ off += 4;
+ }
+
+ if (storeMvccVersion()) {
+ long mvccCrd = rowIo.getMvccCoordinatorVersion(srcPageAddr, srcIdx);
+ long mvccCntr = rowIo.getMvccCounter(srcPageAddr, srcIdx);
+ int mvccOpCntr = rowIo.getMvccOperationCounter(srcPageAddr, srcIdx);
+
+ assert mvccVersionIsValid(mvccCrd, mvccCntr, mvccOpCntr);
+
+ long lockCrdVer = rowIo.getMvccLockCoordinatorVersion(srcPageAddr, srcIdx);
+ long lockCntr = rowIo.getMvccLockCounter(srcPageAddr, srcIdx);
+
+ // Lock version cannot be blank
+ assert mvccVersionIsValid(lockCrdVer, lockCntr);
+
+ PageUtils.putLong(dstPageAddr, off, mvccCrd);
+ off += 8;
+
+ PageUtils.putLong(dstPageAddr, off, mvccCntr);
+ off += 8;
+
+ PageUtils.putInt(dstPageAddr, off, mvccOpCntr);
+ off += 4;
+
+ PageUtils.putLong(dstPageAddr, off, lockCrdVer);
+ off += 8;
+
+ PageUtils.putLong(dstPageAddr, off, lockCntr);
}
}
/** {@inheritDoc} */
- @Override public CacheSearchRow getLookupRow(BPlusTree<CacheSearchRow, ?> tree, long buf, int idx) {
- int cacheId = getCacheId(buf, idx);
- int hash = getHash(buf, idx);
- long link = getLink(buf, idx);
+ @Override public final CacheSearchRow getLookupRow(BPlusTree<CacheSearchRow, ?> tree, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ long link = getLink(pageAddr, idx);
+ int hash = getHash(pageAddr, idx);
+
+ int cacheId = storeCacheId() ? getCacheId(pageAddr, idx) : CU.UNDEFINED_CACHE_ID;
+
+ if (storeMvccVersion()) {
+ long mvccCrd = getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = getMvccCounter(pageAddr, idx);
+ int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
+
+ assert mvccVersionIsValid(mvccCrd, mvccCntr, mvccOpCntr);
+
+ return ((CacheDataTree)tree).rowStore().mvccRow(cacheId,
+ hash,
+ link,
+ CacheDataRowAdapter.RowData.KEY_ONLY,
+ mvccCrd,
+ mvccCntr,
+ mvccOpCntr);
+ }
return ((CacheDataTree)tree).rowStore().keySearchRow(cacheId, hash, link);
}
/** {@inheritDoc} */
- @Override public long getLink(long pageAddr, int idx) {
+ @Override public final long getLink(long pageAddr, int idx) {
assert idx < getCount(pageAddr) : idx;
return PageUtils.getLong(pageAddr, offset(idx));
}
/** {@inheritDoc} */
- @Override public int getHash(long pageAddr, int idx) {
+ @Override public final int getHash(long pageAddr, int idx) {
return PageUtils.getInt(pageAddr, offset(idx) + 8);
}
@@ -104,5 +188,14 @@ public abstract class AbstractDataLeafIO extends BPlusLeafIO<CacheSearchRow> imp
/**
* @return {@code True} if cache ID has to be stored.
*/
- protected abstract boolean storeCacheId();
+ protected boolean storeCacheId() {
+ return false;
+ }
+
+ /**
+ * @return {@code True} if mvcc version has to be stored.
+ */
+ protected boolean storeMvccVersion() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataRowStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataRowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataRowStore.java
index 28d8919..7514798 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataRowStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataRowStore.java
@@ -23,6 +23,7 @@ import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapt
import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
import org.apache.ignite.internal.processors.cache.persistence.RowStore;
import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
import org.apache.ignite.internal.util.typedef.internal.CU;
/**
@@ -54,12 +55,30 @@ public class CacheDataRowStore extends RowStore {
* @return Search row.
*/
CacheSearchRow keySearchRow(int cacheId, int hash, long link) {
- DataRow dataRow = new DataRow(grp, hash, link, partId, CacheDataRowAdapter.RowData.KEY_ONLY);
+ return initDataRow(new DataRow(grp, hash, link, partId, CacheDataRowAdapter.RowData.KEY_ONLY), cacheId);
+ }
- if (dataRow.cacheId() == CU.UNDEFINED_CACHE_ID && grp.sharedGroup())
- dataRow.cacheId(cacheId);
+ /**
+ * @param cacheId Cache ID.
+ * @param hash Hash code.
+ * @param link Link.
+ * @param rowData Required row data.
+ * @param crdVer Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param opCntr Mvcc operation counter.
+ * @return Search row.
+ */
+ MvccDataRow mvccRow(int cacheId, int hash, long link, CacheDataRowAdapter.RowData rowData, long crdVer, long mvccCntr, int opCntr) {
+ MvccDataRow dataRow = new MvccDataRow(grp,
+ hash,
+ link,
+ partId,
+ rowData,
+ crdVer,
+ mvccCntr,
+ opCntr);
- return dataRow;
+ return initDataRow(dataRow, cacheId);
}
/**
@@ -70,8 +89,14 @@ public class CacheDataRowStore extends RowStore {
* @return Data row.
*/
CacheDataRow dataRow(int cacheId, int hash, long link, CacheDataRowAdapter.RowData rowData) {
- DataRow dataRow = new DataRow(grp, hash, link, partId, rowData);
+ return initDataRow(new DataRow(grp, hash, link, partId, rowData), cacheId);
+ }
+ /**
+ * @param dataRow Data row.
+ * @param cacheId Cache ID.
+ */
+ private <T extends DataRow> T initDataRow(T dataRow, int cacheId) {
if (dataRow.cacheId() == CU.UNDEFINED_CACHE_ID && grp.sharedGroup())
dataRow.cacheId(cacheId);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java
index c6214eb..4226ec4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java
@@ -21,6 +21,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
@@ -28,12 +29,19 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPagePayload;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccCacheIdAwareDataInnerIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccCacheIdAwareDataLeafIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataInnerIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataLeafIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.internal.CU;
import static org.apache.ignite.internal.pagemem.PageIdUtils.itemId;
import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.MVCC_INFO_SIZE;
/**
*
@@ -46,7 +54,7 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
private final CacheGroupContext grp;
/**
- * @param grp Ccahe group.
+ * @param grp Cache group.
* @param name Tree name.
* @param reuseList Reuse list.
* @param rowStore Row store.
@@ -69,8 +77,8 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
grp.offheap().globalRemoveId(),
metaPageId,
reuseList,
- grp.sharedGroup() ? CacheIdAwareDataInnerIO.VERSIONS : DataInnerIO.VERSIONS,
- grp.sharedGroup() ? CacheIdAwareDataLeafIO.VERSIONS : DataLeafIO.VERSIONS,
+ innerIO(grp),
+ leafIO(grp),
grp.shared().kernalContext().failure());
assert rowStore != null;
@@ -84,15 +92,40 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
}
/**
+ * @param grp Cache group.
+ * @return Tree inner IO.
+ */
+ private static IOVersions<? extends AbstractDataInnerIO> innerIO(CacheGroupContext grp) {
+ if (grp.mvccEnabled())
+ return grp.sharedGroup() ? MvccCacheIdAwareDataInnerIO.VERSIONS : MvccDataInnerIO.VERSIONS;
+
+ return grp.sharedGroup() ? CacheIdAwareDataInnerIO.VERSIONS : DataInnerIO.VERSIONS;
+ }
+
+ /**
+ * @param grp Cache group.
+ * @return Tree leaf IO.
+ */
+ private static IOVersions<? extends AbstractDataLeafIO> leafIO(CacheGroupContext grp) {
+ if (grp.mvccEnabled())
+ return grp.sharedGroup() ? MvccCacheIdAwareDataLeafIO.VERSIONS : MvccDataLeafIO.VERSIONS;
+
+ return grp.sharedGroup() ? CacheIdAwareDataLeafIO.VERSIONS : DataLeafIO.VERSIONS;
+ }
+
+ /**
* @return Row store.
*/
- CacheDataRowStore rowStore() {
+ public CacheDataRowStore rowStore() {
return rowStore;
}
/** {@inheritDoc} */
@Override protected int compare(BPlusIO<CacheSearchRow> iox, long pageAddr, int idx, CacheSearchRow row)
throws IgniteCheckedException {
+ assert !grp.mvccEnabled() || row.mvccCoordinatorVersion() != 0
+ || (row.getClass() == SearchRow.class && row.key() == null) : row;
+
RowLinkIO io = (RowLinkIO)iox;
int cmp;
@@ -102,8 +135,6 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
int cacheId = io.getCacheId(pageAddr, idx);
- assert cacheId != CU.UNDEFINED_CACHE_ID : "Cache ID is not stored";
-
cmp = Integer.compare(cacheId, row.cacheId());
if (cmp != 0)
@@ -130,21 +161,43 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
assert row.key() != null : row;
- return compareKeys(row.key(), link);
+ cmp = compareKeys(row.key(), link);
+
+ if (cmp != 0 || !grp.mvccEnabled())
+ return cmp;
+
+ long crd = io.getMvccCoordinatorVersion(pageAddr, idx);
+ long cntr = io.getMvccCounter(pageAddr, idx);
+ int opCntr = io.getMvccOperationCounter(pageAddr, idx);
+
+ assert MvccUtils.mvccVersionIsValid(crd, cntr, opCntr);
+
+ return -MvccUtils.compare(crd, cntr, opCntr, row); // descending order
}
/** {@inheritDoc} */
- @Override protected CacheDataRow getRow(BPlusIO<CacheSearchRow> io, long pageAddr, int idx, Object flags)
+ @Override public CacheDataRow getRow(BPlusIO<CacheSearchRow> io, long pageAddr, int idx, Object flags)
throws IgniteCheckedException {
- long link = ((RowLinkIO)io).getLink(pageAddr, idx);
- int hash = ((RowLinkIO)io).getHash(pageAddr, idx);
- int cacheId = ((RowLinkIO)io).getCacheId(pageAddr, idx);
+ RowLinkIO rowIo = (RowLinkIO)io;
+
+ long link = rowIo.getLink(pageAddr, idx);
+ int hash = rowIo.getHash(pageAddr, idx);
+
+ int cacheId = grp.sharedGroup() ? rowIo.getCacheId(pageAddr, idx) : CU.UNDEFINED_CACHE_ID;
CacheDataRowAdapter.RowData x = flags != null ?
(CacheDataRowAdapter.RowData)flags :
CacheDataRowAdapter.RowData.FULL;
- return rowStore.dataRow(cacheId, hash, link, x);
+ if (grp.mvccEnabled()) {
+ long mvccCrdVer = rowIo.getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = rowIo.getMvccCounter(pageAddr, idx);
+ int mvccOpCntr = rowIo.getMvccOperationCounter(pageAddr, idx);
+
+ return rowStore.mvccRow(cacheId, hash, link, x, mvccCrdVer, mvccCntr, mvccOpCntr);
+ }
+ else
+ return rowStore.dataRow(cacheId, hash, link, x);
}
/**
@@ -174,6 +227,9 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
if (data.nextLink() == 0) {
long addr = pageAddr + data.offset();
+ if (grp.mvccEnabled())
+ addr += MVCC_INFO_SIZE; // Skip MVCC info.
+
if (grp.storeCacheIdInDataPage())
addr += 4; // Skip cache id.
@@ -220,7 +276,7 @@ public class CacheDataTree extends BPlusTree<CacheSearchRow, CacheDataRow> {
}
// TODO GG-11768.
- CacheDataRowAdapter other = new CacheDataRowAdapter(link);
+ CacheDataRowAdapter other = grp.mvccEnabled() ? new MvccDataRow(link) : new CacheDataRowAdapter(link);
other.initFromLink(grp, CacheDataRowAdapter.RowData.KEY_ONLY);
byte[] bytes1 = other.key().valueBytes(grp.cacheObjectContext());
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataInnerIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataInnerIO.java
index acb42a0..65d90e9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataInnerIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataInnerIO.java
@@ -37,12 +37,12 @@ public final class CacheIdAwareDataInnerIO extends AbstractDataInnerIO {
}
/** {@inheritDoc} */
- @Override public int getCacheId(long pageAddr, int idx) {
- return PageUtils.getInt(pageAddr, offset(idx) + 12);
+ @Override protected boolean storeCacheId() {
+ return true;
}
/** {@inheritDoc} */
- @Override protected boolean storeCacheId() {
- return true;
+ @Override public int getCacheId(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 12);
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataLeafIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataLeafIO.java
index 7263168..70f0f84 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataLeafIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheIdAwareDataLeafIO.java
@@ -37,12 +37,12 @@ public final class CacheIdAwareDataLeafIO extends AbstractDataLeafIO {
}
/** {@inheritDoc} */
- @Override public int getCacheId(long pageAddr, int idx) {
- return PageUtils.getInt(pageAddr, offset(idx) + 12);
+ @Override protected boolean storeCacheId() {
+ return true;
}
/** {@inheritDoc} */
- @Override protected boolean storeCacheId() {
- return true;
+ @Override public int getCacheId(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 12);
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataInnerIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataInnerIO.java
index 8625338..cd8debb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataInnerIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataInnerIO.java
@@ -18,7 +18,6 @@
package org.apache.ignite.internal.processors.cache.tree;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
-import org.apache.ignite.internal.util.typedef.internal.CU;
/**
*
@@ -35,14 +34,4 @@ public final class DataInnerIO extends AbstractDataInnerIO {
private DataInnerIO(int ver) {
super(T_DATA_REF_INNER, ver, true, 12);
}
-
- /** {@inheritDoc} */
- @Override public int getCacheId(long pageAddr, int idx) {
- return CU.UNDEFINED_CACHE_ID;
- }
-
- /** {@inheritDoc} */
- @Override protected boolean storeCacheId() {
- return false;
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataLeafIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataLeafIO.java
index d53964f..3ba9619 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataLeafIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataLeafIO.java
@@ -18,7 +18,6 @@
package org.apache.ignite.internal.processors.cache.tree;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
-import org.apache.ignite.internal.util.typedef.internal.CU;
/**
*
@@ -35,14 +34,4 @@ public final class DataLeafIO extends AbstractDataLeafIO {
private DataLeafIO(int ver) {
super(T_DATA_REF_LEAF, ver, 12);
}
-
- /** {@inheritDoc} */
- @Override public int getCacheId(long pageAddr, int idx) {
- return CU.UNDEFINED_CACHE_ID;
- }
-
- /** {@inheritDoc} */
- @Override protected boolean storeCacheId() {
- return false;
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataRow.java
index 29bbaaf..806f030 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/DataRow.java
@@ -42,7 +42,7 @@ public class DataRow extends CacheDataRowAdapter {
* @param part Partition.
* @param rowData Required row data.
*/
- DataRow(CacheGroupContext grp, int hash, long link, int part, RowData rowData) {
+ protected DataRow(CacheGroupContext grp, int hash, long link, int part, RowData rowData) {
super(link);
this.hash = hash;
@@ -50,8 +50,9 @@ public class DataRow extends CacheDataRowAdapter {
this.part = part;
try {
- // We can not init data row lazily because underlying buffer can be concurrently cleared.
- initFromLink(grp, rowData);
+ // We can not init data row lazily outside of entry lock because underlying buffer can be concurrently cleared.
+ if (rowData != RowData.LINK_ONLY)
+ initFromLink(grp, rowData);
}
catch (IgniteCheckedException e) {
throw new IgniteException(e);
@@ -81,6 +82,27 @@ public class DataRow extends CacheDataRowAdapter {
this.cacheId = cacheId;
}
+ /**
+ * @param link Link.
+ */
+ protected DataRow(long link) {
+ super(link);
+ }
+
+ /**
+ *
+ */
+ DataRow() {
+ super(0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void key(KeyCacheObject key) {
+ super.key(key);
+
+ hash = key.hashCode();
+ }
+
/** {@inheritDoc} */
@Override public int partition() {
return part;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingEntriesTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingEntriesTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingEntriesTree.java
index 78a9f56..554b86a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingEntriesTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingEntriesTree.java
@@ -111,7 +111,7 @@ public class PendingEntriesTree extends BPlusTree<PendingRow, PendingRow> {
}
/** {@inheritDoc} */
- @Override protected PendingRow getRow(BPlusIO<PendingRow> io, long pageAddr, int idx, Object flag)
+ @Override public PendingRow getRow(BPlusIO<PendingRow> io, long pageAddr, int idx, Object flag)
throws IgniteCheckedException {
PendingRow row = io.getLookupRow(this, pageAddr, idx);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingRow.java
index 1a6a183..4116ae0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/PendingRow.java
@@ -21,6 +21,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
import org.apache.ignite.internal.util.typedef.internal.S;
/**
@@ -68,7 +69,7 @@ public class PendingRow {
* @throws IgniteCheckedException If failed.
*/
PendingRow initKey(CacheGroupContext grp) throws IgniteCheckedException {
- CacheDataRowAdapter rowData = new CacheDataRowAdapter(link);
+ CacheDataRowAdapter rowData = grp.mvccEnabled() ? new MvccDataRow(link) : new CacheDataRowAdapter(link);
rowData.initFromLink(grp, CacheDataRowAdapter.RowData.KEY_ONLY);
key = rowData.key();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/RowLinkIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/RowLinkIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/RowLinkIO.java
index 55f880c..8487351 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/RowLinkIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/RowLinkIO.java
@@ -40,5 +40,70 @@ public interface RowLinkIO {
* @param idx Index.
* @return Cache ID or {@code 0} if cache ID is not defined.
*/
- public int getCacheId(long pageAddr, int idx);
+ default int getCacheId(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc coordinator version.
+ */
+ default long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc operation counter.
+ */
+ default int getMvccOperationCounter(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc counter.
+ */
+ default long getMvccCounter(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc coordinator version.
+ */
+ default long getMvccLockCoordinatorVersion(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc counter.
+ */
+ default long getMvccLockCounter(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @param lockCrd Mvcc lock coordinator version.
+ */
+ default void setMvccLockCoordinatorVersion(long pageAddr, int idx, long lockCrd) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @param lockCntr Mvcc lock counter.
+ */
+ default void setMvccLockCounter(long pageAddr, int idx, long lockCntr) {
+ throw new UnsupportedOperationException();
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/SearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/SearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/SearchRow.java
index 6257013..4b166d4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/SearchRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/SearchRow.java
@@ -18,7 +18,13 @@
package org.apache.ignite.internal.processors.cache.tree;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
/**
*
@@ -73,4 +79,29 @@ public class SearchRow implements CacheSearchRow {
@Override public int cacheId() {
return cacheId;
}
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return MVCC_CRD_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return MVCC_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return MVCC_OP_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte mvccTxState() {
+ return TxState.NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SearchRow.class, this);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataInnerIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataInnerIO.java
new file mode 100644
index 0000000..32cd8ba
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataInnerIO.java
@@ -0,0 +1,68 @@
+/*
+ * 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.tree.mvcc.data;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.tree.AbstractDataInnerIO;
+
+/**
+ *
+ */
+public final class MvccCacheIdAwareDataInnerIO extends AbstractDataInnerIO {
+ /** */
+ public static final IOVersions<MvccCacheIdAwareDataInnerIO> VERSIONS = new IOVersions<>(
+ new MvccCacheIdAwareDataInnerIO(1)
+ );
+
+ /**
+ * @param ver Page format version.
+ */
+ private MvccCacheIdAwareDataInnerIO(int ver) {
+ super(T_CACHE_ID_DATA_REF_MVCC_INNER, ver, true, 36);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean storeCacheId() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean storeMvccVersion() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getCacheId(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 12);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 24);
+ }
+
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 32);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataLeafIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataLeafIO.java
new file mode 100644
index 0000000..3da7729
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccCacheIdAwareDataLeafIO.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tree.mvcc.data;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.tree.AbstractDataLeafIO;
+
+/**
+ *
+ */
+public final class MvccCacheIdAwareDataLeafIO extends AbstractDataLeafIO {
+ /** */
+ public static final IOVersions<MvccCacheIdAwareDataLeafIO> VERSIONS = new IOVersions<>(
+ new MvccCacheIdAwareDataLeafIO(1)
+ );
+
+ /**
+ * @param ver Page format version.
+ */
+ private MvccCacheIdAwareDataLeafIO(int ver) {
+ super(T_CACHE_ID_DATA_REF_MVCC_LEAF, ver, 52);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean storeCacheId() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean storeMvccVersion() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getCacheId(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 12);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 24);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 32);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccLockCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 36);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccLockCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 44);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMvccLockCoordinatorVersion(long pageAddr, int idx, long lockCrd) {
+ PageUtils.putLong(pageAddr, offset(idx) + 36, lockCrd);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMvccLockCounter(long pageAddr, int idx, long lockCntr) {
+ PageUtils.putLong(pageAddr, offset(idx) + 44, lockCntr);
+ }
+}
[21/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index 8facd32..7dda67f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -33,10 +33,10 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.cache.configuration.Factory;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cache.query.BulkLoadContextCursor;
import org.apache.ignite.cache.query.FieldsQueryCursor;
-import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInterruptedCheckedException;
import org.apache.ignite.internal.IgniteVersionUtils;
@@ -56,9 +56,11 @@ import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.processors.query.SqlClientContext;
import org.apache.ignite.internal.util.GridSpinBusyLock;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -105,6 +107,9 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
/** Busy lock. */
private final GridSpinBusyLock busyLock;
+ /** Worker. */
+ private final JdbcRequestHandlerWorker worker;
+
/** Maximum allowed cursors. */
private final int maxCursors;
@@ -126,6 +131,9 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
/** Automatic close of cursors. */
private final boolean autoCloseCursors;
+ /** Nested transactions handling mode. */
+ private final NestedTxMode nestedTxMode;
+
/** Protocol version. */
private ClientListenerProtocolVersion protocolVer;
@@ -151,7 +159,7 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
public JdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock,
JdbcResponseSender sender, int maxCursors,
boolean distributedJoins, boolean enforceJoinOrder, boolean collocated, boolean replicatedOnly,
- boolean autoCloseCursors, boolean lazy, boolean skipReducerOnUpdate,
+ boolean autoCloseCursors, boolean lazy, boolean skipReducerOnUpdate, NestedTxMode nestedTxMode,
AuthorizationContext actx, ClientListenerProtocolVersion protocolVer) {
this.ctx = ctx;
this.sender = sender;
@@ -176,10 +184,16 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
this.busyLock = busyLock;
this.maxCursors = maxCursors;
this.autoCloseCursors = autoCloseCursors;
+ this.nestedTxMode = nestedTxMode;
this.protocolVer = protocolVer;
this.actx = actx;
log = ctx.log(getClass());
+
+ if (ctx.grid().configuration().isMvccEnabled())
+ worker = new JdbcRequestHandlerWorker(ctx.igniteInstanceName(), log, this, ctx);
+ else
+ worker = null;
}
/** {@inheritDoc} */
@@ -190,6 +204,34 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
JdbcRequest req = (JdbcRequest)req0;
+ if (worker == null)
+ return doHandle(req);
+ else {
+ GridFutureAdapter<ClientListenerResponse> fut = worker.process(req);
+
+ try {
+ return fut.get();
+ }
+ catch (IgniteCheckedException e) {
+ return exceptionToResult(e);
+ }
+ }
+ }
+
+ /**
+ * Start worker, if it's present.
+ */
+ void start() {
+ if (worker != null)
+ worker.start();
+ }
+
+ /**
+ * Actually handle the request.
+ * @param req Request.
+ * @return Request handling result.
+ */
+ ClientListenerResponse doHandle(JdbcRequest req) {
if (!busyLock.enterBusy())
return new JdbcResponse(IgniteQueryErrorCode.UNKNOWN,
"Failed to handle JDBC request because node is stopping.");
@@ -364,6 +406,17 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
public void onDisconnect() {
if (busyLock.enterBusy())
{
+ if (worker != null) {
+ worker.cancel();
+
+ try {
+ worker.join();
+ }
+ catch (InterruptedException e) {
+ // No-op.
+ }
+ }
+
try
{
for (JdbcQueryCursor cursor : qryCursors.values())
@@ -411,11 +464,11 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
try {
String sql = req.sqlQuery();
- SqlFieldsQuery qry;
+ SqlFieldsQueryEx qry;
switch(req.expectedStatementType()) {
case ANY_STATEMENT_TYPE:
- qry = new SqlFieldsQuery(sql);
+ qry = new SqlFieldsQueryEx(sql, null);
break;
@@ -440,6 +493,8 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
qry.setCollocated(cliCtx.isCollocated());
qry.setReplicatedOnly(cliCtx.isReplicatedOnly());
qry.setLazy(cliCtx.isLazy());
+ qry.setNestedTxMode(nestedTxMode);
+ qry.setAutoCommit(req.autoCommit());
if (req.pageSize() <= 0)
return new JdbcResponse(IgniteQueryErrorCode.UNKNOWN, "Invalid fetch size: " + req.pageSize());
@@ -656,6 +711,8 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler {
qry.setCollocated(cliCtx.isCollocated());
qry.setReplicatedOnly(cliCtx.isReplicatedOnly());
qry.setLazy(cliCtx.isLazy());
+ qry.setNestedTxMode(nestedTxMode);
+ qry.setAutoCommit(req.autoCommit());
qry.setSchema(schemaName);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java
new file mode 100644
index 0000000..7211787
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandlerWorker.java
@@ -0,0 +1,131 @@
+/*
+ * 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.odbc.jdbc;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.odbc.ClientListenerNioListener;
+import org.apache.ignite.internal.processors.odbc.ClientListenerResponse;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.thread.IgniteThread;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * JDBC request handler worker to maintain single threaded transactional execution of SQL statements when MVCC is on.<p>
+ * This worker is intended for internal use as a temporary solution and from within {@link JdbcRequestHandler},
+ * therefore it does not do any fine-grained lifecycle handling as it relies on existing guarantees from
+ * {@link ClientListenerNioListener}.
+ */
+class JdbcRequestHandlerWorker extends GridWorker {
+ /** Requests queue.*/
+ private final LinkedBlockingQueue<T2<JdbcRequest, GridFutureAdapter<ClientListenerResponse>>> queue =
+ new LinkedBlockingQueue<>();
+
+ /** Handler.*/
+ private final JdbcRequestHandler hnd;
+
+ /** Context.*/
+ private final GridKernalContext ctx;
+
+ /** Response */
+ private final static ClientListenerResponse ERR_RESPONSE = new JdbcResponse(IgniteQueryErrorCode.UNKNOWN,
+ "Connection closed.");
+
+ /**
+ * Constructor.
+ * @param igniteInstanceName Instance name.
+ * @param log Logger.
+ * @param hnd Handler.
+ * @param ctx Kernal context.
+ */
+ JdbcRequestHandlerWorker(@Nullable String igniteInstanceName, IgniteLogger log, JdbcRequestHandler hnd,
+ GridKernalContext ctx) {
+ super(igniteInstanceName, "jdbc-request-handler-worker", log);
+
+ A.notNull(hnd, "hnd");
+
+ this.hnd = hnd;
+
+ this.ctx = ctx;
+ }
+
+ /**
+ * Start this worker.
+ */
+ void start() {
+ new IgniteThread(this).start();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void body() throws InterruptedException, IgniteInterruptedCheckedException {
+ try {
+ while (!isCancelled()) {
+ T2<JdbcRequest, GridFutureAdapter<ClientListenerResponse>> req = queue.take();
+
+ GridFutureAdapter<ClientListenerResponse> fut = req.get2();
+
+ try {
+ ClientListenerResponse res = hnd.doHandle(req.get1());
+
+ fut.onDone(res);
+ }
+ catch (Exception e) {
+ fut.onDone(e);
+ }
+ }
+ }
+ finally {
+ // Notify indexing that this worker is being stopped.
+ try {
+ ctx.query().getIndexing().onClientDisconnect();
+ }
+ catch (Exception e) {
+ // No-op.
+ }
+
+ // Drain the queue on stop.
+ T2<JdbcRequest, GridFutureAdapter<ClientListenerResponse>> req = queue.poll();
+
+ while (req != null) {
+ req.get2().onDone(ERR_RESPONSE);
+
+ req = queue.poll();
+ }
+ }
+ }
+
+ /**
+ * Initiate request processing.
+ * @param req Request.
+ * @return Future to track request processing.
+ */
+ GridFutureAdapter<ClientListenerResponse> process(JdbcRequest req) {
+ GridFutureAdapter<ClientListenerResponse> fut = new GridFutureAdapter<>();
+
+ queue.add(new T2<>(req, fut));
+
+ return fut;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
index 253b183..2b51741 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
@@ -17,8 +17,6 @@
package org.apache.ignite.internal.processors.odbc.odbc;
-import java.util.HashSet;
-import java.util.Set;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.binary.BinaryReaderExImpl;
@@ -27,8 +25,12 @@ import org.apache.ignite.internal.processors.odbc.ClientListenerAbstractConnecti
import org.apache.ignite.internal.processors.odbc.ClientListenerMessageParser;
import org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.util.GridSpinBusyLock;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* ODBC Connection Context.
*/
@@ -45,7 +47,7 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
/** Version 2.3.2: added multiple statements support. */
public static final ClientListenerProtocolVersion VER_2_3_2 = ClientListenerProtocolVersion.create(2, 3, 2);
- /** Version 2.5.0: added authentication. */
+ /** Version 2.5.0: added authentication and transactions. */
public static final ClientListenerProtocolVersion VER_2_5_0 = ClientListenerProtocolVersion.create(2, 5, 0);
/** Version 2.7.0: added precision and scale. */
@@ -111,6 +113,7 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
boolean enforceJoinOrder = reader.readBoolean();
boolean replicatedOnly = reader.readBoolean();
boolean collocated = reader.readBoolean();
+
boolean lazy = false;
if (ver.compareTo(VER_2_1_5) >= 0)
@@ -124,17 +127,25 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
String user = null;
String passwd = null;
+ NestedTxMode nestedTxMode = NestedTxMode.DEFAULT;
+
if (ver.compareTo(VER_2_5_0) >= 0) {
user = reader.readString();
passwd = reader.readString();
+
+ byte nestedTxModeVal = reader.readByte();
+
+ nestedTxMode = NestedTxMode.fromByte(nestedTxModeVal);
}
AuthorizationContext actx = authenticate(user, passwd);
- handler = new OdbcRequestHandler(ctx, busyLock, maxCursors, distributedJoins,
- enforceJoinOrder, replicatedOnly, collocated, lazy, skipReducerOnUpdate, actx, ver);
+ handler = new OdbcRequestHandler(ctx, busyLock, maxCursors, distributedJoins, enforceJoinOrder,
+ replicatedOnly, collocated, lazy, skipReducerOnUpdate, actx, nestedTxMode, ver);
parser = new OdbcMessageParser(ctx, ver);
+
+ handler.start();
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcMessageParser.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcMessageParser.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcMessageParser.java
index c2137bd..552841d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcMessageParser.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcMessageParser.java
@@ -47,7 +47,7 @@ public class OdbcMessageParser implements ClientListenerMessageParser {
protected static final int INIT_CAP = 1024;
/** Kernal context. */
- protected GridKernalContext ctx;
+ protected final GridKernalContext ctx;
/** Logger. */
private final IgniteLogger log;
@@ -101,7 +101,12 @@ public class OdbcMessageParser implements ClientListenerMessageParser {
if (ver.compareTo(OdbcConnectionContext.VER_2_3_2) >= 0)
timeout = reader.readInt();
- res = new OdbcQueryExecuteRequest(schema, sql, params, timeout);
+ boolean autoCommit = true;
+
+ if (ver.compareTo(OdbcConnectionContext.VER_2_5_0) >= 0)
+ autoCommit = reader.readBoolean();
+
+ res = new OdbcQueryExecuteRequest(schema, sql, params, timeout, autoCommit);
break;
}
@@ -123,7 +128,12 @@ public class OdbcMessageParser implements ClientListenerMessageParser {
if (ver.compareTo(OdbcConnectionContext.VER_2_3_2) >= 0)
timeout = reader.readInt();
- res = new OdbcQueryExecuteBatchRequest(schema, sql, last, params, timeout);
+ boolean autoCommit = true;
+
+ if (ver.compareTo(OdbcConnectionContext.VER_2_5_0) >= 0)
+ autoCommit = reader.readBoolean();
+
+ res = new OdbcQueryExecuteBatchRequest(schema, sql, last, params, timeout, autoCommit);
break;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteBatchRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteBatchRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteBatchRequest.java
index 0e4effd..75c2831 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteBatchRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteBatchRequest.java
@@ -41,6 +41,10 @@ public class OdbcQueryExecuteBatchRequest extends OdbcRequest {
@GridToStringExclude
private final Object[][] args;
+ /** Autocommit flag. */
+ @GridToStringInclude
+ private final boolean autoCommit;
+
/** Query timeout in seconds. */
@GridToStringInclude
private final int timeout;
@@ -51,9 +55,10 @@ public class OdbcQueryExecuteBatchRequest extends OdbcRequest {
* @param last Last page flag.
* @param args Arguments list.
* @param timeout Timeout in seconds.
+ * @param autoCommit Autocommit flag.
*/
public OdbcQueryExecuteBatchRequest(@Nullable String schema, String sqlQry, boolean last, Object[][] args,
- int timeout) {
+ int timeout, boolean autoCommit) {
super(QRY_EXEC_BATCH);
assert sqlQry != null : "SQL query should not be null";
@@ -64,6 +69,7 @@ public class OdbcQueryExecuteBatchRequest extends OdbcRequest {
this.last = last;
this.args = args;
this.timeout = timeout;
+ this.autoCommit = autoCommit;
}
/**
@@ -106,4 +112,11 @@ public class OdbcQueryExecuteBatchRequest extends OdbcRequest {
@Override public String toString() {
return S.toString(OdbcQueryExecuteBatchRequest.class, this, "args", args, true);
}
+
+ /**
+ * @return Autocommit flag.
+ */
+ public boolean autoCommit() {
+ return autoCommit;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteRequest.java
index 1fde908..7034b86 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcQueryExecuteRequest.java
@@ -37,6 +37,10 @@ public class OdbcQueryExecuteRequest extends OdbcRequest {
@GridToStringExclude
private final Object[] args;
+ /** Autocommit flag. */
+ @GridToStringInclude
+ private final boolean autoCommit;
+
/** Query timeout in seconds. */
@GridToStringInclude
private final int timeout;
@@ -47,7 +51,8 @@ public class OdbcQueryExecuteRequest extends OdbcRequest {
* @param args Arguments list.
* @param timeout Timeout in seconds.
*/
- public OdbcQueryExecuteRequest(@Nullable String schema, String sqlQry, Object[] args, int timeout) {
+ public OdbcQueryExecuteRequest(@Nullable String schema, String sqlQry, Object[] args, int timeout,
+ boolean autoCommit) {
super(QRY_EXEC);
assert sqlQry != null : "SQL query should not be null";
@@ -56,6 +61,7 @@ public class OdbcQueryExecuteRequest extends OdbcRequest {
this.sqlQry = sqlQry;
this.args = args;
this.timeout = timeout;
+ this.autoCommit = autoCommit;
}
/**
@@ -91,4 +97,10 @@ public class OdbcQueryExecuteRequest extends OdbcRequest {
return S.toString(OdbcQueryExecuteRequest.class, this, "args", args, true);
}
+ /**
+ * @return Autocommit flag.
+ */
+ public boolean autoCommit() {
+ return autoCommit;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
index b0f27d0..9510e63 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandler.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cache.query.FieldsQueryCursor;
@@ -46,7 +47,9 @@ import org.apache.ignite.internal.processors.odbc.odbc.escape.OdbcEscapeUtils;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.util.GridSpinBusyLock;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -76,6 +79,9 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
/** Busy lock. */
private final GridSpinBusyLock busyLock;
+ /** Worker. */
+ private final OdbcRequestHandlerWorker worker;
+
/** Maximum allowed cursors. */
private final int maxCursors;
@@ -91,6 +97,9 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
/** Replicated only flag. */
private final boolean replicatedOnly;
+ /** Nested transaction behaviour. */
+ private final NestedTxMode nestedTxMode;
+
/** Collocated flag. */
private final boolean collocated;
@@ -101,7 +110,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
private final boolean skipReducerOnUpdate;
/** Authentication context */
- private AuthorizationContext actx;
+ private final AuthorizationContext actx;
/** Client version. */
private ClientListenerProtocolVersion ver;
@@ -117,12 +126,13 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
* @param collocated Collocated flag.
* @param lazy Lazy flag.
* @param skipReducerOnUpdate Skip reducer on update flag.
+ * @param nestedTxMode Nested transaction mode.
* @param actx Authentication context.
* @param ver Client protocol version.
*/
public OdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock, int maxCursors,
boolean distributedJoins, boolean enforceJoinOrder, boolean replicatedOnly, boolean collocated, boolean lazy,
- boolean skipReducerOnUpdate, AuthorizationContext actx, ClientListenerProtocolVersion ver) {
+ boolean skipReducerOnUpdate, AuthorizationContext actx, NestedTxMode nestedTxMode, ClientListenerProtocolVersion ver) {
this.ctx = ctx;
this.busyLock = busyLock;
this.maxCursors = maxCursors;
@@ -133,20 +143,56 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
this.lazy = lazy;
this.skipReducerOnUpdate = skipReducerOnUpdate;
this.actx = actx;
+ this.nestedTxMode = nestedTxMode;
this.ver = ver;
log = ctx.log(getClass());
+
+ if (ctx.grid().configuration().isMvccEnabled())
+ worker = new OdbcRequestHandlerWorker(ctx.igniteInstanceName(), log, this, ctx);
+ else
+ worker = null;
}
/** {@inheritDoc} */
@Override public ClientListenerResponse handle(ClientListenerRequest req0) {
assert req0 != null;
+ assert req0 instanceof OdbcRequest;
+
OdbcRequest req = (OdbcRequest)req0;
+ if (worker == null)
+ return doHandle(req);
+ else {
+ GridFutureAdapter<ClientListenerResponse> fut = worker.process(req);
+
+ try {
+ return fut.get();
+ }
+ catch (IgniteCheckedException e) {
+ return exceptionToResult(e);
+ }
+ }
+ }
+
+ /**
+ * Start worker, if it's present.
+ */
+ void start() {
+ if (worker != null)
+ worker.start();
+ }
+
+ /**
+ * Handle ODBC request.
+ * @param req ODBC request.
+ * @return Response.
+ */
+ public ClientListenerResponse doHandle(OdbcRequest req) {
if (!busyLock.enterBusy())
return new OdbcResponse(IgniteQueryErrorCode.UNKNOWN,
- "Failed to handle ODBC request because node is stopping: " + req);
+ "Failed to handle ODBC request because node is stopping: " + req);
if (actx != null)
AuthorizationContext.context(actx);
@@ -204,6 +250,17 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
public void onDisconnect() {
if (busyLock.enterBusy())
{
+ if (worker != null) {
+ worker.cancel();
+
+ try {
+ worker.join();
+ }
+ catch (InterruptedException e) {
+ // No-op.
+ }
+ }
+
try
{
for (OdbcQueryResults res : qryResults.values())
@@ -220,9 +277,11 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
* @param schema Schema.
* @param sql SQL request.
* @param args Arguments.
+ * @param autoCommit Autocommit transaction.
+ * @param timeout Query timeout.
* @return Query instance.
*/
- private SqlFieldsQueryEx makeQuery(String schema, String sql, Object[] args, int timeout) {
+ private SqlFieldsQueryEx makeQuery(String schema, String sql, Object[] args, int timeout, boolean autoCommit) {
SqlFieldsQueryEx qry = new SqlFieldsQueryEx(sql, null);
qry.setArgs(args);
@@ -234,6 +293,8 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
qry.setLazy(lazy);
qry.setSchema(schema);
qry.setSkipReducerOnUpdate(skipReducerOnUpdate);
+ qry.setNestedTxMode(nestedTxMode);
+ qry.setAutoCommit(autoCommit);
qry.setTimeout(timeout, TimeUnit.SECONDS);
@@ -264,7 +325,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
log.debug("ODBC query parsed [reqId=" + req.requestId() + ", original=" + req.sqlQuery() +
", parsed=" + sql + ']');
- SqlFieldsQuery qry = makeQuery(req.schema(), sql, req.arguments(), req.timeout());
+ SqlFieldsQuery qry = makeQuery(req.schema(), sql, req.arguments(), req.timeout(), req.autoCommit());
List<FieldsQueryCursor<List<?>>> cursors = ctx.query().querySqlFields(qry, true, false);
@@ -313,7 +374,7 @@ public class OdbcRequestHandler implements ClientListenerRequestHandler {
log.debug("ODBC query parsed [reqId=" + req.requestId() + ", original=" + req.sqlQuery() +
", parsed=" + sql + ']');
- SqlFieldsQueryEx qry = makeQuery(req.schema(), sql, null, req.timeout());
+ SqlFieldsQueryEx qry = makeQuery(req.schema(), sql, null, req.timeout(), req.autoCommit());
Object[][] paramSet = req.arguments();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java
new file mode 100644
index 0000000..4184b6a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcRequestHandlerWorker.java
@@ -0,0 +1,131 @@
+/*
+ * 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.odbc.odbc;
+
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.odbc.ClientListenerNioListener;
+import org.apache.ignite.internal.processors.odbc.ClientListenerResponse;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.thread.IgniteThread;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * ODBC request handler worker to maintain single threaded transactional execution of SQL statements when MVCC is on.<p>
+ * This worker is intended for internal use as a temporary solution and from within {@link OdbcRequestHandler},
+ * therefore it does not do any fine-grained lifecycle handling as it relies on existing guarantees from
+ * {@link ClientListenerNioListener}.
+ */
+class OdbcRequestHandlerWorker extends GridWorker {
+ /** Requests queue.*/
+ private final LinkedBlockingQueue<T2<OdbcRequest, GridFutureAdapter<ClientListenerResponse>>> queue =
+ new LinkedBlockingQueue<>();
+
+ /** Handler.*/
+ private final OdbcRequestHandler hnd;
+
+ /** Context.*/
+ private final GridKernalContext ctx;
+
+ /** Response */
+ private final static ClientListenerResponse ERR_RESPONSE = new OdbcResponse(IgniteQueryErrorCode.UNKNOWN,
+ "Connection closed.");
+
+ /**
+ * Constructor.
+ * @param igniteInstanceName Instance name.
+ * @param log Logger.
+ * @param hnd Handler.
+ * @param ctx Kernal context.
+ */
+ OdbcRequestHandlerWorker(@Nullable String igniteInstanceName, IgniteLogger log, OdbcRequestHandler hnd,
+ GridKernalContext ctx) {
+ super(igniteInstanceName, "odbc-request-handler-worker", log);
+
+ A.notNull(hnd, "hnd");
+
+ this.hnd = hnd;
+
+ this.ctx = ctx;
+ }
+
+ /**
+ * Start this worker.
+ */
+ void start() {
+ new IgniteThread(this).start();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void body() throws InterruptedException, IgniteInterruptedCheckedException {
+ try {
+ while (!isCancelled()) {
+ T2<OdbcRequest, GridFutureAdapter<ClientListenerResponse>> req = queue.take();
+
+ GridFutureAdapter<ClientListenerResponse> fut = req.get2();
+
+ try {
+ ClientListenerResponse res = hnd.doHandle(req.get1());
+
+ fut.onDone(res);
+ }
+ catch (Exception e) {
+ fut.onDone(e);
+ }
+ }
+ }
+ finally {
+ // Notify indexing that this worker is being stopped.
+ try {
+ ctx.query().getIndexing().onClientDisconnect();
+ }
+ catch (Exception e) {
+ // No-op.
+ }
+
+ // Drain the queue on stop.
+ T2<OdbcRequest, GridFutureAdapter<ClientListenerResponse>> req = queue.poll();
+
+ while (req != null) {
+ req.get2().onDone(ERR_RESPONSE);
+
+ req = queue.poll();
+ }
+ }
+ }
+
+ /**
+ * Initiate request processing.
+ * @param req Request.
+ * @return Future to track request processing.
+ */
+ GridFutureAdapter<ClientListenerResponse> process(OdbcRequest req) {
+ GridFutureAdapter<ClientListenerResponse> fut = new GridFutureAdapter<>();
+
+ queue.add(new T2<>(req, fut));
+
+ return fut;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/EnlistOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/EnlistOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/EnlistOperation.java
new file mode 100644
index 0000000..fdb6f1e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/EnlistOperation.java
@@ -0,0 +1,91 @@
+/*
+ * 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.query;
+
+import org.apache.ignite.internal.processors.cache.GridCacheOperation;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Operations on entries which could be performed during transaction.
+ * Operations are used during SQL statements execution, but does not define exact SQL statements semantics.
+ * It is better to treat them independently and having their own semantics.
+ */
+public enum EnlistOperation {
+ /**
+ * This operation creates entry if it does not exist or raises visible failure otherwise.
+ */
+ INSERT(GridCacheOperation.CREATE),
+ /**
+ * This operation creates entry if it does not exist or modifies existing one otherwise.
+ */
+ UPSERT(GridCacheOperation.UPDATE),
+ /**
+ * This operation modifies existing entry or does nothing if entry does not exist.
+ */
+ UPDATE(GridCacheOperation.UPDATE),
+ /**
+ * This operation deletes existing entry or does nothing if entry does not exist.
+ */
+ DELETE(GridCacheOperation.DELETE),
+ /**
+ * This operation locks existing entry protecting it from updates by other transactions
+ * or does notrhing if entry does not exist.
+ */
+ LOCK(null);
+
+ /** */
+ private final GridCacheOperation cacheOp;
+
+ /** */
+ EnlistOperation(GridCacheOperation cacheOp) {
+ this.cacheOp = cacheOp;
+ }
+
+ /**
+ * @return Corresponding Cache operation.
+ */
+ public GridCacheOperation cacheOperation() {
+ return cacheOp;
+ }
+
+ /** */
+ public boolean isDeleteOrLock() {
+ return this == DELETE || this == LOCK;
+ }
+
+ /**
+ * Indicates that an operation cannot create new row.
+ */
+ public boolean noCreate() {
+ // has no meaning for LOCK
+ assert this != LOCK;
+
+ return this == UPDATE || this == DELETE;
+ }
+
+ /** Enum values. */
+ private static final EnlistOperation[] VALS = values();
+
+ /**
+ * @param ord Ordinal value.
+ * @return Enum value.
+ */
+ @Nullable public static EnlistOperation fromOrdinal(int ord) {
+ return ord < 0 || ord >= VALS.length ? null : VALS[ord];
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
index dedd075..7aa4021 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
@@ -31,6 +31,8 @@ import org.apache.ignite.cache.query.SqlQuery;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
import org.apache.ignite.internal.util.GridSpinBusyLock;
@@ -61,6 +63,13 @@ public interface GridQueryIndexing {
public void stop() throws IgniteCheckedException;
/**
+ * Performs necessary actions on disconnect of a stateful client (say, one associated with a transaction).
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ public void onClientDisconnect() throws IgniteCheckedException;
+
+ /**
* Parses SQL query into two step query and executes it.
*
* @param schemaName Schema name.
@@ -79,10 +88,11 @@ public interface GridQueryIndexing {
* @param cliCtx Client context.
* @param keepBinary Keep binary flag.
* @param failOnMultipleStmts Whether an exception should be thrown for multiple statements query.
- * @param cancel Query cancel state handler. @return Cursor.
+ * @param tracker Query tracker.
+ * @return Cursor.
*/
public List<FieldsQueryCursor<List<?>>> querySqlFields(String schemaName, SqlFieldsQuery qry,
- SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, GridQueryCancel cancel);
+ SqlClientContext cliCtx, boolean keepBinary, boolean failOnMultipleStmts, MvccQueryTracker tracker, GridQueryCancel cancel);
/**
* Execute an INSERT statement using data streamer as receiver.
@@ -223,6 +233,28 @@ public interface GridQueryIndexing {
public void unregisterCache(GridCacheContext cctx, boolean rmvIdx) throws IgniteCheckedException;
/**
+ *
+ * @param cctx Cache context.
+ * @param ids Involved cache ids.
+ * @param parts Partitions.
+ * @param schema Schema name.
+ * @param qry Query string.
+ * @param params Query parameters.
+ * @param flags Flags.
+ * @param pageSize Fetch page size.
+ * @param timeout Timeout.
+ * @param topVer Topology version.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param cancel Query cancel object.
+ * @return Cursor over entries which are going to be changed.
+ * @throws IgniteCheckedException If failed.
+ */
+ public UpdateSourceIterator<?> prepareDistributedUpdate(GridCacheContext<?, ?> cctx, int[] ids, int[] parts,
+ String schema, String qry, Object[] params, int flags,
+ int pageSize, int timeout, AffinityTopologyVersion topVer,
+ MvccSnapshot mvccSnapshot, GridQueryCancel cancel) throws IgniteCheckedException;
+
+ /**
* Registers type if it was not known before or updates it otherwise.
*
* @param cctx Cache context.
@@ -243,7 +275,10 @@ public interface GridQueryIndexing {
* @param prevRowAvailable Whether previous row is available.
* @throws IgniteCheckedException If failed.
*/
- public void store(GridCacheContext cctx, GridQueryTypeDescriptor type, CacheDataRow row, CacheDataRow prevRow,
+ public void store(GridCacheContext cctx,
+ GridQueryTypeDescriptor type,
+ CacheDataRow row,
+ CacheDataRow prevRow,
boolean prevRowAvailable) throws IgniteCheckedException;
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 57eaa4a..bd5d91c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -66,6 +66,7 @@ import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.query.CacheQueryFuture;
import org.apache.ignite.internal.processors.cache.query.CacheQueryType;
@@ -1752,15 +1753,20 @@ public class GridQueryProcessor extends GridProcessorAdapter {
* @param cacheIds Cache IDs.
* @return Future that will be completed when rebuilding is finished.
*/
- public IgniteInternalFuture<?> rebuildIndexesFromHash(Collection<Integer> cacheIds) {
+ public IgniteInternalFuture<?> rebuildIndexesFromHash(Set<Integer> cacheIds) {
if (!busyLock.enterBusy())
throw new IllegalStateException("Failed to rebuild indexes from hash (grid is stopping).");
+ // Because of alt type ids, there can be few entries in 'types' for a single cache.
+ // In order to avoid processing a cache more than once, let's track processed names.
+ Set<String> processedCacheNames = new HashSet<>();
+
try {
GridCompoundFuture<Object, ?> fut = new GridCompoundFuture<Object, Object>();
for (Map.Entry<QueryTypeIdKey, QueryTypeDescriptorImpl> e : types.entrySet()) {
- if (cacheIds.contains(CU.cacheId(e.getKey().cacheName())))
+ if (cacheIds.contains(CU.cacheId(e.getKey().cacheName())) &&
+ processedCacheNames.add(e.getKey().cacheName()))
fut.add(rebuildIndexesFromHash(e.getKey().cacheName(), e.getValue()));
}
@@ -2040,6 +2046,32 @@ public class GridQueryProcessor extends GridProcessorAdapter {
}
/**
+ *
+ * @param cctx Cache context.
+ * @param cacheIds Involved cache ids.
+ * @param parts Partitions.
+ * @param schema Schema name.
+ * @param qry Query string.
+ * @param params Query parameters.
+ * @param flags Flags.
+ * @param pageSize Fetch page size.
+ * @param timeout Timeout.
+ * @param topVer Topology version.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param cancel Query cancel object.
+ * @return Cursor over entries which are going to be changed.
+ * @throws IgniteCheckedException If failed.
+ */
+ public UpdateSourceIterator<?> prepareDistributedUpdate(GridCacheContext<?, ?> cctx, int[] cacheIds,
+ int[] parts, String schema, String qry, Object[] params, int flags, int pageSize, int timeout,
+ AffinityTopologyVersion topVer, MvccSnapshot mvccSnapshot,
+ GridQueryCancel cancel) throws IgniteCheckedException {
+ checkxEnabled();
+
+ return idx.prepareDistributedUpdate(cctx, cacheIds, parts, schema, qry, params, flags, pageSize, timeout, topVer, mvccSnapshot, cancel);
+ }
+
+ /**
* Query SQL fields.
*
* @param qry Query.
@@ -2104,7 +2136,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
GridQueryCancel cancel = new GridQueryCancel();
List<FieldsQueryCursor<List<?>>> res =
- idx.querySqlFields(schemaName, qry, cliCtx, keepBinary, failOnMultipleStmts, cancel);
+ idx.querySqlFields(schemaName, qry, cliCtx, keepBinary, failOnMultipleStmts, null, cancel);
if (cctx != null)
sendQueryExecutedEvent(qry.getSql(), qry.getArgs(), cctx);
@@ -2519,15 +2551,15 @@ public class GridQueryProcessor extends GridProcessorAdapter {
/**
* @param cctx Cache context.
- * @param val Row.
+ * @param row Row removed from cache.
* @throws IgniteCheckedException Thrown in case of any errors.
*/
- public void remove(GridCacheContext cctx, CacheDataRow val)
+ public void remove(GridCacheContext cctx, CacheDataRow row)
throws IgniteCheckedException {
- assert val != null;
+ assert row != null;
if (log.isDebugEnabled())
- log.debug("Remove [cacheName=" + cctx.name() + ", key=" + val.key()+ ", val=" + val.value() + "]");
+ log.debug("Remove [cacheName=" + cctx.name() + ", key=" + row.key()+ ", val=" + row.value() + "]");
if (idx == null)
return;
@@ -2538,14 +2570,14 @@ public class GridQueryProcessor extends GridProcessorAdapter {
try {
QueryTypeDescriptorImpl desc = typeByValue(cctx.name(),
cctx.cacheObjectContext(),
- val.key(),
- val.value(),
+ row.key(),
+ row.value(),
false);
if (desc == null)
return;
- idx.remove(cctx, desc, val);
+ idx.remove(cctx, desc, row);
}
finally {
busyLock.leaveBusy();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NestedTxMode.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NestedTxMode.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NestedTxMode.java
new file mode 100644
index 0000000..3569003
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/NestedTxMode.java
@@ -0,0 +1,60 @@
+/*
+ * 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.query;
+
+import org.apache.ignite.IgniteException;
+
+/**
+ * Behavior options when an attempt to start a nested transaction is made.
+ */
+public enum NestedTxMode {
+ /** Previously started transaction will be committed, new transaction will be started. */
+ COMMIT,
+
+ /** Warning will be printed to log, no new transaction will be started. */
+ IGNORE,
+
+ /** Exception will be thrown, previously started transaction will be rolled back. */
+ ERROR;
+
+ /** Default handling mode. */
+ public final static NestedTxMode DEFAULT = ERROR;
+
+ /**
+ * Get enum value from int
+ *
+ * @param val Int value.
+ * @return Enum value.
+ * @throws IgniteException if the is no enum value associated with the int value.
+ */
+ public static NestedTxMode fromByte(byte val) {
+ switch (val) {
+ case 1:
+ return COMMIT;
+
+ case 2:
+ return IGNORE;
+
+ case 3:
+ return ERROR;
+
+ default:
+ throw new IgniteException("Invalid nested transactions handling mode: " + val);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/UpdateSourceIterator.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/UpdateSourceIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/UpdateSourceIterator.java
new file mode 100644
index 0000000..69feb0f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/UpdateSourceIterator.java
@@ -0,0 +1,89 @@
+/*
+ * 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.query;
+
+import java.util.Iterator;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.util.lang.GridCloseableIterator;
+import org.jetbrains.annotations.NotNull;
+
+/** */
+public interface UpdateSourceIterator<T> extends GridCloseableIterator<T> {
+ /**
+ * @return Operation.
+ */
+ public EnlistOperation operation();
+
+ /**
+ * Callback method which should be called before moving iteration into another thread.
+ */
+ public default void beforeDetach() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override default void close() throws IgniteCheckedException {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override default boolean isClosed() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override default void removeX() throws IgniteCheckedException {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ /** {@inheritDoc} */
+ @Override default boolean hasNext() {
+ try {
+ return hasNextX();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override default T next() {
+ try {
+ return nextX();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override default void remove() {
+ try {
+ removeX();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override @NotNull default Iterator<T> iterator() {
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
index 4d918a0..2d1ec36 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/messages/GridQueryNextPageResponse.java
@@ -70,6 +70,9 @@ public class GridQueryNextPageResponse implements Message {
/** Last page flag. */
private boolean last;
+ /** Remove mapping flag. */
+ private boolean removeMapping;
+
/**
* For {@link Externalizable}.
*/
@@ -230,6 +233,12 @@ public class GridQueryNextPageResponse implements Message {
return false;
writer.incrementState();
+
+ case 9:
+ if (!writer.writeBoolean("removeMapping", removeMapping))
+ return false;
+
+ writer.incrementState();
}
return true;
@@ -314,6 +323,13 @@ public class GridQueryNextPageResponse implements Message {
return false;
reader.incrementState();
+ case 9:
+ removeMapping = reader.readBoolean("removeMapping");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
}
return reader.afterMessageRead(GridQueryNextPageResponse.class);
@@ -326,7 +342,7 @@ public class GridQueryNextPageResponse implements Message {
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 9;
+ return 10;
}
/**
@@ -357,6 +373,20 @@ public class GridQueryNextPageResponse implements Message {
this.last = last;
}
+ /**
+ * @param removeMapping Remove mapping flag.
+ */
+ public void removeMapping(boolean removeMapping) {
+ this.removeMapping = removeMapping;
+ }
+
+ /**
+ * @return Remove mapping flag.
+ */
+ public boolean removeMapping() {
+ return removeMapping;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridQueryNextPageResponse.class, this,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/subscription/GridInternalSubscriptionProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/subscription/GridInternalSubscriptionProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/subscription/GridInternalSubscriptionProcessor.java
index 6c0942a..6db7fa5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/subscription/GridInternalSubscriptionProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/subscription/GridInternalSubscriptionProcessor.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
+import org.apache.ignite.internal.processors.cache.persistence.DatabaseLifecycleListener;
import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetastorageLifecycleListener;
import org.jetbrains.annotations.NotNull;
@@ -35,6 +36,10 @@ public class GridInternalSubscriptionProcessor extends GridProcessorAdapter {
/** */
private List<MetastorageLifecycleListener> metastorageListeners = new ArrayList<>();
+ /** */
+ private List<DatabaseLifecycleListener> databaseListeners = new ArrayList<>();
+
+
/**
* @param ctx Kernal context.
*/
@@ -54,4 +59,17 @@ public class GridInternalSubscriptionProcessor extends GridProcessorAdapter {
public List<MetastorageLifecycleListener> getMetastorageSubscribers() {
return metastorageListeners;
}
+
+ /** */
+ public void registerDatabaseListener(@NotNull DatabaseLifecycleListener databaseListener) {
+ if (databaseListener == null)
+ throw new NullPointerException("Database subscriber should be not-null.");
+
+ databaseListeners.add(databaseListener);
+ }
+
+ /** */
+ public List<DatabaseLifecycleListener> getDatabaseListeners() {
+ return databaseListeners;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
index be76482..658e176 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlKeyword.java
@@ -18,7 +18,9 @@
package org.apache.ignite.internal.sql;
import java.lang.reflect.Field;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.Set;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.util.typedef.F;
@@ -38,6 +40,9 @@ public class SqlKeyword {
/** Keyword: BATCH_SIZE */
public static final String BATCH_SIZE = "BATCH_SIZE";
+ /** Keyword: BEGIN. */
+ public static final String BEGIN = "BEGIN";
+
/** Keyword: BIGINT */
public static final String BIGINT = "BIGINT";
@@ -65,6 +70,9 @@ public class SqlKeyword {
/** Keyword: CHARSET. */
public static final String CHARSET = "CHARSET";
+ /** Keyword: COMMIT. */
+ public static final String COMMIT = "COMMIT";
+
/** Keyword: COPY. */
public static final String COPY = "COPY";
@@ -206,6 +214,9 @@ public class SqlKeyword {
/** Keyword: RESTRICT. */
public static final String RESTRICT = "RESTRICT";
+ /** Keyword: ROLLBACK. */
+ public static final String ROLLBACK = "ROLLBACK";
+
/** Keyword: SET. */
public static final String SET = "SET";
@@ -221,6 +232,9 @@ public class SqlKeyword {
/** Keyword: SPATIAL. */
public static final String SPATIAL = "SPATIAL";
+ /** Keyword: START. */
+ public static final String START = "START";
+
/** Keyword: STREAMING. */
public static final String STREAMING = "STREAMING";
@@ -236,6 +250,9 @@ public class SqlKeyword {
/** Keyword: TINYINT. */
public static final String TINYINT = "TINYINT";
+ /** Keyword: TRANSACTION. */
+ public static final String TRANSACTION = "TRANSACTION";
+
/** Keyword: UNIQUE. */
public static final String UNIQUE = "UNIQUE";
@@ -251,6 +268,9 @@ public class SqlKeyword {
/** Keyword: VARCHAR_CASESENSITIVE. */
public static final String VARCHAR_CASESENSITIVE = "VARCHAR_CASESENSITIVE";
+ /** Keyword: WORK. */
+ public static final String WORK = "WORK";
+
/** Keyword: YEAR. */
public static final String YEAR = "YEAR";
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
index d46863a..0be2623 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParser.java
@@ -17,17 +17,22 @@
package org.apache.ignite.internal.sql;
+import org.apache.ignite.internal.sql.command.SqlBeginTransactionCommand;
import org.apache.ignite.internal.sql.command.SqlAlterTableCommand;
import org.apache.ignite.internal.sql.command.SqlAlterUserCommand;
import org.apache.ignite.internal.sql.command.SqlBulkLoadCommand;
import org.apache.ignite.internal.sql.command.SqlCommand;
+import org.apache.ignite.internal.sql.command.SqlCommitTransactionCommand;
import org.apache.ignite.internal.sql.command.SqlCreateIndexCommand;
import org.apache.ignite.internal.sql.command.SqlCreateUserCommand;
import org.apache.ignite.internal.sql.command.SqlDropIndexCommand;
import org.apache.ignite.internal.sql.command.SqlSetStreamingCommand;
+import org.apache.ignite.internal.sql.command.SqlRollbackTransactionCommand;
import org.apache.ignite.internal.sql.command.SqlDropUserCommand;
import org.jetbrains.annotations.Nullable;
+import static org.apache.ignite.internal.sql.SqlKeyword.BEGIN;
+import static org.apache.ignite.internal.sql.SqlKeyword.COMMIT;
import static org.apache.ignite.internal.sql.SqlKeyword.ALTER;
import static org.apache.ignite.internal.sql.SqlKeyword.COPY;
import static org.apache.ignite.internal.sql.SqlKeyword.CREATE;
@@ -35,15 +40,21 @@ import static org.apache.ignite.internal.sql.SqlKeyword.DROP;
import static org.apache.ignite.internal.sql.SqlKeyword.HASH;
import static org.apache.ignite.internal.sql.SqlKeyword.INDEX;
import static org.apache.ignite.internal.sql.SqlKeyword.PRIMARY;
+import static org.apache.ignite.internal.sql.SqlKeyword.ROLLBACK;
import static org.apache.ignite.internal.sql.SqlKeyword.SET;
import static org.apache.ignite.internal.sql.SqlKeyword.SPATIAL;
+import static org.apache.ignite.internal.sql.SqlKeyword.START;
+import static org.apache.ignite.internal.sql.SqlKeyword.TRANSACTION;
import static org.apache.ignite.internal.sql.SqlKeyword.STREAMING;
import static org.apache.ignite.internal.sql.SqlKeyword.TABLE;
import static org.apache.ignite.internal.sql.SqlKeyword.UNIQUE;
+import static org.apache.ignite.internal.sql.SqlKeyword.WORK;
import static org.apache.ignite.internal.sql.SqlKeyword.USER;
import static org.apache.ignite.internal.sql.SqlParserUtils.errorUnexpectedToken;
import static org.apache.ignite.internal.sql.SqlParserUtils.errorUnsupportedIfMatchesKeyword;
import static org.apache.ignite.internal.sql.SqlParserUtils.matchesKeyword;
+import static org.apache.ignite.internal.sql.SqlParserUtils.skipIfMatchesKeyword;
+import static org.apache.ignite.internal.sql.SqlParserUtils.skipIfMatchesOptionalKeyword;
/**
* SQL parser.
@@ -102,6 +113,16 @@ public class SqlParser {
SqlCommand cmd = null;
switch (lex.token()) {
+ case BEGIN:
+ cmd = processBegin();
+
+ break;
+
+ case COMMIT:
+ cmd = processCommit();
+
+ break;
+
case CREATE:
cmd = processCreate();
@@ -112,6 +133,16 @@ public class SqlParser {
break;
+ case ROLLBACK:
+ cmd = processRollback();
+
+ break;
+
+ case START:
+ cmd = processStart();
+
+ break;
+
case COPY:
try {
cmd = processCopy();
@@ -139,7 +170,7 @@ public class SqlParser {
return cmd;
}
else
- throw errorUnexpectedToken(lex, CREATE, DROP, ALTER, COPY, SET);
+ throw errorUnexpectedToken(lex, BEGIN, COMMIT, CREATE, DROP, ROLLBACK, COPY, SET, ALTER, START);
case QUOTED:
case MINUS:
@@ -154,6 +185,30 @@ public class SqlParser {
}
/**
+ * Process BEGIN keyword.
+ *
+ * @return Command.
+ */
+ private SqlCommand processBegin() {
+ skipIfMatchesOptionalKeyword(lex, TRANSACTION);
+
+ skipIfMatchesOptionalKeyword(lex, WORK);
+
+ return new SqlBeginTransactionCommand();
+ }
+
+ /**
+ * Process COMMIT keyword.
+ *
+ * @return Command.
+ */
+ private SqlCommand processCommit() {
+ skipIfMatchesOptionalKeyword(lex, TRANSACTION);
+
+ return new SqlCommitTransactionCommand();
+ }
+
+ /**
* Process SET keyword.
*
* @return Command.
@@ -214,7 +269,7 @@ public class SqlParser {
errorUnsupportedIfMatchesKeyword(lex, HASH, PRIMARY, UNIQUE);
}
- throw errorUnexpectedToken(lex, INDEX, SPATIAL);
+ throw errorUnexpectedToken(lex, INDEX, SPATIAL, USER);
}
/**
@@ -242,7 +297,29 @@ public class SqlParser {
return cmd.parse(lex);
}
- throw errorUnexpectedToken(lex, INDEX);
+ throw errorUnexpectedToken(lex, INDEX, USER);
+ }
+
+ /**
+ * Process ROLLBACK keyword.
+ *
+ * @return Command.
+ */
+ private SqlCommand processRollback() {
+ skipIfMatchesOptionalKeyword(lex, TRANSACTION);
+
+ return new SqlRollbackTransactionCommand();
+ }
+
+ /**
+ * Process START keyword.
+ *
+ * @return Command.
+ */
+ private SqlCommand processStart() {
+ skipIfMatchesKeyword(lex, TRANSACTION);
+
+ return new SqlBeginTransactionCommand();
}
/**
@@ -270,6 +347,6 @@ public class SqlParser {
return cmd.parse(lex);
}
- throw errorUnexpectedToken(lex, TABLE);
+ throw errorUnexpectedToken(lex, TABLE, USER);
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParserUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParserUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParserUtils.java
index e99af1d..074bffa 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParserUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/SqlParserUtils.java
@@ -261,6 +261,20 @@ public class SqlParserUtils {
}
/**
+ * Skip token if it matches expected keyword by using lookahead.
+ * If next token is not what we expect, no shift is done.
+ *
+ * @param lex Lexer.
+ * @param expKeyword Expected keyword.
+ */
+ static void skipIfMatchesOptionalKeyword(SqlLexer lex, String expKeyword) {
+ SqlLexerToken nextTok = lex.lookAhead();
+
+ if (matchesKeyword(nextTok, expKeyword))
+ lex.shift();
+ }
+
+ /**
* Skip next token if it matches expected type.
*
* @param lex Lexer.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlBeginTransactionCommand.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlBeginTransactionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlBeginTransactionCommand.java
new file mode 100644
index 0000000..e890cc4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlBeginTransactionCommand.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sql.command;
+
+import org.apache.ignite.internal.sql.SqlLexer;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * BEGIN [TRANSACTION] command.
+ */
+public class SqlBeginTransactionCommand implements SqlCommand {
+ /** {@inheritDoc} */
+ @Override public SqlCommand parse(SqlLexer lex) {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String schemaName() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void schemaName(String schemaName) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SqlBeginTransactionCommand.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCommitTransactionCommand.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCommitTransactionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCommitTransactionCommand.java
new file mode 100644
index 0000000..da14dea
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlCommitTransactionCommand.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sql.command;
+
+import org.apache.ignite.internal.sql.SqlLexer;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * COMMIT command.
+ */
+public class SqlCommitTransactionCommand implements SqlCommand {
+ /** {@inheritDoc} */
+ @Override public SqlCommand parse(SqlLexer lex) {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String schemaName() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void schemaName(String schemaName) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SqlCommitTransactionCommand.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlRollbackTransactionCommand.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlRollbackTransactionCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlRollbackTransactionCommand.java
new file mode 100644
index 0000000..341b794
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/command/SqlRollbackTransactionCommand.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sql.command;
+
+import org.apache.ignite.internal.sql.SqlLexer;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * ROLLBACK command.
+ */
+public class SqlRollbackTransactionCommand implements SqlCommand {
+ /** {@inheritDoc} */
+ @Override public SqlCommand parse(SqlLexer lex) {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String schemaName() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void schemaName(String schemaName) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SqlRollbackTransactionCommand.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/transactions/IgniteTxMvccVersionCheckedException.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/transactions/IgniteTxMvccVersionCheckedException.java b/modules/core/src/main/java/org/apache/ignite/internal/transactions/IgniteTxMvccVersionCheckedException.java
new file mode 100644
index 0000000..8a0a54b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/transactions/IgniteTxMvccVersionCheckedException.java
@@ -0,0 +1,47 @@
+/*
+ * 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.transactions;
+
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Exception thrown whenever transaction enters an unknown state.
+ */
+public class IgniteTxMvccVersionCheckedException extends IgniteCheckedException {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Creates new exception with given error message.
+ *
+ * @param msg Error message.
+ */
+ public IgniteTxMvccVersionCheckedException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Creates new exception with given error message and optional nested exception.
+ *
+ * @param msg Error message.
+ * @param cause Optional nested exception (can be <tt>null</tt>).
+ */
+ public IgniteTxMvccVersionCheckedException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridCompoundFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridCompoundFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridCompoundFuture.java
index a724060..87f5882 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridCompoundFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridCompoundFuture.java
@@ -285,12 +285,12 @@ public class GridCompoundFuture<T, R> extends GridFutureAdapter<R> implements Ig
onDone(rdc != null ? rdc.reduce() : null);
}
catch (RuntimeException e) {
- logError(null, "Failed to execute compound future reducer: " + this, e);
+ logError(logger(), "Failed to execute compound future reducer: " + this, e);
onDone(e);
}
catch (AssertionError e) {
- logError(null, "Failed to execute compound future reducer: " + this, e);
+ logError(logger(), "Failed to execute compound future reducer: " + this, e);
onDone(e);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java b/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
index b36bf16..7b68a6b 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.managers.communication.GridIoMessage;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiInClosure;
import org.apache.ignite.lang.IgniteBiPredicate;
import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.lang.IgnitePredicate;
@@ -65,6 +66,9 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
/** */
private IgniteBiPredicate<ClusterNode, Message> blockP;
+ /** */
+ private volatile IgniteBiInClosure<ClusterNode, Message> c;
+
/**
* @param node Node.
* @return Test SPI.
@@ -87,6 +91,9 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
Message msg0 = ioMsg.message();
+ if (c != null)
+ c.apply(node, msg0);
+
synchronized (this) {
boolean record = (recordClasses != null && recordClasses.contains(msg0.getClass())) ||
(recordP != null && recordP.apply(node, msg0));
@@ -196,20 +203,16 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
* @throws InterruptedException If interrupted.
*/
public void waitForBlocked() throws InterruptedException {
- synchronized (this) {
- while (blockedMsgs.isEmpty())
- wait();
- }
+ waitForBlocked(1);
}
/**
- * @param cnt Number of messages to wait.
- *
+ * @param size Number of messages to wait for.
* @throws InterruptedException If interrupted.
*/
- public void waitForBlocked(int cnt) throws InterruptedException {
+ public void waitForBlocked(int size) throws InterruptedException {
synchronized (this) {
- while (blockedMsgs.size() < cnt)
+ while (blockedMsgs.size() < size)
wait();
}
}
@@ -240,6 +243,13 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
}
/**
+ * @param c Message closure.
+ */
+ public void closure(IgniteBiInClosure<ClusterNode, Message> c) {
+ this.c = c;
+ }
+
+ /**
* @param blockP Message block predicate.
*/
public void blockMessages(IgniteBiPredicate<ClusterNode, Message> blockP) {
[04/45] ignite git commit: IGNITE-9370 Fixed execution of REST
commands in demo mode.
Posted by ag...@apache.org.
IGNITE-9370 Fixed execution of REST commands in demo mode.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/4ea23b0f
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/4ea23b0f
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/4ea23b0f
Branch: refs/heads/ignite-9273
Commit: 4ea23b0f35736b5698bc66ce92dce0b57f600792
Parents: 2b9fc2e
Author: Vasiliy Sisko <vs...@gridgain.com>
Authored: Thu Aug 30 18:11:21 2018 +0700
Committer: Andrey Novikov <an...@gridgain.com>
Committed: Thu Aug 30 18:11:21 2018 +0700
----------------------------------------------------------------------
modules/web-console/backend/app/browsersHandler.js | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea23b0f/modules/web-console/backend/app/browsersHandler.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/app/browsersHandler.js b/modules/web-console/backend/app/browsersHandler.js
index 5aade60..3ae91da 100644
--- a/modules/web-console/backend/app/browsersHandler.js
+++ b/modules/web-console/backend/app/browsersHandler.js
@@ -205,10 +205,11 @@ module.exports = {
nodeListeners(sock) {
// Return command result from grid to browser.
sock.on('node:rest', ({clusterId, params, credentials} = {}, cb) => {
- if (_.isNil(clusterId) || _.isNil(params))
+ const demo = sock.request._query.IgniteDemoMode === 'true';
+
+ if ((_.isNil(clusterId) && !demo) || _.isNil(params))
return cb('Invalid format of message: "node:rest"');
- const demo = sock.request._query.IgniteDemoMode === 'true';
const token = sock.request.user.token;
const agent = this._agentHnd.agent(token, demo, clusterId);
@@ -237,10 +238,11 @@ module.exports = {
// Return command result from grid to browser.
sock.on('node:visor', ({clusterId, params, credentials} = {}, cb) => {
- if (_.isNil(clusterId) || _.isNil(params))
+ const demo = sock.request._query.IgniteDemoMode === 'true';
+
+ if ((_.isNil(clusterId) && !demo) || _.isNil(params))
return cb('Invalid format of message: "node:visor"');
- const demo = sock.request._query.IgniteDemoMode === 'true';
const token = sock.request.user.token;
const {taskId, nids, args = []} = params;
[40/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
index 6c128a4..e24ecbb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java
@@ -46,6 +46,7 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcTableMeta;
import org.apache.ignite.internal.util.typedef.F;
import static java.sql.Connection.TRANSACTION_NONE;
+import static java.sql.Connection.TRANSACTION_REPEATABLE_READ;
import static java.sql.ResultSet.CONCUR_READ_ONLY;
import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
@@ -630,17 +631,19 @@ public class JdbcThinDatabaseMetadata implements DatabaseMetaData {
/** {@inheritDoc} */
@Override public int getDefaultTransactionIsolation() throws SQLException {
- return TRANSACTION_NONE;
+ return conn.igniteVersion().greaterThanEqual(2, 5, 0) ? TRANSACTION_REPEATABLE_READ :
+ TRANSACTION_NONE;
}
/** {@inheritDoc} */
@Override public boolean supportsTransactions() throws SQLException {
- return false;
+ return conn.igniteVersion().greaterThanEqual(2, 5, 0);
}
/** {@inheritDoc} */
@Override public boolean supportsTransactionIsolationLevel(int level) throws SQLException {
- return false;
+ return conn.igniteVersion().greaterThanEqual(2, 5, 0) &&
+ TRANSACTION_REPEATABLE_READ == level;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
index 30e446f..f0f7337 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinStatement.java
@@ -46,6 +46,8 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteResult;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResult;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResultInfo;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcStatementType;
+import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBulkLoadBatchRequest;
+import org.apache.ignite.internal.processors.odbc.jdbc.JdbcStatementType;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.sql.SqlKeyword;
import org.apache.ignite.internal.sql.SqlParseException;
@@ -208,7 +210,7 @@ public class JdbcThinStatement implements Statement {
}
JdbcResult res0 = conn.sendRequest(new JdbcQueryExecuteRequest(stmtType, schema, pageSize,
- maxRows, sql, args == null ? null : args.toArray(new Object[args.size()])));
+ maxRows, conn.getAutoCommit(), sql, args == null ? null : args.toArray(new Object[args.size()])));
assert res0 != null;
@@ -646,7 +648,8 @@ public class JdbcThinStatement implements Statement {
throw new SQLException("Batch is empty.");
try {
- JdbcBatchExecuteResult res = conn.sendRequest(new JdbcBatchExecuteRequest(conn.getSchema(), batch, false));
+ JdbcBatchExecuteResult res = conn.sendRequest(new JdbcBatchExecuteRequest(conn.getSchema(), batch,
+ conn.getAutoCommit(), false));
if (res.errorCode() != ClientListenerResponse.STATUS_SUCCESS) {
throw new BatchUpdateException(res.errorMessage(), IgniteQueryErrorCode.codeToSqlState(res.errorCode()),
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
index 6128d07..2c3f321 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
@@ -326,6 +326,7 @@ public class JdbcThinTcpIo {
writer.writeBoolean(connProps.isAutoCloseServerCursor());
writer.writeBoolean(connProps.isLazy());
writer.writeBoolean(connProps.isSkipReducerOnUpdate());
+ writer.writeString(connProps.nestedTxMode());
if (!F.isEmpty(connProps.getUsername())) {
assert ver.compareTo(VER_2_5_0) >= 0 : "Authentication is supported since 2.5";
@@ -374,8 +375,9 @@ public class JdbcThinTcpIo {
+ ", url=" + connProps.getUrl() + ']', SqlStateCode.CONNECTION_REJECTED);
}
- if (VER_2_4_0.equals(srvProtocolVer) || VER_2_3_0.equals(srvProtocolVer) ||
- VER_2_1_5.equals(srvProtocolVer))
+ if (VER_2_4_0.equals(srvProtocolVer)
+ || VER_2_3_0.equals(srvProtocolVer)
+ || VER_2_1_5.equals(srvProtocolVer))
handshake(srvProtocolVer);
else if (VER_2_1_0.equals(srvProtocolVer))
handshake_2_1_0();
@@ -541,8 +543,8 @@ public class JdbcThinTcpIo {
int cnt = !F.isEmpty(qrys) ? Math.min(MAX_BATCH_QRY_CNT, qrys.size()) : 0;
- // One additional byte for last batch flag.
- cap = cnt * DYNAMIC_SIZE_MSG_CAP + 1;
+ // One additional byte for autocommit and last batch flags.
+ cap = cnt * DYNAMIC_SIZE_MSG_CAP + 2;
}
else if (req instanceof JdbcQueryCloseRequest)
cap = QUERY_CLOSE_MSG_SIZE;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
index 8d9a700..f515d57 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
@@ -67,6 +67,7 @@ import org.apache.ignite.internal.managers.GridManagerAdapter;
import org.apache.ignite.internal.managers.deployment.GridDeployment;
import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccMessage;
import org.apache.ignite.internal.processors.platform.message.PlatformMessageFilter;
import org.apache.ignite.internal.processors.pool.PoolProcessor;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
@@ -101,6 +102,7 @@ import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
import static org.apache.ignite.events.EventType.EVT_NODE_JOINED;
import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
+import static org.apache.ignite.internal.GridTopic.TOPIC_CACHE_COORDINATOR;
import static org.apache.ignite.internal.GridTopic.TOPIC_COMM_USER;
import static org.apache.ignite.internal.GridTopic.TOPIC_IO_TEST;
import static org.apache.ignite.internal.managers.communication.GridIoPolicy.AFFINITY_POOL;
@@ -1112,6 +1114,17 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
return;
}
+ if (msg.topicOrdinal() == TOPIC_CACHE_COORDINATOR.ordinal()) {
+ MvccMessage msg0 = (MvccMessage)msg.message();
+
+ // see IGNITE-8609
+ /*if (msg0.processedFromNioThread())
+ c.run();
+ else*/
+ ctx.getStripedExecutorService().execute(-1, c);
+
+ return;
+ }
if (plc == GridIoPolicy.SYSTEM_POOL && msg.partition() != GridIoMessage.STRIPE_DISABLED_PART) {
ctx.getStripedExecutorService().execute(msg.partition(), c);
@@ -1648,6 +1661,9 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
if (e.getCause() instanceof ClusterTopologyCheckedException)
throw (ClusterTopologyCheckedException)e.getCause();
+ if (!ctx.discovery().alive(node))
+ throw new ClusterTopologyCheckedException("Failed to send message, node left: " + node.id());
+
throw new IgniteCheckedException("Failed to send message (node may have left the grid or " +
"TCP connection cannot be established due to firewall issues) " +
"[node=" + node + ", topic=" + topic +
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
index 581c32e..8dddd8b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoMessageFactory.java
@@ -47,13 +47,13 @@ import org.apache.ignite.internal.processors.cache.CacheInvokeDirectResult;
import org.apache.ignite.internal.processors.cache.CacheObjectByteArrayImpl;
import org.apache.ignite.internal.processors.cache.CacheObjectImpl;
import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo;
import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.GridChangeGlobalStateMessageResponse;
import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl;
import org.apache.ignite.internal.processors.cache.WalStateAckMessage;
import org.apache.ignite.internal.processors.cache.binary.MetadataRequestMessage;
import org.apache.ignite.internal.processors.cache.binary.MetadataResponseMessage;
-import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.latch.LatchAckMessage;
import org.apache.ignite.internal.processors.cache.distributed.GridCacheTtlUpdateRequest;
import org.apache.ignite.internal.processors.cache.distributed.GridCacheTxRecoveryRequest;
import org.apache.ignite.internal.processors.cache.distributed.GridCacheTxRecoveryResponse;
@@ -68,11 +68,15 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffini
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffinityAssignmentResponse;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLockRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLockResponse;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsUpdateCountersMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishResponse;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxOnePhaseCommitAckRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxPrepareRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxPrepareResponse;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxQueryEnlistRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxQueryEnlistResponse;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxQueryFirstEnlistRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtUnlockRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicDeferredUpdateResponse;
import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicNearResponse;
@@ -97,6 +101,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Gri
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsFullMessage;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsSingleMessage;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsSingleRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.latch.LatchAckMessage;
import org.apache.ignite.internal.processors.cache.distributed.near.CacheVersionedValue;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetResponse;
@@ -108,7 +113,24 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxFi
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxFinishResponse;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxPrepareRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxPrepareResponse;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistResponse;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistResponse;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearUnlockRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotWithoutTxs;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestQueryCntr;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestQueryId;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTx;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTxAndQueryCntr;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTxAndQueryId;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccActiveQueriesMessage;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccFutureResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccQuerySnapshotRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccSnapshotResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccTxSnapshotRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccWaitTxsRequest;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryRequest;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryResponse;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
@@ -927,6 +949,116 @@ public class GridIoMessageFactory implements MessageFactory {
break;
+ case 136:
+ msg = new MvccTxSnapshotRequest();
+
+ break;
+
+ case 137:
+ msg = new MvccAckRequestTx();
+
+ break;
+
+ case 138:
+ msg = new MvccFutureResponse();
+
+ break;
+
+ case 139:
+ msg = new MvccQuerySnapshotRequest();
+
+ break;
+
+ case 140:
+ msg = new MvccAckRequestQueryCntr();
+
+ break;
+
+ case 141:
+ msg = new MvccSnapshotResponse();
+
+ break;
+
+ case 142:
+ msg = new MvccWaitTxsRequest();
+
+ break;
+
+ case 143:
+ msg = new GridCacheMvccEntryInfo();
+
+ break;
+
+ case 144:
+ msg = new GridDhtTxQueryEnlistResponse();
+
+ break;
+
+ case 145:
+ msg = new MvccAckRequestQueryId();
+
+ break;
+
+ case 146:
+ msg = new MvccAckRequestTxAndQueryCntr();
+
+ break;
+
+ case 147:
+ msg = new MvccAckRequestTxAndQueryId();
+
+ break;
+
+ case 148:
+ msg = new MvccVersionImpl();
+
+ break;
+
+ case 149:
+ msg = new MvccActiveQueriesMessage();
+
+ break;
+
+ case 150:
+ msg = new MvccSnapshotWithoutTxs();
+
+ break;
+
+ case 151:
+ msg = new GridNearTxQueryEnlistRequest();
+
+ break;
+
+ case 152:
+ msg = new GridNearTxQueryEnlistResponse();
+
+ break;
+
+ case 153:
+ msg = new GridNearTxQueryResultsEnlistRequest();
+
+ break;
+
+ case 154:
+ msg = new GridNearTxQueryResultsEnlistResponse();
+
+ break;
+
+ case 155:
+ msg = new GridDhtTxQueryEnlistRequest();
+
+ break;
+
+ case 156:
+ msg = new GridDhtTxQueryFirstEnlistRequest();
+
+ break;
+
+ case 157:
+ msg = new GridDhtPartitionsUpdateCountersMap();
+
+ break;
+
// [-3..119] [124..129] [-23..-27] [-36..-55]- this
// [120..123] - DR
// [-4..-22, -30..-35] - SQL
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
index 8cdcbf3..84bcab1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
@@ -26,6 +26,7 @@ import java.util.UUID;
import org.apache.ignite.cluster.BaselineNode;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
import org.apache.ignite.internal.util.GridConcurrentHashSet;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -108,10 +109,14 @@ public class DiscoCache {
/** */
private final P1<ClusterNode> aliveNodePred;
+ /** */
+ private final MvccCoordinator mvccCrd;
+
/**
* @param topVer Topology version.
* @param state Current cluster state.
* @param loc Local node.
+ * @param mvccCrd MVCC coordinator node.
* @param rmtNodes Remote nodes.
* @param allNodes All nodes.
* @param srvNodes Server nodes.
@@ -130,6 +135,7 @@ public class DiscoCache {
AffinityTopologyVersion topVer,
DiscoveryDataClusterState state,
ClusterNode loc,
+ MvccCoordinator mvccCrd,
List<ClusterNode> rmtNodes,
List<ClusterNode> allNodes,
List<ClusterNode> srvNodes,
@@ -148,6 +154,7 @@ public class DiscoCache {
this.topVer = topVer;
this.state = state;
this.loc = loc;
+ this.mvccCrd = mvccCrd;
this.rmtNodes = rmtNodes;
this.allNodes = allNodes;
this.srvNodes = srvNodes;
@@ -157,7 +164,7 @@ public class DiscoCache {
this.allCacheNodes = allCacheNodes;
this.cacheGrpAffNodes = cacheGrpAffNodes;
this.nodeMap = nodeMap;
- alives.addAll(alives0);
+ this.alives.addAll(alives0);
this.minNodeVer = minNodeVer;
this.minSrvNodeVer = minSrvNodeVer;
this.nodeIdToConsIdx = nodeIdToConsIdx;
@@ -177,6 +184,13 @@ public class DiscoCache {
}
/**
+ * @return Mvcc coordinator node.
+ */
+ @Nullable public MvccCoordinator mvccCoordinator() {
+ return mvccCrd;
+ }
+
+ /**
* @return Topology version.
*/
public AffinityTopologyVersion version() {
@@ -461,6 +475,7 @@ public class DiscoCache {
ver,
state == null ? this.state : state,
loc,
+ mvccCrd,
rmtNodes,
allNodes,
srvNodes,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
index a13f31e..d19e08b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
@@ -84,6 +84,7 @@ import org.apache.ignite.internal.processors.cache.DynamicCacheChangeRequest;
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cluster.BaselineTopology;
import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateFinishMessage;
@@ -159,6 +160,7 @@ import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_LATE_AFFINITY
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_MACS;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_MARSHALLER_USE_BINARY_STRING_SER_VER_2;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_MARSHALLER_USE_DFLT_SUID;
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_MVCC_ENABLED;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_OFFHEAP_SIZE;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_PEER_CLASSLOADING;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_PHY_RAM;
@@ -644,6 +646,8 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
updateClientNodes(node.id());
}
+ ctx.coordinators().onDiscoveryEvent(type, topSnapshot, topVer);
+
boolean locJoinEvt = type == EVT_NODE_JOINED && node.id().equals(locNode.id());
ChangeGlobalStateFinishMessage stateFinishMsg = null;
@@ -1204,6 +1208,8 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
Boolean locSrvcCompatibilityEnabled = locNode.attribute(ATTR_SERVICES_COMPATIBILITY_MODE);
Boolean locSecurityCompatibilityEnabled = locNode.attribute(ATTR_SECURITY_COMPATIBILITY_MODE);
+ Boolean locMvccEnabled = locNode.attribute(ATTR_MVCC_ENABLED);
+
for (ClusterNode n : nodes) {
int rmtJvmMajVer = nodeJavaMajorVersion(n);
@@ -1301,6 +1307,17 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
", locNodeId=" + locNode.id() + ", rmtNode=" + U.toShortString(n) + "]");
}
+ Boolean rmtMvccEnabled = n.attribute(ATTR_MVCC_ENABLED);
+
+ if (!F.eq(locMvccEnabled, rmtMvccEnabled)) {
+ throw new IgniteCheckedException("Remote node has MVCC mode different from local " +
+ "[locId8=" + U.id8(locNode.id()) +
+ ", locMvccMode=" + (Boolean.TRUE.equals(locMvccEnabled) ? "ENABLED" : "DISABLED") +
+ ", rmtId8=" + U.id8(n.id()) +
+ ", rmtMvccMode=" + (Boolean.TRUE.equals(rmtMvccEnabled) ? "ENABLED" : "DISABLED") +
+ ", rmtAddrs=" + U.addressesAsString(n) + ", rmtNode=" + U.toShortString(n) + "]");
+ }
+
if (n.version().compareToIgnoreTimestamp(SERVICE_PERMISSIONS_SINCE) >= 0
&& ctx.security().enabled() // Matters only if security enabled.
) {
@@ -2353,6 +2370,8 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
Collection<ClusterNode> topSnapshot) {
assert topSnapshot.contains(loc);
+ MvccCoordinator mvccCrd = ctx.coordinators().coordinatorFromDiscoveryEvent();
+
HashSet<UUID> alives = U.newHashSet(topSnapshot.size());
HashMap<UUID, ClusterNode> nodeMap = U.newHashMap(topSnapshot.size());
@@ -2454,6 +2473,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
topVer,
state,
loc,
+ mvccCrd,
Collections.unmodifiableList(rmtNodes),
Collections.unmodifiableList(allNodes),
Collections.unmodifiableList(srvNodes),
@@ -3362,6 +3382,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
topVer,
discoCache.state(),
discoCache.localNode(),
+ discoCache.mvccCoordinator(),
discoCache.remoteNodes(),
allNodes,
discoCache.serverNodes(),
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
index 5475bef..ff0c66a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
@@ -26,6 +26,7 @@ import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheSharedManager;
import org.apache.ignite.internal.processors.cache.StoredCacheData;
+import org.apache.ignite.internal.processors.cache.persistence.AllocatedPageTracker;
import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
/**
@@ -43,6 +44,18 @@ public interface IgnitePageStoreManager extends GridCacheSharedManager, IgniteCh
public void finishRecover() throws IgniteCheckedException;
/**
+ * Initializes disk store structures.
+ *
+ * @param cacheId Cache id.
+ * @param partitions Partitions count.
+ * @param workingDir Working directory.
+ * @param tracker Allocation tracker.
+ * @throws IgniteCheckedException If failed.
+ */
+ void initialize(int cacheId, int partitions, String workingDir, AllocatedPageTracker tracker)
+ throws IgniteCheckedException;
+
+ /**
* Callback called when a cache is starting.
*
* @param grpDesc Cache group descriptor.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
index 585336a..a555aae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
@@ -184,7 +184,16 @@ public abstract class WALRecord {
RESERVED,
/** Rotated id part record. */
- ROTATED_ID_PART_RECORD;
+ ROTATED_ID_PART_RECORD,
+
+ /** */
+ MVCC_DATA_PAGE_MARK_UPDATED_RECORD,
+
+ /** */
+ MVCC_DATA_PAGE_TX_STATE_HINT_UPDATED_RECORD,
+
+ /** */
+ MVCC_DATA_PAGE_NEW_TX_STATE_HINT_UPDATED_RECORD;
/** */
private static final RecordType[] VALS = RecordType.values();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccMarkUpdatedRecord.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccMarkUpdatedRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccMarkUpdatedRecord.java
new file mode 100644
index 0000000..5e89f8e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccMarkUpdatedRecord.java
@@ -0,0 +1,103 @@
+/*
+ * 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.pagemem.wal.record.delta;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * MVCC mark updated record.
+ */
+public class DataPageMvccMarkUpdatedRecord extends PageDeltaRecord {
+ /** */
+ private int itemId;
+
+ /** */
+ private long newMvccCrd;
+
+ /** */
+ private long newMvccCntr;
+
+ /** */
+ private int newMvccOpCntr;
+
+ /**
+ * @param grpId Cache group ID.
+ * @param pageId Page ID.
+ * @param itemId Item id.
+ * @param newMvccCrd New MVCC coordinator version.
+ * @param newMvccCntr New MVCC counter version.
+ * @param newMvccOpCntr New MVCC operation counter.
+ */
+ public DataPageMvccMarkUpdatedRecord(int grpId, long pageId, int itemId, long newMvccCrd, long newMvccCntr, int newMvccOpCntr) {
+ super(grpId, pageId);
+
+ this.itemId = itemId;
+ this.newMvccCrd = newMvccCrd;
+ this.newMvccCntr = newMvccCntr;
+ this.newMvccOpCntr = newMvccOpCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void applyDelta(PageMemory pageMem, long pageAddr) throws IgniteCheckedException {
+ DataPageIO io = PageIO.getPageIO(pageAddr);
+
+ io.updateNewVersion(pageAddr, itemId, pageMem.pageSize(), newMvccCrd, newMvccCntr, newMvccOpCntr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public RecordType type() {
+ return RecordType.MVCC_DATA_PAGE_MARK_UPDATED_RECORD;
+ }
+
+ /**
+ * @return Item id.
+ */
+ public int itemId() {
+ return itemId;
+ }
+
+ /**
+ * @return New MVCC coordinator version.
+ */
+ public long newMvccCrd() {
+ return newMvccCrd;
+ }
+
+ /**
+ * @return New MVCC counter version.
+ */
+ public long newMvccCntr() {
+ return newMvccCntr;
+ }
+
+ /**
+ * @return New MVCC operation counter.
+ */
+ public int newMvccOpCntr() {
+ return newMvccOpCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(DataPageMvccMarkUpdatedRecord.class, this, "super", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateNewTxStateHintRecord.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateNewTxStateHintRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateNewTxStateHintRecord.java
new file mode 100644
index 0000000..4a244a1
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateNewTxStateHintRecord.java
@@ -0,0 +1,79 @@
+/*
+ * 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.pagemem.wal.record.delta;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * MVCC update tx state hint record.
+ */
+public class DataPageMvccUpdateNewTxStateHintRecord extends PageDeltaRecord {
+ /** */
+ private int itemId;
+
+ /** */
+ private byte txState;
+
+ /**
+ * @param grpId Cache group ID.
+ * @param pageId Page ID.
+ * @param itemId Item id.
+ * @param txState Tx state hint.
+ */
+ public DataPageMvccUpdateNewTxStateHintRecord(int grpId, long pageId, int itemId, byte txState) {
+ super(grpId, pageId);
+
+ this.itemId = itemId;
+ this.txState = txState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void applyDelta(PageMemory pageMem, long pageAddr) throws IgniteCheckedException {
+ DataPageIO io = PageIO.getPageIO(pageAddr);
+
+ io.updateNewTxState(pageAddr, itemId, pageMem.pageSize(), txState);
+ }
+
+ /** {@inheritDoc} */
+ @Override public RecordType type() {
+ return RecordType.MVCC_DATA_PAGE_NEW_TX_STATE_HINT_UPDATED_RECORD;
+ }
+
+ /**
+ * @return Item id.
+ */
+ public int itemId() {
+ return itemId;
+ }
+
+ /**
+ * @return Tx state hint.
+ */
+ public byte txState() {
+ return txState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(DataPageMvccUpdateNewTxStateHintRecord.class, this, "super", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateTxStateHintRecord.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateTxStateHintRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateTxStateHintRecord.java
new file mode 100644
index 0000000..7e53609
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/DataPageMvccUpdateTxStateHintRecord.java
@@ -0,0 +1,79 @@
+/*
+ * 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.pagemem.wal.record.delta;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * MVCC update tx state hint record.
+ */
+public class DataPageMvccUpdateTxStateHintRecord extends PageDeltaRecord {
+ /** */
+ private int itemId;
+
+ /** */
+ private byte txState;
+
+ /**
+ * @param grpId Cache group ID.
+ * @param pageId Page ID.
+ * @param itemId Item id.
+ * @param txState Tx state hint.
+ */
+ public DataPageMvccUpdateTxStateHintRecord(int grpId, long pageId, int itemId, byte txState) {
+ super(grpId, pageId);
+
+ this.itemId = itemId;
+ this.txState = txState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void applyDelta(PageMemory pageMem, long pageAddr) throws IgniteCheckedException {
+ DataPageIO io = PageIO.getPageIO(pageAddr);
+
+ io.updateTxState(pageAddr, itemId, pageMem.pageSize(), txState);
+ }
+
+ /** {@inheritDoc} */
+ @Override public RecordType type() {
+ return RecordType.MVCC_DATA_PAGE_TX_STATE_HINT_UPDATED_RECORD;
+ }
+
+ /**
+ * @return Item id.
+ */
+ public int itemId() {
+ return itemId;
+ }
+
+ /**
+ * @return Tx state hint.
+ */
+ public byte txState() {
+ return txState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(DataPageMvccUpdateTxStateHintRecord.class, this, "super", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
index f78ab60..1c321d3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
@@ -17,12 +17,12 @@
package org.apache.ignite.internal.processors.affinity;
-import org.apache.ignite.cluster.ClusterNode;
-
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
/**
* Cached affinity calculations.
@@ -90,4 +90,9 @@ public interface AffinityAssignment {
* @return Backup partitions for specified node ID.
*/
public Set<Integer> backupPartitions(UUID nodeId);
+
+ /**
+ * @return Mvcc coordinator.
+ */
+ public MvccCoordinator mvccCoordinator();
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignment.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignment.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignment.java
index cbec1a1..f96bc9d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignment.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignment.java
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.util.typedef.internal.S;
/**
@@ -40,6 +41,9 @@ public class GridAffinityAssignment implements AffinityAssignment, Serializable
/** Topology version. */
private final AffinityTopologyVersion topVer;
+ /** */
+ private final MvccCoordinator mvccCrd;
+
/** Collection of calculated affinity nodes. */
private List<List<ClusterNode>> assignment;
@@ -73,6 +77,7 @@ public class GridAffinityAssignment implements AffinityAssignment, Serializable
this.topVer = topVer;
primary = new HashMap<>();
backup = new HashMap<>();
+ mvccCrd = null;
clientEvtChange = false;
}
@@ -83,7 +88,8 @@ public class GridAffinityAssignment implements AffinityAssignment, Serializable
*/
GridAffinityAssignment(AffinityTopologyVersion topVer,
List<List<ClusterNode>> assignment,
- List<List<ClusterNode>> idealAssignment) {
+ List<List<ClusterNode>> idealAssignment,
+ MvccCoordinator mvccCrd) {
assert topVer != null;
assert assignment != null;
assert idealAssignment != null;
@@ -91,6 +97,7 @@ public class GridAffinityAssignment implements AffinityAssignment, Serializable
this.topVer = topVer;
this.assignment = assignment;
this.idealAssignment = idealAssignment.equals(assignment) ? assignment : idealAssignment;
+ this.mvccCrd = mvccCrd;
primary = new HashMap<>();
backup = new HashMap<>();
@@ -110,6 +117,7 @@ public class GridAffinityAssignment implements AffinityAssignment, Serializable
idealAssignment = aff.idealAssignment;
primary = aff.primary;
backup = aff.backup;
+ mvccCrd = aff.mvccCrd;
clientEvtChange = true;
}
@@ -283,6 +291,11 @@ public class GridAffinityAssignment implements AffinityAssignment, Serializable
}
/** {@inheritDoc} */
+ @Override public MvccCoordinator mvccCoordinator() {
+ return mvccCrd;
+ }
+
+ /** {@inheritDoc} */
@Override public int hashCode() {
return topVer.hashCode();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
index 34e2b0a..cc2c17c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
@@ -43,6 +43,7 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.cluster.NodeOrderComparator;
import org.apache.ignite.internal.managers.discovery.DiscoCache;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.processors.cache.ExchangeDiscoveryEvents;
import org.apache.ignite.internal.processors.cluster.BaselineTopology;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
@@ -202,11 +203,25 @@ public class GridAffinityAssignmentCache {
* @param affAssignment Affinity assignment for topology version.
*/
public void initialize(AffinityTopologyVersion topVer, List<List<ClusterNode>> affAssignment) {
+ MvccCoordinator mvccCrd = ctx.cache().context().coordinators().currentCoordinator(topVer);
+
+ initialize(topVer, affAssignment, mvccCrd);
+ }
+
+ /**
+ * Initializes affinity with given topology version and assignment.
+ *
+ * @param topVer Topology version.
+ * @param affAssignment Affinity assignment for topology version.
+ * @param mvccCrd Mvcc coordinator.
+ */
+ public void initialize(AffinityTopologyVersion topVer, List<List<ClusterNode>> affAssignment, MvccCoordinator mvccCrd) {
assert topVer.compareTo(lastVersion()) >= 0 : "[topVer = " + topVer + ", last=" + lastVersion() + ']';
assert idealAssignment != null;
+ assert mvccCrd == null || topVer.compareTo(mvccCrd.topologyVersion()) >= 0 : "[mvccCrd=" + mvccCrd + ", topVer=" + topVer + ']';
- GridAffinityAssignment assignment = new GridAffinityAssignment(topVer, affAssignment, idealAssignment);
+ GridAffinityAssignment assignment = new GridAffinityAssignment(topVer, affAssignment, idealAssignment, mvccCrd);
HistoryAffinityAssignment hAff = affCache.put(topVer, new HistoryAffinityAssignment(assignment));
@@ -745,7 +760,9 @@ public class GridAffinityAssignmentCache {
idealAssignment(aff.idealAssignment());
- initialize(aff.lastVersion(), aff.assignments(aff.lastVersion()));
+ AffinityAssignment assign = aff.cachedAffinity(aff.lastVersion());
+
+ initialize(aff.lastVersion(), assign.assignment(), assign.mvccCoordinator());
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
index 08333c3..4a0908c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityProcessor.java
@@ -423,7 +423,7 @@ public class GridAffinityProcessor extends GridProcessorAdapter {
try {
GridAffinityAssignment assign = assign0 instanceof GridAffinityAssignment ?
(GridAffinityAssignment)assign0 :
- new GridAffinityAssignment(topVer, assign0.assignment(), assign0.idealAssignment());
+ new GridAffinityAssignment(topVer, assign0.assignment(), assign0.idealAssignment(), assign0.mvccCoordinator());
AffinityInfo info = new AffinityInfo(
cctx.config().getAffinity(),
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityUtils.java
index abd5292..15d7e4e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityUtils.java
@@ -184,7 +184,7 @@ class GridAffinityUtils {
GridAffinityAssignment assign = assign0 instanceof GridAffinityAssignment ?
(GridAffinityAssignment)assign0 :
- new GridAffinityAssignment(topVer, assign0.assignment(), assign0.idealAssignment());
+ new GridAffinityAssignment(topVer, assign0.assignment(), assign0.idealAssignment(), assign0.mvccCoordinator());
return F.t(
affinityMessage(ctx, cctx.config().getAffinity()),
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignment.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignment.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignment.java
index 94eaab4..1a6b2ef 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignment.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignment.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.processors.affinity;
import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -44,17 +45,26 @@ public class HistoryAffinityAssignment implements AffinityAssignment {
/** */
private final boolean clientEvtChange;
+ /** */
+ private final MvccCoordinator mvccCrd;
+
/**
* @param assign Assignment.
*/
- public HistoryAffinityAssignment(GridAffinityAssignment assign) {
+ HistoryAffinityAssignment(GridAffinityAssignment assign) {
this.topVer = assign.topologyVersion();
this.assignment = assign.assignment();
this.idealAssignment = assign.idealAssignment();
+ this.mvccCrd = assign.mvccCoordinator();
this.clientEvtChange = assign.clientEventChange();
}
/** {@inheritDoc} */
+ @Override public MvccCoordinator mvccCoordinator() {
+ return mvccCrd;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean clientEventChange() {
return clientEvtChange;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
index 0b448e1..5859452 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
@@ -58,6 +58,8 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Gri
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsFullMessage;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
+import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateFinishMessage;
import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.GridPartitionStateMap;
@@ -458,7 +460,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
if (grpHolder.client()) {
ClientCacheDhtTopologyFuture topFut = new ClientCacheDhtTopologyFuture(topVer);
- grp.topology().updateTopologyVersion(topFut, discoCache, -1, false);
+ grp.topology().updateTopologyVersion(topFut,
+ discoCache,
+ cctx.coordinators().currentCoordinator(),
+ -1,
+ false);
grpHolder = new CacheGroupHolder1(grp, grpHolder.affinity());
@@ -506,6 +512,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
assert grp != null;
GridDhtAffinityAssignmentResponse res = fetchAffinity(topVer,
+ cctx.coordinators().currentCoordinator(),
null,
discoCache,
grp.affinity(),
@@ -528,7 +535,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
new ClusterTopologyServerNotFoundException("All server nodes left grid."));
}
- grp.topology().updateTopologyVersion(topFut, discoCache, -1, false);
+ grp.topology().updateTopologyVersion(topFut,
+ discoCache,
+ cctx.coordinators().currentCoordinator(),
+ -1,
+ false);
grp.topology().update(topVer, partMap, null, Collections.<Integer>emptySet(), null, null);
@@ -1284,7 +1295,12 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
fetchFut.init(false);
- fetchAffinity(evts.topologyVersion(), evts, evts.discoveryCache(), aff, fetchFut);
+ fetchAffinity(evts.topologyVersion(),
+ cctx.coordinators().currentCoordinator(),
+ evts,
+ evts.discoveryCache(),
+ aff,
+ fetchFut);
}
}
@@ -1682,6 +1698,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
int grpId = fetchFut.groupId();
fetchAffinity(topVer,
+ cctx.coordinators().currentCoordinator(),
fut.events(),
fut.events().discoveryCache(),
cctx.cache().cacheGroup(grpId).affinity(),
@@ -1691,6 +1708,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
/**
* @param topVer Topology version.
+ * @param mvccCrd Mvcc coordinator to set in affinity.
* @param events Discovery events.
* @param discoCache Discovery data cache.
* @param affCache Affinity.
@@ -1698,7 +1716,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
* @throws IgniteCheckedException If failed.
* @return Affinity assignment response.
*/
- private GridDhtAffinityAssignmentResponse fetchAffinity(AffinityTopologyVersion topVer,
+ private GridDhtAffinityAssignmentResponse fetchAffinity(
+ AffinityTopologyVersion topVer,
+ MvccCoordinator mvccCrd,
@Nullable ExchangeDiscoveryEvents events,
DiscoCache discoCache,
GridAffinityAssignmentCache affCache,
@@ -1711,7 +1731,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
if (res == null) {
List<List<ClusterNode>> aff = affCache.calculate(topVer, events, discoCache);
- affCache.initialize(topVer, aff);
+ affCache.initialize(topVer, aff, mvccCrd);
}
else {
List<List<ClusterNode>> idealAff = res.idealAffinityAssignment(discoCache);
@@ -1728,7 +1748,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
assert aff != null : res;
- affCache.initialize(topVer, aff);
+ affCache.initialize(topVer, aff, mvccCrd);
}
return res;
@@ -1847,6 +1867,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
@Override public void applyx(IgniteInternalFuture<GridDhtAffinityAssignmentResponse> fetchFut)
throws IgniteCheckedException {
fetchAffinity(prev.topologyVersion(),
+ null, // Pass null mvcc coordinator, this affinity version should be used for queries.
prev.events(),
prev.events().discoveryCache(),
aff,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
index 49f77fa..614d7c0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
@@ -37,6 +37,18 @@ public class CacheEntryInfoCollection implements Message {
@GridDirectCollection(GridCacheEntryInfo.class)
private List<GridCacheEntryInfo> infos;
+ /** */
+ public CacheEntryInfoCollection() {
+ // No-op
+ }
+
+ /**
+ * @param infos List of cache entry info.
+ */
+ public CacheEntryInfoCollection(List<GridCacheEntryInfo> infos) {
+ this.infos = infos;
+ }
+
/**
*
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
index c8fe283..c100d16 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
@@ -30,6 +30,7 @@ import org.apache.ignite.cache.affinity.AffinityFunction;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.TopologyValidator;
import org.apache.ignite.events.CacheRebalancingEvent;
import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
@@ -60,6 +61,7 @@ import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.mxbean.CacheGroupMetricsMXBean;
import org.jetbrains.annotations.Nullable;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
import static org.apache.ignite.cache.CacheMode.LOCAL;
import static org.apache.ignite.cache.CacheMode.REPLICATED;
import static org.apache.ignite.cache.CacheRebalanceMode.NONE;
@@ -147,6 +149,9 @@ public class CacheGroupContext {
/** */
private boolean qryEnabled;
+ /** */
+ private boolean mvccEnabled;
+
/** MXBean. */
private CacheGroupMetricsMXBean mxBean;
@@ -218,6 +223,28 @@ public class CacheGroupContext {
caches = new ArrayList<>();
mxBean = new CacheGroupMetricsMXBeanImpl(this);
+
+ mvccEnabled = mvccEnabled(ctx.gridConfig(), ccfg, cacheType);
+ }
+
+ /**
+ * @param cfg Ignite configuration.
+ * @param ccfg Cache configuration.
+ * @param cacheType Cache typr.
+ * @return {@code True} if mvcc is enabled for given cache.
+ */
+ public static boolean mvccEnabled(IgniteConfiguration cfg, CacheConfiguration ccfg, CacheType cacheType) {
+ return cfg.isMvccEnabled() &&
+ cacheType == CacheType.USER &&
+ ccfg.getCacheMode() != LOCAL &&
+ ccfg.getAtomicityMode() == TRANSACTIONAL;
+ }
+
+ /**
+ * @return Mvcc flag.
+ */
+ public boolean mvccEnabled() {
+ return mvccEnabled;
}
/**
@@ -396,6 +423,13 @@ public class CacheGroupContext {
}
/**
+ * @return {@code True} if cache created by user.
+ */
+ public boolean userCache() {
+ return cacheType.userCache();
+ }
+
+ /**
* Adds rebalancing event.
*
* @param part Partition.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOperationContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOperationContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOperationContext.java
index 8a7afe7..53f9e22 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOperationContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOperationContext.java
@@ -163,7 +163,7 @@ public class CacheOperationContext implements Serializable {
/**
* Gets data center ID.
*
- * @return Client ID.
+ * @return Datacenter ID.
*/
@Nullable public Byte dataCenterId() {
return dataCenterId;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
index 3aaf7f3..8eab9c0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
@@ -147,7 +147,7 @@ class ClusterCachesInfo {
if (ccfg == null)
grpCfgs.put(info.cacheData().config().getGroupName(), info.cacheData().config());
else
- validateCacheGroupConfiguration(ccfg, info.cacheData().config());
+ validateCacheGroupConfiguration(ccfg, info.cacheData().config(), info.cacheType());
}
String conflictErr = processJoiningNode(joinDiscoData, ctx.localNodeId(), true);
@@ -220,7 +220,7 @@ class ClusterCachesInfo {
}
if (checkConsistency)
- validateStartCacheConfiguration(locCfg);
+ validateStartCacheConfiguration(locCfg, cacheData.cacheType());
}
}
@@ -1864,16 +1864,17 @@ class ClusterCachesInfo {
/**
* @param ccfg Cache configuration to start.
+ * @param cacheType Cache type.
* @throws IgniteCheckedException If failed.
*/
- public void validateStartCacheConfiguration(CacheConfiguration ccfg) throws IgniteCheckedException {
+ void validateStartCacheConfiguration(CacheConfiguration ccfg, CacheType cacheType) throws IgniteCheckedException {
if (ccfg.getGroupName() != null) {
CacheGroupDescriptor grpDesc = cacheGroupByName(ccfg.getGroupName());
if (grpDesc != null) {
assert ccfg.getGroupName().equals(grpDesc.groupName());
- validateCacheGroupConfiguration(grpDesc.config(), ccfg);
+ validateCacheGroupConfiguration(grpDesc.config(), ccfg, cacheType);
}
}
}
@@ -1881,9 +1882,10 @@ class ClusterCachesInfo {
/**
* @param cfg Existing configuration.
* @param startCfg Cache configuration to start.
+ * @param cacheType Cache type.
* @throws IgniteCheckedException If validation failed.
*/
- private void validateCacheGroupConfiguration(CacheConfiguration cfg, CacheConfiguration startCfg)
+ private void validateCacheGroupConfiguration(CacheConfiguration cfg, CacheConfiguration startCfg, CacheType cacheType)
throws IgniteCheckedException {
GridCacheAttributes attr1 = new GridCacheAttributes(cfg);
GridCacheAttributes attr2 = new GridCacheAttributes(startCfg);
@@ -1891,6 +1893,11 @@ class ClusterCachesInfo {
CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "cacheMode", "Cache mode",
cfg.getCacheMode(), startCfg.getCacheMode(), true);
+ CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "mvccEnabled", "MVCC mode",
+ CacheGroupContext.mvccEnabled(ctx.config(), cfg, cacheType),
+ CacheGroupContext.mvccEnabled(ctx.config(), startCfg, cacheType),
+ true);
+
CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "affinity", "Affinity function",
attr1.cacheAffinityClassName(), attr2.cacheAffinityClassName(), true);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.java
index 4046c98..34ed048 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.java
@@ -17,11 +17,15 @@
package org.apache.ignite.internal.processors.cache;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsFullMessage;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.jetbrains.annotations.Nullable;
@@ -51,11 +55,20 @@ public class ExchangeContext {
/** */
private final boolean compatibilityNode = getBoolean(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, false);
+ /** */
+ private final boolean newMvccCrd;
+
+ /** Currently running mvcc queries, initialized when mvcc coordinator is changed. */
+ private Map<UUID, GridLongList> activeQueries;
+
/**
* @param crd Coordinator flag.
+ * @param newMvccCrd {@code True} if new coordinator assigned during this exchange.
* @param fut Exchange future.
*/
- public ExchangeContext(boolean crd, GridDhtPartitionsExchangeFuture fut) {
+ public ExchangeContext(boolean crd, boolean newMvccCrd, GridDhtPartitionsExchangeFuture fut) {
+ this.newMvccCrd = newMvccCrd;
+
int protocolVer = exchangeProtocolVersion(fut.firstEventCache().minimumNodeVersion());
if (compatibilityNode || (crd && fut.localJoinExchange())) {
@@ -124,6 +137,34 @@ public class ExchangeContext {
return merge;
}
+ /**
+ * @return {@code True} if new node assigned as mvcc coordinator node during this exchange.
+ */
+ public boolean newMvccCoordinator() {
+ return newMvccCrd;
+ }
+
+ /**
+ * @return Active queries.
+ */
+ public Map<UUID, GridLongList> activeQueries() {
+ return activeQueries;
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param nodeQueries Node queries.
+ */
+ public void addActiveQueries(UUID nodeId, @Nullable GridLongList nodeQueries) {
+ if (nodeQueries == null)
+ return;
+
+ if (activeQueries == null)
+ activeQueries = new HashMap<>();
+
+ activeQueries.put(nodeId, nodeQueries);
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(ExchangeContext.class, this);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GatewayProtectedCacheProxy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GatewayProtectedCacheProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GatewayProtectedCacheProxy.java
index 96c5e29..c99eb00 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GatewayProtectedCacheProxy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GatewayProtectedCacheProxy.java
@@ -49,6 +49,7 @@ import org.apache.ignite.cache.query.QueryMetrics;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cluster.ClusterGroup;
import org.apache.ignite.internal.AsyncSupportAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.GridKernalState;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.future.IgniteFutureImpl;
@@ -145,6 +146,8 @@ public class GatewayProtectedCacheProxy<K, V> extends AsyncSupportAdapter<Ignite
CacheOperationGate opGate = onEnter();
try {
+ MvccUtils.verifyMvccOperationSupport(delegate.context(), "withExpiryPolicy");
+
return new GatewayProtectedCacheProxy<>(delegate, opCtx.withExpiryPolicy(plc), lock);
}
finally {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index bc14c54..159d681 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -92,6 +92,8 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopolo
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
import org.apache.ignite.internal.processors.cache.dr.GridCacheDrInfo;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalAdapter;
@@ -138,7 +140,6 @@ import org.apache.ignite.mxbean.CacheMetricsMXBean;
import org.apache.ignite.plugin.security.SecurityPermission;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.resources.JobContextResource;
-import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionConcurrency;
import org.apache.ignite.transactions.TransactionIsolation;
@@ -816,6 +817,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
ctx.checkSecurity(SecurityPermission.CACHE_READ);
+ //TODO IGNITE-7955
+ MvccUtils.verifyMvccOperationSupport(ctx, "Peek");
+
PeekModes modes = parsePeekModes(peekModes, false);
KeyCacheObject cacheKey = ctx.toCacheKeyObject(key);
@@ -1121,6 +1125,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
@Override public void clearLocally(boolean srv, boolean near, boolean readers) {
ctx.checkSecurity(SecurityPermission.CACHE_REMOVE);
+ //TODO IGNITE-7952
+ MvccUtils.verifyMvccOperationSupport(ctx, "Clear");
+
List<GridCacheClearAllRunnable<K, V>> jobs = splitClearLocally(srv, near, readers);
if (!F.isEmpty(jobs)) {
@@ -1190,6 +1197,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
* @throws IgniteCheckedException In case of error.
*/
private void clear(@Nullable Set<? extends K> keys) throws IgniteCheckedException {
+ //TODO IGNITE-7952
+ MvccUtils.verifyMvccOperationSupport(ctx, "Clear");
+
if (isLocal()) {
if (keys == null)
clearLocally(true, false, false);
@@ -1207,6 +1217,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
* @return Future.
*/
private IgniteInternalFuture<?> clearAsync(@Nullable final Set<? extends K> keys) {
+ //TODO IGNITE-7952
+ MvccUtils.verifyMvccOperationSupport(ctx, "Clear");
+
if (isLocal())
return clearLocallyAsync(keys);
else
@@ -1262,6 +1275,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (F.isEmpty(keys))
return;
+ //TODO IGNITE-7952
+ MvccUtils.verifyMvccOperationSupport(ctx, "Clear");
+
GridCacheVersion obsoleteVer = ctx.versions().next();
for (KeyCacheObject key : keys) {
@@ -1881,7 +1897,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
skipVals,
/*keep cache objects*/false,
recovery,
- needVer);
+ needVer,
+ null); // TODO IGNITE-7371
}
/**
@@ -1896,6 +1913,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
* @param skipVals Skip values flag.
* @param keepCacheObjects Keep cache objects.
* @param needVer If {@code true} returns values as tuples containing value and version.
+ * @param mvccSnapshot MVCC snapshot.
* @return Future.
*/
protected final <K1, V1> IgniteInternalFuture<Map<K1, V1>> getAllAsync0(
@@ -1910,7 +1928,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
final boolean skipVals,
final boolean keepCacheObjects,
final boolean recovery,
- final boolean needVer
+ final boolean needVer,
+ MvccSnapshot mvccSnapshot
) {
if (F.isEmpty(keys))
return new GridFinishedFuture<>(Collections.<K1, V1>emptyMap());
@@ -1967,7 +1986,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
boolean skipEntry = readNoEntry;
if (readNoEntry) {
- CacheDataRow row = ctx.offheap().read(ctx, key);
+ CacheDataRow row = mvccSnapshot != null ? ctx.offheap().mvccRead(ctx, key, mvccSnapshot) :
+ ctx.offheap().read(ctx, key);
if (row != null) {
long expireTime = row.expireTime();
@@ -2030,6 +2050,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
taskName,
expiry,
!deserializeBinary,
+ mvccSnapshot,
readerArgs);
assert res != null;
@@ -2054,6 +2075,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
taskName,
expiry,
!deserializeBinary,
+ mvccSnapshot,
readerArgs);
if (res == null)
@@ -3072,7 +3094,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
List<K> keys = new ArrayList<>(Math.min(REMOVE_ALL_KEYS_BATCH, size()));
do {
- for (Iterator<CacheDataRow> it = ctx.offheap().cacheIterator(ctx.cacheId(), true, true, null);
+ for (Iterator<CacheDataRow> it = ctx.offheap().cacheIterator(ctx.cacheId(), true, true, null, null);
it.hasNext() && keys.size() < REMOVE_ALL_KEYS_BATCH; )
keys.add((K)it.next().key());
@@ -3397,6 +3419,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(keys);
+ //TODO IGNITE-7764
+ MvccUtils.verifyMvccOperationSupport(ctx, "Lock");
+
IgniteInternalFuture<Boolean> fut = lockAllAsync(keys, timeout);
boolean isInterrupted = false;
@@ -3425,6 +3450,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKey(key);
+ //TODO IGNITE-7764
+ MvccUtils.verifyMvccOperationSupport(ctx, "Lock");
+
return lockAllAsync(Collections.singletonList(key), timeout);
}
@@ -3534,6 +3562,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
/** {@inheritDoc} */
@Override public void localLoadCache(final IgniteBiPredicate<K, V> p, Object[] args)
throws IgniteCheckedException {
+ //TODO IGNITE-7954
+ MvccUtils.verifyMvccOperationSupport(ctx, "Load");
+
final boolean replicate = ctx.isDrEnabled();
final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion();
@@ -3671,6 +3702,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (!ctx.store().configured())
return new GridFinishedFuture<>();
+ //TODO IGNITE-7954
+ MvccUtils.verifyMvccOperationSupport(ctx, "Load");
+
CacheOperationContext opCtx = ctx.operationContextPerCall();
ExpiryPolicy plc = opCtx != null ? opCtx.expiry() : null;
@@ -3755,6 +3789,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
*/
public void localLoad(Collection<? extends K> keys, @Nullable ExpiryPolicy plc, final boolean keepBinary)
throws IgniteCheckedException {
+ //TODO IGNITE-7954
+ MvccUtils.verifyMvccOperationSupport(ctx, "Load");
+
final boolean replicate = ctx.isDrEnabled();
final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion();
@@ -3827,6 +3864,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
assert !F.isEmpty(nodes) : "There are not datanodes fo cache: " + ctx.name();
+ //TODO IGNITE-7954
+ MvccUtils.verifyMvccOperationSupport(ctx, "Load");
+
final boolean keepBinary = opCtx != null && opCtx.isKeepBinary();
ComputeTaskInternalFuture fut = ctx.kernalContext().closure().callAsync(BROADCAST,
@@ -4185,6 +4225,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
READ_COMMITTED,
tCfg.getDefaultTxTimeout(),
!ctx.skipStore(),
+ false,
0,
null
);
@@ -4286,6 +4327,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
READ_COMMITTED,
txCfg.getDefaultTxTimeout(),
!skipStore,
+ false,
0,
null);
@@ -4556,6 +4598,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
* @param readers Whether to clear readers.
*/
private boolean clearLocally0(K key, boolean readers) {
+ //TODO IGNITE-7952
+ MvccUtils.verifyMvccOperationSupport(ctx, "Clear");
+
ctx.checkSecurity(SecurityPermission.CACHE_REMOVE);
if (keyCheck)
@@ -4593,6 +4638,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKey(key);
+ //TODO IGNITE-7956
+ MvccUtils.verifyMvccOperationSupport(ctx, "Evict");
+
return evictx(key, ctx.versions().next(), CU.empty0());
}
@@ -4606,6 +4654,9 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKey(keys);
+ //TODO IGNITE-7956
+ MvccUtils.verifyMvccOperationSupport(ctx, "Evict");
+
GridCacheVersion obsoleteVer = ctx.versions().next();
try {
@@ -4890,7 +4941,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
/*transformClo*/null,
/*taskName*/null,
/*expiryPlc*/null,
- !deserializeBinary);
+ !deserializeBinary,
+ null); // TODO IGNITE-7371
if (val == null)
return null;
@@ -4950,6 +5002,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
READ_COMMITTED,
CU.transactionConfiguration(ctx, ctx.kernalContext().config()).getDefaultTxTimeout(),
opCtx == null || !opCtx.skipStore(),
+ false,
0,
null);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
index c9ee38c..cf4344d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
@@ -32,6 +32,7 @@ import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.Nullable;
@@ -243,6 +244,10 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
return aff0.cachedAffinity(topVer);
}
+ public MvccCoordinator mvccCoordinator(AffinityTopologyVersion topVer) {
+ return assignment(topVer).mvccCoordinator();
+ }
+
/**
* @param key Key to check.
* @param topVer Topology version.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
index 61b1878..ac9de7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
@@ -2114,6 +2114,13 @@ public class GridCacheContext<K, V> implements Externalizable {
}
/**
+ * @return {@code True} if mvcc is enabled for cache.
+ */
+ public boolean mvccEnabled() {
+ return grp.mvccEnabled();
+ }
+
+ /**
* @param part Partition.
* @param topVer Topology version.
* @return {@code True} if partition is available locally.
[31/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistFuture.java
new file mode 100644
index 0000000..21967d7
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistFuture.java
@@ -0,0 +1,382 @@
+/*
+ * 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.distributed.near;
+
+import java.util.Collection;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxQueryEnlistFuture;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import static org.apache.ignite.internal.processors.cache.distributed.dht.NearTxQueryEnlistResultHandler.createResponse;
+
+/**
+ * Cache lock future.
+ */
+@SuppressWarnings("ForLoopReplaceableByForEach")
+public class GridNearTxQueryEnlistFuture extends GridNearTxAbstractEnlistFuture {
+ /** */
+ private static final long serialVersionUID = -2155104765461405820L;
+ /** Involved cache ids. */
+ private final int[] cacheIds;
+
+ /** Partitions. */
+ private final int[] parts;
+
+ /** Schema name. */
+ private final String schema;
+
+ /** Query string. */
+ private final String qry;
+
+ /** Query parameters. */
+ private final Object[] params;
+
+ /** Flags. */
+ private final int flags;
+
+ /** Fetch page size. */
+ private final int pageSize;
+
+ /**
+ * @param cctx Cache context.
+ * @param tx Transaction.
+ * @param cacheIds Involved cache ids.
+ * @param parts Partitions.
+ * @param schema Schema name.
+ * @param qry Query string.
+ * @param params Query parameters.
+ * @param flags Flags.
+ * @param pageSize Fetch page size.
+ * @param timeout Timeout.
+ */
+ protected GridNearTxQueryEnlistFuture(
+ GridCacheContext<?, ?> cctx, GridNearTxLocal tx, int[] cacheIds, int[] parts, String schema, String qry,
+ Object[] params, int flags, int pageSize, long timeout) {
+ super(cctx, tx, timeout);
+
+ this.cacheIds = cacheIds;
+ this.parts = parts;
+ this.schema = schema;
+ this.qry = qry;
+ this.params = params;
+ this.flags = flags;
+ this.pageSize = pageSize;
+ }
+
+ /**
+ * @param topLocked Topology locked flag.
+ */
+ protected void map(final boolean topLocked) {
+ MiniFuture mini = null;
+
+ try {
+ final AffinityAssignment assignment = cctx.affinity().assignment(topVer);
+
+ Collection<ClusterNode> primary;
+
+ if (parts != null) {
+ primary = U.newHashSet(parts.length);
+
+ for (int i = 0; i < parts.length; i++) {
+ ClusterNode pNode = assignment.get(parts[i]).get(0);
+
+ primary.add(pNode);
+
+ updateMappings(pNode);
+ }
+ }
+ else {
+ primary = assignment.primaryPartitionNodes();
+
+ for (ClusterNode pNode : primary) {
+ updateMappings(pNode);
+ }
+ }
+
+ boolean locallyMapped = primary.contains(cctx.localNode());
+
+ if (locallyMapped)
+ add(new MiniFuture(cctx.localNode()));
+
+ int idx = locallyMapped ? 1 : 0;
+ boolean first = true;
+ boolean clientFirst = false;
+
+ for (ClusterNode node : F.view(primary, F.remoteNodes(cctx.localNodeId()))) {
+ add(mini = new MiniFuture(node));
+
+ if (first) {
+ clientFirst = cctx.localNode().isClient() && !topLocked && !tx.hasRemoteLocks();
+
+ first = false;
+ }
+
+ GridNearTxQueryEnlistRequest req = new GridNearTxQueryEnlistRequest(
+ cctx.cacheId(),
+ threadId,
+ futId,
+ ++idx,
+ tx.subjectId(),
+ topVer,
+ lockVer,
+ mvccSnapshot,
+ cacheIds,
+ parts,
+ schema,
+ qry,
+ params,
+ flags,
+ pageSize,
+ remainingTime(),
+ tx.remainingTime(),
+ tx.taskNameHash(),
+ clientFirst
+ );
+
+ sendRequest(req, node.id(), mini);
+ }
+
+ if (locallyMapped) {
+ final MiniFuture localMini = mini = miniFuture(-1);
+
+ assert localMini != null;
+
+ GridDhtTxQueryEnlistFuture fut = new GridDhtTxQueryEnlistFuture(
+ cctx.localNode().id(),
+ lockVer,
+ mvccSnapshot,
+ threadId,
+ futId,
+ -1,
+ tx,
+ cacheIds,
+ parts,
+ schema,
+ qry,
+ params,
+ flags,
+ pageSize,
+ remainingTime(),
+ cctx);
+
+ updateLocalFuture(fut);
+
+ fut.listen(new CI1<IgniteInternalFuture<Long>>() {
+ @Override public void apply(IgniteInternalFuture<Long> fut) {
+ assert fut.error() != null || fut.result() != null : fut;
+
+ try {
+ clearLocalFuture((GridDhtTxQueryEnlistFuture)fut);
+
+ GridNearTxQueryEnlistResponse res = fut.error() == null ? createResponse(fut) : null;
+
+ localMini.onResult(res, fut.error());
+ }
+ catch (IgniteCheckedException e) {
+ localMini.onResult(null, e);
+ }
+ finally {
+ CU.unwindEvicts(cctx);
+ }
+ }
+ });
+
+ fut.init();
+ }
+ }
+ catch (Throwable e) {
+ if (mini != null)
+ mini.onResult(null, e);
+ else
+ onDone(e);
+
+ if (e instanceof Error)
+ throw (Error)e;
+ }
+
+ markInitialized();
+ }
+
+ /**
+ *
+ * @param req Request.
+ * @param nodeId Remote node ID.
+ * @param fut Result future.
+ * @throws IgniteCheckedException if failed to send.
+ */
+ private void sendRequest(GridCacheMessage req, UUID nodeId, MiniFuture fut) throws IgniteCheckedException {
+ IgniteInternalFuture<?> txSync = cctx.tm().awaitFinishAckAsync(nodeId, tx.threadId());
+
+ if (txSync == null || txSync.isDone())
+ cctx.io().send(nodeId, req, cctx.ioPolicy());
+ else
+ txSync.listen(new CI1<IgniteInternalFuture<?>>() {
+ @Override public void apply(IgniteInternalFuture<?> f) {
+ try {
+ cctx.io().send(nodeId, req, cctx.ioPolicy());
+ }
+ catch (IgniteCheckedException e) {
+ fut.onResult(null, e);
+ }
+ }
+ });
+ }
+
+ /**
+ * @param nodeId Left node ID
+ * @return {@code True} if node was in the list.
+ */
+ @Override public synchronized boolean onNodeLeft(UUID nodeId) {
+ for (IgniteInternalFuture<?> fut : futures()) {
+ MiniFuture f = (MiniFuture)fut;
+
+ if (f.node.id().equals(nodeId)) {
+ if (log.isDebugEnabled())
+ log.debug("Found mini-future for left node [nodeId=" + nodeId + ", mini=" + f + ", fut=" +
+ this + ']');
+
+ return f.onResult(null, newTopologyException(nodeId));
+ }
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Future does not have mapping for left node (ignoring) [nodeId=" + nodeId +
+ ", fut=" + this + ']');
+
+ return false;
+ }
+
+ /**
+ * Finds pending mini future by the given mini ID.
+ *
+ * @param miniId Mini ID to find.
+ * @return Mini future.
+ */
+ private MiniFuture miniFuture(int miniId) {
+ synchronized (this) {
+ int idx = Math.abs(miniId) - 1;
+
+ assert idx >= 0 && idx < futuresCountNoLock();
+
+ IgniteInternalFuture<Long> fut = future(idx);
+
+ if (!fut.isDone())
+ return (MiniFuture)fut;
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates new topology exception for cases when primary node leaves grid during mapping.
+ *
+ * @param nodeId Node ID.
+ * @return Topology exception with user-friendly message.
+ */
+ private ClusterTopologyCheckedException newTopologyException(UUID nodeId) {
+ ClusterTopologyCheckedException topEx = new ClusterTopologyCheckedException("Failed to enlist keys " +
+ "(primary node left grid, retry transaction if possible) [node=" + nodeId + ']');
+
+ topEx.retryReadyFuture(cctx.shared().nextAffinityReadyFuture(topVer));
+
+ return topEx;
+ }
+
+ /**
+ * @param nodeId Sender node id.
+ * @param res Response.
+ */
+ public void onResult(UUID nodeId, GridNearTxQueryEnlistResponse res) {
+ MiniFuture mini = miniFuture(res.miniId());
+
+ if (mini != null)
+ mini.onResult(res, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxQueryEnlistFuture.class, this, super.toString());
+ }
+
+ /** */
+ private class MiniFuture extends GridFutureAdapter<Long> {
+ /** */
+ private boolean completed;
+
+ /** Node ID. */
+ @GridToStringExclude
+ private final ClusterNode node;
+
+ /**
+ * @param node Cluster node.
+ */
+ private MiniFuture(ClusterNode node) {
+ this.node = node;
+ }
+
+ /**
+ * @param res Response.
+ * @param err Exception.
+ * @return {@code True} if future was completed by this call.
+ */
+ public boolean onResult(GridNearTxQueryEnlistResponse res, Throwable err) {
+ assert res != null || err != null : this;
+
+ if (err == null && res.error() != null)
+ err = res.error();
+
+ synchronized (this) {
+ if (completed)
+ return false;
+
+ completed = true;
+ }
+
+ if (X.hasCause(err, ClusterTopologyCheckedException.class)
+ || (res != null && res.removeMapping())) {
+ GridDistributedTxMapping m = tx.mappings().get(node.id());
+
+ assert m != null && m.empty();
+
+ tx.removeMapping(node.id());
+
+ if (node.isLocal())
+ tx.colocatedLocallyMapped(false);
+ }
+ else if (res != null && res.result() > 0 && !node.isLocal())
+ tx.hasRemoteLocks(true);
+
+ return err != null ? onDone(err) : onDone(res.result(), res.error());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistRequest.java
new file mode 100644
index 0000000..472937b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistRequest.java
@@ -0,0 +1,616 @@
+/*
+ * 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.distributed.near;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class GridNearTxQueryEnlistRequest extends GridCacheIdMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long threadId;
+
+ /** */
+ private IgniteUuid futId;
+
+ /** */
+ private boolean clientFirst;
+
+ /** */
+ private int miniId;
+
+ /** */
+ private UUID subjId;
+
+ /** */
+ private AffinityTopologyVersion topVer;
+
+ /** */
+ private GridCacheVersion lockVer;
+
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
+ /** */
+ private int[] cacheIds;
+
+ /** */
+ private int[] parts;
+
+ /** */
+ private String schema;
+
+ /** */
+ private String qry;
+
+ /** */
+ @GridDirectTransient
+ private Object[] params;
+
+ /** */
+ private byte[] paramsBytes;
+
+ /** */
+ private int flags;
+
+ /** */
+ private long timeout;
+
+ /** */
+ private long txTimeout;
+
+ /** */
+ private int taskNameHash;
+
+ /** */
+ private int pageSize;
+
+ /** */
+ public GridNearTxQueryEnlistRequest() {
+ // No-op.
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param threadId Thread id.
+ * @param futId Future id.
+ * @param miniId Mini fture id.
+ * @param subjId Subject id.
+ * @param topVer Topology version.
+ * @param lockVer Lock version.
+ * @param mvccSnapshot MVCC snspshot.
+ * @param cacheIds Involved cache ids.
+ * @param parts Partitions.
+ * @param schema Schema name.
+ * @param qry Query string.
+ * @param params Query parameters.
+ * @param flags Flags.
+ * @param pageSize Fetch page size.
+ * @param timeout Timeout milliseconds.
+ * @param txTimeout Tx timeout milliseconds.
+ * @param taskNameHash Task name hash.
+ * @param clientFirst {@code True} if this is the first client request.
+ */
+ public GridNearTxQueryEnlistRequest(
+ int cacheId,
+ long threadId,
+ IgniteUuid futId,
+ int miniId,
+ UUID subjId,
+ AffinityTopologyVersion topVer,
+ GridCacheVersion lockVer,
+ MvccSnapshot mvccSnapshot,
+ int[] cacheIds,
+ int[] parts,
+ String schema,
+ String qry,
+ Object[] params,
+ int flags,
+ int pageSize,
+ long timeout,
+ long txTimeout,
+ int taskNameHash,
+ boolean clientFirst) {
+ this.cacheIds = cacheIds;
+ this.parts = parts;
+ this.schema = schema;
+ this.qry = qry;
+ this.params = params;
+ this.flags = flags;
+ this.pageSize = pageSize;
+ this.txTimeout = txTimeout;
+ this.cacheId = cacheId;
+ this.threadId = threadId;
+ this.futId = futId;
+ this.miniId = miniId;
+ this.subjId = subjId;
+ this.topVer = topVer;
+ this.lockVer = lockVer;
+ this.mvccSnapshot = mvccSnapshot;
+ this.timeout = timeout;
+ this.taskNameHash = taskNameHash;
+ this.clientFirst = clientFirst;
+ }
+
+ /**
+ * @return Thread id.
+ */
+ public long threadId() {
+ return threadId;
+ }
+
+ /**
+ * @return Future id.
+ */
+ public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /**
+ * @return Mini future ID.
+ */
+ public int miniId() {
+ return miniId;
+ }
+
+ /**
+ * @return Subject id.
+ */
+ public UUID subjectId() {
+ return subjId;
+ }
+
+ /**
+ * @return Topology version.
+ */
+ @Override public AffinityTopologyVersion topologyVersion() {
+ return topVer;
+ }
+
+ /**
+ * @return Lock version.
+ */
+ public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ /**
+ * @return MVCC snapshot.
+ */
+ public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
+ * @return Involved cache ids.
+ */
+ public int[] cacheIds() {
+ return cacheIds;
+ }
+
+ /**
+ * @return Partitions.
+ */
+ public int[] partitions() {
+ return parts;
+ }
+
+ /**
+ * @return Schema name.
+ */
+ public String schemaName() {
+ return schema;
+ }
+
+ /**
+ * @return Query string.
+ */
+ public String query() {
+ return qry;
+ }
+
+ /**
+ * @return Query parameters.
+ */
+ public Object[] parameters() {
+ return params;
+ }
+
+ /**
+ * @return Flags.
+ */
+ public int flags() {
+ return flags;
+ }
+
+ /**
+ * @return Fetch page size.
+ */
+ public int pageSize() {
+ return pageSize;
+ }
+
+ /**
+ * @return Timeout milliseconds.
+ */
+ public long timeout() {
+ return timeout;
+ }
+
+ /**
+ * @return Tx timeout milliseconds.
+ */
+ public long txTimeout() {
+ return txTimeout;
+ }
+
+ /**
+ * @return Task name hash.
+ */
+ public int taskNameHash() {
+ return taskNameHash;
+ }
+
+ /**
+ * @return {@code True} if this is the first client request.
+ */
+ public boolean firstClientRequest() {
+ return clientFirst;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addDeploymentInfo() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 21;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void prepareMarshal(GridCacheSharedContext ctx) throws IgniteCheckedException {
+ super.prepareMarshal(ctx);
+
+ if (params != null && paramsBytes == null)
+ paramsBytes = U.marshal(ctx, params);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void finishUnmarshal(GridCacheSharedContext ctx, ClassLoader ldr) throws IgniteCheckedException {
+ super.finishUnmarshal(ctx, ldr);
+
+ if (paramsBytes != null && params == null)
+ params = U.unmarshal(ctx, paramsBytes, ldr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeIntArray("cacheIds", cacheIds))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeBoolean("clientFirst", clientFirst))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeInt("flags", flags))
+ return false;
+
+ writer.incrementState();
+
+ case 6:
+ if (!writer.writeIgniteUuid("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 7:
+ if (!writer.writeMessage("lockVer", lockVer))
+ return false;
+
+ writer.incrementState();
+
+ case 8:
+ if (!writer.writeInt("miniId", miniId))
+ return false;
+
+ writer.incrementState();
+
+ case 9:
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
+ return false;
+
+ writer.incrementState();
+
+ case 10:
+ if (!writer.writeInt("pageSize", pageSize))
+ return false;
+
+ writer.incrementState();
+
+ case 11:
+ if (!writer.writeByteArray("paramsBytes", paramsBytes))
+ return false;
+
+ writer.incrementState();
+
+ case 12:
+ if (!writer.writeIntArray("parts", parts))
+ return false;
+
+ writer.incrementState();
+
+ case 13:
+ if (!writer.writeString("qry", qry))
+ return false;
+
+ writer.incrementState();
+
+ case 14:
+ if (!writer.writeString("schema", schema))
+ return false;
+
+ writer.incrementState();
+
+ case 15:
+ if (!writer.writeUuid("subjId", subjId))
+ return false;
+
+ writer.incrementState();
+
+ case 16:
+ if (!writer.writeInt("taskNameHash", taskNameHash))
+ return false;
+
+ writer.incrementState();
+
+ case 17:
+ if (!writer.writeLong("threadId", threadId))
+ return false;
+
+ writer.incrementState();
+
+ case 18:
+ if (!writer.writeLong("timeout", timeout))
+ return false;
+
+ writer.incrementState();
+
+ case 19:
+ if (!writer.writeMessage("topVer", topVer))
+ return false;
+
+ writer.incrementState();
+
+ case 20:
+ if (!writer.writeLong("txTimeout", txTimeout))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ cacheIds = reader.readIntArray("cacheIds");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ clientFirst = reader.readBoolean("clientFirst");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ flags = reader.readInt("flags");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 6:
+ futId = reader.readIgniteUuid("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 7:
+ lockVer = reader.readMessage("lockVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 8:
+ miniId = reader.readInt("miniId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 9:
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 10:
+ pageSize = reader.readInt("pageSize");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 11:
+ paramsBytes = reader.readByteArray("paramsBytes");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 12:
+ parts = reader.readIntArray("parts");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 13:
+ qry = reader.readString("qry");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 14:
+ schema = reader.readString("schema");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 15:
+ subjId = reader.readUuid("subjId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 16:
+ taskNameHash = reader.readInt("taskNameHash");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 17:
+ threadId = reader.readLong("threadId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 18:
+ timeout = reader.readLong("timeout");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 19:
+ topVer = reader.readMessage("topVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 20:
+ txTimeout = reader.readLong("txTimeout");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridNearTxQueryEnlistRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 151;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxQueryEnlistRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistResponse.java
new file mode 100644
index 0000000..39e33c4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryEnlistResponse.java
@@ -0,0 +1,298 @@
+/*
+ * 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.distributed.near;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.ExceptionAware;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class GridNearTxQueryEnlistResponse extends GridCacheIdMessage implements ExceptionAware {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Future ID. */
+ private IgniteUuid futId;
+
+ /** Error. */
+ @GridDirectTransient
+ private Throwable err;
+
+ /** Serialized error. */
+ private byte[] errBytes;
+
+ /** Mini future id. */
+ private int miniId;
+
+ /** Result. */
+ private long res;
+
+ /** Remove mapping flag. */
+ private boolean removeMapping;
+
+ /** */
+ private GridCacheVersion lockVer;
+
+ /**
+ * Default constructor.
+ */
+ public GridNearTxQueryEnlistResponse() {
+ // No-op.
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param futId Future id.
+ * @param miniId Mini future id.
+ * @param lockVer Lock version.
+ * @param err Error.
+ */
+ public GridNearTxQueryEnlistResponse(int cacheId, IgniteUuid futId, int miniId, GridCacheVersion lockVer, Throwable err) {
+ this.cacheId = cacheId;
+ this.futId = futId;
+ this.miniId = miniId;
+ this.lockVer = lockVer;
+ this.err = err;
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param futId Future id.
+ * @param miniId Mini future id.
+ * @param lockVer Lock version.
+ * @param res Result.
+ * @param removeMapping Remove mapping flag.
+ */
+ public GridNearTxQueryEnlistResponse(int cacheId, IgniteUuid futId, int miniId, GridCacheVersion lockVer, long res, boolean removeMapping) {
+ this.cacheId = cacheId;
+ this.futId = futId;
+ this.miniId = miniId;
+ this.lockVer = lockVer;
+ this.res = res;
+ this.removeMapping = removeMapping;
+ }
+
+ /**
+ * @return Loc version.
+ */
+ public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ /**
+ * @return Future id.
+ */
+ public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /**
+ * @return Mini future id.
+ */
+ public int miniId() {
+ return miniId;
+ }
+
+ /**
+ * @return Result.
+ */
+ public long result() {
+ return res;
+ }
+
+ /**
+ * @return Remove mapping flag.
+ */
+ public boolean removeMapping() {
+ return removeMapping;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public Throwable error() {
+ return err;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addDeploymentInfo() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 9;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeByteArray("errBytes", errBytes))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeIgniteUuid("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeMessage("lockVer", lockVer))
+ return false;
+
+ writer.incrementState();
+
+ case 6:
+ if (!writer.writeInt("miniId", miniId))
+ return false;
+
+ writer.incrementState();
+
+ case 7:
+ if (!writer.writeBoolean("removeMapping", removeMapping))
+ return false;
+
+ writer.incrementState();
+
+ case 8:
+ if (!writer.writeLong("res", res))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ errBytes = reader.readByteArray("errBytes");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ futId = reader.readIgniteUuid("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ lockVer = reader.readMessage("lockVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 6:
+ miniId = reader.readInt("miniId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 7:
+ removeMapping = reader.readBoolean("removeMapping");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 8:
+ res = reader.readLong("res");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridNearTxQueryEnlistResponse.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 152;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void prepareMarshal(GridCacheSharedContext ctx) throws IgniteCheckedException {
+ super.prepareMarshal(ctx);
+
+ if (err != null && errBytes == null)
+ errBytes = U.marshal(ctx.marshaller(), err);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void finishUnmarshal(GridCacheSharedContext ctx, ClassLoader ldr) throws IgniteCheckedException {
+ super.finishUnmarshal(ctx, ldr);
+
+ if (errBytes != null)
+ err = U.unmarshal(ctx, errBytes, U.resolveClassLoader(ldr, ctx.gridConfig()));
+ }
+
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxQueryEnlistResponse.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistFuture.java
new file mode 100644
index 0000000..910deb9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistFuture.java
@@ -0,0 +1,670 @@
+/*
+ * 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.distributed.near;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxAbstractEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxQueryResultsEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxRemote;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotWithoutTxs;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.cache.distributed.dht.NearTxQueryEnlistResultHandler.createResponse;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * A future tracking requests for remote nodes transaction enlisting and locking
+ * of entries produced with complex DML queries requiring reduce step.
+ */
+public class GridNearTxQueryResultsEnlistFuture extends GridNearTxAbstractEnlistFuture {
+ /** */
+ private static final long serialVersionUID = 4339957209840477447L;
+
+ /** */
+ public static final int DFLT_BATCH_SIZE = 1024;
+
+ /** Res field updater. */
+ private static final AtomicLongFieldUpdater<GridNearTxQueryResultsEnlistFuture> RES_UPD =
+ AtomicLongFieldUpdater.newUpdater(GridNearTxQueryResultsEnlistFuture.class, "res");
+
+ /** SkipCntr field updater. */
+ private static final AtomicIntegerFieldUpdater<GridNearTxQueryResultsEnlistFuture> SKIP_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(GridNearTxQueryResultsEnlistFuture.class, "skipCntr");
+
+ /** Marker object. */
+ private static final Object FINISHED = new Object();
+
+ /** */
+ private final UpdateSourceIterator<?> it;
+
+ /** */
+ private int batchSize;
+
+ /** */
+ private AtomicInteger batchCntr = new AtomicInteger();
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ private volatile int skipCntr;
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ private volatile long res;
+
+ /** */
+ private final Map<UUID, Batch> batches = new ConcurrentHashMap<>();
+
+ /** Row extracted from iterator but not yet used. */
+ private Object peek;
+
+ /** Topology locked flag. */
+ private boolean topLocked;
+
+ /** */
+ private final boolean sequential;
+
+ /**
+ * @param cctx Cache context.
+ * @param tx Transaction.
+ * @param timeout Timeout.
+ * @param it Rows iterator.
+ * @param batchSize Batch size.
+ * @param sequential Sequential locking flag.
+ */
+ public GridNearTxQueryResultsEnlistFuture(GridCacheContext<?, ?> cctx,
+ GridNearTxLocal tx,
+ long timeout,
+ UpdateSourceIterator<?> it,
+ int batchSize,
+ boolean sequential) {
+ super(cctx, tx, timeout);
+
+ this.it = it;
+ this.batchSize = batchSize > 0 ? batchSize : DFLT_BATCH_SIZE;
+ this.sequential = sequential;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void map(boolean topLocked) {
+ this.topLocked = topLocked;
+
+ sendNextBatches(null);
+ }
+
+ /**
+ * Continue iterating the data rows and form new batches.
+ *
+ * @param nodeId Node that is ready for a new batch.
+ */
+ private void sendNextBatches(@Nullable UUID nodeId) {
+ try {
+ Collection<Batch> next = continueLoop(nodeId);
+
+ if (next == null)
+ return;
+
+ boolean first = (nodeId != null);
+
+ for (Batch batch : next) {
+ ClusterNode node = batch.node();
+
+ sendBatch(node, batch, first);
+
+ if (!node.isLocal())
+ first = false;
+ }
+ }
+ catch (Throwable e) {
+ onDone(e);
+
+ if (e instanceof Error)
+ throw (Error)e;
+ }
+ }
+
+ /**
+ * Iterate data rows and form batches.
+ *
+ * @param nodeId Id of node acknowledged the last batch.
+ * @return Collection of newly completed batches.
+ * @throws IgniteCheckedException If failed.
+ */
+ private Collection<Batch> continueLoop(@Nullable UUID nodeId) throws IgniteCheckedException {
+ if (nodeId != null)
+ batches.remove(nodeId);
+
+ // Accumulate number of batches released since we got here.
+ // Let only one thread do the looping.
+ if (isDone() || SKIP_UPD.getAndIncrement(this) != 0)
+ return null;
+
+ ArrayList<Batch> res = null; Batch batch = null;
+
+ boolean flush = false;
+
+ EnlistOperation op = it.operation();
+
+ while (true) {
+ while (hasNext0()) {
+ checkCompleted();
+
+ Object cur = next0();
+
+ KeyCacheObject key = cctx.toCacheKeyObject(op.isDeleteOrLock() ? cur : ((IgniteBiTuple)cur).getKey());
+
+ List<ClusterNode> nodes = cctx.affinity().nodesByKey(key, topVer);
+
+ ClusterNode node;
+
+ if (F.isEmpty(nodes) || ((node = nodes.get(0)) == null))
+ throw new ClusterTopologyCheckedException("Failed to get primary node " +
+ "[topVer=" + topVer + ", key=" + key + ']');
+
+ if (!sequential)
+ batch = batches.get(node.id());
+ else if (batch != null && !batch.node().equals(node))
+ res = markReady(res, batch);
+
+ if (batch == null)
+ batches.put(node.id(), batch = new Batch(node));
+
+ if (batch.ready()) {
+ // Can't advance further at the moment.
+ batch = null;
+
+ peek = cur;
+
+ it.beforeDetach();
+
+ flush = true;
+
+ break;
+ }
+
+ batch.add(op.isDeleteOrLock() ? key : cur,
+ op != EnlistOperation.LOCK && cctx.affinityNode() && (cctx.isReplicated() || nodes.indexOf(cctx.localNode()) > 0));
+
+ if (batch.size() == batchSize)
+ res = markReady(res, batch);
+ }
+
+ if (SKIP_UPD.decrementAndGet(this) == 0)
+ break;
+
+ skipCntr = 1;
+ }
+
+ if (flush)
+ return res;
+
+ // No data left - flush incomplete batches.
+ for (Batch batch0 : batches.values()) {
+ if (!batch0.ready()) {
+ if (res == null)
+ res = new ArrayList<>();
+
+ batch0.ready(true);
+
+ res.add(batch0);
+ }
+ }
+
+ if (batches.isEmpty())
+ onDone(this.res);
+
+ return res;
+ }
+
+ /** */
+ private Object next0() {
+ if (!hasNext0())
+ throw new NoSuchElementException();
+
+ Object cur;
+
+ if ((cur = peek) != null)
+ peek = null;
+ else
+ cur = it.next();
+
+ return cur;
+ }
+
+ /** */
+ private boolean hasNext0() {
+ if (peek == null && !it.hasNext())
+ peek = FINISHED;
+
+ return peek != FINISHED;
+ }
+
+ /** */
+ private ArrayList<Batch> markReady(ArrayList<Batch> batches, Batch batch) {
+ if (!batch.ready()) {
+ batch.ready(true);
+
+ if (batches == null)
+ batches = new ArrayList<>();
+
+ batches.add(batch);
+ }
+
+ return batches;
+ }
+
+ /**
+ *
+ * @param primaryId Primary node id.
+ * @param rows Rows.
+ * @param dhtVer Dht version assigned at primary node.
+ * @param dhtFutId Dht future id assigned at primary node.
+ * @param updCntrs Update counters.
+ */
+ private void processBatchLocalBackupKeys(UUID primaryId, List<Object> rows, GridCacheVersion dhtVer,
+ IgniteUuid dhtFutId, GridLongList updCntrs) {
+ assert dhtVer != null;
+ assert dhtFutId != null;
+
+ EnlistOperation op = it.operation();
+
+ assert op != EnlistOperation.LOCK;
+
+ boolean keysOnly = op.isDeleteOrLock();
+
+ final ArrayList<KeyCacheObject> keys = new ArrayList<>(rows.size());
+ final ArrayList<Message> vals = keysOnly ? null : new ArrayList<>(rows.size());
+
+ for (Object row : rows) {
+ if (keysOnly)
+ keys.add(cctx.toCacheKeyObject(row));
+ else {
+ keys.add(cctx.toCacheKeyObject(((IgniteBiTuple)row).getKey()));
+ vals.add(cctx.toCacheObject(((IgniteBiTuple)row).getValue()));
+ }
+ }
+
+ try {
+ GridDhtTxRemote dhtTx = cctx.tm().tx(dhtVer);
+
+ if (dhtTx == null) {
+ dhtTx = new GridDhtTxRemote(cctx.shared(),
+ cctx.localNodeId(),
+ dhtFutId,
+ primaryId,
+ lockVer,
+ topVer,
+ dhtVer,
+ null,
+ cctx.systemTx(),
+ cctx.ioPolicy(),
+ PESSIMISTIC,
+ REPEATABLE_READ,
+ false,
+ tx.remainingTime(),
+ -1,
+ this.tx.subjectId(),
+ this.tx.taskNameHash(),
+ false);
+
+ dhtTx.mvccSnapshot(new MvccSnapshotWithoutTxs(mvccSnapshot.coordinatorVersion(),
+ mvccSnapshot.counter(), MVCC_OP_COUNTER_NA, mvccSnapshot.cleanupVersion()));
+
+ dhtTx = cctx.tm().onCreated(null, dhtTx);
+
+ if (dhtTx == null || !cctx.tm().onStarted(dhtTx)) {
+ throw new IgniteTxRollbackCheckedException("Failed to update backup " +
+ "(transaction has been completed): " + dhtVer);
+ }
+ }
+
+ dhtTx.mvccEnlistBatch(cctx, it.operation(), keys, vals, mvccSnapshot.withoutActiveTransactions(), updCntrs);
+ }
+ catch (IgniteCheckedException e) {
+ onDone(e);
+
+ return;
+ }
+
+ sendNextBatches(primaryId);
+ }
+
+ /**
+ *
+ * @param node Node.
+ * @param batch Batch.
+ * @param first First mapping flag.
+ */
+ private void sendBatch(ClusterNode node, Batch batch, boolean first) throws IgniteCheckedException {
+ updateMappings(node);
+
+ boolean clientFirst = first && cctx.localNode().isClient() && !topLocked && !tx.hasRemoteLocks();
+
+ int batchId = batchCntr.incrementAndGet();
+
+ if (node.isLocal())
+ enlistLocal(batchId, node.id(), batch);
+ else
+ sendBatch(batchId, node.id(), batch, clientFirst);
+ }
+
+ /**
+ * Send batch request to remote data node.
+ *
+ * @param batchId Id of a batch mini-future.
+ * @param nodeId Node id.
+ * @param batchFut Mini-future for the batch.
+ * @param clientFirst {@code true} if originating node is client and it is a first request to any data node.
+ */
+ private void sendBatch(int batchId, UUID nodeId, Batch batchFut, boolean clientFirst) throws IgniteCheckedException {
+ assert batchFut != null;
+
+ GridNearTxQueryResultsEnlistRequest req = new GridNearTxQueryResultsEnlistRequest(cctx.cacheId(),
+ threadId,
+ futId,
+ batchId,
+ tx.subjectId(),
+ topVer,
+ lockVer,
+ mvccSnapshot,
+ clientFirst,
+ remainingTime(),
+ tx.remainingTime(),
+ tx.taskNameHash(),
+ batchFut.rows(),
+ it.operation());
+
+ sendRequest(req, nodeId);
+ }
+
+ /**
+ *
+ * @param req Request.
+ * @param nodeId Remote node ID
+ * @throws IgniteCheckedException if failed to send.
+ */
+ private void sendRequest(GridCacheMessage req, UUID nodeId) throws IgniteCheckedException {
+ IgniteInternalFuture<?> txSync = cctx.tm().awaitFinishAckAsync(nodeId, tx.threadId());
+
+ if (txSync == null || txSync.isDone())
+ cctx.io().send(nodeId, req, cctx.ioPolicy());
+ else
+ txSync.listen(new CI1<IgniteInternalFuture<?>>() {
+ @Override public void apply(IgniteInternalFuture<?> future) {
+ try {
+ cctx.io().send(nodeId, req, cctx.ioPolicy());
+ }
+ catch (IgniteCheckedException e) {
+ GridNearTxQueryResultsEnlistFuture.this.onDone(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Enlist batch of entries to the transaction on local node.
+ *
+ * @param batchId Id of a batch mini-future.
+ * @param nodeId Node id.
+ * @param batch Batch.
+ */
+ private void enlistLocal(int batchId, UUID nodeId, Batch batch) throws IgniteCheckedException {
+ Collection<Object> rows = batch.rows();
+
+ GridDhtTxQueryResultsEnlistFuture fut = new GridDhtTxQueryResultsEnlistFuture(nodeId,
+ lockVer,
+ mvccSnapshot,
+ threadId,
+ futId,
+ batchId,
+ tx,
+ remainingTime(),
+ cctx,
+ rows,
+ it.operation());
+
+ updateLocalFuture(fut);
+
+ fut.listen(new CI1<IgniteInternalFuture<Long>>() {
+ @Override public void apply(IgniteInternalFuture<Long> fut) {
+ assert fut.error() != null || fut.result() != null : fut;
+
+ try {
+ clearLocalFuture((GridDhtTxAbstractEnlistFuture)fut);
+
+ GridNearTxQueryResultsEnlistResponse res = fut.error() == null ? createResponse(fut) : null;
+
+ if (checkResponse(nodeId, res, fut.error()))
+ sendNextBatches(nodeId);
+ }
+ catch (IgniteCheckedException e) {
+ checkResponse(nodeId, null, e);
+ }
+ finally {
+ CU.unwindEvicts(cctx);
+ }
+ }
+ });
+
+ fut.init();
+ }
+
+ /**
+ * @param nodeId Sender node id.
+ * @param res Response.
+ */
+ public void onResult(UUID nodeId, GridNearTxQueryResultsEnlistResponse res) {
+ if (checkResponse(nodeId, res, res.error())) {
+
+ Batch batch = batches.get(nodeId);
+
+ if (batch != null && !F.isEmpty(batch.localBackupRows()) && res.dhtFutureId() != null)
+ processBatchLocalBackupKeys(nodeId, batch.localBackupRows(), res.dhtVersion(), res.dhtFutureId(),
+ res.updateCounters());
+ else
+ sendNextBatches(nodeId);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onNodeLeft(UUID nodeId) {
+ if (batches.keySet().contains(nodeId)) {
+ if (log.isDebugEnabled())
+ log.debug("Found unacknowledged batch for left node [nodeId=" + nodeId + ", fut=" +
+ this + ']');
+
+ ClusterTopologyCheckedException topEx = new ClusterTopologyCheckedException("Failed to enlist keys " +
+ "(primary node left grid, retry transaction if possible) [node=" + nodeId + ']');
+
+ topEx.retryReadyFuture(cctx.shared().nextAffinityReadyFuture(topVer));
+
+ processFailure(topEx, null);
+
+ batches.remove(nodeId);
+
+ if (batches.isEmpty()) // Wait for all pending requests.
+ onDone();
+
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Future does not have mapping for left node (ignoring) [nodeId=" + nodeId +
+ ", fut=" + this + ']');
+
+ return false;
+ }
+
+ /**
+ * @param nodeId Originating node ID.
+ * @param res Response.
+ * @param err Exception.
+ * @return {@code True} if future was completed by this call.
+ */
+ public boolean checkResponse(UUID nodeId, GridNearTxQueryResultsEnlistResponse res, Throwable err) {
+ assert res != null || err != null : this;
+
+ if (err == null && res.error() != null)
+ err = res.error();
+
+ if (X.hasCause(err, ClusterTopologyCheckedException.class))
+ tx.removeMapping(nodeId);
+
+ if (err != null)
+ processFailure(err, null);
+
+ if (ex != null) {
+ batches.remove(nodeId);
+
+ if (batches.isEmpty()) // Wait for all pending requests.
+ onDone();
+
+ return false;
+ }
+
+ assert res != null;
+
+ RES_UPD.getAndAdd(this, res.result());
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxQueryResultsEnlistFuture.class, this, super.toString());
+ }
+
+ /**
+ * A batch of rows
+ */
+ private class Batch {
+ /** Node ID. */
+ @GridToStringExclude
+ private final ClusterNode node;
+
+ /** Rows. */
+ private ArrayList<Object> rows = new ArrayList<>();
+
+ /** Local backup rows. */
+ private ArrayList<Object> locBkpRows;
+
+ /** Readiness flag. Set when batch is full or no new rows are expected. */
+ private boolean ready;
+
+ /**
+ * @param node Cluster node.
+ */
+ private Batch(ClusterNode node) {
+ this.node = node;
+ }
+
+ /**
+ * @return Node.
+ */
+ public ClusterNode node() {
+ return node;
+ }
+
+ /**
+ * Adds a row.
+ *
+ * @param row Row.
+ * @param localBackup {@code true}, when the row key has local backup.
+ */
+ public void add(Object row, boolean localBackup) {
+ rows.add(row);
+
+ if (localBackup) {
+ if (locBkpRows == null)
+ locBkpRows = new ArrayList<>();
+
+ locBkpRows.add(row);
+ }
+ }
+
+ /**
+ * @return number of rows.
+ */
+ public int size() {
+ return rows.size();
+ }
+
+ /**
+ * @return Collection of rows.
+ */
+ public Collection<Object> rows() {
+ return rows;
+ }
+
+ /**
+ * @return Collection of local backup rows.
+ */
+ public List<Object> localBackupRows() {
+ return locBkpRows;
+ }
+
+ /**
+ * @return Readiness flag.
+ */
+ public boolean ready() {
+ return ready;
+ }
+
+ /**
+ * Sets readiness flag.
+ *
+ * @param ready Flag value.
+ */
+ public void ready(boolean ready) {
+ this.ready = ready;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistRequest.java
new file mode 100644
index 0000000..f350d50
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistRequest.java
@@ -0,0 +1,578 @@
+/*
+ * 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.distributed.near;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.CacheObjectContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Request to enlist into transaction and acquire locks for entries produced
+ * with complex DML queries with reducer step.
+ *
+ * One request per batch of entries is used.
+ */
+public class GridNearTxQueryResultsEnlistRequest extends GridCacheIdMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long threadId;
+
+ /** */
+ private IgniteUuid futId;
+
+ /** */
+ private boolean clientFirst;
+
+ /** */
+ private int miniId;
+
+ /** */
+ private UUID subjId;
+
+ /** */
+ private AffinityTopologyVersion topVer;
+
+ /** */
+ private GridCacheVersion lockVer;
+
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
+ /** */
+ private long timeout;
+
+ /** */
+ private long txTimeout;
+
+ /** */
+ private int taskNameHash;
+
+ /** */
+ @GridDirectTransient
+ private Collection<Object> rows;
+
+ /** */
+ @GridToStringExclude
+ private KeyCacheObject[] keys;
+
+ /** */
+ @GridToStringExclude
+ private CacheObject[] values;
+
+ /** */
+ private EnlistOperation op;
+
+ /**
+ * Default constructor.
+ */
+ public GridNearTxQueryResultsEnlistRequest() {
+ // No-op.
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param threadId Thread id.
+ * @param futId Future id.
+ * @param miniId Mini-future id.
+ * @param subjId Transaction subject id.
+ * @param topVer Topology version.
+ * @param lockVer Lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param clientFirst First client request flag.
+ * @param timeout Timeout.
+ * @param txTimeout Tx timeout.
+ * @param taskNameHash Task name hash.
+ * @param rows Rows.
+ * @param op Operation.
+ */
+ GridNearTxQueryResultsEnlistRequest(int cacheId,
+ long threadId,
+ IgniteUuid futId,
+ int miniId,
+ UUID subjId,
+ AffinityTopologyVersion topVer,
+ GridCacheVersion lockVer,
+ MvccSnapshot mvccSnapshot,
+ boolean clientFirst,
+ long timeout,
+ long txTimeout, int taskNameHash,
+ Collection<Object> rows,
+ EnlistOperation op) {
+ this.txTimeout = txTimeout;
+ this.cacheId = cacheId;
+ this.threadId = threadId;
+ this.futId = futId;
+ this.miniId = miniId;
+ this.subjId = subjId;
+ this.topVer = topVer;
+ this.lockVer = lockVer;
+ this.mvccSnapshot = mvccSnapshot;
+ this.clientFirst = clientFirst;
+ this.timeout = timeout;
+ this.taskNameHash = taskNameHash;
+ this.rows = rows;
+ this.op = op;
+ }
+
+ /**
+ * @return Thread id.
+ */
+ public long threadId() {
+ return threadId;
+ }
+
+ /**
+ * @return Future id.
+ */
+ public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /**
+ * @return Mini future ID.
+ */
+ public int miniId() {
+ return miniId;
+ }
+
+ /**
+ * @return Subject id.
+ */
+ public UUID subjectId() {
+ return subjId;
+ }
+
+ /**
+ * @return Topology version.
+ */
+ @Override public AffinityTopologyVersion topologyVersion() {
+ return topVer;
+ }
+
+ /**
+ * @return Lock version.
+ */
+ public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ /**
+ * @return MVCC snapshot.
+ */
+ public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
+ * @return Timeout milliseconds.
+ */
+ public long timeout() {
+ return timeout;
+ }
+
+ /**
+ * @return Tx timeout milliseconds.
+ */
+ public long txTimeout() {
+ return txTimeout;
+ }
+
+ /**
+ * @return Task name hash.
+ */
+ public int taskNameHash() {
+ return taskNameHash;
+ }
+
+ /**
+ * @return {@code True} if this is the first client request.
+ */
+ public boolean firstClientRequest() {
+ return clientFirst;
+ }
+
+ /**
+ * @return Collection of rows.
+ */
+ public Collection<Object> rows() {
+ return rows;
+ }
+
+ /**
+ * @return Operation.
+ */
+ public EnlistOperation operation() {
+ return op;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void prepareMarshal(GridCacheSharedContext ctx) throws IgniteCheckedException {
+ super.prepareMarshal(ctx);
+
+ GridCacheContext cctx = ctx.cacheContext(cacheId);
+ CacheObjectContext objCtx = cctx.cacheObjectContext();
+
+ if (rows != null && keys == null) {
+ keys = new KeyCacheObject[rows.size()];
+
+ int i = 0;
+
+ boolean keysOnly = op.isDeleteOrLock();
+
+ values = keysOnly ? null : new CacheObject[keys.length];
+
+ for (Object row : rows) {
+ Object key, val = null;
+
+ if (keysOnly)
+ key = row;
+ else {
+ key = ((IgniteBiTuple)row).getKey();
+ val = ((IgniteBiTuple)row).getValue();
+ }
+
+ assert key != null && (keysOnly || val != null): "key=" + key + ", val=" + val;
+
+ KeyCacheObject key0 = cctx.toCacheKeyObject(key);
+
+ assert key0 != null;
+
+ key0.prepareMarshal(objCtx);
+
+ keys[i] = key0;
+
+ if (!keysOnly) {
+ CacheObject val0 = cctx.toCacheObject(val);
+
+ assert val0 != null;
+
+ val0.prepareMarshal(objCtx);
+
+ values[i] = val0;
+ }
+
+ i++;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void finishUnmarshal(GridCacheSharedContext ctx, ClassLoader ldr) throws IgniteCheckedException {
+ super.finishUnmarshal(ctx, ldr);
+
+ if (keys != null) {
+ rows = new ArrayList<>(keys.length);
+
+ CacheObjectContext objCtx = ctx.cacheContext(cacheId).cacheObjectContext();
+
+ for (int i = 0; i < keys.length; i++) {
+ keys[i].finishUnmarshal(objCtx, ldr);
+
+ if (op.isDeleteOrLock())
+ rows.add(keys[i]);
+ else {
+ if (values[i] != null)
+ values[i].finishUnmarshal(objCtx, ldr);
+
+ rows.add(new IgniteBiTuple<>(keys[i], values[i]));
+ }
+ }
+
+ keys = null;
+ values = null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeBoolean("clientFirst", clientFirst))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeIgniteUuid("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeObjectArray("keys", keys, MessageCollectionItemType.MSG))
+ return false;
+
+ writer.incrementState();
+
+ case 6:
+ if (!writer.writeMessage("lockVer", lockVer))
+ return false;
+
+ writer.incrementState();
+
+ case 7:
+ if (!writer.writeInt("miniId", miniId))
+ return false;
+
+ writer.incrementState();
+
+ case 8:
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
+ return false;
+
+ writer.incrementState();
+
+ case 9:
+ if (!writer.writeByte("op", op != null ? (byte)op.ordinal() : -1))
+ return false;
+
+ writer.incrementState();
+
+ case 10:
+ if (!writer.writeUuid("subjId", subjId))
+ return false;
+
+ writer.incrementState();
+
+ case 11:
+ if (!writer.writeInt("taskNameHash", taskNameHash))
+ return false;
+
+ writer.incrementState();
+
+ case 12:
+ if (!writer.writeLong("threadId", threadId))
+ return false;
+
+ writer.incrementState();
+
+ case 13:
+ if (!writer.writeLong("timeout", timeout))
+ return false;
+
+ writer.incrementState();
+
+ case 14:
+ if (!writer.writeMessage("topVer", topVer))
+ return false;
+
+ writer.incrementState();
+
+ case 15:
+ if (!writer.writeLong("txTimeout", txTimeout))
+ return false;
+
+ writer.incrementState();
+
+ case 16:
+ if (!writer.writeObjectArray("values", values, MessageCollectionItemType.MSG))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ clientFirst = reader.readBoolean("clientFirst");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ futId = reader.readIgniteUuid("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ keys = reader.readObjectArray("keys", MessageCollectionItemType.MSG, KeyCacheObject.class);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 6:
+ lockVer = reader.readMessage("lockVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 7:
+ miniId = reader.readInt("miniId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 8:
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 9:
+ byte opOrd;
+
+ opOrd = reader.readByte("op");
+
+ if (!reader.isLastRead())
+ return false;
+
+ op = EnlistOperation.fromOrdinal(opOrd);
+
+ reader.incrementState();
+
+ case 10:
+ subjId = reader.readUuid("subjId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 11:
+ taskNameHash = reader.readInt("taskNameHash");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 12:
+ threadId = reader.readLong("threadId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 13:
+ timeout = reader.readLong("timeout");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 14:
+ topVer = reader.readMessage("topVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 15:
+ txTimeout = reader.readLong("txTimeout");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 16:
+ values = reader.readObjectArray("values", MessageCollectionItemType.MSG, CacheObject.class);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridNearTxQueryResultsEnlistRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 17;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addDeploymentInfo() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 153;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxQueryResultsEnlistRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistResponse.java
new file mode 100644
index 0000000..749afd3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxQueryResultsEnlistResponse.java
@@ -0,0 +1,202 @@
+/*
+ * 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.distributed.near;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * A response to {@link GridNearTxQueryResultsEnlistRequest}.
+ */
+public class GridNearTxQueryResultsEnlistResponse extends GridNearTxQueryEnlistResponse {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private GridCacheVersion dhtVer;
+
+ /** */
+ private IgniteUuid dhtFutId;
+
+ /** */
+ private GridLongList updCntrs;
+
+ /**
+ * Default-constructor.
+ */
+ public GridNearTxQueryResultsEnlistResponse() {
+ // No-op.
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param futId Future id.
+ * @param miniId Mini future id.
+ * @param lockVer Lock version.
+ * @param res Result.
+ * @param dhtFutId Dht future id.
+ * @param dhtVer Dht version.
+ * @param updCntrs Update counters.
+ */
+ public GridNearTxQueryResultsEnlistResponse(int cacheId,
+ IgniteUuid futId,
+ int miniId,
+ GridCacheVersion lockVer,
+ long res,
+ GridCacheVersion dhtVer,
+ IgniteUuid dhtFutId,
+ GridLongList updCntrs) {
+ super(cacheId, futId, miniId, lockVer, res, false);
+
+ this.dhtVer = dhtVer;
+ this.dhtFutId = dhtFutId;
+ this.updCntrs = updCntrs;
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param futId Future id.
+ * @param miniId Mini future id.
+ * @param lockVer Lock version.
+ * @param err Error.
+ */
+ public GridNearTxQueryResultsEnlistResponse(int cacheId,
+ IgniteUuid futId,
+ int miniId,
+ GridCacheVersion lockVer,
+ Throwable err) {
+ super(cacheId, futId, miniId, lockVer, err);
+ }
+
+ /**
+ * @return Dht version.
+ */
+ public GridCacheVersion dhtVersion() {
+ return dhtVer;
+ }
+
+ /**
+ * @return Dht future id.
+ */
+ public IgniteUuid dhtFutureId() {
+ return dhtFutId;
+ }
+
+ /**
+ * @return Update counters.
+ */
+ public GridLongList updateCounters() {
+ return updCntrs;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 12;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 9:
+ if (!writer.writeIgniteUuid("dhtFutId", dhtFutId))
+ return false;
+
+ writer.incrementState();
+
+ case 10:
+ if (!writer.writeMessage("dhtVer", dhtVer))
+ return false;
+
+ writer.incrementState();
+
+ case 11:
+ if (!writer.writeMessage("updCntrs", updCntrs))
+ return false;
+
+ writer.incrementState();
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 9:
+ dhtFutId = reader.readIgniteUuid("dhtFutId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 10:
+ dhtVer = reader.readMessage("dhtVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 11:
+ updCntrs = reader.readMessage("updCntrs");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+ }
+
+ return reader.afterMessageRead(GridNearTxQueryResultsEnlistResponse.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 154;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxQueryResultsEnlistResponse.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
index 5477af9..879bf26 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
@@ -243,13 +243,16 @@ public class GridNearTxRemote extends GridDistributedTxRemoteAdapter {
return nearXidVer;
}
- /**
- * @param cntrs Partition indexes.
- */
+ /** {@inheritDoc} */
@Override public void setPartitionUpdateCounters(long[] cntrs) {
// No-op.
}
+ /** {@inheritDoc} */
+ @Override public void addActiveCache(GridCacheContext cacheCtx, boolean recovery) throws IgniteCheckedException {
+ throw new UnsupportedOperationException("Near tx doesn't track active caches.");
+ }
+
/**
* Adds owned versions to map.
*
[09/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSelectForUpdateQueryAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSelectForUpdateQueryAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSelectForUpdateQueryAbstractTest.java
new file mode 100644
index 0000000..8492d2a
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSelectForUpdateQueryAbstractTest.java
@@ -0,0 +1,374 @@
+/*
+ * 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.mvcc;
+
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import javax.cache.CacheException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.connect;
+import static org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.execute;
+
+/**
+ * Test for {@code SELECT FOR UPDATE} queries.
+ */
+public abstract class CacheMvccSelectForUpdateQueryAbstractTest extends CacheMvccAbstractTest {
+ /** */
+ private static final int CACHE_SIZE = 50;
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ disableScheduledVacuum = getName().equals("testSelectForUpdateAfterAbortedTx");
+
+ startGrids(3);
+
+ CacheConfiguration seg = new CacheConfiguration("segmented*");
+
+ seg.setCacheMode(cacheMode());
+
+ if (seg.getCacheMode() == PARTITIONED)
+ seg.setQueryParallelism(4);
+
+ grid(0).addCacheConfiguration(seg);
+
+ try (Connection c = connect(grid(0))) {
+ execute(c, "create table person (id int primary key, firstName varchar, lastName varchar) " +
+ "with \"atomicity=transactional,cache_name=Person\"");
+
+ execute(c, "create table person_seg (id int primary key, firstName varchar, lastName varchar) " +
+ "with \"atomicity=transactional,cache_name=PersonSeg,template=segmented\"");
+
+ try (Transaction tx = grid(0).transactions().txStart(TransactionConcurrency.PESSIMISTIC,
+ TransactionIsolation.REPEATABLE_READ)) {
+
+ for (int i = 1; i <= CACHE_SIZE; i++) {
+ execute(c, "insert into person(id, firstName, lastName) values(" + i + ",'" + i + "','" + i + "')");
+
+ execute(c, "insert into person_seg(id, firstName, lastName) " +
+ "values(" + i + ",'" + i + "','" + i + "')");
+ }
+
+ tx.commit();
+ }
+ }
+
+ AffinityTopologyVersion curVer = grid(0).context().cache().context().exchange().readyAffinityVersion();
+
+ AffinityTopologyVersion nextVer = curVer.nextMinorVersion();
+
+ // Let's wait for rebalance to complete.
+ for (int i = 0; i < 3; i++) {
+ IgniteEx node = grid(i);
+
+ IgniteInternalFuture<AffinityTopologyVersion> fut =
+ node.context().cache().context().exchange().affinityReadyFuture(nextVer);
+
+ if (fut != null)
+ fut.get();
+ }
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateDistributed() throws Exception {
+ doTestSelectForUpdateDistributed("Person", false);
+ }
+
+
+ /**
+ *
+ */
+ public void testSelectForUpdateLocal() throws Exception {
+ doTestSelectForUpdateLocal("Person", false);
+ }
+
+ /**
+ *
+ * @throws Exception If failed.
+ */
+ public void testSelectForUpdateOutsideTx() throws Exception {
+ doTestSelectForUpdateDistributed("Person", true);
+ }
+
+ /**
+ *
+ * @throws Exception If failed.
+ */
+ public void testSelectForUpdateOutsideTxLocal() throws Exception {
+ doTestSelectForUpdateLocal("Person", true);
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @param outsideTx Whether select is executed outside transaction
+ * @throws Exception If failed.
+ */
+ void doTestSelectForUpdateLocal(String cacheName, boolean outsideTx) throws Exception {
+ Ignite node = grid(0);
+
+ IgniteCache<Integer, ?> cache = node.cache(cacheName);
+
+ Transaction ignored = outsideTx ? null : node.transactions().txStart(TransactionConcurrency.PESSIMISTIC,
+ TransactionIsolation.REPEATABLE_READ);
+
+ try {
+ SqlFieldsQuery qry = new SqlFieldsQuery("select id, * from " + tableName(cache) + " order by id for update")
+ .setLocal(true);
+
+ FieldsQueryCursor<List<?>> query = cache.query(qry);
+
+ List<List<?>> res = query.getAll();
+
+ List<Integer> keys = new ArrayList<>();
+
+ for (List<?> r : res)
+ keys.add((Integer)r.get(0));
+
+ checkLocks(cacheName, keys, !outsideTx);
+ }
+ finally {
+ U.close(ignored, log);
+ }
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @param outsideTx Whether select is executed outside transaction
+ * @throws Exception If failed.
+ */
+ void doTestSelectForUpdateDistributed(String cacheName, boolean outsideTx) throws Exception {
+ Ignite node = grid(0);
+
+ IgniteCache<Integer, ?> cache = node.cache(cacheName);
+
+ Transaction ignored = outsideTx ? null : node.transactions().txStart(TransactionConcurrency.PESSIMISTIC,
+ TransactionIsolation.REPEATABLE_READ);
+
+ try {
+ SqlFieldsQuery qry = new SqlFieldsQuery("select id, * from " + tableName(cache) + " order by id for update")
+ .setPageSize(10);
+
+ FieldsQueryCursor<List<?>> query = cache.query(qry);
+
+ List<List<?>> res = query.getAll();
+
+ List<Integer> keys = new ArrayList<>();
+
+ for (List<?> r : res)
+ keys.add((Integer)r.get(0));
+
+ checkLocks(cacheName, keys, !outsideTx);
+ }
+ finally {
+ U.close(ignored, log);
+ }
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateWithUnion() {
+ assertQueryThrows("select id from person union select 1 for update",
+ "SELECT UNION FOR UPDATE is not supported.");
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateWithJoin() {
+ assertQueryThrows("select p1.id from person p1 join person p2 on p1.id = p2.id for update",
+ "SELECT FOR UPDATE with joins is not supported.");
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateWithLimit() {
+ assertQueryThrows("select id from person limit 0,5 for update",
+ "LIMIT/OFFSET clauses are not supported for SELECT FOR UPDATE.");
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateWithGroupings() {
+ assertQueryThrows("select count(*) from person for update",
+ "SELECT FOR UPDATE with aggregates and/or GROUP BY is not supported.");
+
+ assertQueryThrows("select lastName, count(*) from person group by lastName for update",
+ "SELECT FOR UPDATE with aggregates and/or GROUP BY is not supported.");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSelectForUpdateAfterAbortedTx() throws Exception {
+ assert disableScheduledVacuum;
+
+ Ignite node = grid(0);
+
+ IgniteCache<Integer, ?> cache = node.cache("Person");
+
+ List<List<?>> res;
+
+ try (Transaction tx = node.transactions().txStart(TransactionConcurrency.PESSIMISTIC,
+ TransactionIsolation.REPEATABLE_READ)) {
+
+ res = cache.query(new SqlFieldsQuery("update person set lastName=UPPER(lastName)")).getAll();
+
+ assertEquals((long)CACHE_SIZE, res.get(0).get(0));
+
+ tx.rollback();
+ }
+
+ try (Transaction tx = node.transactions().txStart(TransactionConcurrency.PESSIMISTIC,
+ TransactionIsolation.REPEATABLE_READ)) {
+
+ res = cache.query(new SqlFieldsQuery("select id, * from person order by id for update")).getAll();
+
+ assertEquals(CACHE_SIZE, res.size());
+
+ List<Integer> keys = new ArrayList<>();
+
+ for (List<?> r : res)
+ keys.add((Integer)r.get(0));
+
+ checkLocks("Person", keys, true);
+
+ tx.rollback();
+ }
+ }
+
+ /**
+ * Check that an attempt to get a lock on any key from given list fails by timeout.
+ *
+ * @param cacheName Cache name to check.
+ * @param keys Keys to check.
+ * @param locked Whether the key is locked
+ * @throws Exception if failed.
+ */
+ @SuppressWarnings({"ThrowableNotThrown", "unchecked"})
+ private void checkLocks(String cacheName, List<Integer> keys, boolean locked) throws Exception {
+ Ignite node = ignite(2);
+ IgniteCache cache = node.cache(cacheName);
+
+ List<IgniteInternalFuture<Integer>> calls = new ArrayList<>();
+
+ for (int key : keys) {
+ calls.add(GridTestUtils.runAsync(new Callable<Integer>() {
+ /** {@inheritDoc} */
+ @Override public Integer call() {
+ try (Transaction ignored = node.transactions().txStart(TransactionConcurrency.PESSIMISTIC,
+ TransactionIsolation.REPEATABLE_READ)) {
+ List<List<?>> res = cache
+ .query(
+ new SqlFieldsQuery("select * from " + tableName(cache) +
+ " where id = " + key + " for update").setTimeout(1, TimeUnit.SECONDS)
+ )
+ .getAll();
+
+ return (Integer)res.get(0).get(0);
+ }
+ }
+ }));
+ }
+
+ for (IgniteInternalFuture fut : calls) {
+ if (!locked)
+ fut.get(TX_TIMEOUT);
+ else {
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ try {
+ return fut.get(TX_TIMEOUT);
+ }
+ catch (IgniteCheckedException e) {
+ if (X.hasCause(e, CacheException.class))
+ throw X.cause(e, CacheException.class);
+
+ throw e;
+ }
+ }
+ }, CacheException.class, "IgniteTxTimeoutCheckedException");
+ }
+ }
+ }
+
+ /**
+ * @param cache Cache.
+ * @return Name of the table contained by this cache.
+ */
+ @SuppressWarnings("unchecked")
+ private static String tableName(IgniteCache<?, ?> cache) {
+ return ((Collection<QueryEntity>)cache.getConfiguration(CacheConfiguration.class).getQueryEntities())
+ .iterator().next().getTableName();
+ }
+
+ /**
+ * Test that query throws exception with expected message.
+ * @param qry SQL.
+ * @param exMsg Expected message.
+ */
+ private void assertQueryThrows(String qry, String exMsg) {
+ assertQueryThrows(qry, exMsg, false);
+
+ assertQueryThrows(qry, exMsg, true);
+ }
+
+ /**
+ * Test that query throws exception with expected message.
+ * @param qry SQL.
+ * @param exMsg Expected message.
+ * @param loc Local query flag.
+ */
+ private void assertQueryThrows(String qry, String exMsg, boolean loc) {
+ Ignite node = grid(0);
+
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() {
+ return node.cache("Person").query(new SqlFieldsQuery(qry).setLocal(loc)).getAll();
+ }
+ }, IgniteSQLException.class, exMsg);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentJdbcTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentJdbcTransactionTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentJdbcTransactionTest.java
new file mode 100644
index 0000000..437195f
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentJdbcTransactionTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public class CacheMvccSizeWithConcurrentJdbcTransactionTest extends CacheMvccSizeWithConcurrentTransactionTest {
+ /** {@inheritDoc} */
+ @Override boolean jdbcTx() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlQueriesAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlQueriesAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlQueriesAbstractTest.java
new file mode 100644
index 0000000..4ad667b9
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlQueriesAbstractTest.java
@@ -0,0 +1,1611 @@
+/*
+ * 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.mvcc;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.cache.processor.MutableEntry;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteTransactions;
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.util.lang.GridInClosure3;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL_SUM;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.PUT;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * TODO IGNITE-6739: text/spatial indexes with mvcc.
+ * TODO IGNITE-6739: indexingSpi with mvcc.
+ * TODO IGNITE-6739: setQueryParallelism with mvcc.
+ * TODO IGNITE-6739: dynamic index create.
+ */
+@SuppressWarnings("unchecked")
+public abstract class CacheMvccSqlQueriesAbstractTest extends CacheMvccAbstractTest {
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_WithRemoves_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_SingleNode_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxSql_SingleNode();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSumSql_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL_SUM, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_WithRemoves_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_WithRemoves_SingleNode_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxSql_WithRemoves_SingleNode();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdateSingleValue_SingleNode() throws Exception {
+ updateSingleValue(true, false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdateSingleValue_LocalQuery_SingleNode() throws Exception {
+ updateSingleValue(true, true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdateSingleValue_ClientServer() throws Exception {
+ updateSingleValue(false, false);
+ }
+
+ /**
+ * @param singleNode {@code True} for test with single node.
+ * @param locQry Local query flag.
+ * @throws Exception If failed.
+ */
+ private void updateSingleValue(boolean singleNode, final boolean locQry) throws Exception {
+ final int VALS = 100;
+
+ final int writers = 4;
+
+ final int readers = 4;
+
+ final int INC_BY = 110;
+
+ final IgniteInClosure<IgniteCache<Object, Object>> init = new IgniteInClosure<IgniteCache<Object, Object>>() {
+ @Override public void apply(IgniteCache<Object, Object> cache) {
+ Map<Integer, MvccTestSqlIndexValue> vals = new HashMap<>();
+
+ for (int i = 0; i < VALS; i++)
+ vals.put(i, new MvccTestSqlIndexValue(i));
+
+ cache.putAll(vals);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ try {
+ Integer key = rnd.nextInt(VALS);
+
+ cache.cache.invoke(key, new CacheEntryProcessor<Integer, MvccTestSqlIndexValue, Object>() {
+ @Override public Object process(MutableEntry<Integer, MvccTestSqlIndexValue> e, Object... args) {
+ Integer key = e.getKey();
+
+ MvccTestSqlIndexValue val = e.getValue();
+
+ int newIdxVal;
+
+ if (val.idxVal1 < INC_BY) {
+ assertEquals(key.intValue(), val.idxVal1);
+
+ newIdxVal = val.idxVal1 + INC_BY;
+ }
+ else {
+ assertEquals(INC_BY + key, val.idxVal1);
+
+ newIdxVal = key;
+ }
+
+ e.setValue(new MvccTestSqlIndexValue(newIdxVal));
+
+ return null;
+ }
+ });
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ List<SqlFieldsQuery> fieldsQrys = new ArrayList<>();
+
+ fieldsQrys.add(
+ new SqlFieldsQuery("select _key, idxVal1 from MvccTestSqlIndexValue where idxVal1=?").setLocal(locQry));
+
+ fieldsQrys.add(new SqlFieldsQuery("select _key, idxVal1 from MvccTestSqlIndexValue where idxVal1=? or idxVal1=?").setLocal(locQry));
+
+ fieldsQrys.add(new SqlFieldsQuery("select _key, idxVal1 from MvccTestSqlIndexValue where _key=?").setLocal(locQry));
+
+ List<SqlQuery<Integer, MvccTestSqlIndexValue>> sqlQrys = new ArrayList<>();
+
+ sqlQrys.add(new SqlQuery<Integer, MvccTestSqlIndexValue>(MvccTestSqlIndexValue.class, "idxVal1=?").setLocal(locQry));
+
+ sqlQrys.add(new SqlQuery<Integer, MvccTestSqlIndexValue>(MvccTestSqlIndexValue.class, "idxVal1=? or idxVal1=?").setLocal(locQry));
+
+ sqlQrys.add(new SqlQuery<Integer, MvccTestSqlIndexValue>(MvccTestSqlIndexValue.class, "_key=?").setLocal(locQry));
+
+ while (!stop.get()) {
+ Integer key = rnd.nextInt(VALS);
+
+ int qryIdx = rnd.nextInt(3);
+
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ List<List<?>> res;
+
+ try {
+ if (rnd.nextBoolean()) {
+ SqlFieldsQuery qry = fieldsQrys.get(qryIdx);
+
+ if (qryIdx == 1)
+ qry.setArgs(key, key + INC_BY);
+ else
+ qry.setArgs(key);
+
+ res = cache.cache.query(qry).getAll();
+ }
+ else {
+ SqlQuery<Integer, MvccTestSqlIndexValue> qry = sqlQrys.get(qryIdx);
+
+ if (qryIdx == 1)
+ qry.setArgs(key, key + INC_BY);
+ else
+ qry.setArgs(key);
+
+ res = new ArrayList<>();
+
+ for (IgniteCache.Entry<Integer, MvccTestSqlIndexValue> e : cache.cache.query(qry).getAll()) {
+ List<Object> row = new ArrayList<>(2);
+
+ row.add(e.getKey());
+ row.add(e.getValue().idxVal1);
+
+ res.add(row);
+ }
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ assertTrue(qryIdx == 0 || !res.isEmpty());
+
+ if (!res.isEmpty()) {
+ assertEquals(1, res.size());
+
+ List<?> resVals = res.get(0);
+
+ Integer key0 = (Integer)resVals.get(0);
+ Integer val0 = (Integer)resVals.get(1);
+
+ assertEquals(key, key0);
+ assertTrue(val0.equals(key) || val0.equals(key + INC_BY));
+ }
+ }
+
+ if (idx == 0) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("select _key, idxVal1 from MvccTestSqlIndexValue");
+
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ List<List<?>> res;
+
+ try {
+ res = cache.cache.query(qry).getAll();
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ assertEquals(VALS, res.size());
+
+ for (List<?> vals : res)
+ info("Value: " + vals);
+ }
+ }
+ };
+
+ int srvs;
+ int clients;
+
+ if (singleNode) {
+ srvs = 1;
+ clients = 0;
+ }
+ else {
+ srvs = 4;
+ clients = 2;
+ }
+
+ readWriteTest(
+ null,
+ srvs,
+ clients,
+ 0,
+ DFLT_PARTITION_COUNT,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, MvccTestSqlIndexValue.class),
+ init,
+ writer,
+ reader);
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testJoinTransactional_SingleNode() throws Exception {
+ joinTransactional(true, false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testJoinTransactional_ClientServer() throws Exception {
+ joinTransactional(false, false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testJoinTransactional_DistributedJoins_ClientServer() throws Exception {
+ joinTransactional(false, true);
+ }
+
+ /**
+ * @param singleNode {@code True} for test with single node.
+ * @param distributedJoin {@code True} to test distributed joins.
+ * @throws Exception If failed.
+ */
+ private void joinTransactional(boolean singleNode, final boolean distributedJoin) throws Exception {
+ final int KEYS = 100;
+
+ final int writers = 4;
+
+ final int readers = 4;
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Object, Object> cache = randomCache(caches, rnd);
+
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ try {
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ Integer key = rnd.nextInt(KEYS);
+
+ JoinTestChildKey childKey = new JoinTestChildKey(key);
+
+ JoinTestChild child = (JoinTestChild)cache.cache.get(childKey);
+
+ if (child == null) {
+ Integer parentKey = distributedJoin ? key + 100 : key;
+
+ child = new JoinTestChild(parentKey);
+
+ cache.cache.put(childKey, child);
+
+ JoinTestParent parent = new JoinTestParent(parentKey);
+
+ cache.cache.put(new JoinTestParentKey(parentKey), parent);
+ }
+ else {
+ cache.cache.remove(childKey);
+
+ cache.cache.remove(new JoinTestParentKey(child.parentId));
+ }
+
+ tx.commit();
+ }
+
+ cnt++;
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ List<SqlFieldsQuery> qrys = new ArrayList<>();
+
+ qrys.add(new SqlFieldsQuery("select c.parentId, p.id from " +
+ "JoinTestChild c left outer join JoinTestParent p on (c.parentId = p.id)").
+ setDistributedJoins(distributedJoin));
+
+ qrys.add(new SqlFieldsQuery("select c.parentId, p.id from " +
+ "JoinTestChild c left outer join JoinTestParent p on (c.parentId = p.id) where p.id = 10").
+ setDistributedJoins(distributedJoin));
+
+ qrys.add(new SqlFieldsQuery("select c.parentId, p.id from " +
+ "JoinTestChild c left outer join JoinTestParent p on (c.parentId = p.id) where p.id != 10").
+ setDistributedJoins(distributedJoin));
+
+ while (!stop.get()) {
+ TestCache<Object, Object> cache = randomCache(caches, rnd);
+
+ try {
+ for (SqlFieldsQuery qry : qrys) {
+ List<List<?>> res = cache.cache.query(qry).getAll();
+
+ if (!res.isEmpty()) {
+ for (List<?> resRow : res) {
+ Integer parentId = (Integer)resRow.get(1);
+
+ assertNotNull(parentId);
+ }
+ }
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ if (idx == 0) {
+ TestCache<Object, Object> cache = randomCache(caches, rnd);
+
+ try {
+ List<List<?>> res = cache.cache.query(qrys.get(0)).getAll();
+
+ info("Reader finished, result: " + res);
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+ }
+ };
+
+ int srvs;
+ int clients;
+
+ if (singleNode) {
+ srvs = 1;
+ clients = 0;
+ }
+ else {
+ srvs = 4;
+ clients = 2;
+ }
+
+ readWriteTest(
+ null,
+ srvs,
+ clients,
+ 0,
+ DFLT_PARTITION_COUNT,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ new InitIndexing(JoinTestParentKey.class, JoinTestParent.class,
+ JoinTestChildKey.class, JoinTestChild.class),
+ null,
+ writer,
+ reader);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testJoinTransactional_DistributedJoins_ClientServer2() throws Exception {
+ final int KEYS = 100;
+
+ final int writers = 1;
+
+ final int readers = 4;
+
+ final int CHILDREN_CNT = 10;
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Object, Object> cache = randomCache(caches, rnd);
+
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ try {
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ Integer key = rnd.nextInt(KEYS);
+
+ JoinTestParentKey parentKey = new JoinTestParentKey(key);
+
+ JoinTestParent parent = (JoinTestParent)cache.cache.get(parentKey);
+
+ if (parent == null) {
+ for (int i = 0; i < CHILDREN_CNT; i++)
+ cache.cache.put(new JoinTestChildKey(key * 10_000 + i), new JoinTestChild(key));
+
+ cache.cache.put(parentKey, new JoinTestParent(key));
+ }
+ else {
+ for (int i = 0; i < CHILDREN_CNT; i++)
+ cache.cache.remove(new JoinTestChildKey(key * 10_000 + i));
+
+ cache.cache.remove(parentKey);
+ }
+
+ tx.commit();
+ }
+
+ cnt++;
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("select c.parentId, p.id from " +
+ "JoinTestChild c left outer join JoinTestParent p on (c.parentId = p.id) where p.id=?").
+ setDistributedJoins(true);
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Object, Object> cache = randomCache(caches, rnd);
+
+ qry.setArgs(rnd.nextInt(KEYS));
+
+ try {
+ List<List<?>> res = cache.cache.query(qry).getAll();
+
+ if (!res.isEmpty())
+ assertEquals(CHILDREN_CNT, res.size());
+
+ cnt++;
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Reader finished, read count: " + cnt);
+ }
+ };
+
+ readWriteTest(
+ null,
+ 4,
+ 2,
+ 0,
+ DFLT_PARTITION_COUNT,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ new InitIndexing(JoinTestParentKey.class, JoinTestParent.class,
+ JoinTestChildKey.class, JoinTestChild.class),
+ null,
+ writer,
+ reader);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testDistributedJoinSimple() throws Exception {
+ startGridsMultiThreaded(4);
+
+ Ignite srv0 = ignite(0);
+
+ int[] backups = {0, 1, 2};
+
+ for (int b : backups) {
+ IgniteCache<Object, Object> cache = srv0.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, b, DFLT_PARTITION_COUNT).
+ setIndexedTypes(JoinTestParentKey.class, JoinTestParent.class, JoinTestChildKey.class, JoinTestChild.class));
+
+ int cntr = 0;
+
+ int expCnt = 0;
+
+ for (int i = 0; i < 10; i++) {
+ JoinTestParentKey parentKey = new JoinTestParentKey(i);
+
+ cache.put(parentKey, new JoinTestParent(i));
+
+ for (int c = 0; c < i; c++) {
+ JoinTestChildKey childKey = new JoinTestChildKey(cntr++);
+
+ cache.put(childKey, new JoinTestChild(i));
+
+ expCnt++;
+ }
+ }
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("select c.parentId, p.id from " +
+ "JoinTestChild c join JoinTestParent p on (c.parentId = p.id)").
+ setDistributedJoins(true);
+
+ Map<Integer, Integer> resMap = new HashMap<>();
+
+ List<List<?>> res = cache.query(qry).getAll();
+
+ assertEquals(expCnt, res.size());
+
+ for (List<?> resRow : res) {
+ Integer parentId = (Integer)resRow.get(0);
+
+ Integer cnt = resMap.get(parentId);
+
+ if (cnt == null)
+ resMap.put(parentId, 1);
+ else
+ resMap.put(parentId, cnt + 1);
+ }
+
+ for (int i = 1; i < 10; i++)
+ assertEquals(i, (Object)resMap.get(i));
+
+ srv0.destroyCache(cache.getName());
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCacheRecreate() throws Exception {
+ cacheRecreate(new InitIndexing(Integer.class, MvccTestAccount.class));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCacheRecreateChangeIndexedType() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ final int PARTS = 64;
+
+ {
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, PARTS).
+ setIndexedTypes(Integer.class, MvccTestAccount.class);
+
+ IgniteCache<Integer, MvccTestAccount> cache = (IgniteCache)srv0.createCache(ccfg);
+
+ for (int k = 0; k < PARTS * 2; k++) {
+ assertNull(cache.get(k));
+
+ int vals = k % 3 + 1;
+
+ for (int v = 0; v < vals; v++)
+ cache.put(k, new MvccTestAccount(v, 1));
+
+ assertEquals(vals - 1, cache.get(k).val);
+ }
+
+ assertEquals(PARTS * 2, cache.query(new SqlQuery<>(MvccTestAccount.class, "true")).getAll().size());
+
+ srv0.destroyCache(cache.getName());
+ }
+
+ {
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, PARTS).
+ setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class);
+
+ IgniteCache<Integer, MvccTestSqlIndexValue> cache = (IgniteCache)srv0.createCache(ccfg);
+
+ for (int k = 0; k < PARTS * 2; k++) {
+ assertNull(cache.get(k));
+
+ int vals = k % 3 + 1;
+
+ for (int v = 0; v < vals; v++)
+ cache.put(k, new MvccTestSqlIndexValue(v));
+
+ assertEquals(vals - 1, cache.get(k).idxVal1);
+ }
+
+ assertEquals(PARTS * 2, cache.query(new SqlQuery<>(MvccTestSqlIndexValue.class, "true")).getAll().size());
+
+ srv0.destroyCache(cache.getName());
+ }
+
+ {
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, PARTS).
+ setIndexedTypes(Long.class, Long.class);
+
+ IgniteCache<Long, Long> cache = (IgniteCache)srv0.createCache(ccfg);
+
+ for (int k = 0; k < PARTS * 2; k++) {
+ assertNull(cache.get((long)k));
+
+ int vals = k % 3 + 1;
+
+ for (int v = 0; v < vals; v++)
+ cache.put((long)k, (long)v);
+
+ assertEquals((long)(vals - 1), (Object)cache.get((long)k));
+ }
+
+ assertEquals(PARTS * 2, cache.query(new SqlQuery<>(Long.class, "true")).getAll().size());
+
+ srv0.destroyCache(cache.getName());
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testChangeValueType1() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class, Integer.class, Integer.class);
+
+ IgniteCache<Object, Object> cache = srv0.createCache(ccfg);
+
+ cache.put(1, new MvccTestSqlIndexValue(1));
+ cache.put(1, new MvccTestSqlIndexValue(2));
+
+ checkSingleResult(cache, new SqlFieldsQuery("select idxVal1 from MvccTestSqlIndexValue"), 2);
+
+ cache.put(1, 1);
+
+ assertEquals(0, cache.query(new SqlFieldsQuery("select idxVal1 from MvccTestSqlIndexValue")).getAll().size());
+
+ checkSingleResult(cache, new SqlFieldsQuery("select _val from Integer"), 1);
+
+ cache.put(1, 2);
+
+ checkSingleResult(cache, new SqlFieldsQuery("select _val from Integer"), 2);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testChangeValueType2() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ CacheConfiguration<Object, Object> ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class, Integer.class, Integer.class);
+
+ IgniteCache<Object, Object> cache = srv0.createCache(ccfg);
+
+ cache.put(1, new MvccTestSqlIndexValue(1));
+ cache.put(1, new MvccTestSqlIndexValue(2));
+
+ checkSingleResult(cache, new SqlFieldsQuery("select idxVal1 from MvccTestSqlIndexValue"), 2);
+
+ cache.remove(1);
+
+ assertEquals(0, cache.query(new SqlFieldsQuery("select idxVal1 from MvccTestSqlIndexValue")).getAll().size());
+
+ cache.put(1, 1);
+
+ assertEquals(0, cache.query(new SqlFieldsQuery("select idxVal1 from MvccTestSqlIndexValue")).getAll().size());
+
+ checkSingleResult(cache, new SqlFieldsQuery("select _val from Integer"), 1);
+
+ cache.put(1, 2);
+
+ checkSingleResult(cache, new SqlFieldsQuery("select _val from Integer"), 2);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCountTransactional_SingleNode() throws Exception {
+ countTransactional(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCountTransactional_ClientServer() throws Exception {
+ countTransactional(false);
+ }
+
+ /**
+ * @param singleNode {@code True} for test with single node.
+ * @throws Exception If failed.
+ */
+ private void countTransactional(boolean singleNode) throws Exception {
+ final int writers = 4;
+
+ final int readers = 4;
+
+ final int THREAD_KEY_RANGE = 100;
+
+ final int VAL_RANGE = 10;
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int min = idx * THREAD_KEY_RANGE;
+ int max = min + THREAD_KEY_RANGE;
+
+ info("Thread range [min=" + min + ", max=" + max + ']');
+
+ int cnt = 0;
+
+ Set<Integer> keys = new LinkedHashSet<>();
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ try {
+ // Add or remove 10 keys.
+ if (!keys.isEmpty() && (keys.size() == THREAD_KEY_RANGE || rnd.nextInt(3) == 0)) {
+ Set<Integer> rmvKeys = new HashSet<>();
+
+ for (Integer key : keys) {
+ rmvKeys.add(key);
+
+ if (rmvKeys.size() == 10)
+ break;
+ }
+
+ assertEquals(10, rmvKeys.size());
+
+ cache.cache.removeAll(rmvKeys);
+
+ keys.removeAll(rmvKeys);
+ }
+ else {
+ TreeMap<Integer, MvccTestSqlIndexValue> map = new TreeMap<>();
+
+ while (map.size() != 10) {
+ Integer key = rnd.nextInt(min, max);
+
+ if (keys.add(key))
+ map.put(key, new MvccTestSqlIndexValue(rnd.nextInt(VAL_RANGE)));
+ }
+
+ assertEquals(10, map.size());
+
+ cache.cache.putAll(map);
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ List<SqlFieldsQuery> qrys = new ArrayList<>();
+
+ qrys.add(new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue"));
+
+ qrys.add(new SqlFieldsQuery(
+ "select count(*) from MvccTestSqlIndexValue where idxVal1 >= 0 and idxVal1 <= " + VAL_RANGE));
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ try {
+ for (SqlFieldsQuery qry : qrys) {
+ List<List<?>> res = cache.cache.query(qry).getAll();
+
+ assertEquals(1, res.size());
+
+ Long cnt = (Long)res.get(0).get(0);
+
+ assertTrue(cnt % 10 == 0);
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+ }
+ };
+
+ int srvs;
+ int clients;
+
+ if (singleNode) {
+ srvs = 1;
+ clients = 0;
+ }
+ else {
+ srvs = 4;
+ clients = 2;
+ }
+
+ readWriteTest(
+ null,
+ srvs,
+ clients,
+ 0,
+ DFLT_PARTITION_COUNT,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, MvccTestSqlIndexValue.class),
+ null,
+ writer,
+ reader);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMaxMinTransactional_SingleNode() throws Exception {
+ maxMinTransactional(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMaxMinTransactional_ClientServer() throws Exception {
+ maxMinTransactional(false);
+ }
+
+ /**
+ * @param singleNode {@code True} for test with single node.
+ * @throws Exception If failed.
+ */
+ private void maxMinTransactional(boolean singleNode) throws Exception {
+ final int writers = 1;
+
+ final int readers = 1;
+
+ final int THREAD_OPS = 10;
+
+ final int OP_RANGE = 10;
+
+ final int THREAD_KEY_RANGE = OP_RANGE * THREAD_OPS;
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int min = idx * THREAD_KEY_RANGE;
+
+ info("Thread range [start=" + min + ']');
+
+ int cnt = 0;
+
+ boolean add = true;
+
+ int op = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ try {
+ int startKey = min + op * OP_RANGE;
+
+ if (add) {
+ Map<Integer, MvccTestSqlIndexValue> vals = new HashMap<>();
+
+ for (int i = 0; i < 10; i++) {
+ Integer key = startKey + i + 1;
+
+ vals.put(key, new MvccTestSqlIndexValue(key));
+ }
+
+ cache.cache.putAll(vals);
+
+ // info("put " + vals.keySet());
+ }
+ else {
+ Set<Integer> rmvKeys = new HashSet<>();
+
+ for (int i = 0; i < 10; i++)
+ rmvKeys.add(startKey + i + 1);
+
+ cache.cache.removeAll(rmvKeys);
+
+ // info("remove " + rmvKeys);
+ }
+
+ if (++op == THREAD_OPS) {
+ add = !add;
+
+ op = 0;
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ List<SqlFieldsQuery> maxQrys = new ArrayList<>();
+ List<SqlFieldsQuery> minQrys = new ArrayList<>();
+
+ maxQrys.add(new SqlFieldsQuery("select max(idxVal1) from MvccTestSqlIndexValue"));
+ maxQrys.add(new SqlFieldsQuery("select max(idxVal1) from MvccTestSqlIndexValue where idxVal1 >= 0"));
+
+ minQrys.add(new SqlFieldsQuery("select min(idxVal1) from MvccTestSqlIndexValue"));
+ minQrys.add(new SqlFieldsQuery("select min(idxVal1) from MvccTestSqlIndexValue where idxVal1 >= 0"));
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestSqlIndexValue> cache = randomCache(caches, rnd);
+
+ try {
+ for (SqlFieldsQuery qry : maxQrys) {
+ List<List<?>> res = cache.cache.query(qry).getAll();
+
+ assertEquals(1, res.size());
+
+ Integer m = (Integer)res.get(0).get(0);
+
+ assertTrue(m == null || m % 10 == 0);
+ }
+
+ for (SqlFieldsQuery qry : minQrys) {
+ List<List<?>> res = cache.cache.query(qry).getAll();
+
+ assertEquals(1, res.size());
+
+ Integer m = (Integer)res.get(0).get(0);
+
+ assertTrue(m == null || m % 10 == 1);
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+ }
+ };
+
+ int srvs;
+ int clients;
+
+ if (singleNode) {
+ srvs = 1;
+ clients = 0;
+ }
+ else {
+ srvs = 4;
+ clients = 2;
+ }
+
+ readWriteTest(
+ null,
+ srvs,
+ clients,
+ 0,
+ DFLT_PARTITION_COUNT,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, MvccTestSqlIndexValue.class),
+ null,
+ writer,
+ reader);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlQueriesWithMvcc() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ IgniteCache<Integer, MvccTestSqlIndexValue> cache = (IgniteCache)srv0.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class));
+
+ for (int i = 0; i < 10; i++)
+ cache.put(i, new MvccTestSqlIndexValue(i));
+
+ sqlQueriesWithMvcc(cache, true);
+
+ sqlQueriesWithMvcc(cache, false);
+
+ // TODO IGNITE-8031
+// startGrid(1);
+//
+// awaitPartitionMapExchange();
+//
+// sqlQueriesWithMvcc(cache, false);
+ }
+
+ /**
+ * @param cache Cache.
+ * @param loc Local query flag.
+ */
+ private void sqlQueriesWithMvcc(IgniteCache<Integer, MvccTestSqlIndexValue> cache, boolean loc) {
+ assertEquals(10,
+ cache.query(new SqlQuery<>(MvccTestSqlIndexValue.class, "true").setLocal(loc)).getAll().size());
+
+ assertEquals(10,
+ cache.query(new SqlFieldsQuery("select idxVal1 from MvccTestSqlIndexValue").setLocal(loc)).getAll().size());
+
+ assertEquals(10,
+ cache.query(new SqlFieldsQuery("" +
+ "select (select count (*) from MvccTestSqlIndexValue where idxVal1 = t1.idxVal1) as c1," +
+ " (select 0 from dual) as c2" +
+ " from MvccTestSqlIndexValue as t1" +
+ " join (select * from MvccTestSqlIndexValue) as t2 on t1.idxVal1 = t2.idxVal1").setLocal(loc)).getAll().size());
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select max(idxVal1) from MvccTestSqlIndexValue").setLocal(loc), 9);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select max(idxVal1) from MvccTestSqlIndexValue where idxVal1 > 0").setLocal(loc), 9);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select max(idxVal1) from MvccTestSqlIndexValue where idxVal1 < 5").setLocal(loc), 4);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select min(idxVal1) from MvccTestSqlIndexValue").setLocal(loc), 0);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select min(idxVal1) from MvccTestSqlIndexValue where idxVal1 < 100").setLocal(loc), 0);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select min(idxVal1) from MvccTestSqlIndexValue where idxVal1 < 5").setLocal(loc), 0);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select min(idxVal1) from MvccTestSqlIndexValue where idxVal1 > 5").setLocal(loc), 6);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue").setLocal(loc), 10L);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue where idxVal1 >= 0").setLocal(loc), 10L);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue where idxVal1 >= 0 and idxVal1 < 100").setLocal(loc), 10L);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue where idxVal1 >0 and idxVal1 < 5").setLocal(loc), 4L);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue where idxVal1 >= 1").setLocal(loc), 9L);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue where idxVal1 > 100").setLocal(loc), 0L);
+
+ checkSingleResult(cache,
+ new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue where idxVal1 = 1").setLocal(loc), 1L);
+ }
+
+ /**
+ * @param cache Cache.
+ * @param qry Query.
+ * @param exp Expected value.
+ */
+ private void checkSingleResult(IgniteCache cache, SqlFieldsQuery qry, Object exp) {
+ List<List<?>> res = cache.query(qry).getAll();
+
+ assertEquals(1, res.size());
+
+ List<?> row = res.get(0);
+
+ assertEquals(1, row.size());
+
+ assertEquals(exp, row.get(0));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlSimple() throws Exception {
+ startGrid(0);
+
+ for (int i = 0; i < 4; i++)
+ sqlSimple(i * 512);
+
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ for (int i = 0; i < 5; i++)
+ sqlSimple(rnd.nextInt(2048));
+ }
+
+ /**
+ * @param inlineSize Inline size.
+ * @throws Exception If failed.
+ */
+ private void sqlSimple(int inlineSize) throws Exception {
+ Ignite srv0 = ignite(0);
+
+ IgniteCache<Integer, MvccTestSqlIndexValue> cache = (IgniteCache)srv0.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class).
+ setSqlIndexMaxInlineSize(inlineSize));
+
+ Map<Integer, Integer> expVals = new HashMap<>();
+
+ checkValues(expVals, cache);
+
+ cache.put(1, new MvccTestSqlIndexValue(1));
+ expVals.put(1, 1);
+
+ checkValues(expVals, cache);
+
+ cache.put(1, new MvccTestSqlIndexValue(2));
+ expVals.put(1, 2);
+
+ checkValues(expVals, cache);
+
+ cache.put(2, new MvccTestSqlIndexValue(1));
+ expVals.put(2, 1);
+ cache.put(3, new MvccTestSqlIndexValue(1));
+ expVals.put(3, 1);
+ cache.put(4, new MvccTestSqlIndexValue(1));
+ expVals.put(4, 1);
+
+ checkValues(expVals, cache);
+
+ cache.remove(1);
+ expVals.remove(1);
+
+ checkValues(expVals, cache);
+
+ checkNoValue(1, cache);
+
+ cache.put(1, new MvccTestSqlIndexValue(10));
+ expVals.put(1, 10);
+
+ checkValues(expVals, cache);
+
+ checkActiveQueriesCleanup(srv0);
+
+ srv0.destroyCache(cache.getName());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlSimplePutRemoveRandom() throws Exception {
+ startGrid(0);
+
+ testSqlSimplePutRemoveRandom(0);
+
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ for (int i = 0; i < 3; i++)
+ testSqlSimplePutRemoveRandom(rnd.nextInt(2048));
+ }
+
+ /**
+ * @param inlineSize Inline size.
+ * @throws Exception If failed.
+ */
+ private void testSqlSimplePutRemoveRandom(int inlineSize) throws Exception {
+ Ignite srv0 = grid(0);
+
+ IgniteCache<Integer, MvccTestSqlIndexValue> cache = (IgniteCache) srv0.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT).
+ setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class).
+ setSqlIndexMaxInlineSize(inlineSize));
+
+ Map<Integer, Integer> expVals = new HashMap<>();
+
+ final int KEYS = 100;
+ final int VALS = 10;
+
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ long stopTime = System.currentTimeMillis() + 5_000;
+
+ for (int i = 0; i < 100_000; i++) {
+ Integer key = rnd.nextInt(KEYS);
+
+ if (rnd.nextInt(5) == 0) {
+ cache.remove(key);
+
+ expVals.remove(key);
+ }
+ else {
+ Integer val = rnd.nextInt(VALS);
+
+ cache.put(key, new MvccTestSqlIndexValue(val));
+
+ expVals.put(key, val);
+ }
+
+ checkValues(expVals, cache);
+
+ if (System.currentTimeMillis() > stopTime) {
+ info("Stop test, iteration: " + i);
+
+ break;
+ }
+ }
+
+ for (int i = 0; i < KEYS; i++) {
+ if (!expVals.containsKey(i))
+ checkNoValue(i, cache);
+ }
+
+ checkActiveQueriesCleanup(srv0);
+
+ srv0.destroyCache(cache.getName());
+ }
+
+ /**
+ * @param key Key.
+ * @param cache Cache.
+ */
+ private void checkNoValue(Object key, IgniteCache cache) {
+ SqlQuery<Integer, MvccTestSqlIndexValue> qry;
+
+ qry = new SqlQuery<>(MvccTestSqlIndexValue.class, "_key = ?");
+
+ qry.setArgs(key);
+
+ List<IgniteCache.Entry<Integer, MvccTestSqlIndexValue>> res = cache.query(qry).getAll();
+
+ assertTrue(res.isEmpty());
+ }
+
+ /**
+ * @param expVals Expected values.
+ * @param cache Cache.
+ */
+ private void checkValues(Map<Integer, Integer> expVals, IgniteCache<Integer, MvccTestSqlIndexValue> cache) {
+ SqlFieldsQuery cntQry = new SqlFieldsQuery("select count(*) from MvccTestSqlIndexValue");
+
+ Long cnt = (Long)cache.query(cntQry).getAll().get(0).get(0);
+
+ assertEquals((long)expVals.size(), (Object)cnt);
+
+ SqlQuery<Integer, MvccTestSqlIndexValue> qry;
+
+ qry = new SqlQuery<>(MvccTestSqlIndexValue.class, "true");
+
+ Map<Integer, Integer> vals = new HashMap<>();
+
+ for (IgniteCache.Entry<Integer, MvccTestSqlIndexValue> e : cache.query(qry).getAll())
+ assertNull(vals.put(e.getKey(), e.getValue().idxVal1));
+
+ assertEquals(expVals, vals);
+
+ qry = new SqlQuery<>(MvccTestSqlIndexValue.class, "_key >= 0");
+
+ vals = new HashMap<>();
+
+ for (IgniteCache.Entry<Integer, MvccTestSqlIndexValue> e : cache.query(qry).getAll())
+ assertNull(vals.put(e.getKey(), e.getValue().idxVal1));
+
+ assertEquals(expVals, vals);
+
+ qry = new SqlQuery<>(MvccTestSqlIndexValue.class, "idxVal1 >= 0");
+
+ vals = new HashMap<>();
+
+ for (IgniteCache.Entry<Integer, MvccTestSqlIndexValue> e : cache.query(qry).getAll())
+ assertNull(vals.put(e.getKey(), e.getValue().idxVal1));
+
+ assertEquals(expVals, vals);
+
+ Map<Integer, Set<Integer>> expIdxVals = new HashMap<>();
+
+ for (Map.Entry<Integer, Integer> e : expVals.entrySet()) {
+ qry = new SqlQuery<>(MvccTestSqlIndexValue.class, "_key = ?");
+
+ qry.setArgs(e.getKey());
+
+ List<IgniteCache.Entry<Integer, MvccTestSqlIndexValue>> res = cache.query(qry).getAll();
+
+ assertEquals(1, res.size());
+ assertEquals(e.getKey(), res.get(0).getKey());
+ assertEquals(e.getValue(), (Integer)res.get(0).getValue().idxVal1);
+
+ SqlFieldsQuery fieldsQry = new SqlFieldsQuery("select _key, idxVal1 from MvccTestSqlIndexValue where _key=?");
+ fieldsQry.setArgs(e.getKey());
+
+ List<List<?>> fieldsRes = cache.query(fieldsQry).getAll();
+
+ assertEquals(1, fieldsRes.size());
+ assertEquals(e.getKey(), fieldsRes.get(0).get(0));
+ assertEquals(e.getValue(), fieldsRes.get(0).get(1));
+
+ Integer val = e.getValue();
+
+ Set<Integer> keys = expIdxVals.get(val);
+
+ if (keys == null)
+ expIdxVals.put(val, keys = new HashSet<>());
+
+ assertTrue(keys.add(e.getKey()));
+ }
+
+ for (Map.Entry<Integer, Set<Integer>> expE : expIdxVals.entrySet()) {
+ qry = new SqlQuery<>(MvccTestSqlIndexValue.class, "idxVal1 = ?");
+ qry.setArgs(expE.getKey());
+
+ vals = new HashMap<>();
+
+ for (IgniteCache.Entry<Integer, MvccTestSqlIndexValue> e : cache.query(qry).getAll()) {
+ assertNull(vals.put(e.getKey(), e.getValue().idxVal1));
+
+ assertEquals(expE.getKey(), (Integer)e.getValue().idxVal1);
+
+ assertTrue(expE.getValue().contains(e.getKey()));
+ }
+
+ assertEquals(expE.getValue().size(), vals.size());
+ }
+ }
+
+ /**
+ *
+ */
+ static class JoinTestParentKey implements Serializable {
+ /** */
+ private int key;
+
+ /**
+ * @param key Key.
+ */
+ JoinTestParentKey(int key) {
+ this.key = key;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ JoinTestParentKey that = (JoinTestParentKey)o;
+
+ return key == that.key;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return key;
+ }
+ }
+
+ /**
+ *
+ */
+ static class JoinTestParent {
+ /** */
+ @QuerySqlField(index = true)
+ private int id;
+
+ /**
+ * @param id ID.
+ */
+ JoinTestParent(int id) {
+ this.id = id;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(JoinTestParent.class, this);
+ }
+ }
+
+ /**
+ *
+ */
+ static class JoinTestChildKey implements Serializable {
+ /** */
+ @QuerySqlField(index = true)
+ private int key;
+
+ /**
+ * @param key Key.
+ */
+ JoinTestChildKey(int key) {
+ this.key = key;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ JoinTestChildKey that = (JoinTestChildKey)o;
+
+ return key == that.key;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return key;
+ }
+ }
+
+ /**
+ *
+ */
+ static class JoinTestChild {
+ /** */
+ @QuerySqlField(index = true)
+ private int parentId;
+
+ /**
+ * @param parentId Parent ID.
+ */
+ JoinTestChild(int parentId) {
+ this.parentId = parentId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(JoinTestChild.class, this);
+ }
+ }
+
+ /**
+ *
+ */
+ static class MvccTestSqlIndexValue implements Serializable {
+ /** */
+ @QuerySqlField(index = true)
+ private int idxVal1;
+
+ /**
+ * @param idxVal1 Indexed value 1.
+ */
+ MvccTestSqlIndexValue(int idxVal1) {
+ this.idxVal1 = idxVal1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccTestSqlIndexValue.class, this);
+ }
+ }
+}
[33/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
index 516dfb9..357f3d7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
@@ -90,6 +90,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartit
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsStateValidator;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFutureAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.latch.Latch;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotDiscoveryMessage;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -97,6 +98,7 @@ import org.apache.ignite.internal.processors.cluster.BaselineTopology;
import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateFinishMessage;
import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateMessage;
import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -647,7 +649,17 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
boolean crdNode = crd != null && crd.isLocal();
- exchCtx = new ExchangeContext(crdNode, this);
+ MvccCoordinator mvccCrd = firstEvtDiscoCache.mvccCoordinator();
+
+ boolean mvccCrdChange = mvccCrd != null &&
+ (initialVersion().equals(mvccCrd.topologyVersion()) || activateCluster());
+
+ // Mvcc coordinator should has been initialized before exchange context is created.
+ cctx.kernalContext().coordinators().updateCoordinator(mvccCrd);
+
+ exchCtx = new ExchangeContext(crdNode, mvccCrdChange, this);
+
+ cctx.kernalContext().coordinators().onExchangeStart(mvccCrd, exchCtx, crd);
assert state == null : state;
@@ -658,6 +670,8 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
if (exchLog.isInfoEnabled()) {
exchLog.info("Started exchange init [topVer=" + topVer +
+ ", mvccCrd=" + mvccCrd +
+ ", mvccCrdChange=" + mvccCrdChange +
", crd=" + crdNode +
", evt=" + IgniteUtils.gridEventName(firstDiscoEvt.type()) +
", evtNode=" + firstDiscoEvt.eventNode().id() +
@@ -745,7 +759,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
}
}
- updateTopologies(crdNode);
+ updateTopologies(crd, crdNode, cctx.coordinators().currentCoordinator());
switch (exchange) {
case ALL: {
@@ -895,10 +909,12 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
/**
* Updates topology versions and discovery caches on all topologies.
*
+ * @param exchCrd Exchange coordinator node.
* @param crd Coordinator flag.
+ * @param mvccCrd Mvcc coordinator.
* @throws IgniteCheckedException If failed.
*/
- private void updateTopologies(boolean crd) throws IgniteCheckedException {
+ private void updateTopologies(ClusterNode exchCrd, boolean crd, MvccCoordinator mvccCrd) throws IgniteCheckedException {
for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
if (grp.isLocal())
continue;
@@ -925,12 +941,18 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
top.updateTopologyVersion(
this,
events().discoveryCache(),
+ mvccCrd,
updSeq,
cacheGroupStopping(grp.groupId()));
}
- for (GridClientPartitionTopology top : cctx.exchange().clientTopologies())
- top.updateTopologyVersion(this, events().discoveryCache(), -1, cacheGroupStopping(top.groupId()));
+ for (GridClientPartitionTopology top : cctx.exchange().clientTopologies()) {
+ top.updateTopologyVersion(this,
+ events().discoveryCache(),
+ mvccCrd,
+ -1,
+ cacheGroupStopping(top.groupId()));
+ }
}
/**
@@ -1570,6 +1592,12 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
msg.partitionHistoryCounters(partHistReserved0);
}
+ if (exchCtx.newMvccCoordinator() && cctx.coordinators().currentCoordinatorId().equals(node.id())) {
+ Map<UUID, GridLongList> activeQueries = exchCtx.activeQueries();
+
+ msg.activeQueries(activeQueries != null ? activeQueries.get(cctx.localNodeId()) : null);
+ }
+
if ((stateChangeExchange() || dynamicCacheStartExchange()) && exchangeLocE != null)
msg.setError(exchangeLocE);
else if (localJoinExchange())
@@ -1813,6 +1841,10 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
if (!cctx.localNode().isClient())
tryToPerformLocalSnapshotOperation();
+ if (err == null)
+ cctx.coordinators().onExchangeDone(exchCtx.newMvccCoordinator(), exchCtx.events().discoveryCache(),
+ exchCtx.activeQueries());
+
cctx.cache().onExchangeDone(initialVersion(), exchActions, err);
cctx.exchange().onExchangeDone(res, initialVersion(), err);
@@ -2319,6 +2351,9 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
*/
private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage msg) {
if (msg.client()) {
+ if (msg.activeQueries() != null)
+ cctx.coordinators().processClientActiveQueries(nodeId, msg.activeQueries());
+
waitAndReplyToNode(nodeId, msg);
return;
@@ -2856,6 +2891,9 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
for (Map.Entry<UUID, GridDhtPartitionsSingleMessage> e : msgs.entrySet()) {
GridDhtPartitionsSingleMessage msg = e.getValue();
+ if (exchCtx.newMvccCoordinator())
+ exchCtx.addActiveQueries(e.getKey(), msg.activeQueries());
+
// Apply update counters after all single messages are received.
for (Map.Entry<Integer, GridDhtPartitionMap> entry : msg.partitions().entrySet()) {
Integer grpId = entry.getKey();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
index 804cc03..77d7eab 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.GridDirectTransient;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.T2;
@@ -108,6 +109,9 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
*/
private GridDhtPartitionsFullMessage finishMsg;
+ /** */
+ private GridLongList activeQryTrackers;
+
/**
* Required by {@link Externalizable}.
*/
@@ -132,6 +136,20 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
}
/**
+ * @return Active queries started with previous coordinator.
+ */
+ GridLongList activeQueries() {
+ return activeQryTrackers;
+ }
+
+ /**
+ * @param activeQrys Active queries started with previous coordinator.
+ */
+ void activeQueries(GridLongList activeQrys) {
+ this.activeQryTrackers = activeQrys;
+ }
+
+ /**
* @param finishMsg Exchange finish message (used to restore exchange state on new coordinator).
*/
void finishMessage(GridDhtPartitionsFullMessage finishMsg) {
@@ -331,7 +349,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
byte[] partsBytes0 = null;
byte[] partCntrsBytes0 = null;
byte[] partHistCntrsBytes0 = null;
- byte[] partSizesBytes0 = null;
+ byte[] partsSizesBytes0 = null;
byte[] errBytes0 = null;
if (parts != null && partsBytes == null)
@@ -344,7 +362,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
partHistCntrsBytes0 = U.marshal(ctx, partHistCntrs);
if (partsSizes != null && partsSizesBytes == null)
- partSizesBytes0 = U.marshal(ctx, partsSizes);
+ partsSizesBytes0 = U.marshal(ctx, partsSizes);
if (err != null && errBytes == null)
errBytes0 = U.marshal(ctx, err);
@@ -356,13 +374,13 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
byte[] partsBytesZip = U.zip(partsBytes0);
byte[] partCntrsBytesZip = U.zip(partCntrsBytes0);
byte[] partHistCntrsBytesZip = U.zip(partHistCntrsBytes0);
- byte[] partSizesBytesZip = U.zip(partSizesBytes0);
+ byte[] partsSizesBytesZip = U.zip(partsSizesBytes0);
byte[] exBytesZip = U.zip(errBytes0);
partsBytes0 = partsBytesZip;
partCntrsBytes0 = partCntrsBytesZip;
partHistCntrsBytes0 = partHistCntrsBytesZip;
- partSizesBytes0 = partSizesBytesZip;
+ partsSizesBytes0 = partsSizesBytesZip;
errBytes0 = exBytesZip;
compressed(true);
@@ -375,7 +393,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
partsBytes = partsBytes0;
partCntrsBytes = partCntrsBytes0;
partHistCntrsBytes = partHistCntrsBytes0;
- partsSizesBytes = partSizesBytes0;
+ partsSizesBytes = partsSizesBytes0;
errBytes = errBytes0;
}
}
@@ -456,54 +474,60 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
switch (writer.state()) {
case 5:
- if (!writer.writeBoolean("client", client))
+ if (!writer.writeMessage("activeQryTrackers", activeQryTrackers))
return false;
writer.incrementState();
case 6:
- if (!writer.writeMap("dupPartsData", dupPartsData, MessageCollectionItemType.INT, MessageCollectionItemType.INT))
+ if (!writer.writeBoolean("client", client))
return false;
writer.incrementState();
case 7:
- if (!writer.writeByteArray("errBytes", errBytes))
+ if (!writer.writeMap("dupPartsData", dupPartsData, MessageCollectionItemType.INT, MessageCollectionItemType.INT))
return false;
writer.incrementState();
case 8:
- if (!writer.writeMessage("finishMsg", finishMsg))
+ if (!writer.writeByteArray("errBytes", errBytes))
return false;
writer.incrementState();
case 9:
- if (!writer.writeCollection("grpsAffRequest", grpsAffRequest, MessageCollectionItemType.INT))
+ if (!writer.writeMessage("finishMsg", finishMsg))
return false;
writer.incrementState();
case 10:
- if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes))
+ if (!writer.writeCollection("grpsAffRequest", grpsAffRequest, MessageCollectionItemType.INT))
return false;
writer.incrementState();
case 11:
- if (!writer.writeByteArray("partHistCntrsBytes", partHistCntrsBytes))
+ if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes))
return false;
writer.incrementState();
case 12:
- if (!writer.writeByteArray("partsBytes", partsBytes))
+ if (!writer.writeByteArray("partHistCntrsBytes", partHistCntrsBytes))
return false;
writer.incrementState();
case 13:
+ if (!writer.writeByteArray("partsBytes", partsBytes))
+ return false;
+
+ writer.incrementState();
+
+ case 14:
if (!writer.writeByteArray("partsSizesBytes", partsSizesBytes))
return false;
@@ -525,7 +549,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
switch (reader.state()) {
case 5:
- client = reader.readBoolean("client");
+ activeQryTrackers = reader.readMessage("activeQryTrackers");
if (!reader.isLastRead())
return false;
@@ -533,7 +557,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 6:
- dupPartsData = reader.readMap("dupPartsData", MessageCollectionItemType.INT, MessageCollectionItemType.INT, false);
+ client = reader.readBoolean("client");
if (!reader.isLastRead())
return false;
@@ -541,7 +565,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 7:
- errBytes = reader.readByteArray("errBytes");
+ dupPartsData = reader.readMap("dupPartsData", MessageCollectionItemType.INT, MessageCollectionItemType.INT, false);
if (!reader.isLastRead())
return false;
@@ -549,7 +573,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 8:
- finishMsg = reader.readMessage("finishMsg");
+ errBytes = reader.readByteArray("errBytes");
if (!reader.isLastRead())
return false;
@@ -557,7 +581,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 9:
- grpsAffRequest = reader.readCollection("grpsAffRequest", MessageCollectionItemType.INT);
+ finishMsg = reader.readMessage("finishMsg");
if (!reader.isLastRead())
return false;
@@ -565,7 +589,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 10:
- partCntrsBytes = reader.readByteArray("partCntrsBytes");
+ grpsAffRequest = reader.readCollection("grpsAffRequest", MessageCollectionItemType.INT);
if (!reader.isLastRead())
return false;
@@ -573,7 +597,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 11:
- partHistCntrsBytes = reader.readByteArray("partHistCntrsBytes");
+ partCntrsBytes = reader.readByteArray("partCntrsBytes");
if (!reader.isLastRead())
return false;
@@ -581,7 +605,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 12:
- partsBytes = reader.readByteArray("partsBytes");
+ partHistCntrsBytes = reader.readByteArray("partHistCntrsBytes");
if (!reader.isLastRead())
return false;
@@ -589,6 +613,14 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
reader.incrementState();
case 13:
+ partsBytes = reader.readByteArray("partsBytes");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 14:
partsSizesBytes = reader.readByteArray("partsSizesBytes");
if (!reader.isLastRead())
@@ -607,7 +639,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 14;
+ return 15;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/AckCoordinatorOnRollback.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/AckCoordinatorOnRollback.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/AckCoordinatorOnRollback.java
new file mode 100644
index 0000000..1648da9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/AckCoordinatorOnRollback.java
@@ -0,0 +1,54 @@
+/*
+ * 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.distributed.near;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.util.typedef.CIX1;
+
+/** */
+public class AckCoordinatorOnRollback extends CIX1<IgniteInternalFuture<IgniteInternalTx>> {
+ /** */
+ private static final long serialVersionUID = 8172699207968328284L;
+
+ /** */
+ private final GridNearTxLocal tx;
+
+ /**
+ * @param tx Transaction.
+ */
+ public AckCoordinatorOnRollback(GridNearTxLocal tx) {
+ this.tx = tx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void applyx(IgniteInternalFuture<IgniteInternalTx> fut) throws IgniteCheckedException {
+ assert fut.isDone();
+
+ MvccQueryTracker tracker = tx.mvccQueryTracker();
+ MvccSnapshot mvccSnapshot = tx.mvccSnapshot();
+
+ if (tracker != null) // Optimistic tx.
+ tracker.onDone(tx, false);
+ else if (mvccSnapshot != null)// Pessimistic tx.
+ tx.context().coordinators().ackTxRollback(mvccSnapshot);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
index ee06518..7cf5a6b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetFuture.java
@@ -319,7 +319,8 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
taskName == null ? 0 : taskName.hashCode(),
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ null); // TODO IGNITE-7371
final Collection<Integer> invalidParts = fut.invalidPartitions();
@@ -382,7 +383,8 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
true,
skipVals,
cctx.deploymentEnabled(),
- recovery);
+ recovery,
+ null); // TODO IGNITE-7371
add(fut); // Append new future.
@@ -455,7 +457,8 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
taskName,
expiryPlc,
!deserializeBinary,
- null);
+ null,
+ null); // TODO IGNITE-7371
if (res != null) {
v = res.value();
@@ -473,7 +476,8 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
null,
taskName,
expiryPlc,
- !deserializeBinary);
+ !deserializeBinary,
+ null); // TODO IGNITE-7371
}
}
@@ -599,7 +603,8 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
taskName,
expiryPlc,
!deserializeBinary,
- null);
+ null,
+ null); // TODO IGNITE-7371
if (res != null) {
v = res.value();
@@ -617,7 +622,8 @@ public final class GridNearGetFuture<K, V> extends CacheDistributedGetFutureAdap
null,
taskName,
expiryPlc,
- !deserializeBinary);
+ !deserializeBinary,
+ null); // TODO IGNITE-7371
}
// Entry was not in memory or in swap, so we remove it from cache.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
index dcb167d..c891bfb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
@@ -34,6 +34,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionable;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -45,6 +46,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemTy
import org.apache.ignite.plugin.extensions.communication.MessageReader;
import org.apache.ignite.plugin.extensions.communication.MessageWriter;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* Get request. Responsible for obtaining entry from primary node. 'Near' means 'Initiating node' here, not 'Near Cache'.
@@ -106,6 +108,9 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
/** TTL for read operation. */
private long accessTtl;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -128,6 +133,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
* @param createTtl New TTL to set after entry is created, -1 to leave unchanged.
* @param accessTtl New TTL to set after entry is accessed, -1 to leave unchanged.
* @param addDepInfo Deployment info.
+ * @param mvccSnapshot Mvcc snapshot.
*/
public GridNearGetRequest(
int cacheId,
@@ -144,7 +150,8 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
boolean addReader,
boolean skipVals,
boolean addDepInfo,
- boolean recovery
+ boolean recovery,
+ @Nullable MvccSnapshot mvccSnapshot
) {
assert futId != null;
assert miniId != null;
@@ -173,6 +180,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
this.createTtl = createTtl;
this.accessTtl = accessTtl;
this.addDepInfo = addDepInfo;
+ this.mvccSnapshot = mvccSnapshot;
if (readThrough)
flags |= READ_THROUGH_FLAG_MASK;
@@ -188,6 +196,13 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
}
/**
+ * @return Mvcc version.
+ */
+ @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
* @return Future ID.
*/
public IgniteUuid futureId() {
@@ -382,30 +397,36 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
writer.incrementState();
case 9:
- if (!writer.writeCollection("readersFlags", readersFlags, MessageCollectionItemType.BOOLEAN))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 10:
- if (!writer.writeUuid("subjId", subjId))
+ if (!writer.writeCollection("readersFlags", readersFlags, MessageCollectionItemType.BOOLEAN))
return false;
writer.incrementState();
case 11:
- if (!writer.writeInt("taskNameHash", taskNameHash))
+ if (!writer.writeUuid("subjId", subjId))
return false;
writer.incrementState();
case 12:
- if (!writer.writeMessage("topVer", topVer))
+ if (!writer.writeInt("taskNameHash", taskNameHash))
return false;
writer.incrementState();
case 13:
+ if (!writer.writeMessage("topVer", topVer))
+ return false;
+
+ writer.incrementState();
+
+ case 14:
if (!writer.writeMessage("ver", ver))
return false;
@@ -476,7 +497,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
reader.incrementState();
case 9:
- readersFlags = reader.readCollection("readersFlags", MessageCollectionItemType.BOOLEAN);
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -484,7 +505,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
reader.incrementState();
case 10:
- subjId = reader.readUuid("subjId");
+ readersFlags = reader.readCollection("readersFlags", MessageCollectionItemType.BOOLEAN);
if (!reader.isLastRead())
return false;
@@ -492,7 +513,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
reader.incrementState();
case 11:
- taskNameHash = reader.readInt("taskNameHash");
+ subjId = reader.readUuid("subjId");
if (!reader.isLastRead())
return false;
@@ -500,7 +521,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
reader.incrementState();
case 12:
- topVer = reader.readMessage("topVer");
+ taskNameHash = reader.readInt("taskNameHash");
if (!reader.isLastRead())
return false;
@@ -508,6 +529,14 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
reader.incrementState();
case 13:
+ topVer = reader.readMessage("topVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 14:
ver = reader.readMessage("ver");
if (!reader.isLastRead())
@@ -527,7 +556,7 @@ public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheD
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 14;
+ return 15;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticSerializableTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticSerializableTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticSerializableTxPrepareFuture.java
index a997c45..140c1d5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticSerializableTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticSerializableTxPrepareFuture.java
@@ -38,6 +38,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxMapping;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -50,7 +51,6 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.C1;
import org.apache.ignite.internal.util.typedef.CI1;
import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.P1;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -59,6 +59,7 @@ import org.apache.ignite.lang.IgniteReducer;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.noCoordinatorError;
import static org.apache.ignite.transactions.TransactionState.PREPARED;
import static org.apache.ignite.transactions.TransactionState.PREPARING;
@@ -184,10 +185,20 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
tx.removeMapping(m.primary().id());
}
+ prepareError(e);
+ }
+
+ /**
+ * @param e Error.
+ */
+ private void prepareError(Throwable e) {
ERR_UPD.compareAndSet(this, null, e);
if (keyLockFut != null)
keyLockFut.onDone(e);
+
+ if (mvccVerFut != null)
+ mvccVerFut.onDone();
}
/** {@inheritDoc} */
@@ -229,7 +240,7 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
// Avoid iterator creation.
for (int i = 0; i < size; i++) {
- IgniteInternalFuture<GridNearTxPrepareResponse> fut = future(i);
+ IgniteInternalFuture fut = future(i);
if (!isMini(fut))
continue;
@@ -338,11 +349,25 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
boolean hasNearCache = false;
+ MvccCoordinator mvccCrd = null;
+
for (IgniteTxEntry write : writes) {
map(write, topVer, mappings, txMapping, remap, topLocked);
- if (write.context().isNear())
+ GridCacheContext cctx = write.context();
+
+ if (cctx.isNear())
hasNearCache = true;
+
+ if (cctx.mvccEnabled() && mvccCrd == null) {
+ mvccCrd = cctx.affinity().mvccCoordinator(topVer);
+
+ if (mvccCrd == null) {
+ onDone(noCoordinatorError(topVer));
+
+ return;
+ }
+ }
}
for (IgniteTxEntry read : reads)
@@ -358,6 +383,8 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
return;
}
+ assert !tx.txState().mvccEnabled(cctx) || mvccCrd != null || F.isEmpty(writes);
+
tx.addEntryMapping(mappings.values());
cctx.mvcc().recheckPendingLocks();
@@ -369,23 +396,32 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
MiniFuture locNearEntriesFut = null;
+ int lockCnt = keyLockFut != null ? 1 : 0;
+
// Create futures in advance to have all futures when process {@link GridNearTxPrepareResponse#clientRemapVersion}.
for (GridDistributedTxMapping m : mappings.values()) {
assert !m.empty();
MiniFuture fut = new MiniFuture(this, m, ++miniId);
- add(fut);
+ lockCnt++;
+
+ add((IgniteInternalFuture)fut);
if (m.primary().isLocal() && m.hasNearCacheEntries() && m.hasColocatedCacheEntries()) {
assert locNearEntriesFut == null;
locNearEntriesFut = fut;
- add(new MiniFuture(this, m, ++miniId));
+ add((IgniteInternalFuture)new MiniFuture(this, m, ++miniId));
+
+ lockCnt++;
}
}
+ if (mvccCrd != null)
+ initMvccVersionFuture(lockCnt, remap);
+
Collection<IgniteInternalFuture<?>> futs = (Collection)futures();
Iterator<IgniteInternalFuture<?>> it = futs.iterator();
@@ -639,7 +675,7 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
if (keyLockFut == null) {
keyLockFut = new KeyLockFuture();
- add(keyLockFut);
+ add((IgniteInternalFuture)keyLockFut);
}
keyLockFut.addLockKey(entry.txKey());
@@ -696,20 +732,20 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
Collection<String> futs = F.viewReadOnly(futures(),
new C1<IgniteInternalFuture<?>, String>() {
@Override public String apply(IgniteInternalFuture<?> f) {
- return "[node=" + ((MiniFuture)f).primary().id() +
- ", loc=" + ((MiniFuture)f).primary().isLocal() +
- ", done=" + f.isDone() + "]";
- }
- },
- new P1<IgniteInternalFuture<?>>() {
- @Override public boolean apply(IgniteInternalFuture<?> f) {
- return isMini(f);
+ if (isMini(f)) {
+ return "[node=" + ((MiniFuture)f).primary().id() +
+ ", loc=" + ((MiniFuture)f).primary().isLocal() +
+ ", done=" + f.isDone() +
+ ", err=" + f.error() + "]";
+ }
+ else
+ return f.toString();
}
});
return S.toString(GridNearOptimisticSerializableTxPrepareFuture.class, this,
"innerFuts", futs,
- "keyLockFut", keyLockFut,
+ "remap", remapFut != null,
"tx", tx,
"super", super.toString());
}
@@ -760,7 +796,7 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
*/
private static class MiniFuture extends GridFutureAdapter<GridNearTxPrepareResponse> {
/** Receive result flag updater. */
- private static AtomicIntegerFieldUpdater<MiniFuture> RCV_RES_UPD =
+ private static final AtomicIntegerFieldUpdater<MiniFuture> RCV_RES_UPD =
AtomicIntegerFieldUpdater.newUpdater(MiniFuture.class, "rcvRes");
/** */
@@ -924,7 +960,7 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
err0.retryReadyFuture(affFut);
- ERR_UPD.compareAndSet(parent, null, err0);
+ parent.prepareError(err0);
onDone(err0);
}
@@ -935,7 +971,7 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
parent);
}
- ERR_UPD.compareAndSet(parent, null, e);
+ parent.prepareError(e);
onDone(e);
}
@@ -950,6 +986,9 @@ public class GridNearOptimisticSerializableTxPrepareFuture extends GridNearOptim
// Finish this mini future (need result only on client node).
onDone(parent.cctx.kernalContext().clientNode() ? res : null);
+
+ if (parent.mvccVerFut != null)
+ parent.mvccVerFut.onLockReceived();
}
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
index 566789c..06d7a8c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
@@ -43,6 +43,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxMapping;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
@@ -68,6 +69,7 @@ import org.apache.ignite.transactions.TransactionTimeoutException;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.noCoordinatorError;
import static org.apache.ignite.transactions.TransactionState.PREPARED;
import static org.apache.ignite.transactions.TransactionState.PREPARING;
@@ -220,7 +222,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
int size = futuresCountNoLock();
for (int i = 0; i < size; i++) {
- IgniteInternalFuture<GridNearTxPrepareResponse> fut = future(i);
+ IgniteInternalFuture fut = future(i);
if (isMini(fut) && !fut.isDone()) {
MiniFuture miniFut = (MiniFuture)fut;
@@ -254,7 +256,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
// Avoid iterator creation.
for (int i = size - 1; i >= 0; i--) {
- IgniteInternalFuture<GridNearTxPrepareResponse> fut = future(i);
+ IgniteInternalFuture fut = future(i);
if (!isMini(fut))
continue;
@@ -380,6 +382,18 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
tx.colocatedLocallyMapped(true);
}
+ if (write.context().mvccEnabled()) {
+ MvccCoordinator mvccCrd = write.context().affinity().mvccCoordinator(topVer);
+
+ if (mvccCrd == null) {
+ onDone(noCoordinatorError(topVer));
+
+ return;
+ }
+
+ initMvccVersionFuture(keyLockFut != null ? 2 : 1, remap);
+ }
+
if (keyLockFut != null)
keyLockFut.onAllKeysAdded();
@@ -424,6 +438,8 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
boolean hasNearCache = false;
+ MvccCoordinator mvccCrd = null;
+
for (IgniteTxEntry write : writes) {
write.clearEntryReadVersion();
@@ -433,6 +449,16 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
// an exception occurred while transaction mapping, stop further processing
break;
+ if (write.context().mvccEnabled() && mvccCrd == null) {
+ mvccCrd = write.context().affinity().mvccCoordinator(topVer);
+
+ if (mvccCrd == null) {
+ onDone(noCoordinatorError(topVer));
+
+ break;
+ }
+ }
+
if (write.context().isNear())
hasNearCache = true;
@@ -472,6 +498,11 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
return;
}
+ assert !tx.txState().mvccEnabled(cctx) || mvccCrd != null;
+
+ if (mvccCrd != null)
+ initMvccVersionFuture(keyLockFut != null ? 2 : 1, remap);
+
if (keyLockFut != null)
keyLockFut.onAllKeysAdded();
@@ -495,8 +526,12 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
private void proceedPrepare(final Queue<GridDistributedTxMapping> mappings) {
final GridDistributedTxMapping m = mappings.poll();
- if (m == null)
+ if (m == null) {
+ if (mvccVerFut != null)
+ mvccVerFut.onLockReceived();
+
return;
+ }
proceedPrepare(m, mappings);
}
@@ -562,7 +597,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
req.miniId(fut.futureId());
- add(fut); // Append new future.
+ add((IgniteInternalFuture)fut); // Append new future.
if (n.isLocal()) {
assert !(m.hasColocatedCacheEntries() && m.hasNearCacheEntries()) : m;
@@ -679,7 +714,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
if (keyLockFut == null) {
keyLockFut = new KeyLockFuture();
- add(keyLockFut);
+ add((IgniteInternalFuture)keyLockFut);
}
keyLockFut.addLockKey(entry.txKey());
@@ -738,7 +773,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
int size = futuresCountNoLock();
for (int i = 0; i < size; i++) {
- IgniteInternalFuture<GridNearTxPrepareResponse> fut = future(i);
+ IgniteInternalFuture fut = future(i);
if (isMini(fut) && !fut.isDone()) {
MiniFuture miniFut = (MiniFuture)fut;
@@ -756,7 +791,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
}
}
- add(new GridEmbeddedFuture<>(new IgniteBiClosure<TxDeadlock, Exception, GridNearTxPrepareResponse>() {
+ add(new GridEmbeddedFuture<>(new IgniteBiClosure<TxDeadlock, Exception, Object>() {
@Override public GridNearTxPrepareResponse apply(TxDeadlock deadlock, Exception e) {
if (e != null)
U.warn(log, "Failed to detect deadlock.", e);
@@ -787,7 +822,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
/** {@inheritDoc} */
@Override public void addDiagnosticRequest(IgniteDiagnosticPrepareContext ctx) {
if (!isDone()) {
- for (IgniteInternalFuture<GridNearTxPrepareResponse> fut : futures()) {
+ for (IgniteInternalFuture fut : futures()) {
if (!fut.isDone()) {
if (fut instanceof MiniFuture) {
MiniFuture miniFut = (MiniFuture)fut;
@@ -843,19 +878,22 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
@Override public String toString() {
Collection<String> futs = F.viewReadOnly(futures(), new C1<IgniteInternalFuture<?>, String>() {
@Override public String apply(IgniteInternalFuture<?> f) {
- return "[node=" + ((MiniFuture)f).node().id() +
- ", loc=" + ((MiniFuture)f).node().isLocal() +
- ", done=" + f.isDone() + "]";
+ if (isMini(f)) {
+ return "[node=" + ((MiniFuture)f).node().id() +
+ ", loc=" + ((MiniFuture)f).node().isLocal() +
+ ", done=" + f.isDone() + "]";
+ }
+ else
+ return f.toString();
}
- }, new P1<IgniteInternalFuture<GridNearTxPrepareResponse>>() {
- @Override public boolean apply(IgniteInternalFuture<GridNearTxPrepareResponse> fut) {
+ }, new P1<IgniteInternalFuture<Object>>() {
+ @Override public boolean apply(IgniteInternalFuture<Object> fut) {
return isMini(fut);
}
});
return S.toString(GridNearOptimisticTxPrepareFuture.class, this,
"innerFuts", futs,
- "keyLockFut", keyLockFut,
"tx", tx,
"super", super.toString());
}
@@ -1001,6 +1039,8 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
// Proceed prepare before finishing mini future.
if (mappings != null)
parent.proceedPrepare(mappings);
+ else if (parent.mvccVerFut != null)
+ parent.mvccVerFut.onLockReceived();
// Finish this mini future.
onDone((GridNearTxPrepareResponse)null);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
index 45f8941..6f541d3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
@@ -18,19 +18,20 @@
package org.apache.ignite.internal.processors.cache.distributed.near;
import java.util.Collection;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotResponseListener;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
-import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
import org.apache.ignite.internal.util.GridConcurrentHashSet;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
-import org.apache.ignite.internal.util.lang.GridAbsClosureX;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -42,9 +43,20 @@ import org.jetbrains.annotations.Nullable;
*/
public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearTxPrepareFutureAdapter {
/** */
+ private static final long serialVersionUID = 7460376140787916619L;
+
+ /** */
+ private static final AtomicIntegerFieldUpdater<MvccSnapshotFutureExt> LOCK_CNT_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(MvccSnapshotFutureExt.class, "lockCnt");
+
+ /** */
@GridToStringExclude
protected KeyLockFuture keyLockFut;
+ /** */
+ @GridToStringExclude
+ protected MvccSnapshotFutureExt mvccVerFut;
+
/**
* @param cctx Context.
* @param tx Transaction.
@@ -73,7 +85,7 @@ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearT
}
if (keyLockFut != null)
- add(keyLockFut);
+ add((IgniteInternalFuture)keyLockFut);
}
}
@@ -202,6 +214,29 @@ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearT
protected abstract void prepare0(boolean remap, boolean topLocked);
/**
+ * @param lockCnt Expected number of lock responses.
+ * @param remap Remap flag.
+ */
+ @SuppressWarnings("unchecked")
+ final void initMvccVersionFuture(int lockCnt, boolean remap) {
+ if (!remap) {
+ mvccVerFut = new MvccSnapshotFutureExt();
+
+ mvccVerFut.init(lockCnt);
+
+ if (keyLockFut != null)
+ keyLockFut.listen(mvccVerFut);
+
+ add((IgniteInternalFuture)mvccVerFut);
+ }
+ else {
+ assert mvccVerFut != null;
+
+ mvccVerFut.init(lockCnt);
+ }
+ }
+
+ /**
* @param e Exception.
* @param timedOut {@code True} if timed out.
*/
@@ -223,7 +258,7 @@ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearT
/**
* Keys lock future.
*/
- protected static class KeyLockFuture extends GridFutureAdapter<GridNearTxPrepareResponse> {
+ protected static class KeyLockFuture extends GridFutureAdapter<Void> {
/** */
@GridToStringInclude
protected Collection<IgniteTxKey> lockKeys = new GridConcurrentHashSet<>();
@@ -258,24 +293,20 @@ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearT
checkLocks();
}
- /**
- * @return {@code True} if all locks are owned.
- */
- private boolean checkLocks() {
+ /** */
+ private void checkLocks() {
boolean locked = lockKeys.isEmpty();
if (locked && allKeysAdded) {
if (log.isDebugEnabled())
log.debug("All locks are acquired for near prepare future: " + this);
- onDone((GridNearTxPrepareResponse)null);
+ onDone((Void)null);
}
else {
if (log.isDebugEnabled())
log.debug("Still waiting for locks [fut=" + this + ", keys=" + lockKeys + ']');
}
-
- return locked;
}
/** {@inheritDoc} */
@@ -283,4 +314,82 @@ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearT
return S.toString(KeyLockFuture.class, this, super.toString());
}
}
+
+ /**
+ *
+ */
+ class MvccSnapshotFutureExt extends GridFutureAdapter<Void> implements MvccSnapshotResponseListener, IgniteInClosure<IgniteInternalFuture<Void>> {
+ /** */
+ private static final long serialVersionUID = 5883078648683911226L;
+
+ /** */
+ volatile int lockCnt;
+
+ /** {@inheritDoc} */
+ @Override public void apply(IgniteInternalFuture<Void> keyLockFut) {
+ try {
+ keyLockFut.get();
+
+ onLockReceived();
+ }
+ catch (IgniteCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("MvccSnapshotFutureExt ignores key lock future failure: " + e);
+ }
+ }
+
+ /**
+ * @param lockCnt Expected number of lock responses.
+ */
+ void init(int lockCnt) {
+ assert lockCnt > 0;
+
+ this.lockCnt = lockCnt;
+
+ assert !isDone();
+ }
+
+ /** */
+ void onLockReceived() {
+ int remaining = LOCK_CNT_UPD.decrementAndGet(this);
+
+ assert remaining >= 0 : remaining;
+
+ if (remaining == 0) {
+ try {
+ MvccSnapshot snapshot = cctx.coordinators().tryRequestSnapshotLocal(tx);
+
+ if (snapshot != null)
+ onResponse(snapshot);
+ else
+ cctx.coordinators().requestSnapshotAsync(tx, this);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ onError(e);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onResponse(MvccSnapshot res) {
+ tx.mvccSnapshot(res);
+
+ onDone();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onError(IgniteCheckedException e) {
+ if (e instanceof ClusterTopologyCheckedException)
+ ((ClusterTopologyCheckedException)e).retryReadyFuture(cctx.nextAffinityReadyFuture(tx.topologyVersion()));
+
+ ERR_UPD.compareAndSet(GridNearOptimisticTxPrepareFutureAdapter.this, null, e);
+
+ onDone();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSnapshotFutureExt.class, this, super.toString());
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearPessimisticTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearPessimisticTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearPessimisticTxPrepareFuture.java
index ffde0f3..dbf54c2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearPessimisticTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearPessimisticTxPrepareFuture.java
@@ -36,6 +36,9 @@ import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxMapping;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotResponseListener;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
@@ -43,11 +46,13 @@ import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.C1;
import org.apache.ignite.internal.util.typedef.CI1;
import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.noCoordinatorError;
import static org.apache.ignite.transactions.TransactionState.PREPARED;
import static org.apache.ignite.transactions.TransactionState.PREPARING;
@@ -55,6 +60,9 @@ import static org.apache.ignite.transactions.TransactionState.PREPARING;
*
*/
public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureAdapter {
+ /** */
+ private static final long serialVersionUID = 4014479758215810181L;
+
/**
* @param cctx Context.
* @param tx Transaction.
@@ -80,17 +88,19 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
boolean found = false;
for (IgniteInternalFuture<?> fut : futures()) {
- MiniFuture f = (MiniFuture)fut;
+ if (fut instanceof MiniFuture) {
+ MiniFuture f = (MiniFuture)fut;
- if (f.primary().id().equals(nodeId)) {
- ClusterTopologyCheckedException e = new ClusterTopologyCheckedException("Remote node left grid: " +
- nodeId);
+ if (f.primary().id().equals(nodeId)) {
+ ClusterTopologyCheckedException e = new ClusterTopologyCheckedException("Remote node left grid: " +
+ nodeId);
- e.retryReadyFuture(cctx.nextAffinityReadyFuture(tx.topologyVersion()));
+ e.retryReadyFuture(cctx.nextAffinityReadyFuture(tx.topologyVersion()));
- f.onNodeLeft(e);
+ f.onNodeLeft(e);
- found = true;
+ found = true;
+ }
}
}
@@ -142,13 +152,17 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
// Avoid iterator creation.
for (int i = 0; i < size; i++) {
- MiniFuture mini = (MiniFuture)future(i);
+ IgniteInternalFuture fut = future(i);
- if (mini.futureId() == miniId) {
- if (!mini.isDone())
- return mini;
- else
- return null;
+ if (fut instanceof MiniFuture) {
+ MiniFuture mini = (MiniFuture)fut;
+
+ if (mini.futureId() == miniId) {
+ if (!mini.isDone())
+ return mini;
+ else
+ return null;
+ }
}
}
}
@@ -217,6 +231,8 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
true,
tx.activeCachesDeploymentEnabled());
+ req.queryUpdate(m.queryUpdate());
+
for (IgniteTxEntry txEntry : writes) {
if (txEntry.op() == TRANSFORM)
req.addDhtVersion(txEntry.txKey(), null);
@@ -231,6 +247,7 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
* @param miniId Mini future ID.
* @param nearEntries {@code True} if prepare near cache entries.
*/
+ @SuppressWarnings("unchecked")
private void prepareLocal(GridNearTxPrepareRequest req,
GridDistributedTxMapping m,
int miniId,
@@ -239,7 +256,7 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
req.miniId(fut.futureId());
- add(fut);
+ add((IgniteInternalFuture)fut);
IgniteInternalFuture<GridNearTxPrepareResponse> prepFut = nearEntries ?
cctx.tm().txHandler().prepareNearTxLocal(req) :
@@ -260,6 +277,7 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
/**
*
*/
+ @SuppressWarnings("unchecked")
private void preparePessimistic() {
Map<UUID, GridDistributedTxMapping> mappings = new HashMap<>();
@@ -267,48 +285,77 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
GridDhtTxMapping txMapping = new GridDhtTxMapping();
+ boolean queryMapped = false;
+
+ for (GridDistributedTxMapping m : F.view(tx.mappings().mappings(), CU.FILTER_QUERY_MAPPING)) {
+ GridDistributedTxMapping nodeMapping = mappings.get(m.primary().id());
+
+ if(nodeMapping == null)
+ mappings.put(m.primary().id(), m);
+
+ txMapping.addMapping(F.asList(m.primary()));
+
+ queryMapped = true;
+ }
+
+ MvccCoordinator mvccCrd = null;
+
boolean hasNearCache = false;
- for (IgniteTxEntry txEntry : tx.allEntries()) {
- txEntry.clearEntryReadVersion();
+ if (!queryMapped) {
+ for (IgniteTxEntry txEntry : tx.allEntries()) {
+ txEntry.clearEntryReadVersion();
- GridCacheContext cacheCtx = txEntry.context();
+ GridCacheContext cacheCtx = txEntry.context();
- if (cacheCtx.isNear())
- hasNearCache = true;
+ if (cacheCtx.isNear())
+ hasNearCache = true;
- List<ClusterNode> nodes;
+ List<ClusterNode> nodes;
- if (!cacheCtx.isLocal()) {
- GridDhtPartitionTopology top = cacheCtx.topology();
+ if (!cacheCtx.isLocal()) {
+ GridDhtPartitionTopology top = cacheCtx.topology();
- nodes = top.nodes(cacheCtx.affinity().partition(txEntry.key()), topVer);
- }
- else
- nodes = cacheCtx.affinity().nodesByKey(txEntry.key(), topVer);
+ nodes = top.nodes(cacheCtx.affinity().partition(txEntry.key()), topVer);
+ }
+ else
+ nodes = cacheCtx.affinity().nodesByKey(txEntry.key(), topVer);
- if (F.isEmpty(nodes)) {
- onDone(new ClusterTopologyServerNotFoundException("Failed to map keys to nodes (partition " +
- "is not mapped to any node) [key=" + txEntry.key() +
- ", partition=" + cacheCtx.affinity().partition(txEntry.key()) + ", topVer=" + topVer + ']'));
+ if (tx.mvccSnapshot() == null && mvccCrd == null && cacheCtx.mvccEnabled()) {
+ mvccCrd = cacheCtx.affinity().mvccCoordinator(topVer);
- return;
- }
+ if (mvccCrd == null) {
+ onDone(noCoordinatorError(topVer));
+
+ return;
+ }
+ }
+
+ if (F.isEmpty(nodes)) {
+ onDone(new ClusterTopologyServerNotFoundException("Failed to map keys to nodes (partition " +
+ "is not mapped to any node) [key=" + txEntry.key() +
+ ", partition=" + cacheCtx.affinity().partition(txEntry.key()) + ", topVer=" + topVer + ']'));
+
+ return;
+ }
- ClusterNode primary = nodes.get(0);
+ ClusterNode primary = nodes.get(0);
- GridDistributedTxMapping nodeMapping = mappings.get(primary.id());
+ GridDistributedTxMapping nodeMapping = mappings.get(primary.id());
- if (nodeMapping == null)
- mappings.put(primary.id(), nodeMapping = new GridDistributedTxMapping(primary));
+ if (nodeMapping == null)
+ mappings.put(primary.id(), nodeMapping = new GridDistributedTxMapping(primary));
- txEntry.nodeId(primary.id());
+ txEntry.nodeId(primary.id());
- nodeMapping.add(txEntry);
+ nodeMapping.add(txEntry);
- txMapping.addMapping(nodes);
+ txMapping.addMapping(nodes);
+ }
}
+ assert !tx.txState().mvccEnabled(cctx) || tx.mvccSnapshot() != null || mvccCrd != null;
+
tx.transactionNodes(txMapping.transactionNodes());
if (!hasNearCache)
@@ -329,6 +376,16 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
for (final GridDistributedTxMapping m : mappings.values()) {
final ClusterNode primary = m.primary();
+ boolean needCntr = false;
+
+ if (mvccCrd != null) {
+ if (tx.onePhaseCommit() || mvccCrd.nodeId().equals(primary.id())) {
+ needCntr = true;
+
+ mvccCrd = null;
+ }
+ }
+
if (primary.isLocal()) {
if (m.hasNearCacheEntries() && m.hasColocatedCacheEntries()) {
GridNearTxPrepareRequest nearReq = createRequest(txMapping.transactionNodes(),
@@ -337,6 +394,8 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
m.nearEntriesReads(),
m.nearEntriesWrites());
+ nearReq.requestMvccCounter(needCntr);
+
prepareLocal(nearReq, m, ++miniId, true);
GridNearTxPrepareRequest colocatedReq = createRequest(txNodes,
@@ -350,6 +409,8 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
else {
GridNearTxPrepareRequest req = createRequest(txNodes, m, timeout, m.reads(), m.writes());
+ req.requestMvccCounter(needCntr);
+
prepareLocal(req, m, ++miniId, m.hasNearCacheEntries());
}
}
@@ -360,11 +421,13 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
m.reads(),
m.writes());
+ req.requestMvccCounter(needCntr);
+
final MiniFuture fut = new MiniFuture(m, ++miniId);
req.miniId(fut.futureId());
- add(fut);
+ add((IgniteInternalFuture)fut);
try {
cctx.io().send(primary, req, tx.ioPolicy());
@@ -392,6 +455,16 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
}
}
+ if (mvccCrd != null) {
+ assert !tx.onePhaseCommit();
+
+ MvccSnapshotFutureExt fut = new MvccSnapshotFutureExt();
+
+ cctx.coordinators().requestSnapshotAsync(tx, fut);
+
+ add((IgniteInternalFuture)fut);
+ }
+
markInitialized();
}
@@ -423,20 +496,52 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
@Override public String toString() {
Collection<String> futs = F.viewReadOnly(futures(), new C1<IgniteInternalFuture<?>, String>() {
@Override public String apply(IgniteInternalFuture<?> f) {
- return "[node=" + ((MiniFuture)f).primary().id() +
- ", loc=" + ((MiniFuture)f).primary().isLocal() +
- ", done=" + f.isDone() + "]";
+ if (f instanceof MiniFuture) {
+ return "[node=" + ((MiniFuture)f).primary().id() +
+ ", loc=" + ((MiniFuture)f).primary().isLocal() +
+ ", done=" + f.isDone() + "]";
+ }
+ else
+ return f.toString();
}
});
return S.toString(GridNearPessimisticTxPrepareFuture.class, this,
"innerFuts", futs,
+ "txId", tx.nearXidVersion(),
"super", super.toString());
}
/**
*
*/
+ private class MvccSnapshotFutureExt extends GridFutureAdapter<Void> implements MvccSnapshotResponseListener {
+ /** {@inheritDoc} */
+ @Override public void onResponse(MvccSnapshot res) {
+ tx.mvccSnapshot(res);
+
+ onDone();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onError(IgniteCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Error on tx prepare [fut=" + this + ", err=" + e + ", tx=" + tx + ']');
+
+ if (ERR_UPD.compareAndSet(GridNearPessimisticTxPrepareFuture.this, null, e))
+ tx.setRollbackOnly();
+
+ onDone(e);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSnapshotFutureExt.class, this, super.toString());
+ }
+ }
+
+
+ /** */
private class MiniFuture extends GridFutureAdapter<GridNearTxPrepareResponse> {
/** */
private final int futId;
@@ -475,6 +580,9 @@ public class GridNearPessimisticTxPrepareFuture extends GridNearTxPrepareFutureA
if (res.error() != null)
onError(res.error());
else {
+ if (res.mvccSnapshot() != null)
+ tx.mvccSnapshot(res.mvccSnapshot());
+
onPrepareResponse(m, res, updateMapping);
onDone(res);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
index 00ff4bb..d1aa2e9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
@@ -26,11 +26,13 @@ import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.plugin.extensions.communication.MessageReader;
import org.apache.ignite.plugin.extensions.communication.MessageWriter;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
*
@@ -81,6 +83,9 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
/** TTL for read operation. */
private long accessTtl;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/**
* Empty constructor required for {@link Message}.
*/
@@ -103,6 +108,7 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
* @param addReader Add reader flag.
* @param needVer {@code True} if entry version is needed.
* @param addDepInfo Deployment info.
+ * @param mvccSnapshot MVCC snapshot.
*/
public GridNearSingleGetRequest(
int cacheId,
@@ -118,7 +124,8 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
boolean addReader,
boolean needVer,
boolean addDepInfo,
- boolean recovery
+ boolean recovery,
+ MvccSnapshot mvccSnapshot
) {
assert key != null;
@@ -131,6 +138,7 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
this.createTtl = createTtl;
this.accessTtl = accessTtl;
this.addDepInfo = addDepInfo;
+ this.mvccSnapshot = mvccSnapshot;
if (readThrough)
flags |= READ_THROUGH_FLAG_MASK;
@@ -149,6 +157,13 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
}
/**
+ * @return Mvcc version.
+ */
+ @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
* @return Key.
*/
public KeyCacheObject key() {
@@ -322,7 +337,7 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
reader.incrementState();
case 8:
- subjId = reader.readUuid("subjId");
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -330,7 +345,7 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
reader.incrementState();
case 9:
- taskNameHash = reader.readInt("taskNameHash");
+ subjId = reader.readUuid("subjId");
if (!reader.isLastRead())
return false;
@@ -338,6 +353,14 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
reader.incrementState();
case 10:
+ taskNameHash = reader.readInt("taskNameHash");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 11:
topVer = reader.readMessage("topVer");
if (!reader.isLastRead())
@@ -396,18 +419,24 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
writer.incrementState();
case 8:
- if (!writer.writeUuid("subjId", subjId))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 9:
- if (!writer.writeInt("taskNameHash", taskNameHash))
+ if (!writer.writeUuid("subjId", subjId))
return false;
writer.incrementState();
case 10:
+ if (!writer.writeInt("taskNameHash", taskNameHash))
+ return false;
+
+ writer.incrementState();
+
+ case 11:
if (!writer.writeMessage("topVer", topVer))
return false;
@@ -430,7 +459,7 @@ public class GridNearSingleGetRequest extends GridCacheIdMessage implements Grid
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 11;
+ return 12;
}
/** {@inheritDoc} */
[36/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
index c1293fc..dd6ea48 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
@@ -50,6 +50,8 @@ import org.apache.ignite.internal.processors.cache.GridCacheReturnCompletableWra
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.GridCacheUpdateTxResult;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsUpdateCountersMap;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheEntry;
import org.apache.ignite.internal.processors.cache.persistence.StorageException;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
@@ -205,6 +207,16 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
return false;
}
+ /** {@inheritDoc} */
+ @Override public void activeCachesDeploymentEnabled(boolean depEnabled) {
+ throw new UnsupportedOperationException("Remote tx doesn't support deployment.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addActiveCache(GridCacheContext cacheCtx, boolean recovery) throws IgniteCheckedException {
+ txState.addActiveCache(cacheCtx, recovery, this);
+ }
+
/**
* @return Checks if transaction has no entries.
*/
@@ -460,7 +472,7 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
GridCacheReturnCompletableWrapper wrapper = null;
- if (!F.isEmpty(writeMap)) {
+ if (!F.isEmpty(writeMap) || mvccSnapshot != null) {
GridCacheReturn ret = null;
if (!near() && !local() && onePhaseCommit()) {
@@ -488,6 +500,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
cctx.database().checkpointReadLock();
try {
+ assert !txState.mvccEnabled(cctx) || mvccSnapshot != null : "Mvcc is not initialized: " + this;
+
Collection<IgniteTxEntry> entries = near() || cctx.snapshot().needTxReadLogging() ? allEntries() : writeEntries();
// Data entry to write to WAL and associated with it TxEntry.
@@ -613,7 +627,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
CU.subjectId(this, cctx),
resolveTaskName(),
dhtVer,
- txEntry.updateCounter());
+ txEntry.updateCounter(),
+ mvccSnapshot());
else {
assert val != null : txEntry;
@@ -637,9 +652,10 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
CU.subjectId(this, cctx),
resolveTaskName(),
dhtVer,
- txEntry.updateCounter());
+ txEntry.updateCounter(),
+ mvccSnapshot());
- txEntry.updateCounter(updRes.updatePartitionCounter());
+ txEntry.updateCounter(updRes.updateCounter());
if (updRes.loggedPointer() != null)
ptr = updRes.loggedPointer();
@@ -674,9 +690,10 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
CU.subjectId(this, cctx),
resolveTaskName(),
dhtVer,
- txEntry.updateCounter());
+ txEntry.updateCounter(),
+ mvccSnapshot());
- txEntry.updateCounter(updRes.updatePartitionCounter());
+ txEntry.updateCounter(updRes.updateCounter());
if (updRes.loggedPointer() != null)
ptr = updRes.loggedPointer();
@@ -790,6 +807,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
}
}
+ updateLocalCounters();
+
if (!near() && !F.isEmpty(dataEntries) && cctx.wal() != null) {
// Set new update counters for data entries received from persisted tx entries.
List<DataEntry> entriesWithCounters = dataEntries.stream()
@@ -803,6 +822,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
cctx.wal().flush(ptr, false);
}
catch (StorageException e) {
+ err = e;
+
throw new IgniteCheckedException("Failed to log transaction record " +
"(transaction will be rolled back): " + this, e);
}
@@ -810,6 +831,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
finally {
cctx.database().checkpointReadUnlock();
+ notifyDrManager(state() == COMMITTING && err == null);
+
if (wrapper != null)
wrapper.initialize(ret);
}
@@ -822,6 +845,33 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
}
}
+ /**
+ * Applies update counters to the local partitions.
+ */
+ private void updateLocalCounters() {
+ Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrsMap = updateCountersMap();
+
+ if (F.isEmpty(updCntrsMap))
+ return;
+
+ for (Map.Entry<Integer, GridDhtPartitionsUpdateCountersMap> entry : updCntrsMap.entrySet()) {
+ GridCacheContext cacheCtx = cctx.cacheContext(entry.getKey());
+
+ GridDhtPartitionsUpdateCountersMap cacheUpdCntrs = entry.getValue();
+
+ assert cacheUpdCntrs != null && !F.isEmpty(cacheUpdCntrs.updateCounters());
+
+ for (Map.Entry<Integer, Long> e : cacheUpdCntrs.updateCounters().entrySet()) {
+ Long updCntr = e.getValue();
+ GridDhtLocalPartition part = cacheCtx.topology().localPartition(e.getKey());
+
+ assert part != null && updCntr != null && updCntr > 0;
+
+ part.updateCounter(updCntr);
+ }
+ }
+ }
+
/** {@inheritDoc} */
@Override public final void commitRemoteTx() throws IgniteCheckedException {
if (optimistic())
@@ -901,6 +951,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
/** {@inheritDoc} */
@Override public final void rollbackRemoteTx() {
try {
+ notifyDrManager(false);
+
// Note that we don't evict near entries here -
// they will be deleted by their corresponding transactions.
if (state(ROLLING_BACK) || state() == UNKNOWN) {
@@ -971,8 +1023,17 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
}
/** {@inheritDoc} */
+ @Override public void updateCountersMap(Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrsMap) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public Map<Integer, GridDhtPartitionsUpdateCountersMap> updateCountersMap() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public String toString() {
return GridToStringBuilder.toString(GridDistributedTxRemoteAdapter.class, this, "super", super.toString());
}
-
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/CompoundLockFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/CompoundLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/CompoundLockFuture.java
new file mode 100644
index 0000000..63eff40
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/CompoundLockFuture.java
@@ -0,0 +1,112 @@
+/*
+ * 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.distributed.dht;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxAbstractEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxSelectForUpdateFuture;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.lang.IgniteInClosure;
+
+/**
+ *
+ */
+public class CompoundLockFuture extends GridFutureAdapter<Void> implements DhtLockFuture<Void>, IgniteInClosure<IgniteInternalFuture<?>> {
+ /** */
+ private static final long serialVersionUID = 4644646033267042131L;
+ /** */
+ private static final AtomicIntegerFieldUpdater<CompoundLockFuture> CNT_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(CompoundLockFuture.class, "cnt");
+ /** */
+ private volatile int cnt;
+ /** */
+ private final GridDhtTxLocalAdapter tx;
+
+ /**
+ * @param cnt ResultSet futures count.
+ * @param tx Transaction.
+ */
+ public CompoundLockFuture(int cnt, GridDhtTxLocalAdapter tx) {
+ this.cnt = cnt;
+ this.tx = tx;
+ }
+
+ /**
+ * @param fut ResultSet future.
+ */
+ public void register(IgniteInternalFuture<?> fut) {
+ fut.listen(this);
+ }
+
+ /**
+ * Init method.
+ */
+ public void init() {
+ while(true) {
+ IgniteInternalFuture<?> fut = tx.lockFuture();
+
+ if (fut == GridDhtTxLocalAdapter.ROLLBACK_FUT) {
+ onDone(tx.timedOut() ? tx.timeoutException() : tx.rollbackException());
+
+ break;
+ }
+ else if (fut != null) {
+ // Wait for previous future.
+ assert fut instanceof GridNearTxAbstractEnlistFuture
+ || fut instanceof GridDhtTxAbstractEnlistFuture
+ || fut instanceof CompoundLockFuture
+ || fut instanceof GridNearTxSelectForUpdateFuture : fut;
+
+ // Terminate this future if parent future is terminated by rollback.
+ if (!fut.isDone()) {
+ fut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ if (fut.error() != null)
+ onDone(fut.error());
+ }
+ });
+ }
+ else if (fut.error() != null)
+ onDone(fut.error());
+
+ break;
+ }
+ else if (tx.updateLockFuture(null, this))
+ break;
+ }
+ }
+
+ @Override public void apply(IgniteInternalFuture<?> future) {
+ if (!isDone() && (future.error() != null || CNT_UPD.decrementAndGet(this) == 0)) {
+ Throwable err = future.error();
+
+ if (err == null)
+ tx.clearLockFuture(this);
+
+ onDone(err);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onError(Throwable error) {
+ assert error != null;
+
+ onDone(error);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/DhtLockFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/DhtLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/DhtLockFuture.java
new file mode 100644
index 0000000..b729dcd
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/DhtLockFuture.java
@@ -0,0 +1,30 @@
+/*
+ * 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.distributed.dht;
+
+import org.apache.ignite.internal.IgniteInternalFuture;
+
+/**
+ * Marker interface.
+ */
+public interface DhtLockFuture<T> extends IgniteInternalFuture<T> {
+ /**
+ * @param error Error.
+ */
+ public void onError(Throwable error);
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/ExceptionAware.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/ExceptionAware.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/ExceptionAware.java
new file mode 100644
index 0000000..036492f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/ExceptionAware.java
@@ -0,0 +1,30 @@
+/*
+ * 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.distributed.dht;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public interface ExceptionAware {
+ /**
+ * @return Exception.
+ */
+ @Nullable Throwable error();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
index 54a850c..70ce082 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
@@ -46,6 +46,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Gri
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.util.F0;
import org.apache.ignite.internal.util.GridAtomicLong;
import org.apache.ignite.internal.util.GridPartitionStateMap;
@@ -199,6 +200,11 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
}
/** {@inheritDoc} */
+ @Override public MvccCoordinator mvccCoordinator() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
@Override public boolean holdsLock() {
return lock.isWriteLockedByCurrentThread() || lock.getReadHoldCount() > 0;
}
@@ -207,6 +213,7 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
@Override public void updateTopologyVersion(
GridDhtTopologyFuture exchFut,
DiscoCache discoCache,
+ MvccCoordinator mvccCrd,
long updSeq,
boolean stopping
) throws IgniteInterruptedCheckedException {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
index 5adbda7..cd37650 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
@@ -68,6 +68,8 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetR
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetResponse;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.platform.cache.PlatformCacheEntryFilter;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
@@ -230,16 +232,30 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
entry.unswap();
- GridCacheEntryInfo info = entry.info();
+ if (ctx.mvccEnabled()) {
+ List<GridCacheEntryInfo> infos = entry.allVersionsInfo();
- if (info == null) {
- assert entry.obsolete() : entry;
+ if (infos == null) {
+ assert entry.obsolete() : entry;
- continue;
+ continue;
+ }
+
+ for (int i = 0; i < infos.size(); i++)
+ res.addInfo(infos.get(i));
}
+ else {
+ GridCacheEntryInfo info = entry.info();
+
+ if (info == null) {
+ assert entry.obsolete() : entry;
- if (!info.isNew())
- res.addInfo(info);
+ continue;
+ }
+
+ if (!info.isNew())
+ res.addInfo(info);
+ }
entry.touch(msg.topologyVersion());
@@ -588,6 +604,9 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
return;
}
+ //TODO IGNITE-7954
+ MvccUtils.verifyMvccOperationSupport(ctx, "Load");
+
final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion();
// Version for all loaded entries.
@@ -766,6 +785,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
* @param taskName Task name.
* @param expiry Expiry policy.
* @param skipVals Skip values flag.
+ * @param mvccSnapshot MVCC snapshot.
* @return Get future.
*/
IgniteInternalFuture<Map<KeyCacheObject, EntryGetResult>> getDhtAllAsync(
@@ -776,7 +796,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
String taskName,
@Nullable IgniteCacheExpiryPolicy expiry,
boolean skipVals,
- boolean recovery
+ boolean recovery,
+ MvccSnapshot mvccSnapshot
) {
return getAllAsync0(keys,
readerArgs,
@@ -789,7 +810,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
skipVals,
/*keep cache objects*/true,
recovery,
- /*need version*/true);
+ /*need version*/true,
+ mvccSnapshot);
}
/**
@@ -803,6 +825,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
* @param taskNameHash Task name hash code.
* @param expiry Expiry policy.
* @param skipVals Skip values flag.
+ * @param mvccSnapshot MVCC snapshot.
* @return DHT future.
*/
public GridDhtFuture<Collection<GridCacheEntryInfo>> getDhtAsync(UUID reader,
@@ -815,7 +838,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
int taskNameHash,
@Nullable IgniteCacheExpiryPolicy expiry,
boolean skipVals,
- boolean recovery
+ boolean recovery,
+ MvccSnapshot mvccSnapshot
) {
GridDhtGetFuture<K, V> fut = new GridDhtGetFuture<>(ctx,
msgId,
@@ -828,7 +852,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
expiry,
skipVals,
recovery,
- addReaders);
+ addReaders,
+ mvccSnapshot);
fut.init();
@@ -846,9 +871,10 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
* @param taskNameHash Task name hash.
* @param expiry Expiry.
* @param skipVals Skip vals flag.
+ * @param mvccSnapshot Mvcc snapshot.
* @return Future for the operation.
*/
- public GridDhtGetSingleFuture getDhtSingleAsync(
+ GridDhtGetSingleFuture getDhtSingleAsync(
UUID nodeId,
long msgId,
KeyCacheObject key,
@@ -859,7 +885,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
int taskNameHash,
@Nullable IgniteCacheExpiryPolicy expiry,
boolean skipVals,
- boolean recovery
+ boolean recovery,
+ MvccSnapshot mvccSnapshot
) {
GridDhtGetSingleFuture fut = new GridDhtGetSingleFuture<>(
ctx,
@@ -873,7 +900,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
taskNameHash,
expiry,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot);
fut.init();
@@ -901,7 +929,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
req.taskNameHash(),
expiryPlc,
req.skipValues(),
- req.recovery());
+ req.recovery(),
+ req.mvccSnapshot());
fut.listen(new CI1<IgniteInternalFuture<GridCacheEntryInfo>>() {
@Override public void apply(IgniteInternalFuture<GridCacheEntryInfo> f) {
@@ -1004,7 +1033,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
req.taskNameHash(),
expiryPlc,
req.skipValues(),
- req.recovery());
+ req.recovery(),
+ req.mvccSnapshot());
fut.listen(new CI1<IgniteInternalFuture<Collection<GridCacheEntryInfo>>>() {
@Override public void apply(IgniteInternalFuture<Collection<GridCacheEntryInfo>> f) {
@@ -1230,6 +1260,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
if (expVer.equals(curVer))
return false;
+ // TODO IGNITE-7164 check mvcc crd for mvcc enabled txs.
+
Collection<ClusterNode> cacheNodes0 = ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), expVer);
Collection<ClusterNode> cacheNodes1 = ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), curVer);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
index fe02090..343e418 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
@@ -102,6 +102,11 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
}
/** {@inheritDoc} */
+ @Override protected long nextMvccPartitionCounter() {
+ return locPart.nextMvccUpdateCounter();
+ }
+
+ /** {@inheritDoc} */
@Override public int memorySize() throws IgniteCheckedException {
int rdrsOverhead;
@@ -648,7 +653,10 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
']');
}
- removeValue();
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccRemoveAll(this);
+ else
+ removeValue();
// Give to GC.
update(null, 0L, 0L, ver, true);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
index a969dd3..023c058 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
@@ -37,6 +37,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedExceptio
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.ReaderArguments;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
import org.apache.ignite.internal.util.future.GridCompoundIdentityFuture;
@@ -114,6 +115,9 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
/** */
private final boolean addReaders;
+ /** */
+ private final MvccSnapshot mvccSnapshot;
+
/**
* @param cctx Context.
* @param msgId Message ID.
@@ -125,6 +129,7 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
* @param taskNameHash Task name hash code.
* @param expiryPlc Expiry policy.
* @param skipVals Skip values flag.
+ * @param mvccSnapshot MVCC snapshot.
*/
public GridDhtGetFuture(
GridCacheContext<K, V> cctx,
@@ -138,7 +143,8 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean skipVals,
boolean recovery,
- boolean addReaders
+ boolean addReaders,
+ MvccSnapshot mvccSnapshot
) {
super(CU.<GridCacheEntryInfo>collectionsReducer(keys.size()));
@@ -157,6 +163,7 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
this.skipVals = skipVals;
this.recovery = recovery;
this.addReaders = addReaders;
+ this.mvccSnapshot = mvccSnapshot;
futId = IgniteUuid.randomUuid();
@@ -422,7 +429,8 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
taskName,
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot);
}
else {
final ReaderArguments args = readerArgs;
@@ -445,7 +453,8 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
taskName,
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot);
}
}
);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
index 99a8135..de76eb1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedExceptio
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.ReaderArguments;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.F;
@@ -103,6 +104,9 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
/** Recovery context flag. */
private final boolean recovery;
+ /** */
+ private final MvccSnapshot mvccSnapshot;
+
/**
* @param cctx Context.
* @param msgId Message ID.
@@ -115,6 +119,7 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
* @param taskNameHash Task name hash code.
* @param expiryPlc Expiry policy.
* @param skipVals Skip values flag.
+ * @param mvccSnapshot Mvcc snapshot.
*/
public GridDhtGetSingleFuture(
GridCacheContext<K, V> cctx,
@@ -128,7 +133,8 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
int taskNameHash,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean skipVals,
- boolean recovery
+ boolean recovery,
+ @Nullable MvccSnapshot mvccSnapshot
) {
assert reader != null;
assert key != null;
@@ -145,6 +151,7 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
this.expiryPlc = expiryPlc;
this.skipVals = skipVals;
this.recovery = recovery;
+ this.mvccSnapshot = mvccSnapshot;
futId = IgniteUuid.randomUuid();
@@ -365,7 +372,8 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
taskName,
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot);
}
else {
final ReaderArguments args = readerArgs;
@@ -390,7 +398,8 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
taskName,
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot);
fut0.listen(createGetFutureListener());
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
index 5f976c9..f56df00 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
@@ -976,6 +976,20 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
}
/**
+ * @return Current mvcc update counter value.
+ */
+ public long mvccUpdateCounter() {
+ return store.mvccUpdateCounter();
+ }
+
+ /**
+ * @return Next mvcc update counter.
+ */
+ public long nextMvccUpdateCounter() {
+ return store.nextMvccUpdateCounter();
+ }
+
+ /**
* @return Initial update counter.
*/
public long initialUpdateCounter() {
@@ -1259,6 +1273,15 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
}
/**
+ * Returns group context.
+ *
+ * @return Group context.
+ */
+ public CacheGroupContext group() {
+ return grp;
+ }
+
+ /**
* @param cacheId Cache ID.
*/
void onCacheStopped(int cacheId) {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockFuture.java
index 529d965..c2cd781 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockFuture.java
@@ -54,6 +54,9 @@ import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCa
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException;
import org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtColocatedLockFuture;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -82,7 +85,7 @@ import static org.apache.ignite.internal.processors.dr.GridDrType.DR_PRELOAD;
* Cache lock future.
*/
public final class GridDhtLockFuture extends GridCacheCompoundIdentityFuture<Boolean>
- implements GridCacheVersionedFuture<Boolean>, GridDhtFuture<Boolean>, GridCacheMappedVersion {
+ implements GridCacheVersionedFuture<Boolean>, GridDhtFuture<Boolean>, GridCacheMappedVersion, DhtLockFuture<Boolean> {
/** */
private static final long serialVersionUID = 0L;
@@ -264,7 +267,7 @@ public final class GridDhtLockFuture extends GridCacheCompoundIdentityFuture<Boo
if (tx != null) {
while(true) {
- IgniteInternalFuture<Boolean> fut = tx.lockFut;
+ IgniteInternalFuture fut = tx.lockFut;
if (fut != null) {
if (fut == GridDhtTxLocalAdapter.ROLLBACK_FUT)
@@ -274,8 +277,8 @@ public final class GridDhtLockFuture extends GridCacheCompoundIdentityFuture<Boo
assert fut instanceof GridDhtColocatedLockFuture : fut;
// Terminate this future if parent(collocated) future is terminated by rollback.
- fut.listen(new IgniteInClosure<IgniteInternalFuture<Boolean>>() {
- @Override public void apply(IgniteInternalFuture<Boolean> fut) {
+ fut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
try {
fut.get();
}
@@ -1307,6 +1310,10 @@ public final class GridDhtLockFuture extends GridCacheCompoundIdentityFuture<Boo
try {
if (entry.initialValue(info.value(),
info.version(),
+ cctx.mvccEnabled() ? ((MvccVersionAware)info).mvccVersion() : null,
+ cctx.mvccEnabled() ? ((MvccUpdateVersionAware)info).newMvccVersion() : null,
+ cctx.mvccEnabled() ? ((MvccVersionAware)entry).mvccTxState() : TxState.NA,
+ cctx.mvccEnabled() ? ((MvccUpdateVersionAware)entry).newMvccTxState() : TxState.NA,
info.ttl(),
info.expireTime(),
true,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
index 42ef309..3ae3c8f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Gri
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.jetbrains.annotations.Nullable;
@@ -74,6 +75,7 @@ public interface GridDhtPartitionTopology {
public void updateTopologyVersion(
GridDhtTopologyFuture exchFut,
DiscoCache discoCache,
+ MvccCoordinator mvccCrd,
long updateSeq,
boolean stopping
) throws IgniteInterruptedCheckedException;
@@ -421,4 +423,6 @@ public interface GridDhtPartitionTopology {
* @param updateRebalanceVer {@code True} if need check rebalance state.
*/
public void onExchangeDone(GridDhtPartitionsExchangeFuture fut, AffinityAssignment assignment, boolean updateRebalanceVer);
+
+ public MvccCoordinator mvccCoordinator();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
index c3ff15a..1256a8b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
@@ -52,6 +52,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Gri
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
import org.apache.ignite.internal.util.F0;
import org.apache.ignite.internal.util.GridAtomicLong;
import org.apache.ignite.internal.util.GridPartitionStateMap;
@@ -146,6 +147,9 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
/** */
private volatile AffinityTopologyVersion rebalancedTopVer = AffinityTopologyVersion.NONE;
+ /** */
+ private volatile MvccCoordinator mvccCrd;
+
/**
* @param ctx Cache shared context.
* @param grp Cache group.
@@ -238,6 +242,11 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
}
/** {@inheritDoc} */
+ @Override public MvccCoordinator mvccCoordinator() {
+ return mvccCrd;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean holdsLock() {
return lock.isWriteLockedByCurrentThread() || lock.getReadHoldCount() > 0;
}
@@ -246,6 +255,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
@Override public void updateTopologyVersion(
GridDhtTopologyFuture exchFut,
@NotNull DiscoCache discoCache,
+ MvccCoordinator mvccCrd,
long updSeq,
boolean stopping
) throws IgniteInterruptedCheckedException {
@@ -272,6 +282,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
lastTopChangeVer = exchTopVer;
this.discoCache = discoCache;
+ this.mvccCrd = mvccCrd;
}
finally {
lock.writeLock().unlock();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsStateValidator.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsStateValidator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsStateValidator.java
index 866c513..7faf0ff 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsStateValidator.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsStateValidator.java
@@ -32,6 +32,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsSingleMessage;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.lang.IgniteProductVersion;
@@ -93,11 +94,13 @@ public class GridDhtPartitionsStateValidator {
ignoringNodes.add(id);
}
- // Validate cache sizes.
- result = validatePartitionsSizes(top, messages, ignoringNodes);
+ if (!MvccUtils.mvccEnabled(cctx.kernalContext())) { // TODO: Remove "if" clause in IGNITE-8149.
+ // Validate cache sizes.
+ result = validatePartitionsSizes(top, messages, ignoringNodes);
- if (!result.isEmpty())
- throw new IgniteCheckedException("Partitions cache sizes are inconsistent for " + fold(topVer, result));
+ if (!result.isEmpty())
+ throw new IgniteCheckedException("Partitions cache sizes are inconsistent for " + fold(topVer, result));
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsUpdateCountersMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsUpdateCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsUpdateCountersMap.java
new file mode 100644
index 0000000..7a345d1
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsUpdateCountersMap.java
@@ -0,0 +1,119 @@
+/*
+ * 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.distributed.dht;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.ignite.internal.GridDirectMap;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Partitions update counters message.
+ */
+public class GridDhtPartitionsUpdateCountersMap implements Message {
+ /** */
+ private static final long serialVersionUID = -4599730112233297219L;
+
+ /** Map of update counters made by this tx. Mapping: partId -> updCntr. */
+ @GridDirectMap(keyType = Integer.class, valueType = Long.class)
+ private Map<Integer, Long> updCntrs;
+
+ /**
+ *
+ */
+ public GridDhtPartitionsUpdateCountersMap() {
+ updCntrs = new HashMap<>();
+ }
+
+ /**
+ * @return Update counters.
+ */
+ public Map<Integer, Long> updateCounters() {
+ return updCntrs;
+ }
+
+ /**
+ * @param updCntrs Update counters.
+ */
+ public void updateCounters(Map<Integer, Long> updCntrs) {
+ this.updCntrs = updCntrs;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeMap("updCntrs", updCntrs, MessageCollectionItemType.INT, MessageCollectionItemType.LONG))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ updCntrs = reader.readMap("updCntrs", MessageCollectionItemType.INT, MessageCollectionItemType.LONG, false);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridDhtPartitionsUpdateCountersMap.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 157;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
index f93e6a3..be1c7e2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
@@ -28,7 +28,9 @@ import java.util.ListIterator;
import java.util.Map;
import java.util.UUID;
import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.cluster.ClusterTopologyException;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.NodeStoppingException;
import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
@@ -55,8 +57,16 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLock
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLockResponse;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTransactionalCache;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistResponse;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistResponse;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxRemote;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearUnlockRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotWithoutTxs;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalEx;
@@ -76,6 +86,7 @@ import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.thread.IgniteThread;
import org.apache.ignite.transactions.TransactionIsolation;
import org.jetbrains.annotations.Nullable;
@@ -83,7 +94,9 @@ import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.NOOP;
import static org.apache.ignite.internal.processors.cache.GridCacheUtils.isNearEnabled;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
import static org.apache.ignite.transactions.TransactionState.COMMITTING;
/**
@@ -164,6 +177,18 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
}
});
+ ctx.io().addCacheHandler(ctx.cacheId(), GridNearTxQueryEnlistRequest.class, new CI2<UUID, GridNearTxQueryEnlistRequest>() {
+ @Override public void apply(UUID nodeId, GridNearTxQueryEnlistRequest req) {
+ processNearTxQueryEnlistRequest(nodeId, req);
+ }
+ });
+
+ ctx.io().addCacheHandler(ctx.cacheId(), GridNearTxQueryEnlistResponse.class, new CI2<UUID, GridNearTxQueryEnlistResponse>() {
+ @Override public void apply(UUID nodeId, GridNearTxQueryEnlistResponse req) {
+ processNearEnlistResponse(nodeId, req);
+ }
+ });
+
ctx.io().addCacheHandler(ctx.cacheId(), GridDhtForceKeysRequest.class,
new MessageHandler<GridDhtForceKeysRequest>() {
@Override public void onMessage(ClusterNode node, GridDhtForceKeysRequest msg) {
@@ -177,6 +202,41 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
processForceKeyResponse(node, msg);
}
});
+
+ ctx.io().addCacheHandler(ctx.cacheId(), GridNearTxQueryResultsEnlistRequest.class,
+ new CI2<UUID, GridNearTxQueryResultsEnlistRequest>() {
+ @Override public void apply(UUID nodeId, GridNearTxQueryResultsEnlistRequest req) {
+ processNearTxQueryResultsEnlistRequest(nodeId, req);
+ }
+ });
+
+ ctx.io().addCacheHandler(ctx.cacheId(), GridNearTxQueryResultsEnlistResponse.class,
+ new CI2<UUID, GridNearTxQueryResultsEnlistResponse>() {
+ @Override public void apply(UUID nodeId, GridNearTxQueryResultsEnlistResponse req) {
+ processNearTxEnlistResponse(nodeId, req);
+ }
+ });
+
+ ctx.io().addCacheHandler(ctx.cacheId(), GridDhtTxQueryEnlistRequest.class,
+ new CI2<UUID, GridDhtTxQueryEnlistRequest>() {
+ @Override public void apply(UUID nodeId, GridDhtTxQueryEnlistRequest msg) {
+ processDhtTxQueryEnlistRequest(nodeId, msg, false);
+ }
+ });
+
+ ctx.io().addCacheHandler(ctx.cacheId(), GridDhtTxQueryFirstEnlistRequest.class,
+ new CI2<UUID, GridDhtTxQueryEnlistRequest>() {
+ @Override public void apply(UUID nodeId, GridDhtTxQueryEnlistRequest msg) {
+ processDhtTxQueryEnlistRequest(nodeId, msg, true);
+ }
+ });
+
+ ctx.io().addCacheHandler(ctx.cacheId(), GridDhtTxQueryEnlistResponse.class,
+ new CI2<UUID, GridDhtTxQueryEnlistResponse>() {
+ @Override public void apply(UUID nodeId, GridDhtTxQueryEnlistResponse msg) {
+ processDhtTxQueryEnlistResponse(nodeId, msg);
+ }
+ });
}
/** {@inheritDoc} */
@@ -627,6 +687,85 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
* @param nodeId Node ID.
* @param req Request.
*/
+ private void processNearTxQueryEnlistRequest(UUID nodeId, final GridNearTxQueryEnlistRequest req) {
+ assert nodeId != null;
+ assert req != null;
+
+ ClusterNode nearNode = ctx.discovery().node(nodeId);
+
+ GridDhtTxLocal tx;
+
+ try {
+ tx = initTxTopologyVersion(nodeId,
+ nearNode,
+ req.version(),
+ req.futureId(),
+ req.miniId(),
+ req.firstClientRequest(),
+ req.topologyVersion(),
+ req.threadId(),
+ req.txTimeout(),
+ req.subjectId(),
+ req.taskNameHash());
+ }
+ catch (IgniteCheckedException | IgniteException ex) {
+ GridNearTxQueryEnlistResponse res = new GridNearTxQueryEnlistResponse(req.cacheId(),
+ req.futureId(),
+ req.miniId(),
+ req.version(),
+ ex);
+
+ try {
+ ctx.io().send(nearNode, res, ctx.ioPolicy());
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send near enlist response [" +
+ "txId=" + req.version() +
+ ", node=" + nodeId +
+ ", res=" + res + ']', e);
+ }
+
+ return;
+ }
+
+ GridDhtTxQueryEnlistFuture fut = new GridDhtTxQueryEnlistFuture(
+ nodeId,
+ req.version(),
+ req.mvccSnapshot(),
+ req.threadId(),
+ req.futureId(),
+ req.miniId(),
+ tx,
+ req.cacheIds(),
+ req.partitions(),
+ req.schemaName(),
+ req.query(),
+ req.parameters(),
+ req.flags(),
+ req.pageSize(),
+ req.timeout(),
+ ctx);
+
+ fut.listen(NearTxQueryEnlistResultHandler.instance());
+
+ fut.init();
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param res Response.
+ */
+ private void processNearEnlistResponse(UUID nodeId, final GridNearTxQueryEnlistResponse res) {
+ GridNearTxQueryEnlistFuture fut = (GridNearTxQueryEnlistFuture)ctx.mvcc().versionedFuture(res.version(), res.futureId());
+
+ if (fut != null)
+ fut.onResult(nodeId, res);
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param req Request.
+ */
private void processNearLockRequest(UUID nodeId, GridNearLockRequest req) {
assert ctx.affinityNode();
assert nodeId != null;
@@ -1276,7 +1415,7 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
CacheObject val = null;
- if (ret)
+ if (ret) {
val = e.innerGet(
null,
tx,
@@ -1287,7 +1426,9 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
null,
tx != null ? tx.resolveTaskName() : null,
null,
- req.keepBinary());
+ req.keepBinary(),
+ null); // TODO IGNITE-7371
+ }
assert e.lockedBy(mappedVer) || ctx.mvcc().isRemoved(e.context(), mappedVer) :
"Entry does not own lock for tx [locNodeId=" + ctx.localNodeId() +
@@ -1380,6 +1521,7 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
U.error(log, "Failed to acquire lock for request: " + req, err);
try {
+ // TODO Async rollback
// Don't send reply message to this node or if lock was cancelled or tx was rolled back asynchronously.
if (!nearNode.id().equals(ctx.nodeId()) && !X.hasCause(err, GridDistributedLockCancelledException.class) &&
!X.hasCause(err, IgniteTxRollbackCheckedException.class)) {
@@ -1576,7 +1718,7 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
* @param nodes Nodes.
* @param map Map.
*/
- @SuppressWarnings( {"MismatchedQueryAndUpdateOfCollection"})
+ @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
private void map(GridCacheEntryEx entry,
@Nullable Iterable<? extends ClusterNode> nodes,
Map<ClusterNode, List<KeyCacheObject>> map) {
@@ -1785,4 +1927,321 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
if (nearEntry != null)
nearEntry.markObsolete(ctx.versions().next());
}
+
+ /**
+ * @param nodeId Node ID.
+ * @param req Request.
+ */
+ private void processNearTxQueryResultsEnlistRequest(UUID nodeId, final GridNearTxQueryResultsEnlistRequest req) {
+ assert nodeId != null;
+ assert req != null;
+
+ ClusterNode nearNode = ctx.discovery().node(nodeId);
+
+ GridDhtTxLocal tx;
+
+ try {
+ tx = initTxTopologyVersion(nodeId,
+ nearNode,
+ req.version(),
+ req.futureId(),
+ req.miniId(),
+ req.firstClientRequest(),
+ req.topologyVersion(),
+ req.threadId(),
+ req.txTimeout(),
+ req.subjectId(),
+ req.taskNameHash());
+ }
+ catch (IgniteCheckedException | IgniteException ex) {
+ GridNearTxQueryResultsEnlistResponse res = new GridNearTxQueryResultsEnlistResponse(req.cacheId(),
+ req.futureId(),
+ req.miniId(),
+ req.version(),
+ ex);
+
+ try {
+ ctx.io().send(nearNode, res, ctx.ioPolicy());
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send near enlist response [" +
+ "txId=" + req.version() +
+ ", node=" + nodeId +
+ ", res=" + res + ']', e);
+ }
+
+ return;
+ }
+
+ GridDhtTxQueryResultsEnlistFuture fut = new GridDhtTxQueryResultsEnlistFuture(
+ nodeId,
+ req.version(),
+ req.mvccSnapshot(),
+ req.threadId(),
+ req.futureId(),
+ req.miniId(),
+ tx,
+ req.timeout(),
+ ctx,
+ req.rows(),
+ req.operation());
+
+ fut.listen(NearTxQueryEnlistResultHandler.instance());
+
+ fut.init();
+ }
+
+ /**
+ * @param nodeId Near node id.
+ * @param nearNode Near node.
+ * @param nearLockVer Near lock version.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini-future id.
+ * @param firstClientReq First client request flag.
+ * @param topVer Topology version.
+ * @param nearThreadId Near node thread id.
+ * @param timeout Timeout.
+ * @param txSubjectId Transaction subject id.
+ * @param txTaskNameHash Transaction task name hash.
+ * @return Transaction.
+ */
+ public GridDhtTxLocal initTxTopologyVersion(UUID nodeId,
+ ClusterNode nearNode,
+ GridCacheVersion nearLockVer,
+ IgniteUuid nearFutId,
+ int nearMiniId,
+ boolean firstClientReq,
+ AffinityTopologyVersion topVer,
+ long nearThreadId,
+ long timeout,
+ UUID txSubjectId,
+ int txTaskNameHash) throws IgniteException, IgniteCheckedException {
+
+ assert ctx.affinityNode();
+
+ if (txLockMsgLog.isDebugEnabled()) {
+ txLockMsgLog.debug("Received near enlist request [txId=" + nearLockVer +
+ ", node=" + nodeId + ']');
+ }
+
+ if (nearNode == null) {
+ U.warn(txLockMsgLog, "Received near enlist request from unknown node (will ignore) [txId=" + nearLockVer +
+ ", node=" + nodeId + ']');
+
+ return null;
+ }
+
+ GridDhtTxLocal tx = null;
+
+ GridCacheVersion dhtVer = ctx.tm().mappedVersion(nearLockVer);
+
+ if (dhtVer != null)
+ tx = ctx.tm().tx(dhtVer);
+
+ GridDhtPartitionTopology top = null;
+
+ if (tx == null) {
+ if (firstClientReq) {
+ assert nearNode.isClient();
+
+ top = topology();
+
+ top.readLock();
+
+ GridDhtTopologyFuture topFut = top.topologyVersionFuture();
+
+ if (!topFut.isDone() || !topFut.topologyVersion().equals(topVer)) {
+ // TODO IGNITE-7164 Wait for topology change, remap client TX in case affinity was changed.
+ top.readUnlock();
+
+ throw new ClusterTopologyException("Topology was changed. Please retry on stable topology.");
+ }
+ }
+
+ try {
+ tx = new GridDhtTxLocal(
+ ctx.shared(),
+ topVer,
+ nearNode.id(),
+ nearLockVer,
+ nearFutId,
+ nearMiniId,
+ nearThreadId,
+ false,
+ false,
+ ctx.systemTx(),
+ false,
+ ctx.ioPolicy(),
+ PESSIMISTIC,
+ REPEATABLE_READ,
+ timeout,
+ false,
+ false,
+ false,
+ -1,
+ null,
+ txSubjectId,
+ txTaskNameHash);
+
+ // if (req.syncCommit())
+ tx.syncMode(FULL_SYNC);
+
+ tx = ctx.tm().onCreated(null, tx);
+
+ if (tx == null || !tx.init()) {
+ String msg = "Failed to acquire lock (transaction has been completed): " +
+ nearLockVer;
+
+ U.warn(log, msg);
+
+ try {
+ if (tx != null)
+ tx.rollbackDhtLocal();
+ }
+ catch (IgniteCheckedException ex) {
+ U.error(log, "Failed to rollback the transaction: " + tx, ex);
+ }
+
+ throw new IgniteCheckedException(msg);
+ }
+
+ tx.topologyVersion(topVer);
+ }
+ finally {
+ if (top != null)
+ top.readUnlock();
+ }
+ }
+
+ ctx.tm().txContext(tx);
+
+ return tx;
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param res Response.
+ */
+ private void processNearTxEnlistResponse(UUID nodeId, final GridNearTxQueryResultsEnlistResponse res) {
+ GridNearTxQueryResultsEnlistFuture fut = (GridNearTxQueryResultsEnlistFuture)
+ ctx.mvcc().versionedFuture(res.version(), res.futureId());
+
+ if (fut != null)
+ fut.onResult(nodeId, res);
+ }
+
+ /**
+ * @param primary Primary node.
+ * @param req Request.
+ * @param e Error.
+ */
+ private void onError(UUID primary, GridDhtTxQueryEnlistRequest req, Throwable e) {
+ GridDhtTxQueryEnlistResponse res = new GridDhtTxQueryEnlistResponse(ctx.cacheId(),
+ req.dhtFutureId(),
+ req.batchId(),
+ e);
+
+ try {
+ ctx.io().send(primary, res, ctx.ioPolicy());
+ }
+ catch (IgniteCheckedException ioEx) {
+ U.error(log, "Failed to send DHT enlist reply to primary node [node: " + primary + ", req=" + req +
+ ']', ioEx);
+ }
+ }
+
+ /**
+ * @param primary Primary node.
+ * @param req Message.
+ * @param first Flag if this is a first request in current operation.
+ */
+ private void processDhtTxQueryEnlistRequest(UUID primary, GridDhtTxQueryEnlistRequest req, boolean first) {
+ try {
+ assert req.version() != null && req.op() != null;
+
+ GridDhtTxRemote tx = ctx.tm().tx(req.version());
+
+ if (tx == null) {
+ if (!first)
+ throw new IgniteCheckedException("Can not find a transaction for version [version="
+ + req.version() + ']');
+
+ GridDhtTxQueryFirstEnlistRequest req0 = (GridDhtTxQueryFirstEnlistRequest)req;
+
+ tx = new GridDhtTxRemote(ctx.shared(),
+ req0.nearNodeId(),
+ req0.dhtFutureId(),
+ primary,
+ req0.nearXidVersion(),
+ req0.topologyVersion(),
+ req0.version(),
+ null,
+ ctx.systemTx(),
+ ctx.ioPolicy(),
+ PESSIMISTIC,
+ REPEATABLE_READ,
+ false,
+ req0.timeout(),
+ -1,
+ req0.subjectId(),
+ req0.taskNameHash(),
+ false);
+
+ tx.mvccSnapshot(new MvccSnapshotWithoutTxs(req0.coordinatorVersion(), req0.counter(),
+ MVCC_OP_COUNTER_NA, req0.cleanupVersion()));
+
+ tx = ctx.tm().onCreated(null, tx);
+
+ if (tx == null || !ctx.tm().onStarted(tx)) {
+ throw new IgniteTxRollbackCheckedException("Failed to update backup " +
+ "(transaction has been completed): " + req0.version());
+ }
+ }
+
+ assert tx != null;
+
+ MvccSnapshot s0 = tx.mvccSnapshot();
+
+ MvccSnapshot snapshot = new MvccSnapshotWithoutTxs(s0.coordinatorVersion(), s0.counter(),
+ req.operationCounter(), s0.cleanupVersion());
+
+ tx.mvccEnlistBatch(ctx, req.op(), req.keys(), req.values(), snapshot, req.updateCounters());
+
+ GridDhtTxQueryEnlistResponse res = new GridDhtTxQueryEnlistResponse(req.cacheId(),
+ req.dhtFutureId(),
+ req.batchId(),
+ null);
+
+ try {
+ ctx.io().send(primary, res, ctx.ioPolicy());
+ }
+ catch (IgniteCheckedException ioEx) {
+ U.error(log, "Failed to send DHT enlist reply to primary node [node: " + primary + ", req=" +
+ req + ']', ioEx);
+ }
+ }
+ catch (IgniteCheckedException e) {
+ onError(primary, req, e);
+ }
+ }
+
+ /**
+ * @param backup Backup node.
+ * @param res Response message.
+ */
+ private void processDhtTxQueryEnlistResponse(UUID backup, GridDhtTxQueryEnlistResponse res) {
+ GridDhtTxAbstractEnlistFuture fut = (GridDhtTxAbstractEnlistFuture)
+ ctx.mvcc().future(res.futureId());
+
+ if (fut == null) {
+ U.warn(log, "Received dht enlist response for unknown future [futId=" + res.futureId() +
+ ", batchId=" + res.batchId() +
+ ", node=" + backup + ']');
+
+ return;
+ }
+
+ fut.onResult(backup, res);
+ }
+
}
[07/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java
new file mode 100644
index 0000000..ade3763
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesWithReducerAbstractTest.java
@@ -0,0 +1,829 @@
+/*
+ * 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.mvcc;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.testframework.GridTestUtils.runMultiThreaded;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * Tests for transactional SQL.
+ */
+public abstract class CacheMvccSqlTxQueriesWithReducerAbstractTest extends CacheMvccAbstractTest {
+ /** */
+ private static final int TIMEOUT = 3000;
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ ccfgs = null;
+ ccfg = null;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerInsert() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache =
+ checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1),
+ 2,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2),
+ 3,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3)));
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key + 3, idxVal1 + 3 FROM MvccTestSqlIndexValue";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ qry.setDistributedJoins(true);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(4), cache.get(4));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(5), cache.get(5));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(6), cache.get(6));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerInsertDuplicateKey() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache =
+ checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1),
+ 2,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2),
+ 3,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3)));
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key, idxVal1 FROM MvccTestSqlIndexValue";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ qry.setDistributedJoins(true);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ GridTestUtils.assertThrowsAnyCause(log, new Callable<Object>() {
+ @Override public Object call() {
+ return cache0.query(qry);
+ }
+ }, IgniteSQLException.class, "Duplicate key");
+
+ tx.rollback();
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerMerge() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache =
+ checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1),
+ 2,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2),
+ 3,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3)));
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "MERGE INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key * 2, idxVal1 FROM MvccTestSqlIndexValue";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ qry.setDistributedJoins(true);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(4));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(6));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerMultiBatchPerNodeServer() throws Exception {
+ checkMultiBatchPerNode(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerMultiBatchPerNodeClient() throws Exception {
+ checkMultiBatchPerNode(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ private void checkMultiBatchPerNode(boolean client) throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ Ignite checkNode;
+ Ignite updateNode;
+
+ Random rnd = ThreadLocalRandom.current();
+
+ if (client) {
+ startGridsMultiThreaded(3);
+
+ updateNode = grid(rnd.nextInt(3));
+
+ this.client = true;
+
+ checkNode = startGrid(4);
+ }
+ else {
+ startGridsMultiThreaded(4);
+
+ checkNode = grid(rnd.nextInt(4));
+ updateNode = grid(rnd.nextInt(4));
+ }
+
+ IgniteCache<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache =
+ checkNode.cache(DEFAULT_CACHE_NAME);
+
+ final int count = 6;
+
+ Map<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> vals = new HashMap<>(count);
+
+ for (int idx = 1; idx <= count; ++idx)
+ vals.put(idx, new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(idx));
+
+ cache.putAll(vals);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key + 6, idxVal1 + 6 FROM MvccTestSqlIndexValue";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ qry.setDistributedJoins(true);
+ qry.setPageSize(1);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals((long)count, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerDelete() throws Exception {
+ ccfgs = new CacheConfiguration[] {
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setName("int")
+ .setIndexedTypes(Integer.class, Integer.class),
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class,
+ CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class),
+ };
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, Integer> cache = checkNode.cache("int");
+
+ cache.putAll(F.asMap(1, 1, 3, 3, 5, 5));
+
+ final int count = 6;
+
+ Map<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> vals = new HashMap<>(count);
+
+ for (int idx = 1; idx <= count; ++idx)
+ vals.put(idx, new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(idx));
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ cache0.putAll(vals);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "DELETE FROM MvccTestSqlIndexValue t " +
+ "WHERE EXISTS (SELECT 1 FROM \"int\".Integer WHERE t._key = _key)";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerUpdate() throws Exception {
+ ccfgs = new CacheConfiguration[] {
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setName("int")
+ .setIndexedTypes(Integer.class, Integer.class),
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class,
+ CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class),
+ };
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, Integer> cache = checkNode.cache("int");
+
+ cache.putAll(F.asMap(1, 5, 3, 1, 5, 3));
+
+ final int count = 6;
+
+ Map<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> vals = new HashMap<>(count);
+
+ for (int idx = 1; idx <= count; ++idx)
+ vals.put(idx, new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(idx));
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ cache0.putAll(vals);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "UPDATE MvccTestSqlIndexValue t SET idxVal1=" +
+ "(SELECT _val FROM \"int\".Integer WHERE t._key = _key)" +
+ " WHERE EXISTS (SELECT 1 FROM \"int\".Integer WHERE t._key = _key)";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerImplicitTxInsert() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache =
+ checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1),
+ 2,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2),
+ 3,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3)));
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ String sqlText = "INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key + 3, idxVal1 + 3 FROM MvccTestSqlIndexValue";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ qry.setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ qry.setDistributedJoins(true);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(4), cache.get(4));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(5), cache.get(5));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(6), cache.get(6));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerRollbackInsert() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Integer, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache =
+ checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1),
+ 2,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2),
+ 3,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3)));
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key + 3, idxVal1 + 3 FROM MvccTestSqlIndexValue";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sqlText);
+
+ qry.setDistributedJoins(true);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.rollback();
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), sqlGet(1, cache).get(0).get(0));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), sqlGet(2, cache).get(0).get(0));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), sqlGet(3, cache).get(0).get(0));
+ assertTrue(sqlGet(4, cache).isEmpty());
+ assertTrue(sqlGet(5, cache).isEmpty());
+ assertTrue(sqlGet(6, cache).isEmpty());
+ }
+
+ /**
+ * @param key Key.
+ * @param cache Cache.
+ * @return Result.
+ */
+ private List<List> sqlGet(int key, IgniteCache cache) {
+ return cache.query(new SqlFieldsQuery("SELECT _val from MvccTestSqlIndexValue WHERE _key=" + key)).getAll();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerDeadlockInsert() throws Exception {
+ ccfgs = new CacheConfiguration[] {
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setName("int")
+ .setIndexedTypes(Integer.class, Integer.class),
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class,
+ CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class),
+ };
+
+ startGridsMultiThreaded(2);
+
+ client = true;
+
+ startGridsMultiThreaded(2, 2);
+
+ Ignite checkNode = grid(2);
+
+ IgniteCache<Integer, Integer> cache = checkNode.cache("int");
+
+ HashMap<Integer, Integer> vals = new HashMap<>(100);
+
+ for (int idx = 0; idx < 100; ++idx)
+ vals.put(idx, idx);
+
+ cache.putAll(vals);
+
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+ final AtomicInteger idx = new AtomicInteger(2);
+ final AtomicReference<Exception> ex = new AtomicReference<>();
+
+ multithreaded(new Runnable() {
+ @Override public void run() {
+ int id = idx.getAndIncrement();
+
+ IgniteEx node = grid(id);
+
+ try {
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TIMEOUT);
+
+ String sqlText = "INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT DISTINCT _key, _val FROM \"int\".Integer ORDER BY _key";
+
+ String sqlAsc = sqlText + " ASC";
+ String sqlDesc = sqlText + " DESC";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery((id % 2) == 0 ? sqlAsc : sqlDesc);
+
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ cache0.query(qry).getAll();
+
+ barrier.await();
+
+ qry = new SqlFieldsQuery((id % 2) == 0 ? sqlDesc : sqlAsc);
+
+ cache0.query(qry).getAll();
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ }
+ }, 2);
+
+ Exception ex0 = ex.get();
+
+ assertNotNull(ex0);
+
+ if (!X.hasCause(ex0, IgniteTxTimeoutCheckedException.class))
+ throw ex0;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerInsertVersionConflict() throws Exception {
+ ccfgs = new CacheConfiguration[] {
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setName("int")
+ .setIndexedTypes(Integer.class, Integer.class),
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class,
+ CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class),
+ };
+
+ startGridsMultiThreaded(2);
+
+ client = true;
+
+ final Ignite checkNode = startGrid(2);
+
+ IgniteCache<Integer, Integer> cache = checkNode.cache("int");
+
+ HashMap<Integer, Integer> vals = new HashMap<>(100);
+
+ for (int idx = 0; idx < 10; ++idx)
+ vals.put(idx, idx);
+
+ cache.putAll(vals);
+
+ awaitPartitionMapExchange();
+
+ IgniteCache cache0 = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache0.query(new SqlFieldsQuery("INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) " +
+ "SELECT _key, _val FROM \"int\".Integer")).getAll();
+
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+ final AtomicReference<Exception> ex = new AtomicReference<>();
+
+ runMultiThreaded(new Runnable() {
+ @Override public void run() {
+ try {
+ try (Transaction tx = checkNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ barrier.await();
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("SELECT * FROM MvccTestSqlIndexValue");
+
+ cache0.query(qry).getAll();
+
+ barrier.await();
+
+ String sqlText = "UPDATE MvccTestSqlIndexValue t SET idxVal1=" +
+ "(SELECT _val FROM \"int\".Integer WHERE t._key = _key ORDER BY _key)";
+
+ qry = new SqlFieldsQuery(sqlText);
+
+ cache0.query(qry).getAll();
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ }
+ }, 2, "tx-thread");
+
+ IgniteSQLException ex0 = X.cause(ex.get(), IgniteSQLException.class);
+
+ assertNotNull("Exception has not been thrown.", ex0);
+ assertEquals("Mvcc version mismatch.", ex0.getMessage());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerInsertValues() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite node = grid(rnd.nextInt(4));
+
+ IgniteCache<Object, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO MvccTestSqlIndexValue (_key, idxVal1)" +
+ " values (1,?),(2,?),(3,?)");
+
+ qry.setArgs(1, 2, 3);
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ qry = new SqlFieldsQuery("INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) values (4,4)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(4), cache.get(4));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerMergeValues() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite node = grid(rnd.nextInt(4));
+
+ IgniteCache<Object, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ cache.put(1, new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1));
+ cache.put(3, new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3));
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("MERGE INTO MvccTestSqlIndexValue (_key, idxVal1)" +
+ " values (1,?),(2,?),(3,?)");
+
+ qry.setArgs(1, 4, 6);
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(4), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(6), cache.get(3));
+
+ qry = new SqlFieldsQuery("MERGE INTO MvccTestSqlIndexValue (_key, idxVal1) values (4,4)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(4), cache.get(4));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerFastUpdate() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Object, Object> cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1,1,2,2,3,3));
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE Integer SET _val = 8 WHERE _key = ?").setArgs(1);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ qry = new SqlFieldsQuery("UPDATE Integer SET _val = 9 WHERE _key = 2");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(8, cache.get(1));
+ assertEquals(9, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryReducerFastDelete() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache<Object, Object> cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1),
+ 2,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2),
+ 3,new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3)));
+
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("DELETE FROM MvccTestSqlIndexValue WHERE _key = ?")
+ .setArgs(1);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ qry = new SqlFieldsQuery("DELETE FROM MvccTestSqlIndexValue WHERE _key = 2");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ assertNull(cache.get(1));
+ assertNull(cache.get(2));
+ assertEquals(new CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue(3), cache.get(3));
+ }
+
+ /**
+ * @param ex Exception holder.
+ * @param e Exception.
+ */
+ private void onException(AtomicReference<Exception> ex, Exception e) {
+ if (!ex.compareAndSet(null, e))
+ ex.get().addSuppressed(e);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccStreamingInsertTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccStreamingInsertTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccStreamingInsertTest.java
new file mode 100644
index 0000000..7d2e335
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccStreamingInsertTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.mvcc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteJdbcDriver;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+
+import static java.util.Arrays.asList;
+
+/**
+ *
+ */
+public class CacheMvccStreamingInsertTest extends CacheMvccAbstractTest {
+ /** */
+ private IgniteCache<Object, Object> sqlNexus;
+
+ /** */
+ private Connection conn;
+
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ Ignite ignite = startGrid(0);
+ sqlNexus = ignite.getOrCreateCache(new CacheConfiguration<>("sqlNexus").setSqlSchema("PUBLIC"));
+ sqlNexus.query(q("" +
+ "create table person(" +
+ " id int not null primary key," +
+ " name varchar not null" +
+ ") with \"atomicity=transactional\""
+ ));
+
+ Properties props = new Properties();
+ props.setProperty(IgniteJdbcDriver.PROP_STREAMING, "true");
+ conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1", props);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testStreamingInsertWithoutOverwrite() throws Exception {
+ conn.createStatement().execute("SET STREAMING 1 BATCH_SIZE 2 ALLOW_OVERWRITE 0 " +
+ " PER_NODE_BUFFER_SIZE 1000 FLUSH_FREQUENCY 100");
+ sqlNexus.query(q("insert into person values(1, 'ivan')"));
+
+ PreparedStatement batchStmt = conn.prepareStatement("insert into person values(?, ?)");
+ batchStmt.setInt(1, 1);
+ batchStmt.setString(2, "foo");
+ batchStmt.addBatch();
+ batchStmt.setInt(1, 2);
+ batchStmt.setString(2, "bar");
+ batchStmt.addBatch();
+ TimeUnit.MILLISECONDS.sleep(500);
+
+ List<List<?>> rows = sqlNexus.query(q("select * from person")).getAll();
+ List<List<?>> exp = asList(
+ asList(1, "ivan"),
+ asList(2, "bar")
+ );
+ assertEquals(exp, rows);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdateWithOverwrite() throws Exception {
+ conn.createStatement().execute("SET STREAMING 1 BATCH_SIZE 2 ALLOW_OVERWRITE 1 " +
+ " PER_NODE_BUFFER_SIZE 1000 FLUSH_FREQUENCY 100");
+ sqlNexus.query(q("insert into person values(1, 'ivan')"));
+
+ PreparedStatement batchStmt = conn.prepareStatement("insert into person values(?, ?)");
+ batchStmt.setInt(1, 1);
+ batchStmt.setString(2, "foo");
+ batchStmt.addBatch();
+ batchStmt.setInt(1, 2);
+ batchStmt.setString(2, "bar");
+ batchStmt.addBatch();
+ TimeUnit.MILLISECONDS.sleep(500);
+
+ List<List<?>> rows = sqlNexus.query(q("select * from person")).getAll();
+ List<List<?>> exp = asList(
+ asList(1, "foo"),
+ asList(2, "bar")
+ );
+ assertEquals(exp, rows);
+ }
+
+ /** */
+ private static SqlFieldsQuery q(String sql) {
+ return new SqlFieldsQuery(sql);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
new file mode 100644
index 0000000..57cee61
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
@@ -0,0 +1,350 @@
+/*
+ * 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.query.h2;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.index.DynamicIndexAbstractSelfTest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorImpl;
+import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiTuple;
+
+/**
+ * Index rebuild after node restart test.
+ */
+public class GridIndexRebuildSelfTest extends DynamicIndexAbstractSelfTest {
+ /** Data size. */
+ private final static int AMOUNT = 10;
+
+ /** Data size. */
+ private final static String CACHE_NAME = "T";
+
+ /** Test instance to allow interaction with static context. */
+ private static GridIndexRebuildSelfTest INSTANCE;
+
+ /** Latch to signal that rebuild may start. */
+ private final CountDownLatch rebuildLatch = new CountDownLatch(1);
+
+ /** Latch to signal that concurrent put may start. */
+ private final Semaphore rebuildSemaphore = new Semaphore(1, true);
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration commonConfiguration(int idx) throws Exception {
+ IgniteConfiguration cfg = super.commonConfiguration(idx);
+
+ cfg.getDataStorageConfiguration().getDefaultDataRegionConfiguration().setPersistenceEnabled(true);
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ // Just in case.
+ cleanPersistenceDir();
+
+ INSTANCE = this;
+ }
+
+ /**
+ * Do test with MVCC enabled.
+ */
+ public void testMvccEnabled() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-7259");
+ doTest(true);
+ }
+
+ /**
+ * Do test with MVCC disabled.
+ */
+ public void testMvccDisabled() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-7259");
+ doTest(false);
+ }
+
+ /**
+ * Do test.<p>
+ * Steps are as follows:
+ * <ul>
+ * <li>Put some data;</li>
+ * <li>Stop the node;</li>
+ * <li>Remove index file;</li>
+ * <li>Restart the node and block index rebuild;</li>
+ * <li>For half of the keys do cache puts <b>before</b> corresponding key
+ * has been processed during index rebuild;</li>
+ * <li>Check that:
+ * <ul>
+ * <li>For MVCC case: some keys have all versions that existed before restart, while those
+ * updated concurrently have only put version (one with mark value -1)
+ * and latest version present before node restart;</li>
+ * <li>For non MVCC case: keys updated concurrently must have mark values of -1 despite that
+ * index rebuild for them has happened after put.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * @param mvccEnabled MVCC flag.
+ * @throws Exception if failed.
+ */
+ private void doTest(boolean mvccEnabled) throws Exception {
+ IgniteEx srv = startServer(mvccEnabled);
+
+ execute(srv, "CREATE TABLE T(k int primary key, v int) WITH \"cache_name=T,wrap_value=false," +
+ "atomicity=transactional\"");
+
+ execute(srv, "CREATE INDEX IDX ON T(v)");
+
+ IgniteInternalCache cc = srv.cachex(CACHE_NAME);
+
+ assertNotNull(cc);
+
+ if (mvccEnabled)
+ lockVersion(srv);
+
+ putData(srv, false);
+
+ checkDataState(srv, mvccEnabled, false);
+
+ File cacheWorkDir = ((FilePageStoreManager)cc.context().shared().pageStore()).cacheWorkDir(cc.configuration());
+
+ File idxPath = cacheWorkDir.toPath().resolve("index.bin").toFile();
+
+ stopAllGrids();
+
+ assertTrue(U.delete(idxPath));
+
+ srv = startServer(mvccEnabled);
+
+ putData(srv, true);
+
+ checkDataState(srv, mvccEnabled, true);
+ }
+
+ /**
+ * Check versions presence in index tree.
+ * @param srv Node.
+ * @param mvccEnabled MVCC flag.
+ * @param afterRebuild Whether index rebuild has occurred.
+ * @throws IgniteCheckedException if failed.
+ */
+ @SuppressWarnings({"ConstantConditions", "unchecked"})
+ private void checkDataState(IgniteEx srv, boolean mvccEnabled, boolean afterRebuild) throws IgniteCheckedException {
+ IgniteInternalCache icache = srv.cachex(CACHE_NAME);
+
+ IgniteCache cache = srv.cache(CACHE_NAME);
+
+ assertNotNull(icache);
+
+ for (IgniteCacheOffheapManager.CacheDataStore store : icache.context().offheap().cacheDataStores()) {
+ GridCursor<? extends CacheDataRow> cur = store.cursor();
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ int key = row.key().value(icache.context().cacheObjectContext(), false);
+
+ if (mvccEnabled) {
+ List<IgniteBiTuple<Object, MvccVersion>> vers = store.mvccFindAllVersions(icache.context(), row.key());
+
+ if (!afterRebuild || key <= AMOUNT / 2)
+ assertEquals(key, vers.size());
+ else {
+ // For keys affected by concurrent put there are two versions -
+ // -1 (concurrent put mark) and newest restored value as long as put cleans obsolete versions.
+ assertEquals(2, vers.size());
+
+ assertEquals(-1, vers.get(0).getKey());
+ assertEquals(key, vers.get(1).getKey());
+ }
+ }
+ else {
+ if (!afterRebuild || key <= AMOUNT / 2)
+ assertEquals(key, cache.get(key));
+ else
+ assertEquals(-1, cache.get(key));
+ }
+ }
+ }
+ }
+
+ /**
+ * Lock coordinator version in order to keep MVCC versions in place.
+ * @param node Node.
+ * @throws IgniteCheckedException if failed.
+ */
+ private static void lockVersion(IgniteEx node) throws IgniteCheckedException {
+ node.context().coordinators().requestSnapshotAsync().get();
+ }
+
+ /**
+ * Put data to cache.
+ * @param node Node.
+ * @throws Exception if failed.
+ */
+ private void putData(Ignite node, final boolean forConcurrentPut) throws Exception {
+ final IgniteCache<Integer, Integer> cache = node.cache(CACHE_NAME);
+
+ assertNotNull(cache);
+
+ for (int i = 1; i <= AMOUNT; i++) {
+ if (forConcurrentPut) {
+ // Concurrent put affects only second half of the keys.
+ if (i <= AMOUNT / 2)
+ continue;
+
+ rebuildSemaphore.acquire();
+
+ cache.put(i, -1);
+
+ rebuildLatch.countDown();
+
+ rebuildSemaphore.release();
+ }
+ else {
+ // Data streamer is not used intentionally in order to preserve all versions.
+ for (int j = 1; j <= i; j++)
+ cache.put(i, j);
+ }
+ }
+ }
+
+ /**
+ * Start server node.
+ * @param mvccEnabled MVCC flag.
+ * @return Started node.
+ * @throws Exception if failed.
+ */
+ private IgniteEx startServer(boolean mvccEnabled) throws Exception {
+ // Have to do this for each starting node - see GridQueryProcessor ctor, it nulls
+ // idxCls static field on each call.
+ GridQueryProcessor.idxCls = BlockingIndexing.class;
+
+ IgniteConfiguration cfg = serverConfiguration(0).setMvccEnabled(mvccEnabled);
+
+ IgniteEx res = startGrid(cfg);
+
+ res.active(true);
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+
+ stopAllGrids();
+
+ cleanPersistenceDir();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ super.afterTestsStopped();
+
+ cleanPersistenceDir();
+ }
+
+ /**
+ * Blocking indexing processor.
+ */
+ private static class BlockingIndexing extends IgniteH2Indexing {
+ /** Flag to ignore first rebuild performed on initial node start. */
+ private boolean firstRbld = true;
+
+ /** {@inheritDoc} */
+ @Override public void rebuildIndexesFromHash(String cacheName) throws IgniteCheckedException {
+ if (!firstRbld)
+ U.await(INSTANCE.rebuildLatch);
+ else
+ firstRbld = false;
+
+ int cacheId = CU.cacheId(cacheName);
+
+ GridCacheContext cctx = ctx.cache().context().cacheContext(cacheId);
+
+ final GridCacheQueryManager qryMgr = cctx.queries();
+
+ SchemaIndexCacheVisitor visitor = new SchemaIndexCacheVisitorImpl(cctx);
+
+ visitor.visit(new TestRebuildClosure(qryMgr, cctx.mvccEnabled()));
+
+ for (H2TableDescriptor tblDesc : tables(cacheName))
+ tblDesc.table().markRebuildFromHashInProgress(false);
+ }
+ }
+
+ /**
+ * Test closure.
+ */
+ private final static class TestRebuildClosure extends RebuildIndexFromHashClosure {
+ /** Seen keys set to track moment when concurrent put may start. */
+ private final Set<KeyCacheObject> keys =
+ Collections.newSetFromMap(new ConcurrentHashMap<KeyCacheObject, Boolean>());
+
+ /**
+ * @param qryMgr Query manager.
+ * @param mvccEnabled MVCC status flag.
+ */
+ TestRebuildClosure(GridCacheQueryManager qryMgr, boolean mvccEnabled) {
+ super(qryMgr, mvccEnabled);
+ }
+
+ /** {@inheritDoc} */
+ @Override public synchronized void apply(CacheDataRow row) throws IgniteCheckedException {
+ // For half of the keys, we want to do rebuild
+ // after corresponding key had been put from a concurrent thread.
+ boolean keyFirstMet = keys.add(row.key()) && keys.size() > AMOUNT / 2;
+
+ if (keyFirstMet) {
+ try {
+ INSTANCE.rebuildSemaphore.acquire();
+ }
+ catch (InterruptedException e) {
+ throw new IgniteCheckedException(e);
+ }
+ }
+
+ super.apply(row);
+
+ if (keyFirstMet)
+ INSTANCE.rebuildSemaphore.release();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
index 235b28b..6b76230 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java
@@ -343,7 +343,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
// Fields query
GridQueryFieldsResult fieldsRes =
spi.queryLocalSqlFields(spi.schema("A"), "select a.a.name n1, a.a.age a1, b.a.name n2, " +
- "b.a.age a2 from a.a, b.a where a.a.id = b.a.id ", Collections.emptySet(), null, false, 0, null);
+ "b.a.age a2 from a.a, b.a where a.a.id = b.a.id ", Collections.emptySet(), null, false, false, 0, null);
String[] aliases = {"N1", "A1", "N2", "A2"};
Object[] vals = { "Valera", 19, "Kolya", 25};
@@ -401,7 +401,7 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract
range *= 3;
GridQueryFieldsResult res = spi.queryLocalSqlFields(spi.schema("A"), sql, Arrays.<Object>asList(1,
- range), null, false, 0, null);
+ range), null, false, false, 0, null);
assert res.iterator().hasNext();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2StatementCacheSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2StatementCacheSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2StatementCacheSelfTest.java
new file mode 100644
index 0000000..655d039
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/H2StatementCacheSelfTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.query.h2;
+
+import java.sql.PreparedStatement;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class H2StatementCacheSelfTest extends GridCommonAbstractTest {
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testEviction() throws Exception {
+ H2StatementCache stmtCache = new H2StatementCache(1);
+ H2CachedStatementKey key1 = new H2CachedStatementKey("", "1");
+ PreparedStatement stmt1 = stmt();
+ stmtCache.put(key1, stmt1);
+
+ assertSame(stmt1, stmtCache.get(key1));
+
+ stmtCache.put(new H2CachedStatementKey("mydb", "2"), stmt());
+
+ assertNull(stmtCache.get(key1));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testLruEvictionInStoreOrder() throws Exception {
+ H2StatementCache stmtCache = new H2StatementCache(2);
+
+ H2CachedStatementKey key1 = new H2CachedStatementKey("", "1");
+ H2CachedStatementKey key2 = new H2CachedStatementKey("", "2");
+ stmtCache.put(key1, stmt());
+ stmtCache.put(key2, stmt());
+
+ stmtCache.put(new H2CachedStatementKey("", "3"), stmt());
+
+ assertNull(stmtCache.get(key1));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testLruEvictionInAccessOrder() throws Exception {
+ H2StatementCache stmtCache = new H2StatementCache(2);
+
+ H2CachedStatementKey key1 = new H2CachedStatementKey("", "1");
+ H2CachedStatementKey key2 = new H2CachedStatementKey("", "2");
+ stmtCache.put(key1, stmt());
+ stmtCache.put(key2, stmt());
+ stmtCache.get(key1);
+
+ stmtCache.put(new H2CachedStatementKey("", "3"), stmt());
+
+ assertNull(stmtCache.get(key2));
+ }
+
+ /**
+ *
+ */
+ private static PreparedStatement stmt() {
+ return new PreparedStatementExImpl(null);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExSelfTest.java
new file mode 100644
index 0000000..22bff3b
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExSelfTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.query.h2;
+
+import java.sql.PreparedStatement;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class PreparedStatementExSelfTest extends GridCommonAbstractTest {
+ /**
+ * @throws Exception If failed.
+ */
+ public void testStoringMeta() throws Exception {
+ PreparedStatement stmt = stmt();
+
+ PreparedStatementEx wrapped = stmt.unwrap(PreparedStatementEx.class);
+
+ wrapped.putMeta(0, "0");
+
+ assertEquals("0", wrapped.meta(0));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testStoringMoreMetaKeepsExisting() throws Exception {
+ PreparedStatement stmt = stmt();
+
+ PreparedStatementEx wrapped = stmt.unwrap(PreparedStatementEx.class);
+
+ wrapped.putMeta(0, "0");
+ wrapped.putMeta(1, "1");
+
+ assertEquals("0", wrapped.meta(0));
+ assertEquals("1", wrapped.meta(1));
+ }
+
+ /**
+ *
+ */
+ private static PreparedStatement stmt() {
+ return new PreparedStatementExImpl(null);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPoolSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPoolSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPoolSelfTest.java
new file mode 100644
index 0000000..b7b7a37
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPoolSelfTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.query.h2;
+
+import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.internal.processors.query.h2.ThreadLocalObjectPool.Reusable;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class ThreadLocalObjectPoolSelfTest extends GridCommonAbstractTest {
+ /** */
+ private ThreadLocalObjectPool<Obj> pool = new ThreadLocalObjectPool<>(Obj::new, 1);
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testObjectIsReusedAfterRecycling() throws Exception {
+ Reusable<Obj> o1 = pool.borrow();
+ o1.recycle();
+ Reusable<Obj> o2 = pool.borrow();
+
+ assertSame(o1.object(), o2.object());
+ assertFalse(o1.object().isClosed());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testBorrowedObjectIsNotReturnedTwice() throws Exception {
+ Reusable<Obj> o1 = pool.borrow();
+ Reusable<Obj> o2 = pool.borrow();
+
+ assertNotSame(o1.object(), o2.object());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testObjectShouldBeClosedOnRecycleIfPoolIsFull() throws Exception {
+ Reusable<Obj> o1 = pool.borrow();
+ Reusable<Obj> o2 = pool.borrow();
+ o1.recycle();
+ o2.recycle();
+
+ assertTrue(o2.object().isClosed());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testObjectShouldNotBeReturnedIfPoolIsFull() throws Exception {
+ Reusable<Obj> o1 = pool.borrow();
+ Reusable<Obj> o2 = pool.borrow();
+
+ o1.recycle();
+
+ assertEquals(1, pool.bagSize());
+
+ o2.recycle();
+
+ assertEquals(1, pool.bagSize());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testObjectShouldReturnedToRecyclingThreadBag() throws Exception {
+ Reusable<Obj> o1 = pool.borrow();
+
+ CompletableFuture.runAsync(() -> {
+ o1.recycle();
+
+ assertEquals(1, pool.bagSize());
+ }).join();
+
+ assertEquals(0, pool.bagSize());
+ }
+
+ /** */
+ private static class Obj implements AutoCloseable {
+ /** */
+ private boolean closed = false;
+
+ /** {@inheritDoc} */
+ @Override public void close() {
+ closed = true;
+ }
+
+ /**
+ * @return {@code True} if closed.
+ */
+ public boolean isClosed() {
+ return closed;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
index a362586..de77150 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/GridQueryParsingTest.java
@@ -205,6 +205,7 @@ public class GridQueryParsingTest extends GridCommonAbstractTest {
checkQuery("select * from Person");
checkQuery("select distinct * from Person");
checkQuery("select p.name, date from Person p");
+ checkQuery("select p.name, date from Person p for update");
checkQuery("select * from Person p, sch2.Address a");
checkQuery("select * from Person, sch2.Address");
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java
new file mode 100644
index 0000000..a8087da
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccSqlTestSuite.java
@@ -0,0 +1,73 @@
+/*
+ * 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.testsuites;
+
+import junit.framework.TestSuite;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccBulkLoadTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccDmlSimpleTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccIteratorWithConcurrentJdbcTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedBackupsTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedSelectForUpdateQueryTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedSqlCoordinatorFailoverTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedSqlQueriesTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedSqlTxQueriesTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccPartitionedSqlTxQueriesWithReducerTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedBackupsTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedSelectForUpdateQueryTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedSqlCoordinatorFailoverTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedSqlQueriesTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedSqlTxQueriesTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccReplicatedSqlTxQueriesWithReducerTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccScanQueryWithConcurrentJdbcTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccSizeWithConcurrentJdbcTransactionTest;
+import org.apache.ignite.internal.processors.cache.mvcc.CacheMvccStreamingInsertTest;
+
+/**
+ *
+ */
+public class IgniteCacheMvccSqlTestSuite extends TestSuite {
+ /**
+ * @return Test suite.
+ */
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite("IgniteCache SQL MVCC Test Suite");
+
+ suite.addTestSuite(CacheMvccSizeWithConcurrentJdbcTransactionTest.class);
+ suite.addTestSuite(CacheMvccScanQueryWithConcurrentJdbcTransactionTest.class);
+ suite.addTestSuite(CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest.class);
+ suite.addTestSuite(CacheMvccIteratorWithConcurrentJdbcTransactionTest.class);
+ suite.addTestSuite(CacheMvccPartitionedSqlQueriesTest.class);
+ suite.addTestSuite(CacheMvccReplicatedSqlQueriesTest.class);
+ suite.addTestSuite(CacheMvccPartitionedSqlTxQueriesTest.class);
+ suite.addTestSuite(CacheMvccReplicatedSqlTxQueriesTest.class);
+ suite.addTestSuite(CacheMvccPartitionedSqlTxQueriesWithReducerTest.class);
+ suite.addTestSuite(CacheMvccReplicatedSqlTxQueriesWithReducerTest.class);
+ suite.addTestSuite(CacheMvccPartitionedSelectForUpdateQueryTest.class);
+ suite.addTestSuite(CacheMvccReplicatedSelectForUpdateQueryTest.class);
+ suite.addTestSuite(CacheMvccPartitionedBackupsTest.class);
+ suite.addTestSuite(CacheMvccReplicatedBackupsTest.class);
+ suite.addTestSuite(CacheMvccPartitionedSqlCoordinatorFailoverTest.class);
+ suite.addTestSuite(CacheMvccReplicatedSqlCoordinatorFailoverTest.class);
+ suite.addTestSuite(CacheMvccBulkLoadTest.class);
+ suite.addTestSuite(CacheMvccStreamingInsertTest.class);
+ suite.addTestSuite(CacheMvccDmlSimpleTest.class);
+
+ return suite;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
index 2a61ce6..b0670e8 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.cache.CacheReplicatedQueryDetailMet
import org.apache.ignite.internal.processors.cache.CacheReplicatedQueryMetricsDistributedSelfTest;
import org.apache.ignite.internal.processors.cache.CacheReplicatedQueryMetricsLocalSelfTest;
import org.apache.ignite.internal.processors.cache.CacheSqlQueryValueCopySelfTest;
+import org.apache.ignite.internal.processors.cache.DdlTransactionSelfTest;
import org.apache.ignite.internal.processors.cache.GridCacheCrossCacheQuerySelfTest;
import org.apache.ignite.internal.processors.cache.GridCacheFullTextQuerySelfTest;
import org.apache.ignite.internal.processors.cache.GridCacheLazyQueryPartitionsReleaseTest;
@@ -133,6 +134,8 @@ import org.apache.ignite.internal.processors.cache.index.H2RowCacheSelfTest;
import org.apache.ignite.internal.processors.cache.index.LongIndexNameTest;
import org.apache.ignite.internal.processors.cache.index.OptimizedMarshallerIndexNameTest;
import org.apache.ignite.internal.processors.cache.index.SchemaExchangeSelfTest;
+import org.apache.ignite.internal.processors.cache.index.SqlTransactionsComandsWithMvccDisabledSelfTest;
+import org.apache.ignite.internal.processors.cache.index.SqlTransactionsSelfTest;
import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalAtomicQuerySelfTest;
import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalFieldsQuerySelfTest;
import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalQueryCancelOrTimeoutSelfTest;
@@ -167,9 +170,13 @@ import org.apache.ignite.internal.processors.query.SqlPushDownFunctionTest;
import org.apache.ignite.internal.processors.query.SqlSchemaSelfTest;
import org.apache.ignite.internal.processors.query.h2.GridH2IndexingInMemSelfTest;
import org.apache.ignite.internal.processors.query.h2.GridH2IndexingOffheapSelfTest;
+import org.apache.ignite.internal.processors.query.h2.GridIndexRebuildSelfTest;
+import org.apache.ignite.internal.processors.query.h2.H2StatementCacheSelfTest;
import org.apache.ignite.internal.processors.query.h2.H2ResultSetIteratorNullifyOnEndSelfTest;
import org.apache.ignite.internal.processors.query.h2.IgniteSqlBigIntegerKeyTest;
import org.apache.ignite.internal.processors.query.h2.IgniteSqlQueryMinMaxTest;
+import org.apache.ignite.internal.processors.query.h2.ThreadLocalObjectPoolSelfTest;
+import org.apache.ignite.internal.processors.query.h2.PreparedStatementExSelfTest;
import org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest;
import org.apache.ignite.internal.processors.query.h2.sql.GridQueryParsingTest;
import org.apache.ignite.internal.processors.query.h2.sql.H2CompareBigQueryDistributedJoinsTest;
@@ -184,6 +191,7 @@ import org.apache.ignite.internal.sql.SqlParserBulkLoadSelfTest;
import org.apache.ignite.internal.sql.SqlParserCreateIndexSelfTest;
import org.apache.ignite.internal.sql.SqlParserDropIndexSelfTest;
import org.apache.ignite.internal.sql.SqlParserSetStreamingSelfTest;
+import org.apache.ignite.internal.sql.SqlParserTransactionalKeywordsSelfTest;
import org.apache.ignite.internal.sql.SqlParserUserSelfTest;
import org.apache.ignite.spi.communication.tcp.GridOrderedMessageCancelSelfTest;
import org.apache.ignite.sqltests.PartitionedSqlTest;
@@ -206,6 +214,7 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(SqlParserCreateIndexSelfTest.class);
suite.addTestSuite(SqlParserDropIndexSelfTest.class);
+ suite.addTestSuite(SqlParserTransactionalKeywordsSelfTest.class);
suite.addTestSuite(SqlParserBulkLoadSelfTest.class);
suite.addTestSuite(SqlParserSetStreamingSelfTest.class);
@@ -354,6 +363,8 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
//suite.addTestSuite(H2DynamicIndexingComplexServerTransactionalPartitionedNoBackupsTest.class);
suite.addTestSuite(H2DynamicIndexingComplexServerTransactionalReplicatedTest.class);
+ suite.addTestSuite(DdlTransactionSelfTest.class);
+
// Fields queries.
suite.addTestSuite(SqlFieldsQuerySelfTest.class);
suite.addTestSuite(IgniteCacheLocalFieldsQuerySelfTest.class);
@@ -434,6 +445,11 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(OptimizedMarshallerIndexNameTest.class);
suite.addTestSuite(SqlSystemViewsSelfTest.class);
+ suite.addTestSuite(GridIndexRebuildSelfTest.class);
+
+ suite.addTestSuite(SqlTransactionsSelfTest.class);
+ suite.addTestSuite(SqlTransactionsComandsWithMvccDisabledSelfTest.class);
+
suite.addTestSuite(IgniteSqlDefaultValueTest.class);
suite.addTestSuite(IgniteDecimalSelfTest.class);
suite.addTestSuite(IgniteSQLColumnConstraintsTest.class);
@@ -451,6 +467,10 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(SqlParserUserSelfTest.class);
suite.addTestSuite(SqlUserCommandSelfTest.class);
+ suite.addTestSuite(ThreadLocalObjectPoolSelfTest.class);
+ suite.addTestSuite(H2StatementCacheSelfTest.class);
+ suite.addTestSuite(PreparedStatementExSelfTest.class);
+
return suite;
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person.csv
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person.csv b/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person.csv
new file mode 100644
index 0000000..ef7a087
--- /dev/null
+++ b/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person.csv
@@ -0,0 +1,2 @@
+1,John
+2,Jack
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person_broken.csv
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person_broken.csv b/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person_broken.csv
new file mode 100644
index 0000000..b5c2b3f
--- /dev/null
+++ b/modules/indexing/src/test/resources/org/apache/ignite/internal/processors/cache/mvcc/mvcc_person_broken.csv
@@ -0,0 +1,2 @@
+1,John
+2
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaManager.java
----------------------------------------------------------------------
diff --git a/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaManager.java b/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaManager.java
index 8dcdd57..88c86b2 100644
--- a/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaManager.java
+++ b/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaManager.java
@@ -167,6 +167,7 @@ public class CacheJtaManager extends CacheJtaManagerAdapter {
tCfg.getDefaultTxIsolation(),
tCfg.getDefaultTxTimeout(),
/*store enabled*/true,
+ /*sql*/false,
/*tx size*/0,
null
);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/Makefile.am
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/Makefile.am b/modules/platforms/cpp/odbc-test/Makefile.am
index f412403..14dae7c 100644
--- a/modules/platforms/cpp/odbc-test/Makefile.am
+++ b/modules/platforms/cpp/odbc-test/Makefile.am
@@ -85,6 +85,7 @@ ignite_odbc_tests_SOURCES = \
src/errors_test.cpp \
src/odbc_test_suite.cpp \
src/types_test.cpp \
+ src/transaction_test.cpp \
src/authentication_test.cpp \
../odbc/src/log.cpp \
../odbc/src/cursor.cpp \
@@ -101,7 +102,8 @@ ignite_odbc_tests_SOURCES = \
../odbc/src/column.cpp \
../odbc/src/common_types.cpp \
../odbc/src/utility.cpp \
- ../odbc/src/result_page.cpp
+ ../odbc/src/result_page.cpp \
+ ../odbc/src/nested_tx_mode.cpp
run-check: check
./ignite-odbc-tests -p
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/config/queries-transaction-32.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/config/queries-transaction-32.xml b/modules/platforms/cpp/odbc-test/config/queries-transaction-32.xml
new file mode 100644
index 0000000..94ddd3d
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/config/queries-transaction-32.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <import resource="queries-default.xml"/>
+
+ <bean parent="queries.cfg">
+ <property name="mvccEnabled" value="true"/>
+
+ <property name="memoryConfiguration">
+ <bean class="org.apache.ignite.configuration.MemoryConfiguration">
+ <property name="systemCacheInitialSize" value="#{10 * 1024 * 1024}"/>
+ <property name="systemCacheMaxSize" value="#{40 * 1024 * 1024}"/>
+ <property name="defaultMemoryPolicyName" value="dfltPlc"/>
+
+ <property name="memoryPolicies">
+ <list>
+ <bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration">
+ <property name="name" value="dfltPlc"/>
+ <property name="maxSize" value="#{100 * 1024 * 1024}"/>
+ <property name="initialSize" value="#{10 * 1024 * 1024}"/>
+ </bean>
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</beans>
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/config/queries-transaction.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/config/queries-transaction.xml b/modules/platforms/cpp/odbc-test/config/queries-transaction.xml
new file mode 100644
index 0000000..7d74fc9
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/config/queries-transaction.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <import resource="queries-default.xml"/>
+
+ <bean parent="queries.cfg">
+ <property name="mvccEnabled" value="true"/>
+ </bean>
+</beans>
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
index 87b6559..3410ec5 100644
--- a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
+++ b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj
@@ -164,6 +164,7 @@
<ClCompile Include="..\..\..\odbc\src\diagnostic\diagnostic_record.cpp" />
<ClCompile Include="..\..\..\odbc\src\diagnostic\diagnostic_record_storage.cpp" />
<ClCompile Include="..\..\..\odbc\src\log.cpp" />
+ <ClCompile Include="..\..\..\odbc\src\nested_tx_mode.cpp" />
<ClCompile Include="..\..\..\odbc\src\protocol_version.cpp" />
<ClCompile Include="..\..\..\odbc\src\result_page.cpp" />
<ClCompile Include="..\..\..\odbc\src\row.cpp" />
@@ -200,6 +201,7 @@
<ClCompile Include="..\..\src\teamcity\teamcity_boost.cpp" />
<ClCompile Include="..\..\src\teamcity\teamcity_messages.cpp" />
<ClCompile Include="..\..\src\test_utils.cpp" />
+ <ClCompile Include="..\..\src\transaction_test.cpp" />
<ClCompile Include="..\..\src\types_test.cpp" />
<ClCompile Include="..\..\src\utility_test.cpp" />
</ItemGroup>
@@ -233,6 +235,8 @@
<None Include="..\..\config\queries-ssl.xml" />
<None Include="..\..\config\queries-test-32.xml" />
<None Include="..\..\config\queries-test.xml" />
+ <None Include="..\..\config\queries-transaction-32.xml" />
+ <None Include="..\..\config\queries-transaction.xml" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
index 3d2fcc6..3065df0 100644
--- a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
+++ b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj.filters
@@ -145,6 +145,9 @@
<ClCompile Include="..\..\src\queries_ssl_test.cpp">
<Filter>Code</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\transaction_test.cpp">
+ <Filter>Code</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\odbc\src\config\connection_string_parser.cpp">
<Filter>Externals</Filter>
</ClCompile>
@@ -166,6 +169,9 @@
<ClCompile Include="..\..\src\authentication_test.cpp">
<Filter>Code</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\odbc\src\nested_tx_mode.cpp">
+ <Filter>Externals</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\test_type.h">
@@ -209,5 +215,11 @@
<None Include="..\..\config\queries-auth-32.xml">
<Filter>Configs</Filter>
</None>
+ <None Include="..\..\config\queries-transaction.xml">
+ <Filter>Configs</Filter>
+ </None>
+ <None Include="..\..\config\queries-transaction-32.xml">
+ <Filter>Configs</Filter>
+ </None>
</ItemGroup>
</Project>
\ No newline at end of file
[08/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java
new file mode 100644
index 0000000..cb4ade7
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSqlTxQueriesAbstractTest.java
@@ -0,0 +1,1832 @@
+/*
+ * 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.mvcc;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.future.GridCompoundFuture;
+import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL_SUM;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.DML;
+import static org.apache.ignite.testframework.GridTestUtils.runAsync;
+import static org.apache.ignite.testframework.GridTestUtils.runMultiThreaded;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * Tests for transactional SQL.
+ */
+public abstract class CacheMvccSqlTxQueriesAbstractTest extends CacheMvccAbstractTest {
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_SingleNode_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxDmlSql_SingleNode();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSumSql_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL_SUM, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSumSql_WithRemoves_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL_SUM, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSumSql_WithRemoves__ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL_SUM, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSumSql_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL_SUM, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_SingleNode_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxDmlSql_WithRemoves_SingleNode();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_ClientServer_Backups0_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxDmlSql_WithRemoves_ClientServer_Backups0();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_ClientServer_Backups1() throws Exception {
+ accountsTxReadAll(3, 0, 1, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_ClientServer_Backups1() throws Exception {
+ accountsTxReadAll(4, 2, 1, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_ClientServer_Backups1_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxDmlSql_WithRemoves_ClientServer_Backups1();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), false, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_WithRemoves_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxDmlSql_ClientServer_Backups2_Persistence() throws Exception {
+ persistence = true;
+
+ testAccountsTxDmlSql_ClientServer_Backups2();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (4,4),(5,5),(6,6)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ assertEquals(4, cache.get(4));
+ assertEquals(5, cache.get(5));
+ assertEquals(6, cache.get(6));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertStaticCacheImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryDeleteStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ qry = new SqlFieldsQuery("DELETE FROM Integer WHERE 1 = 1");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertNull(cache.get(1));
+ assertNull(cache.get(2));
+ assertNull(cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryFastDeleteStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ qry = new SqlFieldsQuery("DELETE FROM Integer WHERE _key = 1");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertNull(cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryFastUpdateStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ qry = new SqlFieldsQuery("UPDATE Integer SET _val = 8 WHERE _key = 1");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(8, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryFastDeleteObjectStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1, new MvccTestSqlIndexValue(1),
+ 2, new MvccTestSqlIndexValue(2),
+ 3, new MvccTestSqlIndexValue(3)));
+
+ assertEquals(new MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new MvccTestSqlIndexValue(3), cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("DELETE FROM MvccTestSqlIndexValue WHERE _key = 1");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertNull(cache.get(1));
+ assertEquals(new MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new MvccTestSqlIndexValue(3), cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryFastUpdateObjectStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1, new MvccTestSqlIndexValue(1),
+ 2, new MvccTestSqlIndexValue(2),
+ 3, new MvccTestSqlIndexValue(3)));
+
+ assertEquals(new MvccTestSqlIndexValue(1), cache.get(1));
+ assertEquals(new MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new MvccTestSqlIndexValue(3), cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE MvccTestSqlIndexValue SET idxVal1 = 8 WHERE _key = 1");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(new MvccTestSqlIndexValue(8), cache.get(1));
+ assertEquals(new MvccTestSqlIndexValue(2), cache.get(2));
+ assertEquals(new MvccTestSqlIndexValue(3), cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryDeleteStaticCacheImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("DELETE FROM Integer WHERE 1 = 1")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertNull(cache.get(1));
+ assertNull(cache.get(2));
+ assertNull(cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryUpdateStaticCache() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE Integer SET _val = (_key * 10)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(10, cache.get(1));
+ assertEquals(20, cache.get(2));
+ assertEquals(30, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryUpdateStaticCacheImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE Integer SET _val = (_key * 10)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(10, cache.get(1));
+ assertEquals(20, cache.get(2));
+ assertEquals(30, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryDeadlock() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ client = true;
+
+ startGridsMultiThreaded(2, 2);
+
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+ final AtomicInteger idx = new AtomicInteger();
+ final AtomicReference<Exception> ex = new AtomicReference<>();
+
+ multithreaded(new Runnable() {
+ @Override public void run() {
+ int id = idx.getAndIncrement();
+
+ IgniteEx node = grid(id);
+
+ try {
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ String qry1 = "INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)";
+ String qry2 = "INSERT INTO Integer (_key, _val) values (4,4),(5,5),(6,6)";
+
+ SqlFieldsQuery qry = new SqlFieldsQuery((id % 2) == 0 ? qry1 : qry2);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+
+ barrier.await();
+
+ qry = new SqlFieldsQuery((id % 2) == 0 ? qry2 : qry1);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ }
+ }, 2);
+
+ Exception ex0 = ex.get();
+
+ assertNotNull(ex0);
+
+ if (!X.hasCause(ex0, IgniteTxTimeoutCheckedException.class))
+ throw ex0;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryDeadlockImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 0, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ final Phaser phaser = new Phaser(2);
+ final AtomicReference<Exception> ex = new AtomicReference<>();
+
+ GridTestUtils.runAsync(new Runnable() {
+ @Override public void run() {
+ IgniteEx node = grid(0);
+
+ try {
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+
+ awaitPhase(phaser, 2);
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ finally {
+ phaser.arrive();
+ }
+ }
+ });
+
+ phaser.arriveAndAwaitAdvance();
+
+ IgniteEx node = grid(1);
+
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+ catch (Exception e) {
+ phaser.arrive();
+
+ onException(ex, e);
+ }
+
+ phaser.arriveAndAwaitAdvance();
+
+ Exception ex0 = ex.get();
+
+ assertNotNull(ex0);
+
+ if (!X.hasCause(ex0, IgniteTxTimeoutCheckedException.class))
+ throw ex0;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertClient() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGrid(0);
+
+ client = true;
+
+ startGrid(1);
+
+ awaitPartitionMapExchange();
+
+ Ignite checkNode = grid(0);
+ Ignite updateNode = grid(1);
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (4,4),(5,5),(6,6)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+
+ assertEquals(4, cache.get(4));
+ assertEquals(5, cache.get(5));
+ assertEquals(6, cache.get(6));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertClientImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGrid(0);
+
+ client = true;
+
+ startGrid(1);
+
+ awaitPartitionMapExchange();
+
+ Ignite checkNode = grid(0);
+ Ignite updateNode = grid(1);
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertSubquery() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class, Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ awaitPartitionMapExchange();
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1, new MvccTestSqlIndexValue(1),
+ 2, new MvccTestSqlIndexValue(2),
+ 3, new MvccTestSqlIndexValue(3)));
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val)" +
+ " SELECT _key * 10, idxVal1 FROM MvccTestSqlIndexValue");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(1, cache0.get(10));
+ assertEquals(2, cache0.get(20));
+ assertEquals(3, cache0.get(30));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertSubqueryImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class, Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ awaitPartitionMapExchange();
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1, new MvccTestSqlIndexValue(1),
+ 2, new MvccTestSqlIndexValue(2),
+ 3, new MvccTestSqlIndexValue(3)));
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val)" +
+ " SELECT _key * 10, idxVal1 FROM MvccTestSqlIndexValue")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(1, cache0.get(10));
+ assertEquals(2, cache0.get(20));
+ assertEquals(3, cache0.get(30));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryUpdateSubquery() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class, Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ awaitPartitionMapExchange();
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1, new MvccTestSqlIndexValue(1),
+ 2, new MvccTestSqlIndexValue(2),
+ 3, new MvccTestSqlIndexValue(3)));
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE MvccTestSqlIndexValue AS t " +
+ "SET (idxVal1) = (SELECT idxVal1*10 FROM MvccTestSqlIndexValue WHERE t._key = _key)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(10, ((MvccTestSqlIndexValue)cache.get(1)).idxVal1);
+ assertEquals(20, ((MvccTestSqlIndexValue)cache.get(2)).idxVal1);
+ assertEquals(30, ((MvccTestSqlIndexValue)cache.get(3)).idxVal1);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryUpdateSubqueryImplicit() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class, Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ awaitPartitionMapExchange();
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(
+ 1, new MvccTestSqlIndexValue(1),
+ 2, new MvccTestSqlIndexValue(2),
+ 3, new MvccTestSqlIndexValue(3)));
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE MvccTestSqlIndexValue AS t " +
+ "SET (idxVal1) = (SELECT idxVal1*10 FROM MvccTestSqlIndexValue WHERE t._key = _key)")
+ .setTimeout(TX_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ assertEquals(10, ((MvccTestSqlIndexValue)cache.get(1)).idxVal1);
+ assertEquals(20, ((MvccTestSqlIndexValue)cache.get(2)).idxVal1);
+ assertEquals(30, ((MvccTestSqlIndexValue)cache.get(3)).idxVal1);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertMultithread() throws Exception {
+ final int THREAD_CNT = 8;
+ final int BATCH_SIZE = 1000;
+ final int ROUNDS = 10;
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ client = true;
+
+ startGridsMultiThreaded(2, 2);
+
+ final AtomicInteger seq = new AtomicInteger();
+
+ multithreaded(new Runnable() {
+ @Override public void run() {
+ for (int r = 0; r < ROUNDS; r++) {
+ StringBuilder bldr = new StringBuilder("INSERT INTO Integer (_key, _val) values ");
+
+ int start = seq.getAndAdd(BATCH_SIZE);
+
+ for (int i = start, end = start + BATCH_SIZE; i < end; i++) {
+ if (i != start)
+ bldr.append(',');
+
+ bldr
+ .append('(')
+ .append(i)
+ .append(',')
+ .append(i)
+ .append(')');
+ }
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(bldr.toString()).setPageSize(100);
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals((long)BATCH_SIZE, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+
+ for (int i = start, end = start + BATCH_SIZE; i < end; i++)
+ assertEquals(i, cache.get(i));
+ }
+
+ }
+ }, THREAD_CNT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertUpdateMultithread() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ final Phaser phaser = new Phaser(2);
+ final AtomicReference<Exception> ex = new AtomicReference<>();
+
+ GridCompoundFuture fut = new GridCompoundFuture();
+
+ fut.add(multithreadedAsync(new Runnable() {
+ @Override public void run() {
+ IgniteEx node = grid(0);
+
+ try {
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+
+ awaitPhase(phaser, 2);
+
+ qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (4,4),(5,5),(6,6)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ }
+ }, 1));
+
+ fut.add(multithreadedAsync(new Runnable() {
+ @Override public void run() {
+ IgniteEx node = grid(1);
+
+ try {
+ phaser.arriveAndAwaitAdvance();
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ IgniteCache<Integer, Integer> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ cache0.invokeAllAsync(F.asSet(1, 2, 3, 4, 5, 6), new EntryProcessor<Integer, Integer, Void>() {
+ @Override
+ public Void process(MutableEntry<Integer, Integer> entry,
+ Object... arguments) throws EntryProcessorException {
+ entry.setValue(entry.getValue() * 10);
+
+ return null;
+ }
+ });
+
+ phaser.arrive();
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ }
+ }, 1));
+
+ fut.markInitialized();
+
+ try {
+ fut.get(TX_TIMEOUT);
+ }
+ catch (IgniteCheckedException e) {
+ onException(ex, e);
+ }
+
+ Exception ex0 = ex.get();
+
+ if (ex0 != null)
+ throw ex0;
+
+ IgniteCache cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ assertEquals(10, cache.get(1));
+ assertEquals(20, cache.get(2));
+ assertEquals(30, cache.get(3));
+ assertEquals(40, cache.get(4));
+ assertEquals(50, cache.get(5));
+ assertEquals(60, cache.get(6));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertVersionConflict() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ IgniteCache cache = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+ final AtomicReference<Exception> ex = new AtomicReference<>();
+
+ runMultiThreaded(new Runnable() {
+ @Override public void run() {
+ IgniteEx node = grid(0);
+
+ try {
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ barrier.await();
+
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry;
+
+ synchronized (barrier) {
+ qry = new SqlFieldsQuery("SELECT * FROM Integer");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1, cur.getAll().size());
+ }
+ }
+
+ barrier.await();
+
+ qry = new SqlFieldsQuery("UPDATE Integer SET _val = (_key * 10)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ tx.commit();
+ }
+ }
+ catch (Exception e) {
+ onException(ex, e);
+ }
+ }
+ }, 2, "tx-thread");
+
+ IgniteSQLException ex0 = X.cause(ex.get(), IgniteSQLException.class);
+
+ assertNotNull("Exception has not been thrown.", ex0);
+ assertEquals("Mvcc version mismatch.", ex0.getMessage());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testInsertAndFastDeleteWithoutVersionConflict() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ IgniteCache<?, ?> cache0 = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx1 = grid(0).transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ // obtain tx version
+ cache0.query(new SqlFieldsQuery("select * from Integer where _key = 1"));
+
+ runAsync(() -> {
+ cache0.query(new SqlFieldsQuery("insert into Integer(_key, _val) values(?, ?)").setArgs(1, 1));
+ }).get();
+
+ cache0.query(new SqlFieldsQuery("delete from Integer where _key = ?").setArgs(1));
+
+ tx1.commit();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+
+ fail("Exception is not expected here");
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testInsertAndFastUpdateWithoutVersionConflict() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ IgniteCache<?, ?> cache0 = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx1 = grid(0).transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ // obtain tx version
+ cache0.query(new SqlFieldsQuery("select * from Integer where _key = 1"));
+
+ runAsync(() -> {
+ cache0.query(new SqlFieldsQuery("insert into Integer(_key, _val) values(?, ?)").setArgs(1, 1));
+ }).get();
+
+ cache0.query(new SqlFieldsQuery("update Integer set _val = ? where _key = ?").setArgs(1, 1));
+
+ tx1.commit();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+
+ fail("Exception is not expected here");
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testInsertFastUpdateConcurrent() throws Exception {
+ fail("https://issues.apache.org/jira/browse/IGNITE-9292");
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(2);
+
+ IgniteCache<?, ?> cache0 = grid(0).cache(DEFAULT_CACHE_NAME);
+
+ try {
+ for (int i = 0; i < 100; i++) {
+ int key = i;
+ CompletableFuture.allOf(
+ CompletableFuture.runAsync(() -> {
+ cache0.query(new SqlFieldsQuery("insert into Integer(_key, _val) values(?, ?)").setArgs(key, key));
+ }),
+ CompletableFuture.runAsync(() -> {
+ cache0.query(new SqlFieldsQuery("update Integer set _val = ? where _key = ?").setArgs(key, key));
+ })
+ ).get();
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+
+ fail("Exception is not expected here");
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertRollback() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (4,4),(5,5),(6,6)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ tx.rollback();
+ }
+
+ for (int i = 1; i <= 6; i++)
+ assertTrue(cache.query(new SqlFieldsQuery("SELECT * FROM Integer WHERE _key = 1")).getAll().isEmpty());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertUpdateSameKeys() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ final Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ qry = new SqlFieldsQuery("UPDATE Integer SET _val = (_key * 10)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ cur.getAll();
+ }
+
+ tx.commit();
+ }
+
+ assertEquals(10, cache.get(1));
+ assertEquals(20, cache.get(2));
+ assertEquals(30, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryInsertUpdateSameKeysInSameOperation() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ final Ignite updateNode = grid(rnd.nextInt(4));
+
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(1,2),(1,3)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ cache0.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ }, CacheException.class, "Duplicate key during INSERT [key=KeyCacheObjectImpl");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testQueryPendingUpdates() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite checkNode = grid(rnd.nextInt(4));
+ final Ignite updateNode = grid(rnd.nextInt(4));
+
+ IgniteCache cache = checkNode.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = updateNode.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)");
+
+ IgniteCache<Object, Object> cache0 = updateNode.cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry.setSql("UPDATE Integer SET _val = (_key * 10)"))) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ for (List<?> row : cache0.query(qry.setSql("SELECT _key, _val FROM Integer")).getAll()) {
+ assertEquals((Integer)row.get(0) * 10, row.get(1));
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry.setSql("UPDATE Integer SET _val = 15 where _key = 2"))) {
+ assertEquals(1L, cur.iterator().next().get(0));
+ }
+
+ for (List<?> row : cache0.query(qry.setSql("SELECT _key, _val FROM Integer")).getAll()) {
+ if ((Integer)row.get(0) == 2)
+ assertEquals(15, row.get(1));
+ else
+ assertEquals((Integer)row.get(0) * 10, row.get(1));
+ }
+
+ GridTestUtils.runAsync(new Runnable() {
+ @Override public void run() {
+ SqlFieldsQuery qry = new SqlFieldsQuery("SELECT _key, _val FROM Integer");
+
+ assertTrue(cache.query(qry).getAll().isEmpty());
+ }
+ }).get(TX_TIMEOUT);
+
+ cache0.query(qry.setSql("DELETE FROM Integer")).getAll();
+
+ assertTrue(cache0.query(qry.setSql("SELECT _key, _val FROM Integer")).getAll().isEmpty());
+
+ assertEquals(3L, cache0.query(qry.setSql("INSERT INTO Integer (_key, _val) values (1,1),(2,2),(3,3)")).getAll().iterator().next().get(0));
+
+ tx.commit();
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSelectProducesTransaction() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite node = grid(rnd.nextInt(4));
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) values (1,1),(2,2),(3,3)");
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qry)) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ SqlFieldsQueryEx qryEx = new SqlFieldsQueryEx("SELECT * FROM MvccTestSqlIndexValue", true);
+
+ qryEx.setAutoCommit(false);
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(qryEx)) {
+ assertEquals(3, cur.getAll().size());
+ }
+
+ try (GridNearTxLocal tx = cache.unwrap(IgniteEx.class).context().cache().context().tm().userTx()) {
+ assertNotNull(tx);
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRepeatableRead() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class);
+
+ startGridsMultiThreaded(4);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ IgniteCache<Object, Object> cache = grid(rnd.nextInt(4)).cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(
+ new SqlFieldsQuery("INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) values (1,1),(2,2),(3,3)"))) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+
+ Ignite node = grid(rnd.nextInt(4));
+ IgniteCache<Object, Object> cache0 = node.cache(DEFAULT_CACHE_NAME);
+ SqlFieldsQuery qry = new SqlFieldsQuery("SELECT * FROM MvccTestSqlIndexValue");
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3, cur.getAll().size());
+ }
+
+ runAsync(new Runnable() {
+ @Override public void run() {
+ IgniteCache<Object, Object> cache = grid(ThreadLocalRandom.current().nextInt(4))
+ .cache(DEFAULT_CACHE_NAME);
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(
+ new SqlFieldsQuery("INSERT INTO MvccTestSqlIndexValue (_key, idxVal1) values (4,4),(5,5),(6,6)"))) {
+ assertEquals(3L, cur.iterator().next().get(0));
+ }
+ }
+ }).get(TX_TIMEOUT);
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(3, cur.getAll().size());
+ }
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache0.query(qry)) {
+ assertEquals(6, cur.getAll().size());
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testIterator() throws Exception {
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ startGrid(getConfiguration("grid").setMvccVacuumTimeInterval(Integer.MAX_VALUE));
+
+ Ignite client = startGrid(getConfiguration("client").setClientMode(true));
+
+ IgniteCache<Object, Object> cache = client.cache(DEFAULT_CACHE_NAME);
+
+ cache.put(1, 1);
+ cache.put(2, 2);
+ cache.put(3, 3);
+ cache.put(4, 4);
+
+ List<List<?>> res;
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ res = cache.query(new SqlFieldsQuery("UPDATE Integer SET _val = CASE _key " +
+ "WHEN 1 THEN 10 WHEN 2 THEN 20 ELSE 30 END")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ tx.rollback();
+ }
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ res = cache.query(new SqlFieldsQuery("UPDATE Integer SET _val = CASE _val " +
+ "WHEN 1 THEN 10 WHEN 2 THEN 20 ELSE 30 END")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ res = cache.query(new SqlFieldsQuery("UPDATE Integer SET _val = CASE _val " +
+ "WHEN 10 THEN 100 WHEN 20 THEN 200 ELSE 300 END")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ res = cache.query(new SqlFieldsQuery("DELETE FROM Integer WHERE _key = 4")).getAll();
+
+ assertEquals(1L, res.get(0).get(0));
+
+ tx.commit();
+ }
+
+ IgniteCache<Integer, Integer> cache0 = client.cache(DEFAULT_CACHE_NAME);
+
+ Iterator<Cache.Entry<Integer, Integer>> it = cache0.iterator();
+
+ Map<Integer, Integer> map = new HashMap<>();
+
+ while (it.hasNext()) {
+ Cache.Entry<Integer, Integer> e = it.next();
+
+ assertNull("duplicate key returned from iterator", map.putIfAbsent(e.getKey(), e.getValue()));
+ }
+
+ assertEquals(3, map.size());
+
+ assertEquals(100, map.get(1).intValue());
+ assertEquals(200, map.get(2).intValue());
+ assertEquals(300, map.get(3).intValue());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testHints() throws Exception {
+ persistence = true;
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ Ignite node = startGrid(getConfiguration("grid").setMvccVacuumTimeInterval(100));
+
+ node.cluster().active(true);
+
+ Ignite client = startGrid(getConfiguration("client").setClientMode(true));
+
+ IgniteCache<Object, Object> cache = client.cache(DEFAULT_CACHE_NAME);
+
+ List<List<?>> res;
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ res = cache.query(new SqlFieldsQuery("INSERT INTO Integer (_key, _val) " +
+ "VALUES (1, 1), (2, 2), (3, 3), (4, 4)")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ tx.commit();
+ }
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ res = cache.query(new SqlFieldsQuery("UPDATE Integer SET _val = CASE _key " +
+ "WHEN 1 THEN 10 WHEN 2 THEN 20 ELSE 30 END")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ tx.rollback();
+ }
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ res = cache.query(new SqlFieldsQuery("UPDATE Integer SET _val = CASE _val " +
+ "WHEN 1 THEN 10 WHEN 2 THEN 20 ELSE 30 END")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ res = cache.query(new SqlFieldsQuery("UPDATE Integer SET _val = CASE _val " +
+ "WHEN 10 THEN 100 WHEN 20 THEN 200 ELSE 300 END")).getAll();
+
+ assertEquals(4L, res.get(0).get(0));
+
+ res = cache.query(new SqlFieldsQuery("DELETE FROM Integer WHERE _key = 4")).getAll();
+
+ assertEquals(1L, res.get(0).get(0));
+
+ tx.commit();
+ }
+
+ mvccProcessor(node).runVacuum().get(TX_TIMEOUT);
+
+ checkAllVersionsHints(node.cache(DEFAULT_CACHE_NAME));
+ }
+
+ /** */
+ private void checkAllVersionsHints(IgniteCache cache) throws IgniteCheckedException {
+ IgniteCacheProxy cache0 = (IgniteCacheProxy)cache;
+ GridCacheContext cctx = cache0.context();
+
+ assert cctx.mvccEnabled();
+
+ for (Object e : cache) {
+ IgniteBiTuple entry = (IgniteBiTuple)e;
+
+ KeyCacheObject key = cctx.toCacheKeyObject(entry.getKey());
+
+ GridCursor<CacheDataRow> cur = cctx.offheap().mvccAllVersionsCursor(cctx, key, CacheDataRowAdapter.RowData.LINK_WITH_HEADER);
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ assertTrue(row.mvccTxState() != 0);
+ }
+ }
+ }
+
+ /**
+ * @param ex Exception holder.
+ * @param e Exception.
+ */
+ private <T extends Throwable> void onException(AtomicReference<T> ex, T e) {
+ if (!ex.compareAndSet(null, e))
+ ex.get().addSuppressed(e);
+ }
+
+ /**
+ * @param phaser Phaser.
+ * @param phase Phase to wait for.
+ */
+ private void awaitPhase(Phaser phaser, int phase) {
+ int p;
+ do {
+ p = phaser.arriveAndAwaitAdvance();
+ }
+ while (p < phase);
+ }
+
+ /**
+ *
+ */
+ static class MvccTestSqlIndexValue implements Serializable {
+ /** */
+ @QuerySqlField(index = true)
+ private int idxVal1;
+
+ /**
+ * @param idxVal1 Indexed value 1.
+ */
+ MvccTestSqlIndexValue(int idxVal1) {
+ this.idxVal1 = idxVal1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ MvccTestSqlIndexValue value = (MvccTestSqlIndexValue)o;
+ return idxVal1 == value.idxVal1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(idxVal1);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccTestSqlIndexValue.class, this);
+ }
+ }
+}
[17/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccTransactionsTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccTransactionsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccTransactionsTest.java
new file mode 100644
index 0000000..01268de
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccTransactionsTest.java
@@ -0,0 +1,3674 @@
+/*
+ * 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.mvcc;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import javax.cache.Cache;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.TouchedExpiryPolicy;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteTransactions;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.cluster.ClusterTopologyException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.TestRecordingCommunicationSpi;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.TestCacheNodeExcludingFilter;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetResponse;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxFinishRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxFinishResponse;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxPrepareRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxPrepareResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestQueryCntr;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTx;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccSnapshotResponse;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.util.lang.GridAbsPredicate;
+import org.apache.ignite.internal.util.lang.GridInClosure3;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiInClosure;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.apache.ignite.transactions.TransactionOptimisticException;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Assert;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.GET;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SCAN;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.PUT;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker.MVCC_TRACKER_ID_NA;
+import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE;
+
+/**
+ * TODO IGNITE-6739: tests reload
+ * TODO IGNITE-6739: extend tests to use single/mutiple nodes, all tx types.
+ * TODO IGNITE-6739: test with cache groups.
+ */
+@SuppressWarnings("unchecked")
+public class CacheMvccTransactionsTest extends CacheMvccAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPessimisticTx1() throws Exception {
+ checkTx1(PESSIMISTIC, REPEATABLE_READ);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticSerializableTx1() throws Exception {
+ checkTx1(OPTIMISTIC, SERIALIZABLE);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticRepeatableReadTx1() throws Exception {
+ checkTx1(OPTIMISTIC, REPEATABLE_READ);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticReadCommittedTx1() throws Exception {
+ checkTx1(OPTIMISTIC, READ_COMMITTED);
+ }
+
+ /**
+ * @param concurrency Transaction concurrency.
+ * @param isolation Transaction isolation.
+ * @throws Exception If failed.
+ */
+ private void checkTx1(final TransactionConcurrency concurrency, final TransactionIsolation isolation)
+ throws Exception {
+ checkTxWithAllCaches(new CI1<IgniteCache<Integer, Integer>>() {
+ @Override public void apply(IgniteCache<Integer, Integer> cache) {
+ try {
+ IgniteTransactions txs = cache.unwrap(Ignite.class).transactions();
+
+ List<Integer> keys = testKeys(cache);
+
+ for (Integer key : keys) {
+ log.info("Test key: " + key);
+
+ try (Transaction tx = txs.txStart(concurrency, isolation)) {
+ Integer val = cache.get(key);
+
+ assertNull(val);
+
+ cache.put(key, key);
+
+ val = (Integer)checkAndGet(true, cache, key, GET, SCAN);
+
+ assertEquals(key, val);
+
+ tx.commit();
+ }
+
+ Integer val = (Integer)checkAndGet(false, cache, key, SCAN, GET);
+
+ assertEquals(key, val);
+ }
+ }
+ catch (Exception e) {
+ throw new IgniteException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPessimisticTx2() throws Exception {
+ checkTx2(PESSIMISTIC, REPEATABLE_READ);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticSerializableTx2() throws Exception {
+ checkTx2(OPTIMISTIC, SERIALIZABLE);
+ }
+
+ /**
+ * @param concurrency Transaction concurrency.
+ * @param isolation Transaction isolation.
+ * @throws Exception If failed.
+ */
+ private void checkTx2(final TransactionConcurrency concurrency, final TransactionIsolation isolation)
+ throws Exception {
+ checkTxWithAllCaches(new CI1<IgniteCache<Integer, Integer>>() {
+ @Override public void apply(IgniteCache<Integer, Integer> cache) {
+ try {
+ IgniteTransactions txs = cache.unwrap(Ignite.class).transactions();
+
+ List<Integer> keys = testKeys(cache);
+
+ for (Integer key : keys) {
+ log.info("Test key: " + key);
+
+ try (Transaction tx = txs.txStart(concurrency, isolation)) {
+ cache.put(key, key);
+ cache.put(key + 1, key + 1);
+
+ assertEquals(key, checkAndGet(true, cache, key, GET, SCAN));
+ assertEquals(key + 1, checkAndGet(true, cache, key + 1, GET, SCAN));
+
+ tx.commit();
+ }
+
+ assertEquals(key, checkAndGet(false, cache, key, GET, SCAN));
+ assertEquals(key + 1, checkAndGet(false, cache, key + 1, GET, SCAN));
+ }
+ }
+ catch (Exception e) {
+ throw new IgniteException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * @param c Closure to run.
+ * @throws Exception If failed.
+ */
+ private void checkTxWithAllCaches(IgniteInClosure<IgniteCache<Integer, Integer>> c) throws Exception {
+ client = false;
+
+ startGridsMultiThreaded(SRVS);
+
+ client = true;
+
+ startGrid(SRVS);
+
+ try {
+ for (CacheConfiguration<Object, Object> ccfg : cacheConfigurations()) {
+ logCacheInfo(ccfg);
+
+ ignite(0).createCache(ccfg);
+
+ try {
+ Ignite node = ignite(0);
+
+ IgniteCache<Integer, Integer> cache = node.cache(ccfg.getName());
+
+ c.apply(cache);
+ }
+ finally {
+ ignite(0).destroyCache(ccfg.getName());
+ }
+ }
+
+ verifyCoordinatorInternalState();
+ }
+ finally {
+ stopAllGrids();
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testWithCacheGroups() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ List<CacheConfiguration> ccfgs = new ArrayList<>();
+
+ for (int c = 0; c < 3; c++) {
+ CacheConfiguration ccfg = cacheConfiguration(PARTITIONED, FULL_SYNC, 0, DFLT_PARTITION_COUNT);
+
+ ccfg.setName("cache-" + c);
+ ccfg.setGroupName("grp1");
+
+ ccfgs.add(ccfg);
+ }
+
+ srv0.createCaches(ccfgs);
+
+ final int PUTS = 5;
+
+ for (int i = 0; i < PUTS; i++) {
+ for (int c = 0; c < 3; c++) {
+ IgniteCache cache = srv0.cache("cache-" + c);
+
+ Map<Integer, Integer> vals = new HashMap<>();
+
+ for (int k = 0; k < 10; k++) {
+ cache.put(k, i);
+
+ vals.put(k, i);
+
+ assertEquals(i, checkAndGet(false, cache, k, SCAN, GET));
+ }
+
+ assertEquals(vals, checkAndGetAll(false, cache, vals.keySet(), GET, SCAN));
+ }
+ }
+
+ for (int c = 0; c < 3; c++) {
+ IgniteCache cache = srv0.cache("cache-" + c);
+
+ Map<Integer, Integer> vals = new HashMap<>();
+
+ for (int k = 0; k < 10; k++) {
+ if (k % 2 == 0)
+ vals.put(k, PUTS - 1);
+ else {
+ cache.remove(k);
+
+ assertNull(checkAndGet(false, cache, k, SCAN, GET));
+ }
+ }
+
+ assertEquals(vals, checkAndGetAll(false, cache, vals.keySet(), GET, SCAN));
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCacheRecreate() throws Exception {
+ cacheRecreate(null);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testActiveQueriesCleanup() throws Exception {
+ activeQueriesCleanup(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testActiveQueriesCleanupTx() throws Exception {
+ activeQueriesCleanup(true);
+ }
+
+ /**
+ * @param tx If {@code true} tests reads inside transaction.
+ * @throws Exception If failed.
+ */
+ private void activeQueriesCleanup(final boolean tx) throws Exception {
+ startGridsMultiThreaded(SRVS);
+
+ client = true;
+
+ Ignite client = startGrid(SRVS);
+
+ final int NODES = SRVS + 1;
+
+ CacheConfiguration ccfg = cacheConfiguration(PARTITIONED, FULL_SYNC, 1, 512);
+
+ client.createCache(ccfg);
+
+ final long stopTime = System.currentTimeMillis() + 5000;
+
+ GridTestUtils.runMultiThreaded(new IgniteInClosure<Integer>() {
+ @Override public void apply(Integer idx) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Ignite node = ignite(idx % NODES);
+
+ IgniteTransactions txs = node.transactions();
+
+ IgniteCache cache = node.cache(DEFAULT_CACHE_NAME);
+
+ while (System.currentTimeMillis() < stopTime) {
+ int keyCnt = rnd.nextInt(10) + 1;
+
+ Set<Integer> keys = new HashSet<>();
+
+ for (int i = 0; i < keyCnt; i++)
+ keys.add(rnd.nextInt());
+
+ if (tx) {
+ try (Transaction tx = txs.txStart(OPTIMISTIC, SERIALIZABLE)) {
+ cache.getAll(keys);
+
+ if (rnd.nextBoolean())
+ tx.commit();
+ else
+ tx.rollback();
+ }
+ }
+ else
+ cache.getAll(keys);
+ }
+ }
+ }, NODES * 2, "get-thread");
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxReadIsolationSimple() throws Exception {
+ Ignite srv0 = startGrids(4);
+
+ client = true;
+
+ startGrid(4);
+
+ for (CacheConfiguration ccfg : cacheConfigurations()) {
+ IgniteCache<Object, Object> cache0 = srv0.createCache(ccfg);
+
+ final Map<Integer, Integer> startVals = new HashMap<>();
+
+ final int KEYS = 10;
+
+ for (int i = 0; i < KEYS; i++)
+ startVals.put(i, 0);
+
+ for (final TransactionIsolation isolation : TransactionIsolation.values()) {
+ for (final Ignite node : G.allGrids()) {
+ info("Run test [node=" + node.name() + ", isolation=" + isolation + ']');
+
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache0.putAll(startVals);
+
+ tx.commit();
+ }
+
+ final CountDownLatch readStart = new CountDownLatch(1);
+
+ final CountDownLatch readProceed = new CountDownLatch(1);
+
+ IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = node.transactions().txStart(OPTIMISTIC, isolation)) {
+ assertEquals(0, checkAndGet(false, cache, 0, SCAN, GET));
+
+ readStart.countDown();
+
+ assertTrue(readProceed.await(5, TimeUnit.SECONDS));
+
+ if (isolation == READ_COMMITTED) {
+ assertNull(checkAndGet(false, cache, 1, SCAN, GET));
+
+ assertEquals(1, checkAndGet(false, cache, 2, SCAN, GET));
+
+ Map<Object, Object> res = checkAndGetAll(false, cache, startVals.keySet(), SCAN, GET);
+
+ assertEquals(startVals.size() / 2, res.size());
+
+ for (Map.Entry<Object, Object> e : res.entrySet())
+ assertEquals("Invalid value for key: " + e.getKey(), 1, e.getValue());
+ }
+ else {
+ assertEquals(0, checkAndGet(true, cache, 1, GET, SCAN));
+
+ assertEquals(0, checkAndGet(true, cache, 2, GET, SCAN));
+
+ Map<Object, Object> res = checkAndGetAll(true, cache, startVals.keySet(), GET, SCAN);
+
+ assertEquals(startVals.size(), res.size());
+
+ for (Map.Entry<Object, Object> e : res.entrySet())
+ assertEquals("Invalid value for key: " + e.getKey(), 0, e.getValue());
+ }
+
+ tx.rollback();
+ }
+
+ return null;
+ }
+ });
+
+ assertTrue(readStart.await(5, TimeUnit.SECONDS));
+
+ for (int i = 0; i < KEYS; i++) {
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ if (i % 2 == 0)
+ cache0.put(i, 1);
+ else
+ cache0.remove(i);
+
+ tx.commit();
+ }
+ }
+
+ readProceed.countDown();
+
+ fut.get();
+ }
+ }
+
+ srv0.destroyCache(cache0.getName());
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutGetAllSimple() throws Exception {
+ Ignite node = startGrid(0);
+
+ IgniteTransactions txs = node.transactions();
+
+ final IgniteCache<Object, Object> cache = node.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 1));
+
+ final int KEYS = 10_000;
+
+ Set<Integer> keys = new HashSet<>();
+
+ for (int k = 0; k < KEYS; k++)
+ keys.add(k);
+
+ Map<Object, Object> map = checkAndGetAll(false, cache, keys, SCAN, GET);
+
+ assertTrue(map.isEmpty());
+
+ for (int v = 0; v < 3; v++) {
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 0; k < KEYS; k++) {
+ if (k % 2 == 0)
+ cache.put(k, v);
+ }
+
+ tx.commit();
+ }
+
+ map = checkAndGetAll(false, cache, keys, SCAN, GET);
+
+ for (int k = 0; k < KEYS; k++) {
+ if (k % 2 == 0)
+ assertEquals(v, map.get(k));
+ else
+ assertNull(map.get(k));
+ }
+
+ assertEquals(KEYS / 2, map.size());
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ map = checkAndGetAll(true, cache, keys, SCAN, GET);
+
+ for (int k = 0; k < KEYS; k++) {
+ if (k % 2 == 0)
+ assertEquals(v, map.get(k));
+ else
+ assertNull(map.get(k));
+ }
+
+ assertEquals(KEYS / 2, map.size());
+
+ tx.commit();
+ }
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutRemoveSimple() throws Exception {
+ putRemoveSimple(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutRemoveSimple_LargeKeys() throws Exception {
+ putRemoveSimple(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ * @param largeKeys {@code True} to use large keys (not fitting in single page).
+ */
+ private void putRemoveSimple(boolean largeKeys) throws Exception {
+ Ignite node = startGrid(0);
+
+ IgniteTransactions txs = node.transactions();
+
+ final IgniteCache<Object, Object> cache = node.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 1));
+
+ final int KEYS = 100;
+
+ checkValues(new HashMap<>(), cache);
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 0; k < KEYS; k++)
+ cache.remove(testKey(largeKeys, k));
+
+ tx.commit();
+ }
+
+ checkValues(new HashMap<>(), cache);
+
+ Map<Object, Object> expVals = new HashMap<>();
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 0; k < KEYS; k++) {
+ Object key = testKey(largeKeys, k);
+
+ expVals.put(key, k);
+
+ cache.put(key, k);
+ }
+
+ tx.commit();
+ }
+
+ checkValues(expVals, cache);
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 0; k < KEYS; k++) {
+ if (k % 2 == 0) {
+ Object key = testKey(largeKeys, k);
+
+ cache.remove(key);
+
+ expVals.remove(key);
+ }
+ }
+
+ tx.commit();
+ }
+
+ checkValues(expVals, cache);
+
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Object key = testKey(largeKeys, 0);
+
+ for (int i = 0; i < 500; i++) {
+ boolean rmvd;
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ if (rnd.nextBoolean()) {
+ cache.remove(key);
+
+ rmvd = true;
+ }
+ else {
+ cache.put(key, i);
+
+ rmvd = false;
+ }
+
+ tx.commit();
+ }
+
+ if (rmvd) {
+ assertNull(checkAndGet(false, cache, key, SCAN, GET));
+ assertTrue(checkAndGetAll(false, cache, F.asSet(key), SCAN, GET).isEmpty());
+ }
+ else {
+ assertEquals(i, checkAndGet(false, cache, key, SCAN, GET));
+
+ Map<Object, Object> res = checkAndGetAll(false, cache, F.asSet(key), SCAN, GET);
+
+ assertEquals(i, res.get(key));
+ }
+ }
+ }
+
+ /**
+ * @param largeKeys {@code True} to use large keys (not fitting in single page).
+ * @param idx Index.
+ * @return Key instance.
+ */
+ private static Object testKey(boolean largeKeys, int idx) {
+ if (largeKeys) {
+ int payloadSize = PAGE_SIZE + ThreadLocalRandom.current().nextInt(PAGE_SIZE * 10);
+
+ return new TestKey(idx, payloadSize);
+ }
+ else
+ return idx;
+ }
+
+ /**
+ * @param expVals Expected values.
+ * @param cache Cache.
+ */
+ private void checkValues(Map<Object, Object> expVals, IgniteCache<Object, Object> cache) {
+ for (Map.Entry<Object, Object> e : expVals.entrySet())
+ assertEquals(e.getValue(), checkAndGet(false, cache, e.getKey(), SCAN, GET));
+
+ Map<Object, Object> res = checkAndGetAll(false, cache, expVals.keySet(), SCAN, GET);
+
+ assertEquals(expVals, res);
+
+ res = new HashMap<>();
+
+ for (IgniteCache.Entry<Object, Object> e : cache)
+ res.put(e.getKey(), e.getValue());
+
+ assertEquals(expVals, res);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testThreadUpdatesAreVisibleForThisThread() throws Exception {
+ final Ignite ignite = startGrid(0);
+
+ final IgniteCache<Object, Object> cache = ignite.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 1));
+
+ final int THREADS = Runtime.getRuntime().availableProcessors() * 2;
+
+ final int KEYS = 10;
+
+ final CyclicBarrier b = new CyclicBarrier(THREADS);
+
+ GridTestUtils.runMultiThreaded(new IgniteInClosure<Integer>() {
+ @Override public void apply(Integer idx) {
+ try {
+ int min = idx * KEYS;
+ int max = min + KEYS;
+
+ Set<Integer> keys = new HashSet<>();
+
+ for (int k = min; k < max; k++)
+ keys.add(k);
+
+ b.await();
+
+ for (int i = 0; i < 100; i++) {
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = min; k < max; k++)
+ cache.put(k, i);
+
+ tx.commit();
+ }
+
+ Map<Object, Object> res = checkAndGetAll(false, cache, keys, SCAN, GET);
+
+ for (Integer key : keys)
+ assertEquals(i, res.get(key));
+
+ assertEquals(KEYS, res.size());
+ }
+ }
+ catch (Exception e) {
+ error("Unexpected error: " + e, e);
+
+ fail("Unexpected error: " + e);
+ }
+ }
+ }, THREADS, "test-thread");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testWaitPreviousTxAck() throws Exception {
+ testSpi = true;
+
+ startGrid(0);
+
+ client = true;
+
+ final Ignite ignite = startGrid(1);
+
+ final IgniteCache<Object, Object> cache =
+ ignite.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 16));
+
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(1, 1);
+ cache.put(2, 1);
+ cache.put(3, 1);
+
+ tx.commit();
+ }
+
+ TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(ignite);
+
+ clientSpi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
+ /** */
+ boolean block = true;
+
+ @Override public boolean apply(ClusterNode node, Message msg) {
+ if (block && msg instanceof MvccAckRequestTx) {
+ block = false;
+
+ return true;
+ }
+
+ return false;
+ }
+ });
+
+ IgniteInternalFuture<?> txFut1 = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(2, 2);
+ cache.put(3, 2);
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ });
+
+ IgniteInternalFuture<?> txFut2 = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(1, 3);
+ cache.put(2, 3);
+
+ tx.commit();
+ }
+
+ // Should see changes mady by both tx1 and tx2.
+ Map<Object, Object> res = checkAndGetAll(false, cache, F.asSet(1, 2, 3), SCAN, GET);
+
+ assertEquals(3, res.get(1));
+ assertEquals(3, res.get(2));
+ assertEquals(2, res.get(3));
+
+ return null;
+ }
+ });
+
+ clientSpi.waitForBlocked();
+
+ Thread.sleep(1000);
+
+ clientSpi.stopBlock(true);
+
+ txFut1.get();
+ txFut2.get();
+
+ Map<Object, Object> res = checkAndGetAll(false, cache, F.asSet(1, 2, 3), SCAN, GET);
+
+ assertEquals(3, res.get(1));
+ assertEquals(3, res.get(2));
+ assertEquals(2, res.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPartialCommitResultNoVisible() throws Exception {
+ testSpi = true;
+
+ startGrids(2);
+
+ client = true;
+
+ final Ignite ignite = startGrid(2);
+
+ awaitPartitionMapExchange();
+
+ final IgniteCache<Object, Object> cache =
+ ignite.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 16));
+
+ final Integer key1 = primaryKey(ignite(0).cache(cache.getName()));
+ final Integer key2 = primaryKey(ignite(1).cache(cache.getName()));
+
+ info("Test keys [key1=" + key1 + ", key2=" + key2 + ']');
+
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, 1);
+ cache.put(key2, 1);
+
+ tx.commit();
+ }
+
+ Integer val = 1;
+
+ // Allow finish update for key1 and block update for key2.
+
+ TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(ignite);
+ TestRecordingCommunicationSpi srvSpi = TestRecordingCommunicationSpi.spi(ignite(0));
+
+ for (int i = 0; i < 10; i++) {
+ info("Iteration: " + i);
+
+ clientSpi.blockMessages(GridNearTxFinishRequest.class, getTestIgniteInstanceName(1));
+
+ srvSpi.record(GridNearTxFinishResponse.class);
+
+ final Integer newVal = val + 1;
+
+ IgniteInternalFuture<?> fut = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, newVal);
+ cache.put(key2, newVal);
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ });
+
+ try {
+ srvSpi.waitForRecorded();
+
+ srvSpi.recordedMessages(true);
+
+ assertFalse(fut.isDone());
+
+ if (i % 2 == 1) {
+ // Execute one more update to increase counter.
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(primaryKeys(jcache(0), 1, 100_000).get(0), 1);
+
+ tx.commit();
+ }
+ }
+
+ Set<Integer> keys = new HashSet<>();
+
+ keys.add(key1);
+ keys.add(key2);
+
+ Map<Object, Object> res;
+
+ res = checkAndGetAll(false, cache, keys, SCAN, GET);
+
+ assertEquals(val, res.get(key1));
+ assertEquals(val, res.get(key2));
+
+ clientSpi.stopBlock(true);
+
+ fut.get();
+
+ res = checkAndGetAll(false, cache, keys, SCAN, GET);
+
+ assertEquals(newVal, res.get(key1));
+ assertEquals(newVal, res.get(key2));
+
+ val = newVal;
+ }
+ finally {
+ clientSpi.stopBlock(true);
+ }
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCleanupWaitsForGet1() throws Exception {
+ boolean vals[] = {true, false};
+
+ for (boolean otherPuts : vals) {
+ for (boolean putOnStart : vals) {
+ for (boolean inTx : vals) {
+ cleanupWaitsForGet1(otherPuts, putOnStart, inTx);
+
+ afterTest();
+ }
+ }
+ }
+ }
+
+ /**
+ * @param otherPuts {@code True} to update unrelated keys to increment mvcc counter.
+ * @param putOnStart {@code True} to put data in cache before getAll.
+ * @param inTx {@code True} to read inside transaction.
+ * @throws Exception If failed.
+ */
+ private void cleanupWaitsForGet1(boolean otherPuts, final boolean putOnStart, final boolean inTx) throws Exception {
+ info("cleanupWaitsForGet [otherPuts=" + otherPuts +
+ ", putOnStart=" + putOnStart +
+ ", inTx=" + inTx + "]");
+
+ testSpi = true;
+
+ client = false;
+
+ final Ignite srv = startGrid(0);
+
+ client = true;
+
+ final Ignite client = startGrid(1);
+
+ awaitPartitionMapExchange();
+
+ final IgniteCache<Object, Object> srvCache =
+ srv.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 16));
+
+ final Integer key1 = 1;
+ final Integer key2 = 2;
+
+ if (putOnStart) {
+ try (Transaction tx = srv.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ srvCache.put(key1, 0);
+ srvCache.put(key2, 0);
+
+ tx.commit();
+ }
+ }
+
+ if (otherPuts) {
+ for (int i = 0; i < 3; i++) {
+ try (Transaction tx = srv.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ srvCache.put(1_000_000 + i, 99);
+
+ tx.commit();
+ }
+ }
+ }
+
+ TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(client);
+
+ clientSpi.blockMessages(GridNearGetRequest.class, getTestIgniteInstanceName(0));
+
+ IgniteInternalFuture<?> getFut = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ IgniteCache<Integer, Integer> cache = client.cache(srvCache.getName());
+
+
+ Map<Integer, Integer> vals;
+
+ if (inTx) {
+ try (Transaction tx = client.transactions().txStart(OPTIMISTIC, SERIALIZABLE)) {
+ vals = checkAndGetAll(false, cache, F.asSet(key1, key2), SCAN, GET);
+
+ tx.rollback();
+ }
+ }
+ else
+ vals = checkAndGetAll(false, cache, F.asSet(key1, key2), SCAN, GET);
+
+ if (putOnStart) {
+ assertEquals(2, vals.size());
+ assertEquals(0, (Object)vals.get(key1));
+ assertEquals(0, (Object)vals.get(key2));
+ }
+ else
+ assertEquals(0, vals.size());
+
+ return null;
+ }
+ }, "get-thread");
+
+ clientSpi.waitForBlocked();
+
+ for (int i = 0; i < 5; i++) {
+ try (Transaction tx = srv.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ srvCache.put(key1, i + 1);
+ srvCache.put(key2, i + 1);
+
+ tx.commit();
+ }
+ }
+
+ clientSpi.stopBlock(true);
+
+ getFut.get();
+
+ IgniteCache<Integer, Integer> cache = client.cache(srvCache.getName());
+
+ Map<Integer, Integer> vals = checkAndGetAll(false, cache, F.asSet(key1, key2), SCAN, GET);
+
+ assertEquals(2, vals.size());
+ assertEquals(5, (Object)vals.get(key1));
+ assertEquals(5, (Object)vals.get(key2));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCleanupWaitsForGet2() throws Exception {
+ /*
+ Simulate case when there are two active transactions modifying the same key
+ (it is possible if key lock is released but ack message is delayed), and at this moment
+ query is started.
+ */
+ testSpi = true;
+
+ client = false;
+
+ startGrids(2);
+
+ client = true;
+
+ final Ignite client = startGrid(2);
+
+ awaitPartitionMapExchange();
+
+ final IgniteCache<Object, Object> cache = client.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 16).
+ setNodeFilter(new TestCacheNodeExcludingFilter(ignite(0).name())));
+
+ final Integer key1 = 1;
+ final Integer key2 = 2;
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, 0);
+ cache.put(key2, 0);
+
+ tx.commit();
+ }
+
+ TestRecordingCommunicationSpi crdSpi = TestRecordingCommunicationSpi.spi(grid(0));
+
+ TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(client);
+
+ final CountDownLatch getLatch = new CountDownLatch(1);
+
+ clientSpi.closure(new IgniteBiInClosure<ClusterNode, Message>() {
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (msg instanceof MvccAckRequestTx)
+ doSleep(2000);
+ }
+ });
+
+ crdSpi.closure(new IgniteBiInClosure<ClusterNode, Message>() {
+ /** */
+ private AtomicInteger cntr = new AtomicInteger();
+
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (msg instanceof MvccSnapshotResponse) {
+ if (cntr.incrementAndGet() == 2) {
+ getLatch.countDown();
+
+ doSleep(1000);
+ }
+ }
+ }
+ });
+
+ final IgniteInternalFuture<?> putFut1 = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, 1);
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ }, "put1");
+
+ final IgniteInternalFuture<?> putFut2 = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, 2);
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ }, "put2");
+
+ IgniteInternalFuture<?> getFut = GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ U.await(getLatch);
+
+ while (!putFut1.isDone() || !putFut2.isDone()) {
+ Map<Object, Object> vals1 = checkAndGetAll(false, cache, F.asSet(key1, key2), SCAN);
+ Map<Object, Object> vals2 = checkAndGetAll(false, cache, F.asSet(key1, key2), GET);
+
+ assertEquals(2, vals1.size());
+ assertEquals(2, vals2.size());
+ }
+
+ return null;
+ }
+ }, 4, "get-thread");
+
+ putFut1.get();
+ putFut2.get();
+ getFut.get();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCleanupWaitsForGet3() throws Exception {
+ for (int i = 0; i < 4; i++) {
+ cleanupWaitsForGet3(i + 1);
+
+ afterTest();
+ }
+ }
+
+ /**
+ * @param updates Number of updates.
+ * @throws Exception If failed.
+ */
+ private void cleanupWaitsForGet3(int updates) throws Exception {
+ /*
+ Simulate case when coordinator assigned query version has active transaction,
+ query is delayed, after this active transaction finish and the same key is
+ updated several more times before query starts.
+ */
+ testSpi = true;
+
+ client = false;
+
+ startGrids(1);
+
+ client = true;
+
+ final Ignite client = startGrid(1);
+
+ awaitPartitionMapExchange();
+
+ final IgniteCache<Object, Object> cache = client.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 16));
+
+ final Integer key1 = 1;
+ final Integer key2 = 2;
+
+ for (int i = 0; i < updates; i++) {
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, i);
+ cache.put(key2, i);
+
+ tx.commit();
+ }
+ }
+
+ TestRecordingCommunicationSpi crdSpi = TestRecordingCommunicationSpi.spi(grid(0));
+
+ TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(client);
+
+ clientSpi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
+ /** */
+ private boolean blocked;
+
+ @Override public boolean apply(ClusterNode node, Message msg) {
+ if (!blocked && (msg instanceof MvccAckRequestTx)) {
+ blocked = true;
+
+ return true;
+ }
+ return false;
+ }
+ });
+
+ final IgniteInternalFuture<?> putFut = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key2, 3);
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ }, "put");
+
+ clientSpi.waitForBlocked();
+
+ for (int i = 0; i < updates; i++) {
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key1, i + 3);
+
+ tx.commit();
+ }
+ }
+
+ // Delay version for getAll.
+ crdSpi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
+ /** */
+ private boolean blocked;
+
+ @Override public boolean apply(ClusterNode node, Message msg) {
+ if (!blocked && (msg instanceof MvccSnapshotResponse)) {
+ blocked = true;
+
+ return true;
+ }
+ return false;
+ }
+ });
+
+ final IgniteInternalFuture<?> getFut = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ final Map<Object, Object> res1 = checkAndGetAll(false, cache, F.asSet(key1, key2), SCAN);
+ final Map<Object, Object> res2 = checkAndGetAll(false, cache, F.asSet(key1, key2), GET);
+
+ assertEquals(2, res1.size());
+ assertEquals(2, res2.size());
+
+ return null;
+ }
+ }, "get");
+
+ crdSpi.waitForBlocked();
+
+ clientSpi.stopBlock(true);
+
+ putFut.get();
+
+ for (int i = 0; i < updates; i++) {
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(key2, i + 4);
+
+ tx.commit();
+ }
+ }
+
+ crdSpi.stopBlock(true);
+
+ getFut.get();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_GetAll() throws Exception {
+ putAllGetAll(null, 1, 0, 0, 64, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_SinglePartition_GetAll() throws Exception {
+ putAllGetAll(null, 1, 0, 0, 1, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups0_GetAll() throws Exception {
+ putAllGetAll(null, 4, 2, 0, 64, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups0_Persistence_GetAll() throws Exception {
+ persistence = true;
+
+ testPutAllGetAll_ClientServer_Backups0_GetAll();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_GetAll() throws Exception {
+ putAllGetAll(null, 4, 2, 1, 64, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups2_GetAll() throws Exception {
+ putAllGetAll(null, 4, 2, 2, 64, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_RestartCoordinator_GetAll() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD, 4, 2, 1, 64, null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_Scan() throws Exception {
+ putAllGetAll(null, 1, 0, 0, 64, null, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_SinglePartition_Scan() throws Exception {
+ putAllGetAll(null, 1, 0, 0, 1, null, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups0_Scan() throws Exception {
+ putAllGetAll(null, 4, 2, 0, 64, null, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups0_Persistence_Scan() throws Exception {
+ persistence = true;
+
+ testPutAllGetAll_ClientServer_Backups0_Scan();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_Scan() throws Exception {
+ putAllGetAll(null, 4, 2, 1, 64, null, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups2_Scan() throws Exception {
+ putAllGetAll(null, 4, 2, 2, 64, null, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_RestartCoordinator_Scan() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD, 4, 2, 1, 64, null, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_Restart_Scan() throws Exception {
+ putAllGetAll(RestartMode.RESTART_RND_SRV, 4, 2, 1, 64, null, SCAN, PUT);
+ }
+
+
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64, null, false, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_WithRemoves_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64, null, true, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1, null, false, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_WithRemoves_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1, null, true, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64, null, false, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_WithRemoves_ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64, null, true, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_ClientServer_Backups1() throws Exception {
+ accountsTxReadAll(4, 2, 1, 64, null, false, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_WithRemoves_ClientServer_Backups1() throws Exception {
+ accountsTxReadAll(4, 2, 1, 64, null, true, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64, null, false, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGetAll_WithRemoves_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64, null, true, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1, null, false, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_WithRemoves_SingleNode_SinglePartition() throws Exception {
+ accountsTxReadAll(1, 0, 0, 1, null, true, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64, null, false, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_WithRemoves_SingleNode() throws Exception {
+ accountsTxReadAll(1, 0, 0, 64, null, true, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64, null, false, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_WithRemoves_ClientServer_Backups0() throws Exception {
+ accountsTxReadAll(4, 2, 0, 64, null, true, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_ClientServer_Backups1() throws Exception {
+ accountsTxReadAll(4, 2, 1, 64, null, false, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_WithRemoves_ClientServer_Backups1() throws Exception {
+ accountsTxReadAll(4, 2, 1, 64, null, true, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64, null, false, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_WithRemoves_ClientServer_Backups2() throws Exception {
+ accountsTxReadAll(4, 2, 2, 64, null, true, SCAN, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPessimisticTxGetAllReadsSnapshot_SingleNode_SinglePartition() throws Exception {
+ txReadsSnapshot(1, 0, 0, 1, true, GET);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPessimisticTxGetAllReadsSnapshot_ClientServer() throws Exception {
+ txReadsSnapshot(4, 2, 1, 64, true, GET);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticTxGetAllReadsSnapshot_SingleNode() throws Exception {
+ txReadsSnapshot(1, 0, 0, 64, false, GET);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticTxGetAllReadsSnapshot_SingleNode_SinglePartition() throws Exception {
+ txReadsSnapshot(1, 0, 0, 1, false, GET);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testOptimisticTxGetAllReadsSnapshot_ClientServer() throws Exception {
+ txReadsSnapshot(4, 2, 1, 64, false, GET);
+ }
+
+// TODO: IGNITE-7371
+// /**
+// * @throws Exception If failed.
+// */
+// public void testPessimisticTxScanReadsSnapshot_SingleNode_SinglePartition() throws Exception {
+// txReadsSnapshot(1, 0, 0, 1, true, SCAN);
+// }
+//
+// /**
+// * @throws Exception If failed.
+// */
+// public void testPessimisticTxScanReadsSnapshot_ClientServer() throws Exception {
+// txReadsSnapshot(4, 2, 1, 64, true, SCAN);
+// }
+//
+// /**
+// * @throws Exception If failed.
+// */
+// public void testOptimisticTxScanReadsSnapshot_SingleNode() throws Exception {
+// txReadsSnapshot(1, 0, 0, 64, false, SCAN);
+// }
+//
+// /**
+// * @throws Exception If failed.
+// */
+// public void testOptimisticTxScanReadsSnapshot_SingleNode_SinglePartition() throws Exception {
+// txReadsSnapshot(1, 0, 0, 1, false, SCAN);
+// }
+//
+// /**
+// * @throws Exception If failed.
+// */
+// public void testOptimisticTxScanReadsSnapshot_ClientServer() throws Exception {
+// txReadsSnapshot(4, 2, 1, 64, false, SCAN);
+// }
+
+ /**
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param pessimistic If {@code true} uses pessimistic tx, otherwise optimistic.
+ * @param readMode Read mode.
+ * @throws Exception If failed.
+ */
+ private void txReadsSnapshot(
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ final boolean pessimistic,
+ ReadMode readMode
+ ) throws Exception {
+ final int ACCOUNTS = 20;
+
+ final int ACCOUNT_START_VAL = 1000;
+
+ final int writers = 4;
+
+ final int readers = 4;
+
+ final TransactionConcurrency concurrency;
+ final TransactionIsolation isolation;
+
+ if (pessimistic) {
+ concurrency = PESSIMISTIC;
+ isolation = REPEATABLE_READ;
+ }
+ else {
+ concurrency = OPTIMISTIC;
+ isolation = SERIALIZABLE;
+ }
+
+ final IgniteInClosure<IgniteCache<Object, Object>> init = new IgniteInClosure<IgniteCache<Object, Object>>() {
+ @Override public void apply(IgniteCache<Object, Object> cache) {
+ final IgniteTransactions txs = cache.unwrap(Ignite.class).transactions();
+
+ Map<Integer, MvccTestAccount> accounts = new HashMap<>();
+
+ for (int i = 0; i < ACCOUNTS; i++)
+ accounts.put(i, new MvccTestAccount(ACCOUNT_START_VAL, 1));
+
+ try (Transaction tx = txs.txStart(concurrency, isolation)) {
+ cache.putAll(accounts);
+
+ tx.commit();
+ }
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestAccount> cache = randomCache(caches, rnd);
+
+ try {
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ cnt++;
+
+ Integer id1 = rnd.nextInt(ACCOUNTS);
+ Integer id2 = rnd.nextInt(ACCOUNTS);
+
+ while (id1.equals(id2))
+ id2 = rnd.nextInt(ACCOUNTS);
+
+ TreeSet<Integer> keys = new TreeSet<>();
+
+ keys.add(id1);
+ keys.add(id2);
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ MvccTestAccount a1;
+ MvccTestAccount a2;
+
+ Map<Integer, MvccTestAccount> accounts = checkAndGetAll(false, cache.cache, keys, readMode);
+
+ a1 = accounts.get(id1);
+ a2 = accounts.get(id2);
+
+ assertNotNull(a1);
+ assertNotNull(a2);
+
+ cache.cache.put(id1, new MvccTestAccount(a1.val + 1, 1));
+ cache.cache.put(id2, new MvccTestAccount(a2.val - 1, 1));
+
+ tx.commit();
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, MvccTestAccount> cache = randomCache(caches, rnd);
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ Map<Integer, MvccTestAccount> accounts = new HashMap<>();
+
+ if (pessimistic) {
+ try (Transaction tx = txs.txStart(concurrency, isolation)) {
+ int remaining = ACCOUNTS;
+
+ do {
+ int readCnt = rnd.nextInt(remaining) + 1;
+
+ Set<Integer> readKeys = new TreeSet<>();
+
+ for (int i = 0; i < readCnt; i++)
+ readKeys.add(accounts.size() + i);
+
+ Map<Integer, MvccTestAccount> readRes =
+ checkAndGetAll(false, cache.cache, readKeys, readMode);
+
+ assertEquals(readCnt, readRes.size());
+
+ accounts.putAll(readRes);
+
+ remaining = ACCOUNTS - accounts.size();
+ }
+ while (remaining > 0);
+
+ validateSum(accounts);
+
+ tx.commit();
+
+ cnt++;
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+ else {
+ try (Transaction tx = txs.txStart(concurrency, isolation)) {
+ int remaining = ACCOUNTS;
+
+ do {
+ int readCnt = rnd.nextInt(remaining) + 1;
+
+ if (rnd.nextInt(3) == 0) {
+ for (int i = 0; i < readCnt; i++) {
+ Integer key = rnd.nextInt(ACCOUNTS);
+
+ MvccTestAccount account =
+ (MvccTestAccount)checkAndGet(false, cache.cache, key, readMode);
+
+ assertNotNull(account);
+
+ accounts.put(key, account);
+ }
+ }
+ else {
+ Set<Integer> readKeys = new LinkedHashSet<>();
+
+ for (int i = 0; i < readCnt; i++)
+ readKeys.add(rnd.nextInt(ACCOUNTS));
+
+ Map<Integer, MvccTestAccount> readRes =
+ checkAndGetAll(false, cache.cache, readKeys, readMode);
+
+ assertEquals(readKeys.size(), readRes.size());
+
+ accounts.putAll(readRes);
+ }
+
+ remaining = ACCOUNTS - accounts.size();
+ }
+ while (remaining > 0);
+
+ validateSum(accounts);
+
+ cnt++;
+
+ tx.commit();
+ }
+ catch (TransactionOptimisticException ignore) {
+ // No-op.
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+ }
+
+ info("Reader finished, txs: " + cnt);
+ }
+
+ /**
+ * @param accounts Read accounts.
+ */
+ private void validateSum(Map<Integer, MvccTestAccount> accounts) {
+ int sum = 0;
+
+ for (int i = 0; i < ACCOUNTS; i++) {
+ MvccTestAccount account = accounts.get(i);
+
+ assertNotNull(account);
+
+ sum += account.val;
+ }
+
+ assertEquals(ACCOUNTS * ACCOUNT_START_VAL, sum);
+ }
+ };
+
+ readWriteTest(
+ null,
+ srvs,
+ clients,
+ cacheBackups,
+ cacheParts,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ null,
+ init,
+ writer,
+ reader);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceScanConsistency_SingleNode_SinglePartition() throws Exception {
+ operationsSequenceConsistency(1, 0, 0, 1, SCAN);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceScanConsistency_SingleNode() throws Exception {
+ operationsSequenceConsistency(1, 0, 0, 64, SCAN);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceScanConsistency_ClientServer_Backups0() throws Exception {
+ operationsSequenceConsistency(4, 2, 0, 64, SCAN);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceScanConsistency_ClientServer_Backups1() throws Exception {
+ operationsSequenceConsistency(4, 2, 1, 64, SCAN);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceGetConsistency_SingleNode_SinglePartition() throws Exception {
+ operationsSequenceConsistency(1, 0, 0, 1, GET);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceGetConsistency_SingleNode() throws Exception {
+ operationsSequenceConsistency(1, 0, 0, 64, GET);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceGetConsistency_ClientServer_Backups0() throws Exception {
+ operationsSequenceConsistency(4, 2, 0, 64, GET);
+ }
+
+ /**
+ * @throws Exception If failed
+ */
+ public void testOperationsSequenceGetConsistency_ClientServer_Backups1() throws Exception {
+ operationsSequenceConsistency(4, 2, 1, 64, GET);
+ }
+
+ /**
+ * @param srvs Number of server nodes.
+ * @param clients Number of client nodes.
+ * @param cacheBackups Number of cache backups.
+ * @param cacheParts Number of cache partitions.
+ * @param readMode Read mode.
+ * @throws Exception If failed.
+ */
+ private void operationsSequenceConsistency(
+ final int srvs,
+ final int clients,
+ int cacheBackups,
+ int cacheParts,
+ ReadMode readMode
+ )
+ throws Exception
+ {
+ final int writers = 4;
+
+ final int readers = 4;
+
+ final long time = 10_000;
+
+ final AtomicInteger keyCntr = new AtomicInteger();
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ TestCache<Integer, Value> cache = randomCache(caches, rnd);
+
+ try {
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ Integer key = keyCntr.incrementAndGet();
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.cache.put(key, new Value(idx, cnt++));
+
+ tx.commit();
+ }
+
+ if (key > 100_000)
+ break;
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+
+ info("Writer finished, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Set keys = new HashSet();
+
+ while (!stop.get()) {
+ TestCache<Integer, Value> cache = randomCache(caches, rnd);
+
+ try {
+ Map<Integer, TreeSet<Integer>> vals = new HashMap<>();
+
+ switch (readMode) {
+ case SCAN:
+ for (Cache.Entry<Integer, Value> e : cache.cache) {
+ Value val = e.getValue();
+
+ assertNotNull(val);
+
+ TreeSet<Integer> cntrs = vals.get(val.key);
+
+ if (cntrs == null)
+ vals.put(val.key, cntrs = new TreeSet<>());
+
+ boolean add = cntrs.add(val.cnt);
+
+ assertTrue(add);
+ }
+
+ break;
+
+ case GET:
+ for (int i = keys.size(); i < keyCntr.get(); i++)
+ keys.add(i);
+
+ Iterable<Map.Entry<Integer, Value>> entries = cache.cache.getAll(keys).entrySet();
+
+ for (Map.Entry<Integer, Value> e : entries) {
+ Value val = e.getValue();
+
+ assertNotNull(val);
+
+ TreeSet<Integer> cntrs = vals.get(val.key);
+
+ if (cntrs == null)
+ vals.put(val.key, cntrs = new TreeSet<>());
+
+ boolean add = cntrs.add(val.cnt);
+
+ assertTrue(add);
+ }
+
+ break;
+
+ default:
+ fail("Unsupported read mode: " + readMode.name() + '.');
+ }
+
+ for (TreeSet<Integer> readCntrs : vals.values()) {
+ for (int i = 0; i < readCntrs.size(); i++)
+ assertTrue(readCntrs.contains(i));
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+ }
+ }
+ };
+
+ readWriteTest(
+ null,
+ srvs,
+ clients,
+ cacheBackups,
+ cacheParts,
+ writers,
+ readers,
+ time,
+ null,
+ null,
+ writer,
+ reader);
+ }
+
+ /**
+ * TODO IGNITE-5935 enable when recovery is implemented.
+ *
+ * @throws Exception If failed.
+ */
+ public void _testNodesRestartNoHang() throws Exception {
+ final int srvs = 4;
+ final int clients = 4;
+ final int writers = 6;
+ final int readers = 2;
+
+ final int KEYS = 100_000;
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> writer =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Map<Integer, Integer> map = new TreeMap<>();
+
+ int cnt = 0;
+
+ while (!stop.get()) {
+ int keys = rnd.nextInt(32) + 1;
+
+ while (map.size() < keys)
+ map.put(rnd.nextInt(KEYS), cnt);
+
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+
+ try {
+ IgniteTransactions txs = cache.cache.unwrap(Ignite.class).transactions();
+
+ TransactionConcurrency concurrency;
+ TransactionIsolation isolation;
+
+ switch (rnd.nextInt(3)) {
+ case 0: {
+ concurrency = PESSIMISTIC;
+ isolation = REPEATABLE_READ;
+
+ break;
+ }
+ case 1: {
+ concurrency = OPTIMISTIC;
+ isolation = REPEATABLE_READ;
+
+ break;
+ }
+ case 2: {
+ concurrency = OPTIMISTIC;
+ isolation = SERIALIZABLE;
+
+ break;
+ }
+ default: {
+ fail();
+
+ return;
+ }
+ }
+
+ try (Transaction tx = txs.txStart(concurrency, isolation)) {
+ if (rnd.nextBoolean()) {
+ Map<Integer, Integer> res = checkAndGetAll(false, cache.cache, map.keySet(),
+ rnd.nextBoolean() ? GET : SCAN);
+
+ assertNotNull(res);
+ }
+
+ cache.cache.putAll(map);
+
+ tx.commit();
+ }
+ catch (TransactionOptimisticException e) {
+ assertEquals(SERIALIZABLE, isolation);
+ }
+ catch (Exception e) {
+ Assert.assertTrue("Unexpected error: " + e, X.hasCause(e, ClusterTopologyException.class));
+ }
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ map.clear();
+
+ cnt++;
+ }
+
+ info("Writer done, updates: " + cnt);
+ }
+ };
+
+ GridInClosure3<Integer, List<TestCache>, AtomicBoolean> reader =
+ new GridInClosure3<Integer, List<TestCache>, AtomicBoolean>() {
+ @Override public void apply(Integer idx, List<TestCache> caches, AtomicBoolean stop) {
+ ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ Set<Integer> keys = new LinkedHashSet<>();
+
+ while (!stop.get()) {
+ int keyCnt = rnd.nextInt(64) + 1;
+
+ while (keys.size() < keyCnt)
+ keys.add(rnd.nextInt(KEYS));
+
+ TestCache<Integer, Integer> cache = randomCache(caches, rnd);
+
+ Map<Integer, Integer> map;
+
+ try {
+ map = checkAndGetAll(false, cache.cache, keys, rnd.nextBoolean() ? GET : SCAN);
+
+ assertNotNull(map);
+ }
+ finally {
+ cache.readUnlock();
+ }
+
+ keys.clear();
+ }
+ }
+ };
+
+ readWriteTest(
+ RestartMode.RESTART_RND_SRV,
+ srvs,
+ clients,
+ 1,
+ 256,
+ writers,
+ readers,
+ DFLT_TEST_TIME,
+ null,
+ null,
+ writer,
+ reader);
+
+ for (Ignite node : G.allGrids())
+ checkActiveQueriesCleanup(node);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testActiveQueryCleanupOnNodeFailure() throws Exception {
+ testSpi = true;
+
+ final Ignite srv = startGrid(0);
+
+ srv.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 0, 1024));
+
+ client = true;
+
+ final Ignite client = startGrid(1);
+
+ TestRecordingCommunicationSpi srvSpi = TestRecordingCommunicationSpi.spi(srv);
+
+ srvSpi.blockMessages(GridNearGetResponse.class, getTestIgniteInstanceName(1));
+
+ TestRecordingCommunicationSpi.spi(client).blockMessages(MvccAckRequestQueryCntr.class,
+ getTestIgniteInstanceName(0));
+
+ IgniteInternalFuture<?> fut = GridTestUtils.runAsync(new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ IgniteCache cache = client.cache(DEFAULT_CACHE_NAME);
+
+ cache.getAll(F.asSet(1, 2, 3));
+
+ return null;
+ }
+ });
+
+ srvSpi.waitForBlocked();
+
+ assertFalse(fut.isDone());
+
+ stopGrid(1);
+
+ checkActiveQueriesCleanup(ignite(0));
+
+ verifyCoordinatorInternalState();
+
+ try {
+ fut.get();
+ }
+ catch (Exception ignore) {
+ // No-op.
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRebalanceSimple() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ IgniteCache<Integer, Integer> cache = (IgniteCache)srv0.createCache(
+ cacheConfiguration(PARTITIONED, FULL_SYNC, 0, DFLT_PARTITION_COUNT));
+
+ Map<Integer, Integer> map;
+ Map<Integer, Integer> resMap;
+
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ map = new HashMap<>();
+
+ for (int i = 0; i < DFLT_PARTITION_COUNT * 3; i++)
+ map.put(i, i);
+
+ cache.putAll(map);
+
+ tx.commit();
+ }
+
+ startGrid(1);
+
+ awaitPartitionMapExchange();
+
+ resMap = checkAndGetAll(false, cache, map.keySet(), GET, SCAN);
+
+ assertEquals(map.size(), resMap.size());
+
+ for (int i = 0; i < map.size(); i++)
+ assertEquals(i, (Object)resMap.get(i));
+
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int i = 0; i < DFLT_PARTITION_COUNT * 3; i++)
+ map.put(i, i + 1);
+
+ cache.putAll(map);
+
+ tx.commit();
+ }
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int i = 0; i < DFLT_PARTITION_COUNT * 3; i++)
+ map.put(i, i + 2);
+
+ cache.putAll(map);
+
+ tx.commit();
+ }
+
+ startGrid(2);
+
+ awaitPartitionMapExchange();
+
+ resMap = checkAndGetAll(false, cache, map.keySet(), GET, SCAN);
+
+ assertEquals(map.size(), map.size());
+
+ for (int i = 0; i < map.size(); i++)
+ assertEquals(i + 2, (Object)resMap.get(i));
+
+ // Run fake transaction
+ try (Transaction tx = srv0.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ Integer val = cache.get(0);
+
+ cache.put(0, val);
+
+ tx.commit();
+ }
+
+ resMap = checkAndGetAll(false, cache, map.keySet(), GET, SCAN);
+
+ assertEquals(map.size(), map.size());
+
+ for (int i = 0; i < map.size(); i++)
+ assertEquals(i + 2, (Object)resMap.get(i));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRebalanceWithRemovedValuesSimple() throws Exception {
+ Ignite node = startGrid(0);
+
+ IgniteTransactions txs = node.transactions();
+
+ final IgniteCache<Object, Object> cache = node.createCache(cacheConfiguration(PARTITIONED, FULL_SYNC, 1, 64));
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 0; k < 100; k++)
+ cache.remove(k);
+
+ tx.commit();
+ }
+
+ Map<Object, Object> expVals = new HashMap<>();
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 100; k < 200; k++) {
+ cache.put(k, k);
+
+ expVals.put(k, k);
+ }
+
+ tx.commit();
+ }
+
+ try (Transaction tx = txs.txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ for (int k = 100; k < 200; k++) {
+ if (k % 2 == 0) {
+ cache.remove(k);
+
+ expVals.remove(k);
+ }
+ }
+
+ tx.commit();
+ }
+
+ startGrid(1);
+
+ awaitPartitionMapExchange();
+
+ checkValues(expVals, jcache(1));
+
+ stopGrid(0);
+
+ checkValues(expVals, jcache(1));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxPrepareFailureSimplePessimisticTx() throws Exception {
+ txPrepareFailureSimple(PESSIMISTIC, REPEATABLE_READ);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxPrepareFailureSimpleSerializableTx() throws Exception {
+ txPrepareFailureSimple(OPTIMISTIC, SERIALIZABLE);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxPrepareFailureSimpleOptimisticTx() throws Exception {
+ txPrepareFailureSimple(OPTIMISTIC, REPEATABLE_READ);
+ }
+
+ /**
+ * @param concurrency Transaction concurrency.
+ * @param isolation Transaction isolation.
+ * @throws Exception If failed.
+ */
+ private void txPrepareFailureSimple(
+ final TransactionConcurrency concurrency,
+ final TransactionIsolation isolation
+ ) throws Exception {
+ testSpi = true;
+
+ startGrids(3);
+
+ client = true;
+
+ final Ignite client = startGrid(3);
+
+ final IgniteCache cache = client.createCache(
+ cacheConfiguration(PARTITIONED, FULL_SYNC, 0, DFLT_PARTITION_COUNT));
+
+ final Integer key1 = primaryKey(jcache(1));
+ final Integer key2 = primaryKey(jcache(2));
+
+ TestRecordingCommunicationSpi srv1Spi = TestRecordingCommunicationSpi.spi(ignite(1));
+
+ srv1Spi.blockMessages(GridNearTxPrepareResponse.class, client.name());
+
+ IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() {
+ @Override public Object call() throws Exception {
+ try {
+ try (Transaction tx = client.transactions().txStart(concurrency, isolation)) {
+ cache.put(key1, 1);
+ cache.put(key2, 2);
+
+ tx.commit();
+ }
+
+ fail();
+ }
+ catch (ClusterTopologyException e) {
+ info("Expected exception: " + e);
+
+ assertNotNull(e.retryReadyFuture());
+
+ e.retryReadyFuture().get();
+ }
+
+ return null;
+ }
+ }, "tx-thread");
+
+ srv1Spi.waitForBlocked();
+
+ assertFalse(fut.isDone());
+
+ stopGrid(1);
+
+ fut.get();
+
+ assertNull(cache.get(key1));
+ assertNull(cache.get(key2));
+
+ try (Transaction tx = client.transactions().txStart(concurrency, isolation)) {
+ cache.put(key1, 1);
+ cache.put(key2, 2);
+
+ tx.commit();
+ }
+
+ assertEquals(1, checkAndGet(false, cache, key1, GET, SCAN));
+ assertEquals(2, checkAndGet(false, cache, key2, GET, SCAN));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSerializableTxRemap() throws Exception {
+ testSpi = true;
+
+ startGrids(2);
+
+ client = true;
+
+ final Ignite client = startGrid(2);
+
+ final IgniteCache cache = client.createCache(
+ cacheConfiguration(PARTITIONED, FULL_SYNC, 0, DFLT_PARTITION_COUNT));
+
+ final Map<Object, Object> vals = new HashMap<>();
+
+ for (int i = 0; i < 100; i++)
+ vals.put(i, i);
+
+ TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(ignite(2));
+
+ clientSpi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
+ @Override public boolean apply(ClusterNode node, Message msg) {
+ return msg instanceof GridNearTxPrepareRequest;
+ }
+ });
+
+ IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() {
+ @Override public Object call() throws Exception {
+ try (Transaction tx = client.transactions().txStart(OPTIMISTIC, SERIALIZABLE)) {
+ cache.putAll(vals);
+
+ tx.commit();
+ }
+
+ return null;
+ }
+ }, "tx-thread");
+
+ clientSpi.waitForBlocked(2);
+
+ this.client = false;
+
+ startGrid(3);
+
+ assertFalse(fut.isDone());
+
+ clientSpi.stopBlock();
+
+ fut.get();
+
+ for (Ignite node : G.allGrids())
+ checkValues(vals, node.cache(cache.getName()));
+ }
+
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMvccCoordinatorChangeSimple() throws Exception {
+ Ignite srv0 = startGrid(0);
+
+ final List<String> cacheNames = new ArrayList<>();
+
+ for (CacheConfiguration ccfg : cacheConfigurations()) {
+ ccfg.setName("cache-" + cacheNames.size());
+
+ cacheNames.add(ccfg.getName());
+
+ srv0.createCache(ccfg);
+ }
+
+ checkPutGet(cacheNames);
+
+ for (int i = 0; i < 3; i++) {
+ startGrid(i + 1);
+
+ checkPutGet(cacheNames);
+
+ checkCoordinatorsConsistency(null);
+ }
+
+ client = true;
+
+ for (int i = 0; i < 3; i++) {
+ Ignite node = startGrid(i + 4);
+
+ // Init client caches outside of transactions.
+ for (String cacheName : cacheNames)
+ node.cache(cacheName);
+
+ checkPutGet(cacheNames);
+
+ checkCoordinatorsConsistency(null);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ stopGrid(i);
+
+ awaitPartitionMapExchange();
+
+ checkPutGet(cacheNames);
+
+ checkCoordinatorsConsistency(null);
+ }
+ }
+
+ /**
+ * @param cacheNames Cache names.
+ */
+ private void checkPutGet(List<String> cacheNames) {
+ List<Ignite> nodes = G.allGrids();
+
+ assertFalse(nodes.isEmpty());
+
+ Ignite putNode = nodes.get(ThreadLocalRandom.current().nextInt(nodes.size()));
+
+ Map<Integer, Integer> vals = new HashMap();
+
+ Integer val = ThreadLocalRandom.current().nextInt();
+
+ for (int i = 0; i < 10; i++)
+ vals.put(i, val);
+
+ TransactionConcurrency concurrency;
+ TransactionIsolation isolation;
+
+ if (ThreadLocalRandom.current().nextBoolean()) {
+ concurrency = PESSIMISTIC;
+ isolation = REPEATABLE_READ;
+ }
+ else {
+ concurrency = OPTIMISTIC;
+ isolation = SERIALIZABLE;
+ }
+
+ try (Transaction tx = putNode.transactions().txStart(concurrency, isolation)) {
+ for (String cacheName : cacheNames)
+ putNode.cache(cacheName).putAll(vals);
+
+ tx.commit();
+ }
+
+ for (Ignite node : nodes) {
+ for (String cacheName : cacheNames) {
+ Map<Object, Object> res = checkAndGetAll(false, node.cache(cacheName), vals.keySet(), SCAN, GET);
+
+ assertEquals(vals, res);
+ }
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMvccCoordinatorInfoConsistency() throws Exception {
+ for (int i = 0; i < 4; i++) {
+ startGrid(i);
+
+ if (persistence && i == 0)
+ ignite(i).active(true);
+
+ checkCoordinatorsConsistency(i + 1);
+ }
+
+ client = true;
+
+ startGrid(4);
+
+ checkCoordinatorsConsistency(5);
+
+ startGrid(5);
+
+ checkCoordinatorsConsistency(6);
+
+ client = false;
+
+ stopGrid(0);
+
+ awaitPartitionMapExchange();
+
+ checkCoordinatorsConsistency(5);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMvccCoordinatorInfoConsistency_Persistence() throws Exception {
+ persistence = true;
+
+ testMvccCoordinatorInfoConsistency();
+ }
+
+ /**
+ * @param expNodes Expected nodes number.
+ */
+ private void checkCoordinatorsConsistency(@Nullable Integer expNodes) {
+ List<Ignite> nodes = G.allGrids();
+
+ if (expNodes != null)
+ assertEquals(expNodes, (Integer)nodes.size());
+
+ MvccCo
<TRUNCATED>
[24/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
index b9ab76a..f160549 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneGridKernalContext.java
@@ -49,6 +49,7 @@ import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor;
import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccProcessor;
import org.apache.ignite.internal.processors.cache.persistence.filename.PdsFolderSettings;
import org.apache.ignite.internal.processors.cache.persistence.filename.PdsFoldersResolver;
import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
@@ -467,6 +468,11 @@ public class StandaloneGridKernalContext implements GridKernalContext {
}
/** {@inheritDoc} */
+ @Override public MvccProcessor coordinators() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean invalid() {
return false;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
index 1807d1d..65f0aae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
@@ -35,6 +35,9 @@ public final class RecordTypes {
DELTA_TYPE_SET.add(WALRecord.RecordType.DATA_PAGE_INSERT_FRAGMENT_RECORD);
DELTA_TYPE_SET.add(WALRecord.RecordType.DATA_PAGE_REMOVE_RECORD);
DELTA_TYPE_SET.add(WALRecord.RecordType.DATA_PAGE_SET_FREE_LIST_PAGE);
+ DELTA_TYPE_SET.add(WALRecord.RecordType.MVCC_DATA_PAGE_MARK_UPDATED_RECORD);
+ DELTA_TYPE_SET.add(WALRecord.RecordType.MVCC_DATA_PAGE_TX_STATE_HINT_UPDATED_RECORD);
+ DELTA_TYPE_SET.add(WALRecord.RecordType.MVCC_DATA_PAGE_NEW_TX_STATE_HINT_UPDATED_RECORD);
DELTA_TYPE_SET.add(WALRecord.RecordType.BTREE_META_PAGE_INIT_ROOT);
DELTA_TYPE_SET.add(WALRecord.RecordType.BTREE_META_PAGE_ADD_ROOT);
DELTA_TYPE_SET.add(WALRecord.RecordType.BTREE_META_PAGE_CUT_ROOT);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
index ad06090..aa89c5a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
@@ -41,6 +41,8 @@ import org.apache.ignite.internal.pagemem.wal.record.TxRecord;
import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageInsertFragmentRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageInsertRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccUpdateNewTxStateHintRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccUpdateTxStateHintRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageRemoveRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageSetFreeListPageRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageUpdateRecord;
@@ -61,6 +63,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateLastSuc
import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateLastSuccessfulSnapshotId;
import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateNextSnapshotId;
import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdatePartitionDataRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccMarkUpdatedRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.NewRootInitRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.PageListMetaResetCountRecord;
import org.apache.ignite.internal.pagemem.wal.record.delta.PagesListAddPageRecord;
@@ -194,6 +197,15 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
case DATA_PAGE_SET_FREE_LIST_PAGE:
return 4 + 8 + 8;
+ case MVCC_DATA_PAGE_MARK_UPDATED_RECORD:
+ return 4 + 8 + 4 + 8 + 8 + 4;
+
+ case MVCC_DATA_PAGE_TX_STATE_HINT_UPDATED_RECORD:
+ return 4 + 8 + 4 + 1;
+
+ case MVCC_DATA_PAGE_NEW_TX_STATE_HINT_UPDATED_RECORD:
+ return 4 + 8 + 4 + 1;
+
case INIT_NEW_PAGE_RECORD:
return 4 + 8 + 2 + 2 + 8;
@@ -504,6 +516,41 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
break;
+ case MVCC_DATA_PAGE_MARK_UPDATED_RECORD:
+ cacheId = in.readInt();
+ pageId = in.readLong();
+
+ itemId = in.readInt();
+ long newMvccCrd = in.readLong();
+ long newMvccCntr = in.readLong();
+ int newMvccOpCntr = in.readInt();
+
+ res = new DataPageMvccMarkUpdatedRecord(cacheId, pageId, itemId, newMvccCrd, newMvccCntr, newMvccOpCntr);
+
+ break;
+
+ case MVCC_DATA_PAGE_TX_STATE_HINT_UPDATED_RECORD:
+ cacheId = in.readInt();
+ pageId = in.readLong();
+
+ itemId = in.readInt();
+ byte txState = in.readByte();
+
+ res = new DataPageMvccUpdateTxStateHintRecord(cacheId, pageId, itemId, txState);
+
+ break;
+
+ case MVCC_DATA_PAGE_NEW_TX_STATE_HINT_UPDATED_RECORD:
+ cacheId = in.readInt();
+ pageId = in.readLong();
+
+ itemId = in.readInt();
+ byte newTxState = in.readByte();
+
+ res = new DataPageMvccUpdateNewTxStateHintRecord(cacheId, pageId, itemId, newTxState);
+
+ break;
+
case INIT_NEW_PAGE_RECORD:
cacheId = in.readInt();
pageId = in.readLong();
@@ -1035,6 +1082,41 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
break;
+ case MVCC_DATA_PAGE_MARK_UPDATED_RECORD:
+ DataPageMvccMarkUpdatedRecord rmvRec = (DataPageMvccMarkUpdatedRecord)rec;
+
+ buf.putInt(rmvRec.groupId());
+ buf.putLong(rmvRec.pageId());
+
+ buf.putInt(rmvRec.itemId());
+ buf.putLong(rmvRec.newMvccCrd());
+ buf.putLong(rmvRec.newMvccCntr());
+ buf.putInt(rmvRec.newMvccOpCntr());
+
+ break;
+
+ case MVCC_DATA_PAGE_TX_STATE_HINT_UPDATED_RECORD:
+ DataPageMvccUpdateTxStateHintRecord txStRec = (DataPageMvccUpdateTxStateHintRecord)rec;
+
+ buf.putInt(txStRec.groupId());
+ buf.putLong(txStRec.pageId());
+
+ buf.putInt(txStRec.itemId());
+ buf.put(txStRec.txState());
+
+ break;
+
+ case MVCC_DATA_PAGE_NEW_TX_STATE_HINT_UPDATED_RECORD:
+ DataPageMvccUpdateNewTxStateHintRecord newTxStRec = (DataPageMvccUpdateNewTxStateHintRecord)rec;
+
+ buf.putInt(newTxStRec.groupId());
+ buf.putLong(newTxStRec.pageId());
+
+ buf.putInt(newTxStRec.itemId());
+ buf.put(newTxStRec.txState());
+
+ break;
+
case INIT_NEW_PAGE_RECORD:
InitNewPageRecord inpRec = (InitNewPageRecord)rec;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
index aac1659..c5f64c9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.IgniteInterruptedCheckedException;
import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.managers.communication.GridIoPolicy;
import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
import org.apache.ignite.internal.util.GridBoundedConcurrentOrderedSet;
import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
@@ -195,6 +196,9 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
*/
@SuppressWarnings("unchecked")
@Override void processQueryRequest(UUID sndId, GridCacheQueryRequest req) {
+ assert req.mvccSnapshot() != null || !cctx.mvccEnabled() || req.cancel() ||
+ (req.type() == null && !req.fields()) : req; // Last assertion means next page request.
+
if (req.cancel()) {
cancelIds.add(new CancelMessageId(req.id(), sndId));
@@ -277,7 +281,8 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
req.includeMetaData(),
req.keepBinary(),
req.subjectId(),
- req.taskHash()
+ req.taskHash(),
+ req.mvccSnapshot()
);
return new GridCacheQueryInfo(
@@ -531,6 +536,8 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
String clsName = qry.query().queryClassName();
+ MvccSnapshot mvccSnapshot = qry.query().mvccSnapshot();
+
final GridCacheQueryRequest req = new GridCacheQueryRequest(
cctx.cacheId(),
reqId,
@@ -551,6 +558,7 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
qry.query().subjectId(),
qry.query().taskHash(),
queryTopologyVersion(),
+ mvccSnapshot,
// Force deployment anyway if scan query is used.
cctx.deploymentEnabled() || (qry.query().scanFilter() != null && cctx.gridDeploy().enabled()));
@@ -581,6 +589,7 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
Collection<ClusterNode> nodes) throws IgniteCheckedException {
assert !cctx.isLocal() : cctx.name();
assert qry.type() == GridCacheQueryType.SCAN: qry;
+ assert qry.mvccSnapshot() != null || !cctx.mvccEnabled();
GridCloseableIterator locIter0 = null;
@@ -606,7 +615,7 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
final GridCacheQueryBean bean = new GridCacheQueryBean(qry, null, qry.<K, V>transform(), null);
- final CacheQueryFuture fut = (CacheQueryFuture)queryDistributed(bean, nodes);
+ final CacheQueryFuture fut = queryDistributed(bean, nodes);
return new GridCloseableIteratorAdapter() {
/** */
@@ -749,6 +758,7 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
qry.query().subjectId(),
qry.query().taskHash(),
queryTopologyVersion(),
+ null,
cctx.deploymentEnabled());
addQueryFuture(req.id(), fut);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryAdapter.java
index 51fdd58..f21a22f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryAdapter.java
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -29,6 +30,7 @@ import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.query.Query;
@@ -43,6 +45,9 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtUnreservedPartitionException;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
import org.apache.ignite.internal.util.GridEmptyCloseableIterator;
@@ -60,6 +65,7 @@ import org.apache.ignite.lang.IgniteBiPredicate;
import org.apache.ignite.lang.IgniteClosure;
import org.apache.ignite.lang.IgniteReducer;
import org.apache.ignite.plugin.security.SecurityPermission;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.cache.CacheMode.LOCAL;
@@ -127,6 +133,9 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
/** */
private int taskHash;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/**
* @param cctx Context.
* @param type Query type.
@@ -213,6 +222,7 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
* @param keepBinary Keep binary flag.
* @param subjId Security subject ID.
* @param taskHash Task hash.
+ * @param mvccSnapshot Mvcc version.
*/
public GridCacheQueryAdapter(GridCacheContext<?, ?> cctx,
GridCacheQueryType type,
@@ -229,7 +239,8 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
boolean incMeta,
boolean keepBinary,
UUID subjId,
- int taskHash) {
+ int taskHash,
+ MvccSnapshot mvccSnapshot) {
this.cctx = cctx;
this.type = type;
this.log = log;
@@ -246,6 +257,14 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
this.keepBinary = keepBinary;
this.subjId = subjId;
this.taskHash = taskHash;
+ this.mvccSnapshot = mvccSnapshot;
+ }
+
+ /**
+ * @return MVCC snapshot.
+ */
+ @Nullable MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
}
/**
@@ -400,7 +419,7 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
*/
@SuppressWarnings("unchecked")
@Nullable public <K, V> IgniteClosure<Map.Entry<K, V>, Object> transform() {
- return (IgniteClosure<Map.Entry<K, V>, Object>) transform;
+ return (IgniteClosure<Map.Entry<K, V>, Object>)transform;
}
/**
@@ -519,15 +538,23 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
final GridCacheQueryManager qryMgr = cctx.queries();
+ MvccQueryTracker mvccTracker = null;
+
+ if (cctx.mvccEnabled() && mvccSnapshot == null)
+ mvccSnapshot = (mvccTracker = MvccUtils.mvccTracker(cctx, false)).snapshot();
+
boolean loc = nodes.size() == 1 && F.first(nodes).id().equals(cctx.localNodeId());
- if (loc)
- return qryMgr.scanQueryLocal(this, true);
+ GridCloseableIterator it;
- if (part != null)
- return new ScanQueryFallbackClosableIterator(part, this, qryMgr, cctx);
+ if (loc)
+ it = qryMgr.scanQueryLocal(this, true);
+ else if (part != null)
+ it = new ScanQueryFallbackClosableIterator(part, this, qryMgr, cctx);
else
- return qryMgr.scanQueryDistributed(this, nodes);
+ it = qryMgr.scanQueryDistributed(this, nodes);
+
+ return mvccTracker != null ? new MvccTrackingIterator(it, mvccTracker) : it;
}
/**
@@ -781,7 +808,7 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
* @return Cache entry
*/
private Object convert(Object obj) {
- if(qry.transform() != null)
+ if (qry.transform() != null)
return obj;
Map.Entry e = (Map.Entry)obj;
@@ -853,4 +880,93 @@ public class GridCacheQueryAdapter<T> implements CacheQuery<T> {
t.get2().cancel();
}
}
+
+ /**
+ * Wrapper for an MVCC-related iterators.
+ */
+ private static class MvccTrackingIterator implements GridCloseableIterator {
+ /** Serial version uid. */
+ private static final long serialVersionUID = -1905248502802333832L;
+ /** Underlying iterator. */
+ private final GridCloseableIterator it;
+
+ /** Query MVCC tracker. */
+ private final MvccQueryTracker mvccTracker;
+
+ /**
+ * Constructor.
+ *
+ * @param it Underlying iterator.
+ * @param mvccTracker Query MVCC tracker.
+ */
+ MvccTrackingIterator(GridCloseableIterator it, MvccQueryTracker mvccTracker) {
+ assert it != null && mvccTracker != null;
+
+ this.it = it;
+ this.mvccTracker = mvccTracker;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void close() throws IgniteCheckedException {
+ if (isClosed())
+ return;
+
+ try {
+ it.close();
+ }
+ finally {
+ mvccTracker.onDone();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isClosed() {
+ return it.isClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasNext() {
+ boolean hasNext = it.hasNext();
+
+ if (!hasNext)
+ try {
+ close();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+
+ return hasNext;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasNextX() throws IgniteCheckedException {
+ boolean hasNext = it.hasNext();
+
+ if (!hasNext)
+ close();
+
+ return hasNext;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object nextX() throws IgniteCheckedException {
+ return it.nextX();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeX() throws IgniteCheckedException {
+ it.removeX();
+ }
+
+ /** {@inheritDoc} */
+ @NotNull @Override public Iterator iterator() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object next() {
+ return it.next();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
index 37bdb80..c209602 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
@@ -380,8 +380,8 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
* @param prevRowAvailable Whether previous row is available.
* @throws IgniteCheckedException In case of error.
*/
- public void store(CacheDataRow newRow, @Nullable CacheDataRow prevRow, boolean prevRowAvailable)
- throws IgniteCheckedException {
+ public void store(CacheDataRow newRow, @Nullable CacheDataRow prevRow,
+ boolean prevRowAvailable) throws IgniteCheckedException {
assert enabled();
assert newRow != null && newRow.value() != null && newRow.link() != 0 : newRow;
@@ -414,7 +414,8 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
* @param prevRow Previous row.
* @throws IgniteCheckedException Thrown in case of any errors.
*/
- public void remove(KeyCacheObject key, @Nullable CacheDataRow prevRow) throws IgniteCheckedException {
+ public void remove(KeyCacheObject key, @Nullable CacheDataRow prevRow)
+ throws IgniteCheckedException {
if (!QueryUtils.isEnabled(cctx.config()))
return; // No-op.
@@ -803,6 +804,8 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
private GridCloseableIterator scanIterator(final GridCacheQueryAdapter<?> qry, IgniteClosure transformer,
boolean locNode)
throws IgniteCheckedException {
+ assert !cctx.mvccEnabled() || qry.mvccSnapshot() != null;
+
final IgniteBiPredicate<K, V> keyValFilter = qry.scanFilter();
final InternalScanFilter<K,V> intFilter = keyValFilter != null ? new InternalScanFilter<>(keyValFilter) : null;
@@ -843,13 +846,12 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
locPart = locPart0;
- it = cctx.offheap().cachePartitionIterator(cctx.cacheId(), part);
+ it = cctx.offheap().cachePartitionIterator(cctx.cacheId(), part, qry.mvccSnapshot());
}
else {
locPart = null;
- // TODO shouldn't we reserve all involved partitions?
- it = cctx.offheap().cacheIterator(cctx.cacheId(), true, backups, topVer);
+ it = cctx.offheap().cacheIterator(cctx.cacheId(), true, backups, topVer, qry.mvccSnapshot());
}
return new ScanQueryIterator(it, qry, topVer, locPart, keyValFilter, transformer, locNode, cctx, log);
@@ -2923,11 +2925,13 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
boolean locNode,
GridCacheContext cctx,
IgniteLogger log) {
+
this.it = it;
this.topVer = topVer;
this.locPart = locPart;
this.intScanFilter = scanFilter != null ? new InternalScanFilter<>(scanFilter) : null;
this.cctx = cctx;
+
this.log = log;
this.locNode = locNode;
@@ -2938,7 +2942,7 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
readEvt = cctx.events().isRecordable(EVT_CACHE_QUERY_OBJECT_READ) &&
cctx.gridEvents().hasListener(EVT_CACHE_QUERY_OBJECT_READ);
- if(readEvt){
+ if (readEvt){
taskName = cctx.kernalContext().task().resolveTaskName(qry.taskHash());
subjId = qry.subjectId();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
index 9dc7817..ebbca35 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
@@ -26,8 +26,8 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.CU;
@@ -129,6 +129,9 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
/** */
private AffinityTopologyVersion topVer;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/**
* Required by {@link Externalizable}
*/
@@ -223,6 +226,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
* @param subjId Subject ID.
* @param taskHash Task name hash code.
* @param topVer Topology version.
+ * @param mvccSnapshot Mvcc snapshot.
* @param addDepInfo Deployment info flag.
*/
public GridCacheQueryRequest(
@@ -245,6 +249,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
UUID subjId,
int taskHash,
AffinityTopologyVersion topVer,
+ MvccSnapshot mvccSnapshot,
boolean addDepInfo
) {
assert type != null || fields;
@@ -270,9 +275,17 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
this.subjId = subjId;
this.taskHash = taskHash;
this.topVer = topVer;
+ this.mvccSnapshot = mvccSnapshot;
this.addDepInfo = addDepInfo;
}
+ /**
+ * @return Mvcc version.
+ */
+ @Nullable MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
/** {@inheritDoc} */
@Override public AffinityTopologyVersion topologyVersion() {
return topVer != null ? topVer : AffinityTopologyVersion.NONE;
@@ -573,48 +586,54 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
writer.incrementState();
case 15:
- if (!writer.writeInt("pageSize", pageSize))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 16:
- if (!writer.writeInt("part", part))
+ if (!writer.writeInt("pageSize", pageSize))
return false;
writer.incrementState();
case 17:
- if (!writer.writeByteArray("rdcBytes", rdcBytes))
+ if (!writer.writeInt("part", part))
return false;
writer.incrementState();
case 18:
- if (!writer.writeUuid("subjId", subjId))
+ if (!writer.writeByteArray("rdcBytes", rdcBytes))
return false;
writer.incrementState();
case 19:
- if (!writer.writeInt("taskHash", taskHash))
+ if (!writer.writeUuid("subjId", subjId))
return false;
writer.incrementState();
case 20:
- if (!writer.writeMessage("topVer", topVer))
+ if (!writer.writeInt("taskHash", taskHash))
return false;
writer.incrementState();
case 21:
- if (!writer.writeByteArray("transBytes", transBytes))
+ if (!writer.writeMessage("topVer", topVer))
return false;
writer.incrementState();
case 22:
+ if (!writer.writeByteArray("transBytes", transBytes))
+ return false;
+
+ writer.incrementState();
+
+ case 23:
if (!writer.writeByte("type", type != null ? (byte)type.ordinal() : -1))
return false;
@@ -733,7 +752,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 15:
- pageSize = reader.readInt("pageSize");
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -741,7 +760,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 16:
- part = reader.readInt("part");
+ pageSize = reader.readInt("pageSize");
if (!reader.isLastRead())
return false;
@@ -749,7 +768,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 17:
- rdcBytes = reader.readByteArray("rdcBytes");
+ part = reader.readInt("part");
if (!reader.isLastRead())
return false;
@@ -757,7 +776,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 18:
- subjId = reader.readUuid("subjId");
+ rdcBytes = reader.readByteArray("rdcBytes");
if (!reader.isLastRead())
return false;
@@ -765,7 +784,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 19:
- taskHash = reader.readInt("taskHash");
+ subjId = reader.readUuid("subjId");
if (!reader.isLastRead())
return false;
@@ -773,7 +792,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 20:
- topVer = reader.readMessage("topVer");
+ taskHash = reader.readInt("taskHash");
if (!reader.isLastRead())
return false;
@@ -781,7 +800,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 21:
- transBytes = reader.readByteArray("transBytes");
+ topVer = reader.readMessage("topVer");
if (!reader.isLastRead())
return false;
@@ -789,6 +808,14 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
reader.incrementState();
case 22:
+ transBytes = reader.readByteArray("transBytes");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 23:
byte typeOrd;
typeOrd = reader.readByte("type");
@@ -812,7 +839,7 @@ public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCac
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 23;
+ return 24;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
index 2547e1b..5dab5fd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/IgniteQueryErrorCode.java
@@ -106,6 +106,20 @@ public final class IgniteQueryErrorCode {
/** Attempt to INSERT, UPDATE or MERGE value that exceed maximum column length. */
public final static int TOO_LONG_VALUE = 4008;
+ /* 5xxx - transactions related runtime errors. */
+
+ /** Transaction is already open. */
+ public final static int TRANSACTION_EXISTS = 5001;
+
+ /** MVCC disabled. */
+ public final static int MVCC_DISABLED = 5002;
+
+ /** Transaction type mismatch (SQL/non SQL). */
+ public final static int TRANSACTION_TYPE_MISMATCH = 5003;
+
+ /** Transaction is already completed. */
+ public final static int TRANSACTION_COMPLETED = 5004;
+
/** */
private IgniteQueryErrorCode() {
// No-op.
@@ -159,6 +173,12 @@ public final class IgniteQueryErrorCode {
case KEY_UPDATE:
return SqlStateCode.PARSING_EXCEPTION;
+ case MVCC_DISABLED:
+ case TRANSACTION_EXISTS:
+ case TRANSACTION_TYPE_MISMATCH:
+ case TRANSACTION_COMPLETED:
+ return SqlStateCode.TRANSACTION_STATE_EXCEPTION;
+
default:
return SqlStateCode.INTERNAL_ERROR;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/SqlFieldsQueryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/SqlFieldsQueryEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/SqlFieldsQueryEx.java
index ff10e3d..676e61c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/SqlFieldsQueryEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/SqlFieldsQueryEx.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.util.typedef.F;
/**
@@ -36,6 +37,12 @@ public final class SqlFieldsQueryEx extends SqlFieldsQuery {
/** Whether server side DML should be enabled. */
private boolean skipReducerOnUpdate;
+ /** Auto commit flag. */
+ private boolean autoCommit = true;
+
+ /** Nested transactions handling mode. */
+ private NestedTxMode nestedTxMode = NestedTxMode.DEFAULT;
+
/** Batched arguments list. */
private List<Object[]> batchedArgs;
@@ -57,6 +64,8 @@ public final class SqlFieldsQueryEx extends SqlFieldsQuery {
this.isQry = qry.isQry;
this.skipReducerOnUpdate = qry.skipReducerOnUpdate;
+ this.autoCommit = qry.autoCommit;
+ this.nestedTxMode = qry.nestedTxMode;
this.batchedArgs = qry.batchedArgs;
}
@@ -159,6 +168,36 @@ public final class SqlFieldsQueryEx extends SqlFieldsQuery {
return skipReducerOnUpdate;
}
+ /**
+ * @return Nested transactions handling mode - behavior when the user attempts to open a transaction in scope of
+ * another transaction.
+ */
+ public NestedTxMode getNestedTxMode() {
+ return nestedTxMode;
+ }
+
+ /**
+ * @param nestedTxMode Nested transactions handling mode - behavior when the user attempts to open a transaction
+ * in scope of another transaction.
+ */
+ public void setNestedTxMode(NestedTxMode nestedTxMode) {
+ this.nestedTxMode = nestedTxMode;
+ }
+
+ /**
+ * @return Auto commit flag.
+ */
+ public boolean isAutoCommit() {
+ return autoCommit;
+ }
+
+ /**
+ * @param autoCommit Auto commit flag.
+ */
+ public void setAutoCommit(boolean autoCommit) {
+ this.autoCommit = autoCommit;
+ }
+
/** {@inheritDoc} */
@Override public SqlFieldsQuery copy() {
return new SqlFieldsQueryEx(this);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
index 4005dd8..996e7f4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
@@ -29,6 +29,7 @@ import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -76,7 +77,6 @@ import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.plugin.security.SecurityPermission;
import org.apache.ignite.resources.LoggerResource;
import org.jetbrains.annotations.Nullable;
-import java.util.concurrent.ConcurrentHashMap;
import static javax.cache.event.EventType.CREATED;
import static javax.cache.event.EventType.EXPIRED;
@@ -481,6 +481,11 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
final boolean keepBinary,
final boolean includeExpired) throws IgniteCheckedException
{
+ //TODO IGNITE-7953
+ if (!cctx.atomic() && cctx.kernalContext().config().isMvccEnabled())
+ throw new UnsupportedOperationException("Continuous queries are not supported for transactional caches " +
+ "when MVCC is enabled.");
+
IgniteOutClosure<CacheContinuousQueryHandler> clsr;
if (rmtTransFactory != null) {
@@ -741,7 +746,8 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
final Iterator<CacheDataRow> it = cctx.offheap().cacheIterator(cctx.cacheId(),
true,
true,
- AffinityTopologyVersion.NONE);
+ AffinityTopologyVersion.NONE,
+ null);
locLsnr.onUpdated(new Iterable<CacheEntryEvent>() {
@Override public Iterator<CacheEntryEvent> iterator() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteInternalTx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteInternalTx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteInternalTx.java
index 9e06d9d..4acf078 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteInternalTx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteInternalTx.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedExceptio
import org.apache.ignite.internal.processors.cache.GridCacheFilterFailedException;
import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
import org.apache.ignite.internal.util.lang.GridTuple;
@@ -262,6 +263,11 @@ public interface IgniteInternalTx {
public boolean activeCachesDeploymentEnabled();
/**
+ * @param depEnabled Flag indicating whether deployment is enabled for caches from this transaction or not.
+ */
+ public void activeCachesDeploymentEnabled(boolean depEnabled);
+
+ /**
* Attempts to set topology version and returns the current value.
* If topology version was previously set, then it's value will
* be returned (but not updated).
@@ -634,4 +640,14 @@ public interface IgniteInternalTx {
* @param e Commit error.
*/
public void commitError(Throwable e);
+
+ /**
+ * @param mvccSnapshot Mvcc snapshot.
+ */
+ public void mvccSnapshot(MvccSnapshot mvccSnapshot);
+
+ /**
+ * @return Mvcc snapshot.
+ */
+ public MvccSnapshot mvccSnapshot();
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTransactionsImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTransactionsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTransactionsImpl.java
index 25ba849..11bf219 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTransactionsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTransactionsImpl.java
@@ -31,9 +31,9 @@ import org.apache.ignite.lang.IgniteClosure;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionException;
import org.apache.ignite.transactions.TransactionIsolation;
import org.apache.ignite.transactions.TransactionMetrics;
-import org.apache.ignite.transactions.TransactionException;
import org.jetbrains.annotations.Nullable;
/**
@@ -48,6 +48,7 @@ public class IgniteTransactionsImpl<K, V> implements IgniteTransactionsEx {
/**
* @param cctx Cache shared context.
+ * @param lb Label.
*/
public IgniteTransactionsImpl(GridCacheSharedContext<K, V> cctx, @Nullable String lb) {
this.cctx = cctx;
@@ -175,6 +176,7 @@ public class IgniteTransactionsImpl<K, V> implements IgniteTransactionsEx {
isolation,
timeout,
true,
+ null,
txSize,
lb
);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
index bdd0c53..ee5a58e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
@@ -53,6 +53,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheInvokeEntry;
import org.apache.ignite.internal.processors.cache.CacheLazyEntry;
import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.CacheOperationContext;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
@@ -62,6 +63,8 @@ import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheEntry;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
import org.apache.ignite.internal.processors.cache.version.GridCacheLazyPlainVersionedEntry;
@@ -71,6 +74,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEnt
import org.apache.ignite.internal.processors.cluster.BaselineTopology;
import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.GridIntIterator;
import org.apache.ignite.internal.util.GridSetWrapper;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter;
@@ -84,6 +88,7 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.thread.IgniteThread;
import org.apache.ignite.transactions.TransactionConcurrency;
@@ -263,6 +268,13 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
/** UUID to consistent id mapper. */
protected ConsistentIdMapper consistentIdMapper;
+ /** Mvcc tx update snapshot. */
+ protected volatile MvccSnapshot mvccSnapshot;
+
+ /** Rollback finish future. */
+ @GridToStringExclude
+ private volatile IgniteInternalFuture rollbackFut;
+
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -382,6 +394,18 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
}
/**
+ * @return Mvcc info.
+ */
+ @Override @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void mvccSnapshot(MvccSnapshot mvccSnapshot) {
+ this.mvccSnapshot = mvccSnapshot;
+ }
+
+ /**
* @return Shared cache context.
*/
public GridCacheSharedContext<?, ?> context() {
@@ -687,6 +711,20 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
}
/**
+ * @return Rollback future.
+ */
+ public IgniteInternalFuture rollbackFuture() {
+ return rollbackFut;
+ }
+
+ /**
+ * @param fut Rollback future.
+ */
+ public void rollbackFuture(IgniteInternalFuture fut) {
+ rollbackFut = fut;
+ }
+
+ /**
* Gets remaining allowed transaction time.
*
* @return Remaining transaction time. {@code 0} if timeout isn't specified. {@code -1} if time is out.
@@ -1109,9 +1147,62 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
if (state != ACTIVE && state != SUSPENDED)
seal();
- if (cctx.wal() != null && cctx.tm().logTxRecords() && txNodes != null) {
+ if (state == PREPARED || state == COMMITTED || state == ROLLED_BACK) {
+ if (mvccSnapshot != null) {
+ byte txState;
+
+ switch (state) {
+ case PREPARED:
+ txState = TxState.PREPARED;
+ break;
+ case ROLLED_BACK:
+ txState = TxState.ABORTED;
+ break;
+ case COMMITTED:
+ txState = TxState.COMMITTED;
+ break;
+ default:
+ throw new IllegalStateException("Illegal state: " + state);
+ }
+
+ try {
+ if (!cctx.localNode().isClient()) {
+ if (dht() && remote())
+ cctx.coordinators().updateState(mvccSnapshot, txState, false);
+ else if (local()) {
+ IgniteInternalFuture<?> rollbackFut = rollbackFuture();
+
+ boolean syncUpdate = txState == TxState.PREPARED || txState == TxState.COMMITTED ||
+ rollbackFut == null || rollbackFut.isDone();
+
+ if (syncUpdate)
+ cctx.coordinators().updateState(mvccSnapshot, txState);
+ else {
+ // If tx was aborted, we need to wait tx log is updated on all backups.
+ rollbackFut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ try {
+ cctx.coordinators().updateState(mvccSnapshot, txState);
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to log TxState: " + txState, e);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to log TxState: " + txState, e);
+
+ throw new IgniteException("Failed to log TxState: " + txState, e);
+ }
+ }
+
// Log tx state change to WAL.
- if (state == PREPARED || state == COMMITTED || state == ROLLED_BACK) {
+ if (cctx.wal() != null && cctx.tm().logTxRecords() && txNodes != null) {
+
BaselineTopology baselineTop = cctx.kernalContext().state().clusterState().baselineTopology();
Map<Short, Collection<Short>> participatingNodes = consistentIdMapper
@@ -1569,7 +1660,8 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
/*closure name */recordEvt ? F.first(txEntry.entryProcessors()).get1() : null,
resolveTaskName(),
null,
- keepBinary);
+ keepBinary,
+ null); // TODO IGNITE-7371
}
boolean modified = false;
@@ -1757,6 +1849,32 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
}
/**
+ * Notify Dr on tx finished.
+ *
+ * @param commit {@code True} if commited, {@code False} otherwise.
+ */
+ protected void notifyDrManager(boolean commit) {
+ if (system() || internal())
+ return;
+
+ IgniteTxState txState = txState();
+
+ if (mvccSnapshot == null || txState.cacheIds().isEmpty())
+ return;
+
+ GridIntIterator iter = txState.cacheIds().iterator();
+
+ while (iter.hasNext()) {
+ int cacheId = iter.next();
+
+ GridCacheContext ctx0 = cctx.cacheContext(cacheId);
+
+ if (ctx0.isDrEnabled())
+ ctx0.dr().onTxFinished(mvccSnapshot, commit, topologyVersionSnapshot());
+ }
+ }
+
+ /**
* @param e Transaction entry.
* @param primaryOnly Flag to include backups into check or not.
* @return {@code True} if entry is locally mapped as a primary or back up node.
@@ -1886,6 +2004,16 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
return xidVer.hashCode();
}
+ /**
+ * Adds cache to the list of active caches in transaction.
+ *
+ * @param cacheCtx Cache context to add.
+ * @param recovery Recovery flag. See {@link CacheOperationContext#setRecovery(boolean)}.
+ * @throws IgniteCheckedException If caches already enlisted in this transaction are not compatible with given
+ * cache (e.g. they have different stores).
+ */
+ public abstract void addActiveCache(GridCacheContext cacheCtx, boolean recovery) throws IgniteCheckedException;
+
/** {@inheritDoc} */
@Override public String toString() {
return GridToStringBuilder.toString(IgniteTxAdapter.class, this,
@@ -1960,6 +2088,16 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
}
/** {@inheritDoc} */
+ @Override public void mvccSnapshot(MvccSnapshot mvccSnapshot) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot mvccSnapshot() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean localResult() {
return false;
}
@@ -2040,6 +2178,11 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
}
/** {@inheritDoc} */
+ @Override public void activeCachesDeploymentEnabled(boolean depEnabled) {
+ throw new IllegalStateException("Deserialized transaction can only be used as read-only.");
+ }
+
+ /** {@inheritDoc} */
@Nullable @Override public Object addMeta(int key, Object val) {
throw new IllegalStateException("Deserialized transaction can only be used as read-only.");
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
index 2eba3ac..7541b43 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
@@ -342,8 +342,6 @@ public class IgniteTxHandler {
return new GridFinishedFuture<>(e);
}
- assert firstEntry != null : req;
-
GridDhtTxLocal tx = null;
GridCacheVersion mappedVer = ctx.tm().mappedVersion(req.version());
@@ -363,6 +361,8 @@ public class IgniteTxHandler {
GridDhtPartitionTopology top = null;
if (req.firstClientRequest()) {
+ assert firstEntry != null : req;
+
assert req.concurrency() == OPTIMISTIC : req;
assert nearNode.isClient() : nearNode;
@@ -658,6 +658,8 @@ public class IgniteTxHandler {
if (expVer.equals(curVer))
return false;
+ // TODO IGNITE-6754 check mvcc crd for mvcc enabled txs.
+
for (IgniteTxEntry e : F.concat(false, req.reads(), req.writes())) {
GridCacheContext ctx = e.context();
@@ -912,8 +914,11 @@ public class IgniteTxHandler {
else
tx = ctx.tm().tx(dhtVer);
- if (tx != null)
+ if (tx != null) {
+ tx.mvccSnapshot(req.mvccSnapshot());
+
req.txState(tx.txState());
+ }
if (tx == null && locTx != null && !req.commit()) {
U.warn(log, "DHT local tx not found for near local tx rollback " +
@@ -1378,6 +1383,7 @@ public class IgniteTxHandler {
tx.commitVersion(req.commitVersion());
tx.invalidate(req.isInvalidate());
tx.systemInvalidate(req.isSystemInvalidate());
+ tx.mvccSnapshot(req.mvccSnapshot());
// Complete remote candidates.
tx.doneRemote(req.baseVersion(), null, null, null);
@@ -1385,11 +1391,13 @@ public class IgniteTxHandler {
tx.setPartitionUpdateCounters(
req.partUpdateCounters() != null ? req.partUpdateCounters().array() : null);
+ tx.updateCountersMap(req.updateCountersMap());
+
tx.commitRemoteTx();
}
else {
tx.doneRemote(req.baseVersion(), null, null, null);
-
+ tx.mvccSnapshot(req.mvccSnapshot());
tx.rollbackRemoteTx();
}
}
@@ -1424,6 +1432,7 @@ public class IgniteTxHandler {
try {
tx.commitVersion(req.writeVersion());
tx.invalidate(req.isInvalidate());
+ tx.mvccSnapshot(req.mvccSnapshot());
// Complete remote candidates.
tx.doneRemote(req.version(), null, null, null);
@@ -1608,10 +1617,12 @@ public class IgniteTxHandler {
GridDhtTxPrepareRequest req,
GridDhtTxPrepareResponse res
) throws IgniteCheckedException {
- if (!F.isEmpty(req.writes())) {
+ if (req.queryUpdate() || !F.isEmpty(req.writes())) {
GridDhtTxRemote tx = ctx.tm().tx(req.version());
if (tx == null) {
+ assert !req.queryUpdate();
+
boolean single = req.last() && req.writes().size() == 1;
tx = new GridDhtTxRemote(
@@ -1718,7 +1729,8 @@ public class IgniteTxHandler {
/*transformClo*/null,
tx.resolveTaskName(),
/*expiryPlc*/null,
- /*keepBinary*/true);
+ /*keepBinary*/true,
+ null); // TODO IGNITE-7371
if (val == null)
val = cacheCtx.toCacheObject(cacheCtx.store().load(null, entry.key()));
@@ -1766,7 +1778,7 @@ public class IgniteTxHandler {
res.invalidPartitionsByCacheId(tx.invalidPartitions());
- if (tx.empty() && req.last()) {
+ if (!req.queryUpdate() && tx.empty() && req.last()) {
tx.skipCompletedVersions(req.skipCompletedVersion());
tx.rollbackRemoteTx();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxImplicitSingleStateImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxImplicitSingleStateImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxImplicitSingleStateImpl.java
index 10b06d8..4619a80 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxImplicitSingleStateImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxImplicitSingleStateImpl.java
@@ -31,6 +31,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture;
import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
+import org.apache.ignite.internal.util.GridIntList;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.CU;
@@ -57,9 +58,10 @@ public class IgniteTxImplicitSingleStateImpl extends IgniteTxLocalStateAdapter {
private boolean recovery;
/** {@inheritDoc} */
- @Override public void addActiveCache(GridCacheContext ctx, boolean recovery, IgniteTxLocalAdapter tx)
+ @Override public void addActiveCache(GridCacheContext ctx, boolean recovery, IgniteTxAdapter tx)
throws IgniteCheckedException {
assert cacheCtx == null : "Cache already set [cur=" + cacheCtx.name() + ", new=" + ctx.name() + ']';
+ assert tx.local();
cacheCtx = ctx;
this.recovery = recovery;
@@ -68,6 +70,11 @@ public class IgniteTxImplicitSingleStateImpl extends IgniteTxLocalStateAdapter {
}
/** {@inheritDoc} */
+ @Nullable @Override public GridIntList cacheIds() {
+ return GridIntList.asList(cacheCtx.cacheId());
+ }
+
+ /** {@inheritDoc} */
@Nullable @Override public GridCacheContext singleCacheContext(GridCacheSharedContext cctx) {
return cacheCtx;
}
@@ -289,6 +296,13 @@ public class IgniteTxImplicitSingleStateImpl extends IgniteTxLocalStateAdapter {
}
/** {@inheritDoc} */
+ @Override public boolean mvccEnabled(GridCacheSharedContext cctx) {
+ GridCacheContext ctx0 = cacheCtx;
+
+ return ctx0 != null && ctx0.mvccEnabled();
+ }
+
+ /** {@inheritDoc} */
public String toString() {
return S.toString(IgniteTxImplicitSingleStateImpl.class, this);
}
[10/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBackupsAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBackupsAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBackupsAbstractTest.java
new file mode 100644
index 0000000..71e004b
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBackupsAbstractTest.java
@@ -0,0 +1,808 @@
+/*
+ * 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.mvcc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.TestRecordingCommunicationSpi;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxQueryEnlistResponse;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysResponse;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
+import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.lang.IgniteBiInClosure;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.DML;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * Backups tests.
+ */
+@SuppressWarnings("unchecked")
+public abstract class CacheMvccBackupsAbstractTest extends CacheMvccAbstractTest {
+
+ /** Test timeout. */
+ private final long txLongTimeout = getTestTimeout() / 4;
+
+ /**
+ * Tests backup consistency.
+ *
+ * @throws Exception If fails.
+ */
+ public void testBackupsCoherenceSimple() throws Exception {
+ disableScheduledVacuum = true;
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ final int KEYS_CNT = 5_000;
+ assert KEYS_CNT % 2 == 0;
+
+ startGrids(3);
+
+ Ignite node0 = grid(0);
+ Ignite node1 = grid(1);
+ Ignite node2 = grid(2);
+
+ client = true;
+
+ Ignite client = startGrid();
+
+ awaitPartitionMapExchange();
+
+ IgniteCache clientCache = client.cache(DEFAULT_CACHE_NAME);
+ IgniteCache cache0 = node0.cache(DEFAULT_CACHE_NAME);
+ IgniteCache cache1 = node1.cache(DEFAULT_CACHE_NAME);
+ IgniteCache cache2 = node2.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ for (int i = 0; i < KEYS_CNT / 2; i += 2) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values ("
+ + i + ',' + i * 2 + "), (" + (i + 1) + ',' + (i + 1) * 2 + ')');
+
+ clientCache.query(qry).getAll();
+ }
+
+ tx.commit();
+ }
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ for (int i = 0; i < 10; i++) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("DELETE from Integer WHERE _key = " + i);
+
+ clientCache.query(qry).getAll();
+ }
+
+ for (int i = 10; i < KEYS_CNT + 1; i++) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE Integer SET _val=" + i * 10 + " WHERE _key = " + i);
+
+ clientCache.query(qry).getAll();
+ }
+
+ tx.commit();
+ }
+
+ Map<KeyCacheObject, List<CacheDataRow>> vers0 = allVersions(cache0);
+
+ List res0 = getAll(cache0, "Integer");
+
+ stopGrid(0);
+
+ awaitPartitionMapExchange();
+
+ Map<KeyCacheObject, List<CacheDataRow>> vers1 = allVersions(cache1);
+
+ assertVersionsEquals(vers0, vers1);
+
+ List res1 = getAll(cache1, "Integer");
+
+ assertEqualsCollections(res0, res1);
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ for (int i = 10; i < 20; i++) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("DELETE from Integer WHERE _key = " + i);
+
+ clientCache.query(qry).getAll();
+ }
+
+ for (int i = 20; i < KEYS_CNT + 1; i++) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE Integer SET _val=" + i * 100 + " WHERE _key = " + i);
+
+ clientCache.query(qry).getAll();
+ }
+
+ tx.commit();
+ }
+
+ vers1 = allVersions(cache1);
+
+ res1 = getAll(cache2, "Integer");
+
+ stopGrid(1);
+
+ awaitPartitionMapExchange();
+
+ Map<KeyCacheObject, List<CacheDataRow>> vers2 = allVersions(cache2);
+
+ assertVersionsEquals(vers1, vers2);
+
+ List res2 = getAll(cache2, "Integer");
+
+ assertEqualsCollections(res1, res2);
+ }
+
+ /**
+ * Checks cache backups consistency with large queries.
+ *
+ * @throws Exception If failed.
+ */
+ public void testBackupsCoherenceWithLargeOperations() throws Exception {
+ disableScheduledVacuum = true;
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 1, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ final int KEYS_CNT = 50_000;
+ assert KEYS_CNT % 2 == 0;
+
+ startGrids(2);
+
+ Ignite node1 = grid(0);
+ Ignite node2 = grid(1);
+
+ client = true;
+
+ Ignite client = startGrid();
+
+ awaitPartitionMapExchange();
+
+ IgniteCache clientCache = client.cache(DEFAULT_CACHE_NAME);
+ IgniteCache cache1 = node1.cache(DEFAULT_CACHE_NAME);
+ IgniteCache cache2 = node2.cache(DEFAULT_CACHE_NAME);
+
+ StringBuilder insert = new StringBuilder("INSERT INTO Integer (_key, _val) values ");
+
+ boolean first = true;
+
+ for (int key = 0; key < KEYS_CNT; key++) {
+ if (!first)
+ insert.append(',');
+ else
+ first = false;
+
+ insert.append('(').append(key).append(',').append(key * 10).append(')');
+ }
+
+ String qryStr = insert.toString();
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(qryStr);
+
+ clientCache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ qryStr = "SELECT * FROM Integer WHERE _key >= " + KEYS_CNT / 2 + " FOR UPDATE";
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(qryStr);
+
+ clientCache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+
+ qryStr = "DELETE FROM Integer WHERE _key >= " + KEYS_CNT / 2;
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(qryStr);
+
+ clientCache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ Map<KeyCacheObject, List<CacheDataRow>> cache1Vers = allVersions(cache1);
+
+ List res1 = getAll(cache1, "Integer");
+
+ stopGrid(0);
+
+ awaitPartitionMapExchange();
+
+ Map<KeyCacheObject, List<CacheDataRow>> cache2Vers = allVersions(cache2);
+
+ assertVersionsEquals(cache1Vers, cache2Vers);
+
+ List res2 = getAll(cache2, "Integer");
+
+ assertEqualsCollections(res1, res2);
+ }
+
+ /**
+ * Checks cache backups consistency with in-flight batches overflow.
+ *
+ * @throws Exception If failed.
+ */
+ public void testBackupsCoherenceWithInFlightBatchesOverflow() throws Exception {
+ testSpi = true;
+
+ disableScheduledVacuum = true;
+
+ ccfg = cacheConfiguration(cacheMode(), FULL_SYNC, 1, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, Integer.class);
+
+ final int KEYS_CNT = 30_000;
+ assert KEYS_CNT % 2 == 0;
+
+ startGrids(2);
+
+ Ignite node1 = grid(0);
+ Ignite node2 = grid(1);
+
+ client = true;
+
+ Ignite client = startGrid();
+
+ awaitPartitionMapExchange();
+
+ IgniteCache<?,?> clientCache = client.cache(DEFAULT_CACHE_NAME);
+ IgniteCache<?,?> cache1 = node1.cache(DEFAULT_CACHE_NAME);
+ IgniteCache<?,?> cache2 = node2.cache(DEFAULT_CACHE_NAME);
+
+ AtomicInteger keyGen = new AtomicInteger();
+ Affinity affinity = affinity(clientCache);
+
+ ClusterNode cNode1 = ((IgniteEx)node1).localNode();
+ ClusterNode cNode2 = ((IgniteEx)node2).localNode();
+
+ StringBuilder insert = new StringBuilder("INSERT INTO Integer (_key, _val) values ");
+
+ for (int i = 0; i < KEYS_CNT; i++) {
+ if (i > 0)
+ insert.append(',');
+
+ // To make big batches in near results future.
+ Integer key = i < KEYS_CNT / 2 ? keyForNode(affinity, keyGen, cNode1) : keyForNode(affinity, keyGen, cNode2);
+
+ assert key != null;
+
+ insert.append('(').append(key).append(',').append(key * 10).append(')');
+ }
+
+ String qryStr = insert.toString();
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(qryStr);
+
+ clientCache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ // Add a delay to simulate batches overflow.
+ TestRecordingCommunicationSpi spi1 = TestRecordingCommunicationSpi.spi(node1);
+ TestRecordingCommunicationSpi spi2 = TestRecordingCommunicationSpi.spi(node2);
+
+ spi1.closure(new IgniteBiInClosure<ClusterNode, Message>() {
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (msg instanceof GridDhtTxQueryEnlistResponse)
+ doSleep(100);
+ }
+ });
+
+ spi2.closure(new IgniteBiInClosure<ClusterNode, Message>() {
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (msg instanceof GridDhtTxQueryEnlistResponse)
+ doSleep(100);
+ }
+ });
+
+ qryStr = "DELETE FROM Integer WHERE _key >= " + 10;
+
+ try (Transaction tx = client.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(txLongTimeout);
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(qryStr);
+
+ clientCache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ Map<KeyCacheObject, List<CacheDataRow>> cache1Vers = allVersions(cache1);
+
+ List res1 = getAll(cache1, "Integer");
+
+ stopGrid(0);
+
+ awaitPartitionMapExchange();
+
+ Map<KeyCacheObject, List<CacheDataRow>> cache2Vers = allVersions(cache2);
+
+ assertVersionsEquals(cache1Vers, cache2Vers);
+
+ List res2 = getAll(cache2, "Integer");
+
+ assertEqualsCollections(res1, res2);
+ }
+
+ /**
+ * Tests concurrent updates backups coherence.
+ *
+ * @throws Exception If failed.
+ */
+ public void testBackupsCoherenceWithConcurrentUpdates2ServersNoClients() throws Exception {
+ checkBackupsCoherenceWithConcurrentUpdates(2, 0);
+ }
+
+ /**
+ * Tests concurrent updates backups coherence.
+ *
+ * @throws Exception If failed.
+ */
+ public void testBackupsCoherenceWithConcurrentUpdates4ServersNoClients() throws Exception {
+ checkBackupsCoherenceWithConcurrentUpdates(4, 0);
+ }
+
+ /**
+ * Tests concurrent updates backups coherence.
+ *
+ * @throws Exception If failed.
+ */
+ public void testBackupsCoherenceWithConcurrentUpdates3Servers1Client() throws Exception {
+ checkBackupsCoherenceWithConcurrentUpdates(3, 1);
+ }
+
+ /**
+ * Tests concurrent updates backups coherence.
+ *
+ * @throws Exception If failed.
+ */
+ public void testBackupsCoherenceWithConcurrentUpdates5Servers2Clients() throws Exception {
+ checkBackupsCoherenceWithConcurrentUpdates(5, 2);
+ }
+
+ /**
+ * Tests concurrent updates backups coherence.
+ *
+ * @throws Exception If failed.
+ */
+ private void checkBackupsCoherenceWithConcurrentUpdates(int srvs, int clients) throws Exception {
+ assert srvs > 1;
+
+ disableScheduledVacuum = true;
+
+ accountsTxReadAll(srvs, clients, srvs - 1, DFLT_PARTITION_COUNT,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML, 5_000, null);
+
+ for (int i = 0; i < srvs - 1; i++) {
+ Ignite node1 = grid(i);
+
+ IgniteCache cache1 = node1.cache(DEFAULT_CACHE_NAME);
+
+ Map<KeyCacheObject, List<CacheDataRow>> vers1 = allVersions(cache1);
+
+ List res1 = getAll(cache1, "MvccTestAccount");
+
+ stopGrid(i);
+
+ awaitPartitionMapExchange();
+
+ Ignite node2 = grid(i + 1);
+
+ IgniteCache cache2 = node2.cache(DEFAULT_CACHE_NAME);
+
+ Map<KeyCacheObject, List<CacheDataRow>> vers2 = allVersions(cache2);
+
+ assertVersionsEquals(vers1, vers2);
+
+ List res2 = getAll(cache2, "MvccTestAccount");
+
+ assertEqualsCollections(res1, res2);
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testNoForceKeyRequestDelayedRebalanceNoVacuum() throws Exception {
+ disableScheduledVacuum = true;
+
+ doTestRebalanceNodeAdd(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testNoForceKeyRequestDelayedRebalance() throws Exception {
+ doTestRebalanceNodeAdd(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testNoForceKeyRequestNoVacuum() throws Exception {
+ disableScheduledVacuum = true;
+
+ doTestRebalanceNodeAdd(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testNoForceKeyRequest() throws Exception {
+ doTestRebalanceNodeAdd(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ private void doTestRebalanceNodeAdd(boolean delayRebalance) throws Exception {
+ testSpi = true;
+
+ final Ignite node1 = startGrid(0);
+
+ final IgniteCache<Object, Object> cache = node1.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, 1, 16)
+ .setIndexedTypes(Integer.class, Integer.class));
+
+ try (Transaction tx = node1.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values " +
+ "(1,1),(2,2),(3,3),(4,4),(5,5)");
+
+ cache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ TestRecordingCommunicationSpi spi = TestRecordingCommunicationSpi.spi(node1);
+
+ // Check for a force key request.
+ spi.closure(new IgniteBiInClosure<ClusterNode, Message>() {
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (delayRebalance && msg instanceof GridDhtPartitionSupplyMessage)
+ doSleep(500);
+
+ if (msg instanceof GridDhtForceKeysResponse)
+ fail("Force key request");
+ }
+ });
+
+ final Ignite node2 = startGrid(1);
+
+ TestRecordingCommunicationSpi.spi(node2).closure(
+ new IgniteBiInClosure<ClusterNode, Message>() {
+ @Override public void apply(ClusterNode node, Message msg) {
+ if (msg instanceof GridDhtForceKeysRequest)
+ fail("Force key request");
+ }
+ }
+ );
+
+ IgniteCache<Object, Object> cache2 = node2.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = node2.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("DELETE FROM Integer WHERE _key IN " +
+ "(1,2,3,4,5)");
+
+ cache2.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ awaitPartitionMapExchange();
+
+ doSleep(2000);
+
+ stopGrid(1);
+
+ try (Transaction tx = node1.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+
+ SqlFieldsQuery qry = new SqlFieldsQuery("INSERT INTO Integer (_key, _val) values " +
+ "(1,1),(2,2),(3,3),(4,4),(5,5)");
+
+ cache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ doSleep(1000);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRebalanceNodeLeaveClient() throws Exception {
+ doTestRebalanceNodeLeave(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testRebalanceNodeLeaveServer() throws Exception {
+ doTestRebalanceNodeLeave(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void doTestRebalanceNodeLeave(boolean startClient) throws Exception {
+ testSpi = true;
+ disableScheduledVacuum = true;
+
+ startGridsMultiThreaded(4);
+
+ client = true;
+
+ final Ignite node = startClient ? startGrid(4) : grid(0);
+
+ final IgniteCache<Object, Object> cache = node.createCache(
+ cacheConfiguration(cacheMode(), FULL_SYNC, 2, 16)
+ .setIndexedTypes(Integer.class, Integer.class));
+
+ List<Integer> keys = new ArrayList<>();
+
+ for (int i = 0; i < 4; i++)
+ keys.addAll(primaryKeys(grid(i).cache(DEFAULT_CACHE_NAME), 2));
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ StringBuilder sb = new StringBuilder("INSERT INTO Integer (_key, _val) values ");
+
+ for (int i = 0; i < keys.size(); i++) {
+ if (i > 0)
+ sb.append(", ");
+
+ sb.append("(" + keys.get(i) + ", " + keys.get(i) + ")");
+ }
+
+ SqlFieldsQuery qry = new SqlFieldsQuery(sb.toString());
+
+ cache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ stopGrid(3);
+
+ awaitPartitionMapExchange();
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ SqlFieldsQuery qry = new SqlFieldsQuery("UPDATE Integer SET _val = 10*_key");
+
+ cache.query(qry).getAll();
+
+ tx.commit();
+ }
+
+ awaitPartitionMapExchange();
+
+ for (Integer key : keys) {
+ List<CacheDataRow> vers = null;
+
+ for (int i = 0; i < 3; i++) {
+ ClusterNode n = grid(i).cluster().localNode();
+
+ if (node.affinity(DEFAULT_CACHE_NAME).isPrimaryOrBackup(n, key)) {
+
+ List<CacheDataRow> vers0 = allKeyVersions(grid(i).cache(DEFAULT_CACHE_NAME), key);
+
+ if (vers != null)
+ assertKeyVersionsEquals(vers, vers0);
+
+ vers = vers0;
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves all versions of all keys from cache.
+ *
+ * @param cache Cache.
+ * @return {@link Map} of keys to its versions.
+ * @throws IgniteCheckedException If failed.
+ */
+ private Map<KeyCacheObject, List<CacheDataRow>> allVersions(IgniteCache cache) throws IgniteCheckedException {
+ IgniteCacheProxy cache0 = (IgniteCacheProxy)cache;
+ GridCacheContext cctx = cache0.context();
+
+ assert cctx.mvccEnabled();
+
+ Map<KeyCacheObject, List<CacheDataRow>> vers = new HashMap<>();
+
+ for (Object e : cache) {
+ IgniteBiTuple entry = (IgniteBiTuple)e;
+
+ KeyCacheObject key = cctx.toCacheKeyObject(entry.getKey());
+
+ GridCursor<CacheDataRow> cur = cctx.offheap().mvccAllVersionsCursor(cctx, key, null);
+
+ List<CacheDataRow> rows = new ArrayList<>();
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ rows.add(row);
+ }
+
+ vers.put(key, rows);
+ }
+
+ return vers;
+ }
+
+ /**
+ * @param cache Cache.
+ * @param key Key.
+ * @return Collection of versioned rows.
+ * @throws IgniteCheckedException if failed.
+ */
+ private List<CacheDataRow> allKeyVersions(IgniteCache cache, Object key) throws IgniteCheckedException {
+ IgniteCacheProxy cache0 = (IgniteCacheProxy)cache;
+ GridCacheContext cctx = cache0.context();
+
+ KeyCacheObject key0 = cctx.toCacheKeyObject(key);
+
+ GridCursor<CacheDataRow> cur = cctx.offheap().mvccAllVersionsCursor(cctx, key0, null);
+
+ List<CacheDataRow> rows = new ArrayList<>();
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ rows.add(row);
+ }
+
+ return rows;
+ }
+
+ /**
+ * Checks stored versions equality.
+ *
+ * @param left Keys versions to compare.
+ * @param right Keys versions to compare.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void assertVersionsEquals(Map<KeyCacheObject, List<CacheDataRow>> left,
+ Map<KeyCacheObject, List<CacheDataRow>> right) throws IgniteCheckedException {
+ assertNotNull(left);
+ assertNotNull(right);
+
+ assertTrue(!left.isEmpty());
+ assertTrue(!right.isEmpty());
+
+ assertEqualsCollections(left.keySet(), right.keySet());
+
+ for (KeyCacheObject key : right.keySet()) {
+ List<CacheDataRow> leftRows = left.get(key);
+ List<CacheDataRow> rightRows = right.get(key);
+
+ assertKeyVersionsEquals(leftRows, rightRows);
+ }
+ }
+
+ /**
+ *
+ * @param leftRows Left rows.
+ * @param rightRows Right rows.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void assertKeyVersionsEquals(List<CacheDataRow> leftRows, List<CacheDataRow> rightRows)
+ throws IgniteCheckedException {
+
+ assertNotNull(leftRows);
+ assertNotNull(rightRows);
+
+ assertEquals("leftRows=" + leftRows + ", rightRows=" + rightRows, leftRows.size(), rightRows.size());
+
+ for (int i = 0; i < leftRows.size(); i++) {
+ CacheDataRow leftRow = leftRows.get(i);
+ CacheDataRow rightRow = rightRows.get(i);
+
+ assertNotNull(leftRow);
+ assertNotNull(rightRow);
+
+ assertTrue(leftRow instanceof MvccDataRow);
+ assertTrue(rightRow instanceof MvccDataRow);
+
+ leftRow.key().valueBytes(null);
+
+ assertEquals(leftRow.expireTime(), rightRow.expireTime());
+ assertEquals(leftRow.partition(), rightRow.partition());
+ assertArrayEquals(leftRow.value().valueBytes(null), rightRow.value().valueBytes(null));
+ assertEquals(leftRow.version(), rightRow.version());
+ assertEquals(leftRow.cacheId(), rightRow.cacheId());
+ assertEquals(leftRow.hash(), rightRow.hash());
+ assertEquals(leftRow.key(), rightRow.key());
+ assertTrue(MvccUtils.compare(leftRow, rightRow.mvccVersion()) == 0);
+ assertTrue(MvccUtils.compareNewVersion(leftRow, rightRow.newMvccVersion()) == 0);
+ assertEquals(leftRow.newMvccCoordinatorVersion(), rightRow.newMvccCoordinatorVersion());
+ assertEquals(leftRow.newMvccCounter(), rightRow.newMvccCounter());
+ assertEquals(leftRow.newMvccOperationCounter(), rightRow.newMvccOperationCounter());
+ }
+ }
+
+ /**
+ * Retrieves all table rows from local node.
+ * @param cache Cache.
+ * @param tblName Table name.
+ * @return All table rows.
+ */
+ private List getAll(IgniteCache cache, String tblName) {
+ List res = cache.query(new SqlFieldsQuery("SELECT * FROM " + tblName)).getAll();
+
+ Collections.sort(res, new Comparator<Object>() {
+ @Override public int compare(Object o1, Object o2) {
+ List<?> l1 = (List<?>)o1;
+ List<?> l2 = (List<?>)o2;
+
+ int res = ((Comparable)l1.get(0)).compareTo((Comparable)l2.get(0));
+
+ if (res == 0 && l1.size() > 1)
+ return ((Comparable)l1.get(1)).compareTo((Comparable)l2.get(1));
+ else
+ return res;
+
+ }
+ });
+
+ return res;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBulkLoadTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBulkLoadTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBulkLoadTest.java
new file mode 100644
index 0000000..d620e84
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccBulkLoadTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.mvcc;
+
+import java.io.File;
+import java.io.Serializable;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+
+/**
+ *
+ */
+public class CacheMvccBulkLoadTest extends CacheMvccAbstractTest {
+ /** */
+ private IgniteCache<Object, Object> sqlNexus;
+
+ /** */
+ private Statement stmt;
+
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ Ignite ignite = startGrid(0);
+ sqlNexus = ignite.getOrCreateCache(new CacheConfiguration<>("sqlNexus").setSqlSchema("PUBLIC"));
+ sqlNexus.query(q("" +
+ "create table person(" +
+ " id int not null primary key," +
+ " name varchar not null" +
+ ") with \"atomicity=transactional\""
+ ));
+ stmt = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1").createStatement();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCopyStoresData() throws Exception {
+ String csvFilePath = new File(getClass().getResource("mvcc_person.csv").toURI()).getAbsolutePath();
+ stmt.executeUpdate("copy from '" + csvFilePath + "' into person (id, name) format csv");
+
+ List<List<?>> rows = sqlNexus.query(q("select * from person")).getAll();
+
+ List<List<? extends Serializable>> exp = asList(
+ asList(1, "John"),
+ asList(2, "Jack")
+ );
+ assertEquals(exp, rows);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCopyDoesNotOverwrite() throws Exception {
+ sqlNexus.query(q("insert into person values(1, 'Old')"));
+ String csvFilePath = new File(getClass().getResource("mvcc_person.csv").toURI()).getAbsolutePath();
+ stmt.executeUpdate("copy from '" + csvFilePath + "' into person (id, name) format csv");
+
+ List<List<?>> rows = sqlNexus.query(q("select * from person")).getAll();
+
+ List<List<? extends Serializable>> exp = asList(
+ asList(1, "Old"),
+ asList(2, "Jack")
+ );
+ assertEquals(exp, rows);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCopyLeavesPartialResultsInCaseOfFailure() throws Exception {
+ String csvFilePath = new File(getClass().getResource("mvcc_person_broken.csv").toURI()).getAbsolutePath();
+ try {
+ stmt.executeUpdate("copy from '" + csvFilePath + "' into person (id, name) format csv");
+ fail();
+ }
+ catch (SQLException ignored) {
+ // assert exception is thrown
+ }
+
+ List<List<?>> rows = sqlNexus.query(q("select * from person")).getAll();
+
+ List<List<? extends Serializable>> exp = singletonList(
+ asList(1, "John")
+ );
+ assertEquals(exp, rows);
+ }
+
+ /** */
+ private static SqlFieldsQuery q(String sql) {
+ return new SqlFieldsQuery(sql);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccDmlSimpleTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccDmlSimpleTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccDmlSimpleTest.java
new file mode 100644
index 0000000..bb5e753
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccDmlSimpleTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.mvcc;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.cache.CacheException;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+
+import static java.util.Arrays.asList;
+
+/**
+ *
+ */
+public class CacheMvccDmlSimpleTest extends CacheMvccAbstractTest {
+ /** */
+ private IgniteCache<?, ?> cache;
+
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ cache = startGrid(0).getOrCreateCache(
+ new CacheConfiguration<>("test")
+ .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
+ .setSqlSchema("PUBLIC")
+ .setIndexedTypes(Integer.class, Integer.class)
+ );
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testInsert() throws Exception {
+ int cnt = update("insert into Integer(_key, _val) values(1, 1),(2, 2)");
+
+ assertEquals(2, cnt);
+
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+
+ try {
+ update("insert into Integer(_key, _val) values(3, 3),(1, 1)");
+ } catch (CacheException e) {
+ assertTrue(e.getCause() instanceof IgniteSQLException);
+ assertEquals(IgniteQueryErrorCode.DUPLICATE_KEY, ((IgniteSQLException)e.getCause()).statusCode());
+ }
+
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testMerge() throws Exception {
+ {
+ int cnt = update("merge into Integer(_key, _val) values(1, 1),(2, 2)");
+
+ assertEquals(2, cnt);
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("merge into Integer(_key, _val) values(3, 3),(1, 1)");
+
+ assertEquals(2, cnt);
+ assertEquals(asSet(asList(1, 1), asList(2, 2), asList(3, 3)), query("select * from Integer"));
+ }
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testUpdate() throws Exception {
+ {
+ int cnt = update("update Integer set _val = 42 where _key = 42");
+
+ assertEquals(0, cnt);
+ assertTrue(query("select * from Integer").isEmpty());
+ }
+
+ update("insert into Integer(_key, _val) values(1, 1),(2, 2)");
+
+ {
+ int cnt = update("update Integer set _val = 42 where _key = 42");
+
+ assertEquals(0, cnt);
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("update Integer set _val = 42 where _key >= 42");
+
+ assertEquals(0, cnt);
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("update Integer set _val = 11 where _key = 1");
+
+ assertEquals(1, cnt);
+ assertEquals(asSet(asList(1, 11), asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("update Integer set _val = 12 where _key <= 2");
+
+ assertEquals(asSet(asList(1, 12), asList(2, 12)), query("select * from Integer"));
+ assertEquals(2, cnt);
+ }
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testDelete() throws Exception {
+ {
+ int cnt = update("delete from Integer where _key = 42");
+
+ assertEquals(0, cnt);
+ }
+
+ update("insert into Integer(_key, _val) values(1, 1),(2, 2)");
+
+ {
+ int cnt = update("delete from Integer where _key = 42");
+
+ assertEquals(0, cnt);
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("delete from Integer where _key >= 42");
+
+ assertEquals(0, cnt);
+ assertEquals(asSet(asList(1, 1), asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("delete from Integer where _key = 1");
+
+ assertEquals(1, cnt);
+ assertEquals(asSet(asList(2, 2)), query("select * from Integer"));
+ }
+
+ {
+ int cnt = update("delete from Integer where _key <= 2");
+
+ assertTrue(query("select * from Integer").isEmpty());
+ assertEquals(1, cnt);
+ }
+ }
+
+ /**
+ * @param q Query.
+ * @return Row set.
+ */
+ private Set<List<?>> query(String q) {
+ return new HashSet<>(cache.query(new SqlFieldsQuery(q)).getAll());
+ }
+
+ /**
+ * @param q Query.
+ * @return Updated rows count.
+ */
+ private int update(String q) {
+ return Integer.parseInt(cache.query(new SqlFieldsQuery(q)).getAll().get(0).get(0).toString());
+ }
+
+ /** */
+ private Set<List<?>> asSet(List<?>... ls) {
+ return new HashSet<>(asList(ls));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentJdbcTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentJdbcTransactionTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentJdbcTransactionTest.java
new file mode 100644
index 0000000..235d87f
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentJdbcTransactionTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public class CacheMvccIteratorWithConcurrentJdbcTransactionTest extends CacheMvccIteratorWithConcurrentTransactionTest {
+ /** {@inheritDoc} */
+ @Override boolean jdbcTx() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest.java
new file mode 100644
index 0000000..97c062f
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public class CacheMvccLocalEntriesWithConcurrentJdbcTransactionTest extends
+ CacheMvccLocalEntriesWithConcurrentTransactionTest {
+ /** {@inheritDoc} */
+ @Override boolean jdbcTx() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedBackupsTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedBackupsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedBackupsTest.java
new file mode 100644
index 0000000..71d832c
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedBackupsTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/** */
+public class CacheMvccPartitionedBackupsTest extends CacheMvccBackupsAbstractTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSelectForUpdateQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSelectForUpdateQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSelectForUpdateQueryTest.java
new file mode 100644
index 0000000..12209ab
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSelectForUpdateQueryTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/** */
+public class CacheMvccPartitionedSelectForUpdateQueryTest extends CacheMvccSelectForUpdateQueryAbstractTest {
+ /** {@inheritDoc} */
+ public CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateDistributedSegmented() throws Exception {
+ doTestSelectForUpdateDistributed("PersonSeg", false);
+ }
+
+ /**
+ *
+ */
+ public void testSelectForUpdateLocalSegmented() throws Exception {
+ doTestSelectForUpdateLocal("PersonSeg", false);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlCoordinatorFailoverTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlCoordinatorFailoverTest.java
new file mode 100644
index 0000000..1362b4a
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlCoordinatorFailoverTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SCAN;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.DML;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * SQL Mvcc coordinator failover test for partitioned caches.
+ */
+public class CacheMvccPartitionedSqlCoordinatorFailoverTest extends CacheMvccAbstractSqlCoordinatorFailoverTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_ClientServer_Backups2_CoordinatorFails() throws Exception {
+ accountsTxReadAll(4, 2, 2, DFLT_PARTITION_COUNT,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_Server_Backups1_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ accountsTxReadAll(2, 0, 1, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups3_RestartCoordinator_ScanDml() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD , 5, 2, 3, DFLT_PARTITION_COUNT,
+ new InitIndexing(Integer.class, Integer.class), SCAN, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_RestartCoordinator_ScanDml_Persistence() throws Exception {
+ persistence = true;
+
+ putAllGetAll(RestartMode.RESTART_CRD , 2, 1, 2, DFLT_PARTITION_COUNT,
+ new InitIndexing(Integer.class, Integer.class), SCAN, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups2_RestartCoordinator_SqlDml_Persistence() throws Exception {
+ persistence = true;
+
+ putAllGetAll(RestartMode.RESTART_CRD, 4, 2, 2, 64,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_RestartCoordinator_SqlDml() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD, 2, 1, 1, 64,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_ClientServer_Backups2_Sql() throws Exception {
+ updateNObjectsTest(7, 3, 2, 2, DFLT_PARTITION_COUNT, DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_ClientServer_Backups1_Sql_Persistence() throws Exception {
+ persistence = true;
+
+ updateNObjectsTest(10, 2, 1, 1, DFLT_PARTITION_COUNT, DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlReadInProgressCoordinatorFails() throws Exception {
+ readInProgressCoordinatorFails(false, false, PESSIMISTIC, REPEATABLE_READ, SQL, DML, new InitIndexing(Integer.class, Integer.class));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlReadInsideTxInProgressCoordinatorFails() throws Exception {
+ readInProgressCoordinatorFails(false, true, PESSIMISTIC, REPEATABLE_READ, SQL, DML, new InitIndexing(Integer.class, Integer.class));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlReadInProgressCoordinatorFails_ReadDelay() throws Exception {
+ readInProgressCoordinatorFails(true, false, PESSIMISTIC, REPEATABLE_READ, SQL, DML, new InitIndexing(Integer.class, Integer.class));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testSqlReadInsideTxInProgressCoordinatorFails_ReadDelay() throws Exception {
+ readInProgressCoordinatorFails(true, true, PESSIMISTIC, REPEATABLE_READ, SQL, DML, new InitIndexing(Integer.class, Integer.class));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testReadInProgressCoordinatorFailsSimple_FromServer() throws Exception {
+ readInProgressCoordinatorFailsSimple(false, new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlQueriesTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlQueriesTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlQueriesTest.java
new file mode 100644
index 0000000..e0b4a24
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlQueriesTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/** */
+public class CacheMvccPartitionedSqlQueriesTest extends CacheMvccSqlQueriesAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesTest.java
new file mode 100644
index 0000000..199cfad
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/** */
+public class CacheMvccPartitionedSqlTxQueriesTest extends CacheMvccSqlTxQueriesAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesWithReducerTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesWithReducerTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesWithReducerTest.java
new file mode 100644
index 0000000..03de543
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedSqlTxQueriesWithReducerTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/** */
+public class CacheMvccPartitionedSqlTxQueriesWithReducerTest extends CacheMvccSqlTxQueriesWithReducerAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedBackupsTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedBackupsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedBackupsTest.java
new file mode 100644
index 0000000..02de0a3
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedBackupsTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+
+/** */
+public class CacheMvccReplicatedBackupsTest extends CacheMvccBackupsAbstractTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return REPLICATED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSelectForUpdateQueryTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSelectForUpdateQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSelectForUpdateQueryTest.java
new file mode 100644
index 0000000..a458319
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSelectForUpdateQueryTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+
+/** */
+public class CacheMvccReplicatedSelectForUpdateQueryTest extends CacheMvccSelectForUpdateQueryAbstractTest {
+ /** {@inheritDoc} */
+ public CacheMode cacheMode() {
+ return REPLICATED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlCoordinatorFailoverTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlCoordinatorFailoverTest.java
new file mode 100644
index 0000000..2f72bce
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlCoordinatorFailoverTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+/**
+ * SQL Mvcc coordinator failover test for replicated caches.
+ */
+public class CacheMvccReplicatedSqlCoordinatorFailoverTest extends CacheMvccAbstractSqlCoordinatorFailoverTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.REPLICATED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlQueriesTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlQueriesTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlQueriesTest.java
new file mode 100644
index 0000000..ba8a5c3
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlQueriesTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+
+/** */
+public class CacheMvccReplicatedSqlQueriesTest extends CacheMvccSqlQueriesAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return REPLICATED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesTest.java
new file mode 100644
index 0000000..bde2c5d
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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.mvcc;
+
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import javax.cache.Cache;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/** */
+public class CacheMvccReplicatedSqlTxQueriesTest extends CacheMvccSqlTxQueriesAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return REPLICATED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ ccfgs = null;
+ ccfg = null;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testReplicatedJoinPartitionedClient() throws Exception {
+ checkReplicatedJoinPartitioned(true);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testReplicatedJoinPartitionedServer() throws Exception {
+ checkReplicatedJoinPartitioned(false);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void checkReplicatedJoinPartitioned(boolean client) throws Exception {
+ ccfgs = new CacheConfiguration[] {
+ cacheConfiguration(REPLICATED, FULL_SYNC, 0, DFLT_PARTITION_COUNT)
+ .setName("int")
+ .setIndexedTypes(Integer.class, Integer.class),
+ cacheConfiguration(PARTITIONED, FULL_SYNC, 2, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class,
+ CacheMvccSqlTxQueriesAbstractTest.MvccTestSqlIndexValue.class),
+ cacheConfiguration(REPLICATED, FULL_SYNC, 0, DFLT_PARTITION_COUNT)
+ .setName("target")
+ .setIndexedTypes(Integer.class, Integer.class)
+ };
+
+ startGridsMultiThreaded(3);
+
+ this.client = true;
+
+ startGrid(3);
+
+ Ignite node = client ? grid(3) : grid(0);
+
+ List<List<?>> r;
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ r = runSql(node, "INSERT INTO \"int\".Integer(_key, _val) VALUES (1,1), (2,2), (3,3)");
+
+ assertEquals(3L, r.get(0).get(0));
+
+ tx.commit();
+ }
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ r = runSql(node, "INSERT INTO \"default\".MvccTestSqlIndexValue(_key, idxVal1) " +
+ "VALUES (1,10), (2, 20), (3, 30)");
+
+ assertEquals(3L, r.get(0).get(0));
+
+ tx.commit();
+ }
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ r = runSql(node, "INSERT INTO \"target\".Integer(_key, _val) " +
+ "SELECT a._key, a.idxVal1*b._val FROM \"default\".MvccTestSqlIndexValue a " +
+ "JOIN \"int\".Integer b ON a._key = b._key");
+
+ assertEquals(3L, r.get(0).get(0));
+
+ tx.commit();
+ }
+
+ for (int n = 0; n < 3; ++n) {
+ node = grid(n);
+
+ r = runSqlLocal(node, "SELECT _key, _val FROM \"target\".Integer ORDER BY _key");
+
+ assertEquals(3L, r.size());
+
+ assertEquals(1, r.get(0).get(0));
+ assertEquals(2, r.get(1).get(0));
+ assertEquals(3, r.get(2).get(0));
+
+ assertEquals(10, r.get(0).get(1));
+ assertEquals(40, r.get(1).get(1));
+ assertEquals(90, r.get(2).get(1));
+ }
+ }
+
+ /**
+ *
+ * @throws Exception If failed.
+ */
+ public void testReplicatedAndPartitionedUpdateSingleTransaction() throws Exception {
+ ccfgs = new CacheConfiguration[] {
+ cacheConfiguration(REPLICATED, FULL_SYNC, 0, DFLT_PARTITION_COUNT)
+ .setName("rep")
+ .setIndexedTypes(Integer.class, Integer.class),
+ cacheConfiguration(PARTITIONED, FULL_SYNC, 0, DFLT_PARTITION_COUNT)
+ .setIndexedTypes(Integer.class, MvccTestSqlIndexValue.class)
+ .setName("part"),
+ };
+
+ startGridsMultiThreaded(3);
+
+ client = true;
+
+ startGrid(3);
+
+ Random rnd = ThreadLocalRandom.current();
+
+ Ignite node = grid(rnd.nextInt(4));
+
+ List<List<?>> r;
+
+ Cache<Integer, Integer> repCache = node.cache("rep");
+
+ repCache.put(1, 1);
+ repCache.put(2, 2);
+ repCache.put(3, 3);
+
+ Cache<Integer, MvccTestSqlIndexValue> partCache = node.cache("part");
+
+ partCache.put(1, new MvccTestSqlIndexValue(1));
+ partCache.put(2, new MvccTestSqlIndexValue(2));
+ partCache.put(3, new MvccTestSqlIndexValue(3));
+
+ try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ tx.timeout(TX_TIMEOUT);
+
+ r = runSql(node, "UPDATE \"rep\".Integer SET _val = _key * 10");
+
+ assertEquals(3L, r.get(0).get(0));
+
+ r = runSql(node, "UPDATE \"part\".MvccTestSqlIndexValue SET idxVal1 = _key * 10");
+
+ assertEquals(3L, r.get(0).get(0));
+
+ tx.commit();
+ }
+
+ r = runSql(node, "SELECT COUNT(1) FROM \"rep\".Integer r JOIN \"part\".MvccTestSqlIndexValue p" +
+ " ON r._key = p._key WHERE r._val = p.idxVal1");
+
+ assertEquals(3L, r.get(0).get(0));
+
+ for (int n = 0; n < 3; ++n) {
+ node = grid(n);
+
+ r = runSqlLocal(node, "SELECT _key, _val FROM \"rep\".Integer ORDER BY _key");
+
+ assertEquals(3L, r.size());
+
+ assertEquals(1, r.get(0).get(0));
+ assertEquals(2, r.get(1).get(0));
+ assertEquals(3, r.get(2).get(0));
+
+ assertEquals(10, r.get(0).get(1));
+ assertEquals(20, r.get(1).get(1));
+ assertEquals(30, r.get(2).get(1));
+ }
+ }
+
+ /**
+ * Run query.
+ *
+ * @param node Node.
+ * @param sqlText Query.
+ * @return Results.
+ */
+ private List<List<?>> runSql(Ignite node, String sqlText) {
+ GridQueryProcessor qryProc = ((IgniteEx)node).context().query();
+
+ return qryProc.querySqlFields(new SqlFieldsQuery(sqlText), false).getAll();
+ }
+
+ /**
+ * Run query locally.
+ *
+ * @param node Node.
+ * @param sqlText Query.
+ * @return Results.
+ */
+ private List<List<?>> runSqlLocal(Ignite node, String sqlText) {
+ GridQueryProcessor qryProc = ((IgniteEx)node).context().query();
+
+ return qryProc.querySqlFields(new SqlFieldsQuery(sqlText).setLocal(true), false).getAll();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesWithReducerTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesWithReducerTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesWithReducerTest.java
new file mode 100644
index 0000000..173c43f
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedSqlTxQueriesWithReducerTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+
+/** */
+public class CacheMvccReplicatedSqlTxQueriesWithReducerTest extends CacheMvccSqlTxQueriesWithReducerAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return REPLICATED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentJdbcTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentJdbcTransactionTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentJdbcTransactionTest.java
new file mode 100644
index 0000000..7272def
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentJdbcTransactionTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public class CacheMvccScanQueryWithConcurrentJdbcTransactionTest extends
+ CacheMvccScanQueryWithConcurrentTransactionTest {
+ /** {@inheritDoc} */
+ @Override boolean jdbcTx() {
+ return true;
+ }
+}
[30/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxSelectForUpdateFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxSelectForUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxSelectForUpdateFuture.java
new file mode 100644
index 0000000..a5ab7cd
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxSelectForUpdateFuture.java
@@ -0,0 +1,462 @@
+/*
+ * 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.distributed.near;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheCompoundIdentityFuture;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
+import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
+import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A future tracking requests for remote nodes transaction enlisting and locking
+ * of entries produced with complex DML queries requiring reduce step.
+ */
+public class GridNearTxSelectForUpdateFuture extends GridCacheCompoundIdentityFuture<Long>
+ implements GridCacheVersionedFuture<Long> {
+ /** */
+ private static final long serialVersionUID = 6931664882548658420L;
+
+ /** Done field updater. */
+ private static final AtomicIntegerFieldUpdater<GridNearTxSelectForUpdateFuture> DONE_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(GridNearTxSelectForUpdateFuture.class, "done");
+
+ /** Done field updater. */
+ private static final AtomicReferenceFieldUpdater<GridNearTxSelectForUpdateFuture, Throwable> EX_UPD =
+ AtomicReferenceFieldUpdater.newUpdater(GridNearTxSelectForUpdateFuture.class, Throwable.class, "ex");
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ private volatile int done;
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ private volatile Throwable ex;
+
+ /** Cache context. */
+ @GridToStringExclude
+ private final GridCacheContext<?, ?> cctx;
+
+ /** Transaction. */
+ private final GridNearTxLocal tx;
+
+ /** Mvcc future id. */
+ private final IgniteUuid futId;
+
+ /** Lock version. */
+ private final GridCacheVersion lockVer;
+
+ /** */
+ private AffinityTopologyVersion topVer;
+
+ /** */
+ private final long timeout;
+
+ /** Logger. */
+ @GridToStringExclude
+ private final IgniteLogger log;
+
+ /** Timeout object. */
+ @GridToStringExclude
+ private LockTimeoutObject timeoutObj;
+
+ /** Ids of mini futures. */
+ private final Map<UUID, Integer> miniFutIds = new HashMap<>();
+
+ /**
+ * @param cctx Cache context.
+ * @param tx Transaction.
+ * @param timeout Timeout.
+ */
+ public GridNearTxSelectForUpdateFuture(GridCacheContext<?, ?> cctx,
+ GridNearTxLocal tx,
+ long timeout) {
+ super(CU.longReducer());
+
+ this.cctx = cctx;
+ this.tx = tx;
+ this.timeout = timeout;
+
+ futId = IgniteUuid.randomUuid();
+ lockVer = tx.xidVersion();
+
+ log = cctx.logger(GridNearTxSelectForUpdateFuture.class);
+ }
+
+ /**
+ * @return Cache context.
+ */
+ public GridCacheContext<?, ?> cache() {
+ return cctx;
+ }
+
+ /**
+ * @param node Node.
+ */
+ private void map(ClusterNode node) {
+ GridDistributedTxMapping mapping = tx.mappings().get(node.id());
+
+ if (mapping == null)
+ tx.mappings().put(mapping = new GridDistributedTxMapping(node));
+
+ mapping.markQueryUpdate();
+
+ if (node.isLocal())
+ tx.colocatedLocallyMapped(true);
+
+ int futId = futuresCountNoLock();
+
+ miniFutIds.put(node.id(), futId);
+
+ add(new NodeFuture(node));
+ }
+
+ /**
+ * Process result of query execution on given
+ * @param nodeId Node id.
+ * @param cnt Total rows counter on given node.
+ * @param removeMapping Whether transaction mapping should be removed for node.
+ * @param err Error.
+ */
+ public void onResult(UUID nodeId, Long cnt, boolean removeMapping, @Nullable Throwable err) {
+ NodeFuture nodeFut = mapFuture(nodeId);
+
+ if (nodeFut != null)
+ nodeFut.onResult(cnt, removeMapping, err);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean processFailure(Throwable err, IgniteInternalFuture<Long> fut) {
+ if (ex != null || !EX_UPD.compareAndSet(this, null, err))
+ ex.addSuppressed(err);
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onDone(@Nullable Long res, @Nullable Throwable err, boolean cancelled) {
+ if (!DONE_UPD.compareAndSet(this, 0, 1))
+ return false;
+
+ cctx.tm().txContext(tx);
+
+ Throwable ex0 = ex;
+
+ if (ex0 != null) {
+ if (err != null)
+ ex0.addSuppressed(err);
+
+ err = ex0;
+ }
+
+ if (!cancelled && err == null)
+ tx.clearLockFuture(this);
+ else
+ tx.setRollbackOnly();
+
+ boolean done = super.onDone(res, err, cancelled);
+
+ assert done;
+
+ // Clean up.
+ cctx.mvcc().removeVersionedFuture(this);
+
+ if (timeoutObj != null)
+ cctx.time().removeTimeoutObject(timeoutObj);
+
+ return true;
+ }
+
+ /**
+ * Finds pending map node future by the given ID.
+ *
+ * @param nodeId Node id.
+ * @return Map node future.
+ */
+ private NodeFuture mapFuture(UUID nodeId) {
+ synchronized (this) {
+ Integer idx = miniFutIds.get(nodeId);
+
+ if (idx == null)
+ throw new IllegalStateException("SELECT FOR UPDATE node future not found [nodeId=" + nodeId + "].");
+
+ assert idx >= 0 && idx < futuresCountNoLock();
+
+ IgniteInternalFuture<Long> fut = future(idx);
+
+ if (!fut.isDone())
+ return (NodeFuture)fut;
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onOwnerChanged(GridCacheEntryEx entry, GridCacheMvccCandidate owner) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onNodeLeft(UUID nodeId) {
+ if (topVer == null)
+ return false; // Local query, do nothing.
+
+ for (IgniteInternalFuture<?> fut : futures()) {
+ NodeFuture f = (NodeFuture)fut;
+
+ if (f.node.id().equals(nodeId)) {
+ if (log.isDebugEnabled())
+ log.debug("Found mini-future for left node [nodeId=" + nodeId + ", mini=" + f + ", fut=" +
+ this + ']');
+
+ ClusterTopologyCheckedException topEx = new ClusterTopologyCheckedException("Failed to enlist keys " +
+ "(primary node left grid, retry transaction if possible) [node=" + nodeId + ']');
+
+ topEx.retryReadyFuture(cctx.shared().nextAffinityReadyFuture(topVer));
+
+ return f.onResult(0, false, topEx);
+ }
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("Future does not have mapping for left node (ignoring) [nodeId=" + nodeId +
+ ", fut=" + this + ']');
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean trackable() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void markNotTrackable() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void logError(IgniteLogger log, String msg, Throwable e) {
+ // no-op
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void logDebug(IgniteLogger log, String msg) {
+ // no-op
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxSelectForUpdateFuture.class, this, super.toString());
+ }
+
+ /**
+ * Initialize this future for distributed execution.
+ * @param topVer Topology version.
+ * @param nodes Nodes to run query on.
+ */
+ public synchronized void init(AffinityTopologyVersion topVer, Collection<ClusterNode> nodes) {
+ doInit(topVer, nodes, false);
+ }
+
+ /**
+ * Initialize this future for local execution.
+ */
+ public synchronized void initLocal() {
+ doInit(null, Collections.singletonList(cctx.localNode()), true);
+ }
+
+ /**
+ * Initialize this future for distributed or local execution.
+ * @param topVer Topology version ({@code null} for local case).
+ * @param nodes Nodes to run query on.
+ * @param loc Local query flag.
+ */
+ private void doInit(@Nullable AffinityTopologyVersion topVer, Collection<ClusterNode> nodes, boolean loc) {
+ assert !loc || (topVer == null && nodes.size() == 1 && nodes.iterator().next().isLocal());
+ if (initialized())
+ throw new IllegalStateException("SELECT FOR UPDATE future has been initialized already.");
+
+ tx.init();
+
+ if (timeout < 0) {
+ // Time is out.
+ onDone(timeoutException());
+
+ return;
+ }
+ else if (timeout > 0)
+ timeoutObj = new LockTimeoutObject();
+
+ if (!tx.updateLockFuture(null, this)) {
+ onDone(tx.timedOut() ? tx.timeoutException() : tx.rollbackException());
+
+ return;
+ }
+
+ boolean added = cctx.mvcc().addFuture(this);
+
+ assert added : this;
+
+ try {
+ tx.addActiveCache(cctx, false);
+ }
+ catch (IgniteCheckedException e) {
+ onDone(e);
+
+ return;
+ }
+
+ if (timeoutObj != null)
+ cctx.time().addTimeoutObject(timeoutObj);
+
+ this.topVer = topVer;
+
+ for (ClusterNode n : nodes)
+ map(n);
+
+ markInitialized();
+ }
+
+ /**
+ * @return Timeout exception.
+ */
+ @NotNull private IgniteTxTimeoutCheckedException timeoutException() {
+ return new IgniteTxTimeoutCheckedException("Failed to acquire lock within provided timeout for " +
+ "transaction [timeout=" + timeout + ", tx=" + tx + ']');
+ }
+
+ /**
+ * A future tracking a single MAP request to be enlisted in transaction and locked on data node.
+ */
+ private class NodeFuture extends GridFutureAdapter<Long> {
+ /** */
+ private boolean completed;
+
+ /** Node ID. */
+ @GridToStringExclude
+ private final ClusterNode node;
+
+ /**
+ * @param node Cluster node.
+ *
+ */
+ private NodeFuture(ClusterNode node) {
+ this.node = node;
+ }
+
+ /**
+ * @return Node.
+ */
+ public ClusterNode node() {
+ return node;
+ }
+
+ /**
+ * @param cnt Total rows counter on given node.
+ * @param removeMapping Whether transaction mapping should be removed for node.
+ * @param err Exception.
+ * @return {@code True} if future was completed by this call.
+ */
+ public boolean onResult(long cnt, boolean removeMapping, Throwable err) {
+ synchronized (this) {
+ if (completed)
+ return false;
+
+ completed = true;
+ }
+
+ if (X.hasCause(err, ClusterTopologyCheckedException.class) || removeMapping) {
+ GridDistributedTxMapping m = tx.mappings().get(node.id());
+
+ assert m != null && m.empty();
+
+ tx.removeMapping(node.id());
+
+ if (node.isLocal())
+ tx.colocatedLocallyMapped(false);
+ }
+ else if (err == null && cnt > 0 && !node.isLocal())
+ tx.hasRemoteLocks(true);
+
+ return onDone(cnt, err);
+ }
+ }
+
+ /**
+ * Lock request timeout object.
+ */
+ private class LockTimeoutObject extends GridTimeoutObjectAdapter {
+ /**
+ * Default constructor.
+ */
+ LockTimeoutObject() {
+ super(timeout);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onTimeout() {
+ if (log.isDebugEnabled())
+ log.debug("Timed out waiting for lock response: " + this);
+
+ onDone(timeoutException());
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(LockTimeoutObject.class, this);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.java
index 132c754..44a87f2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.java
@@ -17,15 +17,34 @@
package org.apache.ignite.internal.processors.cache.distributed.near;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
/**
*
*/
-public interface NearTxFinishFuture extends IgniteInternalFuture<IgniteInternalTx> {
+public interface NearTxFinishFuture extends IgniteInternalFuture<IgniteInternalTx> {
/**
* @return Commit flag.
*/
boolean commit();
+
+ /**
+ * @return Transaction.
+ */
+ GridNearTxLocal tx();
+
+ /**
+ *
+ * @param commit Commit flag.
+ * @param clearThreadMap If {@code true} removes {@link GridNearTxLocal} from thread map.
+ * @param onTimeout If {@code true} called from timeout handler.
+ */
+ public void finish(boolean commit, boolean clearThreadMap, boolean onTimeout);
+
+ /**
+ * @param e Error.
+ */
+ public void onNodeStop(IgniteCheckedException e);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/TxTopologyVersionFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/TxTopologyVersionFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/TxTopologyVersionFuture.java
new file mode 100644
index 0000000..b5e3883
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/TxTopologyVersionFuture.java
@@ -0,0 +1,155 @@
+/*
+ * 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.distributed.near;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheStoppedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.CI1;
+
+/**
+ * Future to obtain/lock topology version for SELECT FOR UPDATE.
+ */
+public class TxTopologyVersionFuture extends GridFutureAdapter<AffinityTopologyVersion> {
+ /** Transaction. */
+ private final GridNearTxLocal tx;
+
+ /** Target cache context. */
+ private final GridCacheContext<?, ?> cctx;
+
+ /** Topology locked flag. */
+ private boolean topLocked;
+
+ /**
+ * @param tx Transaction.
+ * @param cctx Target cache context.
+ */
+ public TxTopologyVersionFuture(GridNearTxLocal tx, GridCacheContext cctx) {
+ this.tx = tx;
+ this.cctx = cctx;
+
+ init();
+ }
+
+ /** */
+ private void init() {
+ // Obtain the topology version to use.
+ long threadId = Thread.currentThread().getId();
+
+ AffinityTopologyVersion topVer = cctx.mvcc().lastExplicitLockTopologyVersion(threadId);
+
+ // If there is another system transaction in progress, use it's topology version to prevent deadlock.
+ if (topVer == null && tx.system())
+ topVer = cctx.tm().lockedTopologyVersion(threadId, tx);
+
+ if (topVer != null)
+ tx.topologyVersion(topVer);
+
+ if (topVer == null)
+ topVer = tx.topologyVersionSnapshot();
+
+ if (topVer != null) {
+ for (GridDhtTopologyFuture fut : cctx.shared().exchange().exchangeFutures()) {
+ if (fut.exchangeDone() && fut.topologyVersion().equals(topVer)) {
+ Throwable err = fut.validateCache(cctx, false, false, null, null);
+
+ if (err != null) {
+ onDone(err);
+
+ return;
+ }
+
+ break;
+ }
+ }
+
+ onDone(topVer);
+
+ topLocked = true;
+
+ return;
+ }
+
+ acquireTopologyVersion();
+ }
+
+ /**
+ * Acquire topology future and wait for its completion.
+ */
+ private void acquireTopologyVersion() {
+ cctx.topology().readLock();
+
+ try {
+ if (cctx.topology().stopping()) {
+ onDone(new CacheStoppedException(cctx.name()));
+
+ return;
+ }
+
+ GridDhtTopologyFuture fut = cctx.topologyVersionFuture();
+
+ if (fut.isDone()) {
+ Throwable err = fut.validateCache(cctx, false, false, null, null);
+
+ if (err != null) {
+ onDone(err);
+
+ return;
+ }
+
+ AffinityTopologyVersion topVer = fut.topologyVersion();
+
+ if (tx != null)
+ tx.topologyVersion(topVer);
+
+ onDone(topVer);
+ }
+ else {
+ fut.listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() {
+ @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> fut) {
+ try {
+ fut.get();
+
+ acquireTopologyVersion();
+ }
+ catch (IgniteCheckedException e) {
+ onDone(e);
+ }
+ finally {
+ cctx.shared().txContextReset();
+ }
+ }
+ });
+ }
+ }
+ finally {
+ cctx.topology().readUnlock();
+ }
+ }
+
+ /**
+ * @return Client first flag.
+ */
+ public boolean clientFirst() {
+ return cctx.localNode().isClient() && !topLocked && !tx.hasRemoteLocks();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridCacheDrManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridCacheDrManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridCacheDrManager.java
index f2a4b30..f362374 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridCacheDrManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridCacheDrManager.java
@@ -22,6 +22,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.GridCacheManager;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.dr.GridDrType;
import org.jetbrains.annotations.Nullable;
@@ -56,6 +57,36 @@ public interface GridCacheDrManager extends GridCacheManager {
AffinityTopologyVersion topVer)throws IgniteCheckedException;
/**
+ * Enlist for DR.
+ *
+ * @param key Key.
+ * @param val Value.
+ * @param ttl TTL.
+ * @param expireTime Expire time.
+ * @param ver Version.
+ * @param drType Replication type.
+ * @param topVer Topology version.
+ * @param mvccVer Tx mvcc version.
+ * @throws IgniteCheckedException If failed.
+ */
+ void mvccReplicate(KeyCacheObject key,
+ @Nullable CacheObject val,
+ long ttl,
+ long expireTime,
+ GridCacheVersion ver,
+ GridDrType drType,
+ AffinityTopologyVersion topVer,
+ MvccVersion mvccVer) throws IgniteCheckedException;
+
+ /**
+ * @param mvccVer Tx mvcc version.
+ * @param commit {@code True} if tx committed, {@code False} otherwise.
+ * @param topVer Tx snapshot affinity version.
+ * @throws IgniteCheckedException If failed.
+ */
+ void onTxFinished(MvccVersion mvccVer, boolean commit, AffinityTopologyVersion topVer);
+
+ /**
* Process partitions exchange event.
*
* @param topVer Topology version.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridOsCacheDrManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridOsCacheDrManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridOsCacheDrManager.java
index f3c1b23..8d7e4d8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridOsCacheDrManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/dr/GridOsCacheDrManager.java
@@ -22,6 +22,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.dr.GridDrType;
import org.apache.ignite.lang.IgniteFuture;
@@ -78,6 +79,18 @@ public class GridOsCacheDrManager implements GridCacheDrManager {
}
/** {@inheritDoc} */
+ @Override public void mvccReplicate(KeyCacheObject key, @Nullable CacheObject val, long ttl, long expireTime,
+ GridCacheVersion ver, GridDrType drType, AffinityTopologyVersion topVer,
+ MvccVersion mvccVer) throws IgniteCheckedException {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onTxFinished(MvccVersion mvccVer, boolean commit, AffinityTopologyVersion topVer) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
@Override public void onExchange(AffinityTopologyVersion topVer, boolean left) throws IgniteCheckedException {
// No-op.
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
index ef64ed8..b615952 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
@@ -54,8 +54,8 @@ import org.apache.ignite.internal.processors.cache.GridCachePreloaderAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.local.GridLocalCache;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalEx;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.resource.GridResourceIoc;
@@ -461,6 +461,7 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
taskName,
expiry,
!deserializeBinary,
+ null,
null);
if (res != null) {
@@ -488,7 +489,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
null,
taskName,
expiry,
- !deserializeBinary);
+ !deserializeBinary,
+ null);
if (v != null) {
ctx.addResult(vals,
@@ -1096,7 +1098,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
entryProcessor,
taskName,
null,
- keepBinary);
+ keepBinary,
+ null);
Object oldVal = null;
@@ -1238,7 +1241,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
null,
taskName,
null,
- keepBinary);
+ keepBinary,
+ null);
Object interceptorVal = ctx.config().getInterceptor().onBeforePut(new CacheLazyEntry(
ctx, entry.key(), old, keepBinary), val);
@@ -1273,7 +1277,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
null,
taskName,
null,
- keepBinary);
+ keepBinary,
+ null);
IgniteBiTuple<Boolean, ?> interceptorRes = ctx.config().getInterceptor()
.onBeforeRemove(new CacheLazyEntry(ctx, entry.key(), old, keepBinary));
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccCoordinator.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccCoordinator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccCoordinator.java
new file mode 100644
index 0000000..045177a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccCoordinator.java
@@ -0,0 +1,104 @@
+/*
+ * 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.mvcc;
+
+import java.io.Serializable;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+public class MvccCoordinator implements Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private final UUID nodeId;
+
+ /**
+ * Unique coordinator version, increases when new coordinator is assigned,
+ * can differ from topVer if we decide to assign coordinator manually.
+ */
+ private final long crdVer;
+
+ /** */
+ @GridToStringInclude
+ private final AffinityTopologyVersion topVer;
+
+ /**
+ * @param nodeId Coordinator node ID.
+ * @param crdVer Coordinator version.
+ * @param topVer Topology version when coordinator was assigned.
+ */
+ public MvccCoordinator(UUID nodeId, long crdVer, AffinityTopologyVersion topVer) {
+ assert nodeId != null;
+ assert crdVer > 0 : crdVer;
+ assert topVer != null;
+
+ this.nodeId = nodeId;
+ this.crdVer = crdVer;
+ this.topVer = topVer;
+ }
+
+ /**
+ * @return Unique coordinator version.
+ */
+ public long coordinatorVersion() {
+ return crdVer;
+ }
+
+ /**
+ * @return Coordinator node ID.
+ */
+ public UUID nodeId() {
+ return nodeId;
+ }
+
+ /**
+ * @return Topology version when coordinator was assigned.
+ */
+ public AffinityTopologyVersion topologyVersion() {
+ return topVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ MvccCoordinator that = (MvccCoordinator)o;
+
+ return crdVer == that.crdVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return (int)(crdVer ^ (crdVer >>> 32));
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccCoordinator.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDiscoveryData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDiscoveryData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDiscoveryData.java
new file mode 100644
index 0000000..d2e936f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccDiscoveryData.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.io.Serializable;
+
+/**
+ * MVCC discovery data to be shared between nodes on join.
+ */
+public class MvccDiscoveryData implements Serializable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Current coordinator. */
+ private MvccCoordinator crd;
+
+ /**
+ * @param crd Coordinator.
+ */
+ public MvccDiscoveryData(MvccCoordinator crd) {
+ this.crd = crd;
+ }
+
+ /**
+ * @return Current coordinator.
+ */
+ public MvccCoordinator coordinator() {
+ return crd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccDiscoveryData.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccEmptyLongList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccEmptyLongList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccEmptyLongList.java
new file mode 100644
index 0000000..7963685
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccEmptyLongList.java
@@ -0,0 +1,53 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public class MvccEmptyLongList implements MvccLongList {
+ /** */
+ public static MvccEmptyLongList INSTANCE = new MvccEmptyLongList();
+
+ /**
+ *
+ */
+ private MvccEmptyLongList() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long get(int i) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean contains(long val) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "MvccEmptyLongList[]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccFuture.java
new file mode 100644
index 0000000..0ca82d3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccFuture.java
@@ -0,0 +1,66 @@
+/*
+ * 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.mvcc;
+
+import java.util.UUID;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+public class MvccFuture<T> extends GridFutureAdapter<T> {
+ /** */
+ protected UUID crdId;
+
+ /**
+ * Default constructor.
+ */
+ public MvccFuture() {
+ }
+
+ /**
+ * @param crdId MVCC coordinator node ID.
+ */
+ public MvccFuture(UUID crdId) {
+ assert crdId != null;
+
+ this.crdId = crdId;
+ }
+
+ /**
+ * @return MVCC coordinator node ID.
+ */
+ public UUID coordinatorNodeId() {
+ return crdId;
+ }
+
+ /**
+ * @param crdId MVCC coordinator node ID.
+ */
+ public void coordinatorNodeId(UUID crdId) {
+ assert crdId != null;
+
+ this.crdId = crdId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccFuture.class, this, super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccLongList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccLongList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccLongList.java
new file mode 100644
index 0000000..8b580ed
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccLongList.java
@@ -0,0 +1,29 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public interface MvccLongList {
+ public int size();
+
+ public long get(int i);
+
+ public boolean contains(long val);
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccPreviousCoordinatorQueries.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccPreviousCoordinatorQueries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccPreviousCoordinatorQueries.java
new file mode 100644
index 0000000..024ef70
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccPreviousCoordinatorQueries.java
@@ -0,0 +1,222 @@
+/*
+ * 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.mvcc;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.managers.discovery.DiscoCache;
+import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.F;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker.MVCC_TRACKER_ID_NA;
+
+/**
+ *
+ */
+class MvccPreviousCoordinatorQueries {
+ /** */
+ private volatile boolean prevQueriesDone;
+
+ /** Map of nodes to active {@link MvccQueryTracker} IDs list. */
+ private final ConcurrentHashMap<UUID, Set<Long>> activeQueries = new ConcurrentHashMap<>();
+
+ /** */
+ private final ConcurrentHashMap<UUID, Set<Long>> rcvdAcks = new ConcurrentHashMap<>();
+
+ /** */
+ private Set<UUID> rcvd;
+
+ /** */
+ private Set<UUID> waitNodes;
+
+ /** */
+ private boolean initDone;
+
+ /**
+ * @param nodeQueries Active queries map.
+ * @param discoCache Discovery data.
+ * @param mgr Discovery manager.
+ */
+ void init(Map<UUID, GridLongList> nodeQueries, DiscoCache discoCache, GridDiscoveryManager mgr) {
+ synchronized (this) {
+ assert !initDone;
+ assert waitNodes == null;
+
+ waitNodes = new HashSet<>();
+
+ for (ClusterNode node : discoCache.allNodes()) {
+ if ((nodeQueries == null || !nodeQueries.containsKey(node.id())) &&
+ mgr.alive(node) &&
+ !F.contains(rcvd, node.id()))
+ waitNodes.add(node.id());
+ }
+
+ initDone = waitNodes.isEmpty();
+
+ if (nodeQueries != null) {
+ for (Map.Entry<UUID, GridLongList> e : nodeQueries.entrySet())
+ mergeToActiveQueries(e.getKey(), e.getValue());
+ }
+
+ if (initDone && !prevQueriesDone)
+ prevQueriesDone = activeQueries.isEmpty() && rcvdAcks.isEmpty();
+ }
+ }
+
+ /**
+ * @return {@code True} if all queries on
+ */
+ boolean previousQueriesDone() {
+ return prevQueriesDone;
+ }
+
+ /**
+ * Merges current node active queries with the given ones.
+ *
+ * @param nodeId Node ID.
+ * @param nodeTrackers Active query trackers started on node.
+ */
+ private void mergeToActiveQueries(UUID nodeId, GridLongList nodeTrackers) {
+ if (nodeTrackers == null || nodeTrackers.isEmpty() || prevQueriesDone)
+ return;
+
+ Set<Long> currTrackers = activeQueries.get(nodeId);
+
+ if (currTrackers == null)
+ activeQueries.put(nodeId, currTrackers = addAll(nodeTrackers, null));
+ else
+ addAll(nodeTrackers, currTrackers);
+
+ // Check if there were any acks had been arrived before.
+ Set<Long> currAcks = rcvdAcks.get(nodeId);
+
+ if (!currTrackers.isEmpty() && currAcks != null && !currAcks.isEmpty()) {
+ Collection<Long> intersection = new HashSet<>(currAcks);
+
+ intersection.retainAll(currTrackers);
+
+ currAcks.removeAll(intersection);
+ currTrackers.removeAll(intersection);
+
+ if (currTrackers.isEmpty())
+ activeQueries.remove(nodeId);
+
+ if (currAcks.isEmpty())
+ rcvdAcks.remove(nodeId);
+ }
+
+ if (initDone && !prevQueriesDone)
+ prevQueriesDone = activeQueries.isEmpty() && rcvdAcks.isEmpty();
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param nodeTrackers Active query trackers started on node.
+ */
+ void addNodeActiveQueries(UUID nodeId, @Nullable GridLongList nodeTrackers) {
+ synchronized (this) {
+ if (initDone)
+ return;
+
+ if (waitNodes == null) {
+ if (rcvd == null)
+ rcvd = new HashSet<>();
+
+ rcvd.add(nodeId);
+ }
+ else {
+ waitNodes.remove(nodeId);
+
+ initDone = waitNodes.isEmpty();
+ }
+
+ mergeToActiveQueries(nodeId, nodeTrackers);
+
+ if (initDone && !prevQueriesDone)
+ prevQueriesDone = activeQueries.isEmpty() && rcvdAcks.isEmpty();
+ }
+ }
+
+ /**
+ * @param nodeId Failed node ID.
+ */
+ void onNodeFailed(UUID nodeId) {
+ synchronized (this) {
+ if (waitNodes != null) {
+ waitNodes.remove(nodeId);
+
+ initDone = waitNodes.isEmpty();
+ }
+
+ if (initDone && !prevQueriesDone && activeQueries.remove(nodeId) != null)
+ prevQueriesDone = activeQueries.isEmpty() && rcvdAcks.isEmpty();
+ }
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param qryTrackerId Query tracker Id.
+ */
+ void onQueryDone(UUID nodeId, long qryTrackerId) {
+ if (qryTrackerId == MVCC_TRACKER_ID_NA)
+ return;
+
+ synchronized (this) {
+ Set<Long> nodeTrackers = activeQueries.get(nodeId);
+
+ if (nodeTrackers == null || !nodeTrackers.remove(qryTrackerId)) {
+ Set<Long> nodeAcks = rcvdAcks.get(nodeId);
+
+ if (nodeAcks == null)
+ rcvdAcks.put(nodeId, nodeAcks = new HashSet<>());
+
+ // We received qry done ack before the active qry message. Need to save it.
+ nodeAcks.add(qryTrackerId);
+ }
+
+ if (nodeTrackers != null && nodeTrackers.isEmpty())
+ activeQueries.remove(nodeId);
+
+ if (initDone && !prevQueriesDone)
+ prevQueriesDone = activeQueries.isEmpty() && rcvdAcks.isEmpty();
+ }
+ }
+
+ /**
+ * @param from Long list.
+ * @param to Set.
+ */
+ private Set<Long> addAll(GridLongList from, Set<Long> to) {
+ assert from != null;
+
+ if (to == null)
+ to = new HashSet<>(from.size());
+
+ for (int i = 0; i < from.size(); i++)
+ to.add(from.get(i));
+
+ return to;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessor.java
new file mode 100644
index 0000000..bce61a1
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessor.java
@@ -0,0 +1,250 @@
+/*
+ * 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.mvcc;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteDiagnosticPrepareContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.managers.discovery.DiscoCache;
+import org.apache.ignite.internal.processors.GridProcessor;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.ExchangeContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.util.GridLongList;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public interface MvccProcessor extends GridProcessor {
+ /**
+ * @param evtType Event type.
+ * @param nodes Current nodes.
+ * @param topVer Topology version.
+ */
+ void onDiscoveryEvent(int evtType, Collection<ClusterNode> nodes, long topVer);
+
+ /**
+ * Exchange start callback.
+ *
+ * @param mvccCrd Mvcc coordinator.
+ * @param exchCtx Exchange context.
+ * @param exchCrd Exchange coordinator.
+ */
+ void onExchangeStart(MvccCoordinator mvccCrd, ExchangeContext exchCtx, ClusterNode exchCrd);
+
+ /**
+ * Exchange done callback.
+ *
+ * @param newCoord New coordinator flag.
+ * @param discoCache Disco cache.
+ * @param activeQueries Active queries.
+ */
+ void onExchangeDone(boolean newCoord, DiscoCache discoCache, Map<UUID, GridLongList> activeQueries);
+
+ /**
+ * @param nodeId Node ID
+ * @param activeQueries Active queries.
+ */
+ void processClientActiveQueries(UUID nodeId, @Nullable GridLongList activeQueries);
+
+ /**
+ * @return Coordinator.
+ */
+ @Nullable MvccCoordinator currentCoordinator();
+
+ /**
+ * Check that the given topology is greater or equals to coordinator's one and returns current coordinator.
+ * @param topVer Topology version.
+ * @return Mvcc coordinator.
+ */
+ @Nullable MvccCoordinator currentCoordinator(AffinityTopologyVersion topVer);
+
+ /**
+ * @return Mvcc coordinator received from discovery event.
+ */
+ @Nullable MvccCoordinator coordinatorFromDiscoveryEvent();
+
+ /**
+ * @return Current coordinator node ID.
+ */
+ UUID currentCoordinatorId();
+
+ /**
+ * @param curCrd Coordinator.
+ */
+ void updateCoordinator(MvccCoordinator curCrd);
+
+ /**
+ * @param crdVer Mvcc coordinator version.
+ * @param cntr Mvcc counter.
+ * @return State for given mvcc version.
+ * @throws IgniteCheckedException If fails.
+ */
+ byte state(long crdVer, long cntr) throws IgniteCheckedException;
+
+ /**
+ * @param ver Version to check.
+ * @return State for given mvcc version.
+ * @throws IgniteCheckedException If fails.
+ */
+ byte state(MvccVersion ver) throws IgniteCheckedException;
+
+ /**
+ * @param ver Version.
+ * @param state State.
+ * @throws IgniteCheckedException If fails;
+ */
+ void updateState(MvccVersion ver, byte state) throws IgniteCheckedException;
+
+ /**
+ * @param ver Version.
+ * @param state State.
+ * @param primary Flag if this is primary node.
+ * @throws IgniteCheckedException If fails;
+ */
+ void updateState(MvccVersion ver, byte state, boolean primary) throws IgniteCheckedException;
+
+ /**
+ * @param crd Mvcc coordinator version.
+ * @param cntr Mvcc counter.
+ */
+ void registerLocalTransaction(long crd, long cntr);
+
+ /**
+ * @param crd Mvcc coordinator version.
+ * @param cntr Mvcc counter.
+ * @return {@code True} if there is an active local transaction with given version.
+ */
+ boolean hasLocalTransaction(long crd, long cntr);
+
+ /**
+ * @param cctx Cache context.
+ * @param locked Version the entry is locked by.
+ * @return Future, which is completed as soon as the lock is released.
+ * @throws IgniteCheckedException If failed.
+ */
+ IgniteInternalFuture<Void> waitFor(GridCacheContext cctx, MvccVersion locked) throws IgniteCheckedException;
+
+ /**
+ * @param tracker Query tracker.
+ */
+ void addQueryTracker(MvccQueryTracker tracker);
+
+ /**
+ * @param id Query tracker id.
+ */
+ void removeQueryTracker(Long id);
+
+ /**
+ * @return {@link MvccSnapshot} if this is a coordinator node and coordinator is initialized.
+ * {@code Null} in other cases.
+ * @throws ClusterTopologyCheckedException If coordinator doesn't match locked topology or not assigned.
+ */
+ MvccSnapshot tryRequestSnapshotLocal() throws ClusterTopologyCheckedException;
+
+ /**
+ * @param tx Transaction.
+ * @return {@link MvccSnapshot} if this is a coordinator node and coordinator is initialized.
+ * {@code Null} in other cases.
+ * @throws ClusterTopologyCheckedException If coordinator doesn't match locked topology or not assigned.
+ */
+ MvccSnapshot tryRequestSnapshotLocal(@Nullable IgniteInternalTx tx) throws ClusterTopologyCheckedException;
+
+ /**
+ * Requests snapshot on Mvcc coordinator.
+ *
+ * @return Snapshot future.
+ */
+ IgniteInternalFuture<MvccSnapshot> requestSnapshotAsync();
+
+ /**
+ * Requests snapshot on Mvcc coordinator.
+ *
+ * @param tx Transaction.
+ * @return Snapshot future.
+ */
+ IgniteInternalFuture<MvccSnapshot> requestSnapshotAsync(IgniteInternalTx tx);
+
+ /**
+ * Requests snapshot on Mvcc coordinator.
+ *
+ * @param lsnr Request listener.
+ */
+ void requestSnapshotAsync(MvccSnapshotResponseListener lsnr);
+
+ /**
+ * Requests snapshot on Mvcc coordinator.
+ *
+ * @param tx Transaction
+ * @param lsnr Request listener.
+ */
+ void requestSnapshotAsync(IgniteInternalTx tx, MvccSnapshotResponseListener lsnr);
+
+ /**
+ * @param updateVer Transaction update version.
+ * @return Acknowledge future.
+ */
+ IgniteInternalFuture<Void> ackTxCommit(MvccSnapshot updateVer);
+
+ /**
+ * @param updateVer Transaction update version.
+ * @param readSnapshot Transaction read version.
+ * @param qryId Query tracker id.
+ * @return Acknowledge future.
+ */
+ IgniteInternalFuture<Void> ackTxCommit(MvccVersion updateVer, MvccSnapshot readSnapshot, long qryId);
+
+ /**
+ * @param updateVer Transaction update version.
+ */
+ void ackTxRollback(MvccVersion updateVer);
+
+ /**
+ * @param updateVer Transaction update version.
+ * @param readSnapshot Transaction read version.
+ * @param qryTrackerId Query tracker id.
+ */
+ void ackTxRollback(MvccVersion updateVer, MvccSnapshot readSnapshot, long qryTrackerId);
+
+ /**
+ * @param snapshot Query version.
+ * @param qryId Query tracker ID.
+ */
+ void ackQueryDone(MvccSnapshot snapshot, long qryId);
+
+ /**
+ * @param crdId Coordinator ID.
+ * @param txs Transaction IDs.
+ * @return Future.
+ */
+ IgniteInternalFuture<Void> waitTxsFuture(UUID crdId, GridLongList txs);
+
+ /**
+ * @param log Logger.
+ * @param diagCtx Diagnostic request.
+ */
+ void dumpDebugInfo(IgniteLogger log, @Nullable IgniteDiagnosticPrepareContext diagCtx);
+}
[22/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataInnerIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataInnerIO.java
new file mode 100644
index 0000000..0b2f91c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataInnerIO.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tree.mvcc.data;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.tree.AbstractDataInnerIO;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.lang.IgniteInClosure;
+
+/**
+ *
+ */
+public final class MvccDataInnerIO extends AbstractDataInnerIO {
+ /** */
+ public static final IOVersions<MvccDataInnerIO> VERSIONS = new IOVersions<>(
+ new MvccDataInnerIO(1)
+ );
+
+ /**
+ * @param ver Page format version.
+ */
+ private MvccDataInnerIO(int ver) {
+ super(T_DATA_REF_MVCC_INNER, ver, true, 32);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void visit(long pageAddr, IgniteInClosure<CacheSearchRow> c) {
+ int cnt = getCount(pageAddr);
+
+ for (int i = 0; i < cnt; i++)
+ c.apply(new MvccDataRow(getLink(pageAddr, i)));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean storeMvccVersion() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 12);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 20);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 28);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataLeafIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataLeafIO.java
new file mode 100644
index 0000000..ab498d9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataLeafIO.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tree.mvcc.data;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.tree.AbstractDataLeafIO;
+import org.apache.ignite.lang.IgniteInClosure;
+
+/**
+ *
+ */
+public final class MvccDataLeafIO extends AbstractDataLeafIO {
+ /** */
+ public static final IOVersions<MvccDataLeafIO> VERSIONS = new IOVersions<>(
+ new MvccDataLeafIO(1)
+ );
+
+ /**
+ * @param ver Page format version.
+ */
+ private MvccDataLeafIO(int ver) {
+ super(T_DATA_REF_MVCC_LEAF, ver, 48);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void visit(long pageAddr, IgniteInClosure<CacheSearchRow> c) {
+ int cnt = getCount(pageAddr);
+
+ for (int i = 0; i < cnt; i++)
+ c.apply(new MvccDataRow(getLink(pageAddr, i)));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean storeMvccVersion() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 12);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 20);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 28);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccLockCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 32);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccLockCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 40);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMvccLockCoordinatorVersion(long pageAddr, int idx, long lockCrd) {
+ PageUtils.putLong(pageAddr, offset(idx) + 32, lockCrd);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMvccLockCounter(long pageAddr, int idx, long lockCntr) {
+ PageUtils.putLong(pageAddr, offset(idx) + 40, lockCntr);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataRow.java
new file mode 100644
index 0000000..7dc2051
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccDataRow.java
@@ -0,0 +1,263 @@
+/*
+ * 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.tree.mvcc.data;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.cache.tree.DataRow;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.MVCC_INFO_SIZE;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_BIT_OFF;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_MASK;
+
+/**
+ *
+ */
+public class MvccDataRow extends DataRow {
+ /** Mvcc coordinator version. */
+ @GridToStringInclude
+ protected long mvccCrd;
+
+ /** Mvcc counter. */
+ @GridToStringInclude
+ protected long mvccCntr;
+
+ /** Mvcc operation counter. */
+ @GridToStringInclude
+ protected int mvccOpCntr;
+
+ /** Mvcc tx state. */
+ @GridToStringInclude
+ protected byte mvccTxState;
+
+ /** New mvcc coordinator version. */
+ @GridToStringInclude
+ protected long newMvccCrd;
+
+ /** New mvcc counter. */
+ @GridToStringInclude
+ protected long newMvccCntr;
+
+ /** New mvcc operation counter. */
+ @GridToStringInclude
+ protected int newMvccOpCntr;
+
+ /** New mvcc tx state. */
+ @GridToStringInclude
+ protected byte newMvccTxState;
+
+ /**
+ * @param link Link.
+ */
+ public MvccDataRow(long link) {
+ super(link);
+ }
+
+ /**
+ * @param grp Context.
+ * @param hash Key hash.
+ * @param link Link.
+ * @param part Partition number.
+ * @param rowData Data.
+ * @param crdVer Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param mvccOpCntr Mvcc operation counter.
+ */
+ public MvccDataRow(CacheGroupContext grp,
+ int hash,
+ long link,
+ int part,
+ RowData rowData,
+ long crdVer,
+ long mvccCntr,
+ int mvccOpCntr) {
+ super(grp, hash, link, part, rowData);
+
+ assert MvccUtils.mvccVersionIsValid(crdVer, mvccCntr, mvccOpCntr);
+
+ assert rowData == RowData.LINK_ONLY
+ || this.mvccCrd == crdVer && this.mvccCntr == mvccCntr && this.mvccOpCntr == mvccOpCntr :
+ "mvccVer=" + new MvccVersionImpl(crdVer, mvccCntr, mvccOpCntr) +
+ ", dataMvccVer=" + new MvccVersionImpl(this.mvccCrd, this.mvccCntr, this.mvccOpCntr) ;
+
+ if (rowData == RowData.LINK_ONLY) {
+ this.mvccCrd = crdVer;
+ this.mvccCntr = mvccCntr;
+ this.mvccOpCntr = mvccOpCntr;
+ }
+ }
+
+ /**
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param part Partition.
+ * @param expireTime Expire time.
+ * @param cacheId Cache ID.
+ * @param mvccVer Mvcc version.
+ * @param newMvccVer New mvcc version.
+ */
+ public MvccDataRow(KeyCacheObject key, CacheObject val, GridCacheVersion ver, int part, long expireTime, int cacheId,
+ MvccVersion mvccVer, MvccVersion newMvccVer) {
+ super(key, val, ver, part, expireTime, cacheId);
+
+ this.mvccCrd = mvccVer.coordinatorVersion();
+ this.mvccCntr = mvccVer.counter();
+ this.mvccOpCntr = mvccVer.operationCounter();
+
+ if (newMvccVer == null) {
+ newMvccCrd = MVCC_CRD_COUNTER_NA;
+ newMvccCntr = MVCC_COUNTER_NA;
+ newMvccOpCntr = MVCC_OP_COUNTER_NA;
+ }
+ else {
+ newMvccCrd = newMvccVer.coordinatorVersion();
+ newMvccCntr = newMvccVer.counter();
+ newMvccOpCntr = newMvccVer.operationCounter();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected int readHeader(long addr, int off) {
+ // xid_min.
+ mvccCrd = PageUtils.getLong(addr, off);
+ mvccCntr = PageUtils.getLong(addr, off + 8);
+
+ int withHint = PageUtils.getInt(addr, off + 16);
+
+ mvccOpCntr = withHint & ~MVCC_HINTS_MASK;
+ mvccTxState = (byte)(withHint >>> MVCC_HINTS_BIT_OFF);
+
+ assert MvccUtils.mvccVersionIsValid(mvccCrd, mvccCntr, mvccOpCntr);
+
+ // xid_max.
+ newMvccCrd = PageUtils.getLong(addr, off + 20);
+ newMvccCntr = PageUtils.getLong(addr, off + 28);
+
+ withHint = PageUtils.getInt(addr, off + 36);
+
+ newMvccOpCntr = withHint & ~MVCC_HINTS_MASK;
+ newMvccTxState = (byte)(withHint >>> MVCC_HINTS_BIT_OFF);
+
+ assert newMvccCrd == MVCC_CRD_COUNTER_NA || MvccUtils.mvccVersionIsValid(newMvccCrd, newMvccCntr, newMvccOpCntr);
+
+ return MVCC_INFO_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return mvccCrd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return mvccCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return mvccOpCntr;
+ }
+
+ /** {@inheritDoc} */
+ public byte mvccTxState() {
+ return mvccTxState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCoordinatorVersion() {
+ return newMvccCrd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCounter() {
+ return newMvccCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int newMvccOperationCounter() {
+ return newMvccOpCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte newMvccTxState() {
+ return newMvccTxState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void newMvccVersion(long crd, long cntr, int opCntr) {
+ newMvccCrd = crd;
+ newMvccCntr = cntr;
+ newMvccOpCntr = opCntr;
+
+ // reset tx state
+ newMvccTxState = TxState.NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void mvccVersion(long crd, long cntr, int opCntr) {
+ mvccCrd = crd;
+ mvccCntr = cntr;
+ mvccOpCntr = opCntr;
+
+ // reset tx state
+ mvccTxState = TxState.NA;
+ }
+
+ /**
+ * @param mvccTxState Mvcc version Tx state hint.
+ */
+ public void mvccTxState(byte mvccTxState) {
+ this.mvccTxState = mvccTxState;
+ }
+
+ /**
+ * @param newMvccTxState New Mvcc version Tx state hint.
+ */
+ public void newMvccTxState(byte newMvccTxState) {
+ this.newMvccTxState = newMvccTxState;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ return super.size() + MVCC_INFO_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ return MVCC_INFO_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccDataRow.class, this, "super", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRow.java
new file mode 100644
index 0000000..81b1b58
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRow.java
@@ -0,0 +1,485 @@
+/*
+ * 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.tree.mvcc.data;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.tree.RowLinkIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.compare;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.isActive;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.isVisible;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVersionIsValid;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.unexpectedStateException;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_BIT_OFF;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_MASK;
+
+/**
+ *
+ */
+public class MvccUpdateDataRow extends MvccDataRow implements MvccUpdateResult, BPlusTree.TreeVisitorClosure<CacheSearchRow, CacheDataRow> {
+ /** */
+ private static final int FIRST = DIRTY << 1;
+ /** */
+ private static final int CHECK_VERSION = FIRST << 1;
+ /** */
+ private static final int LAST_COMMITTED_FOUND = CHECK_VERSION << 1;
+ /** */
+ private static final int CAN_CLEANUP = LAST_COMMITTED_FOUND << 1;
+ /** */
+ private static final int PRIMARY = CAN_CLEANUP << 1;
+ /** */
+ private static final int REMOVE_OR_LOCK = PRIMARY << 1;
+ /** */
+ private static final int NEED_HISTORY = REMOVE_OR_LOCK << 1;
+ /**
+ * During mvcc transaction processing conflicting row version could be met in storage.
+ * Not all such cases should lead to transaction abort.
+ * E.g. if UPDATE for a row meets concurrent INSERT for the same row
+ * (and row did not exist before both operations) then it means that UPDATE does not see the row at all
+ * and can proceed.
+ * This flag enables such mode when conflicting version should not lead to abort immediately
+ * but more versions should be checked.
+ */
+ private static final int FAST_UPDATE = NEED_HISTORY << 1;
+ /** */
+ private static final int FAST_MISMATCH = FAST_UPDATE << 1;
+ /** */
+ private static final int DELETED = FAST_MISMATCH << 1;
+
+ /** */
+ @GridToStringExclude
+ private final GridCacheContext cctx;
+
+ /** */
+ private ResultType res;
+
+ /** */
+ @GridToStringExclude
+ private int state;
+
+ /** */
+ private List<MvccLinkAwareSearchRow> cleanupRows;
+
+ /** */
+ private final MvccSnapshot mvccSnapshot;
+
+ /** */
+ private CacheDataRow oldRow;
+
+ /** */
+ @GridToStringExclude
+ private long resCrd;
+
+ /** */
+ @GridToStringExclude
+ private long resCntr;
+
+ /** */
+ private List<MvccLinkAwareSearchRow> historyRows;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param part Partition.
+ * @param expireTime Expire time.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param newVer Update version.
+ * @param primary Primary node flag.
+ * @param lockOnly Whether no actual update should be done and the only thing to do is to acquire lock.
+ * @param needHistory Whether to collect rows created or affected by the current tx.
+ * @param fastUpdate Fast update visit mode.
+ */
+ public MvccUpdateDataRow(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ int part,
+ long expireTime,
+ MvccSnapshot mvccSnapshot,
+ MvccVersion newVer,
+ boolean primary,
+ boolean lockOnly,
+ boolean needHistory,
+ boolean fastUpdate) {
+ super(key,
+ val,
+ ver,
+ part,
+ expireTime,
+ cctx.cacheId(),
+ mvccSnapshot,
+ newVer);
+
+ this.mvccSnapshot = mvccSnapshot;
+ this.cctx = cctx;
+
+ assert !lockOnly || val == null;
+
+ int flags = FIRST;
+
+ if (primary)
+ flags |= PRIMARY | CHECK_VERSION;
+
+ if (primary && (lockOnly || val == null))
+ flags |= CAN_WRITE | REMOVE_OR_LOCK;
+
+ if (needHistory)
+ flags |= NEED_HISTORY;
+
+ if (fastUpdate)
+ flags |= FAST_UPDATE;
+
+ setFlags(flags);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int visit(BPlusTree<CacheSearchRow, CacheDataRow> tree,
+ BPlusIO<CacheSearchRow> io,
+ long pageAddr,
+ int idx, IgniteWriteAheadLogManager wal)
+ throws IgniteCheckedException {
+ unsetFlags(DIRTY);
+
+ RowLinkIO rowIo = (RowLinkIO)io;
+
+ // Check if entry is locked on primary node.
+ if (isFlagsSet(PRIMARY | FIRST)) {
+ long lockCrd = rowIo.getMvccLockCoordinatorVersion(pageAddr, idx);
+ long lockCntr = rowIo.getMvccLockCounter(pageAddr, idx);
+
+ // We cannot continue while entry is locked by another transaction.
+ if ((lockCrd != mvccCrd || lockCntr != mvccCntr)
+ && isActive(cctx, lockCrd, lockCntr, mvccSnapshot)) {
+ resCrd = lockCrd;
+ resCntr = lockCntr;
+
+ res = ResultType.LOCKED;
+
+ return setFlags(STOP);
+ }
+ }
+
+ MvccDataRow row = (MvccDataRow)tree.getRow(io, pageAddr, idx, RowData.LINK_WITH_HEADER);
+
+ // Check whether the row was updated by current transaction.
+ // In this case the row is already locked by current transaction and visible to it.
+ if (isFlagsSet(FIRST)) {
+ boolean removed = row.newMvccCoordinatorVersion() != MVCC_CRD_COUNTER_NA;
+
+ long rowCrd, rowCntr; int rowOpCntr;
+
+ if (removed) {
+ rowCrd = row.newMvccCoordinatorVersion();
+ rowCntr = row.newMvccCounter();
+ rowOpCntr = row.newMvccOperationCounter();
+ }
+ else {
+ rowCrd = row.mvccCoordinatorVersion();
+ rowCntr = row.mvccCounter();
+ rowOpCntr = row.mvccOperationCounter();
+ }
+
+ if (compare(mvccSnapshot, rowCrd, rowCntr) == 0) {
+ res = mvccOpCntr == rowOpCntr ? ResultType.VERSION_FOUND :
+ removed ? ResultType.PREV_NULL : ResultType.PREV_NOT_NULL;
+
+ if (removed)
+ setFlags(DELETED);
+ else
+ oldRow = row;
+
+ setFlags(LAST_COMMITTED_FOUND);
+ }
+ }
+
+ long rowLink = row.link();
+
+ long rowCrd = row.mvccCoordinatorVersion();
+ long rowCntr = row.mvccCounter();
+
+ // with hint bits
+ int rowOpCntr = (row.mvccTxState() << MVCC_HINTS_BIT_OFF) | (row.mvccOperationCounter() & ~MVCC_HINTS_MASK);
+
+ long rowNewCrd = row.newMvccCoordinatorVersion();
+ long rowNewCntr = row.newMvccCounter();
+
+ // with hint bits
+ int rowNewOpCntr = (row.newMvccTxState() << MVCC_HINTS_BIT_OFF) | (row.newMvccOperationCounter() & ~MVCC_HINTS_MASK);
+
+ // Search for youngest committed by another transaction row.
+ if (!isFlagsSet(LAST_COMMITTED_FOUND)) {
+ if (!(resCrd == rowCrd && resCntr == rowCntr)) { // It's possible it is a chain of aborted changes
+ byte txState = MvccUtils.state(cctx, rowCrd, rowCntr, rowOpCntr);
+
+ if (txState == TxState.COMMITTED) {
+ setFlags(LAST_COMMITTED_FOUND);
+
+ if (rowNewCrd != MVCC_CRD_COUNTER_NA) {
+ if (rowNewCrd == rowCrd && rowNewCntr == rowCntr)
+ // Row was deleted by the same Tx it was created
+ txState = TxState.COMMITTED;
+ else if (rowNewCrd == resCrd && rowNewCntr == resCntr)
+ // The row is linked to the previously checked aborted version;
+ txState = TxState.ABORTED;
+ else
+ // Check with TxLog if removed version is committed;
+ txState = MvccUtils.state(cctx, rowNewCrd, rowNewCntr, rowNewOpCntr);
+
+ if (!(txState == TxState.COMMITTED || txState == TxState.ABORTED))
+ throw unexpectedStateException(cctx, txState, rowNewCrd, rowNewCntr, rowNewOpCntr, mvccSnapshot);
+
+ if (txState == TxState.COMMITTED)
+ setFlags(DELETED);
+ }
+
+ if (isFlagsSet(DELETED))
+ res = ResultType.PREV_NULL;
+ else {
+ res = ResultType.PREV_NOT_NULL;
+
+ oldRow = row;
+ }
+
+ if (isFlagsSet(CHECK_VERSION)) {
+ long crdVer, cntr; int opCntr;
+
+ if (isFlagsSet(DELETED)) {
+ crdVer = rowNewCrd;
+ cntr = rowNewCntr;
+ opCntr = rowNewOpCntr;
+ }
+ else {
+ crdVer = rowCrd;
+ cntr = rowCntr;
+ opCntr = rowOpCntr;
+ }
+
+ // If last committed row is not visible it is possible write conflict.
+ if (!isVisible(cctx, mvccSnapshot, crdVer, cntr, opCntr, false)) {
+ // In case when row is accessed without previous version check (FAST_UPDATE)
+ // it is possible that we should consider this row non existent for current transaction
+ // without signalling write conflict.
+ // To do this we need to find youngest visible version and if it is removed version
+ // or there is no visible version then there is no conflict.
+ if (isFlagsSet(FAST_UPDATE)
+ && !(isFlagsSet(DELETED)
+ && isVisible(cctx, mvccSnapshot, rowCrd, rowCntr, rowOpCntr, false))) {
+ res = ResultType.PREV_NULL;
+
+ setFlags(FAST_MISMATCH);
+ }
+ else {
+ resCrd = crdVer;
+ resCntr = cntr;
+
+ res = ResultType.VERSION_MISMATCH; // Write conflict.
+
+ return setFlags(STOP);
+ }
+ }
+ }
+
+ // Lock entry for primary partition if needed.
+ // If invisible row is found for FAST_UPDATE case we should not lock row.
+ if (isFlagsSet(PRIMARY | REMOVE_OR_LOCK) && !isFlagsSet(FAST_MISMATCH)) {
+ rowIo.setMvccLockCoordinatorVersion(pageAddr, idx, mvccCrd);
+ rowIo.setMvccLockCounter(pageAddr, idx, mvccCntr);
+
+ // TODO Delta record IGNITE-7991
+
+ setFlags(DIRTY);
+ }
+
+ unsetFlags(CAN_WRITE); // No need to acquire write locks anymore
+ }
+ else if (txState == TxState.ABORTED) { // save aborted version to fast check new version of next row
+ resCrd = rowCrd;
+ resCntr = rowCntr;
+ }
+ else
+ throw unexpectedStateException(cctx, txState, rowCrd, rowCntr, rowOpCntr, mvccSnapshot);
+ }
+ }
+ // Search for youngest visible row.
+ // If we have not found any visible version then we does not see this row.
+ else if (isFlagsSet(FAST_MISMATCH)) {
+ assert !isFlagsSet(CAN_CLEANUP);
+ assert mvccVersionIsValid(rowNewCrd, rowNewCntr, rowNewOpCntr);
+
+ // Update version could be visible only if it is removal version,
+ // previous create versions were already checked in previous step and are definitely invisible.
+ // If we found visible removal version then we does not see this row.
+ if (isVisible(cctx, mvccSnapshot, rowNewCrd, rowNewCntr, rowNewOpCntr, false))
+ unsetFlags(FAST_MISMATCH);
+ // If the youngest visible for current transaction version is not removal version then it is write conflict.
+ else if (isVisible(cctx, mvccSnapshot, rowCrd, rowCntr, rowOpCntr, false)) {
+ resCrd = rowCrd;
+ resCntr = rowCntr;
+
+ res = ResultType.VERSION_MISMATCH;
+
+ return setFlags(STOP);
+ }
+ }
+
+ long cleanupVer = mvccSnapshot.cleanupVersion();
+
+ if (cleanupVer > MVCC_OP_COUNTER_NA // Do not clean if cleanup version is not assigned.
+ && !isFlagsSet(CAN_CLEANUP)
+ && isFlagsSet(LAST_COMMITTED_FOUND | DELETED)) {
+ assert mvccVersionIsValid(rowNewCrd, rowNewCntr, rowNewOpCntr);
+
+ // We can cleanup previous row only if it was deleted by another
+ // transaction and delete version is less or equal to cleanup one
+ if (rowNewCrd < mvccCrd || Long.compare(cleanupVer, rowNewCntr) >= 0)
+ setFlags(CAN_CLEANUP);
+ }
+
+ if (isFlagsSet(CAN_CLEANUP)
+ || !isFlagsSet(LAST_COMMITTED_FOUND)) { // can cleanup aborted versions
+ if (cleanupRows == null)
+ cleanupRows = new ArrayList<>();
+
+ cleanupRows.add(new MvccLinkAwareSearchRow(cacheId, key, rowCrd, rowCntr, rowOpCntr & ~MVCC_HINTS_MASK, rowLink));
+ }
+ else {
+ // Row obsoleted by current operation, all rows created or updated with current tx.
+ if (isFlagsSet(NEED_HISTORY)
+ && (row == oldRow
+ || (rowCrd == mvccCrd && rowCntr == mvccCntr)
+ || (rowNewCrd == mvccCrd && rowNewCntr == mvccCntr))) {
+ if (historyRows == null)
+ historyRows = new ArrayList<>();
+
+ historyRows.add(new MvccLinkAwareSearchRow(cacheId, key, rowCrd, rowCntr, rowOpCntr & ~MVCC_HINTS_MASK, rowLink));
+ }
+
+ if (cleanupVer > MVCC_OP_COUNTER_NA // Do not clean if cleanup version is not assigned.
+ && !isFlagsSet(CAN_CLEANUP)
+ && isFlagsSet(LAST_COMMITTED_FOUND)
+ && (rowCrd < mvccCrd || Long.compare(cleanupVer, rowCntr) >= 0))
+ // all further versions are guaranteed to be less than cleanup version
+ setFlags(CAN_CLEANUP);
+ }
+
+ return unsetFlags(FIRST);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int state() {
+ return state;
+ }
+
+ /**
+ * @return Old row.
+ */
+ public CacheDataRow oldRow() {
+ return oldRow;
+ }
+
+ /**
+ * @return {@code True} if previous value was non-null.
+ */
+ @Override public ResultType resultType() {
+ return res == null ? ResultType.PREV_NULL : res;
+ }
+
+ /**
+ * @return Rows which are safe to cleanup.
+ */
+ public List<MvccLinkAwareSearchRow> cleanupRows() {
+ return cleanupRows;
+ }
+
+ /**
+ * @return Result version.
+ */
+ @Override public MvccVersion resultVersion() {
+ switch (resultType()) {
+ case VERSION_FOUND:
+ case PREV_NULL:
+
+ return new MvccVersionImpl(mvccCrd, mvccCntr, mvccOpCntr);
+ case PREV_NOT_NULL:
+
+ return new MvccVersionImpl(oldRow.mvccCoordinatorVersion(), oldRow.mvccCounter(), oldRow.mvccOperationCounter());
+ case LOCKED:
+ case VERSION_MISMATCH:
+
+ assert resCrd != MVCC_CRD_COUNTER_NA && resCntr != MVCC_COUNTER_NA;
+
+ return new MvccVersionImpl(resCrd, resCntr, MVCC_OP_COUNTER_NA);
+ default:
+
+ throw new IllegalStateException("Unexpected result type: " + resultType());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<MvccLinkAwareSearchRow> history() {
+ if (isFlagsSet(NEED_HISTORY) && historyRows == null)
+ historyRows = new ArrayList<>();
+
+ return historyRows;
+ }
+
+ /** */
+ private boolean isFlagsSet(int flags) {
+ return (state & flags) == flags;
+ }
+
+ /** */
+ private int setFlags(int flags) {
+ return state |= flags;
+ }
+
+ /** */
+ private int unsetFlags(int flags) {
+ return state &= (~flags);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccUpdateDataRow.class, this, "super", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRowNative.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRowNative.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRowNative.java
new file mode 100644
index 0000000..38611a9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateDataRowNative.java
@@ -0,0 +1,240 @@
+/*
+ * 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.tree.mvcc.data;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.tree.RowLinkIO;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class MvccUpdateDataRowNative extends MvccDataRow implements BPlusTree.TreeRowClosure<CacheSearchRow, CacheDataRow> {
+ /** */
+ private final MvccSnapshot mvccSnapshot;
+ /** */
+ private ResultType res;
+ /** */
+ private boolean canCleanup;
+ /** */
+ private GridLongList activeTxs;
+ /** */
+ private List<MvccLinkAwareSearchRow> cleanupRows;
+ /** */
+ private CacheDataRow oldRow;
+
+ /**
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param newVer Update version.
+ * @param part Partition.
+ * @param cctx Cache context.
+ */
+ public MvccUpdateDataRowNative(
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot,
+ MvccVersion newVer,
+ int part,
+ GridCacheContext cctx) {
+ super(key,
+ val,
+ ver,
+ part,
+ expireTime,
+ cctx.cacheId(),
+ mvccSnapshot,
+ newVer);
+
+ this.mvccSnapshot = mvccSnapshot;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<CacheSearchRow, CacheDataRow> tree,
+ BPlusIO<CacheSearchRow> io,
+ long pageAddr,
+ int idx)
+ throws IgniteCheckedException {
+ RowLinkIO rowIo = (RowLinkIO)io;
+
+ // Assert version grows.
+ assert assertVersion(rowIo, pageAddr, idx);
+
+ boolean checkActive = mvccSnapshot.activeTransactions().size() > 0;
+
+ boolean txActive = false;
+
+ long rowCrdVer = rowIo.getMvccCoordinatorVersion(pageAddr, idx);
+
+ long crdVer = mvccCoordinatorVersion();
+
+ boolean isFirstRmvd = false;
+
+ if (res == null) {
+ int cmp = Long.compare(crdVer, rowCrdVer);
+
+ if (cmp == 0)
+ cmp = Long.compare(mvccSnapshot.counter(), rowIo.getMvccCounter(pageAddr, idx));
+
+ if (cmp == 0)
+ res = ResultType.VERSION_FOUND;
+ else {
+ oldRow = tree.getRow(io, pageAddr, idx, RowData.LINK_WITH_HEADER);
+
+ isFirstRmvd = oldRow.newMvccCoordinatorVersion() != 0;
+
+ if (isFirstRmvd)
+ res = ResultType.PREV_NULL;
+ else
+ res = ResultType.PREV_NOT_NULL;
+ }
+ }
+
+ // Suppose transactions on previous coordinator versions are done.
+ if (checkActive && crdVer == rowCrdVer) {
+ long rowMvccCntr = rowIo.getMvccCounter(pageAddr, idx);
+
+ long activeTx = isFirstRmvd ? oldRow.newMvccCounter() : rowMvccCntr;
+
+ if (mvccSnapshot.activeTransactions().contains(activeTx)) {
+ txActive = true;
+
+ if (activeTxs == null)
+ activeTxs = new GridLongList();
+
+ activeTxs.add(activeTx);
+ }
+ }
+
+ if (!txActive) {
+ assert Long.compare(crdVer, rowCrdVer) >= 0;
+
+ int cmp;
+
+ long rowCntr = rowIo.getMvccCounter(pageAddr, idx);
+
+ if (crdVer == rowCrdVer)
+ cmp = Long.compare(mvccSnapshot.cleanupVersion(), rowCntr);
+ else
+ cmp = 1;
+
+ if (cmp >= 0) {
+ // Do not cleanup oldest version.
+ if (canCleanup) {
+ assert MvccUtils.mvccVersionIsValid(rowCrdVer, rowCntr);
+
+ // Should not be possible to cleanup active tx.
+ assert rowCrdVer != crdVer || !mvccSnapshot.activeTransactions().contains(rowCntr);
+
+ if (cleanupRows == null)
+ cleanupRows = new ArrayList<>();
+
+ cleanupRows.add(new MvccLinkAwareSearchRow(cacheId, key, rowCrdVer, rowCntr,
+ rowIo.getMvccOperationCounter(pageAddr, idx), rowIo.getLink(pageAddr, idx)));
+ }
+ else
+ canCleanup = true;
+ }
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return MvccUtils.MVCC_START_OP_CNTR;
+ }
+
+ /**
+ * @return Old row.
+ */
+ public CacheDataRow oldRow() {
+ return oldRow;
+ }
+
+ /**
+ * @return {@code True} if previous value was non-null.
+ */
+ public ResultType resultType() {
+ return res == null ? ResultType.PREV_NULL : res;
+ }
+
+ /**
+ * @return Active transactions to wait for.
+ */
+ @Nullable public GridLongList activeTransactions() {
+ return activeTxs;
+ }
+
+ /**
+ * @return Rows which are safe to cleanup.
+ */
+ public List<MvccLinkAwareSearchRow> cleanupRows() {
+ return cleanupRows;
+ }
+
+ /**
+ * @param io IO.
+ * @param pageAddr Page address.
+ * @param idx Item index.
+ * @return Always {@code true}.
+ */
+ private boolean assertVersion(RowLinkIO io, long pageAddr, int idx) {
+ long rowCrdVer = io.getMvccCoordinatorVersion(pageAddr, idx);
+ long rowCntr = io.getMvccCounter(pageAddr, idx);
+
+ int cmp = Long.compare(mvccCoordinatorVersion(), rowCrdVer);
+
+ if (cmp == 0)
+ cmp = Long.compare(mvccSnapshot.counter(), rowCntr);
+
+ // Can be equals if execute update on backup and backup already rebalanced value updated on primary.
+ assert cmp >= 0 : "[updCrd=" + mvccCoordinatorVersion() +
+ ", updCntr=" + mvccSnapshot.counter() +
+ ", rowCrd=" + rowCrdVer +
+ ", rowCntr=" + rowCntr + ']';
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccUpdateDataRowNative.class, this, "super", super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateResult.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateResult.java
new file mode 100644
index 0000000..086a30d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/MvccUpdateResult.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tree.mvcc.data;
+
+import java.util.List;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+
+/**
+ *
+ */
+public interface MvccUpdateResult {
+ /**
+ * @return Type of result.
+ */
+ public ResultType resultType();
+
+ /**
+ * @return Result version.
+ */
+ public MvccVersion resultVersion();
+
+ /**
+ *
+ * @return Collection of row created or affected by the current tx.
+ */
+ public List<MvccLinkAwareSearchRow> history();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/ResultType.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/ResultType.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/ResultType.java
new file mode 100644
index 0000000..eecb4a5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/data/ResultType.java
@@ -0,0 +1,34 @@
+/*
+ * 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.tree.mvcc.data;
+
+/**
+ *
+ */
+public enum ResultType {
+ /** */
+ VERSION_FOUND,
+ /** */
+ PREV_NULL,
+ /** */
+ PREV_NOT_NULL,
+ /** */
+ LOCKED,
+ /** */
+ VERSION_MISMATCH
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccFirstRowTreeClosure.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccFirstRowTreeClosure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccFirstRowTreeClosure.java
new file mode 100644
index 0000000..8546a66
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccFirstRowTreeClosure.java
@@ -0,0 +1,76 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.tree.RowLinkIO;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.getNewVersion;
+
+/**
+ * Closure which returns the very first encountered row.
+ */
+public class MvccFirstRowTreeClosure implements MvccTreeClosure {
+ /** */
+ private final GridCacheContext cctx;
+
+ /** */
+ private CacheDataRow res;
+
+ /**
+ * @param cctx Cache context.
+ */
+ public MvccFirstRowTreeClosure(GridCacheContext cctx) {
+ this.cctx = cctx;
+ }
+
+ /**
+ * @return Found row.
+ */
+ @Nullable public CacheDataRow row() {
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<CacheSearchRow, CacheDataRow> tree, BPlusIO<CacheSearchRow> io,
+ long pageAddr, int idx) throws IgniteCheckedException {
+ RowLinkIO rowIo = (RowLinkIO)io;
+
+ MvccVersion newVersion = getNewVersion(cctx, rowIo.getLink(pageAddr, idx));
+
+ if (newVersion == null)
+ res = tree.getRow(io, pageAddr, idx, CacheDataRowAdapter.RowData.NO_KEY);
+
+ return false; // Stop search.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccFirstRowTreeClosure.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccLinkAwareSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccLinkAwareSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccLinkAwareSearchRow.java
new file mode 100644
index 0000000..1e763a6
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccLinkAwareSearchRow.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+
+/**
+ * MVCC search row which contains a link. Now used only for cleanup purposes.
+ */
+public class MvccLinkAwareSearchRow extends MvccSearchRow {
+ /** */
+ private final long link;
+
+ /**
+ * @param cacheId Cache ID.
+ * @param key Key.
+ * @param crdVer Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param link Link.
+ */
+ public MvccLinkAwareSearchRow(int cacheId, KeyCacheObject key, long crdVer, long mvccCntr, int mvccOpCntr, long link) {
+ super(cacheId, key, crdVer, mvccCntr, mvccOpCntr);
+
+ assert link != 0L;
+
+ this.link = link;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long link() {
+ return link;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMaxSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMaxSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMaxSearchRow.java
new file mode 100644
index 0000000..f5f3b67
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMaxSearchRow.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.tree.SearchRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Search row for maximum key version.
+ */
+public class MvccMaxSearchRow extends SearchRow {
+ /**
+ * @param cacheId Cache ID.
+ * @param key Key.
+ */
+ public MvccMaxSearchRow(int cacheId, KeyCacheObject key) {
+ super(cacheId, key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return Long.MAX_VALUE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return Long.MAX_VALUE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return Integer.MAX_VALUE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccMaxSearchRow.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMinSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMinSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMinSearchRow.java
new file mode 100644
index 0000000..a1cb8fc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccMinSearchRow.java
@@ -0,0 +1,55 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.tree.SearchRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Search row for minimum key version.
+ */
+public class MvccMinSearchRow extends SearchRow {
+ /**
+ * @param cacheId Cache ID.
+ * @param key Key.
+ */
+ public MvccMinSearchRow(int cacheId, KeyCacheObject key) {
+ super(cacheId, key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return 1L;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return 1L;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccMinSearchRow.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSearchRow.java
new file mode 100644
index 0000000..d0510f7
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSearchRow.java
@@ -0,0 +1,70 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.tree.SearchRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * MVCC search row which uses specific MVCC version (coordinator + counter) to filter rows.
+ */
+public class MvccSearchRow extends SearchRow {
+ /** Coordinator version. */
+ protected long crdVer;
+
+ /** Counter. */
+ private long cntr;
+
+ /** Operation counter. */
+ protected int opCntr;
+
+ /**
+ * @param cacheId Cache ID.
+ * @param key Key.
+ * @param crdVer Coordinator version.
+ * @param cntr Counter.
+ */
+ public MvccSearchRow(int cacheId, KeyCacheObject key, long crdVer, long cntr, int opCntr) {
+ super(cacheId, key);
+
+ this.crdVer = crdVer;
+ this.cntr = cntr;
+ this.opCntr = opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return crdVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSearchRow.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSnapshotSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSnapshotSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSnapshotSearchRow.java
new file mode 100644
index 0000000..432593d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccSnapshotSearchRow.java
@@ -0,0 +1,113 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.tree.RowLinkIO;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_INVISIBLE;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_VISIBLE;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_VISIBLE_REMOVED;
+
+/**
+ * Search row which returns the first row visible for the given snapshot. Usage:
+ * - set this row as the upper bound
+ * - pass the same row as search closure.
+ */
+public class MvccSnapshotSearchRow extends MvccSearchRow implements MvccTreeClosure {
+ /** */
+ private final GridCacheContext cctx;
+
+ /** Resulting row. */
+ private CacheDataRow res;
+
+ /** */
+ private MvccSnapshot snapshot;
+
+ /**
+ * Constructor.
+ *
+ * @param cctx
+ * @param key Key.
+ * @param snapshot Snapshot.
+ */
+ public MvccSnapshotSearchRow(GridCacheContext cctx, KeyCacheObject key,
+ MvccSnapshot snapshot) {
+ super(cctx.cacheId(), key, snapshot.coordinatorVersion(), snapshot.counter(), Integer.MAX_VALUE);
+
+ this.cctx = cctx;
+
+ this.snapshot = snapshot;
+ }
+
+ /**
+ * @return Found row.
+ */
+ @Nullable public CacheDataRow row() {
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<CacheSearchRow, CacheDataRow> tree, BPlusIO<CacheSearchRow> io,
+ long pageAddr, int idx) throws IgniteCheckedException {
+ RowLinkIO rowIo = (RowLinkIO)io;
+
+ long rowCrdVer = rowIo.getMvccCoordinatorVersion(pageAddr, idx);
+ long rowCntr = rowIo.getMvccCounter(pageAddr, idx);
+ int rowOpCntr = rowIo.getMvccOperationCounter(pageAddr, idx);
+
+ assert MvccUtils.mvccVersionIsValid(rowCrdVer, rowCntr, rowOpCntr);
+
+ if (MvccUtils.isVisible(cctx, snapshot, rowCrdVer, rowCntr, rowOpCntr, false)) {
+ int state = MvccUtils.getVisibleState(cctx, rowIo.getLink(pageAddr, idx), snapshot);
+
+ if (state == MVCC_INVISIBLE)
+ return true;
+
+ if (state == MVCC_VISIBLE_REMOVED)
+ res = null;
+ else {
+ assert state == MVCC_VISIBLE;
+
+ res = tree.getRow(io, pageAddr, idx, CacheDataRowAdapter.RowData.NO_KEY);
+
+ res.key(key());
+ }
+
+ return false; // Stop search.
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSnapshotSearchRow.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccTreeClosure.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccTreeClosure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccTreeClosure.java
new file mode 100644
index 0000000..031903f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/mvcc/search/MvccTreeClosure.java
@@ -0,0 +1,29 @@
+/*
+ * 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.tree.mvcc.search;
+
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+
+/**
+ * Common interface for MVCC search row closures (no-op, only to have clear inheritance hierarchy).
+ */
+public interface MvccTreeClosure extends BPlusTree.TreeRowClosure<CacheSearchRow, CacheDataRow> {
+ // No-op.
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlStateCode.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlStateCode.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlStateCode.java
index eff680f..68ac200 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlStateCode.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlStateCode.java
@@ -61,6 +61,9 @@ public final class SqlStateCode {
/** Requested operation is not supported. */
public final static String UNSUPPORTED_OPERATION = "0A000";
+ /** Transaction state exception. */
+ public final static String TRANSACTION_STATE_EXCEPTION = "25000";
+
/** Parsing exception. */
public final static String PARSING_EXCEPTION = "42000";
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
index bdc558c..065a298 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
@@ -39,6 +39,9 @@ public class JdbcBatchExecuteRequest extends JdbcRequest {
@GridToStringInclude(sensitive = true)
private List<JdbcQuery> queries;
+ /** Client auto commit flag state. */
+ private boolean autoCommit;
+
/**
* Last stream batch flag - whether open streamers on current connection
* must be flushed and closed after this batch.
@@ -63,15 +66,17 @@ public class JdbcBatchExecuteRequest extends JdbcRequest {
/**
* @param schemaName Schema name.
* @param queries Queries.
+ * @param autoCommit Client auto commit flag state.
* @param lastStreamBatch {@code true} in case the request is the last batch at the stream.
*/
- public JdbcBatchExecuteRequest(String schemaName, List<JdbcQuery> queries, boolean lastStreamBatch) {
+ public JdbcBatchExecuteRequest(String schemaName, List<JdbcQuery> queries, boolean autoCommit, boolean lastStreamBatch) {
super(BATCH_EXEC);
assert lastStreamBatch || !F.isEmpty(queries);
this.schemaName = schemaName;
this.queries = queries;
+ this.autoCommit = autoCommit;
this.lastStreamBatch = lastStreamBatch;
}
@@ -81,15 +86,17 @@ public class JdbcBatchExecuteRequest extends JdbcRequest {
* @param type Request type.
* @param schemaName Schema name.
* @param queries Queries.
+ * @param autoCommit Client auto commit flag state.
* @param lastStreamBatch {@code true} in case the request is the last batch at the stream.
*/
- protected JdbcBatchExecuteRequest(byte type, String schemaName, List<JdbcQuery> queries, boolean lastStreamBatch) {
+ protected JdbcBatchExecuteRequest(byte type, String schemaName, List<JdbcQuery> queries, boolean autoCommit, boolean lastStreamBatch) {
super(type);
assert lastStreamBatch || !F.isEmpty(queries);
this.schemaName = schemaName;
this.queries = queries;
+ this.autoCommit = autoCommit;
this.lastStreamBatch = lastStreamBatch;
}
@@ -108,6 +115,13 @@ public class JdbcBatchExecuteRequest extends JdbcRequest {
}
/**
+ * @return Auto commit flag.
+ */
+ boolean autoCommit() {
+ return autoCommit;
+ }
+
+ /**
* @return Last stream batch flag.
*/
public boolean isLastStreamBatch() {
@@ -125,14 +139,17 @@ public class JdbcBatchExecuteRequest extends JdbcRequest {
for (JdbcQuery q : queries)
q.writeBinary(writer);
+
}
else
writer.writeInt(0);
+ writer.writeBoolean(autoCommit);
writer.writeBoolean(lastStreamBatch);
}
/** {@inheritDoc} */
+ @SuppressWarnings("SimplifiableIfStatement")
@Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException {
super.readBinary(reader);
@@ -151,8 +168,12 @@ public class JdbcBatchExecuteRequest extends JdbcRequest {
}
try {
- if (reader.available() > 0)
+ if (reader.available() > 0) {
+ autoCommit = reader.readBoolean();
lastStreamBatch = reader.readBoolean();
+ }
+ else
+ autoCommit = true;
}
catch (IOException e) {
throw new BinaryObjectException(e);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
index 23f4d49..9f3fe2a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
@@ -29,8 +29,10 @@ import org.apache.ignite.internal.processors.odbc.ClientListenerMessageParser;
import org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler;
import org.apache.ignite.internal.processors.odbc.ClientListenerResponse;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.nio.GridNioSession;
+import org.apache.ignite.internal.util.typedef.F;
/**
* JDBC Connection Context.
@@ -129,29 +131,44 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
boolean autoCloseCursors = reader.readBoolean();
boolean lazyExec = false;
+ boolean skipReducerOnUpdate = false;
+
+ NestedTxMode nestedTxMode = NestedTxMode.DEFAULT;
+ AuthorizationContext actx = null;
if (ver.compareTo(VER_2_1_5) >= 0)
lazyExec = reader.readBoolean();
- boolean skipReducerOnUpdate = false;
-
if (ver.compareTo(VER_2_3_0) >= 0)
skipReducerOnUpdate = reader.readBoolean();
- String user = null;
- String passwd = null;
+ if (ver.compareTo(VER_2_5_0) >= 0) {
+ String nestedTxModeName = reader.readString();
- try {
- if (reader.available() > 0) {
- user = reader.readString();
- passwd = reader.readString();
+ if (!F.isEmpty(nestedTxModeName)) {
+ try {
+ nestedTxMode = NestedTxMode.valueOf(nestedTxModeName);
+ }
+ catch (IllegalArgumentException e) {
+ throw new IgniteCheckedException("Invalid nested transactions handling mode: " + nestedTxModeName);
+ }
}
- }
- catch (Exception e) {
- throw new IgniteCheckedException("Handshake error: " + e.getMessage(), e);
- }
- AuthorizationContext actx = authenticate(user, passwd);
+ String user = null;
+ String passwd = null;
+
+ try {
+ if (reader.available() > 0) {
+ user = reader.readString();
+ passwd = reader.readString();
+ }
+ }
+ catch (Exception e) {
+ throw new IgniteCheckedException("Handshake error: " + e.getMessage(), e);
+ }
+
+ actx = authenticate(user, passwd);
+ }
parser = new JdbcMessageParser(ctx);
@@ -169,7 +186,9 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
};
handler = new JdbcRequestHandler(ctx, busyLock, sender, maxCursors, distributedJoins, enforceJoinOrder,
- collocated, replicatedOnly, autoCloseCursors, lazyExec, skipReducerOnUpdate, actx, ver);
+ collocated, replicatedOnly, autoCloseCursors, lazyExec, skipReducerOnUpdate, nestedTxMode, actx, ver);
+
+ handler.start();
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcOrderedBatchExecuteRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcOrderedBatchExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcOrderedBatchExecuteRequest.java
index 3e84731..1d4d4ac 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcOrderedBatchExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcOrderedBatchExecuteRequest.java
@@ -42,12 +42,13 @@ public class JdbcOrderedBatchExecuteRequest extends JdbcBatchExecuteRequest
/**
* @param schemaName Schema name.
* @param queries Queries.
+ * @param autoCommit Client auto commit flag state.
* @param lastStreamBatch {@code true} in case the request is the last batch at the stream.
* @param order Request order.
*/
public JdbcOrderedBatchExecuteRequest(String schemaName, List<JdbcQuery> queries,
- boolean lastStreamBatch, long order) {
- super(BATCH_EXEC_ORDERED, schemaName, queries, lastStreamBatch);
+ boolean autoCommit, boolean lastStreamBatch, long order) {
+ super(BATCH_EXEC_ORDERED, schemaName, queries, autoCommit, lastStreamBatch);
this.order = order;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
index 3e54fc8..c13117a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
@@ -51,6 +51,9 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
/** Expected statement type. */
private JdbcStatementType stmtType;
+ /** Client auto commit flag state. */
+ private boolean autoCommit;
+
/**
*/
JdbcQueryExecuteRequest() {
@@ -62,11 +65,12 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
* @param schemaName Cache name.
* @param pageSize Fetch size.
* @param maxRows Max rows.
+ * @param autoCommit Connection auto commit flag state.
* @param sqlQry SQL query.
* @param args Arguments list.
*/
public JdbcQueryExecuteRequest(JdbcStatementType stmtType, String schemaName, int pageSize, int maxRows,
- String sqlQry, Object[] args) {
+ boolean autoCommit, String sqlQry, Object[] args) {
super(QRY_EXEC);
this.schemaName = F.isEmpty(schemaName) ? null : schemaName;
@@ -75,6 +79,7 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
this.sqlQry = sqlQry;
this.args = args;
this.stmtType = stmtType;
+ this.autoCommit = autoCommit;
}
/**
@@ -119,6 +124,13 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
return stmtType;
}
+ /**
+ * @return Auto commit flag.
+ */
+ boolean autoCommit() {
+ return autoCommit;
+ }
+
/** {@inheritDoc} */
@Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException {
super.writeBinary(writer);
@@ -136,9 +148,12 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
}
writer.writeByte((byte)stmtType.ordinal());
+
+ writer.writeBoolean(autoCommit);
}
/** {@inheritDoc} */
+ @SuppressWarnings("SimplifiableIfStatement")
@Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException {
super.readBinary(reader);
@@ -163,6 +178,16 @@ public class JdbcQueryExecuteRequest extends JdbcRequest {
catch (IOException e) {
throw new BinaryObjectException(e);
}
+
+ try {
+ if (reader.available() > 0)
+ autoCommit = reader.readBoolean();
+ else
+ autoCommit = true;
+ }
+ catch (IOException e) {
+ throw new BinaryObjectException(e);
+ }
}
/** {@inheritDoc} */
[28/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTrackerImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTrackerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTrackerImpl.java
new file mode 100644
index 0000000..f46d1e0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTrackerImpl.java
@@ -0,0 +1,348 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.util.future.GridFinishedFuture;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.jetbrains.annotations.NotNull;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.noCoordinatorError;
+
+/**
+ * Tracker used for an optimistic tx and not-in-tx queries.
+ */
+@SuppressWarnings("unchecked")
+public class MvccQueryTrackerImpl implements MvccQueryTracker {
+ /** */
+ @GridToStringExclude
+ private final GridCacheContext cctx;
+
+ /** */
+ @GridToStringExclude
+ private final IgniteLogger log;
+
+ /** */
+ @GridToStringExclude
+ private long crdVer;
+
+ /** */
+ private final long id;
+
+ /** */
+ private MvccSnapshot snapshot;
+
+ /** */
+ private volatile AffinityTopologyVersion topVer;
+
+ /** */
+ private final boolean canRemap;
+
+ /**
+ * @param cctx Cache context.
+ */
+ public MvccQueryTrackerImpl(GridCacheContext cctx) {
+ this(cctx, true);
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param canRemap {@code True} if tracker can remap on coordinator fail.
+ */
+ public MvccQueryTrackerImpl(GridCacheContext cctx, boolean canRemap) {
+ this.cctx = cctx;
+ this.id = ID_CNTR.incrementAndGet();
+ this.canRemap = canRemap;
+
+ log = cctx.logger(getClass());
+ }
+
+ /** {@inheritDoc} */
+ @Override public long id() {
+ return id;
+ }
+
+ /** {@inheritDoc} */
+ @Override public synchronized MvccSnapshot snapshot() {
+ return snapshot;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheContext context() {
+ return cctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public AffinityTopologyVersion topologyVersion() {
+ return topVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshot() {
+ MvccSnapshot snapshot; MvccSnapshotFuture fut;
+
+ if ((snapshot = snapshot()) != null)
+ return new GridFinishedFuture<>(snapshot);
+
+ requestSnapshot0(cctx.shared().exchange().readyAffinityVersion(), fut = new MvccSnapshotFuture());
+
+ return fut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshot(@NotNull AffinityTopologyVersion topVer) {
+ MvccSnapshot snapshot; MvccSnapshotFuture fut;
+
+ if ((snapshot = snapshot()) != null)
+ return new GridFinishedFuture<>(snapshot);
+
+ requestSnapshot0(topVer, fut = new MvccSnapshotFuture());
+
+ return fut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void requestSnapshot(@NotNull AffinityTopologyVersion topVer, @NotNull MvccSnapshotResponseListener lsnr) {
+ MvccSnapshot snapshot = snapshot();
+
+ if (snapshot != null)
+ lsnr.onResponse(snapshot);
+ else
+ requestSnapshot0(topVer, lsnr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onDone() {
+ MvccProcessor prc = cctx.shared().coordinators();
+
+ MvccSnapshot snapshot = snapshot();
+
+ if (snapshot != null) {
+ prc.removeQueryTracker(id);
+
+ prc.ackQueryDone(snapshot, id);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> onDone(@NotNull GridNearTxLocal tx, boolean commit) {
+ MvccSnapshot snapshot = snapshot(), txSnapshot = tx.mvccSnapshot();
+
+ if (snapshot == null && txSnapshot == null)
+ return commit ? new GridFinishedFuture<>() : null;
+
+ MvccProcessor prc = cctx.shared().coordinators();
+
+ if (snapshot != null)
+ prc.removeQueryTracker(id);
+
+ if (txSnapshot == null)
+ prc.ackQueryDone(snapshot, id);
+ else if (commit)
+ return prc.ackTxCommit(txSnapshot, snapshot, id);
+ else
+ prc.ackTxRollback(txSnapshot, snapshot, id);
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public synchronized long onMvccCoordinatorChange(MvccCoordinator newCrd) {
+ if (snapshot != null) {
+ assert crdVer != 0 : this;
+
+ if (crdVer != newCrd.coordinatorVersion()) {
+ crdVer = newCrd.coordinatorVersion();
+
+ return id;
+ }
+ else
+ return MVCC_TRACKER_ID_NA;
+ }
+ else if (crdVer != 0)
+ crdVer = 0; // Mark for remap.
+
+ return MVCC_TRACKER_ID_NA;
+ }
+
+ /** */
+ private void requestSnapshot0(AffinityTopologyVersion topVer, MvccSnapshotResponseListener lsnr) {
+ if (checkTopology(topVer, lsnr = decorate(lsnr))) {
+ try {
+ MvccSnapshot snapshot = cctx.shared().coordinators().tryRequestSnapshotLocal();
+
+ if (snapshot == null)
+ cctx.shared().coordinators().requestSnapshotAsync(lsnr);
+ else
+ lsnr.onResponse(snapshot);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ lsnr.onError(e);
+ }
+ }
+ }
+
+ /** */
+ private MvccSnapshotResponseListener decorate(MvccSnapshotResponseListener lsnr) {
+ assert lsnr != null;
+
+ if (lsnr.getClass() == ListenerDecorator.class)
+ return lsnr;
+
+ return new ListenerDecorator(lsnr);
+ }
+
+ /**
+ * Validates if mvcc snapshot could be requested on the given topology.
+ *
+ * @return {@code True} if topology is valid.
+ */
+ private boolean checkTopology(AffinityTopologyVersion topVer, MvccSnapshotResponseListener lsnr) {
+ MvccCoordinator crd = cctx.affinity().mvccCoordinator(topVer);
+
+ if (crd == null) {
+ lsnr.onError(noCoordinatorError(topVer));
+
+ return false;
+ }
+
+ this.topVer = topVer;
+
+ synchronized (this) {
+ crdVer = crd.coordinatorVersion();
+ }
+
+ MvccCoordinator curCrd = cctx.topology().mvccCoordinator();
+
+ if (!crd.equals(curCrd)) {
+ assert cctx.topology().topologyVersionFuture().initialVersion().compareTo(topVer) > 0;
+
+ tryRemap(lsnr);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /** */
+ private void tryRemap(MvccSnapshotResponseListener lsnr) {
+ if (!canRemap) {
+ lsnr.onError(new ClusterTopologyCheckedException("Failed to request mvcc version, coordinator failed."));
+
+ return;
+ }
+
+ IgniteInternalFuture<AffinityTopologyVersion> waitFut =
+ cctx.shared().exchange().affinityReadyFuture(topVer.nextMinorVersion());
+
+ if (waitFut == null)
+ requestSnapshot(cctx.shared().exchange().readyAffinityVersion(), lsnr);
+ else {
+ waitFut.listen(new IgniteInClosure<IgniteInternalFuture<AffinityTopologyVersion>>() {
+ @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> fut) {
+ try {
+ requestSnapshot(fut.get(), lsnr);
+ }
+ catch (IgniteCheckedException e) {
+ lsnr.onError(e);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * @param res Response.
+ * @param lsnr Response listener.
+ * @return {@code false} if need to remap.
+ */
+ private boolean onResponse0(@NotNull MvccSnapshot res, MvccSnapshotResponseListener lsnr) {
+ boolean needRemap = false;
+
+ synchronized (this) {
+ assert snapshot() == null : "[this=" + this + ", rcvdVer=" + res + "]";
+
+ if (crdVer != 0) {
+ this.snapshot = res;
+ }
+ else
+ needRemap = true;
+ }
+
+ if (needRemap) { // Coordinator failed or reassigned, need remap.
+ tryRemap(lsnr);
+
+ return false;
+ }
+
+ cctx.shared().coordinators().addQueryTracker(this);
+
+ return true;
+ }
+
+ /**
+ * @param e Exception.
+ * @param lsnr Response listener.
+ * @return {@code false} if need to remap.
+ */
+ private boolean onError0(IgniteCheckedException e, MvccSnapshotResponseListener lsnr) {
+ if (e instanceof ClusterTopologyCheckedException && canRemap) {
+ if (e instanceof ClusterTopologyServerNotFoundException)
+ return true; // No Mvcc coordinator assigned
+
+ if (log.isDebugEnabled())
+ log.debug("Mvcc coordinator failed, need remap: " + e);
+
+ tryRemap(lsnr);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /** */
+ private final class ListenerDecorator implements MvccSnapshotResponseListener {
+ /** */
+ private final MvccSnapshotResponseListener lsnr;
+
+ /** */
+ private ListenerDecorator(MvccSnapshotResponseListener lsnr) {
+ this.lsnr = lsnr;
+ }
+
+ @Override public void onResponse(MvccSnapshot res) {
+ if (onResponse0(res, this))
+ lsnr.onResponse(res);
+ }
+
+ @Override public void onError(IgniteCheckedException e) {
+ if (onError0(e, this))
+ lsnr.onError(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshot.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshot.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshot.java
new file mode 100644
index 0000000..5ed743a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshot.java
@@ -0,0 +1,48 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.plugin.extensions.communication.Message;
+
+/**
+ * MVCC snapshot which holds the following information:
+ * - Current MVCC version which should be used for visibility checks
+ * - List of active transactions which should not be visible to current transaction
+ * - Cleanup version which is used to help vacuum process.
+ */
+public interface MvccSnapshot extends MvccVersion, Message {
+ /**
+ * @return Active transactions.
+ */
+ public MvccLongList activeTransactions();
+
+ /**
+ * @return Cleanup version (all smaller versions are safe to remove).
+ */
+ public long cleanupVersion();
+
+ /**
+ * @return Version without active transactions.
+ */
+ public MvccSnapshot withoutActiveTransactions();
+
+ /**
+ * Increments operation counter.
+ */
+ public void incrementOperationCounter();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotFuture.java
new file mode 100644
index 0000000..934ff2f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotFuture.java
@@ -0,0 +1,43 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+public class MvccSnapshotFuture extends MvccFuture<MvccSnapshot> implements MvccSnapshotResponseListener {
+ /** {@inheritDoc} */
+ @Override public void onResponse(MvccSnapshot res) {
+ assert res != null;
+
+ onDone(res);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onError(IgniteCheckedException err) {
+ onDone(err);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSnapshotFuture.class, this, super.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotResponseListener.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotResponseListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotResponseListener.java
new file mode 100644
index 0000000..e0bf448
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotResponseListener.java
@@ -0,0 +1,35 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ *
+ */
+public interface MvccSnapshotResponseListener {
+ /**
+ * @param res Version.
+ */
+ public void onResponse(MvccSnapshot res);
+
+ /**
+ * @param e Error.
+ */
+ public void onError(IgniteCheckedException e);
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotWithoutTxs.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotWithoutTxs.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotWithoutTxs.java
new file mode 100644
index 0000000..5be6317
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccSnapshotWithoutTxs.java
@@ -0,0 +1,204 @@
+/*
+ * 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.mvcc;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccSnapshotWithoutTxs implements MvccSnapshot {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long crdVer;
+
+ /** */
+ private long cntr;
+
+ /** */
+ private long cleanupVer;
+
+ /** */
+ private int opCntr;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccSnapshotWithoutTxs() {
+ // No-op.
+ }
+
+ /**
+ * @param crdVer Coordinator version.
+ * @param cntr Counter.
+ * @param cleanupVer Cleanup version.
+ */
+ public MvccSnapshotWithoutTxs(long crdVer, long cntr, int opCntr, long cleanupVer) {
+ this.crdVer = crdVer;
+ this.cntr = cntr;
+ this.cleanupVer = cleanupVer;
+ this.opCntr = opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccLongList activeTransactions() {
+ return MvccEmptyLongList.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long coordinatorVersion() {
+ return crdVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long cleanupVersion() {
+ return cleanupVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long counter() {
+ return cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int operationCounter() {
+ return opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void incrementOperationCounter() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot withoutActiveTransactions() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("cleanupVer", cleanupVer))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeLong("cntr", cntr))
+ return false;
+
+ writer.incrementState();
+
+ case 2:
+ if (!writer.writeLong("crdVer", crdVer))
+ return false;
+
+ writer.incrementState();
+
+ case 3:
+ if (!writer.writeInt("opCntr", opCntr))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ cleanupVer = reader.readLong("cleanupVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ cntr = reader.readLong("cntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 2:
+ crdVer = reader.readLong("crdVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 3:
+ opCntr = reader.readInt("opCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccSnapshotWithoutTxs.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 150;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSnapshotWithoutTxs.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUpdateVersionAware.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUpdateVersionAware.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUpdateVersionAware.java
new file mode 100644
index 0000000..17804c4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUpdateVersionAware.java
@@ -0,0 +1,76 @@
+/*
+ * 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.mvcc;
+
+/**
+ * Interface for objects aware theirs mvcc update version.
+ */
+public interface MvccUpdateVersionAware {
+ /**
+ * @return New mvcc coordinator version.
+ */
+ public long newMvccCoordinatorVersion();
+
+ /**
+ * @return New mvcc counter.
+ */
+ public long newMvccCounter();
+
+ /**
+ * @return New mvcc operation counter.
+ */
+ public int newMvccOperationCounter();
+
+ /**
+ * @return New Tx state.
+ */
+ public byte newMvccTxState();
+
+ /**
+ * Copies new MVCC version
+ * @param other Object to copy version from.
+ */
+ public default void newMvccVersion(MvccUpdateVersionAware other) {
+ newMvccVersion(other.newMvccCoordinatorVersion(), other.newMvccCounter(), other.newMvccOperationCounter());
+ }
+
+ /**
+ * Sets new MVCC version
+ * @param ver MVCC version.
+ */
+ public default void newMvccVersion(MvccVersion ver) {
+ newMvccVersion(ver.coordinatorVersion(), ver.counter(), ver.operationCounter());
+ }
+
+ /**
+ * Sets new mvcc version.
+ * @param crd New mvcc coordinator version.
+ * @param cntr New mvcc counter.
+ * @param opCntr New mvcc operation counter.
+ */
+ public default void newMvccVersion(long crd, long cntr, int opCntr) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @return New mvcc version.
+ */
+ public default MvccVersion newMvccVersion() {
+ return new MvccVersionImpl(newMvccCoordinatorVersion(), newMvccCounter(), newMvccOperationCounter());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java
new file mode 100644
index 0000000..33f457d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccUtils.java
@@ -0,0 +1,882 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteTxManager;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.transactions.IgniteTxMvccVersionCheckedException;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.transactions.TransactionState;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.pagemem.PageIdUtils.itemId;
+import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.MVCC_INFO_SIZE;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_BIT_OFF;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_MASK;
+import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.TRANSACTION_COMPLETED;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * Utils for MVCC.
+ */
+public class MvccUtils {
+ /** */
+ public static final long MVCC_CRD_COUNTER_NA = 0L;
+ /** */
+ public static final long MVCC_CRD_START_CNTR = 1L;
+ /** */
+ public static final long MVCC_COUNTER_NA = 0L;
+ /** */
+ public static final long MVCC_INITIAL_CNTR = 1L;
+ /** */
+ public static final long MVCC_START_CNTR = 3L;
+ /** */
+ public static final int MVCC_OP_COUNTER_NA = 0;
+ /** */
+ public static final int MVCC_START_OP_CNTR = 1;
+ /** */
+ public static final int MVCC_READ_OP_CNTR = ~MVCC_HINTS_MASK;
+
+ /** */
+ public static final int MVCC_INVISIBLE = 0;
+ /** */
+ public static final int MVCC_VISIBLE_REMOVED = 1;
+ /** */
+ public static final int MVCC_VISIBLE = 2;
+
+ /** */
+ public static final MvccVersion INITIAL_VERSION =
+ mvccVersion(MVCC_CRD_START_CNTR, MVCC_INITIAL_CNTR, MVCC_START_OP_CNTR);
+
+ /** */
+ public static final MvccVersion MVCC_VERSION_NA =
+ mvccVersion(MVCC_CRD_COUNTER_NA, MVCC_COUNTER_NA, MVCC_OP_COUNTER_NA);
+
+ /** */
+ private static final MvccClosure<Integer> getVisibleState = new GetVisibleState();
+
+ /** */
+ private static final MvccClosure<Boolean> isVisible = new IsVisible();
+
+ /** */
+ private static final MvccClosure<MvccVersion> getNewVer = new GetNewVersion();
+
+ /**
+ *
+ */
+ private MvccUtils(){
+ }
+
+ /**
+ * @param ctx Kernal context.
+ * @return Newly created Mvcc processor.
+ */
+ public static MvccProcessor createProcessor(GridKernalContext ctx) {
+ return mvccEnabled(ctx) ? new MvccProcessorImpl(ctx) : new NoOpMvccProcessor(ctx);
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param mvccCrd Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param snapshot Snapshot.
+ * @return {@code True} if transaction is active.
+ * @see TxState
+ * @throws IgniteCheckedException If failed.
+ */
+ public static boolean isActive(GridCacheContext cctx, long mvccCrd, long mvccCntr, MvccSnapshot snapshot)
+ throws IgniteCheckedException {
+ if (isVisible(cctx, snapshot, mvccCrd, mvccCntr, MVCC_OP_COUNTER_NA, false))
+ return false;
+
+ byte state = state(cctx, mvccCrd, mvccCntr, 0);
+
+ return state != TxState.COMMITTED && state != TxState.ABORTED
+ || cctx.kernalContext().coordinators().hasLocalTransaction(mvccCrd, mvccCntr);
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param mvccCrd Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param mvccOpCntr Mvcc operation counter.
+ * @return TxState
+ * @see TxState
+ * @throws IgniteCheckedException If failed.
+ */
+ public static byte state(GridCacheContext cctx, long mvccCrd, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+ return state(cctx.kernalContext().coordinators(), mvccCrd, mvccCntr, mvccOpCntr);
+ }
+
+ /**
+ * @param grp Cache group context.
+ * @param mvccCrd Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param mvccOpCntr Mvcc operation counter.
+ * @return TxState
+ * @see TxState
+ * @throws IgniteCheckedException If failed.
+ */
+ public static byte state(CacheGroupContext grp, long mvccCrd, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+ return state(grp.shared().coordinators(), mvccCrd, mvccCntr, mvccOpCntr);
+ }
+
+ /**
+ * @param proc Mvcc processor.
+ * @param mvccCrd Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @return TxState
+ * @see TxState
+ * @throws IgniteCheckedException If failed.
+ */
+ private static byte state(MvccProcessor proc, long mvccCrd, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+ if (compare(INITIAL_VERSION, mvccCrd, mvccCntr, mvccOpCntr) == 0)
+ return TxState.COMMITTED; // Initial version is always committed;
+
+ if ((mvccOpCntr & MVCC_HINTS_MASK) != 0)
+ return (byte)(mvccOpCntr >>> MVCC_HINTS_BIT_OFF);
+
+ return proc.state(mvccCrd, mvccCntr);
+ }
+
+ /**
+ * Checks if version is visible from the given snapshot.
+ *
+ * @param cctx Cache context.
+ * @param snapshot Snapshot.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ * @param opCntr Operation counter.
+ * @return {@code True} if visible.
+ * @throws IgniteCheckedException If failed.
+ */
+ public static boolean isVisible(GridCacheContext cctx, MvccSnapshot snapshot, long mvccCrd, long mvccCntr,
+ int opCntr) throws IgniteCheckedException {
+ return isVisible(cctx, snapshot, mvccCrd, mvccCntr, opCntr, true);
+ }
+
+ /**
+ * Checks if version is visible from the given snapshot.
+ *
+ * @param cctx Cache context.
+ * @param snapshot Snapshot.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ * @param opCntr Operation counter.
+ * @param useTxLog {@code True} if TxLog should be used.
+ * @return {@code True} if visible.
+ * @throws IgniteCheckedException If failed.
+ */
+ public static boolean isVisible(GridCacheContext cctx, MvccSnapshot snapshot, long mvccCrd, long mvccCntr,
+ int opCntr, boolean useTxLog) throws IgniteCheckedException {
+ if (mvccCrd == MVCC_CRD_COUNTER_NA) {
+ assert mvccCntr == MVCC_COUNTER_NA && opCntr == MVCC_OP_COUNTER_NA
+ : "rowVer=" + mvccVersion(mvccCrd, mvccCntr, opCntr) + ", snapshot=" + snapshot;
+
+ return false; // Unassigned version is always invisible
+ }
+
+ if (compare(INITIAL_VERSION, mvccCrd, mvccCntr, opCntr) == 0)
+ return true; // Initial version is always visible
+
+ long snapshotCrd = snapshot.coordinatorVersion();
+
+ long snapshotCntr = snapshot.counter();
+ int snapshotOpCntr = snapshot.operationCounter();
+
+ if (mvccCrd > snapshotCrd)
+ return false; // Rows in the future are never visible.
+
+ if (mvccCrd < snapshotCrd)
+ // Don't check the row with TxLog if the row is expected to be committed.
+ return !useTxLog || isCommitted(cctx, mvccCrd, mvccCntr, opCntr);
+
+ if (mvccCntr > snapshotCntr) // we don't see future updates
+ return false;
+
+ if (mvccCntr == snapshotCntr) {
+ assert opCntr <= snapshotOpCntr : "rowVer=" + mvccVersion(mvccCrd, mvccCntr, opCntr) + ", snapshot=" + snapshot;
+
+ return opCntr < snapshotOpCntr; // we don't see own pending updates
+ }
+
+ if (snapshot.activeTransactions().contains(mvccCntr)) // we don't see of other transactions' pending updates
+ return false;
+
+ if (!useTxLog)
+ return true; // The checking row is expected to be committed.
+
+ byte state = state(cctx, mvccCrd, mvccCntr, opCntr);
+
+ if (state != TxState.COMMITTED && state != TxState.ABORTED)
+ throw unexpectedStateException(cctx, state, mvccCrd, mvccCntr, opCntr, snapshot);
+
+ return state == TxState.COMMITTED;
+ }
+
+ /**
+ *
+ * @param grp Cache group context.
+ * @param state State.
+ * @param crd Mvcc coordinator counter.
+ * @param cntr Mvcc counter.
+ * @param opCntr Mvcc operation counter.
+ * @return State exception.
+ */
+ public static IgniteTxMvccVersionCheckedException unexpectedStateException(
+ CacheGroupContext grp, byte state, long crd, long cntr,
+ int opCntr) {
+ return unexpectedStateException(grp.shared().kernalContext(), state, crd, cntr, opCntr, null);
+ }
+
+ /**
+ *
+ * @param cctx Cache context.
+ * @param state State.
+ * @param crd Mvcc coordinator counter.
+ * @param cntr Mvcc counter.
+ * @param opCntr Mvcc operation counter.
+ * @param snapshot Mvcc snapshot
+ * @return State exception.
+ */
+ public static IgniteTxMvccVersionCheckedException unexpectedStateException(
+ GridCacheContext cctx, byte state, long crd, long cntr,
+ int opCntr, MvccSnapshot snapshot) {
+ return unexpectedStateException(cctx.kernalContext(), state, crd, cntr, opCntr, snapshot);
+ }
+
+ /** */
+ private static IgniteTxMvccVersionCheckedException unexpectedStateException(GridKernalContext ctx, byte state, long crd, long cntr,
+ int opCntr, MvccSnapshot snapshot) {
+ String msg = "Unexpected state: [state=" + state + ", rowVer=" + crd + ":" + cntr + ":" + opCntr;
+
+ if (snapshot != null)
+ msg += ", txVer=" + snapshot.coordinatorVersion() + ":" + snapshot.counter() + ":" + snapshot.operationCounter();
+
+ msg += ", localNodeId=" + ctx.localNodeId() + "]";
+
+ return new IgniteTxMvccVersionCheckedException(msg);
+ }
+
+ /**
+ * Checks visibility of the given row versions from the given snapshot.
+ *
+ * @param cctx Context.
+ * @param snapshot Snapshot.
+ * @param crd Mvcc coordinator counter.
+ * @param cntr Mvcc counter.
+ * @param opCntr Mvcc operation counter.
+ * @param link Link to data row (new version is located there).
+ * @return Visibility status.
+ * @throws IgniteCheckedException If failed.
+ */
+ public static boolean isVisible(GridCacheContext cctx, MvccSnapshot snapshot, long crd, long cntr,
+ int opCntr, long link) throws IgniteCheckedException {
+ return isVisible(cctx, snapshot, crd, cntr, opCntr, false)
+ && isVisible(cctx, link, snapshot);
+ }
+
+ /**
+ * Checks if a row has not empty new version (xid_max).
+ *
+ * @param row Row.
+ * @return {@code True} if row has a new version.
+ */
+ public static boolean hasNewVersion(MvccUpdateVersionAware row) {
+ assert row.newMvccCoordinatorVersion() == MVCC_CRD_COUNTER_NA
+ || mvccVersionIsValid(row.newMvccCoordinatorVersion(), row.newMvccCounter(), row.newMvccOperationCounter());
+
+ return row.newMvccCoordinatorVersion() > MVCC_CRD_COUNTER_NA;
+ }
+
+ /**
+ * Checks if a row's new version is visible for the given snapshot.
+ *
+ * @param cctx Cache context.
+ * @param link Link to the row.
+ * @param snapshot Mvcc snapshot.
+ * @return {@code True} if row is visible for the given snapshot.
+ * @throws IgniteCheckedException If failed.
+ */
+ public static int getVisibleState(GridCacheContext cctx, long link, MvccSnapshot snapshot)
+ throws IgniteCheckedException {
+ return invoke(cctx, link, getVisibleState, snapshot);
+ }
+
+ /**
+ * Returns new version of row (xid_max) if any.
+ *
+ * @param cctx Cache context.
+ * @param link Link to the row.
+ * @return New {@code MvccVersion} if row has xid_max, or null if doesn't.
+ * @throws IgniteCheckedException If failed.
+ */
+ public static MvccVersion getNewVersion(GridCacheContext cctx, long link)
+ throws IgniteCheckedException {
+ return invoke(cctx, link, getNewVer, null);
+ }
+
+ /**
+ * Compares row version (xid_min) with the given version.
+ *
+ * @param row Row.
+ * @param ver Version.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(MvccVersionAware row, MvccVersion ver) {
+ return compare(row.mvccCoordinatorVersion(), row.mvccCounter(), row.mvccOperationCounter(),
+ ver.coordinatorVersion(), ver.counter(), ver.operationCounter());
+ }
+
+ /**
+ * Compares to pairs of MVCC versions. See {@link Comparable}.
+ *
+ * @param mvccVerLeft First MVCC version.
+ * @param mvccCrdRight Second coordinator version.
+ * @param mvccCntrRight Second counter.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(MvccVersion mvccVerLeft, long mvccCrdRight, long mvccCntrRight) {
+ return compare(mvccVerLeft.coordinatorVersion(), mvccVerLeft.counter(), mvccCrdRight, mvccCntrRight);
+ }
+
+ /**
+ * Compares to pairs of MVCC versions. See {@link Comparable}.
+ *
+ * @param row First MVCC version.
+ * @param mvccCrdRight Second coordinator version.
+ * @param mvccCntrRight Second counter.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(MvccVersionAware row, long mvccCrdRight, long mvccCntrRight) {
+ return compare(row.mvccCoordinatorVersion(), row.mvccCounter(), mvccCrdRight, mvccCntrRight);
+ }
+
+ /**
+ * Compares to pairs of MVCC versions. See {@link Comparable}.
+ *
+ * @param mvccVerLeft First MVCC version.
+ * @param mvccCrdRight Second coordinator version.
+ * @param mvccCntrRight Second counter.
+ * @param mvccOpCntrRight Second operation counter.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(MvccVersion mvccVerLeft, long mvccCrdRight, long mvccCntrRight, int mvccOpCntrRight) {
+ return compare(mvccVerLeft.coordinatorVersion(), mvccVerLeft.counter(),
+ mvccVerLeft.operationCounter(), mvccCrdRight, mvccCntrRight, mvccOpCntrRight);
+ }
+
+ /**
+ * Compares to pairs of coordinator/counter versions. See {@link Comparable}.
+ *
+ * @param mvccCrdLeft First coordinator version.
+ * @param mvccCntrLeft First counter version.
+ * @param mvccOpCntrLeft First operation counter.
+ * @param other The object to compare with.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(long mvccCrdLeft, long mvccCntrLeft, int mvccOpCntrLeft, MvccVersionAware other) {
+ return compare(mvccCrdLeft, mvccCntrLeft, mvccOpCntrLeft,
+ other.mvccCoordinatorVersion(), other.mvccCounter(), other.mvccOperationCounter());
+ }
+
+ /**
+ * Compares to pairs of coordinator/counter versions. See {@link Comparable}.
+ *
+ * @param mvccCrdLeft First coordinator version.
+ * @param mvccCntrLeft First counter version.
+ * @param mvccCrdRight Second coordinator version.
+ * @param mvccCntrRight Second counter version.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(long mvccCrdLeft, long mvccCntrLeft, long mvccCrdRight, long mvccCntrRight) {
+ return compare(mvccCrdLeft, mvccCntrLeft, 0, mvccCrdRight, mvccCntrRight, 0);
+ }
+
+ /**
+ * Compares to pairs of coordinator/counter versions. See {@link Comparable}.
+ *
+ * @param mvccCrdLeft First coordinator version.
+ * @param mvccCntrLeft First counter version.
+ * @param mvccOpCntrLeft First operation counter.
+ * @param mvccCrdRight Second coordinator version.
+ * @param mvccCntrRight Second counter version.
+ * @param mvccOpCntrRight Second operation counter.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compare(long mvccCrdLeft, long mvccCntrLeft, int mvccOpCntrLeft, long mvccCrdRight,
+ long mvccCntrRight, int mvccOpCntrRight) {
+ int cmp;
+
+ if ((cmp = Long.compare(mvccCrdLeft, mvccCrdRight)) != 0
+ || (cmp = Long.compare(mvccCntrLeft, mvccCntrRight)) != 0
+ || (cmp = Integer.compare(mvccOpCntrLeft & ~MVCC_HINTS_MASK, mvccOpCntrRight & ~MVCC_HINTS_MASK)) != 0)
+ return cmp;
+
+ return 0;
+ }
+
+ /**
+ * Compares new row version (xid_max) with the given counter and coordinator versions.
+ *
+ * @param row Row.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compareNewVersion(MvccUpdateVersionAware row, long mvccCrd, long mvccCntr) {
+ return compare(row.newMvccCoordinatorVersion(), row.newMvccCounter(), mvccCrd, mvccCntr);
+ }
+
+ /**
+ * Compares new row version (xid_max) with the given counter and coordinator versions.
+ *
+ * @param row Row.
+ * @param mvccCrd Mvcc coordinator.
+ * @param mvccCntr Mvcc counter.
+ * @param opCntr Mvcc operation counter.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compareNewVersion(MvccUpdateVersionAware row, long mvccCrd, long mvccCntr, int opCntr) {
+ return compare(row.newMvccCoordinatorVersion(), row.newMvccCounter(), row.newMvccOperationCounter(), mvccCrd, mvccCntr, opCntr);
+ }
+
+ /**
+ * Compares new row version (xid_max) with the given version.
+ *
+ * @param row Row.
+ * @param ver Version.
+ * @return Comparison result, see {@link Comparable}.
+ */
+ public static int compareNewVersion(MvccUpdateVersionAware row, MvccVersion ver) {
+ return compare(row.newMvccCoordinatorVersion(), row.newMvccCounter(), row.newMvccOperationCounter(),
+ ver.coordinatorVersion(), ver.counter(), ver.operationCounter());
+ }
+
+ /**
+ * @param crdVer Mvcc coordinator version.
+ * @param cntr Counter.
+ * @param opCntr Operation counter.
+ * @return Always {@code true}.
+ */
+ public static boolean mvccVersionIsValid(long crdVer, long cntr, int opCntr) {
+ return mvccVersionIsValid(crdVer, cntr) && opCntr != MVCC_OP_COUNTER_NA;
+ }
+
+ /**
+ * @param crdVer Mvcc coordinator version.
+ * @param cntr Counter.
+ * @return {@code True} if version is valid.
+ */
+ public static boolean mvccVersionIsValid(long crdVer, long cntr) {
+ return crdVer > MVCC_CRD_COUNTER_NA && cntr != MVCC_COUNTER_NA;
+ }
+
+ /**
+ * @param topVer Topology version for cache operation.
+ * @return Error.
+ */
+ public static ClusterTopologyServerNotFoundException noCoordinatorError(AffinityTopologyVersion topVer) {
+ return new ClusterTopologyServerNotFoundException("Mvcc coordinator is not assigned for " +
+ "topology version: " + topVer);
+ }
+
+ /**
+ * @return Error.
+ */
+ public static ClusterTopologyServerNotFoundException noCoordinatorError() {
+ return new ClusterTopologyServerNotFoundException("Mvcc coordinator is not assigned.");
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param link Link to the row.
+ * @param snapshot Mvcc snapshot.
+ * @return {@code True} if row is updated for given snapshot.
+ * @throws IgniteCheckedException If failed.
+ */
+ private static boolean isVisible(GridCacheContext cctx, long link,
+ MvccSnapshot snapshot)
+ throws IgniteCheckedException {
+ return invoke(cctx, link, isVisible, snapshot);
+ }
+
+ /**
+ * Encapsulates common logic for working with row mvcc info: page locking/unlocking, checks and other.
+ * Strategy pattern.
+ *
+ * @param cctx Cache group.
+ * @param link Row link.
+ * @param clo Closure to apply.
+ * @param snapshot Mvcc snapshot.
+ * @param <R> Return type.
+ * @return Result.
+ * @throws IgniteCheckedException If failed.
+ */
+ private static <R> R invoke(GridCacheContext cctx, long link, MvccClosure<R> clo, MvccSnapshot snapshot)
+ throws IgniteCheckedException {
+ assert cctx.mvccEnabled();
+
+ PageMemory pageMem = cctx.dataRegion().pageMemory();
+ int grpId = cctx.groupId();
+
+ long pageId = pageId(link);
+ long page = pageMem.acquirePage(grpId, pageId);
+
+ try {
+ long pageAddr = pageMem.readLock(grpId, pageId, page);
+
+ try{
+ DataPageIO dataIo = DataPageIO.VERSIONS.forPage(pageAddr);
+
+ int offset = dataIo.getPayloadOffset(pageAddr, itemId(link), pageMem.pageSize(), MVCC_INFO_SIZE);
+
+ long mvccCrd = dataIo.mvccCoordinator(pageAddr, offset);
+ long mvccCntr = dataIo.mvccCounter(pageAddr, offset);
+ int mvccOpCntr = dataIo.mvccOperationCounter(pageAddr, offset);
+
+ assert mvccVersionIsValid(mvccCrd, mvccCntr, mvccOpCntr) : mvccVersion(mvccCrd, mvccCntr, mvccOpCntr);
+
+ long newMvccCrd = dataIo.newMvccCoordinator(pageAddr, offset);
+ long newMvccCntr = dataIo.newMvccCounter(pageAddr, offset);
+ int newMvccOpCntr = dataIo.newMvccOperationCounter(pageAddr, offset);
+
+ assert newMvccCrd == MVCC_CRD_COUNTER_NA || mvccVersionIsValid(newMvccCrd, newMvccCntr, newMvccOpCntr)
+ : mvccVersion(newMvccCrd, newMvccCntr, newMvccOpCntr);
+
+ return clo.apply(cctx, snapshot, mvccCrd, mvccCntr, mvccOpCntr, newMvccCrd, newMvccCntr, newMvccOpCntr);
+ }
+ finally {
+ pageMem.readUnlock(grpId, pageId, page);
+ }
+ }
+ finally {
+ pageMem.releasePage(grpId, pageId, page);
+ }
+ }
+
+ /**
+ *
+ * @param cctx Cache context.
+ * @param mvccCrd Coordinator version.
+ * @param mvccCntr Counter.
+ * @return {@code True} in case the corresponding transaction is in {@code TxState.COMMITTED} state.
+ * @throws IgniteCheckedException If failed.
+ */
+ private static boolean isCommitted(GridCacheContext cctx, long mvccCrd, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+ return state(cctx, mvccCrd, mvccCntr, mvccOpCntr) == TxState.COMMITTED;
+ }
+
+ /**
+ * Throw an {@link UnsupportedOperationException} if this cache is transactional and MVCC is enabled with
+ * appropriate message about corresponding operation type.
+ * @param cctx Cache context.
+ * @param opType operation type to mention in error message.
+ */
+ public static void verifyMvccOperationSupport(GridCacheContext<?, ?> cctx, String opType) {
+ if (cctx.mvccEnabled())
+ throw new UnsupportedOperationException(opType + " operations are not supported on transactional " +
+ "caches when MVCC is enabled.");
+ }
+
+ /**
+ * Checks transaction state.
+ * @param tx Transaction.
+ * @return Checked transaction.
+ */
+ public static GridNearTxLocal checkActive(GridNearTxLocal tx) {
+ if (tx != null && tx.state() != TransactionState.ACTIVE)
+ throw new IgniteSQLException("Transaction is already completed.", TRANSACTION_COMPLETED);
+
+ return tx;
+ }
+
+
+ /**
+ * @param ctx Grid kernal context.
+ * @return Currently started user transaction, or {@code null} if none started.
+ */
+ @Nullable public static GridNearTxLocal tx(GridKernalContext ctx) {
+ return tx(ctx, null);
+ }
+
+ /**
+ * @param ctx Grid kernal context.
+ * @param txId Transaction ID.
+ * @return Currently started user transaction, or {@code null} if none started.
+ */
+ @Nullable public static GridNearTxLocal tx(GridKernalContext ctx, @Nullable GridCacheVersion txId) {
+ IgniteTxManager tm = ctx.cache().context().tm();
+
+ IgniteInternalTx tx0 = txId == null ? tm.tx() : tm.tx(txId);
+
+ GridNearTxLocal tx = tx0 != null && tx0.user() ? (GridNearTxLocal)tx0 : null;
+
+ if (tx != null) {
+ if (!tx.pessimistic() || !tx.repeatableRead()) {
+ tx.setRollbackOnly();
+
+ throw new IgniteSQLException("Only pessimistic repeatable read transactions are supported at the moment.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ }
+
+ if (!tx.isOperationAllowed(true)) {
+ tx.setRollbackOnly();
+
+ throw new IgniteSQLException("SQL queries and cache operations " +
+ "may not be used in the same transaction.", IgniteQueryErrorCode.TRANSACTION_TYPE_MISMATCH);
+ }
+ }
+
+ return tx;
+ }
+
+
+ /**
+ * @param ctx Grid kernal context.
+ * @param timeout Transaction timeout.
+ * @return Newly started SQL transaction.
+ */
+ public static GridNearTxLocal txStart(GridKernalContext ctx, long timeout) {
+ return txStart(ctx, null, timeout);
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param timeout Transaction timeout.
+ * @return Newly started SQL transaction.
+ */
+ public static GridNearTxLocal txStart(GridCacheContext cctx, long timeout) {
+ return txStart(cctx.kernalContext(), cctx, timeout);
+ }
+
+ /**
+ * @param ctx Grid kernal context.
+ * @param cctx Cache context.
+ * @param timeout Transaction timeout.
+ * @return Newly started SQL transaction.
+ */
+ private static GridNearTxLocal txStart(GridKernalContext ctx, @Nullable GridCacheContext cctx, long timeout) {
+ if (timeout == 0) {
+ TransactionConfiguration tcfg = cctx != null ?
+ CU.transactionConfiguration(cctx, ctx.config()) : null;
+
+ if (tcfg != null)
+ timeout = tcfg.getDefaultTxTimeout();
+ }
+
+ GridNearTxLocal tx = ctx.cache().context().tm().newTx(
+ false,
+ false,
+ cctx != null && cctx.systemTx() ? cctx : null,
+ PESSIMISTIC,
+ REPEATABLE_READ,
+ timeout,
+ cctx == null || !cctx.skipStore(),
+ true,
+ 0,
+ null
+ );
+
+ tx.syncMode(FULL_SYNC);
+
+ return tx;
+ }
+
+ /**
+ * @param ctx Grid kernal context.
+ * @return Whether MVCC is enabled or not on {@link IgniteConfiguration}.
+ */
+ public static boolean mvccEnabled(GridKernalContext ctx) {
+ return ctx.config().isMvccEnabled();
+ }
+
+ /**
+ * Initialises MVCC filter and returns MVCC query tracker if needed.
+ * @param cctx Cache context.
+ * @param startTx Start transaction flag.
+ * @return MVCC query tracker.
+ * @throws IgniteCheckedException If failed.
+ */
+ @NotNull public static MvccQueryTracker mvccTracker(GridCacheContext cctx, boolean startTx) throws IgniteCheckedException {
+ assert cctx != null && cctx.mvccEnabled();
+
+ GridNearTxLocal tx = tx(cctx.kernalContext());
+
+ if (tx == null && startTx)
+ tx = txStart(cctx, 0);
+
+ return mvccTracker(cctx, tx);
+ }
+
+ /**
+ * Initialises MVCC filter and returns MVCC query tracker if needed.
+ * @param cctx Cache context.
+ * @param tx Transaction.
+ * @return MVCC query tracker.
+ * @throws IgniteCheckedException If failed.
+ */
+ @NotNull public static MvccQueryTracker mvccTracker(GridCacheContext cctx,
+ GridNearTxLocal tx) throws IgniteCheckedException {
+ MvccQueryTracker tracker;
+
+ if (tx == null)
+ tracker = new MvccQueryTrackerImpl(cctx);
+ else if ((tracker = tx.mvccQueryTracker()) == null)
+ tracker = new StaticMvccQueryTracker(cctx, requestSnapshot(cctx, tx)) {
+ @Override public void onDone() {
+ // TODO IGNITE-8841
+ checkActive(tx);
+ }
+ };
+
+ if (tracker.snapshot() == null)
+ // TODO IGNITE-7388
+ tracker.requestSnapshot().get();
+
+ return tracker;
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param tx Transaction.
+ * @throws IgniteCheckedException If failed.
+ * @return Mvcc snapshot.
+ */
+ public static MvccSnapshot requestSnapshot(GridCacheContext cctx,
+ GridNearTxLocal tx) throws IgniteCheckedException {
+ MvccSnapshot snapshot; tx = checkActive(tx);
+
+ if ((snapshot = tx.mvccSnapshot()) == null) {
+ MvccProcessor prc = cctx.shared().coordinators();
+
+ snapshot = prc.tryRequestSnapshotLocal(tx);
+
+ if (snapshot == null)
+ // TODO IGNITE-7388
+ snapshot = prc.requestSnapshotAsync(tx).get();
+
+ tx.mvccSnapshot(snapshot);
+ }
+
+ return snapshot;
+ }
+
+ /** */
+ private static MvccVersion mvccVersion(long crd, long cntr, int opCntr) {
+ return new MvccVersionImpl(crd, cntr, opCntr);
+ }
+
+ /**
+ * Mvcc closure interface.
+ * @param <R> Return type.
+ */
+ private interface MvccClosure<R> {
+ /**
+ * Runs closure over the Mvcc info.
+ * @param snapshot Mvcc snapshot.
+ * @param mvccCrd Coordinator version.
+ * @param mvccCntr Counter.
+ * @param mvccOpCntr Operation counter.
+ * @param newMvccCrd New mvcc coordinator
+ * @param newMvccCntr New mvcc counter.
+ * @param newMvccOpCntr New mvcc operation counter.
+ * @return Result.
+ */
+ public R apply(GridCacheContext cctx, MvccSnapshot snapshot, long mvccCrd, long mvccCntr, int mvccOpCntr,
+ long newMvccCrd, long newMvccCntr, int newMvccOpCntr) throws IgniteCheckedException;
+ }
+
+ /**
+ * Closure for checking row visibility for snapshot.
+ */
+ private static class GetVisibleState implements MvccClosure<Integer> {
+ /** {@inheritDoc} */
+ @Override public Integer apply(GridCacheContext cctx, MvccSnapshot snapshot, long mvccCrd, long mvccCntr,
+ int mvccOpCntr, long newMvccCrd, long newMvccCntr, int newMvccOpCntr) throws IgniteCheckedException {
+
+ if (!isVisible(cctx, snapshot, mvccCrd, mvccCntr, mvccOpCntr))
+ return MVCC_INVISIBLE;
+
+ if (newMvccCrd == MVCC_CRD_COUNTER_NA)
+ return MVCC_VISIBLE;
+
+ assert mvccVersionIsValid(newMvccCrd, newMvccCntr, newMvccOpCntr);
+
+ if (mvccCrd == newMvccCrd && mvccCntr == newMvccCntr) // Double-changed in scope of one transaction.
+ return MVCC_VISIBLE_REMOVED;
+
+ return isVisible(cctx, snapshot, newMvccCrd, newMvccCntr, newMvccOpCntr) ? MVCC_VISIBLE_REMOVED :
+ MVCC_VISIBLE;
+ }
+ }
+
+ /**
+ * Closure for checking whether the row is visible for given snapshot.
+ */
+ private static class IsVisible implements MvccClosure<Boolean> {
+ /** {@inheritDoc} */
+ @Override public Boolean apply(GridCacheContext cctx, MvccSnapshot snapshot, long mvccCrd, long mvccCntr,
+ int mvccOpCntr, long newMvccCrd, long newMvccCntr, int newMvccOpCntr) throws IgniteCheckedException {
+
+ if (!isVisible(cctx, snapshot, mvccCrd, mvccCntr, mvccOpCntr))
+ return false;
+
+ if (newMvccCrd == MVCC_CRD_COUNTER_NA)
+ return true;
+
+ assert mvccVersionIsValid(newMvccCrd, newMvccCntr, newMvccOpCntr);
+
+ if (mvccCrd == newMvccCrd && mvccCntr == newMvccCntr) // Double-changed in scope of one transaction.
+ return false;
+
+ return !isVisible(cctx, snapshot, newMvccCrd, newMvccCntr, newMvccOpCntr);
+ }
+ }
+
+ /**
+ * Closure for getting xid_max version of row.
+ */
+ private static class GetNewVersion implements MvccClosure<MvccVersion> {
+ /** {@inheritDoc} */
+ @Override public MvccVersion apply(GridCacheContext cctx, MvccSnapshot snapshot, long mvccCrd, long mvccCntr,
+ int mvccOpCntr, long newMvccCrd, long newMvccCntr, int newMvccOpCntr) {
+ return newMvccCrd == MVCC_CRD_COUNTER_NA ? null : mvccVersion(newMvccCrd, newMvccCntr, newMvccOpCntr);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersion.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersion.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersion.java
new file mode 100644
index 0000000..f43d3b9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersion.java
@@ -0,0 +1,48 @@
+/*
+ * 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.mvcc;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * MVCC version. This is unique version allowing to order all reads and writes within a cluster. Consists of two parts:
+ * - coordinator version - number which increases on every coordinator change;
+ * - counter - local coordinator counter which is increased on every update.
+ */
+public interface MvccVersion extends Comparable<MvccVersion> {
+ /**
+ * @return Coordinator version.
+ */
+ public long coordinatorVersion();
+
+ /**
+ * @return Local counter.
+ */
+ public long counter();
+
+ /**
+ * @return Operation id in scope of current transaction.
+ */
+ public int operationCounter();
+
+ /** {@inheritDoc} */
+ @Override default int compareTo(@NotNull MvccVersion another) {
+ return MvccUtils.compare(coordinatorVersion(), counter(), operationCounter(),
+ another.coordinatorVersion(), another.counter(), another.operationCounter());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionAware.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionAware.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionAware.java
new file mode 100644
index 0000000..3bfefbc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionAware.java
@@ -0,0 +1,76 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public interface MvccVersionAware {
+ /**
+ * @return Mvcc coordinator version.
+ */
+ public long mvccCoordinatorVersion();
+
+ /**
+ * @return Mvcc counter.
+ */
+ public long mvccCounter();
+
+ /**
+ * @return Mvcc operation counter.
+ */
+ public int mvccOperationCounter();
+
+ /**
+ * @return Tx state hint for 'created' mvcc version.
+ */
+ public byte mvccTxState();
+
+ /**
+ * Copies mvcc version from another object.
+ * @param other Info source.
+ */
+ public default void mvccVersion(MvccVersionAware other) {
+ mvccVersion(other.mvccCoordinatorVersion(), other.mvccCounter(), other.mvccOperationCounter());
+ }
+
+ /**
+ * Sets mvcc version.
+ * @param ver Mvcc version.
+ */
+ public default void mvccVersion(MvccVersion ver) {
+ mvccVersion(ver.coordinatorVersion(), ver.counter(), ver.operationCounter());
+ }
+
+ /**
+ * Sets mvcc version.
+ * @param crd Mvcc coordinator version.
+ * @param cntr Mvcc counter.
+ * @param opCntr Mvcc operation counter.
+ */
+ public default void mvccVersion(long crd, long cntr, int opCntr) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @return Mvcc version.
+ */
+ public default MvccVersion mvccVersion() {
+ return new MvccVersionImpl(mvccCoordinatorVersion(), mvccCounter(), mvccOperationCounter());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionImpl.java
new file mode 100644
index 0000000..ec3e137
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccVersionImpl.java
@@ -0,0 +1,193 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base MVCC version implementation.
+ */
+public class MvccVersionImpl implements MvccVersion, Message {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Coordinator version. */
+ private long crdVer;
+
+ /** Local counter. */
+ private long cntr;
+
+ /** Operation counter. */
+ private int opCntr;
+
+ /**
+ * Constructor.
+ */
+ public MvccVersionImpl() {
+ // No-op.
+ }
+
+ /**
+ * @param crdVer Coordinator version.
+ * @param cntr Counter.
+ * @param opCntr Operation counter.
+ */
+ public MvccVersionImpl(long crdVer, long cntr, int opCntr) {
+ this.crdVer = crdVer;
+ this.cntr = cntr;
+ this.opCntr = opCntr;
+ }
+
+ /**
+ * @return Coordinator version.
+ */
+ public long coordinatorVersion() {
+ return crdVer;
+ }
+
+ /**
+ * @return Local counter.
+ */
+ public long counter() {
+ return cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int operationCounter() {
+ return opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ MvccVersionImpl that = (MvccVersionImpl) o;
+
+ return crdVer == that.crdVer && cntr == that.cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int res = (int) (crdVer ^ (crdVer >>> 32));
+
+ res = 31 * res + (int) (cntr ^ (cntr >>> 32));
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("cntr", cntr))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeLong("crdVer", crdVer))
+ return false;
+
+ writer.incrementState();
+
+ case 2:
+ if (!writer.writeInt("opCntr", opCntr))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ cntr = reader.readLong("cntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ crdVer = reader.readLong("crdVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 2:
+ opCntr = reader.readInt("opCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccVersionImpl.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 148;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccVersionImpl.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/NoOpMvccProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/NoOpMvccProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/NoOpMvccProcessor.java
new file mode 100644
index 0000000..b9a5132
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/NoOpMvccProcessor.java
@@ -0,0 +1,215 @@
+/*
+ * 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.mvcc;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteDiagnosticPrepareContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.managers.discovery.DiscoCache;
+import org.apache.ignite.internal.processors.GridProcessorAdapter;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.ExchangeContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.util.GridLongList;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+class NoOpMvccProcessor extends GridProcessorAdapter implements MvccProcessor {
+ /**
+ * @param ctx Kernal context.
+ */
+ protected NoOpMvccProcessor(GridKernalContext ctx) {
+ super(ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onDiscoveryEvent(int evtType, Collection<ClusterNode> nodes, long topVer) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onExchangeStart(MvccCoordinator mvccCrd, ExchangeContext exchCtx, ClusterNode exchCrd) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onExchangeDone(boolean newCoord, DiscoCache discoCache,
+ Map<UUID, GridLongList> activeQueries) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void processClientActiveQueries(UUID nodeId, @Nullable GridLongList activeQueries) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public MvccCoordinator currentCoordinator() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public MvccCoordinator currentCoordinator(AffinityTopologyVersion topVer) {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public MvccCoordinator coordinatorFromDiscoveryEvent() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public UUID currentCoordinatorId() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateCoordinator(MvccCoordinator curCrd) {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte state(long crdVer, long cntr) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte state(MvccVersion ver) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateState(MvccVersion ver, byte state) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateState(MvccVersion ver, byte state, boolean primary) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void registerLocalTransaction(long crd, long cntr) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasLocalTransaction(long crd, long cntr) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> waitFor(GridCacheContext cctx,
+ MvccVersion locked) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addQueryTracker(MvccQueryTracker tracker) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeQueryTracker(Long id) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot tryRequestSnapshotLocal() {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot tryRequestSnapshotLocal(
+ @Nullable IgniteInternalTx tx) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshotAsync() {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshotAsync(IgniteInternalTx tx) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void requestSnapshotAsync(MvccSnapshotResponseListener lsnr) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void requestSnapshotAsync(IgniteInternalTx tx, MvccSnapshotResponseListener lsnr) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> ackTxCommit(MvccSnapshot updateVer) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> ackTxCommit(MvccVersion updateVer, MvccSnapshot readSnapshot,
+ long qryId) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void ackTxRollback(MvccVersion updateVer) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void ackTxRollback(MvccVersion updateVer, MvccSnapshot readSnapshot, long qryTrackerId) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void ackQueryDone(MvccSnapshot snapshot, long qryId) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> waitTxsFuture(UUID crdId, GridLongList txs) {
+ throw processorException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void dumpDebugInfo(IgniteLogger log, @Nullable IgniteDiagnosticPrepareContext diagCtx) {
+ // No-op.
+ }
+
+ /**
+ * @return No-op processor usage exception;
+ */
+ private IgniteException processorException() {
+ return new IgniteException("Current Ignite configuration does not support MVCC functionality " +
+ "(consider adding ignite-schedule module to classpath).");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/StaticMvccQueryTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/StaticMvccQueryTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/StaticMvccQueryTracker.java
new file mode 100644
index 0000000..52fb1db
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/StaticMvccQueryTracker.java
@@ -0,0 +1,96 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.util.future.GridFinishedFuture;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Simple MVCC tracker used only as an Mvcc snapshot holder.
+ */
+public class StaticMvccQueryTracker implements MvccQueryTracker {
+ /** */
+ private final MvccSnapshot snapshot;
+ /** */
+ private final GridCacheContext cctx;
+
+ /**
+ * @param cctx Cache context.
+ * @param snapshot Mvcc snapshot.
+ */
+ public StaticMvccQueryTracker(GridCacheContext cctx, MvccSnapshot snapshot) {
+ this.snapshot = snapshot;
+ this.cctx = cctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot snapshot() {
+ assert snapshot != null : this;
+
+ return snapshot;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheContext context() {
+ return cctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public AffinityTopologyVersion topologyVersion() {
+ return AffinityTopologyVersion.NONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshot() {
+ return new GridFinishedFuture<>(snapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshot(@NotNull final AffinityTopologyVersion topVer) {
+ return new GridFinishedFuture<>(snapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void requestSnapshot(@NotNull AffinityTopologyVersion topVer, @NotNull MvccSnapshotResponseListener lsnr) {
+ lsnr.onResponse(snapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onDone() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> onDone(@NotNull GridNearTxLocal tx, boolean commit) {
+ throw new UnsupportedOperationException("Operation is not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public long onMvccCoordinatorChange(MvccCoordinator newCrd) {
+ return MVCC_TRACKER_ID_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long id() {
+ return MVCC_TRACKER_ID_NA;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetrics.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetrics.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetrics.java
new file mode 100644
index 0000000..1de297f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetrics.java
@@ -0,0 +1,102 @@
+/*
+ * 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.mvcc;
+
+/**
+ *
+ */
+public class VacuumMetrics {
+ /** */
+ private long cleanupRowsCnt;
+
+ /** */
+ private long scannedRowsCnt;
+
+ /** */
+ private long searchNanoTime;
+
+ /** */
+ private long cleanupNanoTime;
+
+ /**
+ * @return Cleanup rows count.
+ */
+ public long cleanupRowsCount() {
+ return cleanupRowsCnt;
+ }
+
+ /**
+ * @return Scanned rows count.
+ */
+ public long scannedRowsCount() {
+ return scannedRowsCnt;
+ }
+
+ /**
+ * @return Search nano time.
+ */
+ public long searchNanoTime() {
+ return searchNanoTime;
+ }
+
+ /**
+ * @return Cleanup nano time
+ */
+ public long cleanupNanoTime() {
+ return cleanupNanoTime;
+ }
+
+
+ /**
+ * @param delta Delta.
+ */
+ public void addCleanupRowsCnt(long delta) {
+ cleanupRowsCnt += delta;
+ }
+
+ /**
+ * @param delta Delta.
+ */
+ public void addScannedRowsCount(long delta) {
+ scannedRowsCnt += delta;
+ }
+
+ /**
+ * @param delta Delta.
+ */
+ public void addSearchNanoTime(long delta) {
+ searchNanoTime += delta;
+ }
+
+ /**
+ * @param delta Delta.
+ */
+ public void addCleanupNanoTime(long delta) {
+ cleanupNanoTime += delta;
+ }
+
+ /** */
+ @Override public String toString() {
+ return "VacuumMetrics[" +
+ "cleanupRowsCnt=" + cleanupRowsCnt +
+ ", scannedRowsCnt=" + scannedRowsCnt +
+ ", searchNanoTime=" + Math.round((float)searchNanoTime / 1_000_000) +
+ " ms, cleanupNanoTime=" + Math.round((float)cleanupNanoTime / 1_000_000) +
+ " ms]";
+ }
+}
[13/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java
new file mode 100644
index 0000000..d1d569e
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2InnerIO.java
@@ -0,0 +1,74 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInnerIO;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+
+/**
+ * Inner page for H2 row references.
+ */
+public abstract class AbstractH2InnerIO extends BPlusInnerIO<GridH2SearchRow> implements H2RowLinkIO {
+ /**
+ * @param type Page type.
+ * @param ver Page format version.
+ * @param itemSize Single item size on page.
+ */
+ AbstractH2InnerIO(int type, int ver, int itemSize) {
+ super(type, ver, true, itemSize);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
+ GridH2Row row0 = (GridH2Row)row;
+
+ H2IOUtils.storeRow(row0, pageAddr, off, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow, ?> tree, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ long link = getLink(pageAddr, idx);
+
+ if (storeMvccInfo()) {
+ long mvccCrdVer = getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = getMvccCounter(pageAddr, idx);
+ int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
+
+ return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+ }
+
+ return ((H2Tree)tree).createRowFromLink(link);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+ H2IOUtils.store(dstPageAddr, offset(dstIdx), srcIo, srcPageAddr, srcIdx, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getLink(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java
new file mode 100644
index 0000000..07f114f
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2LeafIO.java
@@ -0,0 +1,76 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+
+/**
+ * Leaf page for H2 row references.
+ */
+public abstract class AbstractH2LeafIO extends BPlusLeafIO<GridH2SearchRow> implements H2RowLinkIO {
+ /**
+ * @param type Page type.
+ * @param ver Page format version.
+ * @param itemSize Single item size on page.
+ */
+ AbstractH2LeafIO(int type, int ver, int itemSize) {
+ super(type, ver, itemSize);
+ }
+
+ /** {@inheritDoc} */
+ @Override public final void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
+ GridH2Row row0 = (GridH2Row)row;
+
+ H2IOUtils.storeRow(row0, pageAddr, off, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+ assert srcIo == this;
+
+ H2IOUtils.store(dstPageAddr, offset(dstIdx), srcIo, srcPageAddr, srcIdx, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public final GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow,?> tree, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ long link = getLink(pageAddr, idx);
+
+ if (storeMvccInfo()) {
+ long mvccCrdVer = getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = getMvccCounter(pageAddr, idx);
+ int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
+
+ return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+ }
+
+ return ((H2Tree)tree).createRowFromLink(link);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getLink(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
index 294492d..8dc8c96 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasInnerIO.java
@@ -17,120 +17,17 @@
package org.apache.ignite.internal.processors.query.h2.database.io;
-import java.util.List;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageUtils;
-import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInnerIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
-import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
-import org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.h2.result.SearchRow;
-
/**
* Inner page for H2 row references.
*/
-public class H2ExtrasInnerIO extends BPlusInnerIO<SearchRow> implements H2RowLinkIO {
- /** Payload size. */
- private final int payloadSize;
-
- /** */
- public static void register() {
- for (short payload = 1; payload <= PageIO.MAX_PAYLOAD_SIZE; payload++)
- PageIO.registerH2ExtraInner(getVersions((short)(PageIO.T_H2_EX_REF_INNER_START + payload - 1), payload));
- }
-
- /**
- * @param payload Payload size.
- * @return IOVersions for given payload.
- */
- @SuppressWarnings("unchecked")
- public static IOVersions<? extends BPlusInnerIO<SearchRow>> getVersions(int payload) {
- assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
-
- if (payload == 0)
- return H2InnerIO.VERSIONS;
- else
- return (IOVersions<BPlusInnerIO<SearchRow>>)PageIO.getInnerVersions((short)(payload - 1));
- }
-
- /**
- * @param type Type.
- * @param payload Payload size.
- * @return Instance of IO versions.
- */
- private static IOVersions<H2ExtrasInnerIO> getVersions(short type, short payload) {
- return new IOVersions<>(new H2ExtrasInnerIO(type, 1, payload));
- }
-
+public class H2ExtrasInnerIO extends AbstractH2ExtrasInnerIO implements H2RowLinkIO {
/**
* @param type Page type.
* @param ver Page format version.
* @param payloadSize Payload size.
*/
- private H2ExtrasInnerIO(short type, int ver, int payloadSize) {
- super(type, ver, true, 8 + payloadSize);
- this.payloadSize = payloadSize;
- }
-
- /** {@inheritDoc} */
- @SuppressWarnings("ForLoopReplaceableByForEach")
- @Override public void storeByOffset(long pageAddr, int off, SearchRow row) {
- GridH2Row row0 = (GridH2Row)row;
-
- assert row0.link() != 0 : row0;
-
- List<InlineIndexHelper> inlineIdxs = InlineIndexHelper.getCurrentInlineIndexes();
-
- assert inlineIdxs != null : "no inline index helpers";
-
-
- int fieldOff = 0;
-
- for (int i = 0; i < inlineIdxs.size(); i++) {
- InlineIndexHelper idx = inlineIdxs.get(i);
-
- int size = idx.put(pageAddr, off + fieldOff, row.getValue(idx.columnIndex()), payloadSize - fieldOff);
-
- if (size == 0)
- break;
-
- fieldOff += size;
- }
-
- PageUtils.putLong(pageAddr, off + payloadSize, row0.link());
- }
-
- /** {@inheritDoc} */
- @Override public SearchRow getLookupRow(BPlusTree<SearchRow, ?> tree, long pageAddr, int idx)
- throws IgniteCheckedException {
- long link = getLink(pageAddr, idx);
-
- assert link != 0;
-
- return ((H2Tree)tree).createRowFromLink(link);
- }
-
- /** {@inheritDoc} */
- @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<SearchRow> srcIo, long srcPageAddr, int srcIdx) {
- int srcOff = srcIo.offset(srcIdx);
-
- byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
- long link = PageUtils.getLong(srcPageAddr, srcOff + payloadSize);
-
- assert link != 0;
-
- int dstOff = offset(dstIdx);
-
- PageUtils.putBytes(dstPageAddr, dstOff, payload);
- PageUtils.putLong(dstPageAddr, dstOff + payloadSize, link);
- }
-
- /** {@inheritDoc} */
- @Override public long getLink(long pageAddr, int idx) {
- return PageUtils.getLong(pageAddr, offset(idx) + payloadSize);
+ H2ExtrasInnerIO(short type, int ver, int payloadSize) {
+ super(type, ver, 8, payloadSize);
}
}
+
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasLeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasLeafIO.java
index 4770295..085f98b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasLeafIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2ExtrasLeafIO.java
@@ -17,117 +17,16 @@
package org.apache.ignite.internal.processors.query.h2.database.io;
-import java.util.List;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageUtils;
-import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
-import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
-import org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.h2.result.SearchRow;
-
/**
* Leaf page for H2 row references.
*/
-public class H2ExtrasLeafIO extends BPlusLeafIO<SearchRow> implements H2RowLinkIO {
- /** Payload size. */
- private final int payloadSize;
-
- /** */
- public static void register() {
- for (short payload = 1; payload <= PageIO.MAX_PAYLOAD_SIZE; payload++)
- PageIO.registerH2ExtraLeaf(getVersions((short)(PageIO.T_H2_EX_REF_LEAF_START + payload - 1), payload));
- }
-
- /**
- * @param payload Payload size.
- * @return IOVersions for given payload.
- */
- @SuppressWarnings("unchecked")
- public static IOVersions<? extends BPlusLeafIO<SearchRow>> getVersions(int payload) {
- assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
-
- if (payload == 0)
- return H2LeafIO.VERSIONS;
- else
- return (IOVersions<BPlusLeafIO<SearchRow>>)PageIO.getLeafVersions((short)(payload - 1));
- }
-
- /**
- * @param type Type.
- * @param payload Payload size.
- * @return Versions.
- */
- private static IOVersions<H2ExtrasLeafIO> getVersions(short type, short payload) {
- return new IOVersions<>(new H2ExtrasLeafIO(type, 1, payload));
- }
-
+public class H2ExtrasLeafIO extends AbstractH2ExtrasLeafIO {
/**
* @param type Page type.
* @param ver Page format version.
* @param payloadSize Payload size.
*/
- private H2ExtrasLeafIO(short type, int ver, int payloadSize) {
- super(type, ver, 8 + payloadSize);
- this.payloadSize = payloadSize;
- }
-
- /** {@inheritDoc} */
- @SuppressWarnings("ForLoopReplaceableByForEach")
- @Override public void storeByOffset(long pageAddr, int off, SearchRow row) {
- GridH2Row row0 = (GridH2Row)row;
-
- assert row0.link() != 0;
-
- List<InlineIndexHelper> inlineIdxs = InlineIndexHelper.getCurrentInlineIndexes();
-
- assert inlineIdxs != null : "no inline index helpers";
-
- int fieldOff = 0;
-
- for (int i = 0; i < inlineIdxs.size(); i++) {
- InlineIndexHelper idx = inlineIdxs.get(i);
-
- int size = idx.put(pageAddr, off + fieldOff, row.getValue(idx.columnIndex()), payloadSize - fieldOff);
-
- if (size == 0)
- break;
-
- fieldOff += size;
- }
-
- PageUtils.putLong(pageAddr, off + payloadSize, row0.link());
- }
-
- /** {@inheritDoc} */
- @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<SearchRow> srcIo, long srcPageAddr, int srcIdx) {
- int srcOff = srcIo.offset(srcIdx);
-
- byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
- long link = PageUtils.getLong(srcPageAddr, srcOff + payloadSize);
-
- assert link != 0;
-
- int dstOff = offset(dstIdx);
-
- PageUtils.putBytes(dstPageAddr, dstOff, payload);
- PageUtils.putLong(dstPageAddr, dstOff + payloadSize, link);
- }
-
- /** {@inheritDoc} */
- @Override public SearchRow getLookupRow(BPlusTree<SearchRow, ?> tree, long pageAddr, int idx)
- throws IgniteCheckedException {
- long link = getLink(pageAddr, idx);
-
- return ((H2Tree)tree).createRowFromLink(link);
- }
-
- /** {@inheritDoc} */
- @Override public long getLink(long pageAddr, int idx) {
- return PageUtils.getLong(pageAddr, offset(idx) + payloadSize);
+ H2ExtrasLeafIO(short type, int ver, int payloadSize) {
+ super(type, ver, 8, payloadSize);
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java
new file mode 100644
index 0000000..b6bda36
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2IOUtils.java
@@ -0,0 +1,94 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVersionIsValid;
+
+/**
+ *
+ */
+class H2IOUtils {
+ /**
+ *
+ */
+ private H2IOUtils() {}
+
+ /**
+ * @param row Row.
+ * @param pageAddr Page address.
+ * @param off Offset.
+ * @param storeMvcc {@code True} to store mvcc data.
+ */
+ static void storeRow(GridH2Row row, long pageAddr, int off, boolean storeMvcc) {
+ assert row.link() != 0;
+
+ PageUtils.putLong(pageAddr, off, row.link());
+
+ if (storeMvcc) {
+ long mvccCrdVer = row.mvccCoordinatorVersion();
+ long mvccCntr = row.mvccCounter();
+ int mvccOpCntr = row.mvccOperationCounter();
+
+ assert MvccUtils.mvccVersionIsValid(mvccCrdVer, mvccCntr, mvccOpCntr);
+
+ PageUtils.putLong(pageAddr, off + 8, mvccCrdVer);
+ PageUtils.putLong(pageAddr, off + 16, mvccCntr);
+ PageUtils.putInt(pageAddr, off + 24, mvccOpCntr);
+ }
+ }
+
+ /**
+ * @param dstPageAddr Destination page address.
+ * @param dstOff Destination page offset.
+ * @param srcIo Source IO.
+ * @param srcPageAddr Source page address.
+ * @param srcIdx Source index.
+ * @param storeMvcc {@code True} to store mvcc data.
+ */
+ static void store(long dstPageAddr,
+ int dstOff,
+ BPlusIO<GridH2SearchRow> srcIo,
+ long srcPageAddr,
+ int srcIdx,
+ boolean storeMvcc)
+ {
+ H2RowLinkIO rowIo = (H2RowLinkIO)srcIo;
+
+ long link = rowIo.getLink(srcPageAddr, srcIdx);
+
+ PageUtils.putLong(dstPageAddr, dstOff, link);
+
+ if (storeMvcc) {
+ long mvccCrdVer = rowIo.getMvccCoordinatorVersion(srcPageAddr, srcIdx);
+ long mvccCntr = rowIo.getMvccCounter(srcPageAddr, srcIdx);
+ int mvccOpCntr = rowIo.getMvccOperationCounter(srcPageAddr, srcIdx);
+
+ assert MvccUtils.mvccVersionIsValid(mvccCrdVer, mvccCntr, mvccOpCntr);
+
+ PageUtils.putLong(dstPageAddr, dstOff + 8, mvccCrdVer);
+ PageUtils.putLong(dstPageAddr, dstOff + 16, mvccCntr);
+ PageUtils.putInt(dstPageAddr, dstOff + 24, mvccOpCntr);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2InnerIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2InnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2InnerIO.java
index cf37bb7..9baff7a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2InnerIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2InnerIO.java
@@ -17,20 +17,12 @@
package org.apache.ignite.internal.processors.query.h2.database.io;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageUtils;
-import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInnerIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
-import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.h2.result.SearchRow;
/**
* Inner page for H2 row references.
*/
-public class H2InnerIO extends BPlusInnerIO<SearchRow> implements H2RowLinkIO {
+public class H2InnerIO extends AbstractH2InnerIO {
/** */
public static final IOVersions<H2InnerIO> VERSIONS = new IOVersions<>(
new H2InnerIO(1)
@@ -40,35 +32,6 @@ public class H2InnerIO extends BPlusInnerIO<SearchRow> implements H2RowLinkIO {
* @param ver Page format version.
*/
private H2InnerIO(int ver) {
- super(T_H2_REF_INNER, ver, true, 8);
- }
-
- /** {@inheritDoc} */
- @Override public void storeByOffset(long pageAddr, int off, SearchRow row) {
- GridH2Row row0 = (GridH2Row)row;
-
- assert row0.link() != 0;
-
- PageUtils.putLong(pageAddr, off, row0.link());
- }
-
- /** {@inheritDoc} */
- @Override public SearchRow getLookupRow(BPlusTree<SearchRow,?> tree, long pageAddr, int idx)
- throws IgniteCheckedException {
- long link = getLink(pageAddr, idx);
-
- return ((H2Tree)tree).createRowFromLink(link);
- }
-
- /** {@inheritDoc} */
- @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<SearchRow> srcIo, long srcPageAddr, int srcIdx) {
- long link = ((H2RowLinkIO)srcIo).getLink(srcPageAddr, srcIdx);
-
- PageUtils.putLong(dstPageAddr, offset(dstIdx), link);
- }
-
- /** {@inheritDoc} */
- @Override public long getLink(long pageAddr, int idx) {
- return PageUtils.getLong(pageAddr, offset(idx));
+ super(T_H2_REF_INNER, ver, 8);
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2LeafIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2LeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2LeafIO.java
index 55a980f..8954de0 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2LeafIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2LeafIO.java
@@ -17,20 +17,12 @@
package org.apache.ignite.internal.processors.query.h2.database.io;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageUtils;
-import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
-import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
-import org.h2.result.SearchRow;
/**
* Leaf page for H2 row references.
*/
-public class H2LeafIO extends BPlusLeafIO<SearchRow> implements H2RowLinkIO {
+public class H2LeafIO extends AbstractH2LeafIO {
/** */
public static final IOVersions<H2LeafIO> VERSIONS = new IOVersions<>(
new H2LeafIO(1)
@@ -39,36 +31,7 @@ public class H2LeafIO extends BPlusLeafIO<SearchRow> implements H2RowLinkIO {
/**
* @param ver Page format version.
*/
- protected H2LeafIO(int ver) {
+ private H2LeafIO(int ver) {
super(T_H2_REF_LEAF, ver, 8);
}
-
- /** {@inheritDoc} */
- @Override public void storeByOffset(long pageAddr, int off, SearchRow row) {
- GridH2Row row0 = (GridH2Row)row;
-
- assert row0.link() != 0;
-
- PageUtils.putLong(pageAddr, off, row0.link());
- }
-
- /** {@inheritDoc} */
- @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<SearchRow> srcIo, long srcPageAddr, int srcIdx) {
- assert srcIo == this;
-
- PageUtils.putLong(dstPageAddr, offset(dstIdx), getLink(srcPageAddr, srcIdx));
- }
-
- /** {@inheritDoc} */
- @Override public SearchRow getLookupRow(BPlusTree<SearchRow,?> tree, long pageAddr, int idx)
- throws IgniteCheckedException {
- long link = getLink(pageAddr, idx);
-
- return ((H2Tree)tree).createRowFromLink(link);
- }
-
- /** {@inheritDoc} */
- @Override public long getLink(long pageAddr, int idx) {
- return PageUtils.getLong(pageAddr, offset(idx));
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasInnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasInnerIO.java
new file mode 100644
index 0000000..ee6dc2a
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasInnerIO.java
@@ -0,0 +1,55 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+
+/**
+ *
+ */
+class H2MvccExtrasInnerIO extends AbstractH2ExtrasInnerIO {
+ /**
+ * @param type Page type.
+ * @param ver Page format version.
+ * @param payloadSize Payload size.
+ */
+ H2MvccExtrasInnerIO(short type, int ver, int payloadSize) {
+ super(type, ver, 28, payloadSize);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + payloadSize + 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + payloadSize + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + payloadSize + 24);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean storeMvccInfo() {
+ return true;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasLeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasLeafIO.java
new file mode 100644
index 0000000..60a1598
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccExtrasLeafIO.java
@@ -0,0 +1,54 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+
+/**
+ * Leaf page for H2 row references.
+ */
+class H2MvccExtrasLeafIO extends AbstractH2ExtrasLeafIO {
+ /**
+ * @param type Page type.
+ * @param ver Page format version.
+ * @param payloadSize Payload size.
+ */
+ H2MvccExtrasLeafIO(short type, int ver, int payloadSize) {
+ super(type, ver, 28, payloadSize);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + payloadSize + 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + payloadSize + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + payloadSize + 24);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean storeMvccInfo() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccInnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccInnerIO.java
new file mode 100644
index 0000000..dbfe784
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccInnerIO.java
@@ -0,0 +1,58 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+
+/**
+ * Inner page for H2 row references.
+ */
+public class H2MvccInnerIO extends AbstractH2InnerIO {
+ /** */
+ public static final IOVersions<H2MvccInnerIO> VERSIONS = new IOVersions<>(
+ new H2MvccInnerIO(1)
+ );
+
+ /**
+ * @param ver Page format version.
+ */
+ private H2MvccInnerIO(int ver) {
+ super(T_H2_MVCC_REF_INNER, ver, 28);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 24);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean storeMvccInfo() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccLeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccLeafIO.java
new file mode 100644
index 0000000..c7cd998
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2MvccLeafIO.java
@@ -0,0 +1,58 @@
+/*
+ * 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.query.h2.database.io;
+
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+
+/**
+ *
+ */
+public class H2MvccLeafIO extends AbstractH2LeafIO {
+ /** */
+ public static final IOVersions<H2MvccLeafIO> VERSIONS = new IOVersions<>(
+ new H2MvccLeafIO(1)
+ );
+
+ /**
+ * @param ver Page format version.
+ */
+ private H2MvccLeafIO(int ver) {
+ super(T_H2_MVCC_REF_LEAF, ver, 28);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getMvccCounter(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMvccOperationCounter(long pageAddr, int idx) {
+ return PageUtils.getInt(pageAddr, offset(idx) + 24);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean storeMvccInfo() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2RowLinkIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2RowLinkIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2RowLinkIO.java
index ce69197..1942069 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2RowLinkIO.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/H2RowLinkIO.java
@@ -27,4 +27,38 @@ public interface H2RowLinkIO {
* @return Row link.
*/
public long getLink(long pageAddr, int idx);
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc coordinator version.
+ */
+ public default long getMvccCoordinatorVersion(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc counter.
+ */
+ public default long getMvccCounter(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @param pageAddr Page address.
+ * @param idx Index.
+ * @return Mvcc operation counter.
+ */
+ public default int getMvccOperationCounter(long pageAddr, int idx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @return {@code True} if IO stores mvcc information.
+ */
+ public default boolean storeMvccInfo() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
index 9e88f16..8688c4fbd9 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
@@ -38,6 +38,8 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
@@ -125,6 +127,8 @@ public class DdlStatementsProcessor {
try {
isDdlOnSchemaSupported(cmd.schemaName());
+ finishActiveTxIfNecessary();
+
if (cmd instanceof SqlCreateIndexCommand) {
SqlCreateIndexCommand cmd0 = (SqlCreateIndexCommand)cmd;
@@ -260,6 +264,8 @@ public class DdlStatementsProcessor {
IgniteInternalFuture fut = null;
try {
+ finishActiveTxIfNecessary();
+
GridSqlStatement stmt0 = new GridSqlQueryParser(false).parse(prepared);
if (stmt0 instanceof GridSqlCreateIndex) {
@@ -442,6 +448,10 @@ public class DdlStatementsProcessor {
else {
assert tbl.rowDescriptor() != null;
+ if (tbl.cache().mvccEnabled())
+ throw new IgniteSQLException("Cannot drop column(s) with enabled MVCC. " +
+ "Operation is unsupported at the moment.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
if (QueryUtils.isSqlType(tbl.rowDescriptor().type().valueClass()))
throw new SchemaOperationException("Cannot drop column(s) because table was created " +
"with " + PARAM_WRAP_VALUE + "=false option.");
@@ -570,6 +580,23 @@ public class DdlStatementsProcessor {
}
/**
+ * Commits active transaction if exists.
+ *
+ * @throws IgniteCheckedException If failed.
+ */
+ private void finishActiveTxIfNecessary() throws IgniteCheckedException {
+ try (GridNearTxLocal tx = MvccUtils.tx(ctx)) {
+ if (tx == null)
+ return;
+
+ if (!tx.isRollbackOnly())
+ tx.commit();
+ else
+ tx.rollback();
+ }
+ }
+
+ /**
* @return {@link IgniteSQLException} with the message same as of {@code this}'s and
*/
private IgniteSQLException convert(SchemaOperationException e) {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
index 161ff4a..ebf5848 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java
@@ -98,8 +98,6 @@ public final class DmlAstUtils {
GridSqlArray[] args = new GridSqlArray[cols.length];
- boolean noQry = true;
-
for (int i = 0; i < cols.length; i++) {
GridSqlArray arr = new GridSqlArray(rows.size());
@@ -123,18 +121,10 @@ public final class DmlAstUtils {
for (GridSqlElement[] row : rows) {
assert cols.length == row.length;
- for (int i = 0; i < row.length; i++) {
- GridSqlElement el = row[i];
-
- noQry &= (el instanceof GridSqlConst || el instanceof GridSqlParameter);
-
+ for (int i = 0; i < row.length; i++)
args[i].addChild(row[i]);
- }
}
- if (noQry)
- return null;
-
return sel;
}
else {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java
index dcceff3..346ad48 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java
@@ -21,6 +21,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.query.h2.UpdateResult;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlElement;
+import org.apache.ignite.lang.IgniteBiTuple;
import org.jetbrains.annotations.Nullable;
/**
@@ -101,4 +102,20 @@ public final class FastUpdate {
return res ? UpdateResult.ONE : UpdateResult.ZERO;
}
+
+ /**
+ *
+ * @param args Query Parameters.
+ * @return Key and value.
+ * @throws IgniteCheckedException If failed.
+ */
+ public IgniteBiTuple getRow(Object[] args) throws IgniteCheckedException {
+ Object key = keyArg.get(args);
+
+ assert key != null;
+
+ Object newVal = newValArg.get(args);
+
+ return new IgniteBiTuple(key, newVal);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
index 98fbb97..ba4b12b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
@@ -19,20 +19,28 @@ package org.apache.ignite.internal.processors.query.h2.dml;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
+import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.internal.processors.query.h2.H2ConnectionWrapper;
+import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.h2.ThreadLocalObjectPool;
import org.apache.ignite.internal.processors.query.h2.UpdateResult;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.util.GridCloseableIteratorAdapterEx;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.T3;
import org.apache.ignite.lang.IgniteBiTuple;
@@ -358,6 +366,13 @@ public final class UpdatePlan {
}
/**
+ * @return {@code True} if DML can be fast processed.
+ */
+ public boolean fastResult() {
+ return fastUpdate != null;
+ }
+
+ /**
* Process fast DML operation if possible.
*
* @param args QUery arguments.
@@ -468,6 +483,48 @@ public final class UpdatePlan {
}
/**
+ * Create iterator for transaction.
+ *
+ * @param idx Indexing.
+ * @param cur Cursor.
+ * @return Iterator.
+ */
+ public UpdateSourceIterator<?> iteratorForTransaction(IgniteH2Indexing idx, QueryCursor<List<?>> cur) {
+ switch (mode) {
+ case MERGE:
+ return new InsertIterator(idx, cur, this, EnlistOperation.UPSERT);
+ case INSERT:
+ return new InsertIterator(idx, cur, this, EnlistOperation.INSERT);
+ case UPDATE:
+ return new UpdateIterator(idx, cur, this, EnlistOperation.UPDATE);
+ case DELETE:
+ return new DeleteIterator(idx, cur, this, EnlistOperation.DELETE);
+
+ default:
+ throw new IllegalArgumentException(String.valueOf(mode));
+ }
+ }
+
+ /**
+ * @param updMode Update plan mode.
+ * @return Operation.
+ */
+ public static EnlistOperation enlistOperation(UpdateMode updMode) {
+ switch (updMode) {
+ case INSERT:
+ return EnlistOperation.INSERT;
+ case MERGE:
+ return EnlistOperation.UPSERT;
+ case UPDATE:
+ return EnlistOperation.UPDATE;
+ case DELETE:
+ return EnlistOperation.DELETE;
+ default:
+ throw new IllegalArgumentException(String.valueOf(updMode));
+ }
+ }
+
+ /**
* @return Update mode.
*/
public UpdateMode mode() {
@@ -508,4 +565,180 @@ public final class UpdatePlan {
public boolean isLocalSubquery() {
return isLocSubqry;
}
+
+ /**
+ * @param args Query parameters.
+ * @return Iterator.
+ * @throws IgniteCheckedException If failed.
+ */
+ public IgniteBiTuple getFastRow(Object[] args) throws IgniteCheckedException {
+ if (fastUpdate != null)
+ return fastUpdate.getRow(args);
+
+ return null;
+ }
+
+ /**
+ * @param row Row.
+ * @return Resulting entry.
+ * @throws IgniteCheckedException If failed.
+ */
+ public Object processRowForTx(List<?> row) throws IgniteCheckedException {
+ switch (mode()) {
+ case INSERT:
+ case MERGE:
+ return processRow(row);
+
+ case UPDATE: {
+ T3<Object, Object, Object> row0 = processRowForUpdate(row);
+
+ return new IgniteBiTuple<>(row0.get1(), row0.get3());
+ }
+ case DELETE:
+ return row.get(0);
+
+ default:
+ throw new UnsupportedOperationException(String.valueOf(mode()));
+ }
+ }
+
+ /**
+ * Abstract iterator.
+ */
+ private abstract static class AbstractIterator extends GridCloseableIteratorAdapterEx<Object>
+ implements UpdateSourceIterator<Object> {
+ /** */
+ private final IgniteH2Indexing idx;
+
+ /** */
+ private final QueryCursor<List<?>> cur;
+
+ /** */
+ protected final UpdatePlan plan;
+
+ /** */
+ private final Iterator<List<?>> it;
+
+ /** */
+ private final EnlistOperation op;
+
+ /** */
+ private volatile ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> conn;
+
+ /**
+ * @param idx Indexing.
+ * @param cur Query cursor.
+ * @param plan Update plan.
+ * @param op Operation.
+ */
+ private AbstractIterator(IgniteH2Indexing idx, QueryCursor<List<?>> cur, UpdatePlan plan, EnlistOperation op) {
+ this.idx = idx;
+ this.cur = cur;
+ this.plan = plan;
+ this.op = op;
+
+ it = cur.iterator();
+ }
+
+ /** {@inheritDoc} */
+ @Override public EnlistOperation operation() {
+ return op;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void beforeDetach() {
+ ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> conn0 = conn = idx.detach();
+
+ if (isClosed())
+ conn0.recycle();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void onClose() {
+ cur.close();
+
+ ThreadLocalObjectPool.Reusable<H2ConnectionWrapper> conn0 = conn;
+
+ if (conn0 != null)
+ conn0.recycle();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object onNext() throws IgniteCheckedException {
+ return process(it.next());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean onHasNext() throws IgniteCheckedException {
+ return it.hasNext();
+ }
+
+ /** */
+ protected abstract Object process(List<?> row) throws IgniteCheckedException;
+ }
+
+ /** */
+ private static final class UpdateIterator extends AbstractIterator {
+ /** */
+ private static final long serialVersionUID = -4949035950470324961L;
+
+ /**
+ * @param idx Indexing.
+ * @param cur Query cursor.
+ * @param plan Update plan.
+ * @param op Operation.
+ */
+ private UpdateIterator(IgniteH2Indexing idx, QueryCursor<List<?>> cur, UpdatePlan plan, EnlistOperation op) {
+ super(idx, cur, plan, op);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object process(List<?> row) throws IgniteCheckedException {
+ T3<Object, Object, Object> row0 = plan.processRowForUpdate(row);
+
+ return new IgniteBiTuple<>(row0.get1(), row0.get3());
+ }
+ }
+
+ /** */
+ private static final class DeleteIterator extends AbstractIterator {
+ /** */
+ private static final long serialVersionUID = -4949035950470324961L;
+
+ /**
+ * @param idx Indexing.
+ * @param cur Query cursor.
+ * @param plan Update plan.
+ * @param op Operation.
+ */
+ private DeleteIterator(IgniteH2Indexing idx, QueryCursor<List<?>> cur, UpdatePlan plan, EnlistOperation op) {
+ super(idx, cur, plan, op);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object process(List<?> row) throws IgniteCheckedException {
+ return row.get(0);
+ }
+ }
+
+ /** */
+ private static final class InsertIterator extends AbstractIterator {
+ /** */
+ private static final long serialVersionUID = -4949035950470324961L;
+
+ /**
+ * @param idx Indexing.
+ * @param cur Query cursor.
+ * @param plan Update plan.
+ * @param op Operation.
+ */
+ private InsertIterator(IgniteH2Indexing idx, QueryCursor<List<?>> cur, UpdatePlan plan, EnlistOperation op) {
+ super(idx, cur, plan, op);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object process(List<?> row) throws IgniteCheckedException {
+ return plan.processRow(row);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
index 1079005..6c103f3 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
@@ -41,11 +41,15 @@ import org.apache.ignite.internal.processors.query.h2.DmlStatementsProcessor;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAlias;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAst;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlColumn;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlDelete;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlElement;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlInsert;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlMerge;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlParameter;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuery;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter;
@@ -91,15 +95,48 @@ public final class UpdatePlanBuilder {
public static UpdatePlan planForStatement(Prepared prepared, boolean loc, IgniteH2Indexing idx,
@Nullable Connection conn, @Nullable SqlFieldsQuery fieldsQry, @Nullable Integer errKeysPos)
throws IgniteCheckedException {
- GridSqlStatement stmt = new GridSqlQueryParser(false).parse(prepared);
+ assert !prepared.isQuery();
+
+ GridSqlQueryParser parser = new GridSqlQueryParser(false);
+
+ GridSqlStatement stmt = parser.parse(prepared);
+
+ boolean mvccEnabled = false;
+
+ GridCacheContext cctx = null;
+
+ // check all involved caches
+ for (Object o : parser.objectsMap().values()) {
+ if (o instanceof GridSqlInsert)
+ o = ((GridSqlInsert)o).into();
+ else if (o instanceof GridSqlMerge)
+ o = ((GridSqlMerge)o).into();
+ else if (o instanceof GridSqlDelete)
+ o = ((GridSqlDelete)o).from();
+
+ if (o instanceof GridSqlAlias)
+ o = GridSqlAlias.unwrap((GridSqlAst)o);
+
+ if (o instanceof GridSqlTable) {
+ if (((GridSqlTable)o).dataTable() == null) { // Check for virtual tables.
+ throw new IgniteSQLException("Operation not supported for table '" +
+ ((GridSqlTable)o).tableName() + "'", IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
+ if (cctx == null)
+ mvccEnabled = (cctx = (((GridSqlTable)o).dataTable()).cache()).mvccEnabled();
+ else if (((GridSqlTable)o).dataTable().cache().mvccEnabled() != mvccEnabled)
+ throw new IllegalStateException("Using caches with different mvcc settings in same query is forbidden.");
+ }
+ }
if (stmt instanceof GridSqlMerge || stmt instanceof GridSqlInsert)
- return planForInsert(stmt, loc, idx, conn, fieldsQry);
+ return planForInsert(stmt, loc, idx, mvccEnabled, conn, fieldsQry);
else if (stmt instanceof GridSqlUpdate || stmt instanceof GridSqlDelete)
- return planForUpdate(stmt, loc, idx, conn, fieldsQry, errKeysPos);
+ return planForUpdate(stmt, loc, idx, mvccEnabled, conn, fieldsQry, errKeysPos);
else
throw new IgniteSQLException("Unsupported operation: " + prepared.getSQL(),
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
}
/**
@@ -108,6 +145,7 @@ public final class UpdatePlanBuilder {
* @param stmt INSERT or MERGE statement.
* @param loc Local query flag.
* @param idx Indexing.
+ * @param mvccEnabled Mvcc flag.
* @param conn Connection.
* @param fieldsQuery Original query.
* @return Update plan.
@@ -115,8 +153,9 @@ public final class UpdatePlanBuilder {
*/
@SuppressWarnings("ConstantConditions")
private static UpdatePlan planForInsert(GridSqlStatement stmt, boolean loc, IgniteH2Indexing idx,
- @Nullable Connection conn, @Nullable SqlFieldsQuery fieldsQuery) throws IgniteCheckedException {
- GridSqlQuery sel;
+ boolean mvccEnabled, @Nullable Connection conn, @Nullable SqlFieldsQuery fieldsQuery)
+ throws IgniteCheckedException {
+ GridSqlQuery sel = null;
GridSqlElement target;
@@ -140,17 +179,16 @@ public final class UpdatePlanBuilder {
GridH2Table h2Tbl = tbl.dataTable();
- if (h2Tbl == null)
- throw new IgniteSQLException("Operation not supported for table '" + tbl.tableName() + "'",
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ assert h2Tbl != null;
desc = h2Tbl.rowDescriptor();
cols = ins.columns();
- sel = DmlAstUtils.selectForInsertOrMerge(cols, ins.rows(), ins.query());
- if (sel == null)
+ if (noQuery(ins.rows()))
elRows = ins.rows();
+ else
+ sel = DmlAstUtils.selectForInsertOrMerge(cols, ins.rows(), ins.query());
isTwoStepSubqry = (ins.query() != null);
rowsNum = isTwoStepSubqry ? 0 : ins.rows().size();
@@ -164,10 +202,11 @@ public final class UpdatePlanBuilder {
desc = tbl.dataTable().rowDescriptor();
cols = merge.columns();
- sel = DmlAstUtils.selectForInsertOrMerge(cols, merge.rows(), merge.query());
- if (sel == null)
+ if (noQuery(merge.rows()))
elRows = merge.rows();
+ else
+ sel = DmlAstUtils.selectForInsertOrMerge(cols, merge.rows(), merge.query());
isTwoStepSubqry = (merge.query() != null);
rowsNum = isTwoStepSubqry ? 0 : merge.rows().size();
@@ -233,7 +272,8 @@ public final class UpdatePlanBuilder {
String selectSql = sel != null ? sel.getSQL() : null;
DmlDistributedPlanInfo distributed = (rowsNum == 0 && !F.isEmpty(selectSql)) ?
- checkPlanCanBeDistributed(idx, conn, fieldsQuery, loc, selectSql, tbl.dataTable().cacheName()) : null;
+ checkPlanCanBeDistributed(idx, mvccEnabled, conn, fieldsQuery, loc, selectSql,
+ tbl.dataTable().cacheName()) : null;
UpdateMode mode = stmt instanceof GridSqlMerge ? UpdateMode.MERGE : UpdateMode.INSERT;
@@ -276,11 +316,37 @@ public final class UpdatePlanBuilder {
}
/**
+ * @param rows Insert rows.
+ * @return {@code True} if no query optimisation may be used.
+ */
+ private static boolean noQuery(List<GridSqlElement[]> rows) {
+ if (F.isEmpty(rows))
+ return false;
+
+ boolean noQry = true;
+
+ for (int i = 0; i < rows.size(); i++) {
+ GridSqlElement[] row = rows.get(i);
+
+ for (int i1 = 0; i1 < row.length; i1++) {
+ GridSqlElement el = row[i1];
+
+ if(!(noQry &= (el instanceof GridSqlConst || el instanceof GridSqlParameter)))
+ return noQry;
+
+ }
+ }
+
+ return true;
+ }
+
+ /**
* Prepare update plan for UPDATE or DELETE.
*
* @param stmt UPDATE or DELETE statement.
* @param loc Local query flag.
* @param idx Indexing.
+ * @param mvccEnabled Mvcc flag.
* @param conn Connection.
* @param fieldsQuery Original query.
* @param errKeysPos index to inject param for re-run keys at. Null if it's not a re-run plan.
@@ -288,8 +354,8 @@ public final class UpdatePlanBuilder {
* @throws IgniteCheckedException if failed.
*/
private static UpdatePlan planForUpdate(GridSqlStatement stmt, boolean loc, IgniteH2Indexing idx,
- @Nullable Connection conn, @Nullable SqlFieldsQuery fieldsQuery, @Nullable Integer errKeysPos)
- throws IgniteCheckedException {
+ boolean mvccEnabled, @Nullable Connection conn, @Nullable SqlFieldsQuery fieldsQuery,
+ @Nullable Integer errKeysPos) throws IgniteCheckedException {
GridSqlElement target;
FastUpdate fastUpdate;
@@ -319,9 +385,7 @@ public final class UpdatePlanBuilder {
GridH2Table h2Tbl = tbl.dataTable();
- if (h2Tbl == null)
- throw new IgniteSQLException("Operation not supported for table '" + tbl.tableName() + "'",
- IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ assert h2Tbl != null;
GridH2RowDescriptor desc = h2Tbl.rowDescriptor();
@@ -381,7 +445,8 @@ public final class UpdatePlanBuilder {
String selectSql = sel.getSQL();
DmlDistributedPlanInfo distributed = F.isEmpty(selectSql) ? null :
- checkPlanCanBeDistributed(idx, conn, fieldsQuery, loc, selectSql, tbl.dataTable().cacheName());
+ checkPlanCanBeDistributed(idx, mvccEnabled, conn, fieldsQuery, loc, selectSql,
+ tbl.dataTable().cacheName());
return new UpdatePlan(
UpdateMode.UPDATE,
@@ -406,7 +471,8 @@ public final class UpdatePlanBuilder {
String selectSql = sel.getSQL();
DmlDistributedPlanInfo distributed = F.isEmpty(selectSql) ? null :
- checkPlanCanBeDistributed(idx, conn, fieldsQuery, loc, selectSql, tbl.dataTable().cacheName());
+ checkPlanCanBeDistributed(idx, mvccEnabled, conn, fieldsQuery, loc, selectSql,
+ tbl.dataTable().cacheName());
return new UpdatePlan(
UpdateMode.DELETE,
@@ -513,6 +579,7 @@ public final class UpdatePlanBuilder {
* @param colIdx Column index if key or value is present in columns list, {@code -1} if it's not.
* @param hasProps Whether column list affects individual properties of key or value.
* @param key Whether supplier should be created for key or for value.
+ * @param forUpdate {@code FOR UPDATE} flag.
* @return Closure returning key or value.
* @throws IgniteCheckedException If failed.
*/
@@ -705,6 +772,7 @@ public final class UpdatePlanBuilder {
* Checks whether the given update plan can be distributed and returns additional info.
*
* @param idx Indexing.
+ * @param mvccEnabled Mvcc flag.
* @param conn Connection.
* @param fieldsQry Initial update query.
* @param loc Local query flag.
@@ -713,11 +781,10 @@ public final class UpdatePlanBuilder {
* @return distributed update plan info, or {@code null} if cannot be distributed.
* @throws IgniteCheckedException if failed.
*/
- private static DmlDistributedPlanInfo checkPlanCanBeDistributed(IgniteH2Indexing idx,
+ private static DmlDistributedPlanInfo checkPlanCanBeDistributed(IgniteH2Indexing idx, boolean mvccEnabled,
Connection conn, SqlFieldsQuery fieldsQry, boolean loc, String selectQry, String cacheName)
throws IgniteCheckedException {
-
- if (loc || !isSkipReducerOnUpdateQuery(fieldsQry) || DmlUtils.isBatched(fieldsQry))
+ if (loc || (!mvccEnabled && !isSkipReducerOnUpdateQuery(fieldsQry)) || DmlUtils.isBatched(fieldsQry))
return null;
assert conn != null;
@@ -732,7 +799,8 @@ public final class UpdatePlanBuilder {
fieldsQry.getArgs(),
fieldsQry.isCollocated(),
fieldsQry.isDistributedJoins(),
- fieldsQry.isEnforceJoinOrder(), idx);
+ fieldsQry.isEnforceJoinOrder(),
+ idx);
boolean distributed = qry.skipMergeTable() && qry.mapQueries().size() == 1 &&
!qry.mapQueries().get(0).hasSubQueries();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
index 77bd69a..6fd11c1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.managers.communication.GridIoPolicy;
import org.apache.ignite.internal.managers.communication.GridMessageListener;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.processors.query.h2.H2Cursor;
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeRequest;
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeResponse;
@@ -46,7 +47,6 @@ import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.logger.NullLogger;
import org.apache.ignite.plugin.extensions.communication.Message;
-import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.engine.Session;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
@@ -266,15 +266,6 @@ public abstract class GridH2IndexBase extends BaseIndex {
return (GridH2Table)super.getTable();
}
- /**
- * @return Filter for currently running query or {@code null} if none.
- */
- protected static IndexingQueryFilter threadLocalFilter() {
- GridH2QueryContext qctx = GridH2QueryContext.get();
-
- return qctx != null ? qctx.filter() : null;
- }
-
/** {@inheritDoc} */
@Override public long getDiskSpaceUsed() {
return 0;
@@ -421,7 +412,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
// This is the first request containing all the search rows.
assert !msg.bounds().isEmpty() : "empty bounds";
- src = new RangeSource(msg.bounds(), msg.segment(), qctx.filter());
+ src = new RangeSource(msg.bounds(), msg.segment(), filter(qctx));
}
else {
// This is request to fetch next portion of data.
@@ -475,6 +466,14 @@ public abstract class GridH2IndexBase extends BaseIndex {
}
/**
+ * @param qctx Query context.
+ * @return Row filter.
+ */
+ protected BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter(GridH2QueryContext qctx) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* @param node Responded node.
* @param msg Response message.
*/
@@ -1463,20 +1462,17 @@ public abstract class GridH2IndexBase extends BaseIndex {
private final int segment;
/** */
- final IndexingQueryFilter filter;
+ private final BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter;
/** Iterator. */
Iterator<GridH2Row> iter = emptyIterator();
/**
* @param bounds Bounds.
+ * @param segment Segment.
* @param filter Filter.
*/
- RangeSource(
- Iterable<GridH2RowRangeBounds> bounds,
- int segment,
- IndexingQueryFilter filter
- ) {
+ RangeSource(Iterable<GridH2RowRangeBounds> bounds, int segment, BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter) {
this.segment = segment;
this.filter = filter;
boundsIter = bounds.iterator();
@@ -1536,7 +1532,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
IgniteTree t = treeForRead(segment);
- iter = new CursorIteratorWrapper(doFind0(t, first, true, last, filter));
+ iter = new CursorIteratorWrapper(doFind0(t, first, last, filter));
if (!iter.hasNext()) {
// We have to return empty range here.
@@ -1561,7 +1557,6 @@ public abstract class GridH2IndexBase extends BaseIndex {
/**
* @param t Tree.
* @param first Lower bound.
- * @param includeFirst Whether lower bound should be inclusive.
* @param last Upper bound always inclusive.
* @param filter Filter.
* @return Iterator over rows in given range.
@@ -1569,9 +1564,8 @@ public abstract class GridH2IndexBase extends BaseIndex {
protected H2Cursor doFind0(
IgniteTree t,
@Nullable SearchRow first,
- boolean includeFirst,
@Nullable SearchRow last,
- IndexingQueryFilter filter) {
+ BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter) {
throw new UnsupportedOperationException();
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
index 291f8c8..e152054 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyRowOnheap.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.query.h2.opt;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.h2.value.Value;
@@ -60,4 +61,14 @@ public class GridH2KeyRowOnheap extends GridH2Row {
@Override public long expireTime() {
return 0;
}
+
+ /** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ throw new UnsupportedOperationException();
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java
index e855536..f966034 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2KeyValueRowOnheap.java
@@ -65,8 +65,10 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
* @param valType Value type.
* @throws IgniteCheckedException If failed.
*/
- public GridH2KeyValueRowOnheap(GridH2RowDescriptor desc, CacheDataRow row, int keyType, int valType)
- throws IgniteCheckedException {
+ public GridH2KeyValueRowOnheap(GridH2RowDescriptor desc,
+ CacheDataRow row,
+ int keyType,
+ int valType) throws IgniteCheckedException {
super(row);
this.desc = desc;
@@ -81,6 +83,11 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
}
/** {@inheritDoc} */
+ @Override public boolean indexSearchRow() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
@Override public int getColumnCount() {
return DEFAULT_COLUMNS_COUNT + desc.fieldsCount();
}
@@ -220,4 +227,14 @@ public class GridH2KeyValueRowOnheap extends GridH2Row {
@Override public final int hashCode() {
throw new UnsupportedOperationException();
}
+
+ /** {@inheritDoc} */
+ @Override public int size() throws IgniteCheckedException {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int headerSize() {
+ throw new UnsupportedOperationException();
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
index 00312b8..b008c08 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2MetaTable.java
@@ -284,6 +284,11 @@ public class GridH2MetaTable extends TableBase {
throw new IllegalStateException("Index: " + idx);
}
}
+
+ /** {@inheritDoc} */
+ @Override public boolean indexSearchRow() {
+ return false;
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java
index fd8a613..d24dc08 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2PlainRowFactory.java
@@ -70,7 +70,7 @@ public class GridH2PlainRowFactory extends RowFactory {
/**
* @param key Key.
*/
- public RowKey(Value key) {
+ RowKey(Value key) {
this.key = key;
}
@@ -92,6 +92,11 @@ public class GridH2PlainRowFactory extends RowFactory {
}
/** {@inheritDoc} */
+ @Override public boolean indexSearchRow() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
@Override public String toString() {
return S.toString(RowKey.class, this);
}
@@ -138,6 +143,11 @@ public class GridH2PlainRowFactory extends RowFactory {
}
/** {@inheritDoc} */
+ @Override public boolean indexSearchRow() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
@Override public String toString() {
return S.toString(RowPair.class, this);
}
@@ -174,6 +184,11 @@ public class GridH2PlainRowFactory extends RowFactory {
}
/** {@inheritDoc} */
+ @Override public boolean indexSearchRow() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
@Override public String toString() {
return S.toString(RowSimple.class, this);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
index 7b52ea4..f12c0f3 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
@@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentMap;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.query.h2.twostep.MapQueryLazyWorker;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -85,6 +86,9 @@ public class GridH2QueryContext {
private GridH2CollocationModel qryCollocationMdl;
/** */
+ private MvccSnapshot mvccSnapshot;
+
+ /** */
private MapQueryLazyWorker lazyWorker;
/**
@@ -106,13 +110,34 @@ public class GridH2QueryContext {
* @param segmentId Index segment ID.
* @param type Query type.
*/
- public GridH2QueryContext(UUID locNodeId, UUID nodeId, long qryId, int segmentId, GridH2QueryType type) {
+ public GridH2QueryContext(UUID locNodeId,
+ UUID nodeId,
+ long qryId,
+ int segmentId,
+ GridH2QueryType type) {
assert segmentId == 0 || type == MAP;
key = new Key(locNodeId, nodeId, qryId, segmentId, type);
}
/**
+ * @return Mvcc snapshot.
+ */
+ @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
+ * @param mvccSnapshot Mvcc snapshot.
+ * @return {@code this}.
+ */
+ public GridH2QueryContext mvccSnapshot(MvccSnapshot mvccSnapshot) {
+ this.mvccSnapshot = mvccSnapshot;
+
+ return this;
+ }
+
+ /**
* @return Type.
*/
public GridH2QueryType type() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
index 8b1b711..2133e1a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
@@ -27,7 +27,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
*/
public abstract class GridH2Row extends GridH2SearchRowAdapter implements CacheDataRow {
/** Row. */
- private CacheDataRow row;
+ protected final CacheDataRow row;
/**
* @param row Row.
@@ -78,11 +78,57 @@ public abstract class GridH2Row extends GridH2SearchRowAdapter implements CacheD
/** {@inheritDoc} */
@Override public int hash() {
- throw new UnsupportedOperationException();
+ return row.hash();
}
/** {@inheritDoc} */
@Override public int cacheId() {
return row.cacheId();
}
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return row.mvccCoordinatorVersion();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return row.mvccCounter();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return row.mvccOperationCounter();
+ }
+
+ /** {@inheritDoc} */
+ public byte mvccTxState() {
+ return row.mvccTxState();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCoordinatorVersion() {
+ return row.newMvccCoordinatorVersion();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCounter() {
+ return row.newMvccCounter();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int newMvccOperationCounter() {
+ return row.newMvccOperationCounter();
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte newMvccTxState() {
+ return row.newMvccTxState();
+ }
+
+
+ /** {@inheritDoc} */
+ @Override public boolean indexSearchRow() {
+ return false;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
index 1d915e5..23f3ba4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java
@@ -29,6 +29,8 @@ import java.util.UUID;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
@@ -36,7 +38,6 @@ import org.apache.ignite.internal.processors.query.h2.H2TableDescriptor;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
import org.h2.message.DbException;
import org.h2.result.SearchRow;
-import org.h2.result.SimpleRow;
import org.h2.util.LocalDateTimeUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
@@ -58,6 +59,7 @@ import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueUuid;
+import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.KEY_COL;
@@ -280,8 +282,9 @@ public class GridH2RowDescriptor {
GridH2Row row;
try {
- if (dataRow.value() == null) // Only can happen for remove operation, can create simple search row.
+ if (dataRow.value() == null) { // Only can happen for remove operation, can create simple search row.
row = new GridH2KeyRowOnheap(dataRow, wrap(dataRow.key(), keyType));
+ }
else
row = new GridH2KeyValueRowOnheap(this, dataRow, keyType, valType);
}
@@ -465,7 +468,7 @@ public class GridH2RowDescriptor {
copyAliasColumnData(data, KEY_COL, keyAliasColId);
copyAliasColumnData(data, VAL_COL, valAliasColId);
- return new SimpleRow(data);
+ return GridH2PlainRowFactory.create(data);
}
/**
[14/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExImpl.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExImpl.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExImpl.java
new file mode 100644
index 0000000..922c1ab
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/PreparedStatementExImpl.java
@@ -0,0 +1,648 @@
+/*
+ * 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.query.h2;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLType;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Calendar;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * PreparedStatement with extended capability to store additional meta information.
+ */
+@SuppressWarnings("unchecked")
+final class PreparedStatementExImpl implements PreparedStatementEx {
+ /** */
+ private final PreparedStatement delegate;
+
+ /** */
+ private Object[] meta = null;
+
+ /**
+ * @param delegate Wrapped statement.
+ */
+ public PreparedStatementExImpl(PreparedStatement delegate) {
+ this.delegate = delegate;
+ }
+
+ /** {@inheritDoc} */
+ @Override public ResultSet executeQuery() throws SQLException {
+ return delegate.executeQuery();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int executeUpdate() throws SQLException {
+ return delegate.executeUpdate();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNull(int parameterIndex, int sqlType) throws SQLException {
+ delegate.setNull(parameterIndex, sqlType);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBoolean(int parameterIndex, boolean x) throws SQLException {
+ delegate.setBoolean(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setByte(int parameterIndex, byte x) throws SQLException {
+ delegate.setByte(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setShort(int parameterIndex, short x) throws SQLException {
+ delegate.setShort(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setInt(int parameterIndex, int x) throws SQLException {
+ delegate.setInt(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setLong(int parameterIndex, long x) throws SQLException {
+ delegate.setLong(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setFloat(int parameterIndex, float x) throws SQLException {
+ delegate.setFloat(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setDouble(int parameterIndex, double x) throws SQLException {
+ delegate.setDouble(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
+ delegate.setBigDecimal(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setString(int parameterIndex, String x) throws SQLException {
+ delegate.setString(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+ delegate.setBytes(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setDate(int parameterIndex, Date x) throws SQLException {
+ delegate.setDate(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setTime(int parameterIndex, Time x) throws SQLException {
+ delegate.setTime(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
+ delegate.setTimestamp(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
+ delegate.setAsciiStream(parameterIndex, x, length);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ @Override public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
+ delegate.setUnicodeStream(parameterIndex, x, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
+ delegate.setBinaryStream(parameterIndex, x, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clearParameters() throws SQLException {
+ delegate.clearParameters();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
+ delegate.setObject(parameterIndex, x, targetSqlType);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setObject(int parameterIndex, Object x) throws SQLException {
+ delegate.setObject(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean execute() throws SQLException {
+ return delegate.execute();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addBatch() throws SQLException {
+ delegate.addBatch();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
+ delegate.setCharacterStream(parameterIndex, reader, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setRef(int parameterIndex, Ref x) throws SQLException {
+ delegate.setRef(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBlob(int parameterIndex, Blob x) throws SQLException {
+ delegate.setBlob(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setClob(int parameterIndex, Clob x) throws SQLException {
+ delegate.setClob(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setArray(int parameterIndex, Array x) throws SQLException {
+ delegate.setArray(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public ResultSetMetaData getMetaData() throws SQLException {
+ return delegate.getMetaData();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
+ delegate.setDate(parameterIndex, x, cal);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
+ delegate.setTime(parameterIndex, x, cal);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
+ delegate.setTimestamp(parameterIndex, x, cal);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
+ delegate.setNull(parameterIndex, sqlType, typeName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setURL(int parameterIndex, URL x) throws SQLException {
+ delegate.setURL(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public ParameterMetaData getParameterMetaData() throws SQLException {
+ return delegate.getParameterMetaData();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setRowId(int parameterIndex, RowId x) throws SQLException {
+ delegate.setRowId(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNString(int parameterIndex, String value) throws SQLException {
+ delegate.setNString(parameterIndex, value);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
+ delegate.setNCharacterStream(parameterIndex, value, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNClob(int parameterIndex, NClob value) throws SQLException {
+ delegate.setNClob(parameterIndex, value);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
+ delegate.setClob(parameterIndex, reader, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
+ delegate.setBlob(parameterIndex, inputStream, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
+ delegate.setNClob(parameterIndex, reader, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
+ delegate.setSQLXML(parameterIndex, xmlObject);
+ }
+
+ @Override
+ public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
+ delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
+ delegate.setAsciiStream(parameterIndex, x, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
+ delegate.setBinaryStream(parameterIndex, x, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
+ delegate.setCharacterStream(parameterIndex, reader, length);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
+ delegate.setAsciiStream(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
+ delegate.setBinaryStream(parameterIndex, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
+ delegate.setCharacterStream(parameterIndex, reader);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
+ delegate.setNCharacterStream(parameterIndex, value);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setClob(int parameterIndex, Reader reader) throws SQLException {
+ delegate.setClob(parameterIndex, reader);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
+ delegate.setBlob(parameterIndex, inputStream);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNClob(int parameterIndex, Reader reader) throws SQLException {
+ delegate.setNClob(parameterIndex, reader);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
+ delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException {
+ delegate.setObject(parameterIndex, x, targetSqlType);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long executeLargeUpdate() throws SQLException {
+ return delegate.executeLargeUpdate();
+ }
+
+ /** {@inheritDoc} */
+ @Override public ResultSet executeQuery(String sql) throws SQLException {
+ return delegate.executeQuery(sql);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int executeUpdate(String sql) throws SQLException {
+ return delegate.executeUpdate(sql);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void close() throws SQLException {
+ delegate.close();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMaxFieldSize() throws SQLException {
+ return delegate.getMaxFieldSize();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMaxFieldSize(int max) throws SQLException {
+ delegate.setMaxFieldSize(max);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getMaxRows() throws SQLException {
+ return delegate.getMaxRows();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setMaxRows(int max) throws SQLException {
+ delegate.setMaxRows(max);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setEscapeProcessing(boolean enable) throws SQLException {
+ delegate.setEscapeProcessing(enable);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getQueryTimeout() throws SQLException {
+ return delegate.getQueryTimeout();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setQueryTimeout(int seconds) throws SQLException {
+ delegate.setQueryTimeout(seconds);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void cancel() throws SQLException {
+ delegate.cancel();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SQLWarning getWarnings() throws SQLException {
+ return delegate.getWarnings();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clearWarnings() throws SQLException {
+ delegate.clearWarnings();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setCursorName(String name) throws SQLException {
+ delegate.setCursorName(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean execute(String sql) throws SQLException {
+ return delegate.execute(sql);
+ }
+
+ /** {@inheritDoc} */
+ @Override public ResultSet getResultSet() throws SQLException {
+ return delegate.getResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getUpdateCount() throws SQLException {
+ return delegate.getUpdateCount();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean getMoreResults() throws SQLException {
+ return delegate.getMoreResults();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getFetchDirection() throws SQLException {
+ return delegate.getFetchDirection();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setFetchDirection(int direction) throws SQLException {
+ delegate.setFetchDirection(direction);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getFetchSize() throws SQLException {
+ return delegate.getFetchSize();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setFetchSize(int rows) throws SQLException {
+ delegate.setFetchSize(rows);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getResultSetConcurrency() throws SQLException {
+ return delegate.getResultSetConcurrency();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getResultSetType() throws SQLException {
+ return delegate.getResultSetType();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addBatch(String sql) throws SQLException {
+ delegate.addBatch(sql);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clearBatch() throws SQLException {
+ delegate.clearBatch();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int[] executeBatch() throws SQLException {
+ return delegate.executeBatch();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Connection getConnection() throws SQLException {
+ return delegate.getConnection();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean getMoreResults(int current) throws SQLException {
+ return delegate.getMoreResults(current);
+ }
+
+ /** {@inheritDoc} */
+ @Override public ResultSet getGeneratedKeys() throws SQLException {
+ return delegate.getGeneratedKeys();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
+ return delegate.executeUpdate(sql, autoGeneratedKeys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
+ return delegate.executeUpdate(sql, columnIndexes);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int executeUpdate(String sql, String[] columnNames) throws SQLException {
+ return delegate.executeUpdate(sql, columnNames);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
+ return delegate.execute(sql, autoGeneratedKeys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean execute(String sql, int[] columnIndexes) throws SQLException {
+ return delegate.execute(sql, columnIndexes);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean execute(String sql, String[] columnNames) throws SQLException {
+ return delegate.execute(sql, columnNames);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getResultSetHoldability() throws SQLException {
+ return delegate.getResultSetHoldability();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isClosed() throws SQLException {
+ return delegate.isClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isPoolable() throws SQLException {
+ return delegate.isPoolable();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setPoolable(boolean poolable) throws SQLException {
+ delegate.setPoolable(poolable);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void closeOnCompletion() throws SQLException {
+ delegate.closeOnCompletion();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isCloseOnCompletion() throws SQLException {
+ return delegate.isCloseOnCompletion();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getLargeUpdateCount() throws SQLException {
+ return delegate.getLargeUpdateCount();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long getLargeMaxRows() throws SQLException {
+ return delegate.getLargeMaxRows();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setLargeMaxRows(long max) throws SQLException {
+ delegate.setLargeMaxRows(max);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long[] executeLargeBatch() throws SQLException {
+ return delegate.executeLargeBatch();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long executeLargeUpdate(String sql) throws SQLException {
+ return delegate.executeLargeUpdate(sql);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
+ return delegate.executeLargeUpdate(sql, autoGeneratedKeys);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
+ return delegate.executeLargeUpdate(sql, columnIndexes);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
+ return delegate.executeLargeUpdate(sql, columnNames);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public <T> T unwrap(Class<T> iface) throws SQLException {
+ if (iface == PreparedStatementExImpl.class || iface == PreparedStatementEx.class)
+ return (T)this;
+
+ return delegate.unwrap(iface);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return iface == PreparedStatementExImpl.class
+ || iface == PreparedStatementEx.class
+ || delegate.isWrapperFor(iface);
+ }
+
+ /** {@inheritDoc} */
+ @Override public @Nullable <T> T meta(int id) {
+ return meta != null && id < meta.length ? (T)meta[id] : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void putMeta(int id, Object metaObj) {
+ if (meta == null)
+ meta = new Object[id + 1];
+ else if (meta.length <= id)
+ meta = Arrays.copyOf(meta, id + 1);
+
+ meta[id] = metaObj;
+ }
+
+ /**
+ *
+ * @param stmt Prepared statement to wrap.
+ * @return Wrapped statement.
+ */
+ public static PreparedStatement wrap(@NotNull PreparedStatement stmt) {
+ if (stmt.getClass() == PreparedStatementExImpl.class)
+ return stmt;
+
+ return new PreparedStatementExImpl(stmt);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java
new file mode 100644
index 0000000..b635eac
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java
@@ -0,0 +1,47 @@
+/*
+ * 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.query.h2;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
+
+/** */
+class RebuildIndexFromHashClosure implements SchemaIndexCacheVisitorClosure {
+ /** */
+ private final GridCacheQueryManager qryMgr;
+
+ /** MVCC status flag. */
+ private final boolean mvccEnabled;
+
+ /**
+ * @param qryMgr Query manager.
+ * @param mvccEnabled MVCC status flag.
+ */
+ RebuildIndexFromHashClosure(GridCacheQueryManager qryMgr, boolean mvccEnabled) {
+ this.qryMgr = qryMgr;
+ this.mvccEnabled = mvccEnabled;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
+ // prevRowAvailable is always true with MVCC on, and always false *on index rebuild* with MVCC off.
+ qryMgr.store(row, null, mvccEnabled);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ResultSetEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ResultSetEnlistFuture.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ResultSetEnlistFuture.java
new file mode 100644
index 0000000..ee1c0fa
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ResultSetEnlistFuture.java
@@ -0,0 +1,136 @@
+/*
+ * 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.query.h2;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.DhtLockFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Future to process whole local result set of SELECT FOR UPDATE query.
+ */
+public interface ResultSetEnlistFuture extends DhtLockFuture<Long> {
+ /**
+ * @param rs Result set.
+ * @return Update source.
+ */
+ static UpdateSourceIterator<?> createIterator(ResultSet rs) {
+ return new ResultSetUpdateSourceIteratorWrapper(rs);
+ }
+
+ /** */
+ void init();
+
+ /**
+ *
+ * @param nearNodeId Near node ID.
+ * @param nearLockVer Near lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param threadId Thread ID.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini future id.
+ * @param parts Partitions.
+ * @param tx Transaction.
+ * @param timeout Lock acquisition timeout.
+ * @param cctx Cache context.
+ * @param rs Result set to process.
+ * @return Result set enlist future.
+ */
+ static ResultSetEnlistFuture future(UUID nearNodeId, GridCacheVersion nearLockVer,
+ MvccSnapshot mvccSnapshot, long threadId, IgniteUuid nearFutId, int nearMiniId, @Nullable int[] parts,
+ GridDhtTxLocalAdapter tx, long timeout, GridCacheContext<?, ?> cctx, ResultSet rs) {
+
+ if (tx.near())
+ return new NearResultSetEnlistFuture(nearNodeId, nearLockVer, mvccSnapshot, threadId, nearFutId, nearMiniId, parts, tx, timeout, cctx, rs);
+ else
+ return new DhtResultSetEnlistFuture(nearNodeId, nearLockVer, mvccSnapshot, threadId, nearFutId, nearMiniId, parts, tx, timeout, cctx, rs);
+ }
+
+ /**
+ *
+ */
+ public static class ResultSetUpdateSourceIteratorWrapper implements UpdateSourceIterator<Object> {
+ /** */
+ private static final long serialVersionUID = -8745196216234843471L;
+
+ /** */
+ private final ResultSet rs;
+
+ /** */
+ private Boolean hasNext;
+
+ /** */
+ private int keyColIdx;
+
+ /**
+ * @param rs Result set.
+ */
+ public ResultSetUpdateSourceIteratorWrapper(ResultSet rs) {
+ this.rs = rs;
+ keyColIdx = -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public EnlistOperation operation() {
+ return EnlistOperation.LOCK;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasNextX() {
+ try {
+ if (hasNext == null)
+ hasNext = rs.next();
+
+ return hasNext;
+ }
+ catch (SQLException e) {
+ throw new IgniteSQLException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object nextX() {
+ if (!hasNextX())
+ throw new NoSuchElementException();
+
+ try {
+ if (keyColIdx == -1)
+ keyColIdx = rs.getMetaData().getColumnCount();
+
+ return rs.getObject(keyColIdx);
+ }
+ catch (SQLException e) {
+ throw new IgniteSQLException(e);
+ }
+ finally {
+ hasNext = null;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPool.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPool.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPool.java
new file mode 100644
index 0000000..25daa23
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ThreadLocalObjectPool.java
@@ -0,0 +1,103 @@
+/*
+ * 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.query.h2;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.function.Supplier;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Special pool for managing limited number objects for further reuse.
+ * This pool maintains separate object bag for each thread by means of {@link ThreadLocal}.
+ * <p>
+ * If object is borrowed on one thread and recycled on different then it will be returned to
+ * recycling thread bag. For thread-safe use either pooled objects should be thread-safe or
+ * <i>happens-before</i> should be established between borrowing object and subsequent recycling.
+ *
+ * @param <E> pooled objects type
+ */
+public final class ThreadLocalObjectPool<E extends AutoCloseable> {
+ /**
+ * Wrapper for a pooled object with capability to return the object to a pool.
+ *
+ * @param <T> enclosed object type
+ */
+ public static class Reusable<T extends AutoCloseable> {
+ /** */
+ private final ThreadLocalObjectPool<T> pool;
+ /** */
+ private final T object;
+
+ /** */
+ private Reusable(ThreadLocalObjectPool<T> pool, T object) {
+ this.pool = pool;
+ this.object = object;
+ }
+
+ /**
+ * @return enclosed object
+ */
+ public T object() {
+ return object;
+ }
+
+ /**
+ * Returns an object to a pool or closes it if the pool is already full.
+ */
+ public void recycle() {
+ Queue<Reusable<T>> bag = pool.bag.get();
+ if (bag.size() < pool.poolSize)
+ bag.add(this);
+ else
+ U.closeQuiet(object);
+ }
+ }
+
+ /** */
+ private final Supplier<E> objectFactory;
+ /** */
+ private final ThreadLocal<Queue<Reusable<E>>> bag = ThreadLocal.withInitial(LinkedList::new);
+ /** */
+ private final int poolSize;
+
+ /**
+ * @param objectFactory factory used for new objects creation
+ * @param poolSize number of objects which pool can contain
+ */
+ public ThreadLocalObjectPool(Supplier<E> objectFactory, int poolSize) {
+ this.objectFactory = objectFactory;
+ this.poolSize = poolSize;
+ }
+
+ /**
+ * Picks an object from the pool if one is present or creates new one otherwise.
+ * Returns an object wrapper which could be returned to the pool.
+ *
+ * @return reusable object wrapper
+ */
+ public Reusable<E> borrow() {
+ Reusable<E> pooled = bag.get().poll();
+ return pooled != null ? pooled : new Reusable<>(this, objectFactory.get());
+ }
+
+ /** Visible for test */
+ int bagSize() {
+ return bag.get().size();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
index 546f5bb..5d877cd 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
@@ -19,6 +19,7 @@
package org.apache.ignite.internal.processors.query.h2.database;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -26,9 +27,12 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
@@ -84,31 +88,30 @@ public class H2PkHashIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public Cursor find(Session ses, final SearchRow lower, final SearchRow upper) {
- IndexingQueryFilter f = threadLocalFilter();
- IndexingQueryCacheFilter p = null;
+ IndexingQueryCacheFilter filter = null;
+ MvccSnapshot mvccSnapshot = null;
- if (f != null) {
- String cacheName = getTable().cacheName();
+ GridH2QueryContext qctx = GridH2QueryContext.get();
- p = f.forCache(cacheName);
+ if (qctx != null) {
+ IndexingQueryFilter f = qctx.filter();
+ filter = f != null ? f.forCache(getTable().cacheName()) : null;
+ mvccSnapshot = qctx.mvccSnapshot();
}
- KeyCacheObject lowerObj = null;
- KeyCacheObject upperObj = null;
+ assert !cctx.mvccEnabled() || mvccSnapshot != null;
- if (lower != null)
- lowerObj = cctx.toCacheKeyObject(lower.getValue(0).getObject());
-
- if (upper != null)
- upperObj = cctx.toCacheKeyObject(upper.getValue(0).getObject());
+ KeyCacheObject lowerObj = lower != null ? cctx.toCacheKeyObject(lower.getValue(0).getObject()) : null;
+ KeyCacheObject upperObj = upper != null ? cctx.toCacheKeyObject(upper.getValue(0).getObject()) : null;
try {
- List<GridCursor<? extends CacheDataRow>> cursors = new ArrayList<>();
+ Collection<GridCursor<? extends CacheDataRow>> cursors = new ArrayList<>();
for (IgniteCacheOffheapManager.CacheDataStore store : cctx.offheap().cacheDataStores())
- cursors.add(store.cursor(cctx.cacheId(), lowerObj, upperObj));
+ if (filter == null || filter.applyPartition(store.partId()))
+ cursors.add(store.cursor(cctx.cacheId(), lowerObj, upperObj, null, mvccSnapshot));
- return new H2Cursor(new CompositeGridCursor<>(cursors.iterator()), p);
+ return new H2Cursor(cursors.iterator());
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -124,7 +127,6 @@ public class H2PkHashIndex extends GridH2IndexBase {
@SuppressWarnings("StatementWithEmptyBody")
@Override public GridH2Row put(GridH2Row row) {
// Should not be called directly. Rows are inserted into underlying cache data stores.
-
assert false;
throw DbException.getUnsupportedException("put");
@@ -192,28 +194,29 @@ public class H2PkHashIndex extends GridH2IndexBase {
*/
private class H2Cursor implements Cursor {
/** */
- final GridCursor<? extends CacheDataRow> cursor;
+ private final GridH2RowDescriptor desc;
/** */
- final IndexingQueryCacheFilter filter;
+ private final Iterator<GridCursor<? extends CacheDataRow>> iter;
+
+ /** */
+ private GridCursor<? extends CacheDataRow> curr;
/**
- * @param cursor Cursor.
- * @param filter Filter.
+ * @param iter Cursors iterator.
*/
- private H2Cursor(GridCursor<? extends CacheDataRow> cursor, IndexingQueryCacheFilter filter) {
- assert cursor != null;
+ private H2Cursor(Iterator<GridCursor<? extends CacheDataRow>> iter) {
+ assert iter != null;
- this.cursor = cursor;
- this.filter = filter;
+ this.iter = iter;
+
+ desc = tbl.rowDescriptor();
}
/** {@inheritDoc} */
@Override public Row get() {
try {
- CacheDataRow dataRow = cursor.get();
-
- return tbl.rowDescriptor().createRow(dataRow);
+ return desc.createRow(curr.get());
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -228,13 +231,13 @@ public class H2PkHashIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public boolean next() {
try {
- while (cursor.next()) {
- if (filter == null)
- return true;
+ if (curr != null && curr.next())
+ return true;
- CacheDataRow dataRow = cursor.get();
+ while (iter.hasNext()) {
+ curr = iter.next();
- if (filter.applyPartition(dataRow.partition()))
+ if (curr.next())
return true;
}
@@ -250,45 +253,4 @@ public class H2PkHashIndex extends GridH2IndexBase {
throw DbException.getUnsupportedException("previous");
}
}
-
- /**
- *
- */
- private static class CompositeGridCursor<T> implements GridCursor<T> {
- /** */
- private final Iterator<GridCursor<? extends T>> iter;
-
- /** */
- private GridCursor<? extends T> curr;
-
- /**
- *
- */
- public CompositeGridCursor(Iterator<GridCursor<? extends T>> iter) {
- this.iter = iter;
-
- if (iter.hasNext())
- curr = iter.next();
- }
-
- /** {@inheritDoc} */
- @Override public boolean next() throws IgniteCheckedException {
- if (curr.next())
- return true;
-
- while (iter.hasNext()) {
- curr = iter.next();
-
- if (curr.next())
- return true;
- }
-
- return false;
- }
-
- /** {@inheritDoc} */
- @Override public T get() throws IgniteCheckedException {
- return curr.get();
- }
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
index 40b9b0a..724de7e 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
@@ -18,9 +18,10 @@
package org.apache.ignite.internal.processors.query.h2.database;
import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
@@ -60,17 +61,23 @@ public class H2RowFactory {
rowBuilder.initFromLink(cctx.group(), CacheDataRowAdapter.RowData.FULL);
- GridH2Row row;
-
- try {
- row = rowDesc.createRow(rowBuilder);
- }
- catch (IgniteCheckedException e) {
- throw new IgniteException(e);
- }
+ GridH2Row row = rowDesc.createRow(rowBuilder);
assert row.version() != null;
return row;
}
+
+ /**
+ * @param link Link.
+ * @param mvccCrdVer Mvcc coordinator version.
+ * @param mvccCntr Mvcc counter.
+ * @param mvccOpCntr Mvcc operation counter.
+ * @return Row.
+ * @throws IgniteCheckedException If failed.
+ */
+ public GridH2Row getMvccRow(long link, long mvccCrdVer, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+ return rowDesc.createRow(new MvccDataRow(cctx.group(),0, link,
+ PageIdUtils.partId(PageIdUtils.pageId(link)),null, mvccCrdVer, mvccCntr, mvccOpCntr));
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
index 424969e..ce40df0 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
@@ -21,9 +21,9 @@ import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageIdUtils;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusMetaIO;
@@ -35,8 +35,9 @@ import org.apache.ignite.internal.processors.query.h2.database.io.H2ExtrasLeafIO
import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.value.Value;
@@ -44,7 +45,7 @@ import org.jetbrains.annotations.Nullable;
/**
*/
-public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
+public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
/** */
private final H2RowFactory rowStore;
@@ -61,6 +62,9 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
private final int[] columnIds;
/** */
+ private final boolean mvccEnabled;
+
+ /** */
private final Comparator<Value> comp = new Comparator<Value>() {
@Override public int compare(Value o1, Value o2) {
return compareValues(o1, o2);
@@ -82,6 +86,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
* @param metaPageId Meta page ID.
* @param initNew Initialize new index.
* @param rowCache Row cache.
+ * @param mvccEnabled Mvcc flag.
* @param failureProcessor if the tree is corrupted.
* @throws IgniteCheckedException If failed.
*/
@@ -98,6 +103,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
IndexColumn[] cols,
List<InlineIndexHelper> inlineIdxs,
int inlineSize,
+ boolean mvccEnabled,
@Nullable H2RowCache rowCache,
@Nullable FailureProcessor failureProcessor
) throws IgniteCheckedException {
@@ -109,6 +115,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
}
this.inlineSize = inlineSize;
+ this.mvccEnabled = mvccEnabled;
assert rowStore != null;
@@ -121,9 +128,9 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
for (int i = 0; i < cols.length; i++)
columnIds[i] = cols[i].column.getColumnId();
- this.rowCache = rowCache;
+ setIos(H2ExtrasInnerIO.getVersions(inlineSize, mvccEnabled), H2ExtrasLeafIO.getVersions(inlineSize, mvccEnabled));
- setIos(H2ExtrasInnerIO.getVersions(inlineSize), H2ExtrasLeafIO.getVersions(inlineSize));
+ this.rowCache = rowCache;
initTree(initNew, inlineSize);
}
@@ -152,21 +159,34 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
return rowStore.getRow(link);
}
- /** {@inheritDoc} */
- @Override protected GridH2Row getRow(BPlusIO<SearchRow> io, long pageAddr, int idx, Object filter)
- throws IgniteCheckedException {
- if (filter != null) {
- // Filter out not interesting partitions without deserializing the row.
- IndexingQueryCacheFilter filter0 = (IndexingQueryCacheFilter)filter;
+ /**
+ * Create row from link.
+ *
+ * @param link Link.
+ * @param mvccOpCntr
+ * @return Row.
+ * @throws IgniteCheckedException if failed.
+ */
+ public GridH2Row createRowFromLink(long link, long mvccCrdVer, long mvccCntr, int mvccOpCntr) throws IgniteCheckedException {
+ if (rowCache != null) {
+ GridH2Row row = rowCache.get(link);
- long link = ((H2RowLinkIO)io).getLink(pageAddr, idx);
+ if (row == null) {
+ row = rowStore.getMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
- int part = PageIdUtils.partId(PageIdUtils.pageId(link));
+ if (row instanceof GridH2KeyValueRowOnheap)
+ rowCache.put((GridH2KeyValueRowOnheap)row);
+ }
- if (!filter0.applyPartition(part))
- return null;
+ return row;
}
+ else
+ return rowStore.getMvccRow(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+ }
+ /** {@inheritDoc} */
+ @Override public GridH2Row getRow(BPlusIO<GridH2SearchRow> io, long pageAddr, int idx, Object ignore)
+ throws IgniteCheckedException {
return (GridH2Row)io.getLookupRow(this, pageAddr, idx);
}
@@ -206,8 +226,8 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
/** {@inheritDoc} */
@SuppressWarnings("ForLoopReplaceableByForEach")
- @Override protected int compare(BPlusIO<SearchRow> io, long pageAddr, int idx,
- SearchRow row) throws IgniteCheckedException {
+ @Override protected int compare(BPlusIO<GridH2SearchRow> io, long pageAddr, int idx,
+ GridH2SearchRow row) throws IgniteCheckedException {
if (inlineSize() == 0)
return compareRows(getRow(io, pageAddr, idx), row);
else {
@@ -242,7 +262,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
}
if (lastIdxUsed == cols.length)
- return 0;
+ return mvccCompare((H2RowLinkIO)io, pageAddr, idx, row);
SearchRow rowData = getRow(io, pageAddr, idx);
@@ -254,7 +274,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
if (v2 == null) {
// Can't compare further.
- return 0;
+ return mvccCompare((H2RowLinkIO)io, pageAddr, idx, row);
}
Value v1 = rowData.getValue(idx0);
@@ -265,7 +285,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
return InlineIndexHelper.fixSort(c, col.sortType);
}
- return 0;
+ return mvccCompare((H2RowLinkIO)io, pageAddr, idx, row);
}
}
@@ -276,7 +296,8 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
* @param r2 Row 2.
* @return Compare result: see {@link Comparator#compare(Object, Object)} for values.
*/
- public int compareRows(SearchRow r1, SearchRow r2) {
+ public int compareRows(GridH2SearchRow r1, GridH2SearchRow r2) {
+ assert !mvccEnabled || r2.indexSearchRow() || MvccUtils.mvccVersionIsValid(r2.mvccCoordinatorVersion(), r2.mvccCounter()) : r2;
if (r1 == r2)
return 0;
@@ -288,7 +309,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
if (v1 == null || v2 == null) {
// Can't compare further.
- return 0;
+ return mvccCompare(r1, r2);
}
int c = compareValues(v1, v2);
@@ -297,7 +318,47 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
return InlineIndexHelper.fixSort(c, cols[i].sortType);
}
- return 0;
+ return mvccCompare(r1, r2);
+ }
+
+ /**
+ * @param io IO.
+ * @param pageAddr Page address.
+ * @param idx Item index.
+ * @param r2 Search row.
+ * @return Comparison result.
+ */
+ private int mvccCompare(H2RowLinkIO io, long pageAddr, int idx, GridH2SearchRow r2) {
+ if (!mvccEnabled || r2.indexSearchRow())
+ return 0;
+
+ long crd = io.getMvccCoordinatorVersion(pageAddr, idx);
+ long cntr = io.getMvccCounter(pageAddr, idx);
+ int opCntr = io.getMvccOperationCounter(pageAddr, idx);
+
+ assert MvccUtils.mvccVersionIsValid(crd, cntr, opCntr);
+
+ return -MvccUtils.compare(crd, cntr, opCntr, r2); // descending order
+ }
+
+ /**
+ * @param r1 First row.
+ * @param r2 Second row.
+ * @return Comparison result.
+ */
+ private int mvccCompare(GridH2SearchRow r1, GridH2SearchRow r2) {
+ if (!mvccEnabled || r2.indexSearchRow())
+ return 0;
+
+ long crdVer1 = r1.mvccCoordinatorVersion();
+ long crdVer2 = r2.mvccCoordinatorVersion();
+
+ int c = -Long.compare(crdVer1, crdVer2);
+
+ if (c != 0)
+ return c;
+
+ return -Long.compare(r1.mvccCounter(), r2.mvccCounter());
}
/**
@@ -306,4 +367,9 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
* @return Comparison result.
*/
public abstract int compareValues(Value v1, Value v2);
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2Tree.class, this, "super", super.toString());
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java
new file mode 100644
index 0000000..e583546
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeFilterClosure.java
@@ -0,0 +1,109 @@
+/*
+ * 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.query.h2.database;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+import org.apache.ignite.internal.transactions.IgniteTxMvccVersionCheckedException;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
+
+import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.isVisible;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVersionIsValid;
+
+/**
+ *
+ */
+public class H2TreeFilterClosure implements H2Tree.TreeRowClosure<GridH2SearchRow, GridH2Row> {
+ /** */
+ private final MvccSnapshot mvccSnapshot;
+
+ /** */
+ private final IndexingQueryCacheFilter filter;
+
+ /** */
+ private final GridCacheContext cctx;
+
+ /**
+ * @param filter Cache filter.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param cctx Cache context.
+ */
+ public H2TreeFilterClosure(IndexingQueryCacheFilter filter, MvccSnapshot mvccSnapshot, GridCacheContext cctx) {
+ assert (filter != null || mvccSnapshot != null) && cctx != null ;
+
+ this.filter = filter;
+ this.mvccSnapshot = mvccSnapshot;
+ this.cctx = cctx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<GridH2SearchRow, GridH2Row> tree, BPlusIO<GridH2SearchRow> io,
+ long pageAddr, int idx) throws IgniteCheckedException {
+ return (filter == null || applyFilter((H2RowLinkIO)io, pageAddr, idx))
+ && (mvccSnapshot == null || applyMvcc((H2RowLinkIO)io, pageAddr, idx));
+ }
+
+ /**
+ * @param io Row IO.
+ * @param pageAddr Page address.
+ * @param idx Item index.
+ * @return {@code True} if row passes the filter.
+ */
+ private boolean applyFilter(H2RowLinkIO io, long pageAddr, int idx) {
+ assert filter != null;
+
+ return filter.applyPartition(PageIdUtils.partId(pageId(io.getLink(pageAddr, idx))));
+ }
+
+ /**
+ * @param io Row IO.
+ * @param pageAddr Page address.
+ * @param idx Item index.
+ * @return {@code True} if row passes the filter.
+ */
+ private boolean applyMvcc(H2RowLinkIO io, long pageAddr, int idx) throws IgniteCheckedException {
+ assert io.storeMvccInfo() : io;
+
+ long rowCrdVer = io.getMvccCoordinatorVersion(pageAddr, idx);
+ long rowCntr = io.getMvccCounter(pageAddr, idx);
+ int rowOpCntr = io.getMvccOperationCounter(pageAddr, idx);
+
+ assert mvccVersionIsValid(rowCrdVer, rowCntr, rowOpCntr);
+
+ try {
+ return isVisible(cctx, mvccSnapshot, rowCrdVer, rowCntr, rowOpCntr, io.getLink(pageAddr, idx));
+ }
+ catch (IgniteTxMvccVersionCheckedException ignored) {
+ return false; // The row is going to be removed.
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2TreeFilterClosure.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 393ca3b..ab6f42a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -20,36 +20,34 @@ package org.apache.ignite.internal.processors.query.h2.database;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.NoSuchElementException;
-
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteSystemProperties;
-import org.apache.ignite.internal.pagemem.PageIdUtils;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.persistence.RootPage;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.h2.H2Cursor;
import org.apache.ignite.internal.processors.query.h2.H2RowCache;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Cursor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
-import org.apache.ignite.internal.processors.query.h2.database.io.H2RowLinkIO;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
import org.apache.ignite.internal.util.IgniteTree;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
+import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.index.SingleRowCursor;
import org.h2.message.DbException;
-import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
@@ -139,6 +137,7 @@ public class H2TreeIndex extends GridH2IndexBase {
cols,
inlineIdxs,
computeInlineSize(inlineIdxs, inlineSize),
+ cctx.mvccEnabled(),
rowCache,
cctx.kernalContext().failure()) {
@Override public int compareValues(Value v1, Value v2) {
@@ -191,21 +190,22 @@ public class H2TreeIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public Cursor find(Session ses, SearchRow lower, SearchRow upper) {
try {
- IndexingQueryCacheFilter filter = partitionFilter(threadLocalFilter());
+ assert lower == null || lower instanceof GridH2SearchRow : lower;
+ assert upper == null || upper instanceof GridH2SearchRow : upper;
int seg = threadLocalSegment();
H2Tree tree = treeForRead(seg);
- if (indexType.isPrimaryKey() && lower != null && upper != null && tree.compareRows(lower, upper) == 0) {
- GridH2Row row = tree.findOne(lower, filter);
+ if (!cctx.mvccEnabled() && indexType.isPrimaryKey() && lower != null && upper != null &&
+ tree.compareRows((GridH2SearchRow)lower, (GridH2SearchRow)upper) == 0) {
+ GridH2Row row = tree.findOne((GridH2SearchRow)lower, filter(GridH2QueryContext.get()), null);
- return (row == null) ? EMPTY_CURSOR : new SingleRowCursor(row);
+ return (row == null) ? GridH2Cursor.EMPTY : new SingleRowCursor(row);
}
else {
- GridCursor<GridH2Row> cursor = tree.find(lower, upper, filter);
-
- return new H2Cursor(cursor);
+ return new H2Cursor(tree.find((GridH2SearchRow)lower,
+ (GridH2SearchRow)upper, filter(GridH2QueryContext.get()), null));
}
}
catch (IgniteCheckedException e) {
@@ -257,6 +257,8 @@ public class H2TreeIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public GridH2Row remove(SearchRow row) {
+ assert row instanceof GridH2SearchRow : row;
+
try {
InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs);
@@ -266,7 +268,7 @@ public class H2TreeIndex extends GridH2IndexBase {
assert cctx.shared().database().checkpointLockIsHeldByThread();
- return tree.remove(row);
+ return tree.remove((GridH2SearchRow)row);
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -279,6 +281,8 @@ public class H2TreeIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public boolean removex(SearchRow row) {
try {
+ assert row instanceof GridH2SearchRow : row;
+
InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs);
int seg = segmentForRow(row);
@@ -287,7 +291,7 @@ public class H2TreeIndex extends GridH2IndexBase {
assert cctx.shared().database().checkpointLockIsHeldByThread();
- return tree.removex(row);
+ return tree.removex((GridH2SearchRow)row);
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -315,9 +319,9 @@ public class H2TreeIndex extends GridH2IndexBase {
H2Tree tree = treeForRead(seg);
- BPlusTree.TreeRowClosure<SearchRow, GridH2Row> filter = filterClosure();
+ GridH2QueryContext qctx = GridH2QueryContext.get();
- return tree.size(filter);
+ return tree.size(filter(qctx));
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -337,13 +341,10 @@ public class H2TreeIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public Cursor findFirstOrLast(Session session, boolean b) {
try {
- int seg = threadLocalSegment();
+ H2Tree tree = treeForRead(threadLocalSegment());
+ GridH2QueryContext qctx = GridH2QueryContext.get();
- H2Tree tree = treeForRead(seg);
-
- GridH2Row row = b ? tree.findFirst(): tree.findLast();
-
- return new SingleRowCursor(row);
+ return new SingleRowCursor(b ? tree.findFirst(filter(qctx)): tree.findLast(filter(qctx)));
}
catch (IgniteCheckedException e) {
throw DbException.convert(e);
@@ -382,16 +383,13 @@ public class H2TreeIndex extends GridH2IndexBase {
@Override protected H2Cursor doFind0(
IgniteTree t,
@Nullable SearchRow first,
- boolean includeFirst,
@Nullable SearchRow last,
- IndexingQueryFilter filter) {
+ BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter) {
try {
- IndexingQueryCacheFilter pf = partitionFilter(filter);
-
- GridCursor<GridH2Row> range = t.find(first, last, pf);
+ GridCursor<GridH2Row> range = ((BPlusTree)t).find(first, last, filter, null);
if (range == null)
- range = GridH2IndexBase.EMPTY_CURSOR;
+ range = EMPTY_CURSOR;
return new H2Cursor(range);
}
@@ -400,6 +398,26 @@ public class H2TreeIndex extends GridH2IndexBase {
}
}
+ /** {@inheritDoc} */
+ @Override protected BPlusTree.TreeRowClosure<GridH2SearchRow, GridH2Row> filter(GridH2QueryContext qctx) {
+ if (qctx == null) {
+ assert !cctx.mvccEnabled();
+
+ return null;
+ }
+
+ IndexingQueryFilter f = qctx.filter();
+ IndexingQueryCacheFilter p = f == null ? null : f.forCache(getTable().cacheName());
+ MvccSnapshot v = qctx.mvccSnapshot();
+
+ assert !cctx.mvccEnabled() || v != null;
+
+ if(p == null && v == null)
+ return null;
+
+ return new H2TreeFilterClosure(p, v, cctx);
+ }
+
/**
* @param inlineIdxs Inline index helpers.
* @param cfgInlineSize Inline size from cache config.
@@ -457,63 +475,6 @@ public class H2TreeIndex extends GridH2IndexBase {
cctx.offheap().dropRootPageForIndex(cctx.cacheId(), name + "%" + segIdx);
}
- /**
- * Returns a filter which returns true for entries belonging to a particular partition.
- *
- * @param qryFilter Factory that creates a predicate for filtering entries for a particular cache.
- * @return The filter or null if the filter is not needed (e.g., if the cache is not partitioned).
- */
- @Nullable private IndexingQueryCacheFilter partitionFilter(@Nullable IndexingQueryFilter qryFilter) {
- if (qryFilter == null)
- return null;
-
- String cacheName = getTable().cacheName();
-
- return qryFilter.forCache(cacheName);
- }
-
- /**
- * An adapter from {@link IndexingQueryCacheFilter} to {@link BPlusTree.TreeRowClosure} which
- * filters entries that belong to the current partition.
- */
- private static class PartitionFilterTreeRowClosure implements BPlusTree.TreeRowClosure<SearchRow, GridH2Row> {
- /** Filter. */
- private final IndexingQueryCacheFilter filter;
-
- /**
- * Creates a {@link BPlusTree.TreeRowClosure} adapter based on the given partition filter.
- *
- * @param filter The partition filter.
- */
- public PartitionFilterTreeRowClosure(IndexingQueryCacheFilter filter) {
- this.filter = filter;
- }
-
- /** {@inheritDoc} */
- @Override public boolean apply(BPlusTree<SearchRow, GridH2Row> tree,
- BPlusIO<SearchRow> io, long pageAddr, int idx) throws IgniteCheckedException {
-
- H2RowLinkIO h2io = (H2RowLinkIO)io;
-
- return filter.applyPartition(
- PageIdUtils.partId(
- PageIdUtils.pageId(
- h2io.getLink(pageAddr, idx))));
- }
- }
-
- /**
- * Returns a filter to apply to rows in the current index to obtain only the
- * ones owned by the this cache.
- *
- * @return The filter, which returns true for rows owned by this cache.
- */
- @Nullable private BPlusTree.TreeRowClosure<SearchRow, GridH2Row> filterClosure() {
- final IndexingQueryCacheFilter filter = partitionFilter(threadLocalFilter());
-
- return filter != null ? new PartitionFilterTreeRowClosure(filter) : null;
- }
-
/** {@inheritDoc} */
@Override public void refreshColumnIds() {
super.refreshColumnIds();
@@ -528,29 +489,4 @@ public class H2TreeIndex extends GridH2IndexBase {
for (int pos = 0; pos < inlineHelpers.size(); ++pos)
inlineIdxs.set(pos, inlineHelpers.get(pos));
}
-
- /**
- * Empty cursor.
- */
- public static final Cursor EMPTY_CURSOR = new Cursor() {
- /** {@inheritDoc} */
- @Override public Row get() {
- throw DbException.convert(new NoSuchElementException("Empty cursor"));
- }
-
- /** {@inheritDoc} */
- @Override public SearchRow getSearchRow() {
- throw DbException.convert(new NoSuchElementException("Empty cursor"));
- }
-
- /** {@inheritDoc} */
- @Override public boolean next() {
- return false;
- }
-
- /** {@inheritDoc} */
- @Override public boolean previous() {
- return false;
- }
- };
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java
new file mode 100644
index 0000000..fbca917
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasInnerIO.java
@@ -0,0 +1,164 @@
+/*
+ * 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.query.h2.database.io;
+
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInnerIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
+import org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+
+/**
+ * Inner page for H2 row references.
+ */
+public abstract class AbstractH2ExtrasInnerIO extends BPlusInnerIO<GridH2SearchRow> implements H2RowLinkIO {
+ /** Payload size. */
+ protected final int payloadSize;
+
+ /** */
+ public static void register() {
+ register(false);
+
+ register(true);
+ }
+
+ /**
+ * @param mvcc Mvcc flag.
+ */
+ private static void register(boolean mvcc) {
+ short type = mvcc ? PageIO.T_H2_EX_REF_MVCC_INNER_START : PageIO.T_H2_EX_REF_INNER_START;
+
+ for (short payload = 1; payload <= PageIO.MAX_PAYLOAD_SIZE; payload++) {
+ IOVersions<? extends AbstractH2ExtrasInnerIO> io =
+ getVersions((short)(type + payload - 1), payload, mvcc);
+
+ PageIO.registerH2ExtraInner(io, mvcc);
+ }
+ }
+
+ /**
+ * @param payload Payload size.
+ * @param mvccEnabled Mvcc flag.
+ * @return IOVersions for given payload.
+ */
+ @SuppressWarnings("unchecked")
+ public static IOVersions<? extends BPlusInnerIO<GridH2SearchRow>> getVersions(int payload, boolean mvccEnabled) {
+ assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
+
+ if (payload == 0)
+ return mvccEnabled ? H2MvccInnerIO.VERSIONS : H2InnerIO.VERSIONS;
+ else
+ return (IOVersions<BPlusInnerIO<GridH2SearchRow>>)PageIO.getInnerVersions((short)(payload - 1), mvccEnabled);
+ }
+
+ /**
+ * @param type Type.
+ * @param payload Payload size.
+ * @param mvcc Mvcc flag.
+ * @return Instance of IO versions.
+ */
+ private static IOVersions<? extends AbstractH2ExtrasInnerIO> getVersions(short type, short payload, boolean mvcc) {
+ return new IOVersions<>(mvcc ? new H2MvccExtrasInnerIO(type, 1, payload) : new H2ExtrasInnerIO(type, 1, payload));
+ }
+
+ /**
+ * @param type Page type.
+ * @param ver Page format version.
+ * @param itemSize Item size.
+ * @param payloadSize Payload size.
+ */
+ AbstractH2ExtrasInnerIO(short type, int ver, int itemSize, int payloadSize) {
+ super(type, ver, true, itemSize + payloadSize);
+
+ this.payloadSize = payloadSize;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("ForLoopReplaceableByForEach")
+ @Override public final void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
+ GridH2Row row0 = (GridH2Row)row;
+
+ assert row0.link() != 0 : row0;
+
+ List<InlineIndexHelper> inlineIdxs = InlineIndexHelper.getCurrentInlineIndexes();
+
+ assert inlineIdxs != null : "no inline index helpers";
+
+
+ int fieldOff = 0;
+
+ for (int i = 0; i < inlineIdxs.size(); i++) {
+ InlineIndexHelper idx = inlineIdxs.get(i);
+
+ int size = idx.put(pageAddr, off + fieldOff, row.getValue(idx.columnIndex()), payloadSize - fieldOff);
+
+ if (size == 0)
+ break;
+
+ fieldOff += size;
+ }
+
+ H2IOUtils.storeRow(row0, pageAddr, off + payloadSize, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public final GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow, ?> tree, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ long link = getLink(pageAddr, idx);
+
+ assert link != 0;
+
+ if (storeMvccInfo()) {
+ long mvccCrdVer = getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = getMvccCounter(pageAddr, idx);
+ int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
+
+ return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+ }
+
+ return ((H2Tree)tree).createRowFromLink(link);
+ }
+
+ /** {@inheritDoc} */
+ @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+ int srcOff = srcIo.offset(srcIdx);
+
+ byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
+ long link = PageUtils.getLong(srcPageAddr, srcOff + payloadSize);
+
+ assert link != 0;
+
+ int dstOff = offset(dstIdx);
+
+ PageUtils.putBytes(dstPageAddr, dstOff, payload);
+
+ H2IOUtils.store(dstPageAddr, dstOff + payloadSize, srcIo, srcPageAddr, srcIdx, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public final long getLink(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + payloadSize);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java
new file mode 100644
index 0000000..9132795
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/io/AbstractH2ExtrasLeafIO.java
@@ -0,0 +1,161 @@
+/*
+ * 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.query.h2.database.io;
+
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
+import org.apache.ignite.internal.processors.query.h2.database.InlineIndexHelper;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SearchRow;
+
+/**
+ * Leaf page for H2 row references.
+ */
+public abstract class AbstractH2ExtrasLeafIO extends BPlusLeafIO<GridH2SearchRow> implements H2RowLinkIO {
+ /** Payload size. */
+ protected final int payloadSize;
+
+ /** */
+ public static void register() {
+ register(false);
+
+ register(true);
+ }
+
+ /**
+ * @param mvcc Mvcc flag.
+ */
+ private static void register(boolean mvcc) {
+ short type = mvcc ? PageIO.T_H2_EX_REF_MVCC_LEAF_START : PageIO.T_H2_EX_REF_LEAF_START;
+
+ for (short payload = 1; payload <= PageIO.MAX_PAYLOAD_SIZE; payload++) {
+ IOVersions<? extends AbstractH2ExtrasLeafIO> io =
+ getVersions((short)(type + payload - 1), payload, mvcc);
+
+ PageIO.registerH2ExtraLeaf(io, mvcc);
+ }
+ }
+
+ /**
+ * @param payload Payload size.
+ * @param mvccEnabled Mvcc flag.
+ * @return IOVersions for given payload.
+ */
+ @SuppressWarnings("unchecked")
+ public static IOVersions<? extends BPlusLeafIO<GridH2SearchRow>> getVersions(int payload, boolean mvccEnabled) {
+ assert payload >= 0 && payload <= PageIO.MAX_PAYLOAD_SIZE;
+
+ if (payload == 0)
+ return mvccEnabled ? H2MvccLeafIO.VERSIONS : H2LeafIO.VERSIONS;
+ else
+ return (IOVersions<BPlusLeafIO<GridH2SearchRow>>)PageIO.getLeafVersions((short)(payload - 1), mvccEnabled);
+ }
+
+ /**
+ * @param type Type.
+ * @param payload Payload size.
+ * @param mvcc Mvcc flag.
+ * @return Versions.
+ */
+ private static IOVersions<? extends AbstractH2ExtrasLeafIO> getVersions(short type, short payload, boolean mvcc) {
+ return new IOVersions<>(mvcc ? new H2MvccExtrasLeafIO(type, 1, payload) : new H2ExtrasLeafIO(type, 1, payload));
+ }
+
+ /**
+ * @param type Page type.
+ * @param ver Page format version.
+ * @param itemSize Item size.
+ * @param payloadSize Payload size.
+ */
+ AbstractH2ExtrasLeafIO(short type, int ver, int itemSize, int payloadSize) {
+ super(type, ver, itemSize + payloadSize);
+
+ this.payloadSize = payloadSize;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("ForLoopReplaceableByForEach")
+ @Override public final void storeByOffset(long pageAddr, int off, GridH2SearchRow row) {
+ GridH2Row row0 = (GridH2Row)row;
+
+ assert row0.link() != 0;
+
+ List<InlineIndexHelper> inlineIdxs = InlineIndexHelper.getCurrentInlineIndexes();
+
+ assert inlineIdxs != null : "no inline index helpers";
+
+ int fieldOff = 0;
+
+ for (int i = 0; i < inlineIdxs.size(); i++) {
+ InlineIndexHelper idx = inlineIdxs.get(i);
+
+ int size = idx.put(pageAddr, off + fieldOff, row.getValue(idx.columnIndex()), payloadSize - fieldOff);
+
+ if (size == 0)
+ break;
+
+ fieldOff += size;
+ }
+
+ H2IOUtils.storeRow(row0, pageAddr, off + payloadSize, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public final void store(long dstPageAddr, int dstIdx, BPlusIO<GridH2SearchRow> srcIo, long srcPageAddr, int srcIdx) {
+ int srcOff = srcIo.offset(srcIdx);
+
+ byte[] payload = PageUtils.getBytes(srcPageAddr, srcOff, payloadSize);
+ long link = PageUtils.getLong(srcPageAddr, srcOff + payloadSize);
+
+ assert link != 0;
+
+ int dstOff = offset(dstIdx);
+
+ PageUtils.putBytes(dstPageAddr, dstOff, payload);
+
+ H2IOUtils.store(dstPageAddr, dstOff + payloadSize, srcIo, srcPageAddr, srcIdx, storeMvccInfo());
+ }
+
+ /** {@inheritDoc} */
+ @Override public final GridH2SearchRow getLookupRow(BPlusTree<GridH2SearchRow, ?> tree, long pageAddr, int idx)
+ throws IgniteCheckedException {
+ long link = getLink(pageAddr, idx);
+
+ if (storeMvccInfo()) {
+ long mvccCrdVer = getMvccCoordinatorVersion(pageAddr, idx);
+ long mvccCntr = getMvccCounter(pageAddr, idx);
+ int mvccOpCntr = getMvccOperationCounter(pageAddr, idx);
+
+ return ((H2Tree)tree).createRowFromLink(link, mvccCrdVer, mvccCntr, mvccOpCntr);
+ }
+
+ return ((H2Tree)tree).createRowFromLink(link);
+ }
+
+ /** {@inheritDoc} */
+ @Override public final long getLink(long pageAddr, int idx) {
+ return PageUtils.getLong(pageAddr, offset(idx) + payloadSize);
+ }
+}
[34/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistFuture.java
new file mode 100644
index 0000000..dd30855
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistFuture.java
@@ -0,0 +1,140 @@
+/*
+ * 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.distributed.dht;
+
+import java.util.Objects;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.GridQueryCancel;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Cache lock future.
+ */
+public final class GridDhtTxQueryEnlistFuture extends GridDhtTxAbstractEnlistFuture {
+ /** Involved cache ids. */
+ private final int[] cacheIds;
+
+ /** Schema name. */
+ private final String schema;
+
+ /** Query string. */
+ private final String qry;
+
+ /** Query parameters. */
+ private final Object[] params;
+
+ /** Flags. */
+ private final int flags;
+
+ /** Fetch page size. */
+ private final int pageSize;
+
+ /**
+ * @param nearNodeId Near node ID.
+ * @param nearLockVer Near lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param threadId Thread ID.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini future id.
+ * @param tx Transaction.
+ * @param cacheIds Involved cache ids.
+ * @param parts Partitions.
+ * @param schema Schema name.
+ * @param qry Query string.
+ * @param params Query parameters.
+ * @param flags Flags.
+ * @param pageSize Fetch page size.
+ * @param timeout Lock acquisition timeout.
+ * @param cctx Cache context.
+ */
+ public GridDhtTxQueryEnlistFuture(
+ UUID nearNodeId,
+ GridCacheVersion nearLockVer,
+ MvccSnapshot mvccSnapshot,
+ long threadId,
+ IgniteUuid nearFutId,
+ int nearMiniId,
+ GridDhtTxLocalAdapter tx,
+ int[] cacheIds,
+ int[] parts,
+ String schema,
+ String qry,
+ Object[] params,
+ int flags,
+ int pageSize,
+ long timeout,
+ GridCacheContext<?, ?> cctx) {
+ super(nearNodeId,
+ nearLockVer,
+ mvccSnapshot,
+ threadId,
+ nearFutId,
+ nearMiniId,
+ parts,
+ tx,
+ timeout,
+ cctx);
+
+ assert timeout >= 0;
+ assert nearNodeId != null;
+ assert nearLockVer != null;
+ assert threadId == tx.threadId();
+
+ this.cacheIds = cacheIds;
+ this.schema = schema;
+ this.qry = qry;
+ this.params = params;
+ this.flags = flags;
+ this.pageSize = pageSize;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected UpdateSourceIterator<?> createIterator() throws IgniteCheckedException {
+ return cctx.kernalContext().query().prepareDistributedUpdate(cctx, cacheIds, parts, schema, qry,
+ params, flags, pageSize, 0, tx.topologyVersionSnapshot(), mvccSnapshot, new GridQueryCancel());
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ GridDhtTxQueryEnlistFuture future = (GridDhtTxQueryEnlistFuture)o;
+
+ return Objects.equals(futId, future.futId);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return futId.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridDhtTxQueryEnlistFuture.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistRequest.java
new file mode 100644
index 0000000..650e1dc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistRequest.java
@@ -0,0 +1,404 @@
+/*
+ * 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.distributed.dht;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridDirectCollection;
+import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.CacheObjectContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class GridDhtTxQueryEnlistRequest extends GridCacheIdMessage {
+ /** */
+ private static final long serialVersionUID = 5103887309729425173L;
+
+ /** */
+ private IgniteUuid dhtFutId;
+
+ /** */
+ private int batchId;
+
+ /** DHT tx version. */
+ private GridCacheVersion lockVer;
+
+ /** */
+ private EnlistOperation op;
+
+ /** */
+ private int mvccOpCnt;
+
+ /** */
+ @GridDirectCollection(KeyCacheObject.class)
+ private List<KeyCacheObject> keys;
+
+ /** */
+ @GridDirectCollection(Message.class)
+ private List<Message> vals;
+
+ /** */
+ private GridLongList updCntrs;
+
+ /**
+ *
+ */
+ public GridDhtTxQueryEnlistRequest() {
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param dhtFutId DHT future id.
+ * @param lockVer Lock version.
+ * @param op Operation.
+ * @param batchId Batch id.
+ * @param mvccOpCnt Mvcc operation counter.
+ * @param keys Keys.
+ * @param vals Values.
+ * @param updCntrs Update counters.
+ */
+ GridDhtTxQueryEnlistRequest(int cacheId,
+ IgniteUuid dhtFutId,
+ GridCacheVersion lockVer,
+ EnlistOperation op,
+ int batchId,
+ int mvccOpCnt,
+ List<KeyCacheObject> keys,
+ List<Message> vals,
+ GridLongList updCntrs) {
+ this.cacheId = cacheId;
+ this.dhtFutId = dhtFutId;
+ this.lockVer = lockVer;
+ this.op = op;
+ this.batchId = batchId;
+ this.mvccOpCnt = mvccOpCnt;
+ this.keys = keys;
+ this.vals = vals;
+ this.updCntrs = updCntrs;
+ }
+
+ /**
+ * Returns request rows number.
+ *
+ * @return Request rows number.
+ */
+ public int batchSize() {
+ return keys == null ? 0 : keys.size();
+ }
+
+ /**
+ * @return Dht future id.
+ */
+ public IgniteUuid dhtFutureId() {
+ return dhtFutId;
+ }
+
+ /**
+ * @return Lock version.
+ */
+ public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ /**
+ * @return Mvcc operation counter.
+ */
+ public int operationCounter() {
+ return mvccOpCnt;
+ }
+
+ /**
+ * @return Operation.
+ */
+ public EnlistOperation op() {
+ return op;
+ }
+
+ /**
+ * @return Keys.
+ */
+ public List<KeyCacheObject> keys() {
+ return keys;
+ }
+
+ /**
+ * @return Values.
+ */
+ public List<Message> values() {
+ return vals;
+ }
+
+ /**
+ * @return Update counters.
+ */
+ public GridLongList updateCounters() {
+ return updCntrs;
+ }
+
+ /**
+ * @return Batch id.
+ */
+ public int batchId() {
+ return batchId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addDeploymentInfo() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 155;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void prepareMarshal(GridCacheSharedContext ctx) throws IgniteCheckedException {
+ super.prepareMarshal(ctx);
+
+ CacheObjectContext objCtx = ctx.cacheContext(cacheId).cacheObjectContext();
+
+ if (keys != null) {
+ for (int i = 0; i < keys.size(); i++) {
+
+ keys.get(i).prepareMarshal(objCtx);
+
+ if (vals != null) {
+ Message val = vals.get(i);
+
+ if (val instanceof CacheObject)
+ ((CacheObject)val).prepareMarshal(objCtx);
+ else if (val instanceof CacheEntryInfoCollection) {
+ for (GridCacheEntryInfo entry : ((CacheEntryInfoCollection)val).infos()) {
+ CacheObject entryVal = entry.value();
+
+ if (entryVal != null)
+ entryVal.prepareMarshal(objCtx);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void finishUnmarshal(GridCacheSharedContext ctx, ClassLoader ldr) throws IgniteCheckedException {
+ super.finishUnmarshal(ctx, ldr);
+
+ CacheObjectContext objCtx = ctx.cacheContext(cacheId).cacheObjectContext();
+
+ if (keys != null) {
+ for (int i = 0; i < keys.size(); i++) {
+ keys.get(i).finishUnmarshal(objCtx, ldr);
+
+ if (vals != null) {
+ Message val = vals.get(i);
+
+ if (val instanceof CacheObject)
+ ((CacheObject)val).finishUnmarshal(objCtx, ldr);
+ else if (val instanceof CacheEntryInfoCollection) {
+ for (GridCacheEntryInfo entry : ((CacheEntryInfoCollection)val).infos()) {
+ CacheObject entryVal = entry.value();
+
+ if (entryVal != null)
+ entryVal.finishUnmarshal(objCtx, ldr);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeInt("batchId", batchId))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeIgniteUuid("dhtFutId", dhtFutId))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeCollection("keys", keys, MessageCollectionItemType.MSG))
+ return false;
+
+ writer.incrementState();
+
+ case 6:
+ if (!writer.writeMessage("lockVer", lockVer))
+ return false;
+
+ writer.incrementState();
+
+ case 7:
+ if (!writer.writeInt("mvccOpCnt", mvccOpCnt))
+ return false;
+
+ writer.incrementState();
+
+ case 8:
+ if (!writer.writeByte("op", op != null ? (byte)op.ordinal() : -1))
+ return false;
+
+ writer.incrementState();
+
+ case 9:
+ if (!writer.writeMessage("updCntrs", updCntrs))
+ return false;
+
+ writer.incrementState();
+
+ case 10:
+ if (!writer.writeCollection("vals", vals, MessageCollectionItemType.MSG))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ batchId = reader.readInt("batchId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ dhtFutId = reader.readIgniteUuid("dhtFutId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ keys = reader.readCollection("keys", MessageCollectionItemType.MSG);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 6:
+ lockVer = reader.readMessage("lockVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 7:
+ mvccOpCnt = reader.readInt("mvccOpCnt");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 8:
+ byte opOrd;
+
+ opOrd = reader.readByte("op");
+
+ if (!reader.isLastRead())
+ return false;
+
+ op = EnlistOperation.fromOrdinal(opOrd);
+
+ reader.incrementState();
+
+ case 9:
+ updCntrs = reader.readMessage("updCntrs");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 10:
+ vals = reader.readCollection("vals", MessageCollectionItemType.MSG);
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridDhtTxQueryEnlistRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 11;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridDhtTxQueryEnlistRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistResponse.java
new file mode 100644
index 0000000..4118489
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryEnlistResponse.java
@@ -0,0 +1,205 @@
+/*
+ * 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.distributed.dht;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class GridDhtTxQueryEnlistResponse extends GridCacheIdMessage {
+ /** */
+ private static final long serialVersionUID = -1510546400896574705L;
+
+ /** Future ID. */
+ private IgniteUuid futId;
+
+ /** */
+ private int batchId;
+
+ /** Error. */
+ @GridDirectTransient
+ private Throwable err;
+
+ /** Serialized error. */
+ private byte[] errBytes;
+
+ /**
+ *
+ */
+ public GridDhtTxQueryEnlistResponse() {
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param futId Future id.
+ * @param batchId Batch id.
+ * @param err Error.
+ */
+ GridDhtTxQueryEnlistResponse(int cacheId, IgniteUuid futId, int batchId,
+ Throwable err) {
+ this.cacheId = cacheId;
+ this.futId = futId;
+ this.batchId = batchId;
+ this.err = err;
+ }
+
+ /**
+ * @return Future id.
+ */
+ public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /**
+ * @return Batch id.
+ */
+ public int batchId() {
+ return batchId;
+ }
+
+ /**
+ * @return Error.
+ */
+ public Throwable error() {
+ return err;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void prepareMarshal(GridCacheSharedContext ctx) throws IgniteCheckedException {
+ super.prepareMarshal(ctx);
+
+ if (err != null && errBytes == null)
+ errBytes = U.marshal(ctx.marshaller(), err);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void finishUnmarshal(GridCacheSharedContext ctx, ClassLoader ldr) throws IgniteCheckedException {
+ super.finishUnmarshal(ctx, ldr);
+
+ if (errBytes != null)
+ err = U.unmarshal(ctx, errBytes, U.resolveClassLoader(ldr, ctx.gridConfig()));
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addDeploymentInfo() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 144;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 6;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeInt("batchId", batchId))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeByteArray("errBytes", errBytes))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeIgniteUuid("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ batchId = reader.readInt("batchId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ errBytes = reader.readByteArray("errBytes");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ futId = reader.readIgniteUuid("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridDhtTxQueryEnlistResponse.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridDhtTxQueryEnlistResponse.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryFirstEnlistRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryFirstEnlistRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryFirstEnlistRequest.java
new file mode 100644
index 0000000..5df1d58
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryFirstEnlistRequest.java
@@ -0,0 +1,370 @@
+/*
+ * 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.distributed.dht;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotWithoutTxs;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * First enlist request.
+ */
+public class GridDhtTxQueryFirstEnlistRequest extends GridDhtTxQueryEnlistRequest {
+ /** */
+ private static final long serialVersionUID = -7494735627739420176L;
+
+ /** Tx initiator. Primary node in case of remote DHT tx. */
+ private UUID subjId;
+
+ /** */
+ private AffinityTopologyVersion topVer;
+
+ /** */
+ private long crdVer;
+
+ /** */
+ private long cntr;
+
+ /** */
+ private long cleanupVer;
+
+ /** */
+ private long timeout;
+
+ /** */
+ private int taskNameHash;
+
+ /** */
+ private UUID nearNodeId;
+
+ /** Near tx version. */
+ private GridCacheVersion nearXidVer;
+
+ /**
+ *
+ */
+ public GridDhtTxQueryFirstEnlistRequest() {
+ }
+
+ /**
+ * @param cacheId Cache id.
+ * @param dhtFutId DHT future id.
+ * @param subjId Subject id.
+ * @param topVer Topology version.
+ * @param lockVer Lock version.
+ * @param snapshot Mvcc snapshot.
+ * @param timeout Timeout.
+ * @param taskNameHash Task name hash.
+ * @param nearNodeId Near node id.
+ * @param nearXidVer Near xid version.
+ * @param op Operation.
+ * @param batchId Batch id.
+ * @param keys Keys.
+ * @param vals Values.
+ * @param updCntrs Update counters.
+ */
+ GridDhtTxQueryFirstEnlistRequest(int cacheId,
+ IgniteUuid dhtFutId,
+ UUID subjId,
+ AffinityTopologyVersion topVer,
+ GridCacheVersion lockVer,
+ MvccSnapshot snapshot,
+ long timeout,
+ int taskNameHash,
+ UUID nearNodeId,
+ GridCacheVersion nearXidVer,
+ EnlistOperation op,
+ int batchId,
+ List<KeyCacheObject> keys,
+ List<Message> vals,
+ GridLongList updCntrs) {
+ super(cacheId, dhtFutId, lockVer, op, batchId, snapshot.operationCounter(), keys, vals, updCntrs);
+ this.cacheId = cacheId;
+ this.subjId = subjId;
+ this.topVer = topVer;
+ this.crdVer = snapshot.coordinatorVersion();
+ this.cntr = snapshot.counter();
+ this.cleanupVer = snapshot.cleanupVersion();
+ this.timeout = timeout;
+ this.taskNameHash = taskNameHash;
+ this.nearNodeId = nearNodeId;
+ this.nearXidVer = nearXidVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public AffinityTopologyVersion topologyVersion() {
+ return topVer;
+ }
+
+ /**
+ * @return Near node id.
+ */
+ public UUID nearNodeId() {
+ return nearNodeId;
+ }
+
+ /**
+ * @return Near transaction ID.
+ */
+ public GridCacheVersion nearXidVersion() {
+ return nearXidVer;
+ }
+
+ /**
+ * @return Max lock wait time.
+ */
+ public long timeout() {
+ return timeout;
+ }
+
+ /**
+ * @return Subject id.
+ */
+ public UUID subjectId() {
+ return subjId;
+ }
+
+ /**
+ * @return Task name hash.
+ */
+ public int taskNameHash() {
+ return taskNameHash;
+ }
+
+ /**
+ * @return MVCC snapshot.
+ */
+ public MvccSnapshot mvccSnapshot() {
+ return new MvccSnapshotWithoutTxs(crdVer, cntr, operationCounter(), cleanupVer);
+ }
+
+ /**
+ * @return Coordinator version.
+ */
+ public long coordinatorVersion() {
+ return crdVer;
+ }
+
+ /**
+ * @return Counter.
+ */
+ public long counter() {
+ return cntr;
+ }
+
+ /**
+ * @return Cleanup version.
+ */
+ public long cleanupVersion() {
+ return cleanupVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addDeploymentInfo() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 156;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 11:
+ if (!writer.writeLong("cleanupVer", cleanupVer))
+ return false;
+
+ writer.incrementState();
+
+ case 12:
+ if (!writer.writeLong("cntr", cntr))
+ return false;
+
+ writer.incrementState();
+
+ case 13:
+ if (!writer.writeLong("crdVer", crdVer))
+ return false;
+
+ writer.incrementState();
+
+ case 14:
+ if (!writer.writeUuid("nearNodeId", nearNodeId))
+ return false;
+
+ writer.incrementState();
+
+ case 15:
+ if (!writer.writeMessage("nearXidVer", nearXidVer))
+ return false;
+
+ writer.incrementState();
+
+ case 16:
+ if (!writer.writeUuid("subjId", subjId))
+ return false;
+
+ writer.incrementState();
+
+ case 17:
+ if (!writer.writeInt("taskNameHash", taskNameHash))
+ return false;
+
+ writer.incrementState();
+
+ case 18:
+ if (!writer.writeLong("timeout", timeout))
+ return false;
+
+ writer.incrementState();
+
+ case 19:
+ if (!writer.writeMessage("topVer", topVer))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 11:
+ cleanupVer = reader.readLong("cleanupVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 12:
+ cntr = reader.readLong("cntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 13:
+ crdVer = reader.readLong("crdVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 14:
+ nearNodeId = reader.readUuid("nearNodeId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 15:
+ nearXidVer = reader.readMessage("nearXidVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 16:
+ subjId = reader.readUuid("subjId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 17:
+ taskNameHash = reader.readInt("taskNameHash");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 18:
+ timeout = reader.readLong("timeout");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 19:
+ topVer = reader.readMessage("topVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridDhtTxQueryFirstEnlistRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 20;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridDhtTxQueryFirstEnlistRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryResultsEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryResultsEnlistFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryResultsEnlistFuture.java
new file mode 100644
index 0000000..6169e1f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxQueryResultsEnlistFuture.java
@@ -0,0 +1,130 @@
+/*
+ * 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.distributed.dht;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Future processing transaction enlisting and locking of entries
+ * produces by complex DML queries with reduce step.
+ */
+public final class GridDhtTxQueryResultsEnlistFuture extends GridDhtTxAbstractEnlistFuture implements UpdateSourceIterator<Object> {
+ /** */
+ private static final long serialVersionUID = -4933550335145438798L;
+ /** */
+ private EnlistOperation op;
+
+ /** */
+ private Iterator<Object> it;
+
+ /**
+ * @param nearNodeId Near node ID.
+ * @param nearLockVer Near lock version.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @param threadId Thread ID.
+ * @param nearFutId Near future id.
+ * @param nearMiniId Near mini future id.
+ * @param tx Transaction.
+ * @param timeout Lock acquisition timeout.
+ * @param cctx Cache context.
+ * @param rows Collection of rows.
+ * @param op Operation.
+ */
+ public GridDhtTxQueryResultsEnlistFuture(UUID nearNodeId,
+ GridCacheVersion nearLockVer,
+ MvccSnapshot mvccSnapshot,
+ long threadId,
+ IgniteUuid nearFutId,
+ int nearMiniId,
+ GridDhtTxLocalAdapter tx,
+ long timeout,
+ GridCacheContext<?, ?> cctx,
+ Collection<Object> rows,
+ EnlistOperation op) {
+ super(nearNodeId,
+ nearLockVer,
+ mvccSnapshot,
+ threadId,
+ nearFutId,
+ nearMiniId,
+ null,
+ tx,
+ timeout,
+ cctx);
+
+ this.op = op;
+
+ it = rows.iterator();
+
+ skipNearNodeUpdates = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected UpdateSourceIterator<?> createIterator() throws IgniteCheckedException {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ GridDhtTxQueryResultsEnlistFuture future = (GridDhtTxQueryResultsEnlistFuture)o;
+
+ return Objects.equals(futId, future.futId);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return futId.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridDhtTxQueryResultsEnlistFuture.class, this);
+ }
+
+ /** {@inheritDoc} */
+ @Override public EnlistOperation operation() {
+ return op;
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNextX() {
+ return it.hasNext();
+ }
+
+ /** {@inheritDoc} */
+ public Object nextX() {
+ return it.next();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxRemote.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxRemote.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxRemote.java
index 746eb38..08ecf28 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxRemote.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxRemote.java
@@ -21,26 +21,38 @@ import java.io.Externalizable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.cache.processor.EntryProcessor;
import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cluster.ClusterTopologyException;
+import org.apache.ignite.internal.pagemem.wal.WALPointer;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
import org.apache.ignite.internal.processors.cache.GridCacheOperation;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.GridCacheUpdateTxResult;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxRemoteAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxRemoteSingleStateImpl;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxRemoteStateImpl;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.EnlistOperation;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.transactions.TransactionConcurrency;
import org.apache.ignite.transactions.TransactionIsolation;
import org.jetbrains.annotations.Nullable;
@@ -67,6 +79,8 @@ public class GridDhtTxRemote extends GridDistributedTxRemoteAdapter {
/** Store write through flag. */
private boolean storeWriteThrough;
+ /** Map of update counters made by this tx. Mapping: cacheId -> partId -> updCntr. */
+ private Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrs;
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -368,6 +382,136 @@ public class GridDhtTxRemote extends GridDistributedTxRemoteAdapter {
txState.addWriteEntry(key, txEntry);
}
+ /**
+ *
+ * @param ctx Cache context.
+ * @param op Operation.
+ * @param keys Keys.
+ * @param vals Values.
+ * @param snapshot Mvcc snapshot.
+ * @param updCntrs Update counters.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void mvccEnlistBatch(GridCacheContext ctx, EnlistOperation op, List<KeyCacheObject> keys,
+ List<Message> vals, MvccSnapshot snapshot, GridLongList updCntrs) throws IgniteCheckedException {
+ assert keys != null && updCntrs != null && keys.size() == updCntrs.size();
+
+ WALPointer ptr = null;
+
+ GridDhtCacheAdapter dht = ctx.dht();
+
+ addActiveCache(ctx, false);
+
+ for (int i = 0; i < keys.size(); i++) {
+ KeyCacheObject key = keys.get(i);
+
+ assert key != null;
+
+ int part = ctx.affinity().partition(key);
+
+ GridDhtLocalPartition locPart = ctx.topology().localPartition(part, topologyVersion(), false);
+
+ if (locPart == null || !locPart.reserve())
+ throw new ClusterTopologyException("Can not reserve partition. Please retry on stable topology.");
+
+ try {
+ CacheObject val = null;
+
+ Message val0 = vals != null ? vals.get(i) : null;
+
+ CacheEntryInfoCollection entries =
+ val0 instanceof CacheEntryInfoCollection ? (CacheEntryInfoCollection)val0 : null;
+
+ if (entries == null && !op.isDeleteOrLock())
+ val = (val0 instanceof CacheObject) ? (CacheObject)val0 : null;
+
+ GridDhtCacheEntry entry = dht.entryExx(key, topologyVersion());
+
+ GridCacheUpdateTxResult updRes;
+
+ while (true) {
+ ctx.shared().database().checkpointReadLock();
+
+ try {
+ if (entries == null) {
+ switch (op) {
+ case DELETE:
+ updRes = entry.mvccRemove(
+ this,
+ ctx.localNodeId(),
+ topologyVersion(),
+ updCntrs.get(i),
+ snapshot,
+ false);
+
+ break;
+
+ case INSERT:
+ case UPSERT:
+ case UPDATE:
+ updRes = entry.mvccSet(
+ this,
+ ctx.localNodeId(),
+ val,
+ 0,
+ topologyVersion(),
+ updCntrs.get(i),
+ snapshot,
+ op.cacheOperation(),
+ false,
+ false);
+
+ break;
+
+ default:
+ throw new IgniteSQLException("Cannot acquire lock for operation [op= "
+ + op + "]" + "Operation is unsupported at the moment ",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+ }
+ else {
+ updRes = entry.mvccUpdateRowsWithPreloadInfo(this,
+ ctx.localNodeId(),
+ topologyVersion(),
+ updCntrs.get(i),
+ entries.infos(),
+ op.cacheOperation(),
+ snapshot);
+ }
+
+ break;
+ }
+ catch (GridCacheEntryRemovedException ignore) {
+ entry = dht.entryExx(key);
+ }
+ finally {
+ ctx.shared().database().checkpointReadUnlock();
+ }
+ }
+
+ assert updRes.updateFuture() == null : "Entry should not be locked on the backup";
+
+ ptr = updRes.loggedPointer();
+ }
+ finally {
+ locPart.release();
+ }
+ }
+
+ if (ptr != null && !ctx.tm().logTxRecords())
+ ctx.shared().wal().flush(ptr, true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateCountersMap(Map<Integer, GridDhtPartitionsUpdateCountersMap> updCntrsMap) {
+ this.updCntrs = updCntrsMap;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Map<Integer, GridDhtPartitionsUpdateCountersMap> updateCountersMap() {
+ return updCntrs;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return GridToStringBuilder.toString(GridDhtTxRemote.class, this, "super", super.toString());
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
index 30d36e8..a76844a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
@@ -43,6 +43,10 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTrackerImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotResponseListener;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.GridLeanMap;
@@ -64,7 +68,8 @@ import org.jetbrains.annotations.Nullable;
/**
* Colocated get future.
*/
-public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAdapter<K, V> {
+public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAdapter<K, V>
+ implements MvccSnapshotResponseListener {
/** */
private static final long serialVersionUID = 0L;
@@ -74,6 +79,12 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
/** Logger. */
private static IgniteLogger log;
+ /** */
+ protected final MvccSnapshot mvccSnapshot;
+
+ /** */
+ private MvccQueryTracker mvccTracker;
+
/**
* @param cctx Context.
* @param keys Keys.
@@ -88,6 +99,7 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
* @param skipVals Skip values flag.
* @param needVer If {@code true} returns values as tuples containing value and version.
* @param keepCacheObjects Keep cache objects flag.
+ * @param mvccSnapshot Mvcc snapshot.
*/
public GridPartitionedGetFuture(
GridCacheContext<K, V> cctx,
@@ -101,7 +113,8 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean skipVals,
boolean needVer,
- boolean keepCacheObjects
+ boolean keepCacheObjects,
+ @Nullable MvccSnapshot mvccSnapshot
) {
super(cctx,
keys,
@@ -115,12 +128,32 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
needVer,
keepCacheObjects,
recovery);
+ assert mvccSnapshot == null || cctx.mvccEnabled();
+
+ this.mvccSnapshot = mvccSnapshot;
if (log == null)
log = U.logger(cctx.kernalContext(), logRef, GridPartitionedGetFuture.class);
}
/**
+ * @return Mvcc snapshot if mvcc is enabled for cache.
+ */
+ @Nullable private MvccSnapshot mvccSnapshot() {
+ if (!cctx.mvccEnabled())
+ return null;
+
+ if (mvccSnapshot != null)
+ return mvccSnapshot;
+
+ MvccSnapshot snapshot = mvccTracker.snapshot();
+
+ assert snapshot != null : "[fut=" + this + ", mvccTracker=" + mvccTracker + "]";
+
+ return snapshot;
+ }
+
+ /**
* Initializes future.
*
* @param topVer Topology version.
@@ -129,16 +162,45 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
AffinityTopologyVersion lockedTopVer = cctx.shared().lockedTopologyVersion(null);
if (lockedTopVer != null) {
- canRemap = false;
+ topVer = lockedTopVer;
- map(keys, Collections.<ClusterNode, LinkedHashMap<KeyCacheObject, Boolean>>emptyMap(), lockedTopVer);
+ canRemap = false;
}
else {
topVer = topVer.topologyVersion() > 0 ? topVer :
canRemap ? cctx.affinity().affinityTopologyVersion() : cctx.shared().exchange().readyAffinityVersion();
+ }
- map(keys, Collections.<ClusterNode, LinkedHashMap<KeyCacheObject, Boolean>>emptyMap(), topVer);
+ if (!cctx.mvccEnabled() || mvccSnapshot != null)
+ initialMap(topVer);
+ else {
+ mvccTracker = new MvccQueryTrackerImpl(cctx, canRemap);
+
+ trackable = true;
+
+ cctx.mvcc().addFuture(this, futId);
+
+ mvccTracker.requestSnapshot(topVer, this);
}
+ }
+
+ @Override public void onResponse(MvccSnapshot res) {
+ AffinityTopologyVersion topVer = mvccTracker.topologyVersion();
+
+ assert topVer != null;
+
+ initialMap(topVer);
+ }
+
+ @Override public void onError(IgniteCheckedException e) {
+ onDone(e);
+ }
+
+ /**
+ * @param topVer Topology version.
+ */
+ private void initialMap(AffinityTopologyVersion topVer) {
+ map(keys, Collections.<ClusterNode, LinkedHashMap<KeyCacheObject, Boolean>>emptyMap(), topVer);
markInitialized();
}
@@ -197,10 +259,12 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
/** {@inheritDoc} */
@Override public boolean onDone(Map<K, V> res, Throwable err) {
if (super.onDone(res, err)) {
- // Don't forget to clean up.
if (trackable)
cctx.mvcc().removeFuture(futId);
+ if (mvccTracker != null)
+ mvccTracker.onDone();
+
cache().sendTtlUpdateRequest(expiryPlc);
return true;
@@ -293,7 +357,8 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
taskName == null ? 0 : taskName.hashCode(),
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot());
final Collection<Integer> invalidParts = fut.invalidPartitions();
@@ -350,7 +415,8 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
false,
skipVals,
cctx.deploymentEnabled(),
- recovery);
+ recovery,
+ mvccSnapshot());
add(fut); // Append new future.
@@ -463,7 +529,9 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
GridCacheVersion ver = null;
if (readNoEntry) {
- CacheDataRow row = cctx.offheap().read(cctx, key);
+ CacheDataRow row = cctx.mvccEnabled() ?
+ cctx.offheap().mvccRead(cctx, key, mvccSnapshot()) :
+ cctx.offheap().read(cctx, key);
if (row != null) {
long expireTime = row.expireTime();
@@ -506,6 +574,7 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
taskName,
expiryPlc,
!deserializeBinary,
+ mvccSnapshot(),
null);
if (getRes != null) {
@@ -524,7 +593,8 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
null,
taskName,
expiryPlc,
- !deserializeBinary);
+ !deserializeBinary,
+ mvccSnapshot());
}
entry.touch(topVer);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
index 83e46ab..fd9bc77 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
@@ -46,6 +46,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.CacheVersion
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetResponse;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetRequest;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -124,6 +125,9 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
@GridToStringInclude
private ClusterNode node;
+ /** */
+ protected final MvccSnapshot mvccSnapshot;
+
/** Post processing closure. */
private volatile BackupPostProcessingClosure postProcessingClos;
@@ -154,9 +158,11 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
boolean skipVals,
boolean needVer,
boolean keepCacheObjects,
- boolean recovery
+ boolean recovery,
+ @Nullable MvccSnapshot mvccSnapshot
) {
assert key != null;
+ assert mvccSnapshot == null || cctx.mvccEnabled();
AffinityTopologyVersion lockedTopVer = cctx.shared().lockedTopologyVersion(null);
@@ -181,6 +187,7 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
this.keepCacheObjects = keepCacheObjects;
this.recovery = recovery;
this.topVer = topVer;
+ this.mvccSnapshot = mvccSnapshot;
futId = IgniteUuid.randomUuid();
@@ -235,7 +242,8 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
taskName == null ? 0 : taskName.hashCode(),
expiryPlc,
skipVals,
- recovery);
+ recovery,
+ mvccSnapshot);
final Collection<Integer> invalidParts = fut.invalidPartitions();
@@ -305,7 +313,8 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
/*add reader*/false,
needVer,
cctx.deploymentEnabled(),
- recovery);
+ recovery,
+ mvccSnapshot);
try {
cctx.io().send(node, req, cctx.ioPolicy());
@@ -379,7 +388,8 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
boolean skipEntry = readNoEntry;
if (readNoEntry) {
- CacheDataRow row = cctx.offheap().read(cctx, key);
+ CacheDataRow row = mvccSnapshot != null ? cctx.offheap().mvccRead(cctx, key, mvccSnapshot) :
+ cctx.offheap().read(cctx, key);
if (row != null) {
long expireTime = row.expireTime();
@@ -422,6 +432,7 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
taskName,
expiryPlc,
true,
+ mvccSnapshot,
null);
if (res != null) {
@@ -440,7 +451,8 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
null,
taskName,
expiryPlc,
- true);
+ true,
+ mvccSnapshot);
}
entry.touch(topVer);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/NearTxQueryEnlistResultHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/NearTxQueryEnlistResultHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/NearTxQueryEnlistResultHandler.java
new file mode 100644
index 0000000..bb863fb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/NearTxQueryEnlistResultHandler.java
@@ -0,0 +1,169 @@
+/*
+ * 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.distributed.dht;
+
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistResponse;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistResponse;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.lang.GridClosureException;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ *
+ */
+public final class NearTxQueryEnlistResultHandler implements CI1<IgniteInternalFuture<Long>> {
+ /** */
+ private static final long serialVersionUID = 5189735824793607906L;
+
+ /** */
+ private static final NearTxQueryEnlistResultHandler INSTANCE = new NearTxQueryEnlistResultHandler();
+
+ /** */
+ private NearTxQueryEnlistResultHandler() {}
+
+ /**
+ * @return Handler instance.
+ */
+ public static NearTxQueryEnlistResultHandler instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * @param future Enlist future.
+ * @return Enlist response.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends GridNearTxQueryEnlistResponse> T createResponse(IgniteInternalFuture<?> future) {
+ assert future != null;
+
+ Class<?> clazz = future.getClass();
+
+ if (clazz == GridDhtTxQueryResultsEnlistFuture.class)
+ return (T)createResponse((GridDhtTxQueryResultsEnlistFuture)future);
+ else if (clazz == GridDhtTxQueryEnlistFuture.class)
+ return (T)createResponse((GridDhtTxQueryEnlistFuture)future);
+ else
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @param future Enlist future.
+ * @return Enlist response.
+ */
+ @NotNull private static GridNearTxQueryEnlistResponse createResponse(GridDhtTxQueryEnlistFuture future) {
+ try {
+ future.get();
+
+ assert future.tx.queryEnlisted() || future.cnt == 0;
+
+ return new GridNearTxQueryEnlistResponse(future.cctx.cacheId(), future.nearFutId, future.nearMiniId,
+ future.nearLockVer, future.cnt, future.tx.empty() && !future.tx.queryEnlisted());
+ }
+ catch (IgniteCheckedException e) {
+ return new GridNearTxQueryEnlistResponse(future.cctx.cacheId(), future.nearFutId, future.nearMiniId, future.nearLockVer, e);
+ }
+ }
+
+ /**
+ * @param future Enlist future.
+ * @return Enlist response.
+ */
+ @NotNull private static GridNearTxQueryResultsEnlistResponse createResponse(GridDhtTxQueryResultsEnlistFuture future) {
+ try {
+ future.get();
+
+ GridCacheVersion ver = null;
+ IgniteUuid id = null;
+ GridLongList updCntrs = null;
+
+ if (future.hasNearNodeUpdates) {
+ ver = future.cctx.tm().mappedVersion(future.nearLockVer);
+ id = future.futId;
+ updCntrs = future.nearUpdCntrs;
+ }
+
+ return new GridNearTxQueryResultsEnlistResponse(future.cctx.cacheId(), future.nearFutId, future.nearMiniId,
+ future.nearLockVer, future.cnt, ver, id, updCntrs);
+ }
+ catch (IgniteCheckedException e) {
+ return new GridNearTxQueryResultsEnlistResponse(future.cctx.cacheId(), future.nearFutId, future.nearMiniId,
+ future.nearLockVer, e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void apply(IgniteInternalFuture<Long> fut0) {
+ GridDhtTxAbstractEnlistFuture fut = (GridDhtTxAbstractEnlistFuture)fut0;
+
+ GridCacheContext<?, ?> cctx = fut.cctx;
+ GridDhtTxLocal tx = (GridDhtTxLocal)fut.tx;
+ UUID nearNodeId = fut.nearNodeId;
+
+ GridNearTxQueryEnlistResponse res = createResponse(fut);
+
+ if (res.removeMapping()) {
+ // TODO IGNITE-9133
+ tx.rollbackDhtLocalAsync().listen(new CI1<IgniteInternalFuture<IgniteInternalTx>>() {
+ @Override public void apply(IgniteInternalFuture<IgniteInternalTx> fut0) {
+ try {
+ cctx.io().send(nearNodeId, res, cctx.ioPolicy());
+ }
+ catch (IgniteCheckedException e) {
+ U.error(fut.log, "Failed to send near enlist response [" +
+ "tx=" + CU.txString(tx) +
+ ", node=" + nearNodeId +
+ ", res=" + res + ']', e);
+
+ throw new GridClosureException(e);
+ }
+ }
+ });
+
+ return;
+ }
+
+ try {
+ cctx.io().send(nearNodeId, res, cctx.ioPolicy());
+ }
+ catch (IgniteCheckedException e) {
+ U.error(fut.log, "Failed to send near enlist response (will rollback transaction) [" +
+ "tx=" + CU.txString(tx) +
+ ", node=" + nearNodeId +
+ ", res=" + res + ']', e);
+
+ try {
+ tx.rollbackDhtLocalAsync();
+ }
+ catch (Throwable e1) {
+ e.addSuppressed(e1);
+ }
+
+ throw new GridClosureException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index 112b913..7bc04dc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -65,7 +65,6 @@ import org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult;
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtFuture;
@@ -83,6 +82,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSing
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetResponse;
import org.apache.ignite.internal.processors.cache.dr.GridCacheDrExpirationInfo;
import org.apache.ignite.internal.processors.cache.dr.GridCacheDrInfo;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalEx;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionConflictContext;
@@ -1422,7 +1422,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
skipVals,
needVer,
false,
- recovery);
+ recovery,
+ null);
fut.init();
@@ -1531,6 +1532,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
taskName,
expiry,
true,
+ null,
null);
if (getRes != null) {
@@ -1549,7 +1551,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
null,
taskName,
expiry,
- !deserializeBinary);
+ !deserializeBinary,
+ null);
}
// Entry was not in memory or in swap, so we remove it from cache.
@@ -1626,7 +1629,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
expiry,
skipVals,
needVer,
- false);
+ false,
+ null);
fut.init(topVer);
@@ -2097,7 +2101,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
entryProcessor,
taskName,
null,
- req.keepBinary());
+ req.keepBinary(),
+ null);
Object oldVal = null;
Object updatedVal = null;
@@ -2277,7 +2282,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
null,
taskName,
null,
- req.keepBinary());
+ req.keepBinary(),
+ null);
Object val = ctx.config().getInterceptor().onBeforePut(
new CacheLazyEntry(
@@ -2322,7 +2328,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
null,
taskName,
null,
- req.keepBinary());
+ req.keepBinary(),
+ null);
IgniteBiTuple<Boolean, ?> interceptorRes = ctx.config().getInterceptor()
.onBeforeRemove(new CacheLazyEntry(ctx, entry.key(), old, req.keepBinary()));
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
index eaa93ae..f6de594 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
@@ -42,6 +42,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheEntry;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException;
@@ -241,7 +242,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
skipVals,
needVer,
/*keepCacheObjects*/false,
- opCtx != null && opCtx.recovery());
+ opCtx != null && opCtx.recovery(),
+ null);
fut.init();
@@ -319,7 +321,7 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
* @param needVer Need version.
* @return Loaded values.
*/
- public IgniteInternalFuture<Map<K, V>> loadAsync(
+ private IgniteInternalFuture<Map<K, V>> loadAsync(
@Nullable Collection<KeyCacheObject> keys,
boolean readThrough,
boolean forcePrimary,
@@ -341,7 +343,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
expiryPlc,
skipVals,
needVer,
- false);
+ false,
+ null);
}
/**
@@ -370,7 +373,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
boolean skipVals,
boolean needVer,
boolean keepCacheObj,
- boolean recovery
+ boolean recovery,
+ @Nullable MvccSnapshot mvccSnapshot
) {
GridPartitionedSingleGetFuture fut = new GridPartitionedSingleGetFuture(ctx,
ctx.toCacheKeyObject(key),
@@ -384,7 +388,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
skipVals,
needVer,
keepCacheObj,
- recovery);
+ recovery,
+ mvccSnapshot);
fut.init();
@@ -403,6 +408,7 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
* @param skipVals Skip values flag.
* @param needVer If {@code true} returns values as tuples containing value and version.
* @param keepCacheObj Keep cache objects flag.
+ * @param mvccSnapshot Mvcc snapshot.
* @return Load future.
*/
public final IgniteInternalFuture<Map<K, V>> loadAsync(
@@ -417,8 +423,11 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean skipVals,
boolean needVer,
- boolean keepCacheObj
+ boolean keepCacheObj,
+ @Nullable MvccSnapshot mvccSnapshot
) {
+ assert mvccSnapshot == null || ctx.mvccEnabled();
+
if (keys == null || keys.isEmpty())
return new GridFinishedFuture<>(Collections.<K, V>emptyMap());
@@ -426,7 +435,7 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
expiryPlc = expiryPolicy(null);
// Optimization: try to resolve value locally and escape 'get future' creation.
- if (!forcePrimary && ctx.affinityNode()) {
+ if (!forcePrimary && ctx.affinityNode() && (!ctx.mvccEnabled() || mvccSnapshot != null)) {
try {
Map<K, V> locVals = null;
@@ -499,6 +508,7 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
taskName,
expiryPlc,
!deserializeBinary,
+ mvccSnapshot,
null);
if (getRes != null) {
@@ -517,7 +527,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
null,
taskName,
expiryPlc,
- !deserializeBinary);
+ !deserializeBinary,
+ mvccSnapshot);
}
// Entry was not in memory or in swap, so we remove it from cache.
@@ -600,7 +611,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
expiryPlc,
skipVals,
needVer,
- keepCacheObj);
+ keepCacheObj,
+ mvccSnapshot);
fut.init(topVer);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
index fe216a0..52691f0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
@@ -42,6 +42,9 @@ import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtFuture;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.util.F0;
import org.apache.ignite.internal.util.GridLeanSet;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
@@ -537,6 +540,10 @@ public final class GridDhtForceKeysFuture<K, V> extends GridCompoundFuture<Objec
if (entry.initialValue(
info.value(),
info.version(),
+ cctx.mvccEnabled() ? ((MvccVersionAware)info).mvccVersion() : null,
+ cctx.mvccEnabled() ? ((MvccUpdateVersionAware)info).newMvccVersion() : null,
+ cctx.mvccEnabled() ? ((MvccVersionAware)entry).mvccTxState() : TxState.NA,
+ cctx.mvccEnabled() ? ((MvccUpdateVersionAware)entry).newMvccTxState() : TxState.NA,
info.ttl(),
info.expireTime(),
true,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
index 5b5f344..a0f82ea 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
@@ -52,6 +52,9 @@ import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
@@ -881,6 +884,10 @@ public class GridDhtPartitionDemander {
if (cached.initialValue(
entry.value(),
entry.version(),
+ cctx.mvccEnabled() ? ((MvccVersionAware)entry).mvccVersion() : null,
+ cctx.mvccEnabled() ? ((MvccUpdateVersionAware)entry).newMvccVersion() : null,
+ cctx.mvccEnabled() ? ((MvccVersionAware)entry).mvccTxState() : TxState.NA,
+ cctx.mvccEnabled() ? ((MvccUpdateVersionAware)entry).newMvccTxState() : TxState.NA,
entry.ttl(),
entry.expireTime(),
true,
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
index ea7f4c9..7ce4f7e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
@@ -33,10 +33,15 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo;
import org.apache.ignite.internal.processors.cache.IgniteRebalanceIterator;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.T3;
@@ -361,14 +366,39 @@ class GridDhtPartitionSupplier {
if (!remainingParts.contains(part))
continue;
- GridCacheEntryInfo info = new GridCacheEntryInfo();
+ GridCacheEntryInfo info = grp.mvccEnabled() ?
+ new GridCacheMvccEntryInfo() : new GridCacheEntryInfo();
info.key(row.key());
- info.expireTime(row.expireTime());
- info.version(row.version());
- info.value(row.value());
info.cacheId(row.cacheId());
+ if (grp.mvccEnabled()) {
+ byte txState = row.mvccTxState() != TxState.NA ? row.mvccTxState() :
+ MvccUtils.state(grp, row.mvccCoordinatorVersion(), row.mvccCounter(),
+ row.mvccOperationCounter());
+
+ if (txState != TxState.COMMITTED)
+ continue;
+
+ ((MvccVersionAware)info).mvccVersion(row);
+ ((GridCacheMvccEntryInfo)info).mvccTxState(TxState.COMMITTED);
+
+ byte newTxState = row.newMvccTxState() != TxState.NA ? row.newMvccTxState() :
+ MvccUtils.state(grp, row.newMvccCoordinatorVersion(), row.newMvccCounter(),
+ row.newMvccOperationCounter());
+
+ if (newTxState != TxState.ABORTED) {
+ ((MvccUpdateVersionAware)info).newMvccVersion(row);
+
+ if (newTxState == TxState.COMMITTED)
+ ((GridCacheMvccEntryInfo)info).newMvccTxState(TxState.COMMITTED);
+ }
+ }
+
+ info.value(row.value());
+ info.version(row.version());
+ info.expireTime(row.expireTime());
+
if (preloadPred == null || preloadPred.apply(info))
s.addEntry0(part, iter.historical(part), info, grp.shared(), grp.cacheObjectContext());
else {
[32/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxAbstractEnlistFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxAbstractEnlistFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxAbstractEnlistFuture.java
new file mode 100644
index 0000000..f484bd6
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxAbstractEnlistFuture.java
@@ -0,0 +1,499 @@
+/*
+ * 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.distributed.near;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.IgniteFutureCancelledCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheStoppedException;
+import org.apache.ignite.internal.processors.cache.GridCacheCompoundIdentityFuture;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
+import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
+import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
+import org.apache.ignite.internal.processors.cache.distributed.dht.CompoundLockFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxAbstractEnlistFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
+import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public abstract class GridNearTxAbstractEnlistFuture extends GridCacheCompoundIdentityFuture<Long> implements
+ GridCacheVersionedFuture<Long> {
+ /** */
+ private static final long serialVersionUID = -6069985059301497282L;
+
+ /** Done field updater. */
+ private static final AtomicIntegerFieldUpdater<GridNearTxAbstractEnlistFuture> DONE_UPD =
+ AtomicIntegerFieldUpdater.newUpdater(GridNearTxAbstractEnlistFuture.class, "done");
+
+ /** Done field updater. */
+ private static final AtomicReferenceFieldUpdater<GridNearTxAbstractEnlistFuture, Throwable> EX_UPD =
+ AtomicReferenceFieldUpdater.newUpdater(GridNearTxAbstractEnlistFuture.class, Throwable.class, "ex");
+
+ /** Cache context. */
+ @GridToStringExclude
+ protected final GridCacheContext<?, ?> cctx;
+
+ /** Transaction. */
+ protected final GridNearTxLocal tx;
+
+ /** */
+ protected AffinityTopologyVersion topVer;
+
+ /** MVCC snapshot. */
+ protected MvccSnapshot mvccSnapshot;
+
+ /** Logger. */
+ @GridToStringExclude
+ protected final IgniteLogger log;
+
+ /** */
+ protected long timeout;
+
+ /** Initiated thread id. */
+ protected final long threadId;
+
+ /** Mvcc future id. */
+ protected final IgniteUuid futId;
+
+ /** Lock version. */
+ protected final GridCacheVersion lockVer;
+
+ /** */
+ @GridToStringExclude
+ private GridDhtTxAbstractEnlistFuture localEnlistFuture;
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ protected volatile Throwable ex;
+
+ /** */
+ @SuppressWarnings("unused")
+ @GridToStringExclude
+ private volatile int done;
+
+ /** Timeout object. */
+ @GridToStringExclude
+ protected LockTimeoutObject timeoutObj;
+
+ /**
+ * @param cctx Cache context.
+ * @param tx Transaction.
+ * @param timeout Timeout.
+ */
+ public GridNearTxAbstractEnlistFuture(
+ GridCacheContext<?, ?> cctx, GridNearTxLocal tx, long timeout) {
+ super(CU.longReducer());
+
+ assert cctx != null;
+ assert tx != null;
+
+ this.cctx = cctx;
+ this.tx = tx;
+ this.timeout = timeout;
+
+ threadId = tx.threadId();
+ lockVer = tx.xidVersion();
+ futId = IgniteUuid.randomUuid();
+
+ mvccSnapshot = tx.mvccSnapshot();
+
+ assert mvccSnapshot != null;
+
+ log = cctx.logger(getClass());
+ }
+
+ /**
+ *
+ */
+ public void init() {
+ if (timeout < 0) {
+ // Time is out.
+ onDone(timeoutException());
+
+ return;
+ }
+ else if (timeout > 0)
+ timeoutObj = new LockTimeoutObject();
+
+ while(true) {
+ IgniteInternalFuture<?> fut = tx.lockFuture();
+
+ if (fut == GridDhtTxLocalAdapter.ROLLBACK_FUT) {
+ onDone(tx.timedOut() ? tx.timeoutException() : tx.rollbackException());
+
+ return;
+ }
+ else if (fut != null) {
+ // Wait for previous future.
+ assert fut instanceof GridNearTxAbstractEnlistFuture
+ || fut instanceof GridDhtTxAbstractEnlistFuture
+ || fut instanceof CompoundLockFuture
+ || fut instanceof GridNearTxSelectForUpdateFuture : fut;
+
+ // Terminate this future if parent future is terminated by rollback.
+ if (!fut.isDone()) {
+ fut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ if (fut.error() != null)
+ onDone(fut.error());
+ }
+ });
+ }
+ else if (fut.error() != null)
+ onDone(fut.error());
+
+ break;
+ }
+ else if (tx.updateLockFuture(null, this))
+ break;
+ }
+
+ boolean added = cctx.mvcc().addFuture(this);
+
+ assert added : this;
+
+ if (isDone()) {
+ cctx.mvcc().removeFuture(futId);
+
+ return;
+ }
+
+ try {
+ tx.addActiveCache(cctx, false);
+ }
+ catch (IgniteCheckedException e) {
+ onDone(e);
+
+ return;
+ }
+
+ if (timeoutObj != null)
+ cctx.time().addTimeoutObject(timeoutObj);
+
+ // Obtain the topology version to use.
+ long threadId = Thread.currentThread().getId();
+
+ AffinityTopologyVersion topVer = cctx.mvcc().lastExplicitLockTopologyVersion(threadId);
+
+ // If there is another system transaction in progress, use it's topology version to prevent deadlock.
+ if (topVer == null && tx.system())
+ topVer = cctx.tm().lockedTopologyVersion(threadId, tx);
+
+ if (topVer != null)
+ tx.topologyVersion(topVer);
+
+ if (topVer == null)
+ topVer = tx.topologyVersionSnapshot();
+
+ if (topVer != null) {
+ for (GridDhtTopologyFuture fut : cctx.shared().exchange().exchangeFutures()) {
+ if (fut.exchangeDone() && fut.topologyVersion().equals(topVer)) {
+ Throwable err = fut.validateCache(cctx, false, false, null, null);
+
+ if (err != null) {
+ onDone(err);
+
+ return;
+ }
+
+ break;
+ }
+ }
+
+ if (this.topVer == null)
+ this.topVer = topVer;
+
+ map(true);
+
+ return;
+ }
+
+ mapOnTopology();
+ }
+
+ /**
+ * @param node Primary node.
+ * @throws IgniteCheckedException if future is already completed.
+ */
+ protected synchronized void updateMappings(ClusterNode node) throws IgniteCheckedException {
+ checkCompleted();
+
+ IgniteTxMappings m = tx.mappings();
+
+ GridDistributedTxMapping mapping = m.get(node.id());
+
+ if (mapping == null)
+ m.put(mapping = new GridDistributedTxMapping(node));
+
+ mapping.markQueryUpdate();
+
+ if (node.isLocal())
+ tx.colocatedLocallyMapped(true);
+ }
+
+ /**
+ * @param fut Local enlist future.
+ * @throws IgniteCheckedException if future is already completed.
+ */
+ protected synchronized void updateLocalFuture(GridDhtTxAbstractEnlistFuture fut) throws IgniteCheckedException {
+ checkCompleted();
+
+ assert localEnlistFuture == null;
+
+ localEnlistFuture = fut;
+ }
+
+ /**
+ * @param fut Local enlist future.
+ * @throws IgniteCheckedException if future is already completed.
+ */
+ protected synchronized void clearLocalFuture(GridDhtTxAbstractEnlistFuture fut) throws IgniteCheckedException {
+ checkCompleted();
+
+ if (localEnlistFuture == fut)
+ localEnlistFuture = null;
+ }
+
+ /**
+ * @throws IgniteCheckedException if future is already completed.
+ */
+ protected void checkCompleted() throws IgniteCheckedException {
+ if (isDone())
+ throw new IgniteCheckedException("Future is done.");
+ }
+
+
+
+ /**
+ */
+ private void mapOnTopology() {
+ cctx.topology().readLock();
+
+ try {
+ if (cctx.topology().stopping()) {
+ onDone(new CacheStoppedException(cctx.name()));
+
+ return;
+ }
+
+ GridDhtTopologyFuture fut = cctx.topologyVersionFuture();
+
+ if (fut.isDone()) {
+ Throwable err = fut.validateCache(cctx, false, false, null, null);
+
+ if (err != null) {
+ onDone(err);
+
+ return;
+ }
+
+ AffinityTopologyVersion topVer = fut.topologyVersion();
+
+ if (tx != null)
+ tx.topologyVersion(topVer);
+
+ if (this.topVer == null)
+ this.topVer = topVer;
+
+ map(false);
+ }
+ else {
+ fut.listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() {
+ @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> fut) {
+ try {
+ fut.get();
+
+ mapOnTopology();
+ }
+ catch (IgniteCheckedException e) {
+ onDone(e);
+ }
+ finally {
+ cctx.shared().txContextReset();
+ }
+ }
+ });
+ }
+ }
+ finally {
+ cctx.topology().readUnlock();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected boolean processFailure(Throwable err, IgniteInternalFuture<Long> fut) {
+ if (ex != null || !EX_UPD.compareAndSet(this, null, err))
+ ex.addSuppressed(err);
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onDone(@Nullable Long res, @Nullable Throwable err, boolean cancelled) {
+ if (!DONE_UPD.compareAndSet(this, 0, 1))
+ return false;
+
+ cctx.tm().txContext(tx);
+
+ Throwable ex0 = ex;
+
+ if (ex0 != null) {
+ if (err != null)
+ ex0.addSuppressed(err);
+
+ err = ex0;
+ }
+
+ if (!cancelled && err == null)
+ tx.clearLockFuture(this);
+ else
+ tx.setRollbackOnly();
+
+ synchronized (this) {
+ boolean done = super.onDone(res, err, cancelled);
+
+ assert done;
+
+ GridDhtTxAbstractEnlistFuture localFuture0 = localEnlistFuture;
+
+ if (localFuture0 != null && (err != null || cancelled))
+ localFuture0.onDone(cancelled ? new IgniteFutureCancelledCheckedException("Future was cancelled: " + localFuture0) : err);
+
+ // Clean up.
+ cctx.mvcc().removeVersionedFuture(this);
+
+ if (timeoutObj != null)
+ cctx.time().removeTimeoutObject(timeoutObj);
+
+ return true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void logError(IgniteLogger log, String msg, Throwable e) {
+ // no-op
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void logDebug(IgniteLogger log, String msg) {
+ // no-op
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean trackable() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void markNotTrackable() {
+ // No-op.
+ }
+
+ @Override public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ @Override public boolean onOwnerChanged(GridCacheEntryEx entry, GridCacheMvccCandidate owner) {
+ return false;
+ }
+
+ @Override public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /**
+ * Gets remaining allowed time.
+ *
+ * @return Remaining time. {@code 0} if timeout isn't specified. {@code -1} if time is out.
+ * @throws IgniteTxTimeoutCheckedException If timed out.
+ */
+ protected long remainingTime() throws IgniteTxTimeoutCheckedException {
+ if (timeout <= 0)
+ return 0;
+
+ long timeLeft = timeout - (U.currentTimeMillis() - startTime());
+
+ if (timeLeft <= 0)
+ throw timeoutException();
+
+ return timeLeft;
+ }
+
+ /**
+ * @return Timeout exception.
+ */
+ @NotNull protected IgniteTxTimeoutCheckedException timeoutException() {
+ return new IgniteTxTimeoutCheckedException("Failed to acquire lock within provided timeout for " +
+ "transaction [timeout=" + timeout + ", tx=" + tx + ']');
+ }
+
+ /**
+ * Start iterating the data rows and form batches.
+ *
+ * @param topLocked Whether topology was already locked.
+ */
+ protected abstract void map(boolean topLocked);
+
+ /**
+ * Lock request timeout object.
+ */
+ private class LockTimeoutObject extends GridTimeoutObjectAdapter {
+ /**
+ * Default constructor.
+ */
+ LockTimeoutObject() {
+ super(timeout);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onTimeout() {
+ if (log.isDebugEnabled())
+ log.debug("Timed out waiting for lock response: " + this);
+
+ onDone(timeoutException());
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(LockTimeoutObject.class, this);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java
index 53d901a..435f281 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.cache.distributed.near;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
@@ -51,10 +52,15 @@ public class GridNearTxFastFinishFuture extends GridFutureAdapter<IgniteInternal
return commit;
}
+ /** {@inheritDoc} */
+ @Override public GridNearTxLocal tx() {
+ return tx;
+ }
+
/**
* @param clearThreadMap {@code True} if need remove tx from thread map.
*/
- public void finish(boolean clearThreadMap) {
+ public void finish(boolean commit, boolean clearThreadMap, boolean onTimeout) {
try {
if (commit) {
tx.state(PREPARING);
@@ -79,4 +85,9 @@ public class GridNearTxFastFinishFuture extends GridFutureAdapter<IgniteInternal
onDone(tx);
}
}
+
+ /** {@inheritDoc} */
+ @Override public void onNodeStop(IgniteCheckedException e) {
+ onDone(tx, e);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishAndAckFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishAndAckFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishAndAckFuture.java
new file mode 100644
index 0000000..011b2ec
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishAndAckFuture.java
@@ -0,0 +1,144 @@
+/*
+ * 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.distributed.near;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteInClosure;
+
+/**
+ *
+ */
+public class GridNearTxFinishAndAckFuture extends GridFutureAdapter<IgniteInternalTx> implements NearTxFinishFuture {
+ /** */
+ private final NearTxFinishFuture finishFut;
+
+ /**
+ * @param finishFut Finish future.
+ */
+ GridNearTxFinishAndAckFuture(NearTxFinishFuture finishFut) {
+ this.finishFut = finishFut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean commit() {
+ return finishFut.commit();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridNearTxLocal tx() {
+ return finishFut.tx();
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ public void finish(boolean commit, boolean clearThreadMap, boolean onTimeout) {
+ finishFut.finish(commit, clearThreadMap, onTimeout);
+
+ if (finishFut.commit()) {
+ finishFut.listen((IgniteInClosure)new IgniteInClosure<NearTxFinishFuture>() {
+ @Override public void apply(final NearTxFinishFuture fut) {
+ GridNearTxLocal tx = fut.tx();
+
+ IgniteInternalFuture<Void> ackFut = null;
+
+ MvccQueryTracker tracker = tx.mvccQueryTracker();
+
+ MvccSnapshot mvccSnapshot = tx.mvccSnapshot();
+
+ if (tracker != null)
+ ackFut = tracker.onDone(tx, commit);
+ else if (mvccSnapshot != null) {
+ if (commit)
+ ackFut = tx.context().coordinators().ackTxCommit(mvccSnapshot);
+ else
+ tx.context().coordinators().ackTxRollback(mvccSnapshot);
+ }
+
+ if (ackFut != null) {
+ ackFut.listen(new IgniteInClosure<IgniteInternalFuture<Void>>() {
+ @Override public void apply(IgniteInternalFuture<Void> ackFut) {
+ Exception err = null;
+
+ try {
+ fut.get();
+
+ ackFut.get();
+ }
+ catch (Exception e) {
+ err = e;
+ }
+ catch (Error e) {
+ onDone(e);
+
+ throw e;
+ }
+
+ if (err != null)
+ onDone(err);
+ else
+ onDone(fut.tx());
+ }
+ });
+ }
+ else
+ finishWithFutureResult(fut);
+ }
+ });
+ }
+ else {
+ finishFut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ finishWithFutureResult(fut);
+ }
+ });
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onNodeStop(IgniteCheckedException e) {
+ super.onDone(finishFut.tx(), e);
+ }
+
+ /**
+ * @param fut Future.
+ */
+ private void finishWithFutureResult(IgniteInternalFuture<IgniteInternalTx> fut) {
+ try {
+ onDone(fut.get());
+ }
+ catch (IgniteCheckedException | RuntimeException e) {
+ onDone(e);
+ }
+ catch (Error e) {
+ onDone(e);
+
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridNearTxFinishAndAckFuture.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishFuture.java
index 2974563..447d899 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishFuture.java
@@ -43,11 +43,15 @@ import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishRequest;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccCoordinator;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.transactions.IgniteTxHeuristicCheckedException;
import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.C1;
@@ -136,6 +140,13 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
return commit;
}
+ /**
+ * @return Cache context.
+ */
+ GridCacheSharedContext<K, V> context() {
+ return cctx;
+ }
+
/** {@inheritDoc} */
@Override public IgniteUuid futureId() {
return futId;
@@ -390,15 +401,12 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
fut.getClass() == CheckRemoteTxMiniFuture.class;
}
- /**
- * Initializes future.
- *
- * @param commit Commit flag.
- * @param clearThreadMap If {@code true} removes {@link GridNearTxLocal} from thread map.
- * @param onTimeout If {@code true} called from timeout handler.
- */
+ /** {@inheritDoc} */
@SuppressWarnings("ForLoopReplaceableByForEach")
public void finish(final boolean commit, final boolean clearThreadMap, final boolean onTimeout) {
+ if (!cctx.mvcc().addFuture(this, futureId()))
+ return;
+
if (tx.onNeedCheckBackup()) {
assert tx.onePhaseCommit();
@@ -465,18 +473,38 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
private void doFinish(boolean commit, boolean clearThreadMap) {
try {
if (tx.localFinish(commit, clearThreadMap) || (!commit && tx.state() == UNKNOWN)) {
+ GridLongList waitTxs = tx.mvccWaitTransactions();
+
+ if (waitTxs != null) {
+ MvccSnapshot snapshot = tx.mvccSnapshot();
+
+ MvccCoordinator crd = cctx.coordinators().currentCoordinator();
+
+ assert snapshot != null;
+
+ if (snapshot.coordinatorVersion() == crd.coordinatorVersion()) {
+ IgniteInternalFuture fut = cctx.coordinators()
+ .waitTxsFuture(cctx.coordinators().currentCoordinatorId(), waitTxs);
+
+ add(fut);
+ }
+ }
+
if ((tx.onePhaseCommit() && needFinishOnePhase(commit)) || (!tx.onePhaseCommit() && mappings != null)) {
if (mappings.single()) {
GridDistributedTxMapping mapping = mappings.singleMapping();
if (mapping != null) {
- assert !hasFutures() : futures();
+ assert !hasFutures() || waitTxs != null : futures();
finish(1, mapping, commit, !clearThreadMap);
}
}
- else
+ else {
+ assert !hasFutures() || waitTxs != null : futures();
+
finish(mappings.mappings(), commit, !clearThreadMap);
+ }
}
markInitialized();
@@ -501,6 +529,11 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
}
}
+ /** {@inheritDoc} */
+ @Override public void onNodeStop(IgniteCheckedException e) {
+ super.onDone(tx, e);
+ }
+
/**
*
*/
@@ -732,8 +765,6 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
* @param {@code true} If need to add completed version on finish.
*/
private void finish(Iterable<GridDistributedTxMapping> mappings, boolean commit, boolean useCompletedVer) {
- assert !hasFutures() : futures();
-
int miniId = 0;
// Create mini futures.
@@ -750,11 +781,11 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
private void finish(int miniId, GridDistributedTxMapping m, boolean commit, boolean useCompletedVer) {
ClusterNode n = m.primary();
- assert !m.empty() : m + " " + tx.state();
+ assert !m.empty() || m.queryUpdate() : m + " " + tx.state();
CacheWriteSynchronizationMode syncMode = tx.syncMode();
- if (m.explicitLock())
+ if (m.explicitLock() || m.queryUpdate())
syncMode = FULL_SYNC;
GridNearTxFinishRequest req = new GridNearTxFinishRequest(
@@ -775,6 +806,7 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
tx.size(),
tx.subjectId(),
tx.taskNameHash(),
+ tx.mvccSnapshot(),
tx.activeCachesDeploymentEnabled()
);
@@ -869,6 +901,11 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
return "CheckRemoteTxMiniFuture[nodes=" + fut.nodes() + ", done=" + f.isDone() + "]";
}
+ else if (f instanceof MvccFuture) {
+ MvccFuture fut = (MvccFuture)f;
+
+ return "WaitPreviousTxsFut[mvccCrd=" + fut.coordinatorNodeId() + ", done=" + f.isDone() + "]";
+ }
else
return "[loc=true, done=" + f.isDone() + "]";
}
@@ -909,7 +946,8 @@ public final class GridNearTxFinishFuture<K, V> extends GridCacheCompoundIdentit
0,
tx.activeCachesDeploymentEnabled(),
!waitRemoteTxs && (tx.needReturnValue() && tx.implicit()),
- waitRemoteTxs);
+ waitRemoteTxs,
+ null);
finishReq.checkCommitted(true);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishRequest.java
index 00c29e5..6b5aa90 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishRequest.java
@@ -24,6 +24,7 @@ import java.util.UUID;
import org.apache.ignite.cache.CacheWriteSynchronizationMode;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxFinishRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -43,6 +44,9 @@ public class GridNearTxFinishRequest extends GridDistributedTxFinishRequest {
/** Mini future ID. */
private int miniId;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -88,6 +92,7 @@ public class GridNearTxFinishRequest extends GridDistributedTxFinishRequest {
int txSize,
@Nullable UUID subjId,
int taskNameHash,
+ MvccSnapshot mvccSnapshot,
boolean addDepInfo) {
super(
xidVer,
@@ -111,6 +116,15 @@ public class GridNearTxFinishRequest extends GridDistributedTxFinishRequest {
explicitLock(explicitLock);
storeEnabled(storeEnabled);
+
+ this.mvccSnapshot = mvccSnapshot;
+ }
+
+ /**
+ * @return Mvcc info.
+ */
+ @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
}
/**
@@ -178,6 +192,12 @@ public class GridNearTxFinishRequest extends GridDistributedTxFinishRequest {
writer.incrementState();
+ case 22:
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
+ return false;
+
+ writer.incrementState();
+
}
return true;
@@ -202,6 +222,14 @@ public class GridNearTxFinishRequest extends GridDistributedTxFinishRequest {
reader.incrementState();
+ case 22:
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
}
return reader.afterMessageRead(GridNearTxFinishRequest.class);
@@ -214,7 +242,7 @@ public class GridNearTxFinishRequest extends GridDistributedTxFinishRequest {
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 22;
+ return 23;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
index a4acfa6..d03d0d3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
@@ -47,10 +47,10 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
-import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
import org.apache.ignite.internal.processors.cache.GridCacheOperation;
import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.GridCacheVersionedFuture;
import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheEntry;
@@ -61,6 +61,9 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLoca
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxPrepareFuture;
import org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtDetachedCacheEntry;
import org.apache.ignite.internal.processors.cache.dr.GridCacheDrInfo;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTrackerImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
@@ -68,6 +71,7 @@ import org.apache.ignite.internal.processors.cache.transactions.TransactionProxy
import org.apache.ignite.internal.processors.cache.transactions.TransactionProxyImpl;
import org.apache.ignite.internal.processors.cache.transactions.TransactionProxyRollbackOnlyImpl;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
import org.apache.ignite.internal.transactions.IgniteTxOptimisticCheckedException;
import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
@@ -134,6 +138,10 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
private static final AtomicReferenceFieldUpdater<GridNearTxLocal, NearTxFinishFuture> FINISH_FUT_UPD =
AtomicReferenceFieldUpdater.newUpdater(GridNearTxLocal.class, NearTxFinishFuture.class, "finishFut");
+ /** */
+ private static final String TX_TYPE_MISMATCH_ERR_MSG =
+ "SQL queries and cache operations may not be used in the same transaction.";
+
/** DHT mappings. */
private IgniteTxMappings mappings;
@@ -179,6 +187,15 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
/** Tx label. */
private @Nullable String lb;
+ /** */
+ private MvccQueryTracker mvccTracker;
+
+ /** Whether this transaction is for SQL operations or not.<p>
+ * {@code null} means there haven't been any calls made on this transaction, and first operation will give this
+ * field actual value.
+ */
+ private Boolean sql;
+
/**
* Empty constructor required for {@link Externalizable}.
*/
@@ -196,6 +213,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
* @param isolation Isolation.
* @param timeout Timeout.
* @param storeEnabled Store enabled flag.
+ * @param sql Whether this transaction was started via SQL API or not, or {@code null} if unknown.
* @param txSize Transaction size.
* @param subjId Subject ID.
* @param taskNameHash Task name hash code.
@@ -211,6 +229,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
TransactionIsolation isolation,
long timeout,
boolean storeEnabled,
+ Boolean sql,
int txSize,
@Nullable UUID subjId,
int taskNameHash,
@@ -237,11 +256,20 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
mappings = implicitSingle ? new IgniteTxMappingsSingleImpl() : new IgniteTxMappingsImpl();
+ this.sql = sql;
+
initResult();
trackTimeout = timeout() > 0 && !implicit() && cctx.time().addTimeoutObject(this);
}
+ /**
+ * @return Mvcc query version tracker.
+ */
+ public MvccQueryTracker mvccQueryTracker() {
+ return mvccTracker;
+ }
+
/** {@inheritDoc} */
@Override public boolean near() {
return true;
@@ -546,7 +574,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
assert key != null;
try {
- beforePut(cacheCtx, retval);
+ beforePut(cacheCtx, retval, false);
final GridCacheReturn ret = new GridCacheReturn(localResult(), false);
@@ -689,7 +717,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
final boolean retval
) {
try {
- beforePut(cacheCtx, retval);
+ beforePut(cacheCtx, retval, false);
}
catch (IgniteCheckedException e) {
return new GridFinishedFuture(e);
@@ -855,6 +883,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
* @param ret Return value.
* @param skipStore Skip store flag.
* @param singleRmv {@code True} for single key remove operation ({@link Cache#remove(Object)}.
+ * @param recovery Recovery flag.
+ * @param dataCenterId Optional data center Id.
* @return Future for entry values loading.
*/
private <K, V> IgniteInternalFuture<Void> enlistWrite(
@@ -978,6 +1008,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
* @param skipStore Skip store flag.
* @param singleRmv {@code True} for single key remove operation ({@link Cache#remove(Object)}.
* @param keepBinary Keep binary flag.
+ * @param recovery Recovery flag.
* @param dataCenterId Optional data center ID.
* @return Future for enlisting writes.
*/
@@ -1245,6 +1276,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
resolveTaskName(),
null,
keepBinary,
+ null, // TODO IGNITE-7371
null) : null;
if (res != null) {
@@ -1263,7 +1295,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
entryProcessor,
resolveTaskName(),
null,
- keepBinary);
+ keepBinary,
+ null); // TODO IGNITE-7371
}
}
catch (ClusterTopologyCheckedException e) {
@@ -1524,6 +1557,9 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
cacheCtx.checkSecurity(SecurityPermission.CACHE_REMOVE);
+ if (cacheCtx.mvccEnabled() && !isOperationAllowed(false))
+ return txTypeMismatchFinishFuture();
+
if (retval)
needReturnValue(true);
@@ -1732,6 +1768,109 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
}
/**
+ * @param cctx Cache context.
+ * @return Mvcc snapshot for read inside tx (initialized once for OPTIMISTIC SERIALIZABLE and REPEATABLE_READ txs).
+ */
+ private MvccSnapshot mvccReadSnapshot(GridCacheContext cctx) {
+ if (!cctx.mvccEnabled() || mvccTracker == null)
+ return null;
+
+ return mvccTracker.snapshot();
+ }
+
+ /**
+ * @param cacheCtx Cache context.
+ * @param cacheIds Involved cache ids.
+ * @param parts Partitions.
+ * @param schema Schema name.
+ * @param qry Query string.
+ * @param params Query parameters.
+ * @param flags Flags.
+ * @param pageSize Fetch page size.
+ * @param timeout Timeout.
+ * @return Operation future.
+ */
+ public IgniteInternalFuture<Long> updateAsync(GridCacheContext cacheCtx,
+ int[] cacheIds, int[] parts, String schema, String qry, Object[] params,
+ int flags, int pageSize, long timeout) {
+ try {
+ beforePut(cacheCtx, false, true);
+
+ return updateAsync(new GridNearTxQueryEnlistFuture(
+ cacheCtx,
+ this,
+ cacheIds,
+ parts,
+ schema,
+ qry,
+ params,
+ flags,
+ pageSize,
+ timeout));
+ }
+ catch (IgniteCheckedException e) {
+ return new GridFinishedFuture(e);
+ }
+ catch (RuntimeException e) {
+ onException();
+
+ throw e;
+ }
+ }
+
+ /**
+ * @param cacheCtx Cache context.
+ * @param it Entries iterator.
+ * @param pageSize Page size.
+ * @param timeout Timeout.
+ * @param sequential Sequential locking flag.
+ * @return Operation future.
+ */
+ public IgniteInternalFuture<Long> updateAsync(GridCacheContext cacheCtx,
+ UpdateSourceIterator<?> it, int pageSize, long timeout, boolean sequential) {
+ try {
+ beforePut(cacheCtx, false, true);
+
+ return updateAsync(new GridNearTxQueryResultsEnlistFuture(cacheCtx, this,
+ timeout, it, pageSize, sequential));
+ }
+ catch (IgniteCheckedException e) {
+ return new GridFinishedFuture(e);
+ }
+ catch (RuntimeException e) {
+ onException();
+
+ throw e;
+ }
+ }
+
+ /**
+ * @param fut Enlist future.
+ * @return Operation future.
+ */
+ public IgniteInternalFuture<Long> updateAsync(GridNearTxAbstractEnlistFuture fut) {
+ fut.init();
+
+ return nonInterruptable(new GridEmbeddedFuture<>(fut.chain(new CX1<IgniteInternalFuture<Long>, Boolean>() {
+ @Override public Boolean applyx(IgniteInternalFuture<Long> fut0) throws IgniteCheckedException {
+ return fut0.get() != null;
+ }
+ }), new PLC1<Long>(null) {
+ @Override protected Long postLock(Long val) throws IgniteCheckedException {
+ Long res = fut.get();
+
+ assert mvccSnapshot != null;
+ assert res != null;
+
+ if (res > 0)
+ mvccSnapshot.incrementOperationCounter();
+
+ return res;
+ }
+ }));
+ }
+
+ /**
* @param cacheCtx Cache context.
* @param keys Keys to get.
* @param deserializeBinary Deserialize binary flag.
@@ -1744,7 +1883,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
public <K, V> IgniteInternalFuture<Map<K, V>> getAllAsync(
final GridCacheContext cacheCtx,
@Nullable final AffinityTopologyVersion entryTopVer,
- Collection<KeyCacheObject> keys,
+ final Collection<KeyCacheObject> keys,
final boolean deserializeBinary,
final boolean skipVals,
final boolean keepCacheObjects,
@@ -1754,8 +1893,36 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
if (F.isEmpty(keys))
return new GridFinishedFuture<>(Collections.<K, V>emptyMap());
+ if (cacheCtx.mvccEnabled() && !isOperationAllowed(false))
+ return txTypeMismatchFinishFuture();
+
init();
+ if (cacheCtx.mvccEnabled() && (optimistic() && !readCommitted()) && mvccTracker == null) {
+ // TODO IGNITE-7388: support async tx rollback (e.g. on timeout).
+ boolean canRemap = cctx.lockedTopologyVersion(null) == null;
+
+ mvccTracker = new MvccQueryTrackerImpl(cacheCtx, canRemap);
+
+ return new GridEmbeddedFuture<>(mvccTracker.requestSnapshot(topologyVersion()),
+ new IgniteBiClosure<MvccSnapshot, Exception, IgniteInternalFuture<Map<K, V>>>() {
+ @Override public IgniteInternalFuture<Map<K, V>> apply(MvccSnapshot snapshot, Exception e) {
+ if (e != null)
+ return new GridFinishedFuture<>(e);
+
+ return getAllAsync(cacheCtx,
+ entryTopVer,
+ keys,
+ deserializeBinary,
+ skipVals,
+ keepCacheObjects,
+ skipStore,
+ recovery,
+ needVer);
+ }
+ });
+ }
+
int keysCnt = keys.size();
boolean single = keysCnt == 1;
@@ -1878,6 +2045,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
resolveTaskName(),
null,
txEntry.keepBinary(),
+ null, // TODO IGNITE-7371
null);
if (getRes != null) {
@@ -1896,7 +2064,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
transformClo,
resolveTaskName(),
null,
- txEntry.keepBinary());
+ txEntry.keepBinary(),
+ null); // TODO IGNITE-7371
}
// If value is in cache and passed the filter.
@@ -2057,6 +2226,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
* @param skipVals Skip values flag.
* @param keepCacheObjects Keep cache objects flag.
* @param skipStore Skip store flag.
+ * @param recovery Recovery flag..
* @throws IgniteCheckedException If failed.
* @return Enlisted keys.
*/
@@ -2175,7 +2345,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
resolveTaskName(),
null,
txEntry.keepBinary(),
- null);
+ null,
+ null); // TODO IGNITE-7371
if (getRes != null) {
val = getRes.value();
@@ -2193,7 +2364,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
transformClo,
resolveTaskName(),
null,
- txEntry.keepBinary());
+ txEntry.keepBinary(),
+ null); // TODO IGNITE-7371
}
if (val != null) {
@@ -2261,6 +2433,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
resolveTaskName(),
accessPlc,
!deserializeBinary,
+ mvccReadSnapshot(cacheCtx), // TODO IGNITE-7371
null) : null;
if (getRes != null) {
@@ -2279,7 +2452,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
null,
resolveTaskName(),
accessPlc,
- !deserializeBinary);
+ !deserializeBinary,
+ mvccReadSnapshot(cacheCtx)); // TODO IGNITE-7371
}
if (val != null) {
@@ -2466,6 +2640,13 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
}
/**
+ * @return Finished future with error message about tx type mismatch.
+ */
+ private static IgniteInternalFuture txTypeMismatchFinishFuture() {
+ return new GridFinishedFuture(new IgniteCheckedException(TX_TYPE_MISMATCH_ERR_MSG));
+ }
+
+ /**
* @param cacheCtx Cache context.
* @param loadFut Missing keys load future.
* @param ret Future result.
@@ -2551,7 +2732,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
* @param expiryPlc Expiry policy.
* @return Future with {@code True} value if loading took place.
*/
- public IgniteInternalFuture<Void> loadMissing(
+ private IgniteInternalFuture<Void> loadMissing(
final GridCacheContext cacheCtx,
AffinityTopologyVersion topVer,
boolean readThrough,
@@ -2610,7 +2791,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
skipVals,
needVer,
/*keepCacheObject*/true,
- recovery
+ recovery,
+ mvccReadSnapshot(cacheCtx)
).chain(new C1<IgniteInternalFuture<Object>, Void>() {
@Override public Void apply(IgniteInternalFuture<Object> f) {
try {
@@ -2641,7 +2823,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
expiryPlc0,
skipVals,
needVer,
- /*keepCacheObject*/true
+ /*keepCacheObject*/true,
+ mvccReadSnapshot(cacheCtx)
).chain(new C1<IgniteInternalFuture<Map<Object, Object>>, Void>() {
@Override public Void apply(IgniteInternalFuture<Map<Object, Object>> f) {
try {
@@ -2738,7 +2921,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
resolveTaskName(),
expiryPlc0,
txEntry == null ? keepBinary : txEntry.keepBinary(),
- null);
+ null,
+ null); // TODO IGNITE-7371
if (res == null) {
if (misses == null)
@@ -3158,7 +3342,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
if (state != COMMITTING && state != COMMITTED)
throw isRollbackOnly() ? timedOut() ? timeoutException() : rollbackException() :
new IgniteCheckedException("Invalid transaction state for commit [state=" + state() +
- ", tx=" + this + ']');
+ ", tx=" + this + ']');
else {
if (log.isDebugEnabled())
log.debug("Invalid transaction state for commit (another thread is committing): " + this);
@@ -3307,56 +3491,44 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
if (log.isDebugEnabled())
log.debug("Committing near local tx: " + this);
- NearTxFinishFuture fut = finishFut;
-
- if (fut != null)
- return chainFinishFuture(fut, true, true, false);
-
- if (fastFinish()) {
- GridNearTxFastFinishFuture fut0;
+ final NearTxFinishFuture fut, fut0 = finishFut; boolean fastFinish;
- if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFastFinishFuture(this, true)))
- return chainFinishFuture(finishFut, true, true, false);
-
- fut0.finish(false);
-
- return fut0;
- }
-
- final GridNearTxFinishFuture fut0;
-
- if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFinishFuture<>(cctx, this, true)))
+ if (fut0 != null || !FINISH_FUT_UPD.compareAndSet(this, null, fut = finishFuture(fastFinish = fastFinish(), true)))
return chainFinishFuture(finishFut, true, true, false);
- cctx.mvcc().addFuture(fut0, fut0.futureId());
-
- final IgniteInternalFuture<?> prepareFut = prepareNearTxLocal();
+ if (!fastFinish) {
+ final IgniteInternalFuture<?> prepareFut = prepareNearTxLocal();
- prepareFut.listen(new CI1<IgniteInternalFuture<?>>() {
- @Override public void apply(IgniteInternalFuture<?> f) {
- try {
- // Make sure that here are no exceptions.
- prepareFut.get();
+ prepareFut.listen(new CI1<IgniteInternalFuture<?>>() {
+ @Override public void apply(IgniteInternalFuture<?> f) {
+ try {
+ // Make sure that here are no exceptions.
+ prepareFut.get();
- fut0.finish(true, true, false);
- }
- catch (Error | RuntimeException e) {
- COMMIT_ERR_UPD.compareAndSet(GridNearTxLocal.this, null, e);
+ fut.finish(true, true, false);
+ }
+ catch (Error | RuntimeException e) {
+ COMMIT_ERR_UPD.compareAndSet(GridNearTxLocal.this, null, e);
- fut0.finish(false, true, false);
+ fut.finish(false, true, false);
- throw e;
- }
- catch (IgniteCheckedException e) {
- COMMIT_ERR_UPD.compareAndSet(GridNearTxLocal.this, null, e);
+ throw e;
+ }
+ catch (IgniteCheckedException e) {
+ COMMIT_ERR_UPD.compareAndSet(GridNearTxLocal.this, null, e);
- if (!(e instanceof NodeStoppingException))
- fut0.finish(false, true, true);
+ if (!(e instanceof NodeStoppingException))
+ fut.finish(false, true, true);
+ else
+ fut.onNodeStop(e);
+ }
}
- }
- });
+ });
+ }
+ else
+ fut.finish(true, false, false);
- return fut0;
+ return fut;
}
/** {@inheritDoc} */
@@ -3400,61 +3572,70 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
if (onTimeout && prepFut instanceof GridNearTxPrepareFutureAdapter && !prepFut.isDone())
((GridNearTxPrepareFutureAdapter) prepFut).onNearTxLocalTimeout();
- NearTxFinishFuture fut = finishFut;
+ final NearTxFinishFuture fut, fut0 = finishFut; boolean fastFinish;
- if (fut != null)
+ if (fut0 != null)
return chainFinishFuture(finishFut, false, clearThreadMap, onTimeout);
- // Enable fast finish only from tx thread.
- if (clearThreadMap && fastFinish()) {
- GridNearTxFastFinishFuture fut0;
-
- if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFastFinishFuture(this, false)))
- return chainFinishFuture(finishFut, false, true, onTimeout);
-
- fut0.finish(true);
-
- return fut0;
- }
-
- final GridNearTxFinishFuture fut0;
-
- if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFinishFuture<>(cctx, this, false)))
+ if (!FINISH_FUT_UPD.compareAndSet(this, null, fut = finishFuture(fastFinish = clearThreadMap && fastFinish(), false)))
return chainFinishFuture(finishFut, false, clearThreadMap, onTimeout);
- cctx.mvcc().addFuture(fut0, fut0.futureId());
+ rollbackFuture(fut);
- if (prepFut == null || prepFut.isDone()) {
- try {
- // Check for errors in prepare future.
- if (prepFut != null)
- prepFut.get();
- }
- catch (IgniteCheckedException e) {
- if (log.isDebugEnabled())
- log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']');
+ if (!fastFinish) {
+ if (prepFut == null || prepFut.isDone()) {
+ try {
+ // Check for errors in prepare future.
+ if (prepFut != null)
+ prepFut.get();
+ }
+ catch (IgniteCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']');
+ }
+
+ fut.finish(false, clearThreadMap, onTimeout);
}
+ else {
+ prepFut.listen(new CI1<IgniteInternalFuture<?>>() {
+ @Override public void apply(IgniteInternalFuture<?> f) {
+ try {
+ // Check for errors in prepare future.
+ f.get();
+ }
+ catch (IgniteCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']');
+ }
- fut0.finish(false, clearThreadMap, onTimeout);
- }
- else {
- prepFut.listen(new CI1<IgniteInternalFuture<?>>() {
- @Override public void apply(IgniteInternalFuture<?> f) {
- try {
- // Check for errors in prepare future.
- f.get();
- }
- catch (IgniteCheckedException e) {
- if (log.isDebugEnabled())
- log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']');
+ fut.finish(false, clearThreadMap, onTimeout);
}
+ });
+ }
+ }
+ else
+ fut.finish(false, true, onTimeout);
- fut0.finish(false, clearThreadMap, onTimeout);
- }
- });
+ return fut;
+ }
+
+ /**
+ * @return Transaction commit future.
+ * @param fast {@code True} in case of fast finish.
+ * @param commit {@code True} if commit.
+ */
+ private NearTxFinishFuture finishFuture(boolean fast, boolean commit) {
+ NearTxFinishFuture fut = fast ? new GridNearTxFastFinishFuture(this, commit) :
+ new GridNearTxFinishFuture<>(cctx, this, commit);
+
+ if (mvccQueryTracker() != null || mvccSnapshot != null || txState.mvccEnabled(cctx)) {
+ if (commit)
+ fut = new GridNearTxFinishAndAckFuture(fut);
+ else
+ fut.listen(new AckCoordinatorOnRollback(this));
}
- return fut0;
+ return fut;
}
/** {@inheritDoc} */
@@ -3489,12 +3670,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
assert rollbackFut.isDone() : rollbackFut;
}
- else {
- if (!cctx.mvcc().addFuture(rollbackFut, rollbackFut.futureId()))
- return;
-
+ else
rollbackFut.finish(false, clearThreadMap, onTimeout);
- }
}
else {
finishFut.listen(new IgniteInClosure<IgniteInternalFuture<IgniteInternalTx>>() {
@@ -3540,7 +3717,9 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
* @return {@code True} if 'fast finish' path can be used for transaction completion.
*/
private boolean fastFinish() {
- return writeMap().isEmpty() && ((optimistic() && !serializable()) || readMap().isEmpty());
+ return writeMap().isEmpty()
+ && ((optimistic() && !serializable()) || readMap().isEmpty())
+ && (mappings.single() || F.view(mappings.mappings(), CU.FILTER_QUERY_MAPPING).isEmpty());
}
/**
@@ -3985,6 +4164,20 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
}
/**
+ * @return {@code true} if this transaction does not have type flag set or it matches invoking operation,
+ * {@code false} otherwise.
+ */
+ public boolean isOperationAllowed(boolean sqlOp) {
+ if (sql == null) {
+ sql = sqlOp;
+
+ return true;
+ }
+
+ return sql == sqlOp;
+ }
+
+ /**
* @return Public API proxy.
*/
public TransactionProxy proxy() {
@@ -4185,13 +4378,19 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
/**
* @param cacheCtx Cache context.
* @param retval Return value flag.
+ * @param sql SQL operation flag.
* @throws IgniteCheckedException If failed.
*/
- private void beforePut(GridCacheContext cacheCtx, boolean retval) throws IgniteCheckedException {
+ private void beforePut(GridCacheContext cacheCtx, boolean retval, boolean sql) throws IgniteCheckedException {
+ assert !sql || cacheCtx.mvccEnabled();
+
checkUpdatesAllowed(cacheCtx);
cacheCtx.checkSecurity(SecurityPermission.CACHE_PUT);
+ if (cacheCtx.mvccEnabled() && !isOperationAllowed(sql))
+ throw new IgniteCheckedException(TX_TYPE_MISMATCH_ERR_MSG);
+
if (retval)
needReturnValue(true);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareFutureAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareFutureAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareFutureAdapter.java
index 5c0fcec..a9b1848 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareFutureAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareFutureAdapter.java
@@ -49,7 +49,7 @@ import static org.apache.ignite.internal.processors.cache.GridCacheOperation.NOO
* Common code for tx prepare in optimistic and pessimistic modes.
*/
public abstract class GridNearTxPrepareFutureAdapter extends
- GridCacheCompoundFuture<GridNearTxPrepareResponse, IgniteInternalTx> implements GridCacheVersionedFuture<IgniteInternalTx> {
+ GridCacheCompoundFuture<Object, IgniteInternalTx> implements GridCacheVersionedFuture<IgniteInternalTx> {
/** Logger reference. */
protected static final AtomicReference<IgniteLogger> logRef = new AtomicReference<>();
@@ -58,9 +58,9 @@ public abstract class GridNearTxPrepareFutureAdapter extends
AtomicReferenceFieldUpdater.newUpdater(GridNearTxPrepareFutureAdapter.class, Throwable.class, "err");
/** */
- private static final IgniteReducer<GridNearTxPrepareResponse, IgniteInternalTx> REDUCER =
- new IgniteReducer<GridNearTxPrepareResponse, IgniteInternalTx>() {
- @Override public boolean collect(GridNearTxPrepareResponse e) {
+ private static final IgniteReducer<Object, IgniteInternalTx> REDUCER =
+ new IgniteReducer<Object, IgniteInternalTx>() {
+ @Override public boolean collect(Object e) {
return true;
}
@@ -165,7 +165,7 @@ public abstract class GridNearTxPrepareFutureAdapter extends
* @param txMapping Transaction mapping.
*/
final void checkOnePhase(GridDhtTxMapping txMapping) {
- if (tx.storeWriteThrough())
+ if (tx.storeWriteThrough() || tx.txState().mvccEnabled(cctx)) // TODO IGNITE-3479 (onePhase + mvcc)
return;
Map<UUID, Collection<UUID>> map = txMapping.transactionNodes();
@@ -255,7 +255,7 @@ public abstract class GridNearTxPrepareFutureAdapter extends
txEntry.ttl(CU.toTtl(expiry.getExpiryForAccess()));
}
- if (!m.empty()) {
+ if (m.queryUpdate() || !m.empty()) {
// This step is very important as near and DHT versions grow separately.
cctx.versions().onReceived(nodeId, res.dhtVersion());
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareRequest.java
index a670609..55c809d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareRequest.java
@@ -58,6 +58,9 @@ public class GridNearTxPrepareRequest extends GridDistributedTxPrepareRequest {
/** */
private static final int ALLOW_WAIT_TOP_FUT_FLAG_MASK = 0x10;
+ /** */
+ private static final int REQUEST_MVCC_CNTR_FLAG_MASK = 0x20;
+
/** Future ID. */
private IgniteUuid futId;
@@ -150,6 +153,20 @@ public class GridNearTxPrepareRequest extends GridDistributedTxPrepareRequest {
}
/**
+ * @return {@code True} if need request MVCC counter on primary node on prepare step.
+ */
+ public boolean requestMvccCounter() {
+ return isFlag(REQUEST_MVCC_CNTR_FLAG_MASK);
+ }
+
+ /**
+ * @param val {@code True} if need request MVCC counter on primary node on prepare step.
+ */
+ public void requestMvccCounter(boolean val) {
+ setFlag(val, REQUEST_MVCC_CNTR_FLAG_MASK);
+ }
+
+ /**
* @return {@code True} if it is safe for first client request to wait for topology future
* completion.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareResponse.java
index 8162168..92db463 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxPrepareResponse.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheReturn;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxPrepareResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -97,6 +98,9 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
/** Not {@code null} if client node should remap transaction. */
private AffinityTopologyVersion clientRemapVer;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
/**
* Empty constructor required by {@link Externalizable}.
*/
@@ -146,6 +150,20 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
}
/**
+ * @param mvccSnapshot Mvcc info.
+ */
+ public void mvccSnapshot(MvccSnapshot mvccSnapshot) {
+ this.mvccSnapshot = mvccSnapshot;
+ }
+
+ /**
+ * @return Mvcc info.
+ */
+ @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
* @return One-phase commit state on primary node.
*/
public boolean onePhaseCommit() {
@@ -389,30 +407,36 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
writer.incrementState();
case 15:
- if (!writer.writeCollection("ownedValKeys", ownedValKeys, MessageCollectionItemType.MSG))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 16:
- if (!writer.writeCollection("ownedValVals", ownedValVals, MessageCollectionItemType.MSG))
+ if (!writer.writeCollection("ownedValKeys", ownedValKeys, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 17:
- if (!writer.writeCollection("pending", pending, MessageCollectionItemType.MSG))
+ if (!writer.writeCollection("ownedValVals", ownedValVals, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 18:
- if (!writer.writeMessage("retVal", retVal))
+ if (!writer.writeCollection("pending", pending, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 19:
+ if (!writer.writeMessage("retVal", retVal))
+ return false;
+
+ writer.incrementState();
+
+ case 20:
if (!writer.writeMessage("writeVer", writeVer))
return false;
@@ -475,7 +499,7 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
reader.incrementState();
case 15:
- ownedValKeys = reader.readCollection("ownedValKeys", MessageCollectionItemType.MSG);
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -483,7 +507,7 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
reader.incrementState();
case 16:
- ownedValVals = reader.readCollection("ownedValVals", MessageCollectionItemType.MSG);
+ ownedValKeys = reader.readCollection("ownedValKeys", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -491,7 +515,7 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
reader.incrementState();
case 17:
- pending = reader.readCollection("pending", MessageCollectionItemType.MSG);
+ ownedValVals = reader.readCollection("ownedValVals", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -499,7 +523,7 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
reader.incrementState();
case 18:
- retVal = reader.readMessage("retVal");
+ pending = reader.readCollection("pending", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -507,6 +531,14 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
reader.incrementState();
case 19:
+ retVal = reader.readMessage("retVal");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 20:
writeVer = reader.readMessage("writeVer");
if (!reader.isLastRead())
@@ -526,7 +558,7 @@ public class GridNearTxPrepareResponse extends GridDistributedTxPrepareResponse
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 20;
+ return 21;
}
/** {@inheritDoc} */
[38/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
index 4b98060..bfe0001 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
@@ -37,7 +37,6 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.managers.communication.GridIoManager;
import org.apache.ignite.internal.managers.deployment.GridDeploymentManager;
-import org.apache.ignite.internal.managers.discovery.DiscoCache;
import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
@@ -48,6 +47,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopolo
import org.apache.ignite.internal.processors.cache.distributed.dht.PartitionsEvictManager;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
import org.apache.ignite.internal.processors.cache.jta.CacheJtaManagerAdapter;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccProcessor;
import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteCacheSnapshotManager;
import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
@@ -794,6 +794,13 @@ public class GridCacheSharedContext<K, V> {
}
/**
+ * @return Cache mvcc coordinator manager.
+ */
+ public MvccProcessor coordinators() {
+ return kernalCtx.coordinators();
+ }
+
+ /**
* @return Partition evict manager.
*/
public PartitionsEvictManager evict() {
@@ -855,7 +862,7 @@ public class GridCacheSharedContext<K, V> {
/**
* Captures all ongoing operations that we need to wait before we can proceed to the next topology version.
* This method must be called only after
- * {@link GridDhtPartitionTopology#updateTopologyVersion(GridDhtTopologyFuture, DiscoCache, long, boolean)}
+ * {@link GridDhtPartitionTopology#updateTopologyVersion}
* method is called so that all new updates will wait to switch to the new version.
* This method will capture:
* <ul>
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUpdateTxResult.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUpdateTxResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUpdateTxResult.java
index 92af83b..b646cf9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUpdateTxResult.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUpdateTxResult.java
@@ -17,8 +17,12 @@
package org.apache.ignite.internal.processors.cache;
+import java.util.List;
+import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.pagemem.wal.WALPointer;
-import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.jetbrains.annotations.Nullable;
@@ -29,26 +33,62 @@ public class GridCacheUpdateTxResult {
/** Success flag.*/
private final boolean success;
- /** Old value. */
- @GridToStringInclude
- private final CacheObject oldVal;
-
- /** Partition idx. */
+ /** Partition update counter. */
private long updateCntr;
/** */
+ private GridLongList mvccWaitTxs;
+
+ /** */
+ private GridFutureAdapter<GridCacheUpdateTxResult> fut;
+
+ /** */
private WALPointer logPtr;
+ /** */
+ private List<MvccLinkAwareSearchRow> mvccHistory;
+
+ /**
+ * Constructor.
+ *
+ * @param success Success flag.
+ */
+ GridCacheUpdateTxResult(boolean success) {
+ this.success = success;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param success Success flag.
+ * @param logPtr Logger WAL pointer for the update.
+ */
+ GridCacheUpdateTxResult(boolean success, WALPointer logPtr) {
+ this.success = success;
+ this.logPtr = logPtr;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param success Success flag.
+ * @param fut Update future.
+ */
+ GridCacheUpdateTxResult(boolean success, GridFutureAdapter<GridCacheUpdateTxResult> fut) {
+ this.success = success;
+ this.fut = fut;
+ }
+
/**
* Constructor.
*
* @param success Success flag.
- * @param oldVal Old value (if any),
+ * @param updateCntr Update counter.
* @param logPtr Logger WAL pointer for the update.
*/
- GridCacheUpdateTxResult(boolean success, @Nullable CacheObject oldVal, WALPointer logPtr) {
+ GridCacheUpdateTxResult(boolean success, long updateCntr, WALPointer logPtr) {
this.success = success;
- this.oldVal = oldVal;
+ this.updateCntr = updateCntr;
this.logPtr = logPtr;
}
@@ -56,20 +96,21 @@ public class GridCacheUpdateTxResult {
* Constructor.
*
* @param success Success flag.
- * @param oldVal Old value (if any).
+ * @param updateCntr Update counter.
* @param logPtr Logger WAL pointer for the update.
+ * @param mvccWaitTxs List of transactions to wait for completion.
*/
- GridCacheUpdateTxResult(boolean success, @Nullable CacheObject oldVal, long updateCntr, WALPointer logPtr) {
+ GridCacheUpdateTxResult(boolean success, long updateCntr, WALPointer logPtr, GridLongList mvccWaitTxs) {
this.success = success;
- this.oldVal = oldVal;
this.updateCntr = updateCntr;
this.logPtr = logPtr;
+ this.mvccWaitTxs = mvccWaitTxs;
}
/**
- * @return Partition idx.
+ * @return Partition update counter.
*/
- public long updatePartitionCounter() {
+ public long updateCounter() {
return updateCntr;
}
@@ -88,10 +129,33 @@ public class GridCacheUpdateTxResult {
}
/**
- * @return Old value.
+ * @return Update future.
+ */
+ @Nullable public IgniteInternalFuture<GridCacheUpdateTxResult> updateFuture() {
+ return fut;
+ }
+
+ /**
+ * @return List of transactions to wait for completion.
+ */
+ @Nullable public GridLongList mvccWaitTransactions() {
+ return mvccWaitTxs;
+ }
+
+ /**
+ *
+ * @return Mvcc history rows.
+ */
+ @Nullable public List<MvccLinkAwareSearchRow> mvccHistory() {
+ return mvccHistory;
+ }
+
+ /**
+ *
+ * @param mvccHistory Mvcc history rows.
*/
- @Nullable public CacheObject oldValue() {
- return oldVal;
+ public void mvccHistory(List<MvccLinkAwareSearchRow> mvccHistory) {
+ this.mvccHistory = mvccHistory;
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
index 213fb2e..9ef470c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
@@ -32,6 +32,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.LongAdder;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.configuration.Factory;
@@ -67,10 +68,13 @@ import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException
import org.apache.ignite.internal.managers.discovery.IgniteClusterNode;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException;
+import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTxMapping;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog;
+import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -99,7 +103,6 @@ import org.apache.ignite.lang.IgniteReducer;
import org.apache.ignite.lifecycle.LifecycleAware;
import org.apache.ignite.plugin.CachePluginConfiguration;
import org.apache.ignite.plugin.security.SecurityException;
-import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionConcurrency;
import org.apache.ignite.transactions.TransactionIsolation;
@@ -173,6 +176,14 @@ public class GridCacheUtils {
/** System cache name. */
public static final String UTILITY_CACHE_NAME = "ignite-sys-cache";
+ /** Reserved cache names */
+ public static final String[] RESERVED_NAMES = new String[] {
+ SYS_CACHE_HADOOP_MR,
+ UTILITY_CACHE_NAME,
+ MetaStorage.METASTORAGE_CACHE_NAME,
+ TxLog.TX_LOG_CACHE_NAME,
+ };
+
/** */
public static final String CONTINUOUS_QRY_LOG_CATEGORY = "org.apache.ignite.continuous.query";
@@ -319,6 +330,18 @@ public class GridCacheUtils {
}
};
+ /** Query mapped filter. */
+ public static final IgnitePredicate<GridDistributedTxMapping> FILTER_QUERY_MAPPING = new P1<GridDistributedTxMapping>() {
+
+ @Override public boolean apply(GridDistributedTxMapping m) {
+ return m.queryUpdate();
+ }
+
+ @Override public String toString() {
+ return "FILTER_QUERY_MAPPING";
+ }
+ };
+
/** Transaction entry to key. */
private static final IgniteClosure tx2key = new C1<IgniteTxEntry, Object>() {
@Override public Object apply(IgniteTxEntry e) {
@@ -589,6 +612,30 @@ public class GridCacheUtils {
}
/**
+ * @return Long reducer.
+ */
+ public static IgniteReducer<Long, Long> longReducer() {
+ return new IgniteReducer<Long, Long>() {
+ private final LongAdder res = new LongAdder();
+
+ @Override public boolean collect(Long l) {
+ if(l != null)
+ res.add(l);
+
+ return true;
+ }
+
+ @Override public Long reduce() {
+ return res.sum();
+ }
+
+ @Override public String toString() {
+ return "Long reducer: " + res;
+ }
+ };
+ }
+
+ /**
* Gets reducer that aggregates maps into one.
*
* @param size Predicted size of the resulting map to avoid resizings.
@@ -1543,6 +1590,17 @@ public class GridCacheUtils {
}
/**
+ * @param name Cache name.
+ * @throws IllegalArgumentException In case the name is not valid.
+ */
+ public static void validateNewCacheName(String name) throws IllegalArgumentException {
+ validateCacheName(name);
+
+ A.ensure(!isReservedCacheName(name), "Cache name cannot be \"" + name +
+ "\" because it is reserved for internal purposes.");
+ }
+
+ /**
* @param cacheNames Cache names to validate.
* @throws IllegalArgumentException In case the name is not valid.
*/
@@ -1558,7 +1616,20 @@ public class GridCacheUtils {
public static void validateConfigurationCacheNames(Collection<CacheConfiguration> ccfgs)
throws IllegalArgumentException {
for (CacheConfiguration ccfg : ccfgs)
- validateCacheName(ccfg.getName());
+ validateNewCacheName(ccfg.getName());
+ }
+
+ /**
+ * @param name Cache name.
+ * @return {@code True} if it is a reserved cache name.
+ */
+ public static boolean isReservedCacheName(String name) {
+ for (String reserved : RESERVED_NAMES) {
+ if (reserved.equals(name))
+ return true;
+ }
+
+ return false;
}
/**
@@ -1830,6 +1901,44 @@ public class GridCacheUtils {
}
/**
+ * @param sctx Shared context.
+ * @param cacheIds Cache ids.
+ * @return First partitioned cache or {@code null} in case no partitioned cache ids are in list.
+ */
+ public static GridCacheContext<?, ?> firstPartitioned(GridCacheSharedContext<?, ?> sctx, int[] cacheIds) {
+ for (int i = 0; i < cacheIds.length; i++) {
+ GridCacheContext<?, ?> cctx = sctx.cacheContext(cacheIds[i]);
+
+ if (cctx == null)
+ throw new CacheException("Failed to find cache.");
+
+ if (!cctx.isLocal() && !cctx.isReplicated())
+ return cctx;
+ }
+
+ return null;
+ }
+
+ /**
+ * @param sctx Shared context.
+ * @param cacheIds Cache ids.
+ * @return First partitioned cache or {@code null} in case no partitioned cache ids are in list.
+ */
+ public static GridCacheContext<?, ?> firstPartitioned(GridCacheSharedContext<?, ?> sctx, Iterable<Integer> cacheIds) {
+ for (Integer i : cacheIds) {
+ GridCacheContext<?, ?> cctx = sctx.cacheContext(i);
+
+ if (cctx == null)
+ throw new CacheException("Failed to find cache.");
+
+ if (!cctx.isLocal() && !cctx.isReplicated())
+ return cctx;
+ }
+
+ return null;
+ }
+
+ /**
* @param cacheName Name of cache or cache template.
* @return {@code true} if cache name ends with asterisk (*), and therefire is a template name.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
index fa25412..a021394 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
@@ -17,25 +17,33 @@
package org.apache.ignite.internal.processors.cache;
+import java.util.List;
import java.util.Map;
import javax.cache.Cache;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteDhtDemandedPartitionsMap;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
import org.apache.ignite.internal.processors.cache.persistence.RootPage;
import org.apache.ignite.internal.processors.cache.persistence.RowStore;
import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
import org.apache.ignite.internal.processors.cache.tree.PendingEntriesTree;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateResult;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
import org.apache.ignite.internal.util.GridAtomicLong;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.IgniteTree;
import org.apache.ignite.internal.util.lang.GridCloseableIterator;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.lang.GridIterator;
import org.apache.ignite.internal.util.lang.IgniteInClosure2X;
+import org.apache.ignite.lang.IgniteBiTuple;
import org.jetbrains.annotations.Nullable;
/**
@@ -171,6 +179,202 @@ public interface IgniteCacheOffheapManager {
/**
* @param cctx Cache context.
* @param key Key.
+ * @return Cached row, if available, null otherwise.
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable public CacheDataRow mvccRead(GridCacheContext cctx, KeyCacheObject key, MvccSnapshot ver)
+ throws IgniteCheckedException;
+
+ /**
+ * For testing only.
+ *
+ * @param cctx Cache context.
+ * @param key Key.
+ * @return All stored versions for given key.
+ * @throws IgniteCheckedException If failed.
+ */
+ public List<IgniteBiTuple<Object, MvccVersion>> mvccAllVersions(GridCacheContext cctx, KeyCacheObject key)
+ throws IgniteCheckedException;
+
+ /**
+ * Returns iterator over the all row versions for the given key.
+ *
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ * @return Iterator over all versions.
+ * @throws IgniteCheckedException If failed.
+ */
+ GridCursor<CacheDataRow> mvccAllVersionsCursor(GridCacheContext cctx, KeyCacheObject key, Object x)
+ throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @return {@code True} if value was inserted.
+ * @throws IgniteCheckedException If failed.
+ */
+ default boolean mvccInitialValue(
+ GridCacheMapEntry entry,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime
+ ) throws IgniteCheckedException {
+ return mvccInitialValue(entry, val, ver, expireTime, null, null);
+ }
+
+ /**
+ * @param entry Entry.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @param mvccVer MVCC version.
+ * @param newMvccVer New MVCC version.
+ * @return {@code True} if value was inserted.
+ * @throws IgniteCheckedException If failed.
+ */
+ public boolean mvccInitialValue(
+ GridCacheMapEntry entry,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer
+ ) throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @param mvccVer MVCC version.
+ * @param newMvccVer New MVCC version.
+ * @param txState Tx state hint for the mvcc version.
+ * @param newTxState Tx state hint for the new mvcc version.
+ * @return {@code True} if value was inserted.
+ * @throws IgniteCheckedException If failed.
+ */
+ public boolean mvccInitialValueIfAbsent(
+ GridCacheMapEntry entry,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte txState,
+ byte newTxState
+ ) throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @param val Value.
+ * @param ver Cache version.
+ * @param expireTime Expire time.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param primary {@code True} if on primary node.
+ * @param needHistory Flag to collect history.
+ * @param noCreate Flag indicating that row should not be created if absent.
+ * @return Update result.
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable public MvccUpdateResult mvccUpdate(
+ GridCacheMapEntry entry,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param primary {@code True} if on primary node.
+ * @param needHistory Flag to collect history.
+ * @return Update result.
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable public MvccUpdateResult mvccRemove(
+ GridCacheMapEntry entry,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory) throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Update result.
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable public MvccUpdateResult mvccLock(
+ GridCacheMapEntry entry,
+ MvccSnapshot mvccSnapshot
+ ) throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @param val Value.
+ * @param ver Version.
+ * @param mvccVer MVCC version.
+ * @param newMvccVer New MVCC version.
+ * @return {@code True} if value was inserted.
+ * @throws IgniteCheckedException If failed.
+ */
+ public boolean mvccUpdateRowWithPreloadInfo(
+ GridCacheMapEntry entry,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState
+ ) throws IgniteCheckedException;
+
+ /**
+ * @param primary {@code True} if on primary node.
+ * @param entry Entry.
+ * @param val Value.
+ * @param ver Cache version.
+ * @param expireTime Expire time.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Transactions to wait for before finishing current transaction.
+ * @throws IgniteCheckedException If failed.
+ */
+ GridLongList mvccUpdateNative(
+ boolean primary,
+ GridCacheMapEntry entry,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException;
+
+ /**
+ * @param primary {@code True} if on primary node.
+ * @param entry Entry.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Transactions to wait for before finishing current transaction.
+ * @throws IgniteCheckedException If failed.
+ */
+ GridLongList mvccRemoveNative(
+ boolean primary,
+ GridCacheMapEntry entry,
+ MvccSnapshot mvccSnapshot
+ ) throws IgniteCheckedException;
+
+ /**
+ * @param entry Entry.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void mvccRemoveAll(GridCacheMapEntry entry)
+ throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
* @param val Value.
* @param ver Version.
* @param expireTime Expire time.
@@ -209,6 +413,7 @@ public interface IgniteCacheOffheapManager {
public int onUndeploy(ClassLoader ldr);
/**
+ *
* @param cacheId Cache ID.
* @param primary Primary entries flag.
* @param backup Backup entries flag.
@@ -219,7 +424,8 @@ public interface IgniteCacheOffheapManager {
public GridIterator<CacheDataRow> cacheIterator(int cacheId,
boolean primary,
boolean backup,
- final AffinityTopologyVersion topVer)
+ AffinityTopologyVersion topVer,
+ @Nullable MvccSnapshot mvccSnapshot)
throws IgniteCheckedException;
/**
@@ -228,7 +434,20 @@ public interface IgniteCacheOffheapManager {
* @return Partition data iterator.
* @throws IgniteCheckedException If failed.
*/
- public GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, final int part) throws IgniteCheckedException;
+ public default GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, final int part)
+ throws IgniteCheckedException {
+ return cachePartitionIterator(cacheId, part, null);
+ }
+
+ /**
+ * @param cacheId Cache ID.
+ * @param part Partition.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Partition data iterator.
+ * @throws IgniteCheckedException If failed.
+ */
+ public GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, final int part,
+ @Nullable MvccSnapshot mvccSnapshot) throws IgniteCheckedException;
/**
* @param part Partition number.
@@ -251,6 +470,7 @@ public interface IgniteCacheOffheapManager {
* @return Partition data iterator.
* @throws IgniteCheckedException If failed.
*/
+ // TODO: MVCC>
public IgniteRebalanceIterator rebalanceIterator(IgniteDhtDemandedPartitionsMap parts, AffinityTopologyVersion topVer)
throws IgniteCheckedException;
@@ -263,6 +483,7 @@ public interface IgniteCacheOffheapManager {
* @return Entries iterator.
* @throws IgniteCheckedException If failed.
*/
+ // TODO: MVCC>
public <K, V> GridCloseableIterator<Cache.Entry<K, V>> cacheEntriesIterator(
GridCacheContext cctx,
final boolean primary,
@@ -276,6 +497,7 @@ public interface IgniteCacheOffheapManager {
* @return Iterator.
* @throws IgniteCheckedException If failed.
*/
+ // TODO: MVCC>
public GridCloseableIterator<KeyCacheObject> cacheKeysIterator(int cacheId, final int part)
throws IgniteCheckedException;
@@ -287,6 +509,7 @@ public interface IgniteCacheOffheapManager {
* @return Entries count.
* @throws IgniteCheckedException If failed.
*/
+ // TODO: MVCC>
public long cacheEntriesCount(int cacheId, boolean primary, boolean backup, AffinityTopologyVersion topVer)
throws IgniteCheckedException;
@@ -402,7 +625,7 @@ public interface IgniteCacheOffheapManager {
long updateCounter();
/**
- *
+ * @param val Update counter.
*/
void updateCounter(long val);
@@ -412,6 +635,16 @@ public interface IgniteCacheOffheapManager {
public long nextUpdateCounter();
/**
+ * @return Next mvcc update counter.
+ */
+ long nextMvccUpdateCounter();
+
+ /**
+ * @return Current mvcc update counter value.
+ */
+ long mvccUpdateCounter();
+
+ /**
* @return Initial update counter.
*/
public long initialUpdateCounter();
@@ -436,6 +669,24 @@ public interface IgniteCacheOffheapManager {
/**
* @param cctx Cache context.
+ * @param cleanupRows Rows to cleanup.
+ * @throws IgniteCheckedException If failed.
+ * @return Cleaned rows count.
+ */
+ public int cleanup(GridCacheContext cctx, @Nullable List<MvccLinkAwareSearchRow> cleanupRows)
+ throws IgniteCheckedException;
+
+ /**
+ *
+ * @param cctx Cache context.
+ * @param row Row.
+ * @throws IgniteCheckedException
+ */
+ public void updateTxState(GridCacheContext cctx, CacheSearchRow row)
+ throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
* @param key Key.
* @param val Value.
* @param ver Version.
@@ -454,6 +705,163 @@ public interface IgniteCacheOffheapManager {
/**
* @param cctx Cache context.
* @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param mvccVer MVCC version.
+ * @param newMvccVer New MVCC version.
+ * @return {@code True} if new value was inserted.
+ * @throws IgniteCheckedException If failed.
+ */
+ boolean mvccInitialValue(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param mvccVer MVCC version.
+ * @param newMvccVer New MVCC version.
+ * @param txState Tx state hint for the mvcc version.
+ * @param newTxState Tx state hint for the new mvcc version.
+ * @return {@code True} if new value was inserted.
+ * @throws IgniteCheckedException If failed.
+ */
+ boolean mvccInitialValueIfAbsent(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte txState,
+ byte newTxState) throws IgniteCheckedException;
+
+ /**
+ *
+ * @param cctx Grid cache context.
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expiration time.
+ * @param mvccVer Mvcc version.
+ * @param newMvccVer New mvcc version.
+ * @return {@code true} on success.
+ * @throws IgniteCheckedException, if failed.
+ */
+ boolean mvccUpdateRowWithPreloadInfo(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param primary {@code True} if update is executed on primary node.
+ * @param needHistory Flag to collect history.
+ * @param noCreate Flag indicating that row should not be created if absent.
+ * @return Update result.
+ * @throws IgniteCheckedException If failed.
+ */
+ MvccUpdateResult mvccUpdate(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param primary {@code True} if update is executed on primary node.
+ * @param needHistory Flag to collect history.
+ * @return List of transactions to wait for.
+ * @throws IgniteCheckedException If failed.
+ */
+ MvccUpdateResult mvccRemove(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return List of transactions to wait for.
+ * @throws IgniteCheckedException If failed.
+ */
+ MvccUpdateResult mvccLock(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param primary {@code True} if update is executed on primary node.
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Update result.
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable GridLongList mvccUpdateNative(
+ GridCacheContext cctx,
+ boolean primary,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param primary {@code True} if update is executed on primary node.
+ * @param key Key.
+ * @param mvccSnapshot MVCC snapshot.
+ * @return List of transactions to wait for.
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable GridLongList mvccRemoveNative(GridCacheContext cctx,
+ boolean primary,
+ KeyCacheObject key,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @throws IgniteCheckedException If failed.
+ */
+ void mvccRemoveAll(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
* @param c Closure.
* @throws IgniteCheckedException If failed.
*/
@@ -476,12 +884,58 @@ public interface IgniteCacheOffheapManager {
public CacheDataRow find(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException;
/**
+ * Returns iterator over the all row versions for the given key.
+ *
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ * @return Iterator over all versions.
+ * @throws IgniteCheckedException If failed.
+ */
+ GridCursor<CacheDataRow> mvccAllVersionsCursor(GridCacheContext cctx, KeyCacheObject key, Object x)
+ throws IgniteCheckedException;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @return Data row.
+ * @throws IgniteCheckedException If failed.
+ */
+ public CacheDataRow mvccFind(GridCacheContext cctx, KeyCacheObject key, MvccSnapshot snapshot)
+ throws IgniteCheckedException;
+
+ /**
+ * For testing only.
+ *
+ * @param cctx Cache context.
+ * @param key Key.
+ * @return All stored versions for given key.
+ * @throws IgniteCheckedException If failed.
+ */
+ List<IgniteBiTuple<Object, MvccVersion>> mvccFindAllVersions(GridCacheContext cctx, KeyCacheObject key)
+ throws IgniteCheckedException;
+
+ /**
* @return Data cursor.
* @throws IgniteCheckedException If failed.
*/
public GridCursor<? extends CacheDataRow> cursor() throws IgniteCheckedException;
/**
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ * @return Data cursor.
+ * @throws IgniteCheckedException If failed.
+ */
+ public GridCursor<? extends CacheDataRow> cursor(Object x) throws IgniteCheckedException;
+
+ /**
+ * @param mvccSnapshot MVCC snapshot.
+ * @return Data cursor.
+ * @throws IgniteCheckedException If failed.
+ */
+ public GridCursor<? extends CacheDataRow> cursor(MvccSnapshot mvccSnapshot) throws IgniteCheckedException;
+
+ /**
* @param cacheId Cache ID.
* @return Data cursor.
* @throws IgniteCheckedException If failed.
@@ -490,6 +944,15 @@ public interface IgniteCacheOffheapManager {
/**
* @param cacheId Cache ID.
+ * @param mvccSnapshot Mvcc snapshot.
+ * @return Data cursor.
+ * @throws IgniteCheckedException If failed.
+ */
+ public GridCursor<? extends CacheDataRow> cursor(int cacheId, MvccSnapshot mvccSnapshot)
+ throws IgniteCheckedException;
+
+ /**
+ * @param cacheId Cache ID.
* @param lower Lower bound.
* @param upper Upper bound.
* @return Data cursor.
@@ -510,6 +973,18 @@ public interface IgniteCacheOffheapManager {
KeyCacheObject upper, Object x) throws IgniteCheckedException;
/**
+ * @param cacheId Cache ID.
+ * @param lower Lower bound.
+ * @param upper Upper bound.
+ * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+ * @param snapshot Mvcc snapshot.
+ * @return Data cursor.
+ * @throws IgniteCheckedException If failed.
+ */
+ public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
+ KeyCacheObject upper, Object x, MvccSnapshot snapshot) throws IgniteCheckedException;
+
+ /**
* Destroys the tree associated with the store.
*
* @throws IgniteCheckedException If failed.
[06/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/src/sql_get_info_test.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/src/sql_get_info_test.cpp b/modules/platforms/cpp/odbc-test/src/sql_get_info_test.cpp
index 630597c..7842704 100644
--- a/modules/platforms/cpp/odbc-test/src/sql_get_info_test.cpp
+++ b/modules/platforms/cpp/odbc-test/src/sql_get_info_test.cpp
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(TestValues)
CheckStrInfo(SQL_LIKE_ESCAPE_CLAUSE, "N");
CheckStrInfo(SQL_MAX_ROW_SIZE_INCLUDES_LONG, "Y");
CheckStrInfo(SQL_MULT_RESULT_SETS, "N");
- CheckStrInfo(SQL_MULTIPLE_ACTIVE_TXN, "N");
+ CheckStrInfo(SQL_MULTIPLE_ACTIVE_TXN, "Y");
CheckStrInfo(SQL_ORDER_BY_COLUMNS_IN_SELECT, "N");
CheckStrInfo(SQL_PROCEDURE_TERM, "stored procedure");
CheckStrInfo(SQL_PROCEDURES, "N");
@@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(TestValues)
CheckIntInfo(SQL_CREATE_VIEW, 0);
CheckIntInfo(SQL_CURSOR_SENSITIVITY, SQL_INSENSITIVE);
CheckIntInfo(SQL_DDL_INDEX, SQL_DI_CREATE_INDEX | SQL_DI_DROP_INDEX);
- CheckIntInfo(SQL_DEFAULT_TXN_ISOLATION, 0);
+ CheckIntInfo(SQL_DEFAULT_TXN_ISOLATION, SQL_TXN_REPEATABLE_READ);
CheckIntInfo(SQL_DROP_ASSERTION, 0);
CheckIntInfo(SQL_DROP_CHARACTER_SET, 0);
CheckIntInfo(SQL_DROP_COLLATION, 0);
@@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(TestValues)
CheckIntInfo(SQL_SQL92_GRANT, 0);
CheckIntInfo(SQL_SQL92_REVOKE, 0);
CheckIntInfo(SQL_STANDARD_CLI_CONFORMANCE, 0);
- CheckIntInfo(SQL_TXN_ISOLATION_OPTION, 0);
+ CheckIntInfo(SQL_TXN_ISOLATION_OPTION, SQL_TXN_REPEATABLE_READ);
CheckIntInfo(SQL_UNION, SQL_U_UNION | SQL_U_UNION_ALL);
CheckIntInfo(SQL_SCHEMA_USAGE, SQL_SU_DML_STATEMENTS | SQL_SU_TABLE_DEFINITION | SQL_SU_PRIVILEGE_DEFINITION |
@@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(TestValues)
CheckShortInfo(SQL_MAX_CONCURRENT_ACTIVITIES, 0);
CheckShortInfo(SQL_CURSOR_COMMIT_BEHAVIOR, SQL_CB_PRESERVE);
CheckShortInfo(SQL_CURSOR_ROLLBACK_BEHAVIOR, SQL_CB_PRESERVE);
- CheckShortInfo(SQL_TXN_CAPABLE, SQL_TC_NONE);
+ CheckShortInfo(SQL_TXN_CAPABLE, SQL_TC_DDL_COMMIT);
CheckShortInfo(SQL_QUOTED_IDENTIFIER_CASE, SQL_IC_SENSITIVE);
CheckShortInfo(SQL_ACTIVE_ENVIRONMENTS, 0);
CheckShortInfo(SQL_CONCAT_NULL_BEHAVIOR, SQL_CB_NULL);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc-test/src/transaction_test.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc-test/src/transaction_test.cpp b/modules/platforms/cpp/odbc-test/src/transaction_test.cpp
new file mode 100644
index 0000000..fb2b6c9
--- /dev/null
+++ b/modules/platforms/cpp/odbc-test/src/transaction_test.cpp
@@ -0,0 +1,738 @@
+/*
+ * 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.
+ */
+
+#ifdef _WIN32
+# include <windows.h>
+#endif
+
+#include <sql.h>
+#include <sqlext.h>
+
+#include <string>
+
+#ifndef _MSC_VER
+# define BOOST_TEST_DYN_LINK
+#endif
+
+#include <boost/test/unit_test.hpp>
+
+#include "ignite/ignition.h"
+
+#include "test_utils.h"
+#include "odbc_test_suite.h"
+
+using namespace ignite;
+using namespace ignite_test;
+
+using namespace boost::unit_test;
+
+/**
+ * Test setup fixture.
+ */
+struct TransactionTestSuiteFixture : public odbc::OdbcTestSuite
+{
+ static Ignite StartAdditionalNode(const char* name)
+ {
+ return StartTestNode("queries-transaction.xml", name);
+ }
+
+ /**
+ * Constructor.
+ */
+ TransactionTestSuiteFixture() :
+ grid(StartAdditionalNode("NodeMain"))
+ {
+ // No-op.
+ }
+
+ /**
+ * Destructor.
+ */
+ ~TransactionTestSuiteFixture()
+ {
+ // No-op.
+ }
+
+ /**
+ * Insert test string value in cache and make all the neccessary checks.
+ *
+ * @param key Key.
+ * @param value Value.
+ */
+ void InsertTestValue(int64_t key, const std::string& value)
+ {
+ SQLCHAR insertReq[] = "INSERT INTO TestType(_key, strField) VALUES(?, ?)";
+
+ SQLRETURN ret;
+
+ ret = SQLPrepare(stmt, insertReq, SQL_NTS);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ char strField[1024] = { 0 };
+ SQLLEN strFieldLen = 0;
+
+ ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_BIGINT, 0, 0, &key, 0, 0);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(strField),
+ sizeof(strField), &strField, sizeof(strField), &strFieldLen);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ strncpy(strField, value.c_str(), sizeof(strField));
+ strFieldLen = SQL_NTS;
+
+ ret = SQLExecute(stmt);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ SQLLEN affected = 0;
+ ret = SQLRowCount(stmt, &affected);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ BOOST_CHECK_EQUAL(affected, 1);
+
+ ret = SQLMoreResults(stmt);
+
+ if (ret != SQL_NO_DATA)
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ResetStatement();
+ }
+
+ /**
+ * Update test string value in cache and make all the neccessary checks.
+ *
+ * @param key Key.
+ * @param value Value.
+ */
+ void UpdateTestValue(int64_t key, const std::string& value)
+ {
+ SQLCHAR insertReq[] = "UPDATE TestType SET strField=? WHERE _key=?";
+
+ SQLRETURN ret;
+
+ ret = SQLPrepare(stmt, insertReq, SQL_NTS);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ char strField[1024] = { 0 };
+ SQLLEN strFieldLen = 0;
+
+ ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(strField),
+ sizeof(strField), &strField, sizeof(strField), &strFieldLen);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_BIGINT, 0, 0, &key, 0, 0);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ strncpy(strField, value.c_str(), sizeof(strField));
+ strFieldLen = SQL_NTS;
+
+ ret = SQLExecute(stmt);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ SQLLEN affected = 0;
+ ret = SQLRowCount(stmt, &affected);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ BOOST_CHECK_EQUAL(affected, 1);
+
+ ret = SQLMoreResults(stmt);
+
+ if (ret != SQL_NO_DATA)
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ResetStatement();
+ }
+
+ /**
+ * Delete test string value.
+ *
+ * @param key Key.
+ */
+ void DeleteTestValue(int64_t key)
+ {
+ SQLCHAR insertReq[] = "DELETE FROM TestType WHERE _key=?";
+
+ SQLRETURN ret;
+
+ ret = SQLPrepare(stmt, insertReq, SQL_NTS);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_BIGINT, 0, 0, &key, 0, 0);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLExecute(stmt);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ SQLLEN affected = 0;
+ ret = SQLRowCount(stmt, &affected);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ BOOST_CHECK_EQUAL(affected, 1);
+
+ ret = SQLMoreResults(stmt);
+
+ if (ret != SQL_NO_DATA)
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ResetStatement();
+ }
+
+ /**
+ * Selects and checks the value.
+ *
+ * @param key Key.
+ * @param expect Expected value.
+ */
+ void CheckTestValue(int64_t key, const std::string& expect)
+ {
+ // Just selecting everything to make sure everything is OK
+ SQLCHAR selectReq[] = "SELECT strField FROM TestType WHERE _key = ?";
+
+ char strField[1024] = { 0 };
+ SQLLEN strFieldLen = 0;
+
+ SQLRETURN ret = SQLBindCol(stmt, 1, SQL_C_CHAR, &strField, sizeof(strField), &strFieldLen);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_BIGINT, 0, 0, &key, 0, 0);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq));
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLFetch(stmt);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ BOOST_CHECK_EQUAL(std::string(strField, strFieldLen), expect);
+
+ ret = SQLFetch(stmt);
+
+ BOOST_CHECK_EQUAL(ret, SQL_NO_DATA);
+
+ ret = SQLMoreResults(stmt);
+
+ if (ret != SQL_NO_DATA)
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ResetStatement();
+ }
+
+ /**
+ * Selects and checks that value is absent.
+ *
+ * @param key Key.
+ */
+ void CheckNoTestValue(int64_t key)
+ {
+ // Just selecting everything to make sure everything is OK
+ SQLCHAR selectReq[] = "SELECT strField FROM TestType WHERE _key = ?";
+
+ char strField[1024] = { 0 };
+ SQLLEN strFieldLen = 0;
+
+ SQLRETURN ret = SQLBindCol(stmt, 1, SQL_C_CHAR, &strField, sizeof(strField), &strFieldLen);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_BIGINT, 0, 0, &key, 0, 0);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLExecDirect(stmt, selectReq, sizeof(selectReq));
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLFetch(stmt);
+
+ BOOST_CHECK_EQUAL(ret, SQL_NO_DATA);
+
+ ret = SQLMoreResults(stmt);
+
+ if (ret != SQL_NO_DATA)
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ResetStatement();
+ }
+
+ /**
+ * Reset statement state.
+ */
+ void ResetStatement()
+ {
+ SQLRETURN ret = SQLFreeStmt(stmt, SQL_RESET_PARAMS);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+
+ ret = SQLFreeStmt(stmt, SQL_UNBIND);
+
+ if (!SQL_SUCCEEDED(ret))
+ BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt));
+ }
+
+ /** Node started during the test. */
+ Ignite grid;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TransactionTestSuite, TransactionTestSuiteFixture)
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionCommit)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionRollbackInsert)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckNoTestValue(42);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionRollbackUpdate1)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ UpdateTestValue(42, "Other");
+
+ CheckTestValue(42, "Other");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionRollbackUpdate2)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+
+ UpdateTestValue(42, "Other");
+
+ CheckTestValue(42, "Other");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionRollbackDelete1)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ DeleteTestValue(42);
+
+ CheckNoTestValue(42);
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionRollbackDelete2)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+
+ DeleteTestValue(42);
+
+ CheckNoTestValue(42);
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionTxModeError)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache;nested_tx_mode=error");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ ret = ExecQuery("BEGIN");
+
+ BOOST_CHECK_EQUAL(ret, SQL_ERROR);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionTxModeIgnore)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache;nested_tx_mode=ignore");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ ret = ExecQuery("BEGIN");
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckNoTestValue(42);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionConnectionTxModeCommit)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache;nested_tx_mode=commit");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ ret = ExecQuery("BEGIN");
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ UpdateTestValue(42, "Other");
+
+ CheckTestValue(42, "Other");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentCommit)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentRollbackInsert)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckNoTestValue(42);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentRollbackUpdate1)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ UpdateTestValue(42, "Other");
+
+ CheckTestValue(42, "Other");
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentRollbackUpdate2)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+
+ UpdateTestValue(42, "Other");
+
+ CheckTestValue(42, "Other");
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentRollbackDelete1)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ DeleteTestValue(42);
+
+ CheckNoTestValue(42);
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentRollbackDelete2)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ CheckTestValue(42, "Some");
+
+ ret = SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+
+ DeleteTestValue(42);
+
+ CheckNoTestValue(42);
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentTxModeError)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache;nested_tx_mode=error");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ ret = ExecQuery("BEGIN");
+
+ BOOST_CHECK_EQUAL(ret, SQL_ERROR);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentTxModeIgnore)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache;nested_tx_mode=ignore");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ ret = ExecQuery("BEGIN");
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckNoTestValue(42);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionEnvironmentTxModeCommit)
+{
+ Connect("DRIVER={Apache Ignite};address=127.0.0.1:11110;schema=cache;nested_tx_mode=commit");
+
+ SQLRETURN ret = SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ InsertTestValue(42, "Some");
+
+ ret = ExecQuery("BEGIN");
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_DBC, dbc);
+
+ UpdateTestValue(42, "Other");
+
+ CheckTestValue(42, "Other");
+
+ ret = SQLEndTran(SQL_HANDLE_ENV, env, SQL_ROLLBACK);
+
+ ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_ENV, env);
+
+ CheckTestValue(42, "Some");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/Makefile.am
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/Makefile.am b/modules/platforms/cpp/odbc/Makefile.am
index 90e4dc9..f706f4f 100644
--- a/modules/platforms/cpp/odbc/Makefile.am
+++ b/modules/platforms/cpp/odbc/Makefile.am
@@ -83,6 +83,7 @@ libignite_odbc_la_SOURCES = \
src/protocol_version.cpp \
src/result_page.cpp \
src/row.cpp \
+ src/nested_tx_mode.cpp \
src/message.cpp \
src/column.cpp \
src/statement.cpp \
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h b/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
index c19f29d..27de7e8 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/config/configuration.h
@@ -26,6 +26,7 @@
#include "ignite/odbc/config/settable_value.h"
#include "ignite/odbc/ssl/ssl_mode.h"
#include "ignite/odbc/end_point.h"
+#include "ignite/odbc/nested_tx_mode.h"
namespace ignite
{
@@ -104,6 +105,9 @@ namespace ignite
/** Default value for password attribute. */
static const std::string password;
+
+ /** Default value for nestedTxMode attribute. */
+ static const NestedTxMode::Type nestedTxMode;
};
/**
@@ -537,6 +541,27 @@ namespace ignite
bool IsPasswordSet() const;
/**
+ * Get nested transaction mode.
+ *
+ * @return Nested transaction mode.
+ */
+ NestedTxMode::Type GetNestedTxMode() const;
+
+ /**
+ * Set nested transaction mode.
+ *
+ * @param mode Nested transaction mode.
+ */
+ void SetNestedTxMode(NestedTxMode::Type mode);
+
+ /**
+ * Check if the value set.
+ *
+ * @return @true if the value set.
+ */
+ bool IsNestedTxModeSet() const;
+
+ /**
* Get argument map.
*
* @param res Resulting argument map.
@@ -613,6 +638,9 @@ namespace ignite
/** Password. */
SettableValue<std::string> password;
+
+ /** Nested transaction mode. */
+ SettableValue<NestedTxMode::Type> nestedTxMode;
};
template<>
@@ -642,6 +670,10 @@ namespace ignite
template<>
void Configuration::AddToMap<ssl::SslMode::Type>(ArgumentMap& map, const std::string& key,
const SettableValue<ssl::SslMode::Type>& value);
+
+ template<>
+ void Configuration::AddToMap<NestedTxMode::Type>(ArgumentMap& map, const std::string& key,
+ const SettableValue<NestedTxMode::Type>& value);
}
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h b/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
index 2bfe331..605109e 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/config/connection_string_parser.h
@@ -97,6 +97,9 @@ namespace ignite
/** Connection attribute keyword for password attribute. */
static const std::string password;
+
+ /** Connection attribute keyword for nestedTxMode attribute. */
+ static const std::string nestedTxMode;
};
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h b/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
index 68d7816..b3ae5fc 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/connection.h
@@ -33,6 +33,7 @@ namespace ignite
{
namespace odbc
{
+ class Environment;
class Statement;
/**
@@ -99,6 +100,11 @@ namespace ignite
void Release();
/**
+ * Deregister self from the parent.
+ */
+ void Deregister();
+
+ /**
* Create statement associated with the connection.
*
* @return Pointer to valid instance on success and NULL on failure.
@@ -141,6 +147,13 @@ namespace ignite
const config::Configuration& GetConfiguration() const;
/**
+ * Is auto commit.
+ *
+ * @return @c true if the auto commit is enabled.
+ */
+ bool IsAutoCommit();
+
+ /**
* Create diagnostic record associated with the Connection instance.
*
* @param sqlState SQL state.
@@ -432,7 +445,10 @@ namespace ignite
/**
* Constructor.
*/
- Connection();
+ Connection(Environment* env);
+
+ /** Parent. */
+ Environment* env;
/** Client Socket. */
std::auto_ptr<SocketClient> socket;
@@ -443,6 +459,9 @@ namespace ignite
/** Login timeout in seconds. */
int32_t loginTimeout;
+ /** Autocommit flag. */
+ bool autoCommit;
+
/** Message parser. */
Parser parser;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable.h b/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable.h
index 46d79ce..613cbf4 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable.h
@@ -82,6 +82,13 @@ namespace ignite
*/
virtual void AddStatusRecord(const OdbcError& err) = 0;
+ /**
+ * Add new status record.
+ *
+ * @param rec Record.
+ */
+ virtual void AddStatusRecord(const DiagnosticRecord& rec) = 0;
+
protected:
/**
* Default constructor.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable_adapter.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable_adapter.h b/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable_adapter.h
index f1e9164..d8cfeb7 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable_adapter.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/diagnostic/diagnosable_adapter.h
@@ -69,7 +69,7 @@ namespace ignite
*
* @return Diagnostic record.
*/
- virtual const diagnostic::DiagnosticRecordStorage& GetDiagnosticRecords() const
+ virtual const DiagnosticRecordStorage& GetDiagnosticRecords() const
{
return diagnosticRecords;
}
@@ -79,7 +79,7 @@ namespace ignite
*
* @return Diagnostic record.
*/
- virtual diagnostic::DiagnosticRecordStorage& GetDiagnosticRecords()
+ virtual DiagnosticRecordStorage& GetDiagnosticRecords()
{
return diagnosticRecords;
}
@@ -110,6 +110,13 @@ namespace ignite
*/
virtual void AddStatusRecord(const OdbcError& err);
+ /**
+ * Add new status record.
+ *
+ * @param rec Record.
+ */
+ virtual void AddStatusRecord(const DiagnosticRecord& rec);
+
protected:
/** Diagnostic records. */
DiagnosticRecordStorage diagnosticRecords;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/environment.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/environment.h b/modules/platforms/cpp/odbc/include/ignite/odbc/environment.h
index 3eeade6..e6a171f 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/environment.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/environment.h
@@ -18,6 +18,8 @@
#ifndef _IGNITE_ODBC_ENVIRONMENT
#define _IGNITE_ODBC_ENVIRONMENT
+#include <set>
+
#include "ignite/odbc/diagnostic/diagnosable_adapter.h"
namespace ignite
@@ -32,6 +34,9 @@ namespace ignite
class Environment : public diagnostic::DiagnosableAdapter
{
public:
+ /** Connection set type. */
+ typedef std::set<Connection*> ConnectionSet;
+
/**
* Constructor.
*/
@@ -50,6 +55,13 @@ namespace ignite
Connection* CreateConnection();
/**
+ * Deregister connection.
+ *
+ * @param conn Connection to deregister.
+ */
+ void DeregisterConnection(Connection* conn);
+
+ /**
* Perform transaction commit on all the associated connections.
*/
void TransactionCommit();
@@ -125,6 +137,9 @@ namespace ignite
*/
SqlResult::Type InternalGetAttribute(int32_t attr, app::ApplicationDataBuffer& buffer);
+ /** Assotiated connections. */
+ ConnectionSet connections;
+
/** ODBC version. */
int32_t odbcVersion;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/message.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/message.h b/modules/platforms/cpp/odbc/include/ignite/odbc/message.h
index e119d1f..87ba064 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/message.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/message.h
@@ -109,9 +109,10 @@ namespace ignite
* @param sql SQL query.
* @param params Query arguments.
* @param timeout Timeout.
+ * @param autoCommit Auto commit flag.
*/
QueryExecuteRequest(const std::string& schema, const std::string& sql, const app::ParameterSet& params,
- int32_t timeout);
+ int32_t timeout, bool autoCommit);
/**
* Destructor.
@@ -137,6 +138,9 @@ namespace ignite
/** Timeout. */
int32_t timeout;
+
+ /** Auto commit. */
+ bool autoCommit;
};
/**
@@ -154,9 +158,11 @@ namespace ignite
* @param begin Beginng of the interval.
* @param end End of the interval.
* @param timeout Timeout.
+ * @param autoCommit Auto commit flag.
*/
QueryExecuteBatchtRequest(const std::string& schema, const std::string& sql,
- const app::ParameterSet& params, SqlUlen begin, SqlUlen end, bool last, int32_t timeout);
+ const app::ParameterSet& params, SqlUlen begin, SqlUlen end, bool last, int32_t timeout,
+ bool autoCommit);
/**
* Destructor.
@@ -191,6 +197,9 @@ namespace ignite
/** Timeout. */
int32_t timeout;
+
+ /** Auto commit. */
+ bool autoCommit;
};
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/nested_tx_mode.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/nested_tx_mode.h b/modules/platforms/cpp/odbc/include/ignite/odbc/nested_tx_mode.h
new file mode 100644
index 0000000..eb2598d
--- /dev/null
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/nested_tx_mode.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef _IGNITE_ODBC_NESTED_TX_MODE
+#define _IGNITE_ODBC_NESTED_TX_MODE
+
+#include <set>
+#include <string>
+
+namespace ignite
+{
+ namespace odbc
+ {
+ /**
+ * Nested transaction mode.
+ */
+ struct NestedTxMode
+ {
+ /**
+ * Values.
+ */
+ enum Type
+ {
+ /** Commit current transaction if a new one started. */
+ AI_COMMIT = 1,
+
+ /** Ignore start of a new transaction. */
+ AI_IGNORE = 2,
+
+ /** Throw an error. */
+ AI_ERROR = 3,
+
+ /** Returned when value is unknown. */
+ AI_UNKNOWN = 100
+ };
+
+ /** Mode set type. */
+ typedef std::set<Type> ModeSet;
+
+ /**
+ * Get value from a string value.
+ *
+ * @param str String.
+ * @param dflt Value to return on error.
+ * @return Corresponding value on success and @c dflt on failure.
+ */
+ static Type FromString(const std::string& str, Type dflt = AI_UNKNOWN);
+
+ /**
+ * Convert value to a string.
+ *
+ * @param value Value.
+ * @return String value.
+ */
+ static std::string ToString(Type value);
+
+ /**
+ * Get set of all valid values.
+ *
+ * @return Set of all valid values.
+ */
+ static const ModeSet& GetValidValues();
+ };
+ }
+}
+
+#endif //_IGNITE_ODBC_NESTED_TX_MODE
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/protocol_version.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/protocol_version.h b/modules/platforms/cpp/odbc/include/ignite/odbc/protocol_version.h
index 0b64536..1a722e3 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/protocol_version.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/protocol_version.h
@@ -43,7 +43,7 @@ namespace ignite
/** Version 2.3.2: added multiple statements support. */
static const ProtocolVersion VERSION_2_3_2;
- /** Version 2.5.0: added multiple statements support. */
+ /** Version 2.5.0: added authentication and transactions support. */
static const ProtocolVersion VERSION_2_5_0;
/** Version 2.7.0: added fields precision and scale. */
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/include/ignite/odbc/system/ui/dsn_configuration_window.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/system/ui/dsn_configuration_window.h b/modules/platforms/cpp/odbc/include/ignite/odbc/system/ui/dsn_configuration_window.h
index 1666863..77974ad 100644
--- a/modules/platforms/cpp/odbc/include/ignite/odbc/system/ui/dsn_configuration_window.h
+++ b/modules/platforms/cpp/odbc/include/ignite/odbc/system/ui/dsn_configuration_window.h
@@ -61,6 +61,8 @@ namespace ignite
SKIP_REDUCER_ON_UPDATE_CHECK_BOX,
PROTOCOL_VERSION_LABEL,
PROTOCOL_VERSION_COMBO_BOX,
+ NESTED_TX_MODE_LABEL,
+ NESTED_TX_MODE_COMBO_BOX,
SSL_MODE_LABEL,
SSL_MODE_COMBO_BOX,
SSL_KEY_FILE_LABEL,
@@ -312,6 +314,12 @@ namespace ignite
/** Password edit. */
std::auto_ptr<Window> passwordEdit;
+ /** Nested transaction mode label. */
+ std::auto_ptr<Window> nestedTxModeLabel;
+
+ /** Nested transaction mode combo box. */
+ std::auto_ptr<Window> nestedTxModeComboBox;
+
/** Configuration. */
config::Configuration& config;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/os/win/src/system/ui/dsn_configuration_window.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/os/win/src/system/ui/dsn_configuration_window.cpp b/modules/platforms/cpp/odbc/os/win/src/system/ui/dsn_configuration_window.cpp
index a94b479..e3bbb4e 100644
--- a/modules/platforms/cpp/odbc/os/win/src/system/ui/dsn_configuration_window.cpp
+++ b/modules/platforms/cpp/odbc/os/win/src/system/ui/dsn_configuration_window.cpp
@@ -36,7 +36,7 @@ namespace ignite
DsnConfigurationWindow::DsnConfigurationWindow(Window* parent, config::Configuration& config):
CustomWindow(parent, "IgniteConfigureDsn", "Configure Apache Ignite DSN"),
width(360),
- height(580),
+ height(600),
connectionSettingsGroupBox(),
sslSettingsGroupBox(),
authSettingsGroupBox(),
@@ -59,6 +59,7 @@ namespace ignite
userEdit(),
passwordLabel(),
passwordEdit(),
+ nestedTxModeComboBox(),
okButton(),
cancelButton(),
config(config),
@@ -286,7 +287,7 @@ namespace ignite
int DsnConfigurationWindow::CreateAdditionalSettingsGroup(int posX, int posY, int sizeX)
{
- enum { LABEL_WIDTH = 80 };
+ enum { LABEL_WIDTH = 130 };
int labelPosX = posX + INTERVAL;
@@ -309,6 +310,29 @@ namespace ignite
rowPos += INTERVAL + ROW_HEIGHT;
+ nestedTxModeLabel = CreateLabel(labelPosX, rowPos, LABEL_WIDTH, ROW_HEIGHT,
+ "Nested Transaction Mode:", ChildId::NESTED_TX_MODE_LABEL);
+ nestedTxModeComboBox = CreateComboBox(editPosX, rowPos, editSizeX, ROW_HEIGHT,
+ "", ChildId::NESTED_TX_MODE_COMBO_BOX);
+
+ int id = 0;
+
+ const NestedTxMode::ModeSet& supported = NestedTxMode::GetValidValues();
+
+ for (NestedTxMode::ModeSet::const_iterator it = supported.begin(); it != supported.end(); ++it)
+ {
+ nestedTxModeComboBox->AddString(NestedTxMode::ToString(*it));
+
+ if (*it == config.GetNestedTxMode())
+ nestedTxModeComboBox->SetSelection(id);
+
+ ++id;
+ }
+
+ nestedTxModeComboBox->SetEnabled(version >= ProtocolVersion::VERSION_2_5_0);
+
+ rowPos += INTERVAL + ROW_HEIGHT;
+
distributedJoinsCheckBox = CreateCheckBox(labelPosX, rowPos, checkBoxSize, ROW_HEIGHT,
"Distributed Joins", ChildId::DISTRIBUTED_JOINS_CHECK_BOX, config.IsDistributedJoins());
@@ -429,6 +453,7 @@ namespace ignite
ProtocolVersion version = ProtocolVersion::FromString(versionStr);
lazyCheckBox->SetEnabled(version >= ProtocolVersion::VERSION_2_1_5);
skipReducerOnUpdateCheckBox->SetEnabled(version >= ProtocolVersion::VERSION_2_3_0);
+ nestedTxModeComboBox->SetEnabled(version >= ProtocolVersion::VERSION_2_5_0);
break;
}
@@ -568,13 +593,6 @@ namespace ignite
{
std::string pageSizeStr;
- bool distributedJoins;
- bool enforceJoinOrder;
- bool replicatedOnly;
- bool collocated;
- bool lazy;
- bool skipReducerOnUpdate;
-
pageSizeEdit->GetText(pageSizeStr);
int32_t pageSize = common::LexicalCast<int32_t>(pageSizeStr);
@@ -582,15 +600,22 @@ namespace ignite
if (pageSize <= 0)
pageSize = config.GetPageSize();
- distributedJoins = distributedJoinsCheckBox->IsChecked();
- enforceJoinOrder = enforceJoinOrderCheckBox->IsChecked();
- replicatedOnly = replicatedOnlyCheckBox->IsChecked();
- collocated = collocatedCheckBox->IsChecked();
- lazy = lazyCheckBox->IsChecked();
- skipReducerOnUpdate = skipReducerOnUpdateCheckBox->IsChecked();
+ std::string nestedTxModeStr;
+
+ nestedTxModeComboBox->GetText(nestedTxModeStr);
+
+ NestedTxMode::Type mode = NestedTxMode::FromString(nestedTxModeStr, config.GetNestedTxMode());
+
+ bool distributedJoins = distributedJoinsCheckBox->IsChecked();
+ bool enforceJoinOrder = enforceJoinOrderCheckBox->IsChecked();
+ bool replicatedOnly = replicatedOnlyCheckBox->IsChecked();
+ bool collocated = collocatedCheckBox->IsChecked();
+ bool lazy = lazyCheckBox->IsChecked();
+ bool skipReducerOnUpdate = skipReducerOnUpdateCheckBox->IsChecked();
LOG_MSG("Retrieving arguments:");
LOG_MSG("Page size: " << pageSize);
+ LOG_MSG("Nested TX Mode: " << NestedTxMode::ToString(mode));
LOG_MSG("Distributed Joins: " << (distributedJoins ? "true" : "false"));
LOG_MSG("Enforce Join Order: " << (enforceJoinOrder ? "true" : "false"));
LOG_MSG("Replicated only: " << (replicatedOnly ? "true" : "false"));
@@ -599,6 +624,7 @@ namespace ignite
LOG_MSG("Skip reducer on update: " << (skipReducerOnUpdate ? "true" : "false"));
cfg.SetPageSize(pageSize);
+ cfg.SetNestedTxMode(mode);
cfg.SetDistributedJoins(distributedJoins);
cfg.SetEnforceJoinOrder(enforceJoinOrder);
cfg.SetReplicatedOnly(replicatedOnly);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj b/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj
index 352913e..630dc27 100644
--- a/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj
+++ b/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj
@@ -182,6 +182,7 @@
<ClCompile Include="..\..\src\message.cpp" />
<ClCompile Include="..\..\src\meta\column_meta.cpp" />
<ClCompile Include="..\..\src\meta\table_meta.cpp" />
+ <ClCompile Include="..\..\src\nested_tx_mode.cpp" />
<ClCompile Include="..\..\src\odbc.cpp" />
<ClCompile Include="..\..\src\protocol_version.cpp" />
<ClCompile Include="..\..\src\query\batch_query.cpp" />
@@ -230,6 +231,7 @@
<ClInclude Include="..\..\include\ignite\odbc\meta\column_meta.h" />
<ClInclude Include="..\..\include\ignite\odbc\meta\primary_key_meta.h" />
<ClInclude Include="..\..\include\ignite\odbc\meta\table_meta.h" />
+ <ClInclude Include="..\..\include\ignite\odbc\nested_tx_mode.h" />
<ClInclude Include="..\..\include\ignite\odbc\odbc_error.h" />
<ClInclude Include="..\..\include\ignite\odbc\parser.h" />
<ClInclude Include="..\..\include\ignite\odbc\protocol_version.h" />
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj.filters
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj.filters b/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj.filters
index b1956aa..6da0111 100644
--- a/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj.filters
+++ b/modules/platforms/cpp/odbc/project/vs/odbc.vcxproj.filters
@@ -166,6 +166,9 @@
<ClCompile Include="..\..\src\config\config_tools.cpp">
<Filter>Code\config</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\nested_tx_mode.cpp">
+ <Filter>Code</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="module.def">
@@ -329,5 +332,8 @@
<ClInclude Include="..\..\include\ignite\odbc\end_point.h">
<Filter>Code</Filter>
</ClInclude>
+ <ClInclude Include="..\..\include\ignite\odbc\nested_tx_mode.h">
+ <Filter>Code</Filter>
+ </ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/config/configuration.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/config/configuration.cpp b/modules/platforms/cpp/odbc/src/config/configuration.cpp
index ef102d2..a99894d 100644
--- a/modules/platforms/cpp/odbc/src/config/configuration.cpp
+++ b/modules/platforms/cpp/odbc/src/config/configuration.cpp
@@ -56,6 +56,8 @@ namespace ignite
const std::string Configuration::DefaultValue::user = "";
const std::string Configuration::DefaultValue::password = "";
+ const NestedTxMode::Type Configuration::DefaultValue::nestedTxMode = NestedTxMode::AI_ERROR;
+
Configuration::Configuration() :
dsn(DefaultValue::dsn),
driver(DefaultValue::driver),
@@ -76,7 +78,8 @@ namespace ignite
sslCertFile(DefaultValue::sslCertFile),
sslCaFile(DefaultValue::sslCaFile),
user(DefaultValue::user),
- password(DefaultValue::password)
+ password(DefaultValue::password),
+ nestedTxMode(DefaultValue::nestedTxMode)
{
// No-op.
}
@@ -404,6 +407,21 @@ namespace ignite
return password.IsSet();
}
+ NestedTxMode::Type Configuration::GetNestedTxMode() const
+ {
+ return nestedTxMode.GetValue();
+ }
+
+ void Configuration::SetNestedTxMode(NestedTxMode::Type mode)
+ {
+ this->nestedTxMode.SetValue(mode);
+ }
+
+ bool Configuration::IsNestedTxModeSet() const
+ {
+ return nestedTxMode.IsSet();
+ }
+
int32_t Configuration::GetPageSize() const
{
return pageSize.GetValue();
@@ -431,6 +449,7 @@ namespace ignite
AddToMap(res, ConnectionStringParser::Key::sslCaFile, sslCaFile);
AddToMap(res, ConnectionStringParser::Key::user, user);
AddToMap(res, ConnectionStringParser::Key::password, password);
+ AddToMap(res, ConnectionStringParser::Key::nestedTxMode, nestedTxMode);
}
template<>
@@ -486,6 +505,14 @@ namespace ignite
if (value.IsSet())
map[key] = ssl::SslMode::ToString(value.GetValue());
}
+
+ template<>
+ void Configuration::AddToMap(ArgumentMap& map, const std::string& key,
+ const SettableValue<NestedTxMode::Type>& value)
+ {
+ if (value.IsSet())
+ map[key] = NestedTxMode::ToString(value.GetValue());
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/config/connection_info.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/config/connection_info.cpp b/modules/platforms/cpp/odbc/src/config/connection_info.cpp
index 7279ea6..5885381 100644
--- a/modules/platforms/cpp/odbc/src/config/connection_info.cpp
+++ b/modules/platforms/cpp/odbc/src/config/connection_info.cpp
@@ -766,7 +766,7 @@ namespace ignite
#ifdef SQL_MULTIPLE_ACTIVE_TXN
// A character string: "Y" if the driver supports more than one active transaction at the same time,
// "N" if only one transaction can be active at any time.
- strParams[SQL_MULTIPLE_ACTIVE_TXN] = "N";
+ strParams[SQL_MULTIPLE_ACTIVE_TXN] = "Y";
#endif // SQL_MULTIPLE_ACTIVE_TXN
#ifdef SQL_ORDER_BY_COLUMNS_IN_SELECT
@@ -1596,7 +1596,7 @@ namespace ignite
// SQL_TXN_REPEATABLE_READ = Dirty reads and nonrepeatable reads are not possible. Phantoms are possible
// SQL_TXN_SERIALIZABLE = Transactions are serializable. Serializable transactions do not allow dirty
// reads, nonrepeatable reads, or phantoms.
- intParams[SQL_DEFAULT_TXN_ISOLATION] = 0;
+ intParams[SQL_DEFAULT_TXN_ISOLATION] = SQL_TXN_REPEATABLE_READ;
#endif // SQL_DEFAULT_TXN_ISOLATION
#ifdef SQL_DROP_ASSERTION
@@ -2170,7 +2170,7 @@ namespace ignite
// SQL_ATTR_TXN_ISOLATION attribute. For more information, see SQLSetConnectAttr Function.
// An SQL-92 Entry level-conformant driver will always return SQL_TXN_SERIALIZABLE as supported.
// A FIPS Transitional level-conformant driver will always return all of these options as supported.
- intParams[SQL_TXN_ISOLATION_OPTION] = 0;
+ intParams[SQL_TXN_ISOLATION_OPTION] = SQL_TXN_REPEATABLE_READ;
#endif // SQL_TXN_ISOLATION_OPTION
#ifdef SQL_UNION
@@ -2312,7 +2312,7 @@ namespace ignite
#ifdef SQL_TXN_CAPABLE
// Describs the transaction support in the driver or data source.
- shortParams[SQL_TXN_CAPABLE] = SQL_TC_NONE;
+ shortParams[SQL_TXN_CAPABLE] = SQL_TC_DDL_COMMIT;
#endif // SQL_TXN_CAPABLE
#ifdef SQL_QUOTED_IDENTIFIER_CASE
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp b/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
index d23c2cb..7f7c2f4 100644
--- a/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
+++ b/modules/platforms/cpp/odbc/src/config/connection_string_parser.cpp
@@ -24,6 +24,7 @@
#include "ignite/odbc/ssl/ssl_mode.h"
#include "ignite/odbc/config/connection_string_parser.h"
#include "ignite/odbc/config/config_tools.h"
+#include "ignite/odbc/nested_tx_mode.h"
namespace ignite
{
@@ -51,6 +52,7 @@ namespace ignite
const std::string ConnectionStringParser::Key::sslCaFile = "ssl_ca_file";
const std::string ConnectionStringParser::Key::user = "user";
const std::string ConnectionStringParser::Key::password = "password";
+ const std::string ConnectionStringParser::Key::nestedTxMode = "nested_tx_mode";
ConnectionStringParser::ConnectionStringParser(Configuration& cfg):
cfg(cfg)
@@ -424,6 +426,23 @@ namespace ignite
{
cfg.SetPassword(value);
}
+ else if (lKey == Key::nestedTxMode)
+ {
+ NestedTxMode::Type mode = NestedTxMode::FromString(value);
+
+ if (mode == NestedTxMode::AI_UNKNOWN)
+ {
+ if (diag)
+ {
+ diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
+ "Specified nested transaction mode is not supported. Default value used ('error').");
+ }
+
+ return;
+ }
+
+ cfg.SetNestedTxMode(mode);
+ }
else if (diag)
{
std::stringstream stream;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/connection.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/connection.cpp b/modules/platforms/cpp/odbc/src/connection.cpp
index 894efb7..5538549 100644
--- a/modules/platforms/cpp/odbc/src/connection.cpp
+++ b/modules/platforms/cpp/odbc/src/connection.cpp
@@ -25,6 +25,7 @@
#include "ignite/odbc/log.h"
#include "ignite/odbc/utility.h"
+#include "ignite/odbc/environment.h"
#include "ignite/odbc/statement.h"
#include "ignite/odbc/connection.h"
#include "ignite/odbc/message.h"
@@ -54,10 +55,12 @@ namespace ignite
{
namespace odbc
{
- Connection::Connection() :
+ Connection::Connection(Environment* env) :
+ env(env),
socket(),
timeout(0),
loginTimeout(SocketClient::DEFALT_CONNECT_TIMEOUT),
+ autoCommit(true),
parser(),
config(),
info(config)
@@ -186,6 +189,11 @@ namespace ignite
IGNITE_ODBC_API_CALL(InternalRelease());
}
+ void Connection::Deregister()
+ {
+ env->DeregisterConnection(this);
+ }
+
SqlResult::Type Connection::InternalRelease()
{
if (socket.get() == 0)
@@ -367,6 +375,11 @@ namespace ignite
return config;
}
+ bool Connection::IsAutoCommit()
+ {
+ return autoCommit;
+ }
+
diagnostic::DiagnosticRecord Connection::CreateStatusRecord(SqlState::Type sqlState,
const std::string& message, int32_t rowNum, int32_t columnNum)
{
@@ -380,6 +393,37 @@ namespace ignite
SqlResult::Type Connection::InternalTransactionCommit()
{
+ std::string schema = config.GetSchema();
+
+ app::ParameterSet empty;
+
+ QueryExecuteRequest req(schema, "COMMIT", empty, timeout, autoCommit);
+ QueryExecuteResponse rsp;
+
+ try
+ {
+ bool sent = SyncMessage(req, rsp, timeout);
+
+ if (!sent)
+ {
+ AddStatusRecord(SqlState::S08S01_LINK_FAILURE, "Failed to send commit request.");
+
+ return SqlResult::AI_ERROR;
+ }
+ }
+ catch (const OdbcError& err)
+ {
+ AddStatusRecord(err);
+
+ return SqlResult::AI_ERROR;
+ }
+ catch (const IgniteError& err)
+ {
+ AddStatusRecord(SqlState::SHY000_GENERAL_ERROR, err.GetText());
+
+ return SqlResult::AI_ERROR;
+ }
+
return SqlResult::AI_SUCCESS;
}
@@ -390,10 +434,38 @@ namespace ignite
SqlResult::Type Connection::InternalTransactionRollback()
{
- AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
- "Rollback operation is not supported.");
+ std::string schema = config.GetSchema();
+
+ app::ParameterSet empty;
- return SqlResult::AI_ERROR;
+ QueryExecuteRequest req(schema, "ROLLBACK", empty, timeout, autoCommit);
+ QueryExecuteResponse rsp;
+
+ try
+ {
+ bool sent = SyncMessage(req, rsp, timeout);
+
+ if (!sent)
+ {
+ AddStatusRecord(SqlState::S08S01_LINK_FAILURE, "Failed to send rollback request.");
+
+ return SqlResult::AI_ERROR;
+ }
+ }
+ catch (const OdbcError& err)
+ {
+ AddStatusRecord(err);
+
+ return SqlResult::AI_ERROR;
+ }
+ catch (const IgniteError& err)
+ {
+ AddStatusRecord(SqlState::SHY000_GENERAL_ERROR, err.GetText());
+
+ return SqlResult::AI_ERROR;
+ }
+
+ return SqlResult::AI_SUCCESS;
}
void Connection::GetAttribute(int attr, void* buf, SQLINTEGER bufLen, SQLINTEGER* valueLen)
@@ -452,7 +524,10 @@ namespace ignite
{
SQLUINTEGER *val = reinterpret_cast<SQLUINTEGER*>(buf);
- *val = SQL_AUTOCOMMIT_ON;
+ *val = autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
+
+ if (valueLen)
+ *valueLen = SQL_IS_INTEGER;
break;
}
@@ -507,10 +582,17 @@ namespace ignite
case SQL_ATTR_AUTOCOMMIT:
{
- SQLUINTEGER val = static_cast<SQLUINTEGER>(reinterpret_cast<ptrdiff_t>(value));
+ SQLUINTEGER mode = static_cast<SQLUINTEGER>(reinterpret_cast<ptrdiff_t>(value));
+
+ if (mode != SQL_AUTOCOMMIT_ON && mode != SQL_AUTOCOMMIT_OFF)
+ {
+ AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
+ "Specified attribute is not supported.");
- if (val != SQL_AUTOCOMMIT_ON)
return SqlResult::AI_ERROR;
+ }
+
+ autoCommit = mode == SQL_AUTOCOMMIT_ON;
break;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/diagnostic/diagnosable_adapter.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/diagnostic/diagnosable_adapter.cpp b/modules/platforms/cpp/odbc/src/diagnostic/diagnosable_adapter.cpp
index 143d949..6de0716 100644
--- a/modules/platforms/cpp/odbc/src/diagnostic/diagnosable_adapter.cpp
+++ b/modules/platforms/cpp/odbc/src/diagnostic/diagnosable_adapter.cpp
@@ -52,6 +52,11 @@ namespace ignite
{
AddStatusRecord(err.GetStatus(), err.GetErrorMessage(), 0, 0);
}
+
+ void DiagnosableAdapter::AddStatusRecord(const DiagnosticRecord& rec)
+ {
+ diagnosticRecords.AddStatusRecord(rec);
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/dsn_config.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/dsn_config.cpp b/modules/platforms/cpp/odbc/src/dsn_config.cpp
index 1307d7e..dcdb8f4 100644
--- a/modules/platforms/cpp/odbc/src/dsn_config.cpp
+++ b/modules/platforms/cpp/odbc/src/dsn_config.cpp
@@ -212,6 +212,11 @@ namespace ignite
if (password.IsSet() && !config.IsPasswordSet())
config.SetPassword(password.GetValue());
+
+ SettableValue<std::string> nestedTxModeStr = ReadDsnString(dsn, ConnectionStringParser::Key::nestedTxMode);
+
+ if (nestedTxModeStr.IsSet() && !config.IsNestedTxModeSet())
+ config.SetNestedTxMode(NestedTxMode::FromString(nestedTxModeStr.GetValue(), config.GetNestedTxMode()));
}
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/environment.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/environment.cpp b/modules/platforms/cpp/odbc/src/environment.cpp
index 417fedc..5a2ac4f 100644
--- a/modules/platforms/cpp/odbc/src/environment.cpp
+++ b/modules/platforms/cpp/odbc/src/environment.cpp
@@ -25,7 +25,8 @@ namespace ignite
{
namespace odbc
{
- Environment::Environment() :
+ Environment::Environment() :
+ connections(),
odbcVersion(SQL_OV_ODBC3),
odbcNts(SQL_TRUE)
{
@@ -46,9 +47,14 @@ namespace ignite
return connection;
}
+ void Environment::DeregisterConnection(Connection* conn)
+ {
+ connections.erase(conn);
+ }
+
SqlResult::Type Environment::InternalCreateConnection(Connection*& connection)
{
- connection = new Connection;
+ connection = new Connection(this);
if (!connection)
{
@@ -57,6 +63,8 @@ namespace ignite
return SqlResult::AI_ERROR;
}
+ connections.insert(connection);
+
return SqlResult::AI_SUCCESS;
}
@@ -67,7 +75,25 @@ namespace ignite
SqlResult::Type Environment::InternalTransactionCommit()
{
- return SqlResult::AI_SUCCESS;
+ SqlResult::Type res = SqlResult::AI_SUCCESS;
+
+ for (ConnectionSet::iterator it = connections.begin(); it != connections.end(); ++it)
+ {
+ Connection* conn = *it;
+
+ conn->TransactionCommit();
+
+ diagnostic::DiagnosticRecordStorage& diag = conn->GetDiagnosticRecords();
+
+ if (diag.GetStatusRecordsNumber() > 0)
+ {
+ AddStatusRecord(diag.GetStatusRecord(1));
+
+ res = SqlResult::AI_SUCCESS_WITH_INFO;
+ }
+ }
+
+ return res;
}
void Environment::TransactionRollback()
@@ -77,10 +103,25 @@ namespace ignite
SqlResult::Type Environment::InternalTransactionRollback()
{
- AddStatusRecord(SqlState::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED,
- "Rollback operation is not supported.");
+ SqlResult::Type res = SqlResult::AI_SUCCESS;
- return SqlResult::AI_ERROR;
+ for (ConnectionSet::iterator it = connections.begin(); it != connections.end(); ++it)
+ {
+ Connection* conn = *it;
+
+ conn->TransactionRollback();
+
+ diagnostic::DiagnosticRecordStorage& diag = conn->GetDiagnosticRecords();
+
+ if (diag.GetStatusRecordsNumber() > 0)
+ {
+ AddStatusRecord(diag.GetStatusRecord(1));
+
+ res = SqlResult::AI_SUCCESS_WITH_INFO;
+ }
+ }
+
+ return res;
}
void Environment::SetAttribute(int32_t attr, void* value, int32_t len)
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/message.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/message.cpp b/modules/platforms/cpp/odbc/src/message.cpp
index 5b909b8..5730842 100644
--- a/modules/platforms/cpp/odbc/src/message.cpp
+++ b/modules/platforms/cpp/odbc/src/message.cpp
@@ -82,15 +82,18 @@ namespace ignite
{
utility::WriteString(writer, config.GetUser());
utility::WriteString(writer, config.GetPassword());
+
+ writer.WriteInt8(config.GetNestedTxMode());
}
}
QueryExecuteRequest::QueryExecuteRequest(const std::string& schema, const std::string& sql,
- const app::ParameterSet& params, int32_t timeout):
+ const app::ParameterSet& params, int32_t timeout, bool autoCommit):
schema(schema),
sql(sql),
params(params),
- timeout(timeout)
+ timeout(timeout),
+ autoCommit(autoCommit)
{
// No-op.
}
@@ -115,17 +118,21 @@ namespace ignite
if (ver >= ProtocolVersion::VERSION_2_3_2)
writer.WriteInt32(timeout);
+
+ if (ver >= ProtocolVersion::VERSION_2_5_0)
+ writer.WriteBool(autoCommit);
}
QueryExecuteBatchtRequest::QueryExecuteBatchtRequest(const std::string& schema, const std::string& sql,
- const app::ParameterSet& params, SqlUlen begin, SqlUlen end, bool last, int32_t timeout):
+ const app::ParameterSet& params, SqlUlen begin, SqlUlen end, bool last, int32_t timeout, bool autoCommit) :
schema(schema),
sql(sql),
params(params),
begin(begin),
end(end),
last(last),
- timeout(timeout)
+ timeout(timeout),
+ autoCommit(autoCommit)
{
// No-op.
}
@@ -150,6 +157,9 @@ namespace ignite
if (ver >= ProtocolVersion::VERSION_2_3_2)
writer.WriteInt32(timeout);
+
+ if (ver >= ProtocolVersion::VERSION_2_5_0)
+ writer.WriteBool(autoCommit);
}
QueryCloseRequest::QueryCloseRequest(int64_t queryId): queryId(queryId)
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/nested_tx_mode.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/nested_tx_mode.cpp b/modules/platforms/cpp/odbc/src/nested_tx_mode.cpp
new file mode 100644
index 0000000..e4e6118
--- /dev/null
+++ b/modules/platforms/cpp/odbc/src/nested_tx_mode.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include "ignite/odbc/nested_tx_mode.h"
+#include "ignite/common/utils.h"
+
+namespace
+{
+ using ignite::odbc::NestedTxMode;
+ NestedTxMode::Type validValues0[] = {
+ NestedTxMode::AI_COMMIT,
+ NestedTxMode::AI_IGNORE,
+ NestedTxMode::AI_ERROR
+ };
+
+ NestedTxMode::ModeSet validValues(validValues0, validValues0 + (sizeof(validValues0) / sizeof(validValues0[0])));
+}
+
+
+namespace ignite
+{
+ namespace odbc
+ {
+ NestedTxMode::Type NestedTxMode::FromString(const std::string& str, Type dflt)
+ {
+ std::string lower = common::ToLower(str);
+
+ if (lower == "commit")
+ return AI_COMMIT;
+
+ if (lower == "ignore")
+ return AI_IGNORE;
+
+ if (lower == "error")
+ return AI_ERROR;
+
+ return dflt;
+ }
+
+ std::string NestedTxMode::ToString(Type value)
+ {
+ switch (value)
+ {
+ case AI_COMMIT:
+ return "commit";
+
+ case AI_IGNORE:
+ return "ignore";
+
+ case AI_ERROR:
+ return "error";
+
+ default:
+ break;
+ }
+
+ return "default";
+ }
+
+ const NestedTxMode::ModeSet& NestedTxMode::GetValidValues()
+ {
+ return validValues;
+ }
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/odbc.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/odbc.cpp b/modules/platforms/cpp/odbc/src/odbc.cpp
index 3aad52f..5b37da3 100644
--- a/modules/platforms/cpp/odbc/src/odbc.cpp
+++ b/modules/platforms/cpp/odbc/src/odbc.cpp
@@ -204,6 +204,8 @@ namespace ignite
if (!connection)
return SQL_INVALID_HANDLE;
+ connection->Deregister();
+
delete connection;
return SQL_SUCCESS;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/query/batch_query.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/query/batch_query.cpp b/modules/platforms/cpp/odbc/src/query/batch_query.cpp
index 8dada3c..1256c94 100644
--- a/modules/platforms/cpp/odbc/src/query/batch_query.cpp
+++ b/modules/platforms/cpp/odbc/src/query/batch_query.cpp
@@ -144,7 +144,9 @@ namespace ignite
{
const std::string& schema = connection.GetSchema();
- QueryExecuteBatchtRequest req(schema, sql, params, begin, end, last, timeout);
+ QueryExecuteBatchtRequest req(schema, sql, params, begin, end, last, timeout,
+ connection.IsAutoCommit());
+
QueryExecuteBatchResponse rsp;
try
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/platforms/cpp/odbc/src/query/data_query.cpp
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/odbc/src/query/data_query.cpp b/modules/platforms/cpp/odbc/src/query/data_query.cpp
index 6974c7f..69208b8 100644
--- a/modules/platforms/cpp/odbc/src/query/data_query.cpp
+++ b/modules/platforms/cpp/odbc/src/query/data_query.cpp
@@ -206,7 +206,7 @@ namespace ignite
{
const std::string& schema = connection.GetSchema();
- QueryExecuteRequest req(schema, sql, params, timeout);
+ QueryExecuteRequest req(schema, sql, params, timeout, connection.IsAutoCommit());
QueryExecuteResponse rsp;
try
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/config/mvcc/benchmark-mvcc-messages.sh
----------------------------------------------------------------------
diff --git a/modules/yardstick/config/mvcc/benchmark-mvcc-messages.sh b/modules/yardstick/config/mvcc/benchmark-mvcc-messages.sh
new file mode 100644
index 0000000..32068d3
--- /dev/null
+++ b/modules/yardstick/config/mvcc/benchmark-mvcc-messages.sh
@@ -0,0 +1,98 @@
+#
+# 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.
+#
+
+#
+# Configuration to measure increased messages load with mvcc turned on.
+#
+
+now0=`date +'%H%M%S'`
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+JVM_OPTS=${JVM_OPTS}" \
+-Xms8g \
+-Xmx8g \
+-Xloggc:./gc${now0}.log \
+-XX:+PrintGCDetails \
+-verbose:gc \
+-XX:+UseParNewGC \
+-XX:+UseConcMarkSweepGC \
+-XX:+PrintGCDateStamps \
+"
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe,DStatProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Flag which indicates to restart the servers before every benchmark execution.
+RESTART_SERVERS=true
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# The benchmark is designed to run with 1 client node (driver itself) and many (4 for instance) server nodes.
+SERVER_HOSTS=localhost,localhost,localhost,localhost
+DRIVER_HOSTS=localhost
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Warmup.
+w=30
+
+# Duration.
+d=300
+
+# Threads count.
+t=1
+
+# Sync mode.
+sm=FULL_SYNC
+
+# Parameters that should be the same across all the benchmarks launches.
+commonParams="-cfg ${SCRIPT_DIR}/../config/ignite-localhost-config.xml -nn ${nodesNum} -w ${w} -d ${d} \
+ -jdbc jdbc:ignite:thin://auto.find/ -t ${t} -sm ${sm} \
+ --clientNodesAfterId 100 \
+ -sn IgniteNode -cl --range 1000000 \
+ --atomic-mode TRANSACTIONAL"
+
+# Run configuration which contains all benchmarks.
+# Note that each benchmark is set to run for 300 seconds (5 min) with warm-up set to 60 seconds (1 minute).
+CONFIGS="\
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1-backup-0-mvcc-off -b 0 --sqlRange 1, \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1-backup-0-mvcc-on -b 0 --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1000-backup-0-mvcc-off -b 0 --sqlRange 1000, \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1000-backup-0-mvcc-on -b 0 --sqlRange 1000 --mvcc, \
+ \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1-backup-2-mvcc-off -b 2 --sqlRange 1, \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1-backup-2-mvcc-on -b 2 --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1000-backup-2-mvcc-off -b 2 --sqlRange 1000, \
+${commonParams} -dn NativeSqlUpdateRangeBenchmark -ds ${ver}sql-update-batch-1000-backup-2-mvcc-on -b 2 --sqlRange 1000 --mvcc, \
+"
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/config/mvcc/benchmark-mvcc-processor.sh
----------------------------------------------------------------------
diff --git a/modules/yardstick/config/mvcc/benchmark-mvcc-processor.sh b/modules/yardstick/config/mvcc/benchmark-mvcc-processor.sh
new file mode 100644
index 0000000..db45199
--- /dev/null
+++ b/modules/yardstick/config/mvcc/benchmark-mvcc-processor.sh
@@ -0,0 +1,95 @@
+#
+# 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.
+#
+
+#
+# Configuration to compare mvcc on/off. This benchmark creates load on mvcc processor.
+#
+
+now0=`date +'%H%M%S'`
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+JVM_OPTS=${JVM_OPTS}" \
+-Xms8g \
+-Xmx8g \
+-Xloggc:./gc${now0}.log \
+-XX:+PrintGCDetails \
+-verbose:gc \
+-XX:+UseParNewGC \
+-XX:+UseConcMarkSweepGC \
+-XX:+PrintGCDateStamps \
+"
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe,DStatProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Flag which indicates to restart the servers before every benchmark execution.
+RESTART_SERVERS=true
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# The benchmark is designed to run with 4 client nodes (drivers) and 1 server node.
+SERVER_HOSTS=localhost
+DRIVER_HOSTS=localhost,localhost,localhost,localhost
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Warmup.
+w=30
+
+# Duration.
+d=300
+
+# Threads count.
+t=16
+
+# Sync mode.
+sm=FULL_SYNC
+
+# Parameters that should be the same across all the benchmarks launches.
+commonParams="-cfg ${SCRIPT_DIR}/../config/ignite-localhost-config.xml -nn ${nodesNum} -w ${w} -d ${d} \
+ -jdbc jdbc:ignite:thin://auto.find/ -t ${t} -sm ${sm} \
+ --clientNodesAfterId 100 \
+ -sn IgniteNode -cl --range 1000000 \
+ --atomic-mode TRANSACTIONAL"
+
+# Run configuration which contains all benchmarks.
+# Note that each benchmark is set to run for 300 seconds (5 min) with warm-up set to 60 seconds (1 minute).
+CONFIGS="\
+${commonParams} -dn MvccProcessorBenchmark -ds ${ver}sql-update-batch-1-backup-0-mvcc-off -b 0 --sqlRange 1, \
+${commonParams} -dn MvccProcessorBenchmark -ds ${ver}sql-update-batch-1-backup-0-mvcc-on -b 0 --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn MvccProcessorBenchmark -ds ${ver}sql-update-batch-25-backup-0-mvcc-off -b 0 --sqlRange 25, \
+${commonParams} -dn MvccProcessorBenchmark -ds ${ver}sql-update-batch-25-backup-0-mvcc-on -b 0 --sqlRange 25 --mvcc, \
+ \
+${commonParams} -dn MvccProcessorBenchmark -ds ${ver}sql-update-batch-1000-backup-0-mvcc-off -b 0 --sqlRange 1000, \
+${commonParams} -dn MvccProcessorBenchmark -ds ${ver}sql-update-batch-1000-backup-0-mvcc-on -b 0 --sqlRange 1000 --mvcc \
+"
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/yardstick/config/mvcc/benchmark-mvcc-updates-contention.sh
----------------------------------------------------------------------
diff --git a/modules/yardstick/config/mvcc/benchmark-mvcc-updates-contention.sh b/modules/yardstick/config/mvcc/benchmark-mvcc-updates-contention.sh
new file mode 100644
index 0000000..0a92bce
--- /dev/null
+++ b/modules/yardstick/config/mvcc/benchmark-mvcc-updates-contention.sh
@@ -0,0 +1,96 @@
+#
+# 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.
+#
+
+#
+# Configuration to measure performance of concurrent sql updates with contention.
+# Update keys are shared among the threads/hosts.
+#
+now0=`date +'%H%M%S'`
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+JVM_OPTS=${JVM_OPTS}" \
+-Xms8g \
+-Xmx8g \
+-Xloggc:./gc${now0}.log \
+-XX:+PrintGCDetails \
+-verbose:gc \
+-XX:+UseParNewGC \
+-XX:+UseConcMarkSweepGC \
+-XX:+PrintGCDateStamps \
+"
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe,DStatProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Flag which indicates to restart the servers before every benchmark execution.
+RESTART_SERVERS=true
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# The benchmark is designed to run with 4 client node (drivers) and several (2 for instance) server nodes
+SERVER_HOSTS=localhost,localhost
+DRIVER_HOSTS=localhost,localhost,localhost,localhost
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Warmup.
+w=30
+
+# Duration.
+d=300
+
+# Threads count.
+t=16
+
+# Sync mode.
+sm=FULL_SYNC
+
+# Parameters that should be the same across all the benchmarks launches.
+commonParams="-cfg ${SCRIPT_DIR}/../config/ignite-localhost-config.xml -nn ${nodesNum} -w ${w} -d ${d} \
+ -jdbc jdbc:ignite:thin://auto.find/ -t ${t} -sm ${sm} \
+ --clientNodesAfterId 100 \
+ -sn IgniteNode -cl \
+ --range 1000000 --mvcc-contention-range 10000 \
+ --atomic-mode TRANSACTIONAL"
+
+# Run configuration which contains all benchmarks.
+# Note that each benchmark is set to run for 300 seconds (5 min) with warm-up set to 60 seconds (1 minute).
+CONFIGS="\
+${commonParams} -dn MvccUpdateContentionBenchmark -ds ${ver}sql-update-batch-1-backup-0-mvcc-off -b 0 --sqlRange 1, \
+${commonParams} -dn MvccUpdateContentionBenchmark -ds ${ver}sql-update-batch-1-backup-0-mvcc-on -b 0 --sqlRange 1 --mvcc, \
+ \
+${commonParams} -dn MvccUpdateContentionBenchmark -ds ${ver}sql-update-batch-25-backup-0-mvcc-off -b 0 --sqlRange 25, \
+${commonParams} -dn MvccUpdateContentionBenchmark -ds ${ver}sql-update-batch-25-backup-0-mvcc-on -b 0 --sqlRange 25 --mvcc, \
+ \
+${commonParams} -dn MvccUpdateContentionBenchmark -ds ${ver}sql-update-batch-1000-backup-0-mvcc-off -b 0 --sqlRange 1000, \
+${commonParams} -dn MvccUpdateContentionBenchmark -ds ${ver}sql-update-batch-1000-backup-0-mvcc-on -b 0 --sqlRange 1000 --mvcc \
+"
[27/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetricsReducer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetricsReducer.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetricsReducer.java
new file mode 100644
index 0000000..c952a48
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumMetricsReducer.java
@@ -0,0 +1,49 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.lang.IgniteReducer;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Vacuum metrics reducer.
+ */
+public class VacuumMetricsReducer implements IgniteReducer<VacuumMetrics, VacuumMetrics> {
+ /** */
+ private static final long serialVersionUID = 7063457745963917386L;
+
+ /** */
+ private final VacuumMetrics m = new VacuumMetrics();
+
+ /** {@inheritDoc} */
+ @Override public boolean collect(@Nullable VacuumMetrics metrics) {
+ assert metrics != null;
+
+ m.addCleanupRowsCnt(metrics.cleanupRowsCount());
+ m.addScannedRowsCount(metrics.scannedRowsCount());
+ m.addSearchNanoTime(metrics.searchNanoTime());
+ m.addCleanupNanoTime(metrics.cleanupNanoTime());
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public VacuumMetrics reduce() {
+ return m;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumTask.java
new file mode 100644
index 0000000..9a0d9e2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/VacuumTask.java
@@ -0,0 +1,63 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Task for cleaning sing partition.
+ */
+public class VacuumTask extends GridFutureAdapter<VacuumMetrics> {
+ /** */
+ private final MvccSnapshot snapshot;
+
+ /** */
+ @GridToStringExclude
+ private final GridDhtLocalPartition part;
+
+ /**
+ * @param snapshot Snapshot.
+ * @param part Partition to cleanup.
+ */
+ VacuumTask(MvccSnapshot snapshot, GridDhtLocalPartition part) {
+ this.snapshot = snapshot;
+ this.part = part;
+ }
+
+ /**
+ * @return Snapshot.
+ */
+ public MvccSnapshot snapshot() {
+ return snapshot;
+ }
+
+ /**
+ * @return Partition to cleanup.
+ */
+ public GridDhtLocalPartition part() {
+ return part;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(VacuumTask.class, this, "partId", part.id());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryCntr.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryCntr.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryCntr.java
new file mode 100644
index 0000000..0156c53
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryCntr.java
@@ -0,0 +1,130 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccAckRequestQueryCntr implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long cntr;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccAckRequestQueryCntr() {
+ // No-op.
+ }
+
+ /**
+ * @param cntr Query counter.
+ */
+ public MvccAckRequestQueryCntr(long cntr) {
+ this.cntr = cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /**
+ * @return Counter.
+ */
+ public long counter() {
+ return cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("cntr", cntr))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ cntr = reader.readLong("cntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccAckRequestQueryCntr.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 140;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccAckRequestQueryCntr.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryId.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryId.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryId.java
new file mode 100644
index 0000000..7771f4d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestQueryId.java
@@ -0,0 +1,130 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccAckRequestQueryId implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long qryTrackerId;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccAckRequestQueryId() {
+ // No-op.
+ }
+
+ /**
+ * @param qryTrackerId Query tracker Id.
+ */
+ public MvccAckRequestQueryId(long qryTrackerId) {
+ this.qryTrackerId = qryTrackerId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /**
+ * @return Query tracker id.
+ */
+ public long queryTrackerId() {
+ return qryTrackerId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("qryTrackerId", qryTrackerId))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ qryTrackerId = reader.readLong("qryTrackerId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccAckRequestQueryId.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 145;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccAckRequestQueryId.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTx.java
new file mode 100644
index 0000000..69dfd25
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTx.java
@@ -0,0 +1,210 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker.MVCC_TRACKER_ID_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+
+/**
+ *
+ */
+public class MvccAckRequestTx implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private static final int SKIP_RESPONSE_FLAG_MASK = 0x01;
+
+ /** */
+ private long futId;
+
+ /** */
+ private long txCntr;
+
+ /** */
+ private byte flags;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccAckRequestTx() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ * @param txCntr Counter assigned to transaction.
+ */
+ public MvccAckRequestTx(long futId, long txCntr) {
+ this.futId = futId;
+ this.txCntr = txCntr;
+ }
+
+ /**
+ * @return Query counter.
+ */
+ public long queryCounter() {
+ return MVCC_COUNTER_NA;
+ }
+
+ /**
+ * @return Query tracker id.
+ */
+ public long queryTrackerId() {
+ return MVCC_TRACKER_ID_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /**
+ * @return Future ID.
+ */
+ public long futureId() {
+ return futId;
+ }
+
+ /**
+ * @return {@code True} if response message is not needed.
+ */
+ public boolean skipResponse() {
+ return (flags & SKIP_RESPONSE_FLAG_MASK) != 0;
+ }
+
+ /**
+ * @param val {@code True} if response message is not needed.
+ */
+ public void skipResponse(boolean val) {
+ if (val)
+ flags |= SKIP_RESPONSE_FLAG_MASK;
+ else
+ flags &= ~SKIP_RESPONSE_FLAG_MASK;
+ }
+
+ /**
+ * @return Counter assigned tp transaction.
+ */
+ public long txCounter() {
+ return txCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeByte("flags", flags))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeLong("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 2:
+ if (!writer.writeLong("txCntr", txCntr))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ flags = reader.readByte("flags");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ futId = reader.readLong("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 2:
+ txCntr = reader.readLong("txCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccAckRequestTx.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 137;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccAckRequestTx.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryCntr.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryCntr.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryCntr.java
new file mode 100644
index 0000000..99761c3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryCntr.java
@@ -0,0 +1,123 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccAckRequestTxAndQueryCntr extends MvccAckRequestTx {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long qryCntr;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccAckRequestTxAndQueryCntr() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ * @param txCntr Counter assigned to transaction update.
+ * @param qryCntr Counter assigned for transaction reads.
+ */
+ public MvccAckRequestTxAndQueryCntr(long futId, long txCntr, long qryCntr) {
+ super(futId, txCntr);
+
+ this.qryCntr = qryCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long queryCounter() {
+ return qryCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeLong("qryCntr", qryCntr))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ qryCntr = reader.readLong("qryCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccAckRequestTxAndQueryCntr.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 146;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccAckRequestTxAndQueryCntr.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryId.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryId.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryId.java
new file mode 100644
index 0000000..89f09db
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccAckRequestTxAndQueryId.java
@@ -0,0 +1,122 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccAckRequestTxAndQueryId extends MvccAckRequestTx {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long qryTrackerId;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccAckRequestTxAndQueryId() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ * @param txCntr Counter assigned to transaction update.
+ * @param qryTrackerId Query tracker id.
+ */
+ public MvccAckRequestTxAndQueryId(long futId, long txCntr, long qryTrackerId) {
+ super(futId, txCntr);
+
+ this.qryTrackerId = qryTrackerId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long queryTrackerId() {
+ return qryTrackerId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 3:
+ if (!writer.writeLong("qryTrackerId", qryTrackerId))
+ return false;
+
+ writer.incrementState();
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 3:
+ qryTrackerId = reader.readLong("qryTrackerId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccAckRequestTxAndQueryId.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 147;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccAckRequestTxAndQueryId.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccActiveQueriesMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccActiveQueriesMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccActiveQueriesMessage.java
new file mode 100644
index 0000000..4b78c24
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccActiveQueriesMessage.java
@@ -0,0 +1,132 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class MvccActiveQueriesMessage implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private GridLongList activeQrys;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccActiveQueriesMessage() {
+ // No-op.
+ }
+
+ /**
+ * @param activeQrys Active queries.
+ */
+ public MvccActiveQueriesMessage(GridLongList activeQrys) {
+ this.activeQrys = activeQrys;
+ }
+
+ /**
+ * @return Active queries.
+ */
+ @Nullable public GridLongList activeQueries() {
+ return activeQrys;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeMessage("activeQrys", activeQrys))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ activeQrys = reader.readMessage("activeQrys");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccActiveQueriesMessage.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 149;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccActiveQueriesMessage.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccFutureResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccFutureResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccFutureResponse.java
new file mode 100644
index 0000000..72e4c52
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccFutureResponse.java
@@ -0,0 +1,130 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccFutureResponse implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long futId;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccFutureResponse() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ */
+ public MvccFutureResponse(long futId) {
+ this.futId = futId;
+ }
+
+ /**
+ * @return Future ID.
+ */
+ public long futureId() {
+ return futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ futId = reader.readLong("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccFutureResponse.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 138;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccFutureResponse.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccMessage.java
new file mode 100644
index 0000000..6d8b3c4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccMessage.java
@@ -0,0 +1,35 @@
+/*
+ * 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.mvcc.msg;
+
+import org.apache.ignite.plugin.extensions.communication.Message;
+
+/**
+ * Common interface for all MVCC-related messages.
+ */
+public interface MvccMessage extends Message {
+ /**
+ * @return {@code True} if should wait for coordinator initialization.
+ */
+ public boolean waitForCoordinatorInit();
+
+ /**
+ * @return {@code True} if message should be processed from NIO thread.
+ */
+ public boolean processedFromNioThread();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccQuerySnapshotRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccQuerySnapshotRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccQuerySnapshotRequest.java
new file mode 100644
index 0000000..75d33a7
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccQuerySnapshotRequest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Request to get MVCC snapshot for a query.
+ */
+public class MvccQuerySnapshotRequest implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long futId;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccQuerySnapshotRequest() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ */
+ public MvccQuerySnapshotRequest(long futId) {
+ this.futId = futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /**
+ * @return Future ID.
+ */
+ public long futureId() {
+ return futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ futId = reader.readLong("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccQuerySnapshotRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 139;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccQuerySnapshotRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccSnapshotResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccSnapshotResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccSnapshotResponse.java
new file mode 100644
index 0000000..196003c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccSnapshotResponse.java
@@ -0,0 +1,320 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccLongList;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshotWithoutTxs;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccSnapshotResponse implements MvccMessage, MvccSnapshot, MvccLongList {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long futId;
+
+ /** */
+ private long crdVer;
+
+ /** */
+ private long cntr;
+
+ /** */
+ private int opCntr;
+
+ /** */
+ @GridDirectTransient
+ private int txsCnt;
+
+ /** */
+ private long[] txs;
+
+ /** */
+ private long cleanupVer;
+
+ /** */
+ @GridDirectTransient
+ private long tracking;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccSnapshotResponse() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ * @param crdVer Coordinator version.
+ * @param cntr Counter.
+ * @param opCntr Operation counter.
+ * @param cleanupVer Cleanup version.
+ * @param tracking Tracking number.
+ */
+ public void init(long futId, long crdVer, long cntr, int opCntr, long cleanupVer, long tracking) {
+ this.futId = futId;
+ this.crdVer = crdVer;
+ this.cntr = cntr;
+ this.opCntr = opCntr;
+ this.cleanupVer = cleanupVer;
+ this.tracking = tracking;
+
+ if (txsCnt > 0 && txs.length > txsCnt) // truncate if necessary
+ txs = Arrays.copyOf(txs, txsCnt);
+ }
+
+ /**
+ * @param txId Transaction counter.
+ */
+ public void addTx(long txId) {
+ if (txs == null)
+ txs = new long[4];
+ else if (txs.length == txsCnt)
+ txs = Arrays.copyOf(txs, txs.length << 1);
+
+ txs[txsCnt++] = txId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size() {
+ return txsCnt;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long get(int i) {
+ return txs[i];
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean contains(long val) {
+ for (int i = 0; i < txsCnt; i++) {
+ if (txs[i] == val)
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return Tracking counter.
+ */
+ public long tracking() {
+ return tracking;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return false;
+ }
+
+ /**
+ * @return Future ID.
+ */
+ public long futureId() {
+ return futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long cleanupVersion() {
+ return cleanupVer;
+ }
+
+ /** {@inheritDoc} */
+ public long counter() {
+ return cntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int operationCounter() {
+ return opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void incrementOperationCounter() {
+ opCntr++;
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccLongList activeTransactions() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot withoutActiveTransactions() {
+ if (txsCnt > 0)
+ return new MvccSnapshotWithoutTxs(crdVer, cntr, opCntr, cleanupVer);
+
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long coordinatorVersion() {
+ return crdVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("cleanupVer", cleanupVer))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeLong("cntr", cntr))
+ return false;
+
+ writer.incrementState();
+
+ case 2:
+ if (!writer.writeLong("crdVer", crdVer))
+ return false;
+
+ writer.incrementState();
+
+ case 3:
+ if (!writer.writeLong("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeInt("opCntr", opCntr))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeLongArray("txs", txs))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ cleanupVer = reader.readLong("cleanupVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ cntr = reader.readLong("cntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 2:
+ crdVer = reader.readLong("crdVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 3:
+ futId = reader.readLong("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ opCntr = reader.readInt("opCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ txs = reader.readLongArray("txs");
+
+ if (!reader.isLastRead())
+ return false;
+
+ txsCnt = txs != null ? txs.length : 0;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccSnapshotResponse.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 141;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 6;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccSnapshotResponse.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccTxSnapshotRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccTxSnapshotRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccTxSnapshotRequest.java
new file mode 100644
index 0000000..cd30eb8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccTxSnapshotRequest.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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.managers.communication.GridIoMessageFactory;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Request to get MVCC snapshot for a new transaction.
+ */
+public class MvccTxSnapshotRequest implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long futId;
+
+ /**
+ * Required by {@link GridIoMessageFactory}.
+ */
+ public MvccTxSnapshotRequest() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ */
+ public MvccTxSnapshotRequest(long futId) {
+ this.futId = futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /**
+ * @return Future ID.
+ */
+ public long futureId() {
+ return futId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("futId", futId))
+ return false;
+
+ writer.incrementState();
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ futId = reader.readLong("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+ }
+
+ return reader.afterMessageRead(MvccTxSnapshotRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 136;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccTxSnapshotRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccWaitTxsRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccWaitTxsRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccWaitTxsRequest.java
new file mode 100644
index 0000000..ae57507
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/msg/MvccWaitTxsRequest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.mvcc.msg;
+
+import java.nio.ByteBuffer;
+
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ *
+ */
+public class MvccWaitTxsRequest implements MvccMessage {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long futId;
+
+ /** */
+ private GridLongList txs;
+
+ /**
+ *
+ */
+ public MvccWaitTxsRequest() {
+ // No-op.
+ }
+
+ /**
+ * @param futId Future ID.
+ * @param txs Transactions to wait for.
+ */
+ public MvccWaitTxsRequest(long futId, GridLongList txs) {
+ assert txs != null && txs.size() > 0 : txs;
+
+ this.futId = futId;
+ this.txs = txs;
+ }
+
+ /**
+ * @return Future ID.
+ */
+ public long futureId() {
+ return futId;
+ }
+
+ /**
+ * @return Transactions to wait for.
+ */
+ public GridLongList transactions() {
+ return txs;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean waitForCoordinatorInit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean processedFromNioThread() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeLong("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeMessage("txs", txs))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ futId = reader.readLong("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ txs = reader.readMessage("txs");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(MvccWaitTxsRequest.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 142;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(MvccWaitTxsRequest.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxKey.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxKey.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxKey.java
new file mode 100644
index 0000000..92aff7b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxKey.java
@@ -0,0 +1,76 @@
+/*
+ * 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.mvcc.txlog;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+public class TxKey {
+ /** */
+ private final long major;
+
+ /** */
+ private final long minor;
+
+ /**
+ * @param major Major version.
+ * @param minor Minor version
+ */
+ public TxKey(long major, long minor) {
+ this.major = major;
+ this.minor = minor;
+ }
+
+ /**
+ * @return Major version.
+ */
+ public long major() {
+ return major;
+ }
+
+ /**
+ * @return Minor version.
+ */
+ public long minor() {
+ return minor;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || o.getClass() != TxKey.class) return false;
+
+ TxKey txKey = (TxKey) o;
+
+ return major == txKey.major && minor == txKey.minor;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int result = (int) (major ^ (major >>> 32));
+ result = 31 * result + (int) (minor ^ (minor >>> 32));
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(TxKey.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java
new file mode 100644
index 0000000..905bfc4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java
@@ -0,0 +1,584 @@
+/*
+ * 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.mvcc.txlog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageInitRecord;
+import org.apache.ignite.internal.processors.cache.persistence.DbCheckpointListener;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryEx;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
+import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseListImpl;
+import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
+import org.apache.ignite.internal.util.IgniteTree;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.pagemem.PageIdAllocator.FLAG_IDX;
+import static org.apache.ignite.internal.pagemem.PageIdAllocator.INDEX_PARTITION;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+
+/**
+ *
+ */
+public class TxLog implements DbCheckpointListener {
+ /** */
+ public static final String TX_LOG_CACHE_NAME = "TxLog";
+
+ /** */
+ public static final int TX_LOG_CACHE_ID = CU.cacheId(TX_LOG_CACHE_NAME);
+
+ /** */
+ private static final TxKey LOWEST = new TxKey(0, 0);
+
+ /** */
+ private final IgniteCacheDatabaseSharedManager mgr;
+
+ /** */
+ private ReuseListImpl reuseList;
+
+ /** */
+ private TxLogTree tree;
+
+ /** */
+ private ConcurrentMap<TxKey, Sync> keyMap = new ConcurrentHashMap<>();
+
+ /**
+ *
+ * @param ctx Kernal context.
+ * @param mgr Database shared manager.
+ */
+ public TxLog(GridKernalContext ctx, IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException {
+ this.mgr = mgr;
+
+ init(ctx);
+ }
+
+ /**
+ *
+ * @param ctx Kernal context.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void init(GridKernalContext ctx) throws IgniteCheckedException {
+ if (CU.isPersistenceEnabled(ctx.config())) {
+ mgr.checkpointReadLock();
+
+ try {
+ IgniteWriteAheadLogManager wal = ctx.cache().context().wal();
+ PageMemoryEx pageMemory = (PageMemoryEx)mgr.dataRegion(TX_LOG_CACHE_NAME).pageMemory();
+
+ long partMetaId = pageMemory.partitionMetaPageId(TX_LOG_CACHE_ID, 0);
+ long partMetaPage = pageMemory.acquirePage(TX_LOG_CACHE_ID, partMetaId);
+
+ long treeRoot, reuseListRoot;
+
+ boolean isNew = false;
+
+ try {
+ long pageAddr = pageMemory.writeLock(TX_LOG_CACHE_ID, partMetaId, partMetaPage);
+
+ try {
+ if (PageIO.getType(pageAddr) != PageIO.T_PART_META) {
+ // Initialize new page.
+ PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.latest();
+
+ io.initNewPage(pageAddr, partMetaId, pageMemory.pageSize());
+
+ treeRoot = pageMemory.allocatePage(TX_LOG_CACHE_ID, 0, PageMemory.FLAG_DATA);
+ reuseListRoot = pageMemory.allocatePage(TX_LOG_CACHE_ID, 0, PageMemory.FLAG_DATA);
+
+ assert PageIdUtils.flag(treeRoot) == PageMemory.FLAG_DATA;
+ assert PageIdUtils.flag(reuseListRoot) == PageMemory.FLAG_DATA;
+
+ io.setTreeRoot(pageAddr, treeRoot);
+ io.setReuseListRoot(pageAddr, reuseListRoot);
+
+ if (PageHandler.isWalDeltaRecordNeeded(pageMemory, TX_LOG_CACHE_ID, partMetaId, partMetaPage, wal, null))
+ wal.log(new MetaPageInitRecord(
+ TX_LOG_CACHE_ID,
+ partMetaId,
+ io.getType(),
+ io.getVersion(),
+ treeRoot,
+ reuseListRoot
+ ));
+
+ isNew = true;
+ }
+ else {
+ PagePartitionMetaIO io = PageIO.getPageIO(pageAddr);
+
+ treeRoot = io.getTreeRoot(pageAddr);
+ reuseListRoot = io.getReuseListRoot(pageAddr);
+
+ assert PageIdUtils.flag(treeRoot) == PageMemory.FLAG_DATA :
+ U.hexLong(treeRoot) + ", part=" + 0 + ", TX_LOG_CACHE_ID=" + TX_LOG_CACHE_ID;
+ assert PageIdUtils.flag(reuseListRoot) == PageMemory.FLAG_DATA :
+ U.hexLong(reuseListRoot) + ", part=" + 0 + ", TX_LOG_CACHE_ID=" + TX_LOG_CACHE_ID;
+ }
+ }
+ finally {
+ pageMemory.writeUnlock(TX_LOG_CACHE_ID, partMetaId, partMetaPage, null, isNew);
+ }
+ }
+ finally {
+ pageMemory.releasePage(TX_LOG_CACHE_ID, partMetaId, partMetaPage);
+ }
+
+ reuseList = new ReuseListImpl(
+ TX_LOG_CACHE_ID,
+ TX_LOG_CACHE_NAME,
+ pageMemory,
+ wal,
+ reuseListRoot,
+ isNew);
+
+ tree = new TxLogTree(pageMemory, wal, treeRoot, reuseList, ctx.failure(), isNew);
+
+ ((GridCacheDatabaseSharedManager)mgr).addCheckpointListener(this);
+ }
+ finally {
+ mgr.checkpointReadUnlock();
+ }
+ }
+ else {
+ PageMemory pageMemory = mgr.dataRegion(TX_LOG_CACHE_NAME).pageMemory();
+ ReuseList reuseList1 = mgr.reuseList(TX_LOG_CACHE_NAME);
+
+ long treeRoot;
+
+ if ((treeRoot = reuseList1.takeRecycledPage()) == 0L)
+ treeRoot = pageMemory.allocatePage(TX_LOG_CACHE_ID, INDEX_PARTITION, FLAG_IDX);
+
+ tree = new TxLogTree(pageMemory, null, treeRoot, reuseList1, ctx.failure(), true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onCheckpointBegin(Context ctx) throws IgniteCheckedException {
+ reuseList.saveMetadata();
+ }
+
+ /**
+ *
+ * @param major Major version.
+ * @param minor Minor version.
+ * @return Transaction state for given version.
+ * @throws IgniteCheckedException If failed.
+ */
+ public byte get(long major, long minor) throws IgniteCheckedException {
+ return get(new TxKey(major, minor));
+ }
+
+ /**
+ *
+ * @param key Transaction key.
+ * @return Transaction state for given version.
+ * @throws IgniteCheckedException If failed.
+ */
+ public byte get(TxKey key) throws IgniteCheckedException {
+ TxRow row = tree.findOne(key);
+
+ return row == null ? TxState.NA : row.state();
+ }
+
+ /**
+ *
+ * @param key TxKey.
+ * @param state Transaction state for given version.
+ * @param primary Flag if this is a primary node.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void put(TxKey key, byte state, boolean primary) throws IgniteCheckedException {
+ Sync sync = syncObject(key);
+
+ try {
+ mgr.checkpointReadLock();
+
+ try {
+ synchronized (sync) {
+ tree.invoke(key, null, new TxLogUpdateClosure(key.major(), key.minor(), state, primary));
+ }
+ }
+ finally {
+ mgr.checkpointReadUnlock();
+ }
+ } finally {
+ evict(key, sync);
+ }
+ }
+
+ /**
+ * Removes all records less or equals to the given version.
+ *
+ * @param major Major version.
+ * @param minor Minor version.
+ * @throws IgniteCheckedException If failed.
+ */
+ public void removeUntil(long major, long minor) throws IgniteCheckedException {
+ TraversingClosure clo = new TraversingClosure(major, minor);
+
+ tree.iterate(LOWEST, clo, clo);
+
+ if (clo.rows != null) {
+ for (TxKey row : clo.rows) {
+ remove(row);
+ }
+ }
+ }
+
+ /** */
+ private void remove(TxKey key) throws IgniteCheckedException {
+ Sync sync = syncObject(key);
+
+ try {
+ mgr.checkpointReadLock();
+
+ try {
+ synchronized (sync) {
+ tree.removex(key);
+ }
+ }
+ finally {
+ mgr.checkpointReadUnlock();
+ }
+ } finally {
+ evict(key, sync);
+ }
+ }
+
+ /** */
+ private Sync syncObject(TxKey key) {
+ Sync sync = keyMap.get(key);
+
+ while (true) {
+ if (sync == null) {
+ Sync old = keyMap.putIfAbsent(key, sync = new Sync());
+
+ if (old == null)
+ return sync;
+ else
+ sync = old;
+ }
+ else {
+ int cntr = sync.counter;
+
+ while (cntr > 0) {
+ if (sync.casCounter(cntr, cntr + 1))
+ return sync;
+
+ cntr = sync.counter;
+ }
+
+ sync = keyMap.get(key);
+ }
+ }
+ }
+
+ /** */
+ private void evict(TxKey key, Sync sync) {
+ assert sync != null;
+
+ int cntr = sync.counter;
+
+ while (true) {
+ assert cntr > 0;
+
+ if (!sync.casCounter(cntr, cntr - 1)) {
+ cntr = sync.counter;
+
+ continue;
+ }
+
+ if (cntr == 1) {
+ boolean removed = keyMap.remove(key, sync);
+
+ assert removed;
+ }
+
+ break;
+ }
+ }
+
+ /**
+ *
+ */
+ private static class TraversingClosure extends TxKey implements BPlusTree.TreeRowClosure<TxKey, TxRow> {
+ /** */
+ private List<TxKey> rows;
+
+ /**
+ *
+ * @param major Major version.
+ * @param minor Minor version.
+ */
+ TraversingClosure(long major, long minor) {
+ super(major, minor);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<TxKey, TxRow> tree, BPlusIO<TxKey> io, long pageAddr,
+ int idx) throws IgniteCheckedException {
+
+ if (rows == null)
+ rows = new ArrayList<>();
+
+ TxLogIO logIO = (TxLogIO)io;
+ int offset = io.offset(idx);
+
+ rows.add(new TxKey(logIO.getMajor(pageAddr, offset), logIO.getMinor(pageAddr, offset)));
+
+ return true;
+ }
+ }
+
+ /** */
+ private static class Sync {
+ /** */
+ private static final AtomicIntegerFieldUpdater<Sync> UPD = AtomicIntegerFieldUpdater.newUpdater(Sync.class, "counter");
+
+ /** */
+ volatile int counter = 1;
+
+ /** */
+ boolean casCounter(int old, int upd) {
+ return UPD.compareAndSet(this, old, upd);
+ }
+ }
+
+ /**
+ * TxLog update closure.
+ */
+ private static final class TxLogUpdateClosure implements IgniteTree.InvokeClosure<TxRow> {
+ /** */
+ private final long major;
+
+ /** */
+ private final long minor;
+
+ /** */
+ private final byte newState;
+
+ /** */
+ private final boolean primary;
+
+ /** */
+ private IgniteTree.OperationType treeOp;
+
+ /**
+ *
+ * @param major Coordinator version.
+ * @param minor Counter.
+ * @param newState New Tx newState.
+ * @param primary Flag if this is primary node.
+ */
+ TxLogUpdateClosure(long major, long minor, byte newState, boolean primary) {
+ assert major > MVCC_CRD_COUNTER_NA && minor > MVCC_COUNTER_NA && newState != TxState.NA;
+ this.major = major;
+ this.minor = minor;
+ this.newState = newState;
+ this.primary = primary;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void call(@Nullable TxRow row) {
+ if (row == null) {
+ valid();
+
+ return;
+ }
+
+ byte currState = row.state();
+
+ switch (currState) {
+ case TxState.NA:
+ checkNa(currState);
+
+ break;
+
+ case TxState.PREPARED:
+ checkPrepared(currState);
+
+ break;
+
+ case TxState.COMMITTED:
+ checkCommitted(currState);
+
+ break;
+
+ case TxState.ABORTED:
+ checkAborted(currState);
+
+ break;
+
+ default:
+ throw new IllegalStateException("Unknown tx state: " + currState);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public TxRow newRow() {
+ return treeOp == IgniteTree.OperationType.PUT ? new TxRow(major, minor, newState) : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteTree.OperationType operationType() {
+ return treeOp;
+ }
+
+ /**
+ * Checks update possibility for {@code TxState.NA} tx status.
+ *
+ * @param currState Current tx state.
+ */
+ private void checkNa(byte currState) {
+ switch (newState) {
+ case TxState.ABORTED:
+ case TxState.PREPARED:
+ valid();
+
+ break;
+
+ case TxState.COMMITTED:
+ invalid(currState); // TODO IGNITE-8445
+
+ break;
+
+ default:
+ invalid(currState);
+ }
+ }
+
+ /**
+ * Checks update possibility for {@code TxState.PREPARED} status.
+ *
+ * @param currState Current tx state.
+ */
+ private void checkPrepared(byte currState) {
+ switch (newState) {
+ case TxState.ABORTED:
+ case TxState.COMMITTED:
+ valid();
+
+ break;
+
+ case TxState.PREPARED:
+ ignore();
+
+ break;
+
+ default:
+ invalid(currState);
+ }
+ }
+
+ /**
+ * Checks update possibility for {@code TxState.COMMITTED} status.
+ *
+ * @param currState Current tx state.
+ */
+ private void checkCommitted(byte currState) {
+ switch (newState) {
+ case TxState.COMMITTED:
+ ignore();
+
+ break;
+
+ case TxState.PREPARED:
+ if (primary)
+ ignore(); // In case when remote tx has updated the current state before.
+ else
+ invalid(currState);
+
+ break;
+
+ default:
+ invalid(currState);
+ }
+ }
+
+ /**
+ * Checks update possibility for {@code TxState.ABORTED} status.
+ *
+ * @param currState Current tx state.
+ */
+ private void checkAborted(byte currState) {
+ switch (newState) {
+ case TxState.ABORTED:
+ ignore();
+
+ break;
+
+ case TxState.PREPARED:
+ if (primary)
+ ignore(); // In case when remote tx has updated the current state before.
+ else
+ invalid(currState);
+
+ break;
+
+ default:
+ invalid(currState);
+ }
+ }
+
+ /**
+ * Action for valid tx status update.
+ */
+ private void valid() {
+ assert treeOp == null;
+
+ treeOp = IgniteTree.OperationType.PUT;
+ }
+
+ /**
+ * Action for invalid tx status update.
+ */
+ private void invalid(byte currState) {
+ assert treeOp == null;
+
+ throw new IllegalStateException("Unexpected new transaction state. [currState=" +
+ currState + ", newState=" + newState + ", cntr=" + minor +']');
+ }
+
+ /**
+ * Action for ignoring tx status update.
+ */
+ private void ignore() {
+ assert treeOp == null;
+
+ treeOp = IgniteTree.OperationType.NOOP;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogIO.java
new file mode 100644
index 0000000..e952b43
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLogIO.java
@@ -0,0 +1,73 @@
+/*
+ * 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.mvcc.txlog;
+
+/**
+ *
+ */
+public interface TxLogIO {
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @param row Row to compare with.
+ * @return Comparision result.
+ */
+ int compare(long pageAddr, int off, TxKey row);
+
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @return Major version
+ */
+ long getMajor(long pageAddr, int off);
+
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @param major Major version
+ */
+ void setMajor(long pageAddr, int off, long major);
+
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @return Minor version.
+ */
+ long getMinor(long pageAddr, int off);
+
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @param minor Minor version.
+ */
+ void setMinor(long pageAddr, int off, long minor);
+
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @return Transaction state.
+ */
+ byte getState(long pageAddr, int off);
+
+ /**
+ * @param pageAddr Page address.
+ * @param off Item offset.
+ * @param state Transaction state.
+ */
+ void setState(long pageAddr, int off, byte state);
+}
[12/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java
new file mode 100644
index 0000000..5de6216
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRow.java
@@ -0,0 +1,32 @@
+/*
+ * 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.query.h2.opt;
+
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.processors.query.h2.database.H2Tree;
+import org.h2.result.Row;
+
+/**
+ *
+ */
+public interface GridH2SearchRow extends Row, MvccVersionAware {
+ /**
+ * @return {@code True} for rows used for index search (as opposed to rows stored in {@link H2Tree}.
+ */
+ public boolean indexSearchRow();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java
index 24a90b3..2e512b1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SearchRowAdapter.java
@@ -17,15 +17,20 @@
package org.apache.ignite.internal.processors.query.h2.opt;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.value.Value;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
+
/**
* Dummy H2 search row adadpter.
*/
-public abstract class GridH2SearchRowAdapter implements Row {
+public abstract class GridH2SearchRowAdapter implements GridH2SearchRow {
/** {@inheritDoc} */
@Override public void setKeyAndVersion(SearchRow old) {
throw new UnsupportedOperationException();
@@ -100,4 +105,24 @@ public abstract class GridH2SearchRowAdapter implements Row {
@Override public Value[] getValueList() {
throw new UnsupportedOperationException();
}
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return MVCC_CRD_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return MVCC_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return MVCC_OP_COUNTER_NA;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte mvccTxState() {
+ return TxState.NA;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
index 15be253..a612b63 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
@@ -24,7 +24,6 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
@@ -442,12 +441,12 @@ public class GridH2Table extends TableBase {
* @param prevRowAvailable Whether previous row is available.
* @throws IgniteCheckedException If failed.
*/
- public void update(CacheDataRow row, @Nullable CacheDataRow prevRow, boolean prevRowAvailable)
- throws IgniteCheckedException {
+ public void update(CacheDataRow row, @Nullable CacheDataRow prevRow, boolean prevRowAvailable) throws IgniteCheckedException {
assert desc != null;
GridH2KeyValueRowOnheap row0 = (GridH2KeyValueRowOnheap)desc.createRow(row);
- GridH2KeyValueRowOnheap prevRow0 = prevRow != null ? (GridH2KeyValueRowOnheap)desc.createRow(prevRow) : null;
+ GridH2KeyValueRowOnheap prevRow0 = prevRow != null ? (GridH2KeyValueRowOnheap)desc.createRow(prevRow) :
+ null;
row0.prepareValuesCache();
@@ -539,7 +538,6 @@ public class GridH2Table extends TableBase {
/**
* Add row to index.
- *
* @param idx Index to add row to.
* @param row Row to add to index.
* @param prevRow Previous row state, if any.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index 133333e..599d792 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.query.h2.sql;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
+import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -42,6 +43,7 @@ import org.apache.ignite.internal.processors.query.h2.dml.DmlAstUtils;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgniteUuid;
import org.h2.command.Command;
import org.h2.command.CommandContainer;
import org.h2.command.CommandInterface;
@@ -99,6 +101,7 @@ import org.h2.table.TableBase;
import org.h2.table.TableFilter;
import org.h2.table.TableView;
import org.h2.value.DataType;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.AND;
@@ -152,6 +155,15 @@ public class GridSqlQueryParser {
private static final Getter<Select, int[]> GROUP_INDEXES = getter(Select.class, "groupIndex");
/** */
+ private static final Getter<Select, Boolean> SELECT_IS_FOR_UPDATE = getter(Select.class, "isForUpdate");
+
+ /** */
+ private static final Getter<Select, Boolean> SELECT_IS_GROUP_QUERY = getter(Select.class, "isGroupQuery");
+
+ /** */
+ private static final Getter<SelectUnion, Boolean> UNION_IS_FOR_UPDATE = getter(SelectUnion.class, "isForUpdate");
+
+ /** */
private static final Getter<Operation, Integer> OPERATION_TYPE = getter(Operation.class, "opType");
/** */
@@ -527,7 +539,7 @@ public class GridSqlQueryParser {
* @return {@code true} in case of multiple statements.
*/
public static boolean checkMultipleStatements(PreparedStatement stmt) {
- Command cmd = COMMAND.get((JdbcPreparedStatement)stmt);
+ Command cmd = extractCommand(stmt);
return ORG_H2_COMMAND_COMMAND_LIST.equals(cmd.getClass().getName());
}
@@ -537,7 +549,7 @@ public class GridSqlQueryParser {
* @return Parsed select.
*/
public static Prepared prepared(PreparedStatement stmt) {
- Command cmd = COMMAND.get((JdbcPreparedStatement)stmt);
+ Command cmd = extractCommand(stmt);
assert cmd instanceof CommandContainer;
@@ -549,21 +561,99 @@ public class GridSqlQueryParser {
* @return Parsed select.
*/
public static PreparedWithRemaining preparedWithRemaining(PreparedStatement stmt) {
- Command cmd = COMMAND.get((JdbcPreparedStatement)stmt);
+ Command cmd = extractCommand(stmt);
if (cmd instanceof CommandContainer)
return new PreparedWithRemaining(PREPARED.get(cmd), null);
else {
Class<?> cmdCls = cmd.getClass();
- if (cmdCls.getName().equals(ORG_H2_COMMAND_COMMAND_LIST)) {
+ if (cmdCls.getName().equals(ORG_H2_COMMAND_COMMAND_LIST))
return new PreparedWithRemaining(PREPARED.get(LIST_COMMAND.get(cmd)), REMAINING.get(cmd));
- }
else
throw new IgniteSQLException("Unexpected statement command");
}
}
+ /** */
+ private static Command extractCommand(PreparedStatement stmt) {
+ try {
+ return COMMAND.get(stmt.unwrap(JdbcPreparedStatement.class));
+ } catch (SQLException e) {
+ throw new IgniteSQLException(e);
+ }
+ }
+
+ /**
+ * @param p Prepared.
+ * @return Whether {@code p} is an {@code SELECT FOR UPDATE} query.
+ */
+ public static boolean isForUpdateQuery(Prepared p) {
+ boolean union;
+
+ if (p.getClass() == Select.class)
+ union = false;
+ else if (p.getClass() == SelectUnion.class)
+ union = true;
+ else
+ return false;
+
+ boolean forUpdate = (!union && SELECT_IS_FOR_UPDATE.get((Select)p)) ||
+ (union && UNION_IS_FOR_UPDATE.get((SelectUnion)p));
+
+ if (union && forUpdate)
+ throw new IgniteSQLException("SELECT UNION FOR UPDATE is not supported.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ return forUpdate;
+ }
+
+ /**
+ * @param p Statement to rewrite, if needed.
+ * @param inTx Whether there is an active transaction.
+ * @return Query with {@code key} and {@code val} columns appended to the list of columns,
+ * if it's an {@code FOR UPDATE} query, or {@code null} if nothing has to be done.
+ */
+ @NotNull public static String rewriteQueryForUpdateIfNeeded(Prepared p, boolean inTx) {
+ GridSqlStatement gridStmt = new GridSqlQueryParser(false).parse(p);
+ return rewriteQueryForUpdateIfNeeded(gridStmt, inTx);
+ }
+
+ /**
+ * @param stmt Statement to rewrite, if needed.
+ * @param inTx Whether there is an active transaction.
+ * @return Query with {@code key} and {@code val} columns appended to the list of columns,
+ * if it's an {@code FOR UPDATE} query, or {@code null} if nothing has to be done.
+ */
+ @NotNull public static String rewriteQueryForUpdateIfNeeded(GridSqlStatement stmt, boolean inTx) {
+ // We have checked above that it's not an UNION query, so it's got to be SELECT.
+ assert stmt instanceof GridSqlSelect;
+
+ GridSqlSelect sel = (GridSqlSelect)stmt;
+
+ // How'd we get here otherwise?
+ assert sel.isForUpdate();
+
+ if (inTx) {
+ GridSqlAst from = sel.from();
+
+ GridSqlTable gridTbl = from instanceof GridSqlTable ? (GridSqlTable)from :
+ ((GridSqlAlias)from).child();
+
+ GridH2Table tbl = gridTbl.dataTable();
+
+ Column keyCol = tbl.getColumn(0);
+
+ sel.addColumn(new GridSqlAlias("_key_" + IgniteUuid.vmId(),
+ new GridSqlColumn(keyCol, null, keyCol.getName()), true), true);
+ }
+
+ // We need to remove this flag for final flag we'll feed to H2.
+ sel.forUpdate(false);
+
+ return sel.getSQL();
+ }
+
/**
* @param qry Query expression to parse.
* @return Subquery AST.
@@ -611,7 +701,7 @@ public class GridSqlQueryParser {
// We can't cache simple tables because otherwise it will be the same instance for all
// table filters. Thus we will not be able to distinguish one table filter from another.
// Table here is semantically equivalent to a table filter.
- if (tbl instanceof TableBase)
+ if (tbl instanceof TableBase || tbl instanceof MetaTable)
return new GridSqlTable(tbl);
// Other stuff can be cached because we will have separate instances in
@@ -662,6 +752,8 @@ public class GridSqlQueryParser {
TableFilter filter = select.getTopTableFilter();
+ boolean isForUpdate = SELECT_IS_FOR_UPDATE.get(select);
+
do {
assert0(filter != null, select);
assert0(filter.getNestedJoin() == null, select);
@@ -693,6 +785,30 @@ public class GridSqlQueryParser {
res.from(from);
+ if (isForUpdate) {
+ if (!(from instanceof GridSqlTable ||
+ (from instanceof GridSqlAlias && from.size() == 1 && from.child() instanceof GridSqlTable)))
+ throw new IgniteSQLException("SELECT FOR UPDATE with joins is not supported.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ GridSqlTable gridTbl = from instanceof GridSqlTable ? (GridSqlTable)from :
+ ((GridSqlAlias)from).child();
+
+ GridH2Table tbl = gridTbl.dataTable();
+
+ if (tbl == null)
+ throw new IgniteSQLException("SELECT FOR UPDATE query must involve Ignite table.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ if (select.getLimit() != null || select.getOffset() != null)
+ throw new IgniteSQLException("LIMIT/OFFSET clauses are not supported for SELECT FOR UPDATE.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
+ if (SELECT_IS_GROUP_QUERY.get(select))
+ throw new IgniteSQLException("SELECT FOR UPDATE with aggregates and/or GROUP BY is not supported.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+ }
+
ArrayList<Expression> expressions = select.getExpressions();
for (int i = 0; i < expressions.size(); i++)
@@ -708,6 +824,8 @@ public class GridSqlQueryParser {
if (havingIdx >= 0)
res.havingColumn(havingIdx);
+ res.forUpdate(isForUpdate);
+
processSortOrder(select.getSortOrder(), res);
res.limit(parseExpression(select.getLimit(), false));
@@ -1558,6 +1676,29 @@ public class GridSqlQueryParser {
}
/**
+ * @param stmt Prepared.
+ * @return Target table.
+ */
+ @NotNull public static GridH2Table dmlTable(@NotNull Prepared stmt) {
+ Table table;
+
+ if (stmt.getClass() == Insert.class)
+ table = INSERT_TABLE.get((Insert)stmt);
+ else if (stmt.getClass() == Merge.class)
+ table = MERGE_TABLE.get((Merge)stmt);
+ else if (stmt.getClass() == Delete.class)
+ table = DELETE_FROM.get((Delete)stmt).getTable();
+ else if (stmt.getClass() == Update.class)
+ table = UPDATE_TARGET.get((Update)stmt).getTable();
+ else
+ throw new IgniteException("Unsupported statement: " + stmt);
+
+ assert table instanceof GridH2Table : table;
+
+ return (GridH2Table) table;
+ }
+
+ /**
* Check if query may be run locally on all caches mentioned in the query.
* @param replicatedOnlyQry replicated-only query flag from original {@link SqlFieldsQuery}.
* @return {@code true} if query may be run locally on all caches mentioned in the query, i.e. there's no need
@@ -1658,6 +1799,13 @@ public class GridSqlQueryParser {
}
/**
+ * @return H2 to Grid objects map.
+ */
+ public Map<Object, Object> objectsMap() {
+ return h2ObjToGridObj;
+ }
+
+ /**
* @param qry Query.
* @return Parsed query AST.
*/
@@ -1676,6 +1824,10 @@ public class GridSqlQueryParser {
* @return Parsed AST.
*/
private GridSqlUnion parseUnion(SelectUnion union) {
+ if (UNION_IS_FOR_UPDATE.get(union))
+ throw new IgniteSQLException("SELECT UNION FOR UPDATE is not supported.",
+ IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
+
GridSqlUnion res = (GridSqlUnion)h2ObjToGridObj.get(union);
if (res != null)
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
index 5c32005..3e3b449 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
@@ -220,8 +220,10 @@ public class GridSqlQuerySplitter {
qry = parse(optimize(h2, conn, qry.getSQL(), params, false, enforceJoinOrder),
true);
+ boolean forUpdate = GridSqlQueryParser.isForUpdateQuery(prepared);
+
// Do the actual query split. We will update the original query AST, need to be careful.
- splitter.splitQuery(qry);
+ splitter.splitQuery(qry, forUpdate);
assert !F.isEmpty(splitter.mapSqlQrys): "map"; // We must have at least one map query.
assert splitter.rdcSqlQry != null: "rdc"; // We must have a reduce query.
@@ -261,13 +263,16 @@ public class GridSqlQuerySplitter {
// all map queries must have non-empty derivedPartitions to use this feature.
twoStepQry.derivedPartitions(mergePartitionsFromMultipleQueries(twoStepQry.mapQueries()));
+ twoStepQry.forUpdate(forUpdate);
+
return twoStepQry;
}
/**
* @param qry Optimized and normalized query to split.
+ * @param forUpdate {@code SELECT FOR UPDATE} flag.
*/
- private void splitQuery(GridSqlQuery qry) throws IgniteCheckedException {
+ private void splitQuery(GridSqlQuery qry, boolean forUpdate) throws IgniteCheckedException {
// Create a fake parent AST element for the query to allow replacing the query in the parent by split.
GridSqlSubquery fakeQryPrnt = new GridSqlSubquery(qry);
@@ -308,6 +313,15 @@ public class GridSqlQuerySplitter {
// Get back the updated query from the fake parent. It will be our reduce query.
qry = fakeQryPrnt.subquery();
+ // Reset SELECT FOR UPDATE flag for reduce query.
+ if (forUpdate) {
+ assert qry instanceof GridSqlSelect;
+
+ GridSqlSelect sel = (GridSqlSelect)qry;
+
+ sel.forUpdate(false);
+ }
+
String rdcQry = qry.getSQL();
checkNoDataTablesInReduceQuery(qry, rdcQry);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
index bfa0089..8ea61e1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java
@@ -56,6 +56,9 @@ public class GridSqlSelect extends GridSqlQuery {
/** */
private int havingCol = -1;
+ /** */
+ private boolean isForUpdate;
+
/**
* @param colIdx Column index as for {@link #column(int)}.
* @return Child index for {@link #child(int)}.
@@ -167,6 +170,9 @@ public class GridSqlSelect extends GridSqlQuery {
getSortLimitSQL(buff);
+ if (isForUpdate)
+ buff.append("\nFOR UPDATE");
+
return buff.toString();
}
@@ -364,6 +370,20 @@ public class GridSqlSelect extends GridSqlQuery {
}
/**
+ * @return Whether this statement is {@code FOR UPDATE}.
+ */
+ public boolean isForUpdate() {
+ return isForUpdate;
+ }
+
+ /**
+ * @param forUpdate Whether this statement is {@code FOR UPDATE}.
+ */
+ public void forUpdate(boolean forUpdate) {
+ isForUpdate = forUpdate;
+ }
+
+ /**
* @return Index of HAVING column.
*/
public int havingColumn() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
index 216a259..2402247 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java
@@ -18,7 +18,9 @@
package org.apache.ignite.internal.processors.query.h2.twostep;
import java.sql.Connection;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.SQLException;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
@@ -47,25 +50,33 @@ import org.apache.ignite.events.Event;
import org.apache.ignite.events.EventType;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.GridTopic;
+import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.managers.communication.GridMessageListener;
import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.distributed.dht.CompoundLockFuture;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionsReservation;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.query.CacheQueryType;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
import org.apache.ignite.internal.processors.query.GridQueryCancel;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.h2.H2Utils;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.processors.query.h2.ResultSetEnlistFuture;
import org.apache.ignite.internal.processors.query.h2.UpdateResult;
import org.apache.ignite.internal.processors.query.h2.opt.DistributedJoinMode;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2RetryException;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryCancelRequest;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryFailResponse;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageRequest;
@@ -73,14 +84,19 @@ import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQuery
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2DmlRequest;
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2DmlResponse;
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2SelectForUpdateTxDetails;
import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.typedef.CI1;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.apache.ignite.thread.IgniteThread;
+import org.h2.command.Prepared;
import org.h2.jdbc.JdbcResultSet;
import org.h2.value.Value;
import org.jetbrains.annotations.Nullable;
@@ -477,26 +493,6 @@ public class GridMapQueryExecutor {
}
/**
- * @param caches Cache IDs.
- * @return The first found partitioned cache.
- */
- private GridCacheContext<?,?> findFirstPartitioned(List<Integer> caches) {
- GridCacheSharedContext<?,?> sctx = ctx.cache().context();
-
- for (int i = 0; i < caches.size(); i++) {
- GridCacheContext<?,?> mainCctx = sctx.cacheContext(caches.get(i));
-
- if (mainCctx == null)
- throw new CacheException("Failed to find cache.");
-
- if (!mainCctx.isLocal() && !mainCctx.isReplicated())
- return mainCctx;
- }
-
- throw new IllegalStateException("Failed to find a partitioned cache.");
- }
-
- /**
* @param node Node.
* @param req Query request.
*/
@@ -519,10 +515,73 @@ public class GridMapQueryExecutor {
final List<Integer> cacheIds = req.caches();
int segments = explain || replicated || F.isEmpty(cacheIds) ? 1 :
- findFirstPartitioned(cacheIds).config().getQueryParallelism();
+ CU.firstPartitioned(ctx.cache().context(), cacheIds).config().getQueryParallelism();
final Object[] params = req.parameters();
+ final GridDhtTxLocalAdapter tx;
+
+ GridH2SelectForUpdateTxDetails txReq = req.txDetails();
+
+ try {
+ if (txReq != null) {
+ // Prepare to run queries.
+ GridCacheContext<?, ?> mainCctx = mainCacheContext(cacheIds);
+
+ if (mainCctx == null || mainCctx.atomic() || !mainCctx.mvccEnabled() || cacheIds.size() != 1)
+ throw new IgniteSQLException("SELECT FOR UPDATE is supported only for queries " +
+ "that involve single transactional cache.");
+
+ GridDhtTransactionalCacheAdapter txCache = (GridDhtTransactionalCacheAdapter)mainCctx.cache();
+
+ if (!node.isLocal()) {
+ tx = txCache.initTxTopologyVersion(
+ node.id(),
+ node,
+ txReq.version(),
+ txReq.futureId(),
+ txReq.miniId(),
+ txReq.firstClientRequest(),
+ req.topologyVersion(),
+ txReq.threadId(),
+ txReq.timeout(),
+ txReq.subjectId(),
+ txReq.taskNameHash());
+ }
+ else {
+ tx = MvccUtils.tx(ctx, txReq.version());
+
+ assert tx != null;
+ }
+ }
+ else
+ tx = null;
+ }
+ catch (IgniteException | IgniteCheckedException e) {
+ // send error if TX was not initialized properly.
+ releaseReservations();
+
+ U.error(log, "Failed to execute local query.", e);
+
+ sendError(node, req.requestId(), e);
+
+ return;
+ }
+
+ AtomicInteger runCntr;
+ CompoundLockFuture lockFut;
+
+ if (txReq != null && segments > 1) {
+ runCntr = new AtomicInteger(segments);
+ lockFut = new CompoundLockFuture(segments, tx);
+
+ lockFut.init();
+ }
+ else {
+ runCntr = null;
+ lockFut = null;
+ }
+
for (int i = 1; i < segments; i++) {
assert !F.isEmpty(cacheIds);
@@ -544,13 +603,17 @@ public class GridMapQueryExecutor {
false, // Replicated is always false here (see condition above).
req.timeout(),
params,
- true); // Lazy = true.
+ true,
+ req.mvccSnapshot(),
+ tx,
+ txReq,
+ lockFut,
+ runCntr);
}
else {
ctx.closure().callLocal(
new Callable<Void>() {
- @Override
- public Void call() throws Exception {
+ @Override public Void call() {
onQueryRequest0(node,
req.requestId(),
segment,
@@ -566,7 +629,12 @@ public class GridMapQueryExecutor {
false,
req.timeout(),
params,
- false); // Lazy = false.
+ false,
+ req.mvccSnapshot(),
+ tx,
+ txReq,
+ lockFut,
+ runCntr);
return null;
}
@@ -590,7 +658,11 @@ public class GridMapQueryExecutor {
replicated,
req.timeout(),
params,
- lazy);
+ lazy,
+ req.mvccSnapshot(),
+ tx,
+ txReq,
+ lockFut, runCntr);
}
/**
@@ -606,6 +678,11 @@ public class GridMapQueryExecutor {
* @param pageSize Page size.
* @param distributedJoinMode Query distributed join mode.
* @param lazy Streaming flag.
+ * @param mvccSnapshot MVCC snapshot.
+ * @param tx Transaction.
+ * @param txDetails TX details, if it's a {@code FOR UPDATE} request, or {@code null}.
+ * @param lockFut Lock future.
+ * @param runCntr Counter which counts remaining queries in case segmented index is used.
*/
private void onQueryRequest0(
final ClusterNode node,
@@ -623,10 +700,19 @@ public class GridMapQueryExecutor {
final boolean replicated,
final int timeout,
final Object[] params,
- boolean lazy
- ) {
+ boolean lazy,
+ @Nullable final MvccSnapshot mvccSnapshot,
+ @Nullable final GridDhtTxLocalAdapter tx,
+ @Nullable final GridH2SelectForUpdateTxDetails txDetails,
+ @Nullable final CompoundLockFuture lockFut,
+ @Nullable final AtomicInteger runCntr) {
MapQueryLazyWorker worker = MapQueryLazyWorker.currentWorker();
+ // In presence of TX, we also must always have matching details.
+ assert tx == null || txDetails != null;
+
+ boolean inTx = (tx != null);
+
if (lazy && worker == null) {
// Lazy queries must be re-submitted to dedicated workers.
MapQueryLazyWorkerKey key = new MapQueryLazyWorkerKey(node.id(), reqId, segmentId);
@@ -634,8 +720,28 @@ public class GridMapQueryExecutor {
worker.submit(new Runnable() {
@Override public void run() {
- onQueryRequest0(node, reqId, segmentId, schemaName, qrys, cacheIds, topVer, partsMap, parts,
- pageSize, distributedJoinMode, enforceJoinOrder, replicated, timeout, params, true);
+ onQueryRequest0(
+ node,
+ reqId,
+ segmentId,
+ schemaName,
+ qrys,
+ cacheIds,
+ topVer,
+ partsMap,
+ parts,
+ pageSize,
+ distributedJoinMode,
+ enforceJoinOrder,
+ replicated,
+ timeout,
+ params,
+ true,
+ mvccSnapshot,
+ tx,
+ txDetails,
+ lockFut,
+ runCntr);
}
});
@@ -660,9 +766,11 @@ public class GridMapQueryExecutor {
return;
}
+ if (lazy && txDetails != null)
+ throw new IgniteSQLException("Lazy execution of SELECT FOR UPDATE queries is not supported.");
+
// Prepare to run queries.
- GridCacheContext<?, ?> mainCctx =
- !F.isEmpty(cacheIds) ? ctx.cache().context().cacheContext(cacheIds.get(0)) : null;
+ GridCacheContext<?, ?> mainCctx = mainCacheContext(cacheIds);
MapNodeResults nodeRess = resultsForNode(node.id());
@@ -671,7 +779,9 @@ public class GridMapQueryExecutor {
List<GridReservable> reserved = new ArrayList<>();
try {
- if (topVer != null) {
+ // We want to reserve only in not SELECT FOR UPDATE case -
+ // otherwise, their state is protected by locked topology.
+ if (topVer != null && txDetails == null) {
// Reserve primary for topology version or explicit partitions.
if (!reservePartitions(cacheIds, topVer, parts, reserved, node.id(), reqId)) {
// Unregister lazy worker because re-try may never reach this node again.
@@ -684,7 +794,7 @@ public class GridMapQueryExecutor {
}
}
- qr = new MapQueryResults(h2, reqId, qrys.size(), mainCctx, MapQueryLazyWorker.currentWorker());
+ qr = new MapQueryResults(h2, reqId, qrys.size(), mainCctx, MapQueryLazyWorker.currentWorker(), inTx);
if (nodeRess.put(reqId, segmentId, qr) != null)
throw new IllegalStateException();
@@ -701,6 +811,7 @@ public class GridMapQueryExecutor {
.pageSize(pageSize)
.topologyVersion(topVer)
.reservations(reserved)
+ .mvccSnapshot(mvccSnapshot)
.lazyWorker(worker);
Connection conn = h2.connectionForSchema(schemaName);
@@ -729,12 +840,56 @@ public class GridMapQueryExecutor {
for (GridCacheSqlQuery qry : qrys) {
ResultSet rs = null;
+ boolean removeMapping = false;
+
// If we are not the target node for this replicated query, just ignore it.
if (qry.node() == null || (segmentId == 0 && qry.node().equals(ctx.localNodeId()))) {
- rs = h2.executeSqlQueryWithTimer(conn, qry.query(),
- F.asList(qry.parameters(params)), true,
- timeout,
- qr.queryCancel(qryIdx));
+ String sql = qry.query(); Collection<Object> params0 = F.asList(qry.parameters(params));
+
+ PreparedStatement stmt;
+
+ try {
+ stmt = h2.prepareStatement(conn, sql, true);
+ }
+ catch (SQLException e) {
+ throw new IgniteCheckedException("Failed to parse SQL query: " + sql, e);
+ }
+
+ Prepared p = GridSqlQueryParser.prepared(stmt);
+
+ if (GridSqlQueryParser.isForUpdateQuery(p)) {
+ sql = GridSqlQueryParser.rewriteQueryForUpdateIfNeeded(p, inTx);
+ stmt = h2.prepareStatement(conn, sql, true);
+ }
+
+ h2.bindParameters(stmt, params0);
+
+ rs = h2.executeSqlQueryWithTimer(stmt, conn, sql, params0, timeout, qr.queryCancel(qryIdx));
+
+ if (inTx) {
+ ResultSetEnlistFuture enlistFut = ResultSetEnlistFuture.future(
+ ctx.localNodeId(),
+ txDetails.version(),
+ mvccSnapshot,
+ txDetails.threadId(),
+ IgniteUuid.randomUuid(),
+ txDetails.miniId(),
+ parts,
+ tx,
+ timeout,
+ mainCctx,
+ rs
+ );
+
+ if (lockFut != null)
+ lockFut.register(enlistFut);
+
+ enlistFut.init();
+
+ enlistFut.get();
+
+ rs.beforeFirst();
+ }
if (evt) {
ctx.event().record(new CacheQueryExecutedEvent<>(
@@ -763,8 +918,35 @@ public class GridMapQueryExecutor {
throw new QueryCancelledException();
}
+ if (inTx) {
+ if (tx.dht() && (runCntr == null || runCntr.decrementAndGet() == 0)) {
+ if (removeMapping = tx.empty() && !tx.queryEnlisted())
+ tx.rollbackAsync().get();
+ }
+ }
+
// Send the first page.
- sendNextPage(nodeRess, node, qr, qryIdx, segmentId, pageSize);
+ if (lockFut == null)
+ sendNextPage(nodeRess, node, qr, qryIdx, segmentId, pageSize, removeMapping);
+ else {
+ GridQueryNextPageResponse msg = prepareNextPage(nodeRess, node, qr, qryIdx, segmentId, pageSize, removeMapping);
+
+ if (msg != null) {
+ lockFut.listen(new IgniteInClosure<IgniteInternalFuture<Void>>() {
+ @Override public void apply(IgniteInternalFuture<Void> future) {
+ try {
+ if (node.isLocal())
+ h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg);
+ else
+ ctx.io().sendToGridTopic(node, GridTopic.TOPIC_QUERY, msg, QUERY_POOL);
+ }
+ catch (Exception e) {
+ U.error(log, e);
+ }
+ }
+ });
+ }
+ }
qryIdx++;
}
@@ -817,6 +999,14 @@ public class GridMapQueryExecutor {
}
/**
+ * @param cacheIds Cache ids.
+ * @return Id of the first cache in list, or {@code null} if list is empty.
+ */
+ private GridCacheContext mainCacheContext(List<Integer> cacheIds) {
+ return !F.isEmpty(cacheIds) ? ctx.cache().context().cacheContext(cacheIds.get(0)) : null;
+ }
+
+ /**
* Releases reserved partitions.
*/
private void releaseReservations() {
@@ -878,7 +1068,7 @@ public class GridMapQueryExecutor {
final boolean replicated = req.isFlagSet(GridH2QueryRequest.FLAG_REPLICATED);
if (!replicated && !F.isEmpty(cacheIds) &&
- findFirstPartitioned(cacheIds).config().getQueryParallelism() > 1) {
+ CU.firstPartitioned(ctx.cache().context(), cacheIds).config().getQueryParallelism() > 1) {
fldsQry.setDistributedJoins(true);
local = false;
@@ -1010,12 +1200,12 @@ public class GridMapQueryExecutor {
if (lazyWorker != null) {
lazyWorker.submit(new Runnable() {
@Override public void run() {
- sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize());
+ sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize(), false);
}
});
}
else
- sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize());
+ sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize(), false);
}
}
@@ -1026,15 +1216,18 @@ public class GridMapQueryExecutor {
* @param qry Query.
* @param segmentId Index segment ID.
* @param pageSize Page size.
+ * @param removeMapping Remove mapping flag.
+ * @return Next page.
+ * @throws IgniteCheckedException If failed.
*/
- private void sendNextPage(MapNodeResults nodeRess, ClusterNode node, MapQueryResults qr, int qry, int segmentId,
- int pageSize) {
+ private GridQueryNextPageResponse prepareNextPage(MapNodeResults nodeRess, ClusterNode node, MapQueryResults qr, int qry, int segmentId,
+ int pageSize, boolean removeMapping) throws IgniteCheckedException {
MapQueryResult res = qr.result(qry);
assert res != null;
if (res.closed())
- return;
+ return null;
int page = res.page();
@@ -1054,20 +1247,45 @@ public class GridMapQueryExecutor {
}
}
- try {
- boolean loc = node.isLocal();
+ boolean loc = node.isLocal();
- GridQueryNextPageResponse msg = new GridQueryNextPageResponse(qr.queryRequestId(), segmentId, qry, page,
- page == 0 ? res.rowCount() : -1,
- res.columnCount(),
- loc ? null : toMessages(rows, new ArrayList<Message>(res.columnCount())),
- loc ? rows : null,
- last);
+ // In case of SELECT FOR UPDATE the last columns is _KEY,
+ // we can't retrieve them for an arbitrary row otherwise.
+ int colsCnt = !qr.isForUpdate() ? res.columnCount() : res.columnCount() - 1;
- if (loc)
- h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg);
- else
- ctx.io().sendToGridTopic(node, GridTopic.TOPIC_QUERY, msg, QUERY_POOL);
+ GridQueryNextPageResponse msg = new GridQueryNextPageResponse(qr.queryRequestId(), segmentId, qry, page,
+ page == 0 ? res.rowCount() : -1,
+ colsCnt,
+ loc ? null : toMessages(rows, new ArrayList<>(res.columnCount()), colsCnt),
+ loc ? rows : null,
+ last);
+
+ msg.removeMapping(removeMapping);
+
+ return msg;
+ }
+
+ /**
+ * @param nodeRess Results.
+ * @param node Node.
+ * @param qr Query results.
+ * @param qry Query.
+ * @param segmentId Index segment ID.
+ * @param pageSize Page size.
+ * @param removeMapping Remove mapping flag.
+ */
+ @SuppressWarnings("unchecked")
+ private void sendNextPage(MapNodeResults nodeRess, ClusterNode node, MapQueryResults qr, int qry, int segmentId,
+ int pageSize, boolean removeMapping) {
+ try {
+ GridQueryNextPageResponse msg = prepareNextPage(nodeRess, node, qr, qry, segmentId, pageSize, removeMapping);
+
+ if (msg != null) {
+ if (node.isLocal())
+ h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg);
+ else
+ ctx.io().sendToGridTopic(node, GridTopic.TOPIC_QUERY, msg, QUERY_POOL);
+ }
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to send message.", e);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexIterator.java
index 1c0efb3..851e1e4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexIterator.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexIterator.java
@@ -22,11 +22,12 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
-
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
import org.h2.index.Cursor;
import org.h2.result.Row;
+import org.jetbrains.annotations.Nullable;
/**
* Iterator that transparently and sequentially traverses a bunch of {@link GridMergeIndex} objects.
@@ -59,6 +60,9 @@ class GridMergeIndexIterator implements Iterator<List<?>>, AutoCloseable {
/** Whether remote resources were released. */
private boolean released;
+ /** */
+ private MvccQueryTracker mvccTracker;
+
/**
* Constructor.
*
@@ -69,14 +73,19 @@ class GridMergeIndexIterator implements Iterator<List<?>>, AutoCloseable {
* @param distributedJoins Distributed joins.
* @throws IgniteCheckedException if failed.
*/
- GridMergeIndexIterator(GridReduceQueryExecutor rdcExec, Collection<ClusterNode> nodes, ReduceQueryRun run,
- long qryReqId, boolean distributedJoins)
+ GridMergeIndexIterator(GridReduceQueryExecutor rdcExec,
+ Collection<ClusterNode> nodes,
+ ReduceQueryRun run,
+ long qryReqId,
+ boolean distributedJoins,
+ @Nullable MvccQueryTracker mvccTracker)
throws IgniteCheckedException {
this.rdcExec = rdcExec;
this.nodes = nodes;
this.run = run;
this.qryReqId = qryReqId;
this.distributedJoins = distributedJoins;
+ this.mvccTracker = mvccTracker;
this.idxIter = run.indexes().iterator();
@@ -155,7 +164,7 @@ class GridMergeIndexIterator implements Iterator<List<?>>, AutoCloseable {
private void releaseIfNeeded() {
if (!released) {
try {
- rdcExec.releaseRemoteResources(nodes, run, qryReqId, distributedJoins);
+ rdcExec.releaseRemoteResources(nodes, run, qryReqId, distributedJoins, mvccTracker);
}
finally {
released = true;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
index 65aeae9..910ad1a 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
@@ -37,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -60,6 +61,11 @@ import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxSelectForUpdateFuture;
+import org.apache.ignite.internal.processors.cache.distributed.near.TxTopologyVersionFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
@@ -67,6 +73,7 @@ import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
import org.apache.ignite.internal.processors.query.GridQueryCacheObjectsIterator;
import org.apache.ignite.internal.processors.query.GridQueryCancel;
import org.apache.ignite.internal.processors.query.GridRunningQueryInfo;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.h2.H2FieldsIterator;
import org.apache.ignite.internal.processors.query.h2.H2Utils;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
@@ -81,15 +88,18 @@ import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQuery
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2DmlRequest;
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2DmlResponse;
import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest;
+import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2SelectForUpdateTxDetails;
import org.apache.ignite.internal.util.GridIntIterator;
import org.apache.ignite.internal.util.GridIntList;
import org.apache.ignite.internal.util.GridSpinBusyLock;
+import org.apache.ignite.internal.util.typedef.C2;
import org.apache.ignite.internal.util.typedef.CIX2;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiClosure;
import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.transactions.TransactionException;
import org.h2.command.ddl.CreateTableData;
@@ -104,6 +114,7 @@ import org.jetbrains.annotations.Nullable;
import static java.util.Collections.singletonList;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_SQL_RETRY_TIMEOUT;
import static org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion.NONE;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.*;
import static org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery.EMPTY_PARAMS;
import static org.apache.ignite.internal.processors.query.h2.opt.DistributedJoinMode.OFF;
import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.REDUCE;
@@ -350,8 +361,15 @@ public class GridReduceQueryExecutor {
if (msg.retry() != null)
retry(r, msg.retry(), node.id());
- else if (msg.page() == 0) // Do count down on each first page received.
+ else if (msg.page() == 0) {
+ // Do count down on each first page received.
r.latch().countDown();
+
+ GridNearTxSelectForUpdateFuture sfuFut = r.selectForUpdateFuture();
+
+ if (sfuFut != null)
+ sfuFut.onResult(node.id(), (long)msg.allRows(), msg.removeMapping(), null);
+ }
}
/**
@@ -550,6 +568,7 @@ public class GridReduceQueryExecutor {
* @param params Query parameters.
* @param parts Partitions.
* @param lazy Lazy execution flag.
+ * @param mvccTracker Query tracker.
* @return Rows iterator.
*/
public Iterator<List<?>> query(
@@ -561,8 +580,10 @@ public class GridReduceQueryExecutor {
GridQueryCancel cancel,
Object[] params,
final int[] parts,
- boolean lazy
- ) {
+ boolean lazy,
+ MvccQueryTracker mvccTracker) {
+ assert !qry.mvccEnabled() || mvccTracker != null;
+
if (F.isEmpty(params))
params = EMPTY_PARAMS;
@@ -589,19 +610,52 @@ public class GridReduceQueryExecutor {
long qryReqId = qryIdGen.incrementAndGet();
- final ReduceQueryRun r = new ReduceQueryRun(qryReqId, qry.originalSql(), schemaName,
- h2.connectionForSchema(schemaName), qry.mapQueries().size(), qry.pageSize(),
- U.currentTimeMillis(), cancel);
+ List<Integer> cacheIds = qry.cacheIds();
+
+ boolean mvccEnabled = mvccEnabled(ctx);
+
+ final GridNearTxLocal curTx = mvccEnabled ? checkActive(tx(ctx)) : null;
+
+ final GridNearTxSelectForUpdateFuture sfuFut;
- AffinityTopologyVersion topVer = h2.readyTopologyVersion();
+ final boolean clientFirst;
- // Check if topology is changed while retrying on locked topology.
- if (h2.serverTopologyChanged(topVer) && ctx.cache().context().lockedTopologyVersion(null) != null) {
- throw new CacheException(new TransactionException("Server topology is changed during query " +
- "execution inside a transaction. It's recommended to rollback and retry transaction."));
+ AffinityTopologyVersion topVer;
+
+ if (qry.forUpdate()) {
+ // Indexing should have started TX at this point for FOR UPDATE query.
+ assert mvccEnabled && curTx != null;
+
+ try {
+ TxTopologyVersionFuture topFut = new TxTopologyVersionFuture(curTx, mvccTracker.context());
+
+ topVer = topFut.get();
+
+ clientFirst = topFut.clientFirst();
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteSQLException("Failed to map SELECT FOR UPDATE query on topology.", e);
+ }
+
+ sfuFut = new GridNearTxSelectForUpdateFuture(mvccTracker.context(), curTx, timeoutMillis);
}
+ else {
+ sfuFut = null;
- List<Integer> cacheIds = qry.cacheIds();
+ clientFirst = false;
+
+ topVer = h2.readyTopologyVersion();
+
+ // Check if topology has changed while retrying on locked topology.
+ if (h2.serverTopologyChanged(topVer) && ctx.cache().context().lockedTopologyVersion(null) != null) {
+ throw new CacheException(new TransactionException("Server topology is changed during query " +
+ "execution inside a transaction. It's recommended to rollback and retry transaction."));
+ }
+ }
+
+ final ReduceQueryRun r = new ReduceQueryRun(qryReqId, qry.originalSql(), schemaName,
+ h2.connectionForSchema(schemaName), qry.mapQueries().size(), qry.pageSize(),
+ U.currentTimeMillis(), sfuFut, cancel);
Collection<ClusterNode> nodes;
@@ -637,8 +691,12 @@ public class GridReduceQueryExecutor {
partsMap = nodesParts.partitionsMap();
qryMap = nodesParts.queryPartitionsMap();
- if (nodes == null)
+ if (nodes == null) {
+ if (sfuFut != null)
+ sfuFut.onDone(0L, null);
+
continue; // Retry.
+ }
assert !nodes.isEmpty();
@@ -656,6 +714,9 @@ public class GridReduceQueryExecutor {
}
}
+ if (sfuFut != null && !sfuFut.isFailed())
+ sfuFut.init(topVer, nodes);
+
int tblIdx = 0;
final boolean skipMergeTbl = !qry.explain() && qry.skipMergeTable();
@@ -774,7 +835,46 @@ public class GridReduceQueryExecutor {
.timeout(timeoutMillis)
.schemaName(schemaName);
- if (send(nodes, req, parts == null ? null : new ExplicitPartitionsSpecializer(qryMap), false)) {
+ if (curTx != null && curTx.mvccSnapshot() != null)
+ req.mvccSnapshot(curTx.mvccSnapshot());
+ else if (mvccTracker != null)
+ req.mvccSnapshot(mvccTracker.snapshot());
+
+ final C2<ClusterNode, Message, Message> pspec =
+ (parts == null ? null : new ExplicitPartitionsSpecializer(qryMap));
+
+ final C2<ClusterNode, Message, Message> spec;
+
+ if (qry.forUpdate()) {
+ final AtomicInteger cnt = new AtomicInteger();
+
+ spec = new C2<ClusterNode, Message, Message>() {
+ @Override public Message apply(ClusterNode clusterNode, Message msg) {
+ assert msg instanceof GridH2QueryRequest;
+
+ GridH2QueryRequest res = pspec != null ? (GridH2QueryRequest)pspec.apply(clusterNode, msg) :
+ new GridH2QueryRequest((GridH2QueryRequest)msg);
+
+ GridH2SelectForUpdateTxDetails txReq = new GridH2SelectForUpdateTxDetails(
+ curTx.threadId(),
+ IgniteUuid.randomUuid(),
+ cnt.incrementAndGet(),
+ curTx.subjectId(),
+ curTx.xidVersion(),
+ curTx.taskNameHash(),
+ clientFirst,
+ curTx.remainingTime());
+
+ res.txDetails(txReq);
+
+ return res;
+ }
+ };
+ }
+ else
+ spec = pspec;
+
+ if (send(nodes, req, spec, false)) {
awaitAllReplies(r, nodes, cancel);
Object state = r.state();
@@ -795,6 +895,9 @@ public class GridReduceQueryExecutor {
if (state instanceof AffinityTopologyVersion) {
retry = true;
+ // On-the-fly topology change must not be possible in FOR UPDATE case.
+ assert sfuFut == null;
+
// If remote node asks us to retry then we have outdated full partition map.
h2.awaitForReadyTopologyVersion((AffinityTopologyVersion)state);
}
@@ -807,7 +910,12 @@ public class GridReduceQueryExecutor {
if (!retry) {
if (skipMergeTbl) {
- resIter = new GridMergeIndexIterator(this, finalNodes, r, qryReqId, qry.distributedJoins());
+ resIter = new GridMergeIndexIterator(this,
+ finalNodes,
+ r,
+ qryReqId,
+ qry.distributedJoins(),
+ mvccTracker);
release = false;
}
@@ -834,21 +942,28 @@ public class GridReduceQueryExecutor {
timeoutMillis,
cancel);
- resIter = new H2FieldsIterator(res);
+ resIter = new H2FieldsIterator(res, mvccTracker, false);
+
+ mvccTracker = null; // To prevent callback inside finally block;
}
finally {
GridH2QueryContext.clearThreadLocal();
}
}
}
-
- if (retry) {
+ else {
if (Thread.currentThread().isInterrupted())
throw new IgniteInterruptedCheckedException("Query was interrupted.");
+ if (sfuFut != null)
+ sfuFut.onDone(0L);
+
continue;
}
+ if (sfuFut != null)
+ sfuFut.get();
+
return new GridQueryCacheObjectsIterator(resIter, h2.objectContext(), keepBinary);
}
catch (IgniteCheckedException | RuntimeException e) {
@@ -856,11 +971,21 @@ public class GridReduceQueryExecutor {
U.closeQuiet(r.connection());
+ CacheException resEx = null;
+
if (e instanceof CacheException) {
if (wasCancelled((CacheException)e))
- throw new CacheException("Failed to run reduce query locally.", new QueryCancelledException());
+ resEx = new CacheException("Failed to run reduce query locally.",
+ new QueryCancelledException());
+ else
+ resEx = (CacheException)e;
+ }
+
+ if (resEx != null) {
+ if (sfuFut != null)
+ sfuFut.onDone(resEx);
- throw (CacheException)e;
+ throw resEx;
}
Throwable cause = e;
@@ -873,11 +998,16 @@ public class GridReduceQueryExecutor {
cause = disconnectedErr;
}
- throw new CacheException("Failed to run reduce query locally.", cause);
+ resEx = new CacheException("Failed to run reduce query locally.", cause);
+
+ if (sfuFut != null)
+ sfuFut.onDone(resEx);
+
+ throw resEx;
}
finally {
if (release) {
- releaseRemoteResources(finalNodes, r, qryReqId, qry.distributedJoins());
+ releaseRemoteResources(finalNodes, r, qryReqId, qry.distributedJoins(), mvccTracker);
if (!skipMergeTbl) {
for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++)
@@ -1072,7 +1202,10 @@ public class GridReduceQueryExecutor {
* @param distributedJoins Distributed join flag.
*/
public void releaseRemoteResources(Collection<ClusterNode> nodes, ReduceQueryRun r, long qryReqId,
- boolean distributedJoins) {
+ boolean distributedJoins, MvccQueryTracker mvccTracker) {
+ if (mvccTracker != null)
+ mvccTracker.onDone();
+
// For distributedJoins need always send cancel request to cleanup resources.
if (distributedJoins)
send(nodes, new GridQueryCancelRequest(qryReqId), null, false);
@@ -1499,7 +1632,7 @@ public class GridReduceQueryExecutor {
* @param runLocParallel Run local handler in parallel thread.
* @return {@code true} If all messages sent successfully.
*/
- private boolean send(
+ public boolean send(
Collection<ClusterNode> nodes,
Message msg,
@Nullable IgniteBiClosure<ClusterNode, Message, Message> specialize,
@@ -1767,7 +1900,7 @@ public class GridReduceQueryExecutor {
}
/** */
- private static class ExplicitPartitionsSpecializer implements IgniteBiClosure<ClusterNode, Message, Message> {
+ private static class ExplicitPartitionsSpecializer implements C2<ClusterNode, Message, Message> {
/** Partitions map. */
private final Map<ClusterNode, IntArray> partsMap;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridResultPage.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridResultPage.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridResultPage.java
index 103084e..3c17640 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridResultPage.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridResultPage.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.processors.query.h2.twostep;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -26,7 +28,9 @@ import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageResponse;
+import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteClosure;
import org.apache.ignite.plugin.extensions.communication.Message;
import org.h2.value.Value;
@@ -68,9 +72,25 @@ public class GridResultPage {
Collection<?> plainRows = res.plainRows();
if (plainRows != null) {
+ assert plainRows instanceof ArrayList;
+
rowsInPage = plainRows.size();
- rows = (Iterator<Value[]>)plainRows.iterator();
+ if (rowsInPage == 0 || ((ArrayList<Value[]>)plainRows).get(0).length == res.columns())
+ rows = (Iterator<Value[]>)plainRows.iterator();
+ else {
+ // If it's a result of SELECT FOR UPDATE (we can tell by difference in number
+ // of columns checked above), we need to strip off stuff we don't need.
+ rows = F.iterator(plainRows, new IgniteClosure<Object, Value[]>() {
+ @Override public Value[] apply(Object o) {
+ Value[] row = (Value[])o;
+
+ assert row.length >= res.columns();
+
+ return Arrays.copyOfRange(row, 0, res.columns());
+ }
+ }, true);
+ }
}
else {
final int cols = res.columns();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.java
index 45f9c1f..76527bc 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.java
@@ -51,17 +51,22 @@ class MapQueryResults {
/** */
private volatile boolean cancelled;
+ /** {@code SELECT FOR UPDATE} flag. */
+ private final boolean forUpdate;
+
/**
* Constructor.
- *
+ * @param h2 Indexing instance.
* @param qryReqId Query request ID.
* @param qrys Number of queries.
* @param cctx Cache context.
* @param lazyWorker Lazy worker (if any).
+ * @param forUpdate {@code SELECT FOR UPDATE} flag.
*/
@SuppressWarnings("unchecked")
MapQueryResults(IgniteH2Indexing h2, long qryReqId, int qrys, @Nullable GridCacheContext<?, ?> cctx,
- @Nullable MapQueryLazyWorker lazyWorker) {
+ @Nullable MapQueryLazyWorker lazyWorker, boolean forUpdate) {
+ this.forUpdate = forUpdate;
this.h2 = h2;
this.qryReqId = qryReqId;
this.cctx = cctx;
@@ -101,11 +106,11 @@ class MapQueryResults {
/**
* Add result.
- *
* @param qry Query result index.
* @param q Query object.
* @param qrySrcNodeId Query source node.
* @param rs Result set.
+ * @param params Query arguments.
*/
void addResult(int qry, GridCacheSqlQuery q, UUID qrySrcNodeId, ResultSet rs, Object[] params) {
MapQueryResult res = new MapQueryResult(h2, rs, cctx, qrySrcNodeId, q, params, lazyWorker);
@@ -172,4 +177,11 @@ class MapQueryResults {
long queryRequestId() {
return qryReqId;
}
+
+ /**
+ * @return {@code SELECT FOR UPDATE} flag.
+ */
+ public boolean isForUpdate() {
+ return forUpdate;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/ReduceQueryRun.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/ReduceQueryRun.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/ReduceQueryRun.java
index 73bb002..df72e8c 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/ReduceQueryRun.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/ReduceQueryRun.java
@@ -17,20 +17,20 @@
package org.apache.ignite.internal.processors.query.h2.twostep;
-import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
-import org.apache.ignite.internal.processors.query.GridQueryCancel;
-import org.apache.ignite.internal.processors.query.GridRunningQueryInfo;
-import org.h2.jdbc.JdbcConnection;
-import org.jetbrains.annotations.Nullable;
-
-import javax.cache.CacheException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
+import javax.cache.CacheException;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxSelectForUpdateFuture;
+import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
+import org.apache.ignite.internal.processors.query.GridQueryCancel;
+import org.apache.ignite.internal.processors.query.GridRunningQueryInfo;
+import org.h2.jdbc.JdbcConnection;
+import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.query.GridCacheQueryType.SQL_FIELDS;
@@ -56,9 +56,11 @@ class ReduceQueryRun {
/** Can be either CacheException in case of error or AffinityTopologyVersion to retry if needed. */
private final AtomicReference<Object> state = new AtomicReference<>();
+ /** Future controlling {@code SELECT FOR UPDATE} query execution. */
+ private final GridNearTxSelectForUpdateFuture selectForUpdateFut;
+
/**
* Constructor.
- *
* @param id Query ID.
* @param qry Query text.
* @param schemaName Schema name.
@@ -66,17 +68,21 @@ class ReduceQueryRun {
* @param idxsCnt Number of indexes.
* @param pageSize Page size.
* @param startTime Start time.
+ * @param selectForUpdateFut Future controlling {@code SELECT FOR UPDATE} query execution.
* @param cancel Query cancel handler.
*/
ReduceQueryRun(Long id, String qry, String schemaName, Connection conn, int idxsCnt, int pageSize, long startTime,
- GridQueryCancel cancel) {
- this.qry = new GridRunningQueryInfo(id, qry, SQL_FIELDS, schemaName, startTime, cancel, false);
+ GridNearTxSelectForUpdateFuture selectForUpdateFut, GridQueryCancel cancel) {
+ this.qry = new GridRunningQueryInfo(id, qry, SQL_FIELDS, schemaName, startTime, cancel,
+ false);
this.conn = (JdbcConnection)conn;
this.idxs = new ArrayList<>(idxsCnt);
this.pageSize = pageSize > 0 ? pageSize : GridCacheTwoStepQuery.DFLT_PAGE_SIZE;
+
+ this.selectForUpdateFut = selectForUpdateFut;
}
/**
@@ -154,4 +160,11 @@ class ReduceQueryRun {
void latch(CountDownLatch latch) {
this.latch = latch;
}
+
+ /**
+ * @return {@code SELECT FOR UPDATE} future, if any.
+ */
+ @Nullable public GridNearTxSelectForUpdateFuture selectForUpdateFuture() {
+ return selectForUpdateFut;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
index 4e1fadb..8672194 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2QueryRequest.java
@@ -31,6 +31,7 @@ import org.apache.ignite.internal.GridDirectTransient;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
import org.apache.ignite.internal.processors.cache.query.QueryTable;
@@ -42,6 +43,7 @@ import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
import org.apache.ignite.plugin.extensions.communication.MessageReader;
import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery.EMPTY_PARAMS;
@@ -133,6 +135,12 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
/** Schema name. */
private String schemaName;
+ /** */
+ private MvccSnapshot mvccSnapshot;
+
+ /** TX details holder for {@code SELECT FOR UPDATE}, or {@code null} if not applicable. */
+ private GridH2SelectForUpdateTxDetails txReq;
+
/**
* Required by {@link Externalizable}
*/
@@ -157,6 +165,25 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
params = req.params;
paramsBytes = req.paramsBytes;
schemaName = req.schemaName;
+ mvccSnapshot = req.mvccSnapshot;
+ txReq = req.txReq;
+ }
+
+ /**
+ * @return MVCC snapshot.
+ */
+ @Nullable public MvccSnapshot mvccSnapshot() {
+ return mvccSnapshot;
+ }
+
+ /**
+ * @param mvccSnapshot MVCC snapshot version.
+ * @return {@code this}.
+ */
+ public GridH2QueryRequest mvccSnapshot(MvccSnapshot mvccSnapshot) {
+ this.mvccSnapshot = mvccSnapshot;
+
+ return this;
}
/**
@@ -373,6 +400,20 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
return this;
}
+ /**
+ * @return TX details holder for {@code SELECT FOR UPDATE}, or {@code null} if not applicable.
+ */
+ public GridH2SelectForUpdateTxDetails txDetails() {
+ return txReq;
+ }
+
+ /**
+ * @param txDetails TX details holder for {@code SELECT FOR UPDATE}, or {@code null} if not applicable.
+ */
+ public void txDetails(GridH2SelectForUpdateTxDetails txDetails) {
+ this.txReq = txDetails;
+ }
+
/** {@inheritDoc} */
@Override public void marshall(Marshaller m) {
if (paramsBytes != null)
@@ -435,65 +476,77 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
writer.incrementState();
case 2:
- if (!writer.writeInt("pageSize", pageSize))
+ if (!writer.writeMessage("mvccSnapshot", mvccSnapshot))
return false;
writer.incrementState();
case 3:
- if (!writer.writeByteArray("paramsBytes", paramsBytes))
+ if (!writer.writeInt("pageSize", pageSize))
return false;
writer.incrementState();
case 4:
- if (!writer.writeMap("parts", parts, MessageCollectionItemType.UUID, MessageCollectionItemType.INT_ARR))
+ if (!writer.writeByteArray("paramsBytes", paramsBytes))
return false;
writer.incrementState();
case 5:
- if (!writer.writeCollection("qrys", qrys, MessageCollectionItemType.MSG))
+ if (!writer.writeMap("parts", parts, MessageCollectionItemType.UUID, MessageCollectionItemType.INT_ARR))
return false;
writer.incrementState();
case 6:
- if (!writer.writeLong("reqId", reqId))
+ if (!writer.writeIntArray("qryParts", qryParts))
return false;
writer.incrementState();
case 7:
- if (!writer.writeCollection("tbls", tbls, MessageCollectionItemType.MSG))
+ if (!writer.writeCollection("qrys", qrys, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 8:
- if (!writer.writeInt("timeout", timeout))
+ if (!writer.writeLong("reqId", reqId))
return false;
writer.incrementState();
case 9:
- if (!writer.writeMessage("topVer", topVer))
+ if (!writer.writeString("schemaName", schemaName))
return false;
writer.incrementState();
-
case 10:
- if (!writer.writeIntArray("qryParts", qryParts))
+ if (!writer.writeCollection("tbls", tbls, MessageCollectionItemType.MSG))
return false;
writer.incrementState();
case 11:
- if (!writer.writeString("schemaName", schemaName))
+ if (!writer.writeInt("timeout", timeout))
+ return false;
+
+ writer.incrementState();
+
+ case 12:
+ if (!writer.writeMessage("topVer", topVer))
return false;
writer.incrementState();
+
+ case 13:
+ if (!writer.writeMessage("txReq", txReq))
+ return false;
+
+ writer.incrementState();
+
}
return true;
@@ -524,7 +577,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 2:
- pageSize = reader.readInt("pageSize");
+ mvccSnapshot = reader.readMessage("mvccSnapshot");
if (!reader.isLastRead())
return false;
@@ -532,7 +585,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 3:
- paramsBytes = reader.readByteArray("paramsBytes");
+ pageSize = reader.readInt("pageSize");
if (!reader.isLastRead())
return false;
@@ -540,7 +593,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 4:
- parts = reader.readMap("parts", MessageCollectionItemType.UUID, MessageCollectionItemType.INT_ARR, false);
+ paramsBytes = reader.readByteArray("paramsBytes");
if (!reader.isLastRead())
return false;
@@ -548,7 +601,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 5:
- qrys = reader.readCollection("qrys", MessageCollectionItemType.MSG);
+ parts = reader.readMap("parts", MessageCollectionItemType.UUID, MessageCollectionItemType.INT_ARR, false);
if (!reader.isLastRead())
return false;
@@ -556,7 +609,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 6:
- reqId = reader.readLong("reqId");
+ qryParts = reader.readIntArray("qryParts");
if (!reader.isLastRead())
return false;
@@ -564,7 +617,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 7:
- tbls = reader.readCollection("tbls", MessageCollectionItemType.MSG);
+ qrys = reader.readCollection("qrys", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -572,7 +625,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 8:
- timeout = reader.readInt("timeout");
+ reqId = reader.readLong("reqId");
if (!reader.isLastRead())
return false;
@@ -580,16 +633,15 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 9:
- topVer = reader.readMessage("topVer");
+ schemaName = reader.readString("schemaName");
if (!reader.isLastRead())
return false;
reader.incrementState();
-
case 10:
- qryParts = reader.readIntArray("qryParts");
+ tbls = reader.readCollection("tbls", MessageCollectionItemType.MSG);
if (!reader.isLastRead())
return false;
@@ -597,12 +649,29 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
reader.incrementState();
case 11:
- schemaName = reader.readString("schemaName");
+ timeout = reader.readInt("timeout");
if (!reader.isLastRead())
return false;
reader.incrementState();
+
+ case 12:
+ topVer = reader.readMessage("topVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 13:
+ txReq = reader.readMessage("txReq");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
}
return reader.afterMessageRead(GridH2QueryRequest.class);
@@ -615,7 +684,7 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable {
/** {@inheritDoc} */
@Override public byte fieldsCount() {
- return 12;
+ return 14;
}
/** {@inheritDoc} */
[45/45] ignite git commit: Merge branch 'master' of
https://git-wip-us.apache.org/repos/asf/ignite into ignite-9273
Posted by ag...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/ignite into ignite-9273
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/e96c06f3
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/e96c06f3
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/e96c06f3
Branch: refs/heads/ignite-9273
Commit: e96c06f322cbb542ee9e675f2428fb2682b481ed
Parents: 1040589 3307a8b
Author: Alexey Goncharuk <al...@gmail.com>
Authored: Thu Aug 30 15:22:40 2018 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Thu Aug 30 15:22:40 2018 +0300
----------------------------------------------------------------------
.../LogisticRegressionSGDTrainerExample.java | 2 +-
.../benchmarks/jmh/tree/BPlusTreeBenchmark.java | 2 +-
.../internal/jdbc2/JdbcStreamingSelfTest.java | 3 +-
.../jdbc/suite/IgniteJdbcDriverTestSuite.java | 14 +
.../jdbc/thin/JdbcThinAbstractSelfTest.java | 4 +-
.../JdbcThinConnectionMvccEnabledSelfTest.java | 378 ++
.../jdbc/thin/JdbcThinConnectionSelfTest.java | 188 +-
.../thin/JdbcThinStreamingAbstractSelfTest.java | 19 +-
.../JdbcThinStreamingNotOrderedSelfTest.java | 2 +-
.../thin/JdbcThinStreamingOrderedSelfTest.java | 2 +-
...ThinTransactionsAbstractComplexSelfTest.java | 1058 +++++
...sactionsClientAutoCommitComplexSelfTest.java | 33 +
...ctionsClientNoAutoCommitComplexSelfTest.java | 33 +
.../jdbc/thin/JdbcThinTransactionsSelfTest.java | 447 +++
...sactionsServerAutoCommitComplexSelfTest.java | 33 +
...ctionsServerNoAutoCommitComplexSelfTest.java | 33 +
.../ignite/codegen/MessageCodeGenerator.java | 6 +-
.../apache/ignite/IgniteSystemProperties.java | 11 +
.../configuration/CacheConfiguration.java | 1 +
.../configuration/DataStorageConfiguration.java | 42 +
.../configuration/IgniteConfiguration.java | 84 +-
.../apache/ignite/internal/GridComponent.java | 5 +-
.../ignite/internal/GridKernalContext.java | 6 +
.../ignite/internal/GridKernalContextImpl.java | 14 +-
.../org/apache/ignite/internal/GridTopic.java | 5 +-
.../apache/ignite/internal/IgniteKernal.java | 22 +-
.../ignite/internal/IgniteNodeAttributes.java | 3 +
.../org/apache/ignite/internal/IgnitionEx.java | 8 +-
.../jdbc/thin/ConnectionProperties.java | 10 +
.../jdbc/thin/ConnectionPropertiesImpl.java | 37 +-
.../internal/jdbc/thin/JdbcThinConnection.java | 33 +-
.../jdbc/thin/JdbcThinDatabaseMetadata.java | 9 +-
.../internal/jdbc/thin/JdbcThinStatement.java | 7 +-
.../internal/jdbc/thin/JdbcThinTcpIo.java | 10 +-
.../managers/communication/GridIoManager.java | 16 +
.../communication/GridIoMessageFactory.java | 134 +-
.../internal/managers/discovery/DiscoCache.java | 17 +-
.../discovery/GridDiscoveryManager.java | 21 +
.../pagemem/store/IgnitePageStoreManager.java | 13 +
.../pagemem/wal/IgniteWriteAheadLogManager.java | 5 +
.../internal/pagemem/wal/record/WALRecord.java | 11 +-
.../delta/DataPageMvccMarkUpdatedRecord.java | 103 +
.../DataPageMvccUpdateNewTxStateHintRecord.java | 79 +
.../DataPageMvccUpdateTxStateHintRecord.java | 79 +
.../processors/affinity/AffinityAssignment.java | 9 +-
.../affinity/GridAffinityAssignment.java | 15 +-
.../affinity/GridAffinityAssignmentCache.java | 21 +-
.../affinity/GridAffinityProcessor.java | 2 +-
.../processors/affinity/GridAffinityUtils.java | 2 +-
.../affinity/HistoryAffinityAssignment.java | 12 +-
.../cache/CacheAffinitySharedManager.java | 33 +-
.../cache/CacheEntryInfoCollection.java | 12 +
.../processors/cache/CacheGroupContext.java | 34 +
.../cache/CacheInvokeDirectResult.java | 21 +-
.../processors/cache/CacheOperationContext.java | 2 +-
.../processors/cache/ClusterCachesInfo.java | 17 +-
.../processors/cache/ExchangeContext.java | 43 +-
.../cache/GatewayProtectedCacheProxy.java | 3 +
.../processors/cache/GridCacheAdapter.java | 65 +-
.../cache/GridCacheAffinityManager.java | 5 +
.../processors/cache/GridCacheContext.java | 7 +
.../processors/cache/GridCacheEntryEx.java | 135 +-
.../processors/cache/GridCacheEntryInfo.java | 6 +-
.../processors/cache/GridCacheMapEntry.java | 1149 +++++-
.../cache/GridCacheMvccEntryInfo.java | 257 ++
.../processors/cache/GridCacheOperation.java | 2 +-
.../GridCachePartitionExchangeManager.java | 76 +-
.../processors/cache/GridCacheProcessor.java | 28 +-
.../processors/cache/GridCacheReturn.java | 17 +-
.../cache/GridCacheSharedContext.java | 11 +-
.../cache/GridCacheUpdateTxResult.java | 98 +-
.../processors/cache/GridCacheUtils.java | 113 +-
.../cache/IgniteCacheOffheapManager.java | 481 ++-
.../cache/IgniteCacheOffheapManagerImpl.java | 1679 +++++++-
.../processors/cache/IgniteCacheProxyImpl.java | 4 +
.../distributed/GridCacheTxFinishSync.java | 2 +-
.../distributed/GridCacheTxRecoveryFuture.java | 11 +
.../distributed/GridDistributedTxMapping.java | 17 +
.../GridDistributedTxPrepareRequest.java | 22 +
.../GridDistributedTxRemoteAdapter.java | 75 +-
.../distributed/dht/CompoundLockFuture.java | 112 +
.../cache/distributed/dht/DhtLockFuture.java | 30 +
.../cache/distributed/dht/ExceptionAware.java | 30 +
.../dht/GridClientPartitionTopology.java | 7 +
.../distributed/dht/GridDhtCacheAdapter.java | 62 +-
.../distributed/dht/GridDhtCacheEntry.java | 10 +-
.../cache/distributed/dht/GridDhtGetFuture.java | 15 +-
.../distributed/dht/GridDhtGetSingleFuture.java | 15 +-
.../distributed/dht/GridDhtLocalPartition.java | 23 +
.../distributed/dht/GridDhtLockFuture.java | 15 +-
.../dht/GridDhtPartitionTopology.java | 4 +
.../dht/GridDhtPartitionTopologyImpl.java | 11 +
.../dht/GridDhtPartitionsStateValidator.java | 11 +-
.../dht/GridDhtPartitionsUpdateCountersMap.java | 119 +
.../dht/GridDhtTransactionalCacheAdapter.java | 465 ++-
.../dht/GridDhtTxAbstractEnlistFuture.java | 1139 ++++++
.../distributed/dht/GridDhtTxFinishFuture.java | 111 +-
.../distributed/dht/GridDhtTxFinishRequest.java | 77 +-
.../cache/distributed/dht/GridDhtTxLocal.java | 11 +-
.../distributed/dht/GridDhtTxLocalAdapter.java | 45 +-
.../distributed/dht/GridDhtTxPrepareFuture.java | 106 +-
.../dht/GridDhtTxPrepareRequest.java | 67 +-
.../dht/GridDhtTxQueryEnlistFuture.java | 140 +
.../dht/GridDhtTxQueryEnlistRequest.java | 404 ++
.../dht/GridDhtTxQueryEnlistResponse.java | 205 +
.../dht/GridDhtTxQueryFirstEnlistRequest.java | 370 ++
.../dht/GridDhtTxQueryResultsEnlistFuture.java | 130 +
.../cache/distributed/dht/GridDhtTxRemote.java | 144 +
.../dht/GridPartitionedGetFuture.java | 90 +-
.../dht/GridPartitionedSingleGetFuture.java | 22 +-
.../dht/NearTxQueryEnlistResultHandler.java | 169 +
.../dht/atomic/GridDhtAtomicCache.java | 25 +-
.../dht/colocated/GridDhtColocatedCache.java | 30 +-
.../dht/preloader/GridDhtForceKeysFuture.java | 7 +
.../dht/preloader/GridDhtPartitionDemander.java | 7 +
.../dht/preloader/GridDhtPartitionSupplier.java | 38 +-
.../GridDhtPartitionsExchangeFuture.java | 48 +-
.../GridDhtPartitionsSingleMessage.java | 76 +-
.../near/AckCoordinatorOnRollback.java | 54 +
.../distributed/near/GridNearGetFuture.java | 18 +-
.../distributed/near/GridNearGetRequest.java | 49 +-
...arOptimisticSerializableTxPrepareFuture.java | 75 +-
.../near/GridNearOptimisticTxPrepareFuture.java | 68 +-
...ridNearOptimisticTxPrepareFutureAdapter.java | 133 +-
.../GridNearPessimisticTxPrepareFuture.java | 194 +-
.../near/GridNearSingleGetRequest.java | 41 +-
.../near/GridNearTxAbstractEnlistFuture.java | 499 +++
.../near/GridNearTxFastFinishFuture.java | 13 +-
.../near/GridNearTxFinishAndAckFuture.java | 144 +
.../near/GridNearTxFinishFuture.java | 66 +-
.../near/GridNearTxFinishRequest.java | 30 +-
.../cache/distributed/near/GridNearTxLocal.java | 407 +-
.../near/GridNearTxPrepareFutureAdapter.java | 12 +-
.../near/GridNearTxPrepareRequest.java | 17 +
.../near/GridNearTxPrepareResponse.java | 50 +-
.../near/GridNearTxQueryEnlistFuture.java | 382 ++
.../near/GridNearTxQueryEnlistRequest.java | 616 +++
.../near/GridNearTxQueryEnlistResponse.java | 298 ++
.../GridNearTxQueryResultsEnlistFuture.java | 670 ++++
.../GridNearTxQueryResultsEnlistRequest.java | 578 +++
.../GridNearTxQueryResultsEnlistResponse.java | 202 +
.../distributed/near/GridNearTxRemote.java | 9 +-
.../near/GridNearTxSelectForUpdateFuture.java | 462 +++
.../distributed/near/NearTxFinishFuture.java | 21 +-
.../near/TxTopologyVersionFuture.java | 155 +
.../processors/cache/dr/GridCacheDrManager.java | 31 +
.../cache/dr/GridOsCacheDrManager.java | 13 +
.../local/atomic/GridLocalAtomicCache.java | 15 +-
.../processors/cache/mvcc/MvccCoordinator.java | 104 +
.../cache/mvcc/MvccDiscoveryData.java | 52 +
.../cache/mvcc/MvccEmptyLongList.java | 53 +
.../processors/cache/mvcc/MvccFuture.java | 66 +
.../processors/cache/mvcc/MvccLongList.java | 29 +
.../mvcc/MvccPreviousCoordinatorQueries.java | 222 ++
.../processors/cache/mvcc/MvccProcessor.java | 250 ++
.../cache/mvcc/MvccProcessorImpl.java | 2187 +++++++++++
.../processors/cache/mvcc/MvccQueryTracker.java | 102 +
.../cache/mvcc/MvccQueryTrackerImpl.java | 348 ++
.../processors/cache/mvcc/MvccSnapshot.java | 48 +
.../cache/mvcc/MvccSnapshotFuture.java | 43 +
.../mvcc/MvccSnapshotResponseListener.java | 35 +
.../cache/mvcc/MvccSnapshotWithoutTxs.java | 204 +
.../cache/mvcc/MvccUpdateVersionAware.java | 76 +
.../processors/cache/mvcc/MvccUtils.java | 882 +++++
.../processors/cache/mvcc/MvccVersion.java | 48 +
.../processors/cache/mvcc/MvccVersionAware.java | 76 +
.../processors/cache/mvcc/MvccVersionImpl.java | 193 +
.../cache/mvcc/NoOpMvccProcessor.java | 215 +
.../cache/mvcc/StaticMvccQueryTracker.java | 96 +
.../processors/cache/mvcc/VacuumMetrics.java | 102 +
.../cache/mvcc/VacuumMetricsReducer.java | 49 +
.../processors/cache/mvcc/VacuumTask.java | 63 +
.../cache/mvcc/msg/MvccAckRequestQueryCntr.java | 130 +
.../cache/mvcc/msg/MvccAckRequestQueryId.java | 130 +
.../cache/mvcc/msg/MvccAckRequestTx.java | 210 +
.../mvcc/msg/MvccAckRequestTxAndQueryCntr.java | 123 +
.../mvcc/msg/MvccAckRequestTxAndQueryId.java | 122 +
.../mvcc/msg/MvccActiveQueriesMessage.java | 132 +
.../cache/mvcc/msg/MvccFutureResponse.java | 130 +
.../processors/cache/mvcc/msg/MvccMessage.java | 35 +
.../mvcc/msg/MvccQuerySnapshotRequest.java | 130 +
.../cache/mvcc/msg/MvccSnapshotResponse.java | 320 ++
.../cache/mvcc/msg/MvccTxSnapshotRequest.java | 128 +
.../cache/mvcc/msg/MvccWaitTxsRequest.java | 159 +
.../processors/cache/mvcc/txlog/TxKey.java | 76 +
.../processors/cache/mvcc/txlog/TxLog.java | 584 +++
.../processors/cache/mvcc/txlog/TxLogIO.java | 73 +
.../cache/mvcc/txlog/TxLogInnerIO.java | 105 +
.../cache/mvcc/txlog/TxLogLeafIO.java | 105 +
.../processors/cache/mvcc/txlog/TxLogTree.java | 62 +
.../processors/cache/mvcc/txlog/TxRow.java | 44 +
.../processors/cache/mvcc/txlog/TxState.java | 37 +
.../cache/persistence/CacheDataRow.java | 3 +-
.../cache/persistence/CacheDataRowAdapter.java | 114 +-
.../cache/persistence/CacheSearchRow.java | 3 +-
.../persistence/DatabaseLifecycleListener.java | 54 +
.../GridCacheDatabaseSharedManager.java | 55 +-
.../persistence/GridCacheOffheapManager.java | 304 +-
.../IgniteCacheDatabaseSharedManager.java | 66 +-
.../cache/persistence/IndexStorageImpl.java | 2 +-
.../processors/cache/persistence/RowStore.java | 28 +-
.../processors/cache/persistence/Storable.java | 14 +
.../checkpoint/CheckpointHistory.java | 102 +-
.../persistence/file/FilePageStoreManager.java | 27 +-
.../persistence/freelist/AbstractFreeList.java | 20 +-
.../cache/persistence/freelist/FreeList.java | 12 +
.../metastorage/MetastorageDataRow.java | 11 +
.../metastorage/MetastorageTree.java | 2 +-
.../cache/persistence/tree/BPlusTree.java | 1219 +++++-
.../persistence/tree/io/AbstractDataPageIO.java | 35 +-
.../cache/persistence/tree/io/DataPageIO.java | 484 ++-
.../cache/persistence/tree/io/IOVersions.java | 7 +
.../cache/persistence/tree/io/PageIO.java | 133 +-
.../persistence/tree/io/SimpleDataPageIO.java | 6 -
.../cache/persistence/wal/FileDescriptor.java | 136 +
.../wal/FileWriteAheadLogManager.java | 185 +-
.../wal/FsyncModeFileWriteAheadLogManager.java | 214 +-
.../SingleSegmentLogicalRecordsIterator.java | 4 +-
.../wal/reader/IgniteWalIteratorFactory.java | 2 +-
.../wal/reader/StandaloneGridKernalContext.java | 6 +
.../reader/StandaloneWalRecordsIterator.java | 2 +-
.../persistence/wal/record/RecordTypes.java | 3 +
.../wal/serializer/RecordDataV1Serializer.java | 82 +
.../query/GridCacheDistributedQueryManager.java | 14 +-
.../cache/query/GridCacheQueryAdapter.java | 132 +-
.../cache/query/GridCacheQueryManager.java | 18 +-
.../cache/query/GridCacheQueryRequest.java | 59 +-
.../cache/query/IgniteQueryErrorCode.java | 20 +
.../cache/query/SqlFieldsQueryEx.java | 39 +
.../continuous/CacheContinuousQueryManager.java | 10 +-
.../cache/transactions/IgniteInternalTx.java | 16 +
.../transactions/IgniteTransactionsImpl.java | 4 +-
.../cache/transactions/IgniteTxAdapter.java | 149 +-
.../cache/transactions/IgniteTxHandler.java | 26 +-
.../IgniteTxImplicitSingleStateImpl.java | 16 +-
.../transactions/IgniteTxLocalAdapter.java | 311 +-
.../cache/transactions/IgniteTxManager.java | 13 +
.../cache/transactions/IgniteTxRemoteEx.java | 12 +
.../IgniteTxRemoteSingleStateImpl.java | 5 +
.../IgniteTxRemoteStateAdapter.java | 21 +-
.../transactions/IgniteTxRemoteStateImpl.java | 10 +
.../cache/transactions/IgniteTxState.java | 16 +-
.../cache/transactions/IgniteTxStateImpl.java | 26 +-
.../cache/tree/AbstractDataInnerIO.java | 112 +-
.../cache/tree/AbstractDataLeafIO.java | 125 +-
.../cache/tree/CacheDataRowStore.java | 35 +-
.../processors/cache/tree/CacheDataTree.java | 82 +-
.../cache/tree/CacheIdAwareDataInnerIO.java | 8 +-
.../cache/tree/CacheIdAwareDataLeafIO.java | 8 +-
.../processors/cache/tree/DataInnerIO.java | 11 -
.../processors/cache/tree/DataLeafIO.java | 11 -
.../internal/processors/cache/tree/DataRow.java | 28 +-
.../cache/tree/PendingEntriesTree.java | 2 +-
.../processors/cache/tree/PendingRow.java | 3 +-
.../processors/cache/tree/RowLinkIO.java | 67 +-
.../processors/cache/tree/SearchRow.java | 31 +
.../mvcc/data/MvccCacheIdAwareDataInnerIO.java | 68 +
.../mvcc/data/MvccCacheIdAwareDataLeafIO.java | 89 +
.../cache/tree/mvcc/data/MvccDataInnerIO.java | 71 +
.../cache/tree/mvcc/data/MvccDataLeafIO.java | 89 +
.../cache/tree/mvcc/data/MvccDataRow.java | 263 ++
.../cache/tree/mvcc/data/MvccUpdateDataRow.java | 485 +++
.../tree/mvcc/data/MvccUpdateDataRowNative.java | 240 ++
.../cache/tree/mvcc/data/MvccUpdateResult.java | 43 +
.../cache/tree/mvcc/data/ResultType.java | 34 +
.../mvcc/search/MvccFirstRowTreeClosure.java | 76 +
.../mvcc/search/MvccLinkAwareSearchRow.java | 48 +
.../tree/mvcc/search/MvccMaxSearchRow.java | 55 +
.../tree/mvcc/search/MvccMinSearchRow.java | 55 +
.../cache/tree/mvcc/search/MvccSearchRow.java | 70 +
.../tree/mvcc/search/MvccSnapshotSearchRow.java | 113 +
.../cache/tree/mvcc/search/MvccTreeClosure.java | 29 +
.../internal/processors/odbc/SqlStateCode.java | 3 +
.../odbc/jdbc/JdbcBatchExecuteRequest.java | 27 +-
.../odbc/jdbc/JdbcConnectionContext.java | 47 +-
.../jdbc/JdbcOrderedBatchExecuteRequest.java | 5 +-
.../odbc/jdbc/JdbcQueryExecuteRequest.java | 27 +-
.../odbc/jdbc/JdbcRequestHandler.java | 65 +-
.../odbc/jdbc/JdbcRequestHandlerWorker.java | 131 +
.../odbc/odbc/OdbcConnectionContext.java | 21 +-
.../processors/odbc/odbc/OdbcMessageParser.java | 16 +-
.../odbc/odbc/OdbcQueryExecuteBatchRequest.java | 15 +-
.../odbc/odbc/OdbcQueryExecuteRequest.java | 14 +-
.../odbc/odbc/OdbcRequestHandler.java | 73 +-
.../odbc/odbc/OdbcRequestHandlerWorker.java | 131 +
.../utils/PlatformConfigurationUtils.java | 2 +
.../processors/query/EnlistOperation.java | 91 +
.../processors/query/GridQueryIndexing.java | 41 +-
.../processors/query/GridQueryProcessor.java | 52 +-
.../internal/processors/query/NestedTxMode.java | 60 +
.../processors/query/UpdateSourceIterator.java | 89 +
.../messages/GridQueryNextPageResponse.java | 32 +-
.../GridInternalSubscriptionProcessor.java | 18 +
.../apache/ignite/internal/sql/SqlKeyword.java | 20 +
.../apache/ignite/internal/sql/SqlParser.java | 85 +-
.../ignite/internal/sql/SqlParserUtils.java | 14 +
.../sql/command/SqlBeginTransactionCommand.java | 46 +
.../command/SqlCommitTransactionCommand.java | 46 +
.../command/SqlRollbackTransactionCommand.java | 46 +
.../IgniteTxMvccVersionCheckedException.java | 47 +
.../util/future/GridCompoundFuture.java | 4 +-
.../internal/TestRecordingCommunicationSpi.java | 26 +-
.../processors/cache/GridCacheTestEntryEx.java | 73 +-
...IgniteClientCacheInitializationFailTest.java | 20 +-
...eAbstractDataStructuresFailoverSelfTest.java | 16 +-
...eplicatedDataStructuresFailoverSelfTest.java | 8 +-
.../IgniteCacheFailedUpdateResponseTest.java | 2 -
...vccAbstractBasicCoordinatorFailoverTest.java | 685 ++++
...acheMvccAbstractCoordinatorFailoverTest.java | 148 +
.../mvcc/CacheMvccAbstractFeatureTest.java | 294 ++
.../cache/mvcc/CacheMvccAbstractTest.java | 2291 +++++++++++
.../cache/mvcc/CacheMvccClusterRestartTest.java | 178 +
.../CacheMvccConfigurationValidationTest.java | 191 +
...ccIteratorWithConcurrentTransactionTest.java | 64 +
...calEntriesWithConcurrentTransactionTest.java | 65 +
.../mvcc/CacheMvccOperationChecksTest.java | 201 +
...eMvccPartitionedCoordinatorFailoverTest.java | 144 +
.../cache/mvcc/CacheMvccProcessorTest.java | 97 +
...heMvccReplicatedCoordinatorFailoverTest.java | 30 +
...cScanQueryWithConcurrentTransactionTest.java | 76 +
...heMvccSizeWithConcurrentTransactionTest.java | 52 +
.../cache/mvcc/CacheMvccTransactionsTest.java | 3674 ++++++++++++++++++
.../cache/mvcc/CacheMvccVacuumTest.java | 158 +
.../MemoryPolicyInitializationTest.java | 12 +-
.../db/IgnitePdsReserveWalSegmentsTest.java | 188 +
.../db/IgnitePdsUnusedWalSegmentsTest.java | 198 -
...IsReachedDuringIterationOverArchiveTest.java | 2 +-
.../persistence/db/wal/WalCompactionTest.java | 8 +-
.../db/wal/WalDeletionArchiveAbstractTest.java | 277 ++
.../db/wal/WalDeletionArchiveFsyncTest.java | 31 +
.../db/wal/WalDeletionArchiveLogOnlyTest.java | 31 +
.../db/wal/WalRecoveryTxLogicalRecordsTest.java | 44 -
.../db/wal/reader/MockWalIteratorFactory.java | 1 +
.../pagemem/NoOpPageStoreManager.java | 7 +
.../persistence/pagemem/NoOpWALManager.java | 6 +
.../processors/database/BPlusTreeSelfTest.java | 377 +-
.../database/CacheFreeListImplSelfTest.java | 55 +
.../DataStreamProcessorMvccSeflTest.java | 68 +
.../DataStreamProcessorSelfTest.java | 24 +-
.../SqlParserTransactionalKeywordsSelfTest.java | 96 +
.../testframework/junits/GridAbstractTest.java | 8 +
.../testsuites/IgniteCacheMvccTestSuite.java | 61 +
.../ignite/testsuites/IgnitePdsTestSuite2.java | 14 +-
.../development/utils/IgniteWalConverter.java | 5 +-
.../query/h2/opt/GridH2SpatialIndex.java | 11 +-
.../cache/query/GridCacheTwoStepQuery.java | 36 +
.../query/h2/DhtResultSetEnlistFuture.java | 63 +
.../query/h2/DmlStatementsProcessor.java | 352 +-
.../query/h2/H2ConnectionWrapper.java | 52 +-
.../processors/query/h2/H2FieldsIterator.java | 29 +-
.../processors/query/h2/H2KeyValueIterator.java | 2 +-
.../query/h2/H2ResultSetIterator.java | 7 +-
.../processors/query/h2/H2StatementCache.java | 78 +-
.../processors/query/h2/IgniteH2Indexing.java | 983 +++--
.../query/h2/NearResultSetEnlistFuture.java | 62 +
.../query/h2/PreparedStatementEx.java | 45 +
.../query/h2/PreparedStatementExImpl.java | 648 +++
.../query/h2/RebuildIndexFromHashClosure.java | 47 +
.../query/h2/ResultSetEnlistFuture.java | 136 +
.../query/h2/ThreadLocalObjectPool.java | 103 +
.../query/h2/database/H2PkHashIndex.java | 108 +-
.../query/h2/database/H2RowFactory.java | 25 +-
.../processors/query/h2/database/H2Tree.java | 112 +-
.../query/h2/database/H2TreeFilterClosure.java | 109 +
.../query/h2/database/H2TreeIndex.java | 160 +-
.../h2/database/io/AbstractH2ExtrasInnerIO.java | 164 +
.../h2/database/io/AbstractH2ExtrasLeafIO.java | 161 +
.../query/h2/database/io/AbstractH2InnerIO.java | 74 +
.../query/h2/database/io/AbstractH2LeafIO.java | 76 +
.../query/h2/database/io/H2ExtrasInnerIO.java | 111 +-
.../query/h2/database/io/H2ExtrasLeafIO.java | 107 +-
.../query/h2/database/io/H2IOUtils.java | 94 +
.../query/h2/database/io/H2InnerIO.java | 41 +-
.../query/h2/database/io/H2LeafIO.java | 41 +-
.../h2/database/io/H2MvccExtrasInnerIO.java | 55 +
.../h2/database/io/H2MvccExtrasLeafIO.java | 54 +
.../query/h2/database/io/H2MvccInnerIO.java | 58 +
.../query/h2/database/io/H2MvccLeafIO.java | 58 +
.../query/h2/database/io/H2RowLinkIO.java | 34 +
.../query/h2/ddl/DdlStatementsProcessor.java | 27 +
.../processors/query/h2/dml/DmlAstUtils.java | 12 +-
.../processors/query/h2/dml/FastUpdate.java | 17 +
.../processors/query/h2/dml/UpdatePlan.java | 233 ++
.../query/h2/dml/UpdatePlanBuilder.java | 118 +-
.../query/h2/opt/GridH2IndexBase.java | 36 +-
.../query/h2/opt/GridH2KeyRowOnheap.java | 11 +
.../query/h2/opt/GridH2KeyValueRowOnheap.java | 21 +-
.../query/h2/opt/GridH2MetaTable.java | 5 +
.../query/h2/opt/GridH2PlainRowFactory.java | 17 +-
.../query/h2/opt/GridH2QueryContext.java | 27 +-
.../processors/query/h2/opt/GridH2Row.java | 50 +-
.../query/h2/opt/GridH2RowDescriptor.java | 9 +-
.../query/h2/opt/GridH2SearchRow.java | 32 +
.../query/h2/opt/GridH2SearchRowAdapter.java | 27 +-
.../processors/query/h2/opt/GridH2Table.java | 8 +-
.../query/h2/sql/GridSqlQueryParser.java | 164 +-
.../query/h2/sql/GridSqlQuerySplitter.java | 18 +-
.../processors/query/h2/sql/GridSqlSelect.java | 20 +
.../query/h2/twostep/GridMapQueryExecutor.java | 334 +-
.../h2/twostep/GridMergeIndexIterator.java | 17 +-
.../h2/twostep/GridReduceQueryExecutor.java | 183 +-
.../query/h2/twostep/GridResultPage.java | 22 +-
.../query/h2/twostep/MapQueryResults.java | 18 +-
.../query/h2/twostep/ReduceQueryRun.java | 35 +-
.../h2/twostep/msg/GridH2QueryRequest.java | 115 +-
.../msg/GridH2SelectForUpdateTxDetails.java | 299 ++
.../twostep/msg/GridH2ValueMessageFactory.java | 12 +-
.../cache/DdlTransactionSelfTest.java | 335 ++
.../IgniteCacheQueryH2IndexingLeakTest.java | 10 +-
.../cache/index/AbstractSchemaSelfTest.java | 102 +-
...ynamicColumnsAbstractConcurrentSelfTest.java | 13 +-
.../index/DynamicIndexAbstractSelfTest.java | 7 +
.../cache/index/H2ConnectionLeaksSelfTest.java | 8 +-
.../index/H2DynamicIndexAbstractSelfTest.java | 49 -
.../cache/index/H2DynamicTableSelfTest.java | 16 +-
...sactionsComandsWithMvccDisabledSelfTest.java | 83 +
.../cache/index/SqlTransactionsSelfTest.java | 421 ++
...eMvccAbstractSqlCoordinatorFailoverTest.java | 138 +
.../mvcc/CacheMvccBackupsAbstractTest.java | 808 ++++
.../cache/mvcc/CacheMvccBulkLoadTest.java | 123 +
.../cache/mvcc/CacheMvccDmlSimpleTest.java | 200 +
...eratorWithConcurrentJdbcTransactionTest.java | 28 +
...ntriesWithConcurrentJdbcTransactionTest.java | 29 +
.../mvcc/CacheMvccPartitionedBackupsTest.java | 30 +
...MvccPartitionedSelectForUpdateQueryTest.java | 44 +
...ccPartitionedSqlCoordinatorFailoverTest.java | 143 +
.../CacheMvccPartitionedSqlQueriesTest.java | 30 +
.../CacheMvccPartitionedSqlTxQueriesTest.java | 30 +
...cPartitionedSqlTxQueriesWithReducerTest.java | 30 +
.../mvcc/CacheMvccReplicatedBackupsTest.java | 30 +
...eMvccReplicatedSelectForUpdateQueryTest.java | 30 +
...vccReplicatedSqlCoordinatorFailoverTest.java | 30 +
.../mvcc/CacheMvccReplicatedSqlQueriesTest.java | 30 +
.../CacheMvccReplicatedSqlTxQueriesTest.java | 242 ++
...ccReplicatedSqlTxQueriesWithReducerTest.java | 30 +
...nQueryWithConcurrentJdbcTransactionTest.java | 29 +
...cheMvccSelectForUpdateQueryAbstractTest.java | 374 ++
...ccSizeWithConcurrentJdbcTransactionTest.java | 28 +
.../mvcc/CacheMvccSqlQueriesAbstractTest.java | 1611 ++++++++
.../mvcc/CacheMvccSqlTxQueriesAbstractTest.java | 1832 +++++++++
...MvccSqlTxQueriesWithReducerAbstractTest.java | 829 ++++
.../mvcc/CacheMvccStreamingInsertTest.java | 122 +
.../query/h2/GridIndexRebuildSelfTest.java | 350 ++
.../h2/GridIndexingSpiAbstractSelfTest.java | 4 +-
.../query/h2/H2StatementCacheSelfTest.java | 83 +
.../query/h2/PreparedStatementExSelfTest.java | 61 +
.../query/h2/ThreadLocalObjectPoolSelfTest.java | 113 +
.../query/h2/sql/GridQueryParsingTest.java | 1 +
.../testsuites/IgniteCacheMvccSqlTestSuite.java | 73 +
.../IgniteCacheQuerySelfTestSuite.java | 20 +
.../processors/cache/mvcc/mvcc_person.csv | 2 +
.../cache/mvcc/mvcc_person_broken.csv | 2 +
.../processors/cache/jta/CacheJtaManager.java | 1 +
modules/platforms/cpp/odbc-test/Makefile.am | 4 +-
.../odbc-test/config/queries-transaction-32.xml | 50 +
.../odbc-test/config/queries-transaction.xml | 32 +
.../cpp/odbc-test/project/vs/odbc-test.vcxproj | 4 +
.../project/vs/odbc-test.vcxproj.filters | 12 +
.../cpp/odbc-test/src/sql_get_info_test.cpp | 8 +-
.../cpp/odbc-test/src/transaction_test.cpp | 738 ++++
modules/platforms/cpp/odbc/Makefile.am | 1 +
.../include/ignite/odbc/config/configuration.h | 32 +
.../odbc/config/connection_string_parser.h | 3 +
.../cpp/odbc/include/ignite/odbc/connection.h | 21 +-
.../ignite/odbc/diagnostic/diagnosable.h | 7 +
.../odbc/diagnostic/diagnosable_adapter.h | 11 +-
.../cpp/odbc/include/ignite/odbc/environment.h | 15 +
.../cpp/odbc/include/ignite/odbc/message.h | 13 +-
.../odbc/include/ignite/odbc/nested_tx_mode.h | 81 +
.../odbc/include/ignite/odbc/protocol_version.h | 2 +-
.../odbc/system/ui/dsn_configuration_window.h | 8 +
.../src/system/ui/dsn_configuration_window.cpp | 56 +-
.../platforms/cpp/odbc/project/vs/odbc.vcxproj | 2 +
.../cpp/odbc/project/vs/odbc.vcxproj.filters | 6 +
.../cpp/odbc/src/config/configuration.cpp | 29 +-
.../cpp/odbc/src/config/connection_info.cpp | 8 +-
.../src/config/connection_string_parser.cpp | 19 +
modules/platforms/cpp/odbc/src/connection.cpp | 96 +-
.../odbc/src/diagnostic/diagnosable_adapter.cpp | 5 +
modules/platforms/cpp/odbc/src/dsn_config.cpp | 5 +
modules/platforms/cpp/odbc/src/environment.cpp | 53 +-
modules/platforms/cpp/odbc/src/message.cpp | 18 +-
.../platforms/cpp/odbc/src/nested_tx_mode.cpp | 80 +
modules/platforms/cpp/odbc/src/odbc.cpp | 2 +
.../cpp/odbc/src/query/batch_query.cpp | 4 +-
.../platforms/cpp/odbc/src/query/data_query.cpp | 2 +-
.../DataStorageConfigurationParityTest.cs | 3 +-
.../Configuration/DataStorageConfiguration.cs | 14 +
.../IgniteConfigurationSection.xsd | 5 +
.../web-console/backend/app/browsersHandler.js | 10 +-
.../config/mvcc/benchmark-mvcc-messages.sh | 98 +
.../config/mvcc/benchmark-mvcc-processor.sh | 95 +
.../mvcc/benchmark-mvcc-updates-contention.sh | 96 +
.../mvcc/benchmark-thin-native.properties | 124 +
...benchmark-jdbc-thin-inmemory-mvcc.properties | 105 +
.../yardstick/IgniteBenchmarkArguments.java | 38 +
.../org/apache/ignite/yardstick/IgniteNode.java | 2 +
.../yardstick/jdbc/AbstractJdbcBenchmark.java | 2 +-
.../yardstick/jdbc/AbstractNativeBenchmark.java | 2 +-
.../apache/ignite/yardstick/jdbc/JdbcUtils.java | 11 +-
.../jdbc/NativeJavaApiPutRemoveBenchmark.java | 52 +
.../mvcc/AbstractDistributedMvccBenchmark.java | 101 +
.../jdbc/mvcc/MvccProcessorBenchmark.java | 61 +
.../mvcc/MvccUpdateContentionBenchmark.java | 77 +
.../upload/AbstractNativeBenchmark.java | 14 +-
.../upload/AbstractUploadBenchmark.java | 9 +-
.../yardstick/upload/model/QueryFactory.java | 30 +-
507 files changed, 57445 insertions(+), 3213 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/e96c06f3/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/e96c06f3/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/e96c06f3/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
----------------------------------------------------------------------
[18/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccConfigurationValidationTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccConfigurationValidationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccConfigurationValidationTest.java
new file mode 100644
index 0000000..b0a2ec8
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccConfigurationValidationTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.mvcc;
+
+import java.util.concurrent.Callable;
+import javax.cache.CacheException;
+import javax.cache.configuration.Factory;
+import javax.cache.expiry.CreatedExpiryPolicy;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.CacheInterceptorAdapter;
+import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+
+/**
+ *
+ */
+public class CacheMvccConfigurationValidationTest extends GridCommonAbstractTest {
+ /** */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+ cfg.setMvccEnabled(true);
+
+ ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+
+ super.afterTest();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMvccModeMismatchForGroup1() throws Exception {
+ final Ignite node = startGrid(0);
+
+ node.createCache(new CacheConfiguration("cache1").setGroupName("grp1").setAtomicityMode(ATOMIC));
+
+ GridTestUtils.assertThrows(log, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ node.createCache(new CacheConfiguration("cache2").setGroupName("grp1").setAtomicityMode(TRANSACTIONAL));
+
+ return null;
+ }
+ }, CacheException.class, null);
+
+ node.createCache(new CacheConfiguration("cache2").setGroupName("grp1").setAtomicityMode(ATOMIC));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMvccModeMismatchForGroup2() throws Exception {
+ final Ignite node = startGrid(0);
+
+ node.createCache(new CacheConfiguration("cache1").setGroupName("grp1").setAtomicityMode(TRANSACTIONAL));
+
+ GridTestUtils.assertThrows(log, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ node.createCache(new CacheConfiguration("cache2").setGroupName("grp1").setAtomicityMode(ATOMIC));
+
+ return null;
+ }
+ }, CacheException.class, null);
+
+ node.createCache(new CacheConfiguration("cache2").setGroupName("grp1").setAtomicityMode(TRANSACTIONAL));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxCacheWithCacheStore() throws Exception {
+ checkTransactionalModeConflict("cacheStoreFactory", new TestFactory(),
+ "Transactional cache may not have a third party cache store when MVCC is enabled.");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxCacheWithExpiryPolicy() throws Exception {
+ checkTransactionalModeConflict("expiryPolicyFactory0", CreatedExpiryPolicy.factoryOf(Duration.FIVE_MINUTES),
+ "Transactional cache may not have expiry policy when MVCC is enabled.");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxCacheWithInterceptor() throws Exception {
+ checkTransactionalModeConflict("interceptor", new CacheInterceptorAdapter(),
+ "Transactional cache may not have an interceptor when MVCC is enabled.");
+ }
+
+ /**
+ * Check that setting specified property conflicts with transactional cache atomicity mode.
+ * @param propName Property name.
+ * @param obj Property value.
+ * @param errMsg Expected error message.
+ * @throws IgniteCheckedException if failed.
+ */
+ @SuppressWarnings("ThrowableNotThrown")
+ private void checkTransactionalModeConflict(String propName, Object obj, String errMsg)
+ throws Exception {
+ final String setterName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
+
+ try (final Ignite node = startGrid(0)) {
+ final CacheConfiguration cfg = new TestConfiguration("cache");
+
+ cfg.setAtomicityMode(TRANSACTIONAL);
+
+ U.invoke(TestConfiguration.class, cfg, setterName, obj);
+
+ GridTestUtils.assertThrows(log, new Callable<Void>() {
+ @SuppressWarnings("unchecked")
+ @Override public Void call() {
+ node.getOrCreateCache(cfg);
+
+ return null;
+ }
+ }, IgniteCheckedException.class, errMsg);
+ }
+ }
+
+ /**
+ * Dummy class to overcome ambiguous method name "setExpiryPolicyFactory".
+ */
+ private final static class TestConfiguration extends CacheConfiguration {
+ /**
+ *
+ */
+ TestConfiguration(String cacheName) {
+ super(cacheName);
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("unused")
+ public void setExpiryPolicyFactory0(Factory<ExpiryPolicy> plcFactory) {
+ super.setExpiryPolicyFactory(plcFactory);
+ }
+ }
+
+ /**
+ *
+ */
+ private static class TestFactory implements Factory<CacheStore> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override public CacheStore create() {
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentTransactionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentTransactionTest.java
new file mode 100644
index 0000000..90c5b6e
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccIteratorWithConcurrentTransactionTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.mvcc;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import javax.cache.Cache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.util.lang.IgniteClosure2X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public class CacheMvccIteratorWithConcurrentTransactionTest extends CacheMvccAbstractFeatureTest {
+ /**
+ * @throws Exception if failed.
+ */
+ public void testScanQuery() throws Exception {
+ doTestConsistency(clo);
+ }
+
+ /** Test closure. */
+ private final IgniteClosure2X<CountDownLatch, CountDownLatch, List<Person>> clo =
+ new IgniteClosure2X<CountDownLatch, CountDownLatch, List<Person>>() {
+ @Override public List<Person> applyx(CountDownLatch startLatch, CountDownLatch endLatch2)
+ throws IgniteCheckedException {
+ Iterator<Cache.Entry<Integer, Person>> it = cache().iterator();
+
+ List<Cache.Entry<Integer, Person>> pres = new ArrayList<>();
+
+ for (int i = 0; i < 50; i++)
+ pres.add(it.next());
+
+ if (startLatch != null)
+ startLatch.countDown();
+
+ while (it.hasNext())
+ pres.add(it.next());
+
+ if (endLatch2 != null)
+ U.await(endLatch2);
+
+ return entriesToPersons(pres);
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentTransactionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentTransactionTest.java
new file mode 100644
index 0000000..f4c9781
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccLocalEntriesWithConcurrentTransactionTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.mvcc;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import javax.cache.Cache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.internal.util.lang.IgniteClosure2X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public class CacheMvccLocalEntriesWithConcurrentTransactionTest extends CacheMvccAbstractFeatureTest {
+ /**
+ * @throws Exception if failed.
+ */
+ public void testLocalEntries() throws Exception {
+ doTestConsistency(clo);
+ }
+
+ /** Test closure. */
+ private final IgniteClosure2X<CountDownLatch, CountDownLatch, List<Person>> clo =
+ new IgniteClosure2X<CountDownLatch, CountDownLatch, List<Person>>() {
+ @Override public List<Person> applyx(CountDownLatch startLatch, CountDownLatch endLatch2)
+ throws IgniteCheckedException {
+ Iterator<Cache.Entry<Integer, Person>> it = cache().localEntries(CachePeekMode.PRIMARY).iterator();
+
+ List<Cache.Entry<Integer, Person>> pres = new ArrayList<>();
+
+ for (int i = 0; i < 10; i++)
+ pres.add(it.next());
+
+ if (startLatch != null)
+ startLatch.countDown();
+
+ while (it.hasNext())
+ pres.add(it.next());
+
+ if (endLatch2 != null)
+ U.await(endLatch2);
+
+ return entriesToPersons(pres);
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccOperationChecksTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccOperationChecksTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccOperationChecksTest.java
new file mode 100644
index 0000000..98a06c7
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccOperationChecksTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.mvcc;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import javax.cache.expiry.EternalExpiryPolicy;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.testframework.GridTestUtils;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/**
+ *
+ */
+public class CacheMvccOperationChecksTest extends CacheMvccAbstractTest {
+ /** Empty Class[]. */
+ private final static Class[] E = new Class[]{};
+
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ super.afterTestsStopped();
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testClearOperationsUnsupported() throws Exception {
+ checkOperationUnsupported("clear", m("Clear"), E);
+
+ checkOperationUnsupported("clearAsync", m("Clear"), E);
+
+ checkOperationUnsupported("clear", m("Clear"), t(Object.class), 1);
+
+ checkOperationUnsupported("clearAsync", m("Clear"), t(Object.class), 1);
+
+ checkOperationUnsupported("clearAll", m("Clear"), t(Set.class), Collections.singleton(1));
+
+ checkOperationUnsupported("clearAllAsync", m("Clear"), t(Set.class),
+ Collections.singleton(1));
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testLoadOperationsUnsupported() throws Exception {
+ checkOperationUnsupported("loadCache", m("Load"), t(IgniteBiPredicate.class, Object[].class),
+ P, new Object[]{ 1 });
+
+ checkOperationUnsupported("loadCacheAsync", m("Load"), t(IgniteBiPredicate.class, Object[].class),
+ P, new Object[]{ 1 });
+
+ checkOperationUnsupported("localLoadCache", m("Load"), t(IgniteBiPredicate.class, Object[].class),
+ P, new Object[]{ 1 });
+
+ checkOperationUnsupported("localLoadCacheAsync", m("Load"), t(IgniteBiPredicate.class, Object[].class),
+ P, new Object[]{ 1 });
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testLockOperationsUnsupported() throws Exception {
+ checkOperationUnsupported("lock", m("Lock"), t(Object.class), 1);
+
+ checkOperationUnsupported("lockAll", m("Lock"), t(Collection.class), Collections.singleton(1));
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testPeekOperationsUnsupported() throws Exception {
+ checkOperationUnsupported("localPeek", m("Peek"), t(Object.class, CachePeekMode[].class), 1,
+ new CachePeekMode[]{CachePeekMode.NEAR});
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testEvictOperationsUnsupported() throws Exception {
+ checkOperationUnsupported("localEvict", m("Evict"), t(Collection.class), Collections.singleton(1));
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testWithExpiryPolicyUnsupported() throws Exception {
+ checkOperationUnsupported("withExpiryPolicy", m("withExpiryPolicy"), t(ExpiryPolicy.class),
+ EternalExpiryPolicy.factoryOf().create());
+ }
+
+ /**
+ * @param opTypeName Operation type name.
+ * @return Typical error message from {@link GridCacheAdapter}.
+ */
+ private static String m(String opTypeName) {
+ return opTypeName + " operations are not supported on transactional caches when MVCC is enabled.";
+ }
+
+ /**
+ * @param types Parameter types.
+ * @return Types array.
+ */
+ private static Class[] t(Class... types) {
+ return types;
+ }
+
+ /**
+ * @param mtdName Method name.
+ * @param errMsg Expected error message.
+ * @param paramTypes Operation param types.
+ * @param args Operation arguments.
+ * @throws Exception if failed.
+ */
+ @SuppressWarnings("ThrowableNotThrown")
+ private void checkOperationUnsupported(String mtdName, String errMsg, Class[] paramTypes,
+ Object... args) throws Exception {
+ final boolean async = mtdName.endsWith("Async");
+
+ try (final Ignite node = startGrid(0)) {
+ final CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>("cache");
+
+ cfg.setCacheMode(cacheMode());
+ cfg.setAtomicityMode(TRANSACTIONAL);
+
+ try (IgniteCache<Integer, String> cache = node.createCache(cfg)) {
+ GridTestUtils.assertThrows(log, new Callable<Void>() {
+ @SuppressWarnings("unchecked")
+ @Override public Void call() throws Exception {
+ try {
+ Object o = U.invoke(null, cache, mtdName, paramTypes, args);
+
+ if (async) {
+ assertTrue(o instanceof IgniteFuture<?>);
+
+ ((IgniteFuture)o).get();
+ }
+ }
+ catch (Exception e) {
+ if (e.getCause() == null)
+ throw e;
+
+ if (e.getCause().getCause() == null)
+ throw e;
+
+ throw (Exception)e.getCause().getCause();
+ }
+
+ return null;
+ }
+ }, UnsupportedOperationException.class, errMsg);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private final static IgniteBiPredicate<Object, Object> P = new IgniteBiPredicate<Object, Object>() {
+ @Override public boolean apply(Object o, Object o2) {
+ return false;
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedCoordinatorFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedCoordinatorFailoverTest.java
new file mode 100644
index 0000000..3ea1c5b
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccPartitionedCoordinatorFailoverTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.GET;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SCAN;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.PUT;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * Coordinator failover test for partitioned caches.
+ */
+public class CacheMvccPartitionedCoordinatorFailoverTest extends CacheMvccAbstractCoordinatorFailoverTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.PARTITIONED;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGet_ClientServer_Backups2_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ accountsTxReadAll(4, 2, 2, DFLT_PARTITION_COUNT,
+ null, true, GET, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxGet_Server_Backups1_CoordinatorFails() throws Exception {
+ accountsTxReadAll(2, 0, 1, DFLT_PARTITION_COUNT,
+ null, true, GET, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_ClientServer_Backups2_CoordinatorFails() throws Exception {
+ accountsTxReadAll(4, 2, 2, DFLT_PARTITION_COUNT,
+ null, true, SCAN, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxScan_Server_Backups1_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ accountsTxReadAll(2, 0, 1, DFLT_PARTITION_COUNT,
+ null, true, SCAN, PUT, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups2_RestartCoordinator_GetPut() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD, 4, 2, 2, DFLT_PARTITION_COUNT,
+ null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups1_RestartCoordinator_GetPut_Persistence() throws Exception {
+ persistence = true;
+
+ putAllGetAll(RestartMode.RESTART_CRD, 2, 1, 1, 64,
+ null, GET, PUT);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_ClientServer_Backups1_PutGet_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ updateNObjectsTest(3, 5, 3, 1, DFLT_PARTITION_COUNT, DFLT_TEST_TIME,
+ null, GET, PUT, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_ClientServer_Backups1__PutGet_CoordinatorFails() throws Exception {
+ updateNObjectsTest(10, 3, 2, 1, DFLT_PARTITION_COUNT, DFLT_TEST_TIME,
+ null, GET, PUT, RestartMode.RESTART_CRD);
+ }
+
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testGetReadInProgressCoordinatorFails() throws Exception {
+ readInProgressCoordinatorFails(false, false, PESSIMISTIC, REPEATABLE_READ, GET, PUT, null);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testGetReadInsideTxInProgressCoordinatorFails() throws Exception {
+ readInProgressCoordinatorFails(false, true, PESSIMISTIC, REPEATABLE_READ, GET, PUT, null);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testGetReadInProgressCoordinatorFails_ReadDelay() throws Exception {
+ readInProgressCoordinatorFails(true, false, PESSIMISTIC, REPEATABLE_READ, GET, PUT, null);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testGetReadInsideTxInProgressCoordinatorFails_ReadDelay() throws Exception {
+ readInProgressCoordinatorFails(true, true, PESSIMISTIC, REPEATABLE_READ, GET, PUT, null);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testReadInProgressCoordinatorFailsSimple_FromServerPutGet() throws Exception {
+ readInProgressCoordinatorFailsSimple(false, null, GET, PUT);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccProcessorTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccProcessorTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccProcessorTest.java
new file mode 100644
index 0000000..411a4b7
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccProcessorTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+
+/**
+ *
+ */
+public class CacheMvccProcessorTest extends CacheMvccAbstractTest {
+ /** {@inheritDoc} */
+ protected CacheMode cacheMode() {
+ return PARTITIONED;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTreeWithPersistence() throws Exception {
+ persistence = true;
+
+ checkTreeOperations();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTreeWithoutPersistence() throws Exception {
+ persistence = true;
+
+ checkTreeOperations();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ private void checkTreeOperations() throws Exception {
+ IgniteEx grid = startGrid(0);
+
+ grid.cluster().active(true);
+
+ MvccProcessorImpl mvccProcessor = mvccProcessor(grid);
+
+ assertEquals(TxState.NA, mvccProcessor.state(new MvccVersionImpl(1, 1, MvccUtils.MVCC_OP_COUNTER_NA)));
+
+ mvccProcessor.updateState(new MvccVersionImpl(1, 1, MvccUtils.MVCC_OP_COUNTER_NA), TxState.PREPARED);
+ mvccProcessor.updateState(new MvccVersionImpl(1, 2, MvccUtils.MVCC_OP_COUNTER_NA), TxState.PREPARED);
+ mvccProcessor.updateState(new MvccVersionImpl(1, 3, MvccUtils.MVCC_OP_COUNTER_NA), TxState.COMMITTED);
+ mvccProcessor.updateState(new MvccVersionImpl(1, 4, MvccUtils.MVCC_OP_COUNTER_NA), TxState.ABORTED);
+ mvccProcessor.updateState(new MvccVersionImpl(1, 5, MvccUtils.MVCC_OP_COUNTER_NA), TxState.ABORTED);
+ mvccProcessor.updateState(new MvccVersionImpl(1, 6, MvccUtils.MVCC_OP_COUNTER_NA), TxState.PREPARED);
+
+ if (persistence) {
+ stopGrid(0, false);
+ grid = startGrid(0);
+
+ grid.cluster().active(true);
+
+ mvccProcessor = mvccProcessor(grid);
+ }
+
+ assertEquals(TxState.PREPARED, mvccProcessor.state(new MvccVersionImpl(1, 1, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.PREPARED, mvccProcessor.state(new MvccVersionImpl(1, 2, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.COMMITTED, mvccProcessor.state(new MvccVersionImpl(1, 3, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.ABORTED, mvccProcessor.state(new MvccVersionImpl(1, 4, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.ABORTED, mvccProcessor.state(new MvccVersionImpl(1, 5, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.PREPARED, mvccProcessor.state(new MvccVersionImpl(1, 6, MvccUtils.MVCC_OP_COUNTER_NA)));
+
+ mvccProcessor.removeUntil(new MvccVersionImpl(1, 5, MvccUtils.MVCC_OP_COUNTER_NA));
+
+ assertEquals(TxState.NA, mvccProcessor.state(new MvccVersionImpl(1, 1, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.NA, mvccProcessor.state(new MvccVersionImpl(1, 2, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.NA, mvccProcessor.state(new MvccVersionImpl(1, 3, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.NA, mvccProcessor.state(new MvccVersionImpl(1, 4, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.NA, mvccProcessor.state(new MvccVersionImpl(1, 5, MvccUtils.MVCC_OP_COUNTER_NA)));
+ assertEquals(TxState.PREPARED, mvccProcessor.state(new MvccVersionImpl(1, 6, MvccUtils.MVCC_OP_COUNTER_NA)));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedCoordinatorFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedCoordinatorFailoverTest.java
new file mode 100644
index 0000000..dc948cd
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccReplicatedCoordinatorFailoverTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.mvcc;
+
+import org.apache.ignite.cache.CacheMode;
+
+/**
+ * Coordinator failover test for replicated caches.
+ */
+public class CacheMvccReplicatedCoordinatorFailoverTest extends CacheMvccAbstractCoordinatorFailoverTest {
+ /** {@inheritDoc} */
+ @Override protected CacheMode cacheMode() {
+ return CacheMode.REPLICATED;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentTransactionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentTransactionTest.java
new file mode 100644
index 0000000..8af6a5b
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccScanQueryWithConcurrentTransactionTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.mvcc;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import javax.cache.Cache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.internal.util.lang.IgniteClosure2X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiPredicate;
+
+/**
+ *
+ */
+public class CacheMvccScanQueryWithConcurrentTransactionTest extends CacheMvccAbstractFeatureTest {
+ /**
+ * @throws Exception if failed.
+ */
+ public void testScanQuery() throws Exception {
+ doTestConsistency(clo);
+ }
+
+ /** Test closure. */
+ private final IgniteClosure2X<CountDownLatch, CountDownLatch, List<Person>> clo =
+ new IgniteClosure2X<CountDownLatch, CountDownLatch, List<Person>>() {
+ @Override public List<Person> applyx(CountDownLatch startLatch, CountDownLatch endLatch2)
+ throws IgniteCheckedException {
+ IgniteBiPredicate<Integer, Person> f = new IgniteBiPredicate<Integer, Person>() {
+ @Override public boolean apply(Integer k, Person v) {
+ return k % 2 == 0;
+ }
+ };
+
+ try (QueryCursor<Cache.Entry<Integer, Person>> cur = cache().query(new ScanQuery<Integer, Person>()
+ .setFilter(f))) {
+ Iterator<Cache.Entry<Integer, Person>> it = cur.iterator();
+
+ List<Cache.Entry<Integer, Person>> pres = new ArrayList<>();
+
+ for (int i = 0; i < 50; i++)
+ pres.add(it.next());
+
+ if (startLatch != null)
+ startLatch.countDown();
+
+ while (it.hasNext())
+ pres.add(it.next());
+
+ if (endLatch2 != null)
+ U.await(endLatch2);
+
+ return entriesToPersons(pres);
+ }
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentTransactionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentTransactionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentTransactionTest.java
new file mode 100644
index 0000000..2b8b73e
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccSizeWithConcurrentTransactionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mvcc;
+
+import java.util.concurrent.CountDownLatch;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.util.lang.IgniteClosure2X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public class CacheMvccSizeWithConcurrentTransactionTest extends CacheMvccAbstractFeatureTest {
+ /**
+ * @throws Exception if failed.
+ */
+ public void testSize() throws Exception {
+ doTestConsistency(clo);
+ }
+
+ /** Test closure. */
+ private final IgniteClosure2X<CountDownLatch, CountDownLatch, Integer> clo =
+ new IgniteClosure2X<CountDownLatch, CountDownLatch, Integer>() {
+ @Override public Integer applyx(CountDownLatch startLatch, CountDownLatch endLatch2)
+ throws IgniteCheckedException {
+ if (startLatch != null)
+ startLatch.countDown();
+
+ int res = cache().size();
+
+ if (endLatch2 != null)
+ U.await(endLatch2);
+
+ return res;
+ }
+ };
+}
[41/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsAbstractComplexSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsAbstractComplexSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsAbstractComplexSelfTest.java
new file mode 100644
index 0000000..28c65a9
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsAbstractComplexSelfTest.java
@@ -0,0 +1,1058 @@
+/*
+ * 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.jdbc.thin;
+
+import java.sql.BatchUpdateException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.testframework.GridTestUtils;
+
+/**
+ * Test to check various transactional scenarios.
+ */
+public abstract class JdbcThinTransactionsAbstractComplexSelfTest extends JdbcThinAbstractSelfTest {
+ /** Client node index. */
+ final static int CLI_IDX = 1;
+
+ /**
+ * Closure to perform ordinary delete after repeatable read.
+ */
+ private final IgniteInClosure<Connection> afterReadDel = new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+ }
+ };
+
+ /**
+ * Closure to perform fast delete after repeatable read.
+ */
+ private final IgniteInClosure<Connection> afterReadFastDel = new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+ }
+ };
+
+ /**
+ * Closure to perform ordinary update after repeatable read.
+ */
+ private final IgniteInClosure<Connection> afterReadUpdate = new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "UPDATE \"Person\".Person set firstname = 'Joe' where firstname = 'John'");
+ }
+ };
+
+ /**
+ * Closure to perform ordinary delete and rollback after repeatable read.
+ */
+ private final IgniteInClosure<Connection> afterReadDelAndRollback = new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+
+ rollback(conn);
+ }
+ };
+
+ /**
+ * Closure to perform fast delete after repeatable read.
+ */
+ private final IgniteInClosure<Connection> afterReadFastDelAndRollback = new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+
+ rollback(conn);
+ }
+ };
+
+ /**
+ * Closure to perform ordinary update and rollback after repeatable read.
+ */
+ private final IgniteInClosure<Connection> afterReadUpdateAndRollback = new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "UPDATE \"Person\".Person set firstname = 'Joe' where firstname = 'John'");
+
+ rollback(conn);
+ }
+ };
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String testIgniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(testIgniteInstanceName);
+
+ cfg.setMvccEnabled(true);
+
+ CacheConfiguration<Integer, Person> ccfg = new CacheConfiguration<>("Person");
+
+ ccfg.setIndexedTypes(Integer.class, Person.class);
+
+ ccfg.getQueryEntities().iterator().next().setKeyFieldName("id");
+
+ ccfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
+
+ ccfg.setCacheMode(CacheMode.PARTITIONED);
+
+ cfg.setCacheConfiguration(ccfg);
+
+ // Let the node with index 1 be client node.
+ cfg.setClientMode(F.eq(testIgniteInstanceName, getTestIgniteInstanceName(CLI_IDX)));
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ execute("ALTER TABLE \"Person\".person add if not exists cityid int");
+
+ execute("ALTER TABLE \"Person\".person add if not exists companyid int");
+
+ execute("CREATE TABLE City (id int primary key, name varchar, population int) WITH " +
+ "\"atomicity=transactional,template=partitioned,backups=3,cache_name=City\"");
+
+ execute("CREATE TABLE Company (id int, \"cityid\" int, name varchar, primary key (id, \"cityid\")) WITH " +
+ "\"atomicity=transactional,template=partitioned,backups=1,wrap_value=false,affinity_key=cityid," +
+ "cache_name=Company\"");
+
+ execute("CREATE TABLE Product (id int primary key, name varchar, companyid int) WITH " +
+ "\"atomicity=transactional,template=partitioned,backups=2,cache_name=Product\"");
+
+ execute("CREATE INDEX IF NOT EXISTS prodidx ON Product(companyid)");
+
+ execute("CREATE INDEX IF NOT EXISTS persidx ON \"Person\".person(cityid)");
+
+ insertPerson(1, "John", "Smith", 1, 1);
+
+ insertPerson(2, "Mike", "Johns", 1, 2);
+
+ insertPerson(3, "Sam", "Jules", 2, 2);
+
+ insertPerson(4, "Alex", "Pope", 2, 3);
+
+ insertPerson(5, "Peter", "Williams", 2, 3);
+
+ insertCity(1, "Los Angeles", 5000);
+
+ insertCity(2, "Seattle", 1500);
+
+ insertCity(3, "New York", 12000);
+
+ insertCity(4, "Cupertino", 400);
+
+ insertCompany(1, "Microsoft", 2);
+
+ insertCompany(2, "Google", 3);
+
+ insertCompany(3, "Facebook", 1);
+
+ insertCompany(4, "Uber", 1);
+
+ insertCompany(5, "Apple", 4);
+
+ insertProduct(1, "Search", 2);
+
+ insertProduct(2, "Windows", 1);
+
+ insertProduct(3, "Mac", 5);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGrid(0);
+
+ startGrid(1);
+
+ startGrid(2);
+
+ startGrid(3);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+
+ super.afterTestsStopped();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ execute("DELETE FROM \"Person\".Person");
+
+ execute("DROP TABLE City");
+
+ execute("DROP TABLE Company");
+
+ execute("DROP TABLE Product");
+
+ super.afterTest();
+ }
+
+ /**
+ *
+ */
+ public void testSingleDmlStatement() throws SQLException {
+ insertPerson(6, "John", "Doe", 2, 2);
+
+ assertEquals(Collections.singletonList(l(6, "John", "Doe", 2, 2)),
+ execute("SELECT * FROM \"Person\".Person where id = 6"));
+ }
+
+ /**
+ *
+ */
+ public void testMultipleDmlStatements() throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ insertPerson(conn, 6, "John", "Doe", 2, 2);
+
+ // https://issues.apache.org/jira/browse/IGNITE-6938 - we can only see results of
+ // UPDATE of what we have not inserted ourselves.
+ execute(conn, "UPDATE \"Person\".person SET lastname = 'Jameson' where lastname = 'Jules'");
+
+ execute(conn, "DELETE FROM \"Person\".person where id = 5");
+ }
+ });
+
+ assertEquals(l(
+ l(3, "Sam", "Jameson", 2, 2),
+ l(6, "John", "Doe", 2, 2)
+ ), execute("SELECT * FROM \"Person\".Person where id = 3 or id >= 5 order by id"));
+ }
+
+ /**
+ *
+ */
+ public void testBatchDmlStatements() throws SQLException {
+ doBatchedInsert();
+
+ assertEquals(l(
+ l(6, "John", "Doe", 2, 2),
+ l(7, "Mary", "Lee", 1, 3)
+ ), execute("SELECT * FROM \"Person\".Person where id > 5 order by id"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testBatchDmlStatementsIntermediateFailure() throws SQLException {
+ insertPerson(6, "John", "Doe", 2, 2);
+
+ IgniteException e = (IgniteException)GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ doBatchedInsert();
+
+ return null;
+ }
+ }, IgniteException.class, "Duplicate key during INSERT [key=KeyCacheObjectImpl " +
+ "[part=6, val=6, hasValBytes=true]]");
+
+ assertTrue(e.getCause() instanceof BatchUpdateException);
+
+ assertTrue(e.getCause().getMessage().contains("Duplicate key during INSERT [key=KeyCacheObjectImpl " +
+ "[part=6, val=6, hasValBytes=true]]"));
+
+ // First we insert id 7, then 6. Still, 7 is not in the cache as long as the whole batch has failed inside tx.
+ assertEquals(Collections.emptyList(), execute("SELECT * FROM \"Person\".Person where id > 6 order by id"));
+ }
+
+ /**
+ *
+ */
+ private void doBatchedInsert() throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ try {
+ try (PreparedStatement ps = conn.prepareStatement("INSERT INTO \"Person\".person " +
+ "(id, firstName, lastName, cityId, companyId) values (?, ?, ?, ?, ?)")) {
+ ps.setInt(1, 7);
+
+ ps.setString(2, "Mary");
+
+ ps.setString(3, "Lee");
+
+ ps.setInt(4, 1);
+
+ ps.setInt(5, 3);
+
+ ps.addBatch();
+
+ ps.setInt(1, 6);
+
+ ps.setString(2, "John");
+
+ ps.setString(3, "Doe");
+
+ ps.setInt(4, 2);
+
+ ps.setInt(5, 2);
+
+ ps.addBatch();
+
+ ps.executeBatch();
+ }
+ }
+ catch (SQLException e) {
+ throw new IgniteException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ *
+ */
+ public void testInsertAndQueryMultipleCaches() throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ insertCity(conn, 5, "St Petersburg", 6000);
+
+ insertCompany(conn, 6, "VK", 5);
+
+ insertPerson(conn, 6, "Peter", "Sergeev", 5, 6);
+ }
+ });
+
+ try (Connection c = connect("distributedJoins=true")) {
+ assertEquals(l(l(5, "St Petersburg", 6000, 6, 5, "VK", 6, "Peter", "Sergeev", 5, 6)),
+ execute(c, "SELECT * FROM City left join Company on City.id = Company.\"cityid\" " +
+ "left join \"Person\".Person p on City.id = p.cityid WHERE p.id = 6 or company.id = 6"));
+ }
+ }
+
+ /**
+ *
+ */
+ public void testColocatedJoinSelectAndInsertInTransaction() throws SQLException {
+ // We'd like to put some Google into cities with over 1K population which don't have it yet
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ List<Integer> ids = flat(execute(conn, "SELECT distinct City.id from City left join Company c on " +
+ "City.id = c.\"cityid\" where population >= 1000 and c.name <> 'Google' order by City.id"));
+
+ assertEqualsCollections(l(1, 2), ids);
+
+ int i = 5;
+
+ for (int l : ids)
+ insertCompany(conn, ++i, "Google", l);
+ }
+ });
+
+ assertEqualsCollections(l("Los Angeles", "Seattle", "New York"), flat(execute("SELECT City.name from City " +
+ "left join Company c on city.id = c.\"cityid\" WHERE c.name = 'Google' order by City.id")));
+ }
+
+ /**
+ *
+ */
+ public void testDistributedJoinSelectAndInsertInTransaction() throws SQLException {
+ try (Connection c = connect("distributedJoins=true")) {
+ // We'd like to put some Google into cities with over 1K population which don't have it yet
+ executeInTransaction(c, new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ List<?> res = flat(execute(conn, "SELECT p.id,p.name,c.id from Company c left join Product p on " +
+ "c.id = p.companyid left join City on city.id = c.\"cityid\" WHERE c.name <> 'Microsoft' " +
+ "and population < 1000"));
+
+ assertEqualsCollections(l(3, "Mac", 5), res);
+
+ insertProduct(conn, 4, (String)res.get(1), 1);
+ }
+ });
+ }
+
+ try (Connection c = connect("distributedJoins=true")) {
+ assertEqualsCollections(l("Windows", "Mac"), flat(execute(c, "SELECT p.name from Company c left join " +
+ "Product p on c.id = p.companyid WHERE c.name = 'Microsoft' order by p.id")));
+ }
+ }
+
+ /**
+ *
+ */
+ public void testInsertFromExpression() throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "insert into city (id, name, population) values (? + 1, ?, ?)",
+ 8, "Moscow", 15000);
+ }
+ });
+ }
+
+ /**
+ *
+ */
+ public void testAutoRollback() throws SQLException {
+ try (Connection c = connect()) {
+ begin(c);
+
+ insertPerson(c, 6, "John", "Doe", 2, 2);
+ }
+
+ // Connection has not hung on close and update has not been applied.
+ assertTrue(personCache().query(new SqlFieldsQuery("SELECT * FROM \"Person\".Person WHERE id = 6"))
+ .getAll().isEmpty());
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadWithConcurrentDelete() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+ }
+ }, null);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadWithConcurrentFastDelete() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+ }
+ }, null);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadWithConcurrentCacheRemove() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ personCache().remove(1);
+ }
+ }, null);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndDeleteWithConcurrentDelete() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+ }
+ }, afterReadDel);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndDeleteWithConcurrentFastDelete() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+ }
+ }, afterReadDel);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndDeleteWithConcurrentCacheRemove() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ personCache().remove(1);
+ }
+ }, afterReadDel);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndFastDeleteWithConcurrentDelete() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+ }
+ }, afterReadFastDel);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndFastDeleteWithConcurrentFastDelete() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+ }
+ }, afterReadFastDel);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndFastDeleteWithConcurrentCacheRemove() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ personCache().remove(1);
+ }
+ }, afterReadFastDel);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndDeleteWithConcurrentDeleteAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+ }
+ }, afterReadDelAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndDeleteWithConcurrentFastDeleteAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+ }
+ }, afterReadDelAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndDeleteWithConcurrentCacheRemoveAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ personCache().remove(1);
+ }
+ }, afterReadDelAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndFastDeleteWithConcurrentDeleteAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where firstname = 'John'");
+ }
+ }, afterReadFastDelAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndFastDeleteWithConcurrentFastDeleteAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "DELETE FROM \"Person\".Person where id = 1");
+ }
+ }, afterReadFastDelAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndFastDeleteWithConcurrentCacheRemoveAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ personCache().remove(1);
+ }
+ }, afterReadFastDelAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadWithConcurrentUpdate() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "UPDATE \"Person\".Person SET lastname = 'Fix' where firstname = 'John'");
+ }
+ }, null);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadWithConcurrentCacheReplace() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ Person p = new Person();
+
+ p.id = 1;
+ p.firstName = "Luke";
+ p.lastName = "Maxwell";
+
+ personCache().replace(1, p);
+ }
+ }, null);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndUpdateWithConcurrentUpdate() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "UPDATE \"Person\".Person SET lastname = 'Fix' where firstname = 'John'");
+ }
+ }, afterReadUpdate);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndUpdateWithConcurrentCacheReplace() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ Person p = new Person();
+
+ p.id = 1;
+ p.firstName = "Luke";
+ p.lastName = "Maxwell";
+
+ personCache().replace(1, p);
+ }
+ }, afterReadUpdate);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndUpdateWithConcurrentUpdateAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ execute(conn, "UPDATE \"Person\".Person SET lastname = 'Fix' where firstname = 'John'");
+ }
+ }, afterReadUpdateAndRollback);
+ }
+
+ /**
+ *
+ */
+ public void testRepeatableReadAndUpdateWithConcurrentCacheReplaceAndRollback() throws Exception {
+ doTestRepeatableRead(new IgniteInClosure<Connection>() {
+ @Override public void apply(Connection conn) {
+ Person p = new Person();
+
+ p.id = 1;
+ p.firstName = "Luke";
+ p.lastName = "Maxwell";
+
+ personCache().replace(1, p);
+ }
+ }, afterReadUpdateAndRollback);
+ }
+
+ /**
+ * Perform repeatable reads and concurrent changes.
+ * @param concurrentWriteClo Updating closure.
+ * @param afterReadClo Closure making write changes that should also be made inside repeatable read transaction
+ * (must yield an exception).
+ * @throws Exception if failed.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ private void doTestRepeatableRead(final IgniteInClosure<Connection> concurrentWriteClo,
+ final IgniteInClosure<Connection> afterReadClo) throws Exception {
+ final CountDownLatch repeatableReadLatch = new CountDownLatch(1);
+
+ final CountDownLatch initLatch = new CountDownLatch(1);
+
+ final IgniteInternalFuture<?> readFut = multithreadedAsync(new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ List<?> before = flat(execute(conn, "SELECT * from \"Person\".Person where id = 1"));
+
+ assertEqualsCollections(l(1, "John", "Smith", 1, 1), before);
+
+ initLatch.countDown();
+
+ try {
+ U.await(repeatableReadLatch);
+ }
+ catch (IgniteInterruptedCheckedException e) {
+ throw new IgniteException(e);
+ }
+
+ List<?> after = flat(execute(conn, "SELECT * from \"Person\".Person where id = 1"));
+
+ assertEqualsCollections(before, after);
+
+ if (afterReadClo != null)
+ afterReadClo.apply(conn);
+ }
+ });
+
+ return null;
+ }
+ }, 1);
+
+ IgniteInternalFuture<?> conModFut = multithreadedAsync(new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ try {
+ U.await(initLatch);
+ }
+ catch (IgniteInterruptedCheckedException e) {
+ throw new IgniteException(e);
+ }
+
+ concurrentWriteClo.apply(conn);
+
+ repeatableReadLatch.countDown();
+ }
+ });
+
+ return null;
+ }
+ }, 1);
+
+ conModFut.get();
+
+ if (afterReadClo != null) {
+ IgniteCheckedException ex = (IgniteCheckedException)GridTestUtils.assertThrows(null, new Callable() {
+ @Override public Object call() throws Exception {
+ readFut.get();
+
+ return null;
+ }
+ }, IgniteCheckedException.class, "Mvcc version mismatch.");
+
+ assertTrue(X.hasCause(ex, SQLException.class));
+
+ assertTrue(X.getCause(ex).getMessage().contains("Mvcc version mismatch."));
+ }
+ else
+ readFut.get();
+ }
+
+ /**
+ * Create a new connection, a new transaction and run given closure in its scope.
+ * @param clo Closure.
+ * @throws SQLException if failed.
+ */
+ private void executeInTransaction(TransactionClosure clo) throws SQLException {
+ try (Connection conn = connect()) {
+ executeInTransaction(conn, clo);
+ }
+ }
+
+ /**
+ * Create a new transaction and run given closure in its scope.
+ * @param conn Connection.
+ * @param clo Closure.
+ * @throws SQLException if failed.
+ */
+ private void executeInTransaction(Connection conn, TransactionClosure clo) throws SQLException {
+ begin(conn);
+
+ clo.apply(conn);
+
+ commit(conn);
+ }
+
+ /**
+ * @return Auto commit strategy for this test.
+ */
+ abstract boolean autoCommit();
+
+ /**
+ * @param c Connection to begin a transaction on.
+ */
+ private void begin(Connection c) throws SQLException {
+ if (autoCommit())
+ execute(c, "BEGIN");
+ }
+
+ /**
+ * @param c Connection to begin a transaction on.
+ */
+ private void commit(Connection c) throws SQLException {
+ if (autoCommit())
+ execute(c, "COMMIT");
+ else
+ c.commit();
+ }
+
+ /**
+ * @param c Connection to rollback a transaction on.
+ */
+ private void rollback(Connection c) {
+ try {
+ if (autoCommit())
+ execute(c, "ROLLBACK");
+ else
+ c.rollback();
+ }
+ catch (SQLException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /**
+ * @param sql Statement.
+ * @param args Arguments.
+ * @return Result set.
+ * @throws SQLException if failed.
+ */
+ List<List<?>> execute(String sql, Object... args) throws SQLException {
+ try (Connection c = connect()) {
+ c.setAutoCommit(true);
+
+ return execute(c, sql, args);
+ }
+ }
+
+ /**
+ * @param sql Statement.
+ * @param args Arguments.
+ * @return Result set.
+ * @throws RuntimeException if failed.
+ */
+ protected List<List<?>> execute(Connection conn, String sql, Object... args) {
+ try {
+ return super.execute(conn, sql, args);
+ }
+ catch (SQLException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /**
+ * @return New connection to default node.
+ * @throws SQLException if failed.
+ */
+ private Connection connect() throws SQLException {
+ return connect(null);
+ }
+
+ /**
+ * @param params Connection parameters.
+ * @return New connection to default node.
+ * @throws SQLException if failed.
+ */
+ private Connection connect(String params) throws SQLException {
+ Connection c = connect(node(), params);
+
+ c.setAutoCommit(false);
+
+ return c;
+ }
+
+ /**
+ * @param node Node to connect to.
+ * @param params Connection parameters.
+ * @return Thin JDBC connection to specified node.
+ */
+ protected Connection connect(IgniteEx node, String params) {
+ try {
+ return super.connect(node, params);
+ }
+ catch (SQLException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * @return Default node to fire queries from.
+ */
+ private IgniteEx node() {
+ return grid(nodeIndex());
+ }
+
+ /**
+ * @return {@link Person} cache.
+ */
+ private IgniteCache<Integer, Person> personCache() {
+ return node().cache("Person");
+ }
+
+ /**
+ * @return Node index to fire queries from.
+ */
+ abstract int nodeIndex();
+
+ /**
+ * @param id New person's id.
+ * @param firstName First name.
+ * @param lastName Second name.
+ * @param cityId City id.
+ * @param companyId Company id.
+ * @throws SQLException if failed.
+ */
+ private void insertPerson(final int id, final String firstName, final String lastName, final int cityId,
+ final int companyId) throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ insertPerson(conn, id, firstName, lastName, cityId, companyId);
+ }
+ });
+ }
+
+ /**
+ * @param c Connection.
+ * @param id New person's id.
+ * @param firstName First name.
+ * @param lastName Second name.
+ * @param cityId City id.
+ * @param companyId Company id.
+ */
+ private void insertPerson(Connection c, int id, String firstName, String lastName, int cityId, int companyId) {
+ execute(c, "INSERT INTO \"Person\".person (id, firstName, lastName, cityId, companyId) values (?, ?, ?, ?, ?)",
+ id, firstName, lastName, cityId, companyId);
+ }
+
+ /**
+ * @param id New city's id.
+ * @param name City name.
+ * @param population Number of people.
+ * @throws SQLException if failed.
+ */
+ private void insertCity(final int id, final String name, final int population) throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ insertCity(conn, id, name, population);
+ }
+ });
+ }
+
+ /**
+ * @param c Connection.
+ * @param id New city's id.
+ * @param name City name.
+ * @param population Number of people.
+ */
+ private void insertCity(Connection c, int id, String name, int population) {
+ execute(c, "INSERT INTO city (id, name, population) values (?, ?, ?)", id, name, population);
+ }
+
+ /**
+ * @param id New company's id.
+ * @param name Company name.
+ * @param cityId City id.
+ * @throws SQLException if failed.
+ */
+ private void insertCompany(final int id, final String name, final int cityId) throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ insertCompany(conn, id, name, cityId);
+ }
+ });
+ }
+
+ /**
+ * @param c Connection.
+ * @param id New company's id.
+ * @param name Company name.
+ * @param cityId City id.
+ */
+ private void insertCompany(Connection c, int id, String name, int cityId) {
+ execute(c, "INSERT INTO company (id, name, \"cityid\") values (?, ?, ?)", id, name, cityId);
+ }
+
+ /**
+ * @param id New product's id.
+ * @param name Product name.
+ * @param companyId Company id..
+ * @throws SQLException if failed.
+ */
+ private void insertProduct(final int id, final String name, final int companyId) throws SQLException {
+ executeInTransaction(new TransactionClosure() {
+ @Override public void apply(Connection conn) {
+ insertProduct(conn, id, name, companyId);
+ }
+ });
+ }
+
+ /**
+ * @param c Connection.
+ * @param id New product's id.
+ * @param name Product name.
+ * @param companyId Company id..
+ */
+ private void insertProduct(Connection c, int id, String name, int companyId) {
+ execute(c, "INSERT INTO product (id, name, companyid) values (?, ?, ?)", id, name, companyId);
+ }
+
+ /**
+ * Person class.
+ */
+ private final static class Person {
+ /** */
+ @QuerySqlField
+ public int id;
+
+ /** */
+ @QuerySqlField
+ public String firstName;
+
+ /** */
+ @QuerySqlField
+ public String lastName;
+ }
+
+ /**
+ * Closure to be executed in scope of a transaction.
+ */
+ private abstract class TransactionClosure implements IgniteInClosure<Connection> {
+ // No-op.
+ }
+
+ /**
+ * @return List of given arguments.
+ */
+ private static List<?> l(Object... args) {
+ return F.asList(args);
+ }
+
+ /**
+ * Flatten rows.
+ * @param rows Rows.
+ * @return Rows as a single list.
+ */
+ @SuppressWarnings("unchecked")
+ private static <T> List<T> flat(Collection<? extends Collection<?>> rows) {
+ return new ArrayList<>(F.flatCollections((Collection<? extends Collection<T>>)rows));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientAutoCommitComplexSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientAutoCommitComplexSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientAutoCommitComplexSelfTest.java
new file mode 100644
index 0000000..d5b505a
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientAutoCommitComplexSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.jdbc.thin;
+
+/**
+ *
+ */
+public class JdbcThinTransactionsClientAutoCommitComplexSelfTest extends JdbcThinTransactionsAbstractComplexSelfTest {
+ /** {@inheritDoc} */
+ @Override boolean autoCommit() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override int nodeIndex() {
+ return CLI_IDX;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientNoAutoCommitComplexSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientNoAutoCommitComplexSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientNoAutoCommitComplexSelfTest.java
new file mode 100644
index 0000000..7fa69fd
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsClientNoAutoCommitComplexSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.jdbc.thin;
+
+/**
+ *
+ */
+public class JdbcThinTransactionsClientNoAutoCommitComplexSelfTest extends JdbcThinTransactionsAbstractComplexSelfTest {
+ /** {@inheritDoc} */
+ @Override boolean autoCommit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override int nodeIndex() {
+ return CLI_IDX;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsSelfTest.java
new file mode 100644
index 0000000..1619996
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsSelfTest.java
@@ -0,0 +1,447 @@
+/*
+ * 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.jdbc.thin;
+
+import java.sql.BatchUpdateException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridStringLogger;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Tests to check behavior with transactions on.
+ */
+public class JdbcThinTransactionsSelfTest extends JdbcThinAbstractSelfTest {
+ /** IP finder. */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** */
+ private static final String URL = "jdbc:ignite:thin://127.0.0.1";
+
+ /** Logger. */
+ private GridStringLogger log;
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("deprecation")
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ cfg.setMvccEnabled(true);
+
+ cfg.setCacheConfiguration(cacheConfiguration(DEFAULT_CACHE_NAME));
+
+ TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+ disco.setIpFinder(IP_FINDER);
+
+ cfg.setDiscoverySpi(disco);
+
+ cfg.setMarshaller(new BinaryMarshaller());
+
+ cfg.setGridLogger(log = new GridStringLogger());
+
+ return cfg;
+ }
+
+ /**
+ * @param name Cache name.
+ * @return Cache configuration.
+ * @throws Exception In case of error.
+ */
+ private CacheConfiguration cacheConfiguration(@NotNull String name) throws Exception {
+ CacheConfiguration cfg = defaultCacheConfiguration();
+
+ cfg.setName(name);
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ startGrid(0);
+
+ try (Connection c = c(true, NestedTxMode.ERROR)) {
+ try (Statement s = c.createStatement()) {
+ s.execute("CREATE TABLE INTS (k int primary key, v int) WITH \"cache_name=ints,wrap_value=false," +
+ "atomicity=transactional\"");
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+ }
+
+ /**
+ * @param autoCommit Auto commit mode.
+ * @param nestedTxMode Nested transactions mode.
+ * @return Connection.
+ * @throws SQLException if failed.
+ */
+ private static Connection c(boolean autoCommit, NestedTxMode nestedTxMode) throws SQLException {
+ Connection res = DriverManager.getConnection(URL + "/?nestedTransactionsMode=" + nestedTxMode.name());
+
+ res.setAutoCommit(autoCommit);
+
+ return res;
+ }
+
+ /**
+ *
+ */
+ public void testTransactionsBeginCommitRollback() throws IgniteCheckedException {
+ final AtomicBoolean stop = new AtomicBoolean();
+
+ IgniteInternalFuture<?> fut = GridTestUtils.runMultiThreadedAsync(new Runnable() {
+ @Override public void run() {
+ try {
+ try (Connection c = c(false, NestedTxMode.ERROR)) {
+ while (!stop.get()) {
+ try (Statement s = c.createStatement()) {
+ s.execute("BEGIN");
+
+ c.commit();
+
+ s.execute("BEGIN");
+
+ c.rollback();
+ }
+ }
+ }
+ }
+ catch (SQLException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }, 8, "jdbc-transactions");
+
+ U.sleep(5000);
+
+ stop.set(true);
+
+ fut.get();
+ }
+
+ /**
+ *
+ */
+ public void testTransactionsBeginCommitRollbackAutocommit() throws IgniteCheckedException {
+ GridTestUtils.runMultiThreadedAsync(new Runnable() {
+ @Override public void run() {
+ try {
+ try (Connection c = c(true, NestedTxMode.ERROR)) {
+ try (Statement s = c.createStatement()) {
+ s.execute("BEGIN");
+
+ s.execute("COMMIT");
+
+ s.execute("BEGIN");
+
+ s.execute("ROLLBACK");
+ }
+ }
+ }
+ catch (SQLException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }, 8, "jdbc-transactions").get();
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testIgnoreNestedTxAutocommitOff() throws SQLException {
+ try (Connection c = c(false, NestedTxMode.IGNORE)) {
+ doNestedTxStart(c, false);
+ }
+
+ assertTrue(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testCommitNestedTxAutocommitOff() throws SQLException {
+ try (Connection c = c(false, NestedTxMode.COMMIT)) {
+ doNestedTxStart(c, false);
+ }
+
+ assertFalse(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testErrorNestedTxAutocommitOff() throws SQLException {
+ GridTestUtils.assertThrows(null, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Connection c = c(false, NestedTxMode.ERROR)) {
+ doNestedTxStart(c, false);
+ }
+
+ throw new AssertionError();
+ }
+ }, SQLException.class, "Transaction has already been started.");
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testIgnoreNestedTxAutocommitOn() throws SQLException {
+ try (Connection c = c(true, NestedTxMode.IGNORE)) {
+ doNestedTxStart(c, false);
+ }
+
+ assertTrue(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testCommitNestedTxAutocommitOn() throws SQLException {
+ try (Connection c = c(true, NestedTxMode.COMMIT)) {
+ doNestedTxStart(c, false);
+ }
+
+ assertFalse(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testErrorNestedTxAutocommitOn() throws SQLException {
+ GridTestUtils.assertThrows(null, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Connection c = c(true, NestedTxMode.ERROR)) {
+ doNestedTxStart(c, false);
+ }
+
+ throw new AssertionError();
+ }
+ }, SQLException.class, "Transaction has already been started.");
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testIgnoreNestedTxAutocommitOffBatched() throws SQLException {
+ try (Connection c = c(false, NestedTxMode.IGNORE)) {
+ doNestedTxStart(c, true);
+ }
+
+ assertTrue(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testCommitNestedTxAutocommitOffBatched() throws SQLException {
+ try (Connection c = c(false, NestedTxMode.COMMIT)) {
+ doNestedTxStart(c, true);
+ }
+
+ assertFalse(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testErrorNestedTxAutocommitOffBatched() throws SQLException {
+ GridTestUtils.assertThrows(null, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Connection c = c(false, NestedTxMode.ERROR)) {
+ doNestedTxStart(c, true);
+ }
+
+ throw new AssertionError();
+ }
+ }, BatchUpdateException.class, "Transaction has already been started.");
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testIgnoreNestedTxAutocommitOnBatched() throws SQLException {
+ try (Connection c = c(true, NestedTxMode.IGNORE)) {
+ doNestedTxStart(c, true);
+ }
+
+ assertTrue(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testCommitNestedTxAutocommitOnBatched() throws SQLException {
+ try (Connection c = c(true, NestedTxMode.COMMIT)) {
+ doNestedTxStart(c, true);
+ }
+
+ assertFalse(log.toString().contains("ignoring BEGIN command"));
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testErrorNestedTxAutocommitOnBatched() throws SQLException {
+ GridTestUtils.assertThrows(null, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Connection c = c(true, NestedTxMode.ERROR)) {
+ doNestedTxStart(c, true);
+ }
+
+ throw new AssertionError();
+ }
+ }, BatchUpdateException.class, "Transaction has already been started.");
+ }
+
+ /**
+ * Try to start nested transaction via batch as well as separate statements.
+ * @param conn Connection.
+ * @param batched Whether {@link Statement#executeBatch()} should be used.
+ * @throws SQLException if failed.
+ */
+ private void doNestedTxStart(Connection conn, boolean batched) throws SQLException {
+ try (Statement s = conn.createStatement()) {
+ s.executeQuery("SELECT * FROM INTS");
+
+ if (batched) {
+ s.addBatch("BEGIN");
+
+ s.addBatch("BEGIN");
+
+ s.executeBatch();
+ }
+ else {
+ s.execute("BEGIN");
+
+ s.execute("BEGIN");
+ }
+ }
+ }
+
+ /**
+ * @throws SQLException if failed.
+ */
+ public void testAutoCommitSingle() throws SQLException {
+ doTestAutoCommit(false);
+ }
+
+ /**
+ * @throws SQLException if failed.
+ */
+ public void testAutoCommitBatched() throws SQLException {
+ doTestAutoCommit(true);
+ }
+
+ /**
+ * @param batched Batch mode flag.
+ * @throws SQLException if failed.
+ */
+ private void doTestAutoCommit(boolean batched) throws SQLException {
+ IgniteCache<Integer, ?> cache = grid(0).cache("ints");
+
+ try (Connection c = c(false, NestedTxMode.ERROR)) {
+ try (Statement s = c.createStatement()) {
+ assertFalse(s.executeQuery("SELECT * from INTS").next());
+
+ if (batched) {
+ s.addBatch("INSERT INTO INTS(k, v) values(1, 1)");
+
+ s.executeBatch();
+ }
+ else
+ s.execute("INSERT INTO INTS(k, v) values(1, 1)");
+
+ // We haven't committed anything yet - this check shows that autoCommit flag is in effect.
+ assertTrue(cache.query(new SqlFieldsQuery("SELECT * from INTS")).getAll().isEmpty());
+
+ // We should see own updates.
+ assertTrue(s.executeQuery("SELECT * from INTS").next());
+
+ c.commit();
+
+ c.setAutoCommit(true);
+
+ assertEquals(1, cache.get(1));
+
+ assertTrue(s.executeQuery("SELECT * from INTS").next());
+ }
+ }
+ }
+
+ /**
+ * Test that exception in one of the statements does not kill connection worker altogether.
+ * @throws SQLException if failed.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testExceptionHandling() throws SQLException {
+ try (Connection c = c(true, NestedTxMode.ERROR)) {
+ try (Statement s = c.createStatement()) {
+ s.execute("INSERT INTO INTS(k, v) values(1, 1)");
+
+ assertEquals(1, grid(0).cache("ints").get(1));
+
+ GridTestUtils.assertThrows(null, new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ s.execute("INSERT INTO INTS(x, y) values(1, 1)");
+
+ return null;
+ }
+ }, SQLException.class, "Failed to parse query");
+
+ s.execute("INSERT INTO INTS(k, v) values(2, 2)");
+
+ assertEquals(2, grid(0).cache("ints").get(2));
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerAutoCommitComplexSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerAutoCommitComplexSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerAutoCommitComplexSelfTest.java
new file mode 100644
index 0000000..3c473ab
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerAutoCommitComplexSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.jdbc.thin;
+
+/**
+ *
+ */
+public class JdbcThinTransactionsServerAutoCommitComplexSelfTest extends JdbcThinTransactionsAbstractComplexSelfTest {
+ /** {@inheritDoc} */
+ @Override boolean autoCommit() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override int nodeIndex() {
+ return 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerNoAutoCommitComplexSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerNoAutoCommitComplexSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerNoAutoCommitComplexSelfTest.java
new file mode 100644
index 0000000..655d4c5
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinTransactionsServerNoAutoCommitComplexSelfTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.jdbc.thin;
+
+/**
+ *
+ */
+public class JdbcThinTransactionsServerNoAutoCommitComplexSelfTest extends JdbcThinTransactionsAbstractComplexSelfTest {
+ /** {@inheritDoc} */
+ @Override boolean autoCommit() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override int nodeIndex() {
+ return 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
----------------------------------------------------------------------
diff --git a/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
index 99cf849..51365f3 100644
--- a/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
+++ b/modules/codegen/src/main/java/org/apache/ignite/codegen/MessageCodeGenerator.java
@@ -38,6 +38,8 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryEnlistRequest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxQueryResultsEnlistRequest;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.GridCodegenConverter;
import org.apache.ignite.internal.GridDirectCollection;
@@ -168,7 +170,7 @@ public class MessageCodeGenerator {
// gen.generateAll(true);
-// gen.generateAndWrite(GridChangeGlobalStateMessageResponse.class);
+ gen.generateAndWrite(GridNearTxQueryResultsEnlistRequest.class);
// gen.generateAndWrite(GridNearAtomicUpdateRequest.class);
@@ -235,6 +237,8 @@ public class MessageCodeGenerator {
// gen.generateAndWrite(GridCacheVersionEx.class);
// gen.generateAndWrite(GridH2DmlRequest.class);
// gen.generateAndWrite(GridH2DmlResponse.class);
+// gen.generateAndWrite(GridNearTxEnlistRequest.class);
+// gen.generateAndWrite(GridNearTxEnlistResponse.class);
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index 2e35f37..fb3789d 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -1019,6 +1019,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
* @param atomicityMode Cache atomicity mode.
* @return {@code this} for chaining.
*/
+ @SuppressWarnings("unchecked")
public CacheConfiguration<K, V> setAtomicityMode(CacheAtomicityMode atomicityMode) {
this.atomicityMode = atomicityMode;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
index ab54709..3060caa 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java
@@ -214,6 +214,13 @@ public class IgniteConfiguration {
/** Default timeout after which long query warning will be printed. */
public static final long DFLT_LONG_QRY_WARN_TIMEOUT = 3000;
+ /** Default size of MVCC vacuum thread pool. */
+ public static final int DFLT_MVCC_VACUUM_THREAD_CNT = 2;
+
+ /** Default time interval between vacuum process runs (ms). */
+ public static final int DFLT_MVCC_VACUUM_TIME_INTERVAL = 5000;
+
+
/** Optional local Ignite instance name. */
private String igniteInstanceName;
@@ -487,6 +494,15 @@ public class IgniteConfiguration {
/** Client connector configuration. */
private ClientConnectorConfiguration cliConnCfg = ClientListenerProcessor.DFLT_CLI_CFG;
+ /** Flag whether MVCC is enabled. */
+ private boolean mvccEnabled;
+
+ /** Size of MVCC vacuum thread pool. */
+ private int mvccVacuumThreadCnt = DFLT_MVCC_VACUUM_THREAD_CNT;
+
+ /** Time interval between vacuum process runs (ms). */
+ private int mvccVacuumTimeInterval = DFLT_MVCC_VACUUM_TIME_INTERVAL;
+
/** User authentication enabled. */
private boolean authEnabled;
@@ -535,6 +551,7 @@ public class IgniteConfiguration {
addrRslvr = cfg.getAddressResolver();
allResolversPassReq = cfg.isAllSegmentationResolversPassRequired();
atomicCfg = cfg.getAtomicConfiguration();
+ authEnabled = cfg.isAuthenticationEnabled();
autoActivation = cfg.isAutoActivationEnabled();
binaryCfg = cfg.getBinaryConfiguration();
dsCfg = cfg.getDataStorageConfiguration();
@@ -578,6 +595,9 @@ public class IgniteConfiguration {
metricsLogFreq = cfg.getMetricsLogFrequency();
metricsUpdateFreq = cfg.getMetricsUpdateFrequency();
mgmtPoolSize = cfg.getManagementThreadPoolSize();
+ mvccEnabled = cfg.isMvccEnabled();
+ mvccVacuumThreadCnt = cfg.mvccVacuumThreadCnt;
+ mvccVacuumTimeInterval = cfg.mvccVacuumTimeInterval;
netTimeout = cfg.getNetworkTimeout();
nodeId = cfg.getNodeId();
odbcCfg = cfg.getOdbcConfiguration();
@@ -612,7 +632,6 @@ public class IgniteConfiguration {
utilityCachePoolSize = cfg.getUtilityCacheThreadPoolSize();
waitForSegOnStart = cfg.isWaitForSegmentOnStart();
warmupClos = cfg.getWarmupClosure();
- authEnabled = cfg.isAuthenticationEnabled();
}
/**
@@ -2984,6 +3003,69 @@ public class IgniteConfiguration {
}
/**
+ * Whether or not MVCC is enabled.
+ *
+ * @return {@code True} if MVCC is enabled.
+ */
+ public boolean isMvccEnabled() {
+ return mvccEnabled;
+ }
+
+ /**
+ * Sets MVCC enabled flag.
+ *
+ * @param mvccEnabled MVCC enabled flag.
+ * @return {@code this} for chaining.
+ */
+ public IgniteConfiguration setMvccEnabled(boolean mvccEnabled) {
+ this.mvccEnabled = mvccEnabled;
+
+ return this;
+ }
+
+ /**
+ * Returns number of MVCC vacuum cleanup threads.
+ *
+ * @return Number of MVCC vacuum cleanup threads.
+ */
+ public int getMvccVacuumThreadCnt() {
+ return mvccVacuumThreadCnt;
+ }
+
+ /**
+ * Sets number of MVCC vacuum cleanup threads.
+ *
+ * @param mvccVacuumThreadCnt Number of MVCC vacuum cleanup threads.
+ * @return {@code this} for chaining.
+ */
+ public IgniteConfiguration setMvccVacuumThreadCnt(int mvccVacuumThreadCnt) {
+ this.mvccVacuumThreadCnt = mvccVacuumThreadCnt;
+
+ return this;
+ }
+
+ /**
+ * Returns time interval between vacuum runs.
+ *
+ * @return Time interval between vacuum runs.
+ */
+ public int getMvccVacuumTimeInterval() {
+ return mvccVacuumTimeInterval;
+ }
+
+ /**
+ * Sets time interval between vacuum runs.
+ *
+ * @param mvccVacuumTimeInterval Time interval between vacuum runs.
+ * @return {@code this} for chaining.
+ */
+ public IgniteConfiguration setMvccVacuumTimeInterval(int mvccVacuumTimeInterval) {
+ this.mvccVacuumTimeInterval = mvccVacuumTimeInterval;
+
+ return this;
+ }
+
+ /**
* Returns {@code true} if user authentication is enabled for cluster. Otherwise returns {@code false}.
* Default value is false; authentication is disabled.
*
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java b/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
index 504c83f..0cf3a6e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java
@@ -64,7 +64,10 @@ public interface GridComponent {
QUERY_PROC,
/** Authentication processor. */
- AUTH_PROC
+ AUTH_PROC,
+
+ /** */
+ CACHE_CRD_PROC
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
index 051978c..0690565 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContext.java
@@ -32,6 +32,7 @@ import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
import org.apache.ignite.internal.managers.failover.GridFailoverManager;
import org.apache.ignite.internal.managers.indexing.GridIndexingManager;
import org.apache.ignite.internal.managers.loadbalancer.GridLoadBalancerManager;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccProcessor;
import org.apache.ignite.internal.worker.WorkersRegistry;
import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor;
@@ -671,6 +672,11 @@ public interface GridKernalContext extends Iterable<GridComponent> {
public PlatformProcessor platform();
/**
+ * @return Cache mvcc coordinator processor.
+ */
+ public MvccProcessor coordinators();
+
+ /**
* @return PDS mode folder name resolver, also generates consistent ID in case new folder naming is used
*/
public PdsFoldersResolver pdsFolderResolver();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
index 2be64e5..3b7b430 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridKernalContextImpl.java
@@ -47,6 +47,7 @@ import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
import org.apache.ignite.internal.managers.failover.GridFailoverManager;
import org.apache.ignite.internal.managers.indexing.GridIndexingManager;
import org.apache.ignite.internal.managers.loadbalancer.GridLoadBalancerManager;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccProcessor;
import org.apache.ignite.internal.worker.WorkersRegistry;
import org.apache.ignite.internal.processors.affinity.GridAffinityProcessor;
import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor;
@@ -290,6 +291,10 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
@GridToStringExclude
private DataStructuresProcessor dataStructuresProc;
+ /** Cache mvcc coordinators. */
+ @GridToStringExclude
+ private MvccProcessor coordProc;
+
/** */
@GridToStringExclude
private IgniteAuthenticationProcessor authProc;
@@ -356,7 +361,7 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
/** */
@GridToStringExclude
- Map<String, ? extends ExecutorService> customExecSvcs;
+ private Map<String, ? extends ExecutorService> customExecSvcs;
/** */
@GridToStringExclude
@@ -610,6 +615,8 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
poolProc = (PoolProcessor)comp;
else if (comp instanceof GridMarshallerMappingProcessor)
mappingProc = (GridMarshallerMappingProcessor)comp;
+ else if (comp instanceof MvccProcessor)
+ coordProc = (MvccProcessor)comp;
else if (comp instanceof PdsFoldersResolver)
pdsFolderRslvr = (PdsFoldersResolver)comp;
else if (comp instanceof GridInternalSubscriptionProcessor)
@@ -880,6 +887,11 @@ public class GridKernalContextImpl implements GridKernalContext, Externalizable
}
/** {@inheritDoc} */
+ @Override public MvccProcessor coordinators() {
+ return coordProc;
+ }
+
+ /** {@inheritDoc} */
@Override public IgniteAuthenticationProcessor authentication() {
return authProc;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java b/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
index 0b2d41a..68cea4f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/GridTopic.java
@@ -127,7 +127,10 @@ public enum GridTopic {
TOPIC_AUTH,
/** */
- TOPIC_EXCHANGE;
+ TOPIC_EXCHANGE,
+
+ /** */
+ TOPIC_CACHE_COORDINATOR;
/** Enum values. */
private static final GridTopic[] VALS = values();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index eef2140..230c05c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -125,6 +125,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheUtilityKey;
import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
import org.apache.ignite.internal.processors.cache.persistence.filename.PdsConsistentIdProcessor;
import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
@@ -993,6 +994,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
// be able to start receiving messages once discovery completes.
try {
startProcessor(new PdsConsistentIdProcessor(ctx));
+ startProcessor(MvccUtils.createProcessor(ctx));
startProcessor(createComponent(DiscoveryNodeValidationProcessor.class, ctx));
startProcessor(new GridAffinityProcessor(ctx));
startProcessor(createComponent(GridSegmentationProcessor.class, ctx));
@@ -2761,7 +2763,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** {@inheritDoc} */
@Override public <K, V> IgniteCache<K, V> createCache(CacheConfiguration<K, V> cacheCfg) {
A.notNull(cacheCfg, "cacheCfg");
- CU.validateCacheName(cacheCfg.getName());
+ CU.validateNewCacheName(cacheCfg.getName());
guard();
@@ -2817,7 +2819,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** {@inheritDoc} */
@Override public <K, V> IgniteCache<K, V> createCache(String cacheName) {
- CU.validateCacheName(cacheName);
+ CU.validateNewCacheName(cacheName);
guard();
@@ -2846,7 +2848,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
@Override public <K, V> IgniteBiTuple<IgniteCache<K, V>, Boolean> getOrCreateCache0(
CacheConfiguration<K, V> cacheCfg, boolean sql) {
A.notNull(cacheCfg, "cacheCfg");
- CU.validateCacheName(cacheCfg.getName());
+ CU.validateNewCacheName(cacheCfg.getName());
guard();
@@ -2912,7 +2914,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
NearCacheConfiguration<K, V> nearCfg
) {
A.notNull(cacheCfg, "cacheCfg");
- CU.validateCacheName(cacheCfg.getName());
+ CU.validateNewCacheName(cacheCfg.getName());
A.notNull(nearCfg, "nearCfg");
guard();
@@ -2941,7 +2943,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
@Override public <K, V> IgniteCache<K, V> getOrCreateCache(CacheConfiguration<K, V> cacheCfg,
NearCacheConfiguration<K, V> nearCfg) {
A.notNull(cacheCfg, "cacheCfg");
- CU.validateCacheName(cacheCfg.getName());
+ CU.validateNewCacheName(cacheCfg.getName());
A.notNull(nearCfg, "nearCfg");
guard();
@@ -2982,7 +2984,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** {@inheritDoc} */
@Override public <K, V> IgniteCache<K, V> createNearCache(String cacheName, NearCacheConfiguration<K, V> nearCfg) {
- CU.validateCacheName(cacheName);
+ CU.validateNewCacheName(cacheName);
A.notNull(nearCfg, "nearCfg");
guard();
@@ -3014,7 +3016,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** {@inheritDoc} */
@Override public <K, V> IgniteCache<K, V> getOrCreateNearCache(String cacheName,
NearCacheConfiguration<K, V> nearCfg) {
- CU.validateCacheName(cacheName);
+ CU.validateNewCacheName(cacheName);
A.notNull(nearCfg, "nearCfg");
guard();
@@ -3143,7 +3145,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** {@inheritDoc} */
@Override public <K, V> IgniteCache<K, V> getOrCreateCache(String cacheName) {
- CU.validateCacheName(cacheName);
+ CU.validateNewCacheName(cacheName);
guard();
@@ -3172,7 +3174,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
*/
public IgniteInternalFuture<?> getOrCreateCacheAsync(String cacheName, String templateName,
CacheConfigurationOverride cfgOverride, boolean checkThreadTx) {
- CU.validateCacheName(cacheName);
+ CU.validateNewCacheName(cacheName);
guard();
@@ -3192,7 +3194,7 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
/** {@inheritDoc} */
@Override public <K, V> void addCacheConfiguration(CacheConfiguration<K, V> cacheCfg) {
A.notNull(cacheCfg, "cacheCfg");
- CU.validateCacheName(cacheCfg.getName());
+ CU.validateNewCacheName(cacheCfg.getName());
guard();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/IgniteNodeAttributes.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteNodeAttributes.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteNodeAttributes.java
index ed16a77..61a98a1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteNodeAttributes.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteNodeAttributes.java
@@ -205,6 +205,9 @@ public final class IgniteNodeAttributes {
/** Internal attribute name constant. */
public static final String ATTR_DYNAMIC_CACHE_START_ROLLBACK_SUPPORTED = ATTR_PREFIX + ".dynamic.cache.start.rollback.supported";
+ /** Mvcc enabled flag. */
+ public static final String ATTR_MVCC_ENABLED = ATTR_PREFIX + ".mvcc.enabled";
+
/**
* Enforces singleton.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
index 2b408df..148bd21 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
@@ -2349,12 +2349,8 @@ public class IgnitionEx {
"like TcpDiscoverySpi)");
for (CacheConfiguration ccfg : userCaches) {
- if (CU.isHadoopSystemCache(ccfg.getName()))
- throw new IgniteCheckedException("Cache name cannot be \"" + CU.SYS_CACHE_HADOOP_MR +
- "\" because it is reserved for internal purposes.");
-
- if (CU.isUtilityCache(ccfg.getName()))
- throw new IgniteCheckedException("Cache name cannot be \"" + CU.UTILITY_CACHE_NAME +
+ if (CU.isReservedCacheName(ccfg.getName()))
+ throw new IgniteCheckedException("Cache name cannot be \"" + ccfg.getName() +
"\" because it is reserved for internal purposes.");
if (IgfsUtils.matchIgfsCacheName(ccfg.getName()))
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
index 92355a0..5e40bbf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionProperties.java
@@ -164,6 +164,16 @@ public interface ConnectionProperties {
public void setSkipReducerOnUpdate(boolean skipReducerOnUpdate);
/**
+ * @return Nested transactions handling strategy.
+ */
+ public String nestedTxMode();
+
+ /**
+ * @param nestedTxMode Nested transactions handling strategy.
+ */
+ public void nestedTxMode(String nestedTxMode);
+
+ /**
* Gets SSL connection mode.
*
* @return Use SSL flag.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
index 9371a07..51a3837 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/ConnectionPropertiesImpl.java
@@ -27,6 +27,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.internal.processors.odbc.SqlStateCode;
import org.apache.ignite.internal.util.HostAndPortRange;
+import org.apache.ignite.internal.processors.query.NestedTxMode;
import org.apache.ignite.internal.util.typedef.F;
/**
@@ -99,6 +100,26 @@ public class ConnectionPropertiesImpl implements ConnectionProperties, Serializa
private BooleanProperty skipReducerOnUpdate = new BooleanProperty(
"skipReducerOnUpdate", "Enable execution update queries on ignite server nodes", false, false);
+ /** Nested transactions handling strategy. */
+ private StringProperty nestedTxMode = new StringProperty(
+ "nestedTransactionsMode", "Way to handle nested transactions", NestedTxMode.ERROR.name(),
+ new String[] { NestedTxMode.COMMIT.name(), NestedTxMode.ERROR.name(), NestedTxMode.IGNORE.name() },
+ false, new PropertyValidator() {
+ private static final long serialVersionUID = 0L;
+
+ @Override public void validate(String mode) throws SQLException {
+ if (!F.isEmpty(mode)) {
+ try {
+ NestedTxMode.valueOf(mode.toUpperCase());
+ }
+ catch (IllegalArgumentException e) {
+ throw new SQLException("Invalid nested transactions handling mode, allowed values: " +
+ Arrays.toString(nestedTxMode.choices), SqlStateCode.CLIENT_CONNECTION_FAILED);
+ }
+ }
+ }
+ });
+
/** SSL: Use SSL connection to Ignite node. */
private StringProperty sslMode = new StringProperty("sslMode",
"The SSL mode of the connection", SSL_MODE_DISABLE,
@@ -165,7 +186,7 @@ public class ConnectionPropertiesImpl implements ConnectionProperties, Serializa
/** Properties array. */
private final ConnectionProperty [] propsArray = {
distributedJoins, enforceJoinOrder, collocated, replicatedOnly, autoCloseServerCursor,
- tcpNoDelay, lazy, socketSendBuffer, socketReceiveBuffer, skipReducerOnUpdate,
+ tcpNoDelay, lazy, socketSendBuffer, socketReceiveBuffer, skipReducerOnUpdate, nestedTxMode,
sslMode, sslProtocol, sslKeyAlgorithm,
sslClientCertificateKeyStoreUrl, sslClientCertificateKeyStorePassword, sslClientCertificateKeyStoreType,
sslTrustCertificateKeyStoreUrl, sslTrustCertificateKeyStorePassword, sslTrustCertificateKeyStoreType,
@@ -437,6 +458,16 @@ public class ConnectionPropertiesImpl implements ConnectionProperties, Serializa
}
/** {@inheritDoc} */
+ @Override public String nestedTxMode() {
+ return nestedTxMode.value();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void nestedTxMode(String val) {
+ nestedTxMode.setValue(val);
+ }
+
+ /** {@inheritDoc} */
@Override public void setUsername(String name) {
user.setValue(name);
}
@@ -815,11 +846,11 @@ public class ConnectionPropertiesImpl implements ConnectionProperties, Serializa
SqlStateCode.CLIENT_CONNECTION_FAILED);
}
- checkChoices(strVal);
-
if (validator != null)
validator.validate(strVal);
+ checkChoices(strVal);
+
props.remove(name);
init(strVal);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
index b36b319..547b1db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
@@ -197,7 +197,7 @@ public class JdbcThinConnection implements Connection {
}
sendRequest(new JdbcQueryExecuteRequest(JdbcStatementType.ANY_STATEMENT_TYPE,
- schema, 1, 1, sql, null));
+ schema, 1, 1, autoCommit, sql, null));
streamState = new StreamState((SqlSetStreamingCommand)cmd);
}
@@ -328,19 +328,18 @@ public class JdbcThinConnection implements Connection {
@Override public void setAutoCommit(boolean autoCommit) throws SQLException {
ensureNotClosed();
- this.autoCommit = autoCommit;
+ // Do nothing if resulting value doesn't actually change.
+ if (autoCommit != this.autoCommit) {
+ doCommit();
- if (!autoCommit)
- LOG.warning("Transactions are not supported.");
+ this.autoCommit = autoCommit;
+ }
}
/** {@inheritDoc} */
@Override public boolean getAutoCommit() throws SQLException {
ensureNotClosed();
- if (!autoCommit)
- LOG.warning("Transactions are not supported.");
-
return autoCommit;
}
@@ -351,7 +350,7 @@ public class JdbcThinConnection implements Connection {
if (autoCommit)
throw new SQLException("Transaction cannot be committed explicitly in auto-commit mode.");
- LOG.warning("Transactions are not supported.");
+ doCommit();
}
/** {@inheritDoc} */
@@ -359,9 +358,21 @@ public class JdbcThinConnection implements Connection {
ensureNotClosed();
if (autoCommit)
- throw new SQLException("Transaction cannot rollback in auto-commit mode.");
+ throw new SQLException("Transaction cannot be rolled back explicitly in auto-commit mode.");
- LOG.warning("Transactions are not supported.");
+ try (Statement s = createStatement()) {
+ s.execute("ROLLBACK");
+ }
+ }
+
+ /**
+ * Send to the server {@code COMMIT} command.
+ * @throws SQLException if failed.
+ */
+ private void doCommit() throws SQLException {
+ try (Statement s = createStatement()) {
+ s.execute("COMMIT");
+ }
}
/** {@inheritDoc} */
@@ -927,7 +938,7 @@ public class JdbcThinConnection implements Connection {
respSem.acquire();
sendRequestNotWaitResponse(
- new JdbcOrderedBatchExecuteRequest(schema, streamBatch, lastBatch, order));
+ new JdbcOrderedBatchExecuteRequest(schema, streamBatch, autoCommit, lastBatch, order));
streamBatch = null;
[37/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
index a8ad32d..2fe097c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
@@ -17,10 +17,12 @@
package org.apache.ignite.internal.processors.cache;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -36,6 +38,9 @@ import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.internal.NodeStoppingException;
import org.apache.ignite.internal.pagemem.FullPageId;
+import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccMarkUpdatedRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccUpdateNewTxStateHintRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccUpdateTxStateHintRecord;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
@@ -43,28 +48,48 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.Cac
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteDhtDemandedPartitionsMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteHistoricalIterator;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteRebalanceIteratorImpl;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
import org.apache.ignite.internal.processors.cache.persistence.RootPage;
import org.apache.ignite.internal.processors.cache.persistence.RowStore;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
+import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
import org.apache.ignite.internal.processors.cache.tree.CacheDataRowStore;
import org.apache.ignite.internal.processors.cache.tree.CacheDataTree;
import org.apache.ignite.internal.processors.cache.tree.DataRow;
import org.apache.ignite.internal.processors.cache.tree.PendingEntriesTree;
import org.apache.ignite.internal.processors.cache.tree.PendingRow;
+import org.apache.ignite.internal.processors.cache.tree.RowLinkIO;
import org.apache.ignite.internal.processors.cache.tree.SearchRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateDataRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateDataRowNative;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateResult;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.ResultType;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccFirstRowTreeClosure;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccMaxSearchRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccMinSearchRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccSnapshotSearchRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccTreeClosure;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
import org.apache.ignite.internal.util.GridAtomicLong;
import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
import org.apache.ignite.internal.util.GridEmptyCloseableIterator;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.GridSpinBusyLock;
import org.apache.ignite.internal.util.GridStripedLock;
+import org.apache.ignite.internal.util.IgniteTree;
import org.apache.ignite.internal.util.lang.GridCloseableIterator;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.lang.GridIterator;
@@ -72,6 +97,7 @@ import org.apache.ignite.internal.util.lang.IgniteInClosure2X;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteClosure;
import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.lang.IgnitePredicate;
@@ -80,6 +106,22 @@ import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.pagemem.PageIdAllocator.FLAG_IDX;
import static org.apache.ignite.internal.pagemem.PageIdAllocator.INDEX_PARTITION;
import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.INITIAL_VERSION;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_OP_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.compare;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.compareNewVersion;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.isVisible;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.mvccVersionIsValid;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.state;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.unexpectedStateException;
+import static org.apache.ignite.internal.processors.cache.persistence.GridCacheOffheapManager.EMPTY_CURSOR;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO.MVCC_INFO_SIZE;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_BIT_OFF;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_MASK;
+import static org.apache.ignite.internal.util.IgniteTree.OperationType.NOOP;
+import static org.apache.ignite.internal.util.IgniteTree.OperationType.PUT;
/**
*
@@ -397,6 +439,170 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
+ @Override public boolean mvccInitialValue(
+ GridCacheMapEntry entry,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer) throws IgniteCheckedException {
+ return dataStore(entry.localPartition()).mvccInitialValue(
+ entry.context(),
+ entry.key(),
+ val,
+ ver,
+ expireTime,
+ mvccVer,
+ newMvccVer);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean mvccInitialValueIfAbsent(
+ GridCacheMapEntry entry,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte txState,
+ byte newTxState) throws IgniteCheckedException {
+ return dataStore(entry.localPartition()).mvccInitialValueIfAbsent(
+ entry.context(),
+ entry.key(),
+ val,
+ ver,
+ expireTime,
+ mvccVer,
+ newMvccVer,
+ txState,
+ newTxState);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean mvccUpdateRowWithPreloadInfo(
+ GridCacheMapEntry entry,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState
+ ) throws IgniteCheckedException {
+ assert entry.lockedByCurrentThread();
+
+ return dataStore(entry.localPartition()).mvccUpdateRowWithPreloadInfo(
+ entry.context(),
+ entry.key(),
+ val,
+ ver,
+ expireTime,
+ mvccVer,
+ newMvccVer,
+ mvccTxState,
+ newMvccTxState
+ );
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccUpdate(
+ GridCacheMapEntry entry,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException {
+ if (entry.detached() || entry.isNear())
+ return null;
+
+ assert entry.lockedByCurrentThread();
+
+ return dataStore(entry.localPartition()).mvccUpdate(entry.context(),
+ entry.key(),
+ val,
+ ver,
+ expireTime,
+ mvccSnapshot,
+ primary,
+ needHistory,
+ noCreate);
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccRemove(
+ GridCacheMapEntry entry,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory) throws IgniteCheckedException {
+ if (entry.detached() || entry.isNear())
+ return null;
+
+ assert entry.lockedByCurrentThread();
+
+ return dataStore(entry.localPartition()).mvccRemove(entry.context(),
+ entry.key(),
+ mvccSnapshot,
+ primary,
+ needHistory);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridLongList mvccUpdateNative(
+ boolean primary,
+ GridCacheMapEntry entry,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ if (entry.detached() || entry.isNear())
+ return null;
+
+ return dataStore(entry.localPartition()).mvccUpdateNative(entry.context(),
+ primary,
+ entry.key(),
+ val,
+ ver,
+ expireTime,
+ mvccSnapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridLongList mvccRemoveNative(
+ boolean primary,
+ GridCacheMapEntry entry,
+ MvccSnapshot mvccSnapshot
+ ) throws IgniteCheckedException {
+ if (entry.detached() || entry.isNear())
+ return null;
+
+ return dataStore(entry.localPartition()).mvccRemoveNative(entry.context(),
+ primary,
+ entry.key(),
+ mvccSnapshot);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void mvccRemoveAll(GridCacheMapEntry entry) throws IgniteCheckedException {
+ if (entry.detached() || entry.isNear())
+ return;
+
+ dataStore(entry.localPartition()).mvccRemoveAll(entry.context(), entry.key());
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public MvccUpdateResult mvccLock(GridCacheMapEntry entry,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ if (entry.detached() || entry.isNear())
+ return null;
+
+ assert entry.lockedByCurrentThread();
+
+ return dataStore(entry.localPartition()).mvccLock(entry.context(), entry.key(), mvccSnapshot);
+ }
+
+ /** {@inheritDoc} */
@Override public void remove(
GridCacheContext cctx,
KeyCacheObject key,
@@ -418,16 +624,25 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
- @Nullable @Override public CacheDataRow read(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
- CacheDataRow row;
+ @Nullable @Override public CacheDataRow read(GridCacheContext cctx, KeyCacheObject key)
+ throws IgniteCheckedException {
+ CacheDataStore dataStore = dataStore(cctx, key);
- if (cctx.isLocal())
- row = locCacheDataStore.find(cctx, key);
- else {
- GridDhtLocalPartition part = cctx.topology().localPartition(cctx.affinity().partition(key), null, false);
+ CacheDataRow row = dataStore != null ? dataStore.find(cctx, key) : null;
- row = part != null ? dataStore(part).find(cctx, key) : null;
- }
+ assert row == null || row.value() != null : row;
+
+ return row;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public CacheDataRow mvccRead(GridCacheContext cctx, KeyCacheObject key, MvccSnapshot ver)
+ throws IgniteCheckedException {
+ assert ver != null;
+
+ CacheDataStore dataStore = dataStore(cctx, key);
+
+ CacheDataRow row = dataStore != null ? dataStore.mvccFind(cctx, key, ver) : null;
assert row == null || row.value() != null : row;
@@ -435,6 +650,37 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
+ @Override public List<IgniteBiTuple<Object, MvccVersion>> mvccAllVersions(GridCacheContext cctx, KeyCacheObject key)
+ throws IgniteCheckedException {
+ CacheDataStore dataStore = dataStore(cctx, key);
+
+ return dataStore != null ? dataStore.mvccFindAllVersions(cctx, key) :
+ Collections.emptyList();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCursor<CacheDataRow> mvccAllVersionsCursor(GridCacheContext cctx,
+ KeyCacheObject key, Object x) throws IgniteCheckedException {
+ CacheDataStore dataStore = dataStore(cctx, key);
+
+ return dataStore != null ? dataStore.mvccAllVersionsCursor(cctx, key, x) : EMPTY_CURSOR;
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @return Data store.
+ */
+ @Nullable private CacheDataStore dataStore(GridCacheContext cctx, KeyCacheObject key) {
+ if (grp.isLocal())
+ return locCacheDataStore;
+
+ GridDhtLocalPartition part = grp.topology().localPartition(cctx.affinity().partition(key), null, false);
+
+ return part != null ? dataStore(part) : null;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean containsKey(GridCacheMapEntry entry) {
try {
return read(entry) != null;
@@ -470,7 +716,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
@Override public void clearCache(GridCacheContext cctx, boolean readers) {
GridCacheVersion obsoleteVer = null;
- try (GridCloseableIterator<CacheDataRow> it = grp.isLocal() ? iterator(cctx.cacheId(), cacheDataStores().iterator()) :
+ try (GridCloseableIterator<CacheDataRow> it = grp.isLocal() ? iterator(cctx.cacheId(), cacheDataStores().iterator(), null) :
evictionSafeIterator(cctx.cacheId(), cacheDataStores().iterator())) {
while (it.hasNext()) {
cctx.shared().database().checkpointReadLock();
@@ -529,7 +775,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
final boolean backup,
final AffinityTopologyVersion topVer,
final boolean keepBinary) throws IgniteCheckedException {
- final Iterator<CacheDataRow> it = cacheIterator(cctx.cacheId(), primary, backup, topVer);
+ final Iterator<CacheDataRow> it = cacheIterator(cctx.cacheId(), primary, backup, topVer, null);
return new GridCloseableIteratorAdapter<Cache.Entry<K, V>>() {
/** */
@@ -570,7 +816,8 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
- @Override public GridCloseableIterator<KeyCacheObject> cacheKeysIterator(int cacheId, final int part) throws IgniteCheckedException {
+ @Override public GridCloseableIterator<KeyCacheObject> cacheKeysIterator(int cacheId, final int part)
+ throws IgniteCheckedException {
CacheDataStore data = partitionData(part);
if (data == null)
@@ -611,19 +858,21 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
int cacheId,
boolean primary,
boolean backups,
- final AffinityTopologyVersion topVer)
+ final AffinityTopologyVersion topVer,
+ @Nullable MvccSnapshot mvccSnapshot)
throws IgniteCheckedException {
- return iterator(cacheId, cacheData(primary, backups, topVer));
+ return iterator(cacheId, cacheData(primary, backups, topVer), mvccSnapshot);
}
/** {@inheritDoc} */
- @Override public GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, int part) throws IgniteCheckedException {
+ @Override public GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, int part,
+ @Nullable MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
CacheDataStore data = partitionData(part);
if (data == null)
return new GridEmptyCloseableIterator<>();
- return iterator(cacheId, singletonIterator(data));
+ return iterator(cacheId, singletonIterator(data), mvccSnapshot);
}
/** {@inheritDoc} */
@@ -633,15 +882,20 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
if (data == null)
return new GridEmptyCloseableIterator<>();
- return iterator(CU.UNDEFINED_CACHE_ID, singletonIterator(data));
+ return iterator(CU.UNDEFINED_CACHE_ID, singletonIterator(data), null);
}
/**
+ *
* @param cacheId Cache ID.
* @param dataIt Data store iterator.
+ * @param mvccSnapshot Mvcc snapshot.
* @return Rows iterator
*/
- private GridCloseableIterator<CacheDataRow> iterator(final int cacheId, final Iterator<CacheDataStore> dataIt) {
+ private GridCloseableIterator<CacheDataRow> iterator(final int cacheId,
+ final Iterator<CacheDataStore> dataIt,
+ final MvccSnapshot mvccSnapshot)
+ {
return new GridCloseableIteratorAdapter<CacheDataRow>() {
/** */
private GridCursor<? extends CacheDataRow> cur;
@@ -670,7 +924,13 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
CacheDataStore ds = dataIt.next();
curPart = ds.partId();
- cur = cacheId == CU.UNDEFINED_CACHE_ID ? ds.cursor() : ds.cursor(cacheId);
+
+ if (mvccSnapshot == null)
+ cur = cacheId == CU.UNDEFINED_CACHE_ID ? ds.cursor() : ds.cursor(cacheId);
+ else {
+ cur = cacheId == CU.UNDEFINED_CACHE_ID ?
+ ds.cursor(mvccSnapshot) : ds.cursor(cacheId, mvccSnapshot);
+ }
}
else
break;
@@ -1142,6 +1402,13 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
/** Update counter. */
protected final AtomicLong cntr = new AtomicLong();
+ /**
+ * Mvcc update counter. This counter is used for an mvcc-style entries updates where this counter is
+ * incremented on each entry write (which happens before commit), but main update counter is updated
+ * on commit phase only.
+ */
+ protected final AtomicLong mvccUpdCntr = new AtomicLong();
+
/** Partition size. */
private final AtomicLong storageSize = new AtomicLong();
@@ -1151,6 +1418,15 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
/** Initial update counter. */
protected long initCntr;
+ /** Mvcc remove handler. */
+ private final PageHandler<MvccVersion, Boolean> mvccUpdateMarker = new MvccMarkUpdatedHandler();
+
+ /** Mvcc update tx state hint handler. */
+ private final PageHandler<Void, Boolean> mvccUpdateTxStateHint = new MvccUpdateTxStateHintHandler();
+
+ /** */
+ private final PageHandler<MvccDataRow, Boolean> mvccApplyChanges = new MvccApplyChangesHandler();
+
/**
* @param partId Partition number.
* @param name Name.
@@ -1240,13 +1516,16 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
+ @Override public long nextUpdateCounter() {
+ return cntr.incrementAndGet();
+ }
+
+ /** {@inheritDoc} */
@Override public long updateCounter() {
return cntr.get();
}
- /**
- * @param val Update index value.
- */
+ /** {@inheritDoc} */
@Override public void updateCounter(long val) {
while (true) {
long val0 = cntr.get();
@@ -1260,6 +1539,16 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
+ @Override public long nextMvccUpdateCounter() {
+ return mvccUpdCntr.incrementAndGet();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccUpdateCounter() {
+ return mvccUpdCntr.get();
+ }
+
+ /** {@inheritDoc} */
@Override public String name() {
return name;
}
@@ -1273,7 +1562,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
*/
private boolean canUpdateOldRow(GridCacheContext cctx, @Nullable CacheDataRow oldRow, DataRow dataRow)
throws IgniteCheckedException {
- if (oldRow == null || cctx.queries().enabled())
+ if (oldRow == null || cctx.queries().enabled() || grp.mvccEnabled())
return false;
if (oldRow.expireTime() != dataRow.expireTime())
@@ -1282,12 +1571,12 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
// Use grp.sharedGroup() flag since it is possible cacheId is not yet set here.
boolean sizeWithCacheId = grp.sharedGroup();
- int oldLen = DataPageIO.getRowSize(oldRow, sizeWithCacheId);
+ int oldLen = oldRow.size();
if (oldLen > updateValSizeThreshold)
return false;
- int newLen = DataPageIO.getRowSize(dataRow, sizeWithCacheId);
+ int newLen = dataRow.size();
return oldLen == newLen;
}
@@ -1295,15 +1584,26 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
/** {@inheritDoc} */
@Override public void invoke(GridCacheContext cctx, KeyCacheObject key, OffheapInvokeClosure c)
throws IgniteCheckedException {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ invoke0(cctx, new SearchRow(cacheId, key), c);
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param row Search row.
+ * @param c Closure.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClosure c)
+ throws IgniteCheckedException {
if (!busyLock.enterBusy())
throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
try {
- int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
-
assert cctx.shared().database().checkpointLockIsHeldByThread();
- dataTree.invoke(new SearchRow(cacheId, key), CacheDataRowAdapter.RowData.NO_KEY, c);
+ dataTree.invoke(row, CacheDataRowAdapter.RowData.NO_KEY, c);
switch (c.operationType()) {
case PUT: {
@@ -1319,7 +1619,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
case REMOVE: {
CacheDataRow oldRow = c.oldRow();
- finishRemove(cctx, key, oldRow);
+ finishRemove(cctx, row.key(), oldRow);
break;
}
@@ -1368,149 +1668,814 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
- @Override public void update(
+ @Override public boolean mvccInitialValue(
GridCacheContext cctx,
KeyCacheObject key,
- CacheObject val,
+ @Nullable CacheObject val,
GridCacheVersion ver,
long expireTime,
- @Nullable CacheDataRow oldRow
- ) throws IgniteCheckedException {
- assert oldRow == null || oldRow.link() != 0L : oldRow;
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer)
+ throws IgniteCheckedException
+ {
+ assert mvccVer != null || newMvccVer == null : newMvccVer;
if (!busyLock.enterBusy())
throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
try {
- int cacheId = grp.storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
-
- assert oldRow == null || oldRow.cacheId() == cacheId : oldRow;
-
- if (key.partition() == -1)
- key.partition(partId);
-
- DataRow dataRow = new DataRow(key, val, ver, partId, expireTime, cacheId);
-
CacheObjectContext coCtx = cctx.cacheObjectContext();
// Make sure value bytes initialized.
key.valueBytes(coCtx);
- val.valueBytes(coCtx);
- CacheDataRow old;
+ // null is passed for loaded from store.
+ if (mvccVer == null) {
+ mvccVer = INITIAL_VERSION;
- assert cctx.shared().database().checkpointLockIsHeldByThread();
+ // Clean all versions of row
+ mvccRemoveAll(cctx, key);
+ }
- if (canUpdateOldRow(cctx, oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow)) {
- old = oldRow;
+ if (val != null) {
+ val.valueBytes(coCtx);
- dataRow.link(oldRow.link());
- }
- else {
- rowStore.addRow(dataRow);
+ MvccDataRow updateRow = new MvccDataRow(
+ key,
+ val,
+ ver,
+ partId,
+ expireTime,
+ cctx.cacheId(),
+ mvccVer,
+ newMvccVer);
- assert dataRow.link() != 0 : dataRow;
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
- if (grp.sharedGroup() && dataRow.cacheId() == CU.UNDEFINED_CACHE_ID)
- dataRow.cacheId(cctx.cacheId());
+ if (!grp.storeCacheIdInDataPage() && updateRow.cacheId() != CU.UNDEFINED_CACHE_ID) {
+ updateRow.cacheId(CU.UNDEFINED_CACHE_ID);
- if (oldRow != null) {
- old = oldRow;
+ rowStore.addRow(updateRow);
- dataTree.putx(dataRow);
+ updateRow.cacheId(cctx.cacheId());
}
else
- old = dataTree.put(dataRow);
- }
+ rowStore.addRow(updateRow);
- finishUpdate(cctx, dataRow, old);
+ dataTree.putx(updateRow);
+
+ incrementSize(cctx.cacheId());
+
+ if (cctx.queries().enabled())
+ cctx.queries().store(updateRow, null, true);
+
+ return true;
+ }
}
finally {
busyLock.leaveBusy();
}
- }
- /**
- * @param cctx Cache context.
- * @param newRow New row.
- * @param oldRow Old row if available.
- * @throws IgniteCheckedException If failed.
- */
- private void finishUpdate(GridCacheContext cctx, CacheDataRow newRow, @Nullable CacheDataRow oldRow)
- throws IgniteCheckedException {
- if (oldRow == null)
- incrementSize(cctx.cacheId());
+ return false;
+ }
- KeyCacheObject key = newRow.key();
+ /** {@inheritDoc} */
+ @Override public boolean mvccInitialValueIfAbsent(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte txState,
+ byte newTxState)
+ throws IgniteCheckedException
+ {
+ assert mvccVer != null;
- long expireTime = newRow.expireTime();
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
- GridCacheQueryManager qryMgr = cctx.queries();
+ try {
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
- int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
- if (qryMgr.enabled())
- qryMgr.store(newRow, oldRow, true);
+ if (val != null)
+ val.valueBytes(coCtx);
- if (oldRow != null) {
- assert oldRow.link() != 0 : oldRow;
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
- if (pendingTree() != null && oldRow.expireTime() != 0)
- pendingTree().removex(new PendingRow(cacheId, oldRow.expireTime(), oldRow.link()));
+ MvccPutIfAbsentClosure clo = new MvccPutIfAbsentClosure(key,
+ val,
+ ver,
+ partId,
+ expireTime,
+ cctx.cacheId(),
+ mvccVer,
+ newMvccVer,
+ txState,
+ newTxState);
- if (newRow.link() != oldRow.link())
- rowStore.removeRow(oldRow.link());
- }
+ dataTree.invoke(clo, CacheDataRowAdapter.RowData.LINK_ONLY, clo);
- if (pendingTree() != null && expireTime != 0) {
- pendingTree().putx(new PendingRow(cacheId, expireTime, newRow.link()));
+ if (clo.operationType() == PUT)
+ finishUpdate(cctx, clo, null);
- hasPendingEntries = true;
+ return clo.operationType() == PUT;
+ }
+ finally {
+ busyLock.leaveBusy();
}
-
- updateIgfsMetrics(cctx, key, (oldRow != null ? oldRow.value() : null), newRow.value());
}
/** {@inheritDoc} */
- @Override public void remove(GridCacheContext cctx, KeyCacheObject key, int partId) throws IgniteCheckedException {
+ @Override public boolean mvccUpdateRowWithPreloadInfo(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState) throws IgniteCheckedException {
if (!busyLock.enterBusy())
throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
try {
- int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
- assert cctx.shared().database().checkpointLockIsHeldByThread();
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
- CacheDataRow oldRow = dataTree.remove(new SearchRow(cacheId, key));
+ if (val != null)
+ val.valueBytes(coCtx);
- finishRemove(cctx, key, oldRow);
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ MvccUpdateRowWithPreloadInfoClosure clo = new MvccUpdateRowWithPreloadInfoClosure(cctx,
+ key,
+ val,
+ ver,
+ expireTime,
+ mvccVer,
+ newMvccVer,
+ mvccTxState,
+ newMvccTxState);
+
+ invoke0(cctx, clo, clo);
}
finally {
busyLock.leaveBusy();
}
+
+ return true;
}
- /**
- * @param cctx Cache context.
- * @param key Key.
- * @param oldRow Removed row.
- * @throws IgniteCheckedException If failed.
- */
- private void finishRemove(GridCacheContext cctx, KeyCacheObject key, @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
- if (oldRow != null) {
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccUpdate(
+ GridCacheContext cctx,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException {
+ assert mvccSnapshot != null;
+ assert primary || !needHistory;
+
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
- assert oldRow.link() != 0 : oldRow;
- assert cacheId == CU.UNDEFINED_CACHE_ID || oldRow.cacheId() == cacheId :
- "Incorrect cache ID [expected=" + cacheId + ", actual=" + oldRow.cacheId() + "].";
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
- if (pendingTree() != null && oldRow.expireTime() != 0)
- pendingTree().removex(new PendingRow(cacheId, oldRow.expireTime(), oldRow.link()));
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
+ val.valueBytes(coCtx);
- decrementSize(cctx.cacheId());
- }
+ MvccUpdateDataRow updateRow = new MvccUpdateDataRow(
+ cctx,
+ key,
+ val,
+ ver,
+ partId,
+ expireTime,
+ mvccSnapshot,
+ null,
+ primary,
+ false,
+ needHistory,
+ // we follow fast update visit flow here if row cannot be created by current operation
+ noCreate);
- GridCacheQueryManager qryMgr = cctx.queries();
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ dataTree.visit(new MvccMaxSearchRow(cacheId, key), new MvccMinSearchRow(cacheId, key), updateRow);
+
+ ResultType res = updateRow.resultType();
+
+ if (res == ResultType.LOCKED // cannot update locked
+ || res == ResultType.VERSION_MISMATCH) // cannot update on write conflict
+ return updateRow;
+ else if (res == ResultType.VERSION_FOUND) {
+ // Do nothing, except cleaning up not needed versions
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow;
+ }
+ else if (res == ResultType.PREV_NULL && noCreate) {
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow;
+ }
+
+ CacheDataRow oldRow = null;
+
+ if (res == ResultType.PREV_NOT_NULL) {
+ oldRow = updateRow.oldRow();
+
+ assert oldRow != null && oldRow.link() != 0 : oldRow;
+
+ oldRow.key(key);
+
+ rowStore.updateDataRow(oldRow.link(), mvccUpdateMarker, mvccSnapshot);
+ }
+ else
+ assert res == ResultType.PREV_NULL;
+
+ if (!grp.storeCacheIdInDataPage() && updateRow.cacheId() != CU.UNDEFINED_CACHE_ID) {
+ updateRow.cacheId(CU.UNDEFINED_CACHE_ID);
+
+ rowStore.addRow(updateRow);
+
+ updateRow.cacheId(cctx.cacheId());
+ }
+ else
+ rowStore.addRow(updateRow);
+
+ if (needHistory) {
+ assert updateRow.link() != 0;
+
+ updateRow.history().add(new MvccLinkAwareSearchRow(cacheId,
+ key,
+ updateRow.mvccCoordinatorVersion(),
+ updateRow.mvccCounter(),
+ updateRow.mvccOperationCounter(),
+ updateRow.link()));
+ }
+
+ boolean old = dataTree.putx(updateRow);
+
+ assert !old;
+
+ incrementSize(cctx.cacheId());
+
+ GridCacheQueryManager qryMgr = cctx.queries();
+
+ if (qryMgr.enabled())
+ qryMgr.store(updateRow, null, true);
+
+ updatePendingEntries(cctx, updateRow, oldRow);
+
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow;
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccRemove(GridCacheContext cctx,
+ KeyCacheObject key,
+ MvccSnapshot mvccSnapshot,
+ boolean primary,
+ boolean needHistory) throws IgniteCheckedException {
+ assert mvccSnapshot != null;
+ assert primary || mvccSnapshot.activeTransactions().size() == 0 : mvccSnapshot;
+ assert primary || !needHistory;
+
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
+
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
+
+ MvccUpdateDataRow updateRow = new MvccUpdateDataRow(
+ cctx,
+ key,
+ null,
+ null,
+ partId,
+ 0,
+ mvccSnapshot,
+ null,
+ primary,
+ false,
+ needHistory,
+ true);
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ dataTree.visit(new MvccMaxSearchRow(cacheId, key), new MvccMinSearchRow(cacheId, key), updateRow);
+
+ ResultType res = updateRow.resultType();
+
+ if (res == ResultType.LOCKED // cannot update locked
+ || res == ResultType.VERSION_MISMATCH) // cannot update on write conflict
+ return updateRow;
+ else if (res == ResultType.VERSION_FOUND) {
+ // Do nothing, except cleaning up not needed versions
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow;
+ }
+ else if (res == ResultType.PREV_NOT_NULL) {
+ CacheDataRow oldRow = updateRow.oldRow();
+
+ assert oldRow != null && oldRow.link() != 0 : oldRow;
+
+ rowStore.updateDataRow(oldRow.link(), mvccUpdateMarker, mvccSnapshot);
+
+ clearPendingEntries(cctx, oldRow);
+ }
+
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow;
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccUpdateResult mvccLock(GridCacheContext cctx, KeyCacheObject key,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ assert mvccSnapshot != null;
+
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
+
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
+
+ MvccUpdateDataRow updateRow = new MvccUpdateDataRow(
+ cctx,
+ key,
+ null,
+ null,
+ partId,
+ 0,
+ mvccSnapshot,
+ null,
+ true,
+ true,
+ false,
+ false);
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ dataTree.visit(new MvccMaxSearchRow(cacheId, key), new MvccMinSearchRow(cacheId, key), updateRow);
+
+ ResultType res = updateRow.resultType();
+
+ // cannot update locked, cannot update on write conflict
+ if (res == ResultType.LOCKED || res == ResultType.VERSION_MISMATCH)
+ return updateRow;
+
+ // Do nothing, except cleaning up not needed versions
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow;
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridLongList mvccUpdateNative(
+ GridCacheContext cctx,
+ boolean primary,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ assert mvccSnapshot != null;
+ assert primary || mvccSnapshot.activeTransactions().size() == 0 : mvccSnapshot;
+
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
+
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
+ val.valueBytes(coCtx);
+
+ MvccUpdateDataRowNative updateRow = new MvccUpdateDataRowNative(
+ key,
+ val,
+ ver,
+ expireTime,
+ mvccSnapshot,
+ null,
+ partId,
+ cctx);
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ dataTree.iterate(new MvccMaxSearchRow(cacheId, key), new MvccMinSearchRow(cacheId, key), updateRow);
+
+ ResultType res = updateRow.resultType();
+
+ if (res == ResultType.VERSION_FOUND) {
+ // Do nothing, except cleaning up not needed versions
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return null;
+ }
+
+ CacheDataRow oldRow = null;
+
+ if (res == ResultType.PREV_NOT_NULL) {
+ oldRow = updateRow.oldRow();
+
+ assert oldRow != null && oldRow.link() != 0 : oldRow;
+
+ oldRow.key(key);
+
+ rowStore.updateDataRow(oldRow.link(), mvccUpdateMarker, mvccSnapshot);
+ }
+ else
+ assert res == ResultType.PREV_NULL;
+
+ if (!grp.storeCacheIdInDataPage() && updateRow.cacheId() != CU.UNDEFINED_CACHE_ID) {
+ updateRow.cacheId(CU.UNDEFINED_CACHE_ID);
+
+ rowStore.addRow(updateRow);
+
+ updateRow.cacheId(cctx.cacheId());
+ }
+ else
+ rowStore.addRow(updateRow);
+
+ boolean old = dataTree.putx(updateRow);
+
+ assert !old;
+
+ incrementSize(cctx.cacheId());
+
+ GridCacheQueryManager qryMgr = cctx.queries();
+
+ if (qryMgr.enabled())
+ qryMgr.store(updateRow, null, true);
+
+ updatePendingEntries(cctx, updateRow, oldRow);
+
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow.activeTransactions();
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridLongList mvccRemoveNative(GridCacheContext cctx,
+ boolean primary,
+ KeyCacheObject key,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ assert mvccSnapshot != null;
+ assert primary || mvccSnapshot.activeTransactions().size() == 0 : mvccSnapshot;
+
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
+
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
+
+ MvccUpdateDataRowNative updateRow = new MvccUpdateDataRowNative(
+ key,
+ null,
+ null,
+ 0,
+ mvccSnapshot,
+ null,
+ partId,
+ cctx);
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ dataTree.iterate(new MvccMaxSearchRow(cacheId, key) , new MvccMinSearchRow(cacheId, key), updateRow);
+
+ ResultType res = updateRow.resultType();
+
+ if (res == ResultType.VERSION_FOUND) {
+ assert !primary : updateRow;
+
+ // Do nothing, except cleaning up not needed versions
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return null;
+ }
+ else if (res == ResultType.PREV_NOT_NULL) {
+ CacheDataRow oldRow = updateRow.oldRow();
+
+ assert oldRow != null && oldRow.link() != 0 : oldRow;
+
+ rowStore.updateDataRow(oldRow.link(), mvccUpdateMarker, mvccSnapshot);
+
+ clearPendingEntries(cctx, oldRow);
+ }
+
+ cleanup(cctx, updateRow.cleanupRows());
+
+ return updateRow.activeTransactions();
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void mvccRemoveAll(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
+ key.valueBytes(cctx.cacheObjectContext());
+
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ boolean cleanup = cctx.queries().enabled() || hasPendingEntries;
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ GridCursor<CacheDataRow> cur = dataTree.find(
+ new MvccMaxSearchRow(cacheId, key),
+ new MvccMinSearchRow(cacheId, key),
+ cleanup ? CacheDataRowAdapter.RowData.NO_KEY : CacheDataRowAdapter.RowData.LINK_ONLY
+ );
+
+ boolean first = true;
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ row.key(key);
+
+ assert row.link() != 0 : row;
+
+ boolean rmvd = dataTree.removex(row);
+
+ assert rmvd : row;
+
+ if (cleanup) {
+ if (cctx.queries().enabled())
+ cctx.queries().remove(key, row);
+
+ if (first)
+ clearPendingEntries(cctx, row);
+ }
+
+ rowStore.removeRow(row.link());
+
+ decrementSize(cctx.cacheId());
+
+ if (first)
+ first = false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public int cleanup(GridCacheContext cctx, @Nullable List<MvccLinkAwareSearchRow> cleanupRows)
+ throws IgniteCheckedException {
+ int res = 0;
+
+ if (cleanupRows != null) {
+ GridCacheQueryManager qryMgr = cctx.queries();
+
+ for (int i = 0; i < cleanupRows.size(); i++) {
+ MvccLinkAwareSearchRow cleanupRow = cleanupRows.get(i);
+
+ assert cleanupRow.link() != 0 : cleanupRow;
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ CacheDataRow oldRow = dataTree.remove(cleanupRow);
+
+ if (oldRow != null) { // oldRow == null means it was cleaned by another cleanup process.
+ assert oldRow.mvccCounter() == cleanupRow.mvccCounter();
+
+ if (qryMgr.enabled())
+ qryMgr.remove(oldRow.key(), oldRow);
+
+ clearPendingEntries(cctx, oldRow);
+
+ rowStore.removeRow(cleanupRow.link());
+
+ decrementSize(cctx.cacheId());
+
+ res++;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateTxState(GridCacheContext cctx, CacheSearchRow row)
+ throws IgniteCheckedException {
+ assert grp.mvccEnabled();
+ assert mvccVersionIsValid(row.mvccCoordinatorVersion(), row.mvccCounter(), row.mvccOperationCounter()) : row;
+
+ // Need an extra lookup because the row may be already cleaned by another thread.
+ CacheDataRow row0 = dataTree.findOne(row, CacheDataRowAdapter.RowData.LINK_ONLY);
+
+ if (row0 != null)
+ rowStore.updateDataRow(row0.link(), mvccUpdateTxStateHint, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void update(GridCacheContext cctx,
+ KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ @Nullable CacheDataRow oldRow
+ ) throws IgniteCheckedException {
+ assert oldRow == null || oldRow.link() != 0L : oldRow;
+
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
+ int cacheId = grp.storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ assert oldRow == null || oldRow.cacheId() == cacheId : oldRow;
+
+ if (key.partition() == -1)
+ key.partition(partId);
+
+ DataRow dataRow = new DataRow(key, val, ver, partId, expireTime, cacheId);
+
+ CacheObjectContext coCtx = cctx.cacheObjectContext();
+
+ // Make sure value bytes initialized.
+ key.valueBytes(coCtx);
+ val.valueBytes(coCtx);
+
+ CacheDataRow old;
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ if (canUpdateOldRow(cctx, oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow)) {
+ old = oldRow;
+
+ dataRow.link(oldRow.link());
+ }
+ else {
+ rowStore.addRow(dataRow);
+
+ assert dataRow.link() != 0 : dataRow;
+
+ if (grp.sharedGroup() && dataRow.cacheId() == CU.UNDEFINED_CACHE_ID)
+ dataRow.cacheId(cctx.cacheId());
+
+ if (oldRow != null) {
+ old = oldRow;
+
+ dataTree.putx(dataRow);
+ }
+ else
+ old = dataTree.put(dataRow);
+ }
+
+ finishUpdate(cctx, dataRow, old);
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param newRow New row.
+ * @param oldRow Old row if available.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void finishUpdate(GridCacheContext cctx, CacheDataRow newRow, @Nullable CacheDataRow oldRow)
+ throws IgniteCheckedException {
+ if (oldRow == null)
+ incrementSize(cctx.cacheId());
+
+ KeyCacheObject key = newRow.key();
+
+ GridCacheQueryManager qryMgr = cctx.queries();
+
+ if (qryMgr.enabled())
+ qryMgr.store(newRow, oldRow, true);
+
+ updatePendingEntries(cctx, newRow, oldRow);
+
+ if (oldRow != null) {
+ assert oldRow.link() != 0 : oldRow;
+
+ if (newRow.link() != oldRow.link())
+ rowStore.removeRow(oldRow.link());
+ }
+
+ updateIgfsMetrics(cctx, key, (oldRow != null ? oldRow.value() : null), newRow.value());
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param newRow New row.
+ * @param oldRow Old row.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void updatePendingEntries(GridCacheContext cctx, CacheDataRow newRow, @Nullable CacheDataRow oldRow)
+ throws IgniteCheckedException
+ {
+ long expireTime = newRow.expireTime();
+
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ if (oldRow != null) {
+ assert oldRow.link() != 0 : oldRow;
+
+ if (pendingTree() != null && oldRow.expireTime() != 0)
+ pendingTree().removex(new PendingRow(cacheId, oldRow.expireTime(), oldRow.link()));
+ }
+
+ if (pendingTree() != null && expireTime != 0) {
+ pendingTree().putx(new PendingRow(cacheId, expireTime, newRow.link()));
+
+ hasPendingEntries = true;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void remove(GridCacheContext cctx, KeyCacheObject key, int partId) throws IgniteCheckedException {
+ if (!busyLock.enterBusy())
+ throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
+
+ try {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ assert cctx.shared().database().checkpointLockIsHeldByThread();
+
+ CacheDataRow oldRow = dataTree.remove(new SearchRow(cacheId, key));
+
+ finishRemove(cctx, key, oldRow);
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param oldRow Removed row.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void finishRemove(GridCacheContext cctx, KeyCacheObject key, @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
+ if (oldRow != null) {
+ clearPendingEntries(cctx, oldRow);
+
+ decrementSize(cctx.cacheId());
+ }
+
+ GridCacheQueryManager qryMgr = cctx.queries();
if (qryMgr.enabled())
qryMgr.remove(key, oldRow);
@@ -1521,21 +2486,129 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
updateIgfsMetrics(cctx, key, (oldRow != null ? oldRow.value() : null), null);
}
+ /**
+ * @param cctx Cache context.
+ * @param oldRow Old row.
+ * @throws IgniteCheckedException
+ */
+ private void clearPendingEntries(GridCacheContext cctx, CacheDataRow oldRow)
+ throws IgniteCheckedException {
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ assert oldRow.link() != 0 : oldRow;
+ assert cacheId == CU.UNDEFINED_CACHE_ID || oldRow.cacheId() == cacheId :
+ "Incorrect cache ID [expected=" + cacheId + ", actual=" + oldRow.cacheId() + "].";
+
+ if (pendingTree() != null && oldRow.expireTime() != 0)
+ pendingTree().removex(new PendingRow(cacheId, oldRow.expireTime(), oldRow.link()));
+ }
+
/** {@inheritDoc} */
@Override public CacheDataRow find(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
key.valueBytes(cctx.cacheObjectContext());
int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
- CacheDataRow row = dataTree.findOne(new SearchRow(cacheId, key), CacheDataRowAdapter.RowData.NO_KEY);
+ CacheDataRow row;
+
+ if (grp.mvccEnabled()) {
+ MvccFirstRowTreeClosure clo = new MvccFirstRowTreeClosure(cctx);
+
+ dataTree.iterate(
+ new MvccMaxSearchRow(cacheId, key),
+ new MvccMinSearchRow(cacheId, key),
+ clo
+ );
+
+ row = clo.row();
+ }
+ else
+ row = dataTree.findOne(new SearchRow(cacheId, key), CacheDataRowAdapter.RowData.NO_KEY);
+
+ afterRowFound(row, key);
+
+ return row;
+ }
+
+ /** {@inheritDoc} */
+ @Override public List<IgniteBiTuple<Object, MvccVersion>> mvccFindAllVersions(
+ GridCacheContext cctx,
+ KeyCacheObject key)
+ throws IgniteCheckedException
+ {
+ assert grp.mvccEnabled();
+
+ // Note: this method is intended for testing only.
+
+ key.valueBytes(cctx.cacheObjectContext());
+
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ GridCursor<CacheDataRow> cur = dataTree.find(
+ new MvccMaxSearchRow(cacheId, key),
+ new MvccMinSearchRow(cacheId, key)
+ );
+
+ List<IgniteBiTuple<Object, MvccVersion>> res = new ArrayList<>();
+
+ long crd = MVCC_CRD_COUNTER_NA, cntr = MVCC_COUNTER_NA; int opCntr = MVCC_OP_COUNTER_NA;
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ if (compareNewVersion(row, crd, cntr, opCntr) != 0) // deleted row
+ res.add(F.t(null, row.newMvccVersion()));
+
+ res.add(F.t(row.value(), row.mvccVersion()));
+
+ crd = row.mvccCoordinatorVersion(); cntr = row.mvccCounter(); opCntr = row.mvccOperationCounter();
+ }
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCursor<CacheDataRow> mvccAllVersionsCursor(GridCacheContext cctx, KeyCacheObject key, Object x)
+ throws IgniteCheckedException {
+ int cacheId = cctx.cacheId();
+
+ return dataTree.find(new MvccMaxSearchRow(cacheId, key), new MvccMinSearchRow(cacheId, key), x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public CacheDataRow mvccFind(GridCacheContext cctx,
+ KeyCacheObject key,
+ MvccSnapshot snapshot) throws IgniteCheckedException {
+ key.valueBytes(cctx.cacheObjectContext());
+
+ int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID;
+
+ MvccSnapshotSearchRow clo = new MvccSnapshotSearchRow(cctx, key, snapshot);
+
+ dataTree.iterate(
+ clo,
+ new MvccMinSearchRow(cacheId, key),
+ clo
+ );
+
+ CacheDataRow row = clo.row();
+
+ afterRowFound(row, key);
+ return row;
+ }
+
+ /**
+ * @param row Row.
+ * @param key Key.
+ * @throws IgniteCheckedException If failed.
+ */
+ private void afterRowFound(@Nullable CacheDataRow row, KeyCacheObject key) throws IgniteCheckedException {
if (row != null) {
row.key(key);
grp.dataRegion().evictionTracker().touchPage(row.link());
}
-
- return row;
}
/** {@inheritDoc} */
@@ -1544,10 +2617,36 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
}
/** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(Object x) throws IgniteCheckedException {
+ return dataTree.find(null, null, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(MvccSnapshot mvccSnapshot)
+ throws IgniteCheckedException {
+
+ if (mvccSnapshot != null) {
+ assert grp.mvccEnabled();
+
+ return dataTree.find(null, null,
+ new MvccFirstVisibleRowTreeClosure(grp.singleCacheContext(), mvccSnapshot), null);
+ }
+
+ return dataTree.find(null, null);
+ }
+
+ /** {@inheritDoc} */
@Override public GridCursor<? extends CacheDataRow> cursor(int cacheId) throws IgniteCheckedException {
return cursor(cacheId, null, null);
}
+ /** {@inheritDoc}
+ * @param cacheId*/
+ @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId,
+ MvccSnapshot mvccSnapshot) throws IgniteCheckedException {
+ return cursor(cacheId, null, null, null, mvccSnapshot);
+ }
+
/** {@inheritDoc} */
@Override public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
KeyCacheObject upper) throws IgniteCheckedException {
@@ -1557,6 +2656,12 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
/** {@inheritDoc} */
@Override public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
KeyCacheObject upper, Object x) throws IgniteCheckedException {
+ return cursor(cacheId, lower, upper, null, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
+ KeyCacheObject upper, Object x, MvccSnapshot snapshot) throws IgniteCheckedException {
SearchRow lowerRow;
SearchRow upperRow;
@@ -1571,6 +2676,15 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
upperRow = upper != null ? new SearchRow(CU.UNDEFINED_CACHE_ID, upper) : null;
}
+ if (snapshot != null) {
+ assert grp.mvccEnabled();
+
+ GridCacheContext cctx =
+ grp.sharedGroup() ? grp.shared().cacheContext(cacheId) : grp.singleCacheContext();
+
+ return dataTree.find(lowerRow, upperRow, new MvccFirstVisibleRowTreeClosure(cctx, snapshot), x);
+ }
+
return dataTree.find(lowerRow, upperRow, x);
}
@@ -1645,13 +2759,6 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
return rowStore;
}
- /**
- * @return Next update index.
- */
- @Override public long nextUpdateCounter() {
- return cntr.incrementAndGet();
- }
-
/** {@inheritDoc} */
@Override public long initialUpdateCounter() {
return initCntr;
@@ -1739,5 +2846,337 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
else
return 0;
}
+
+ /** */
+ private final class MvccFirstVisibleRowTreeClosure implements MvccTreeClosure {
+ /** */
+ private final GridCacheContext cctx;
+
+ /** */
+ private final MvccSnapshot snapshot;
+
+ /**
+ *
+ * @param cctx Cache context.
+ * @param snapshot MVCC snapshot.
+ */
+ MvccFirstVisibleRowTreeClosure(GridCacheContext cctx, MvccSnapshot snapshot) {
+ this.cctx = cctx;
+ this.snapshot = snapshot;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean apply(BPlusTree<CacheSearchRow, CacheDataRow> tree, BPlusIO<CacheSearchRow> io,
+ long pageAddr, int idx) throws IgniteCheckedException {
+ RowLinkIO rowIo = (RowLinkIO)io;
+
+ long rowCrdVer = rowIo.getMvccCoordinatorVersion(pageAddr, idx);
+ long rowCntr = rowIo.getMvccCounter(pageAddr, idx);
+ int rowOpCntr = rowIo.getMvccOperationCounter(pageAddr, idx);
+
+ assert mvccVersionIsValid(rowCrdVer, rowCntr, rowOpCntr);
+
+ return isVisible(cctx, snapshot, rowCrdVer, rowCntr, rowOpCntr, rowIo.getLink(pageAddr, idx));
+ }
+ }
+
+ /**
+ * Put row if it doesn't exist yet.
+ */
+ private class MvccPutIfAbsentClosure extends MvccDataRow implements IgniteTree.InvokeClosure<CacheDataRow> {
+ /** */
+ private IgniteTree.OperationType op;
+
+ /**
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param part Partition.
+ * @param expireTime Expire time.
+ * @param cacheId Cache ID.
+ * @param mvccVer Mvcc version.
+ * @param newMvccVer New mvcc version.
+ * @param txState Tx state hint for mvcc version.
+ * @param newTxState Tx state hint for new mvcc version.
+ */
+ MvccPutIfAbsentClosure(KeyCacheObject key,
+ CacheObject val,
+ GridCacheVersion ver,
+ int part,
+ long expireTime,
+ int cacheId,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte txState,
+ byte newTxState) {
+ super(key, val, ver, part, expireTime, cacheId, mvccVer, newMvccVer);
+
+ mvccTxState(txState);
+ newMvccTxState(newTxState);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void call(@Nullable CacheDataRow old) throws IgniteCheckedException {
+ if (old == null) {
+ op = PUT;
+
+ int cacheId = cacheId();
+
+ if (!grp.storeCacheIdInDataPage() && cacheId != CU.UNDEFINED_CACHE_ID)
+ cacheId(CU.UNDEFINED_CACHE_ID);
+
+ rowStore().addRow(this);
+
+ cacheId(cacheId);
+ }
+ else
+ op = NOOP;
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccDataRow newRow() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteTree.OperationType operationType() {
+ return op;
+ }
+ }
+
+ /**
+ *
+ */
+ private class MvccUpdateRowWithPreloadInfoClosure extends MvccDataRow implements OffheapInvokeClosure {
+ /** */
+ private CacheDataRow oldRow;
+ /** */
+ private IgniteTree.OperationType op;
+
+ /**
+ * @param cctx Cache context.
+ * @param key Key.
+ * @param val Value.
+ * @param ver Version.
+ * @param expireTime Expire time.
+ * @param mvccVer Mvcc created version.
+ * @param newMvccVer Mvcc updated version.
+ * @param mvccTxState Mvcc Tx state hint.
+ * @param newMvccTxState New Mvcc Tx state hint.
+ */
+ MvccUpdateRowWithPreloadInfoClosure(GridCacheContext cctx,
+ KeyCacheObject key,
+ @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expireTime,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState) {
+ super(key,
+ val,
+ ver,
+ CacheDataStoreImpl.this.partId(),
+ expireTime,
+ cctx.cacheId(),
+ mvccVer,
+ newMvccVer);
+
+ mvccTxState(mvccTxState);
+ newMvccTxState(newMvccTxState);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public CacheDataRow oldRow() {
+ return oldRow;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void call(@Nullable CacheDataRow oldRow) throws IgniteCheckedException {
+ this.oldRow = oldRow;
+
+ if (oldRow == null) {
+ op = PUT;
+
+ int cacheId = cacheId();
+
+ if (!grp.storeCacheIdInDataPage() && cacheId != CU.UNDEFINED_CACHE_ID)
+ cacheId(CU.UNDEFINED_CACHE_ID);
+
+ rowStore().addRow(this);
+
+ cacheId(cacheId);
+ }
+ else {
+ op = NOOP;
+
+ if (oldRow.mvccTxState() != mvccTxState() ||
+ oldRow.newMvccCoordinatorVersion() != newMvccCoordinatorVersion() ||
+ oldRow.newMvccCounter() != newMvccCounter() ||
+ oldRow.newMvccOperationCounter() != newMvccOperationCounter() ||
+ oldRow.newMvccTxState() != newMvccTxState()) {
+
+ rowStore().updateDataRow(oldRow.link(), mvccApplyChanges, this);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public CacheDataRow newRow() {
+ return op == PUT ? this : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteTree.OperationType operationType() {
+ return op == null ? NOOP : op;
+ }
+ }
+ }
+
+ /**
+ * Mvcc remove handler.
+ */
+ private final class MvccMarkUpdatedHandler extends PageHandler<MvccVersion, Boolean> {
+ /** {@inheritDoc} */
+ @Override public Boolean run(int cacheId, long pageId, long page, long pageAddr, PageIO io, Boolean walPlc,
+ MvccVersion newVer, int itemId) throws IgniteCheckedException {
+ assert grp.mvccEnabled();
+
+ DataPageIO iox = (DataPageIO)io;
+
+ int offset = iox.getPayloadOffset(pageAddr, itemId, grp.dataRegion().pageMemory().pageSize(), MVCC_INFO_SIZE);
+
+ long newCrd = iox.newMvccCoordinator(pageAddr, offset);
+ long newCntr = iox.newMvccCounter(pageAddr, offset);
+ int newOpCntr = iox.newMvccOperationCounter(pageAddr, offset);
+
+ assert newCrd == MVCC_CRD_COUNTER_NA || state(grp, newCrd, newCntr, newOpCntr) == TxState.ABORTED;
+
+ iox.updateNewVersion(pageAddr, offset, newVer, TxState.NA);
+
+ if (isWalDeltaRecordNeeded(grp.dataRegion().pageMemory(), cacheId, pageId, page, ctx.wal(), walPlc))
+ ctx.wal().log(new DataPageMvccMarkUpdatedRecord(cacheId, pageId, itemId,
+ newVer.coordinatorVersion(), newVer.counter(), newVer.operationCounter()));
+
+ return Boolean.TRUE;
+ }
+ }
+
+ /**
+ * Mvcc update operation counter hints handler.
+ */
+ private final class MvccUpdateTxStateHintHandler extends PageHandler<Void, Boolean> {
+ /** {@inheritDoc} */
+ @Override public Boolean run(int cacheId, long pageId, long page, long pageAddr, PageIO io,
+ Boolean walPlc, Void ignore,
+ int itemId) throws IgniteCheckedException {
+
+ DataPageIO iox = (DataPageIO)io;
+
+ int pageSize = grp.dataRegion().pageMemory().pageSize();
+ int offset = iox.getPayloadOffset(pageAddr, itemId, pageSize, MVCC_INFO_SIZE);
+
+ long crd = iox.mvccCoordinator(pageAddr, offset);
+ long cntr = iox.mvccCounter(pageAddr, offset);
+ int opCntr = iox.mvccOperationCounter(pageAddr, offset);
+ byte txState = (byte)(opCntr >>> MVCC_HINTS_BIT_OFF);
+
+ if (txState == TxState.NA) {
+ byte state = state(grp, crd, cntr, opCntr);
+
+ if (state == TxState.COMMITTED || state == TxState.ABORTED) {
+ iox.mvccOperationCounter(pageAddr, offset, opCntr | (state << MVCC_HINTS_BIT_OFF));
+
+ if (isWalDeltaRecordNeeded(grp.dataRegion().pageMemory(), cacheId, pageId, page, ctx.wal(), walPlc))
+ ctx.wal().log(new DataPageMvccUpdateTxStateHintRecord(cacheId, pageId, itemId, state));
+ }
+ else
+ throw unexpectedStateException(grp, state, crd, cntr, opCntr);
+ }
+
+ long newCrd = iox.newMvccCoordinator(pageAddr, offset);
+ long newCntr = iox.newMvccCounter(pageAddr, offset);
+ int newOpCntr = iox.newMvccOperationCounter(pageAddr, offset);
+ byte newTxState = (byte)(newOpCntr >>> MVCC_HINTS_BIT_OFF);
+
+ if (newCrd != MVCC_CRD_COUNTER_NA && newTxState == TxState.NA) {
+ byte state = state(grp, newCrd, newCntr, newOpCntr);
+
+ if (state == TxState.COMMITTED || state == TxState.ABORTED) {
+ iox.newMvccOperationCounter(pageAddr, offset, newOpCntr | (state << MVCC_HINTS_BIT_OFF));
+
+ if (isWalDeltaRecordNeeded(grp.dataRegion().pageMemory(), cacheId, pageId, page, ctx.wal(), walPlc))
+ ctx.wal().log(new DataPageMvccUpdateNewTxStateHintRecord(cacheId, pageId, itemId, state));
+ }
+
+ // We do not throw an exception here because new version may be updated by active Tx at this moment.
+ }
+
+ return Boolean.TRUE;
+ }
+ }
+
+ /**
+ * Applies changes to the row.
+ */
+ private final class MvccApplyChangesHandler extends PageHandler<MvccDataRow, Boolean> {
+ /** {@inheritDoc} */
+ @Override public Boolean run(int cacheId, long pageId, long page, long pageAddr, PageIO io, Boolean walPlc,
+ MvccDataRow newRow, int itemId) throws IgniteCheckedException {
+ assert grp.mvccEnabled();
+
+ DataPageIO iox = (DataPageIO)io;
+
+ int offset = iox.getPayloadOffset(pageAddr, itemId, grp.dataRegion().pageMemory().pageSize(), MVCC_INFO_SIZE);
+
+ long crd = iox.mvccCoordinator(pageAddr, offset);
+ long cntr = iox.mvccCounter(pageAddr, offset);
+ int opCntrAndHint = iox.mvccOperationCounter(pageAddr, offset);
+ int opCntr = opCntrAndHint & ~MVCC_HINTS_MASK;
+ byte txState = (byte)(opCntrAndHint >>> MVCC_HINTS_BIT_OFF);
+
+ long newCrd = iox.newMvccCoordinator(pageAddr, offset);
+ long newCntr = iox.newMvccCounter(pageAddr, offset);
+ int newOpCntrAndHint = iox.newMvccOperationCounter(pageAddr, offset);
+ int newOpCntr = newOpCntrAndHint & ~MVCC_HINTS_MASK;
+ byte newTxState = (byte)(newOpCntrAndHint >>> MVCC_HINTS_BIT_OFF);
+
+ assert crd == newRow.mvccCoordinatorVersion();
+ assert cntr == newRow.mvccCounter();
+ assert opCntr == newRow.mvccOperationCounter();
+
+ if (txState != newRow.mvccTxState() && newRow.mvccTxState() != TxState.NA) {
+ assert txState == TxState.NA;
+
+ iox.mvccOperationCounter(pageAddr, offset, opCntr | (newRow.mvccTxState() << MVCC_HINTS_BIT_OFF));
+
+ if (isWalDeltaRecordNeeded(grp.dataRegion().pageMemory(), cacheId, pageId, page, ctx.wal(), walPlc))
+ ctx.wal().log(new DataPageMvccUpdateTxStateHintRecord(cacheId, pageId, itemId, newRow.mvccTxState()));
+ }
+
+ if (compare(newCrd,
+ newCntr,
+ newOpCntr,
+ newRow.newMvccCoordinatorVersion(),
+ newRow.newMvccCounter(),
+ newRow.newMvccOperationCounter()) != 0) {
+
+ iox.updateNewVersion(pageAddr, offset, newRow.newMvccVersion(), newRow.newMvccTxState());
+
+ if (isWalDeltaRecordNeeded(grp.dataRegion().pageMemory(), cacheId, pageId, page, ctx.wal(), walPlc))
+ ctx.wal().log(new DataPageMvccMarkUpdatedRecord(cacheId, pageId, itemId,
+ newRow.newMvccCoordinatorVersion(), newRow.newMvccCounter(), newRow.newMvccOperationCounter()));
+ }
+ else if (newTxState != newRow.newMvccTxState() && newRow.newMvccTxState() != TxState.NA) {
+ assert newTxState == TxState.NA;
+
+ iox.newMvccOperationCounter(pageAddr, offset, newOpCntr | (newRow.newMvccTxState() << MVCC_HINTS_BIT_OFF));
+
+ if (isWalDeltaRecordNeeded(grp.dataRegion().pageMemory(), cacheId, pageId, page, ctx.wal(), walPlc))
+ ctx.wal().log(new DataPageMvccUpdateNewTxStateHintRecord(cacheId, pageId, itemId, newRow.newMvccTxState()));
+ }
+
+ return Boolean.TRUE;
+ }
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
index e34f22f..c21ad0b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheProxyImpl.java
@@ -74,6 +74,7 @@ import org.apache.ignite.internal.AsyncSupportAdapter;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.query.CacheQuery;
import org.apache.ignite.internal.processors.cache.query.CacheQueryFuture;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType;
@@ -347,6 +348,9 @@ public class IgniteCacheProxyImpl<K, V> extends AsyncSupportAdapter<IgniteCache<
/** {@inheritDoc} */
@Override public Lock lockAll(final Collection<? extends K> keys) {
+ //TODO IGNITE-7764
+ MvccUtils.verifyMvccOperationSupport(ctx, "Lock");
+
return new CacheLockImpl<>(ctx.gate(), delegate, ctx.operationContextPerCall(), keys);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxFinishSync.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxFinishSync.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxFinishSync.java
index 7cc368a..1f688f6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxFinishSync.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxFinishSync.java
@@ -248,7 +248,7 @@ public class GridCacheTxFinishSync<K, V> {
log.trace("Moved transaction synchronizer to waiting state [nodeId=" + nodeId +
", threadId=" + threadId + ']');
- assert cnt == 0 || nodeLeft;
+ assert cnt == 0 || nodeLeft : cnt;
if (nodeLeft)
return;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxRecoveryFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxRecoveryFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxRecoveryFuture.java
index 3eeb0db..41e8aba 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxRecoveryFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTxRecoveryFuture.java
@@ -146,6 +146,17 @@ public class GridCacheTxRecoveryFuture extends GridCacheCompoundIdentityFuture<B
*/
@SuppressWarnings("ConstantConditions")
public void prepare() {
+ if (tx.txState().mvccEnabled(cctx)) { // TODO IGNITE-7313
+ U.error(log, "Cannot commit MVCC enabled transaction by recovery procedure. " +
+ "Operation is usupported at the moment [tx=" + CU.txString(tx) + ']');
+
+ onDone(false);
+
+ markInitialized();
+
+ return;
+ }
+
if (nearTxCheck) {
UUID nearNodeId = tx.eventNodeId();
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxMapping.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxMapping.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxMapping.java
index 481c954..b543786 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxMapping.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxMapping.java
@@ -46,6 +46,9 @@ public class GridDistributedTxMapping {
/** Explicit lock flag. */
private boolean explicitLock;
+ /** Query update flag. */
+ private boolean queryUpdate;
+
/** DHT version. */
private GridCacheVersion dhtVer;
@@ -133,6 +136,20 @@ public class GridDistributedTxMapping {
}
/**
+ * @return {@code True} if mapping was created for a query update.
+ */
+ public boolean queryUpdate() {
+ return queryUpdate;
+ }
+
+ /**
+ * Sets query update flag to {@code true}.
+ */
+ public void markQueryUpdate() {
+ queryUpdate = true;
+ }
+
+ /**
* @return {@code True} if lock is explicit.
*/
public boolean explicitLock() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxPrepareRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxPrepareRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxPrepareRequest.java
index 91dcd9e..a5aa0d8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxPrepareRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxPrepareRequest.java
@@ -78,6 +78,9 @@ public class GridDistributedTxPrepareRequest extends GridDistributedBaseMessage
/** */
public static final int STORE_WRITE_THROUGH_FLAG_MASK = 0x20;
+ /** */
+ public static final int QUERY_UPDATE_FLAG_MASK = 0x40;
+
/** Collection to message converter. */
private static final C1<Collection<UUID>, UUIDCollectionMessage> COL_TO_MSG = new C1<Collection<UUID>, UUIDCollectionMessage>() {
@Override public UUIDCollectionMessage apply(Collection<UUID> uuids) {
@@ -253,6 +256,25 @@ public class GridDistributedTxPrepareRequest extends GridDistributedBaseMessage
}
/**
+ *
+ * @return Flag indicating whether it is a query update.
+ */
+ public boolean queryUpdate() {
+ return (flags & QUERY_UPDATE_FLAG_MASK) != 0;
+ }
+
+ /**
+ *
+ * @param queryUpdate Query update value.
+ */
+ public void queryUpdate(boolean queryUpdate) {
+ if (queryUpdate)
+ flags = (byte)(flags | QUERY_UPDATE_FLAG_MASK);
+ else
+ flags &= ~QUERY_UPDATE_FLAG_MASK;
+ }
+
+ /**
* @return IO policy.
*/
public byte policy() {
[39/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
index 1445335..3a2af5d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.processors.cache;
import java.util.Collection;
+import java.util.List;
import java.util.UUID;
import javax.cache.Cache;
import javax.cache.expiry.ExpiryPolicy;
@@ -27,7 +28,11 @@ import org.apache.ignite.cache.eviction.EvictableEntry;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicAbstractUpdateFuture;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
@@ -187,6 +192,11 @@ public interface GridCacheEntryEx {
@Nullable public GridCacheEntryInfo info();
/**
+ * @return Entry info for each MVCC version.
+ */
+ @Nullable public List<GridCacheEntryInfo> allVersionsInfo() throws IgniteCheckedException;
+
+ /**
* Invalidates this entry.
*
* @param newVer New version to set.
@@ -264,7 +274,8 @@ public interface GridCacheEntryEx {
Object transformClo,
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
- boolean keepBinary)
+ boolean keepBinary,
+ @Nullable MvccSnapshot mvccVer)
throws IgniteCheckedException, GridCacheEntryRemovedException;
/**
@@ -292,6 +303,7 @@ public interface GridCacheEntryEx {
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean keepBinary,
+ @Nullable MvccSnapshot mvccVer,
@Nullable ReaderArguments readerArgs)
throws IgniteCheckedException, GridCacheEntryRemovedException;
@@ -313,6 +325,7 @@ public interface GridCacheEntryEx {
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean keepBinary,
+ @Nullable MvccSnapshot mvccVer,
@Nullable ReaderArguments readerArgs) throws IgniteCheckedException, GridCacheEntryRemovedException;
/**
@@ -331,6 +344,64 @@ public interface GridCacheEntryEx {
/**
* @param tx Cache transaction.
+ * @param affNodeId Partitioned node iD.
+ * @param val Value to set.
+ * @param ttl0 TTL.
+ * @param topVer Topology version.
+ * @param updateCntr Update counter.
+ * @param mvccVer Mvcc version.
+ * @param op Cache operation.
+ * @param needHistory Whether to collect rows created or affected by the current tx.
+ * @param noCreate Entry should not be created when enabled, e.g. SQL INSERT.
+ * @return Tuple containing success flag and old value. If success is {@code false},
+ * then value is {@code null}.
+ * @throws IgniteCheckedException If storing value failed.
+ * @throws GridCacheEntryRemovedException If entry has been removed.
+ */
+ public GridCacheUpdateTxResult mvccSet(
+ @Nullable IgniteInternalTx tx,
+ UUID affNodeId,
+ CacheObject val,
+ long ttl0,
+ AffinityTopologyVersion topVer,
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer,
+ GridCacheOperation op,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException, GridCacheEntryRemovedException;
+
+ /**
+ * @param tx Cache transaction.
+ * @param affNodeId Partitioned node iD.
+ * @param topVer Topology version.
+ * @param updateCntr Update counter.
+ * @param mvccVer Mvcc version.
+ * @param needHistory Whether to collect rows created or affected by the current tx.
+ * @return Tuple containing success flag and old value. If success is {@code false},
+ * then value is {@code null}.
+ * @throws IgniteCheckedException If storing value failed.
+ * @throws GridCacheEntryRemovedException If entry has been removed.
+ */
+ public GridCacheUpdateTxResult mvccRemove(
+ @Nullable IgniteInternalTx tx,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer,
+ boolean needHistory) throws IgniteCheckedException, GridCacheEntryRemovedException;
+
+ /**
+ * @param tx Transaction adapter.
+ * @param mvccVer Mvcc version.
+ * @return Lock result.
+ * @throws GridCacheEntryRemovedException If entry has been removed.
+ * @throws IgniteCheckedException If locking failed
+ */
+ GridCacheUpdateTxResult mvccLock(GridDhtTxLocalAdapter tx,
+ MvccSnapshot mvccVer) throws GridCacheEntryRemovedException, IgniteCheckedException;
+
+ /**
+ * @param tx Cache transaction.
* @param evtNodeId ID of node responsible for this change.
* @param affNodeId Partitioned node iD.
* @param val Value to set.
@@ -377,7 +448,8 @@ public interface GridCacheEntryEx {
@Nullable UUID subjId,
String taskName,
@Nullable GridCacheVersion dhtVer,
- @Nullable Long updateCntr
+ @Nullable Long updateCntr,
+ @Nullable MvccSnapshot mvccVer
) throws IgniteCheckedException, GridCacheEntryRemovedException;
/**
@@ -419,7 +491,8 @@ public interface GridCacheEntryEx {
@Nullable UUID subjId,
String taskName,
@Nullable GridCacheVersion dhtVer,
- @Nullable Long updateCntr
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer
) throws IgniteCheckedException, GridCacheEntryRemovedException;
/**
@@ -663,8 +736,43 @@ public interface GridCacheEntryEx {
* @throws IgniteCheckedException In case of error.
* @throws GridCacheEntryRemovedException If entry was removed.
*/
+ default boolean initialValue(CacheObject val,
+ GridCacheVersion ver,
+ long ttl,
+ long expireTime,
+ boolean preload,
+ AffinityTopologyVersion topVer,
+ GridDrType drType,
+ boolean fromStore) throws IgniteCheckedException, GridCacheEntryRemovedException {
+ return initialValue(val, ver, null, null, TxState.NA, TxState.NA,
+ ttl, expireTime, preload, topVer, drType, fromStore);
+ }
+
+ /**
+ * Sets new value if current version is <tt>0</tt>
+ *
+ * @param val New value.
+ * @param ver Version to use.
+ * @param mvccVer Mvcc version.
+ * @param newMvccVer New mvcc version.
+ * @param mvccTxState Tx state hint for mvcc version.
+ * @param newMvccTxState Tx state hint for new mvcc version.
+ * @param ttl Time to live.
+ * @param expireTime Expiration time.
+ * @param preload Flag indicating whether entry is being preloaded.
+ * @param topVer Topology version.
+ * @param drType DR type.
+ * @param fromStore {@code True} if value was loaded from store.
+ * @return {@code True} if initial value was set.
+ * @throws IgniteCheckedException In case of error.
+ * @throws GridCacheEntryRemovedException If entry was removed.
+ */
public boolean initialValue(CacheObject val,
GridCacheVersion ver,
+ @Nullable MvccVersion mvccVer,
+ @Nullable MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState,
long ttl,
long expireTime,
boolean preload,
@@ -1045,6 +1153,27 @@ public interface GridCacheEntryEx {
public boolean lockedByCurrentThread();
/**
+ *
+ * @param tx Transaction.
+ * @param affNodeId Affinity node id.
+ * @param topVer Topology version.
+ * @param updateCntr Update counter.
+ * @param op Cache operation.
+ * @param mvccVer Mvcc version. @return Update result.
+ * @throws IgniteCheckedException, If failed.
+ * @throws GridCacheEntryRemovedException, If entry has been removed.
+ */
+ public GridCacheUpdateTxResult mvccUpdateRowsWithPreloadInfo(
+ IgniteInternalTx tx,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ Long updateCntr,
+ List<GridCacheEntryInfo> entries,
+ GridCacheOperation op,
+ MvccSnapshot mvccVer)
+ throws IgniteCheckedException, GridCacheEntryRemovedException;
+
+ /**
* Touch this entry in its context's eviction manager.
*
* @param topVer Topology version.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
index 7371153..5c8164c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
@@ -17,9 +17,11 @@
package org.apache.ignite.internal.processors.cache;
-import java.nio.ByteBuffer;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridDirectTransient;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -28,6 +30,8 @@ import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.plugin.extensions.communication.MessageReader;
import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import java.nio.ByteBuffer;
+
/**
* Entry information that gets passed over wire.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index a1722db..249fbb3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -34,6 +34,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cache.eviction.EvictableEntry;
+import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.NodeStoppingException;
import org.apache.ignite.internal.UnregisteredBinaryTypeException;
import org.apache.ignite.internal.UnregisteredClassException;
@@ -45,12 +46,17 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult.UpdateOutcome;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxLocalAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicAbstractUpdateFuture;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheEntry;
import org.apache.ignite.internal.processors.cache.extras.GridCacheEntryExtras;
import org.apache.ignite.internal.processors.cache.extras.GridCacheMvccEntryExtras;
import org.apache.ignite.internal.processors.cache.extras.GridCacheObsoleteEntryExtras;
import org.apache.ignite.internal.processors.cache.extras.GridCacheTtlEntryExtras;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter;
import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
@@ -59,6 +65,8 @@ import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalAdapter;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateResult;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.ResultType;
import org.apache.ignite.internal.processors.cache.version.GridCacheLazyPlainVersionedEntry;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionConflictContext;
@@ -66,10 +74,14 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersionEx;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionManager;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEntryEx;
import org.apache.ignite.internal.processors.dr.GridDrType;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheFilter;
import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
+import org.apache.ignite.internal.util.GridLongList;
import org.apache.ignite.internal.util.IgniteTree;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.lang.GridClosureException;
+import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter;
import org.apache.ignite.internal.util.lang.GridTuple;
import org.apache.ignite.internal.util.lang.GridTuple3;
@@ -82,6 +94,7 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteInClosure;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;
@@ -92,13 +105,19 @@ import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT;
import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ;
import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED;
import static org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_UNLOCKED;
+import static org.apache.ignite.internal.processors.cache.GridCacheOperation.CREATE;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.DELETE;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.READ;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
import static org.apache.ignite.internal.processors.cache.GridCacheOperation.UPDATE;
+import static org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.RowData.NO_KEY;
+import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.CONCURRENT_UPDATE;
+import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.DUPLICATE_KEY;
import static org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult.UpdateOutcome.INVOKE_NO_OP;
import static org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult.UpdateOutcome.REMOVE_NO_VAL;
+import static org.apache.ignite.internal.processors.dr.GridDrType.DR_BACKUP;
import static org.apache.ignite.internal.processors.dr.GridDrType.DR_NONE;
+import static org.apache.ignite.internal.processors.dr.GridDrType.DR_PRIMARY;
/**
* Adapter for cache entry.
@@ -359,6 +378,77 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
}
/** {@inheritDoc} */
+ @Nullable @Override public List<GridCacheEntryInfo> allVersionsInfo() throws IgniteCheckedException {
+ assert cctx.mvccEnabled();
+
+ lockEntry();
+
+ try {
+ if (obsolete())
+ return Collections.emptyList();
+
+ GridCursor<? extends CacheDataRow> cur =
+ cctx.offheap().dataStore(localPartition()).mvccAllVersionsCursor(cctx, key, NO_KEY);
+
+ List<GridCacheEntryInfo> res = new ArrayList<>();
+
+ while (cur.next()) {
+ CacheDataRow row = cur.get();
+
+ GridCacheMvccEntryInfo info = new GridCacheMvccEntryInfo();
+
+ info.key(key);
+ info.value(row.value());
+ info.cacheId(cctx.cacheId());
+ info.version(row.version());
+ info.setNew(false);
+ info.setDeleted(false);
+
+ byte txState = row.mvccTxState() != TxState.NA ? row.mvccTxState() :
+ MvccUtils.state(cctx, row.mvccCoordinatorVersion(), row.mvccCounter(),
+ row.mvccOperationCounter());
+
+ if (txState == TxState.ABORTED)
+ continue;
+
+ info.mvccVersion(row.mvccCoordinatorVersion(), row.mvccCounter(), row.mvccOperationCounter());
+ info.mvccTxState(txState);
+
+ byte newTxState = row.newMvccTxState() != TxState.NA ? row.newMvccTxState() :
+ MvccUtils.state(cctx, row.newMvccCoordinatorVersion(), row.newMvccCounter(),
+ row.newMvccOperationCounter());
+
+ if (newTxState != TxState.ABORTED) {
+ info.newMvccVersion(row.newMvccCoordinatorVersion(),
+ row.newMvccCounter(),
+ row.newMvccOperationCounter());
+
+ info.newMvccTxState(newTxState);
+ }
+
+ long expireTime = row.expireTime();
+
+ long ttl;
+
+ ttl = expireTime == CU.EXPIRE_TIME_ETERNAL ? CU.TTL_ETERNAL : expireTime - U.currentTimeMillis();
+
+ if (ttl < 0)
+ ttl = CU.TTL_MINIMUM;
+
+ info.ttl(ttl);
+ info.expireTime(expireTime);
+
+ res.add(info);
+ }
+
+ return res;
+ }
+ finally {
+ unlockEntry();
+ }
+ }
+
+ /** {@inheritDoc} */
@Override public final CacheObject unswap() throws IgniteCheckedException, GridCacheEntryRemovedException {
return unswap(true);
}
@@ -490,7 +580,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
Object transformClo,
String taskName,
@Nullable IgniteCacheExpiryPolicy expirePlc,
- boolean keepBinary)
+ boolean keepBinary,
+ MvccSnapshot mvccVer)
throws IgniteCheckedException, GridCacheEntryRemovedException {
return (CacheObject)innerGet0(
ver,
@@ -505,6 +596,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
false,
keepBinary,
false,
+ mvccVer,
null);
}
@@ -515,6 +607,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean keepBinary,
+ MvccSnapshot mvccVer,
@Nullable ReaderArguments readerArgs) throws IgniteCheckedException, GridCacheEntryRemovedException {
return (EntryGetResult)innerGet0(
/*ver*/null,
@@ -529,6 +622,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
true,
keepBinary,
/*reserve*/true,
+ mvccVer,
readerArgs);
}
@@ -543,6 +637,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
String taskName,
@Nullable IgniteCacheExpiryPolicy expiryPlc,
boolean keepBinary,
+ MvccSnapshot mvccVer,
@Nullable ReaderArguments readerArgs)
throws IgniteCheckedException, GridCacheEntryRemovedException {
return (EntryGetResult)innerGet0(
@@ -558,6 +653,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
true,
keepBinary,
false,
+ mvccVer,
readerArgs);
}
@@ -576,6 +672,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
boolean retVer,
boolean keepBinary,
boolean reserveForLoad,
+ @Nullable MvccSnapshot mvccVer,
@Nullable ReaderArguments readerArgs
) throws IgniteCheckedException, GridCacheEntryRemovedException {
assert !(retVer && readThrough);
@@ -599,41 +696,53 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
try {
checkObsolete();
- boolean valid = valid(tx != null ? tx.topologyVersion() : cctx.affinity().affinityTopologyVersion());
-
CacheObject val;
- if (valid) {
- val = this.val;
-
- if (val == null) {
- if (isStartVersion()) {
- unswap(null, false);
+ if (mvccVer != null) {
+ CacheDataRow row = cctx.offheap().mvccRead(cctx, key, mvccVer);
- val = this.val;
- }
+ if (row != null) {
+ val = row.value();
+ resVer = row.version();
}
+ else
+ val = null;
+ }
+ else {
+ boolean valid = valid(tx != null ? tx.topologyVersion() : cctx.affinity().affinityTopologyVersion());
- if (val != null) {
- long expireTime = expireTimeExtras();
+ if (valid) {
+ val = this.val;
- if (expireTime > 0 && (expireTime < U.currentTimeMillis())) {
- if (onExpired((CacheObject)cctx.unwrapTemporary(val), null)) {
- val = null;
- evt = false;
+ if (val == null) {
+ if (isStartVersion()) {
+ unswap(null, false);
- if (cctx.deferredDelete()) {
- deferred = true;
- ver0 = ver;
+ val = this.val;
+ }
+ }
+
+ if (val != null) {
+ long expireTime = expireTimeExtras();
+
+ if (expireTime > 0 && (expireTime < U.currentTimeMillis())) {
+ if (onExpired((CacheObject)cctx.unwrapTemporary(val), null)) {
+ val = null;
+ evt = false;
+
+ if (cctx.deferredDelete()) {
+ deferred = true;
+ ver0 = ver;
+ }
+ else
+ obsolete = true;
}
- else
- obsolete = true;
}
}
}
+ else
+ val = null;
}
- else
- val = null;
CacheObject ret = val;
@@ -673,7 +782,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
if (ret != null && expiryPlc != null)
updateTtl(expiryPlc);
- if (retVer) {
+ if (retVer && resVer == null) {
resVer = (isNear() && cctx.transactional()) ? ((GridNearCacheEntry)this).dhtVersion() : this.ver;
if (resVer == null)
@@ -759,7 +868,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
long expTime = CU.toExpireTime(ttl);
// Update indexes before actual write to entry.
- storeValue(ret, expTime, nextVer);
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccInitialValue(this, ret, nextVer, expTime);
+ else
+ storeValue(ret, expTime, nextVer);
update(ret, expTime, ttl, nextVer, true);
@@ -870,13 +982,19 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
// Update indexes.
if (ret != null) {
- storeValue(ret, expTime, nextVer);
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccInitialValue(this, ret, nextVer, expTime);
+ else
+ storeValue(ret, expTime, nextVer);
if (cctx.deferredDelete() && !isInternal() && !detached() && deletedUnlocked())
deletedUnlocked(false);
}
else {
- removeValue();
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccRemoveAll(this);
+ else
+ removeValue();
if (cctx.deferredDelete() && !isInternal() && !detached() && !deletedUnlocked())
deletedUnlocked(true);
@@ -912,6 +1030,295 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
}
/** {@inheritDoc} */
+ @Override public final GridCacheUpdateTxResult mvccSet(
+ IgniteInternalTx tx,
+ UUID affNodeId,
+ CacheObject val,
+ long ttl0,
+ AffinityTopologyVersion topVer,
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer,
+ GridCacheOperation op,
+ boolean needHistory,
+ boolean noCreate) throws IgniteCheckedException, GridCacheEntryRemovedException {
+ assert tx != null;
+
+ final boolean valid = valid(tx.topologyVersion());
+
+ final GridCacheVersion newVer;
+
+ WALPointer logPtr = null;
+
+ ensureFreeSpace();
+
+ lockEntry();
+
+ MvccUpdateResult res;
+
+ try {
+ checkObsolete();
+
+ newVer = tx.writeVersion();
+
+ assert newVer != null : "Failed to get write version for tx: " + tx;
+
+ // Determine new ttl and expire time.
+ long expireTime, ttl = ttl0;
+
+ if (ttl == -1L) {
+ ttl = ttlExtras();
+ expireTime = expireTimeExtras();
+ }
+ else
+ expireTime = CU.toExpireTime(ttl);
+
+ assert ttl >= 0 : ttl;
+ assert expireTime >= 0 : expireTime;
+
+ // Detach value before index update.
+ val = cctx.kernalContext().cacheObjects().prepareForCache(val, cctx);
+
+ assert val != null;
+
+ res = cctx.offheap().mvccUpdate(
+ this, val, newVer, expireTime, mvccVer, tx.local(), needHistory, noCreate);
+
+ assert res != null;
+
+ // VERSION_FOUND is possible only on primary node when inserting the same key, or on backup when
+ // updating the key which just has been rebalanced.
+ assert res.resultType() != ResultType.VERSION_FOUND || op == CREATE && tx.local() || !tx.local();
+
+ // PREV_NOT_NULL on CREATE is possible only on primary.
+ assert res.resultType() != ResultType.PREV_NOT_NULL || op != CREATE || tx.local();
+
+ if (res.resultType() == ResultType.VERSION_MISMATCH)
+ throw new IgniteSQLException("Mvcc version mismatch.", CONCURRENT_UPDATE);
+ else if (noCreate && res.resultType() == ResultType.PREV_NULL)
+ return new GridCacheUpdateTxResult(false);
+ else if (res.resultType() == ResultType.LOCKED) {
+ unlockEntry();
+
+ MvccVersion lockVer = res.resultVersion();
+
+ GridFutureAdapter<GridCacheUpdateTxResult> resFut = new GridFutureAdapter<>();
+
+ IgniteInternalFuture<?> lockFut = cctx.kernalContext().coordinators().waitFor(cctx, lockVer);
+
+ lockFut.listen(new MvccUpdateLockListener(tx, this, affNodeId, topVer, val, ttl0, updateCntr, mvccVer,
+ op, needHistory, noCreate, resFut));
+
+ return new GridCacheUpdateTxResult(false, resFut);
+ }
+ else if (op == CREATE && tx.local() && (res.resultType() == ResultType.PREV_NOT_NULL ||
+ res.resultType() == ResultType.VERSION_FOUND))
+ throw new IgniteSQLException("Duplicate key during INSERT [key=" + key + ']', DUPLICATE_KEY);
+
+ if (cctx.deferredDelete() && deletedUnlocked() && !detached())
+ deletedUnlocked(false);
+
+ assert tx.local() && updateCntr == null || !tx.local() && updateCntr != null && updateCntr > 0;
+
+ if (tx.local())
+ updateCntr = nextMvccPartitionCounter();
+
+ if (cctx.group().persistenceEnabled() && cctx.group().walEnabled())
+ logPtr = cctx.shared().wal().log(new DataRecord(new DataEntry(
+ cctx.cacheId(),
+ key,
+ val,
+ res.resultType() == ResultType.PREV_NULL ? CREATE : UPDATE,
+ tx.nearXidVersion(),
+ newVer,
+ expireTime,
+ key.partition(),
+ updateCntr)));
+
+ update(val, expireTime, ttl, newVer, true);
+
+ mvccDrReplicate(tx.local() ? DR_PRIMARY : DR_BACKUP, val, newVer, topVer, mvccVer);
+
+ recordNodeId(affNodeId, topVer);
+ }
+ finally {
+ if (lockedByCurrentThread()) {
+ unlockEntry();
+
+ cctx.evicts().touch(this, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ onUpdateFinished(updateCntr);
+
+ GridCacheUpdateTxResult updRes = valid ? new GridCacheUpdateTxResult(true, updateCntr, logPtr) :
+ new GridCacheUpdateTxResult(false, logPtr);
+
+ updRes.mvccHistory(res.history());
+
+ return updRes;
+ }
+
+ /** {@inheritDoc} */
+ @Override public final GridCacheUpdateTxResult mvccRemove(
+ IgniteInternalTx tx,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ @Nullable Long updateCntr,
+ MvccSnapshot mvccVer,
+ boolean needHistory) throws IgniteCheckedException, GridCacheEntryRemovedException {
+ assert tx != null;
+ assert mvccVer != null;
+
+ final boolean valid = valid(tx.topologyVersion());
+
+ final GridCacheVersion newVer;
+
+ WALPointer logPtr = null;
+
+ lockEntry();
+
+ MvccUpdateResult res;
+
+ try {
+ checkObsolete();
+
+ newVer = tx.writeVersion();
+
+ assert newVer != null : "Failed to get write version for tx: " + tx;
+
+ res = cctx.offheap().mvccRemove(this, mvccVer, tx.local(), needHistory);
+
+ assert res != null;
+
+ if (res.resultType() == ResultType.VERSION_MISMATCH)
+ throw new IgniteSQLException("Mvcc version mismatch.", CONCURRENT_UPDATE);
+ else if (res.resultType() == ResultType.PREV_NULL)
+ return new GridCacheUpdateTxResult(false);
+ else if (res.resultType() == ResultType.LOCKED) {
+ unlockEntry();
+
+ MvccVersion lockVer = res.resultVersion();
+
+ GridFutureAdapter<GridCacheUpdateTxResult> resFut = new GridFutureAdapter<>();
+
+ IgniteInternalFuture<?> lockFut = cctx.kernalContext().coordinators().waitFor(cctx, lockVer);
+
+ lockFut.listen(new MvccRemoveLockListener(tx, this, affNodeId, topVer, updateCntr, mvccVer, needHistory,
+ resFut));
+
+ return new GridCacheUpdateTxResult(false, resFut);
+ }
+
+ if (cctx.deferredDelete() && deletedUnlocked() && !detached())
+ deletedUnlocked(false);
+
+ assert tx.local() && updateCntr == null || !tx.local() && updateCntr != null && updateCntr > 0;
+
+ if (tx.local())
+ updateCntr = nextMvccPartitionCounter();
+
+ if (cctx.group().persistenceEnabled() && cctx.group().walEnabled())
+ logPtr = logTxUpdate(tx, null, 0, updateCntr);
+
+ update(null, 0, 0, newVer, true);
+
+ mvccDrReplicate(tx.local() ? DR_PRIMARY : DR_BACKUP, null, newVer, topVer, mvccVer);
+
+ recordNodeId(affNodeId, topVer);
+ }
+ finally {
+ if (lockedByCurrentThread()) {
+ unlockEntry();
+
+ cctx.evicts().touch(this, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ onUpdateFinished(updateCntr);
+
+ GridCacheUpdateTxResult updRes = valid ? new GridCacheUpdateTxResult(true, updateCntr, logPtr) :
+ new GridCacheUpdateTxResult(false, logPtr);
+
+ updRes.mvccHistory(res.history());
+
+ return updRes;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheUpdateTxResult mvccLock(GridDhtTxLocalAdapter tx, MvccSnapshot mvccVer)
+ throws GridCacheEntryRemovedException, IgniteCheckedException {
+ assert tx != null;
+ assert mvccVer != null;
+
+ final boolean valid = valid(tx.topologyVersion());
+
+ final GridCacheVersion newVer;
+
+ WALPointer logPtr = null;
+
+ lockEntry();
+
+ try {
+ checkObsolete();
+
+ newVer = tx.writeVersion();
+
+ assert newVer != null : "Failed to get write version for tx: " + tx;
+
+ assert tx.local();
+
+ MvccUpdateResult res = cctx.offheap().mvccLock(this, mvccVer);
+
+ assert res != null;
+
+ if (res.resultType() == ResultType.VERSION_MISMATCH)
+ throw new IgniteSQLException("Mvcc version mismatch.", CONCURRENT_UPDATE);
+ else if (res.resultType() == ResultType.LOCKED) {
+ unlockEntry();
+
+ MvccVersion lockVer = res.resultVersion();
+
+ GridFutureAdapter<GridCacheUpdateTxResult> resFut = new GridFutureAdapter<>();
+
+ IgniteInternalFuture<?> lockFut = cctx.kernalContext().coordinators().waitFor(cctx, lockVer);
+
+ lockFut.listen(new MvccAcquireLockListener(tx, this, mvccVer, resFut));
+
+ return new GridCacheUpdateTxResult(false, resFut);
+ }
+ }
+ finally {
+ if (lockedByCurrentThread()) {
+ unlockEntry();
+
+ cctx.evicts().touch(this, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ onUpdateFinished(0L);
+
+ return new GridCacheUpdateTxResult(valid, logPtr);
+ }
+
+ /**
+ * Enlist for DR if needed.
+ *
+ * @param drType DR type.
+ * @param val Value.
+ * @param ver Version.
+ * @param topVer Topology version.
+ * @param mvccVer MVCC snapshot.
+ * @throws IgniteCheckedException In case of exception.
+ */
+ private void mvccDrReplicate(GridDrType drType, CacheObject val, GridCacheVersion ver,
+ AffinityTopologyVersion topVer,
+ MvccSnapshot mvccVer) throws IgniteCheckedException {
+
+ if (cctx.isDrEnabled() && drType != DR_NONE && !isInternal())
+ cctx.dr().mvccReplicate(key, val, rawTtl(), rawExpireTime(), ver.conflictVersion(), drType, topVer, mvccVer);
+ }
+
+ /** {@inheritDoc} */
@Override public final GridCacheUpdateTxResult innerSet(
@Nullable IgniteInternalTx tx,
UUID evtNodeId,
@@ -933,15 +1340,16 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
@Nullable UUID subjId,
String taskName,
@Nullable GridCacheVersion dhtVer,
- @Nullable Long updateCntr
+ @Nullable Long updateCntr,
+ @Nullable MvccSnapshot mvccVer
) throws IgniteCheckedException, GridCacheEntryRemovedException {
CacheObject old;
- boolean valid = valid(tx != null ? tx.topologyVersion() : topVer);
+ final boolean valid = valid(tx != null ? tx.topologyVersion() : topVer);
// Lock should be held by now.
if (!cctx.isAll(this, filter))
- return new GridCacheUpdateTxResult(false, null, null);
+ return new GridCacheUpdateTxResult(false);
final GridCacheVersion newVer;
@@ -955,6 +1363,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
ensureFreeSpace();
+ GridLongList mvccWaitTxs = null;
+
lockEntry();
try {
@@ -965,7 +1375,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
// It is possible that 'get' could load more recent value.
if (!((GridNearCacheEntry)this).recordDhtVersion(dhtVer))
- return new GridCacheUpdateTxResult(false, null, logPtr);
+ return new GridCacheUpdateTxResult(false, logPtr);
}
assert tx == null || (!tx.local() && tx.onePhaseCommit()) || tx.ownsLock(this) :
@@ -1001,7 +1411,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
key0 = e.key();
if (interceptorVal == null)
- return new GridCacheUpdateTxResult(false, (CacheObject)cctx.unwrapTemporary(old), logPtr);
+ return new GridCacheUpdateTxResult(false, logPtr);
else if (interceptorVal != val0)
val0 = cctx.unwrapTemporary(interceptorVal);
@@ -1033,7 +1443,18 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
assert val != null;
- storeValue(val, expireTime, newVer);
+ if (cctx.mvccEnabled()) {
+ assert mvccVer != null;
+
+ mvccWaitTxs = cctx.offheap().mvccUpdateNative(tx.local(),
+ this,
+ val,
+ newVer,
+ expireTime,
+ mvccVer);
+ }
+ else
+ storeValue(val, expireTime, newVer);
if (cctx.deferredDelete() && deletedUnlocked() && !isInternal() && !detached())
deletedUnlocked(false);
@@ -1112,8 +1533,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
if (intercept)
cctx.config().getInterceptor().onAfterPut(new CacheLazyEntry(cctx, key, key0, val, val0, keepBinary, updateCntr0));
- return valid ? new GridCacheUpdateTxResult(true, retval ? old : null, updateCntr0, logPtr) :
- new GridCacheUpdateTxResult(false, null, logPtr);
+ return valid ? new GridCacheUpdateTxResult(true, updateCntr0, logPtr, mvccWaitTxs) :
+ new GridCacheUpdateTxResult(false, logPtr);
}
/**
@@ -1142,7 +1563,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
@Nullable UUID subjId,
String taskName,
@Nullable GridCacheVersion dhtVer,
- @Nullable Long updateCntr
+ @Nullable Long updateCntr,
+ @Nullable MvccSnapshot mvccVer
) throws IgniteCheckedException, GridCacheEntryRemovedException {
assert cctx.transactional();
@@ -1150,11 +1572,11 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
GridCacheVersion newVer;
- boolean valid = valid(tx != null ? tx.topologyVersion() : topVer);
+ final boolean valid = valid(tx != null ? tx.topologyVersion() : topVer);
// Lock should be held by now.
if (!cctx.isAll(this, filter))
- return new GridCacheUpdateTxResult(false, null, null);
+ return new GridCacheUpdateTxResult(false);
GridCacheVersion obsoleteVer = null;
@@ -1172,6 +1594,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
boolean marked = false;
+ GridLongList mvccWaitTxs = null;
+
lockEntry();
try {
@@ -1182,7 +1606,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
// It is possible that 'get' could load more recent value.
if (!((GridNearCacheEntry)this).recordDhtVersion(dhtVer))
- return new GridCacheUpdateTxResult(false, null, logPtr);
+ return new GridCacheUpdateTxResult(false, logPtr);
}
assert tx == null || (!tx.local() && tx.onePhaseCommit()) || tx.ownsLock(this) :
@@ -1210,11 +1634,17 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
if (cctx.cancelRemove(interceptRes)) {
CacheObject ret = cctx.toCacheObject(cctx.unwrapTemporary(interceptRes.get2()));
- return new GridCacheUpdateTxResult(false, ret, logPtr);
+ return new GridCacheUpdateTxResult(false, logPtr);
}
}
- removeValue();
+ if (cctx.mvccEnabled()) {
+ assert mvccVer != null;
+
+ mvccWaitTxs = cctx.offheap().mvccRemoveNative(tx.local(), this, mvccVer);
+ }
+ else
+ removeValue();
update(null, 0, 0, newVer, true);
@@ -1336,18 +1766,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
if (intercept)
cctx.config().getInterceptor().onAfterRemove(entry0);
- if (valid) {
- CacheObject ret;
-
- if (interceptRes != null)
- ret = cctx.toCacheObject(cctx.unwrapTemporary(interceptRes.get2()));
- else
- ret = old;
-
- return new GridCacheUpdateTxResult(true, ret, updateCntr0, logPtr);
- }
+ if (valid)
+ return new GridCacheUpdateTxResult(true, updateCntr0, logPtr, mvccWaitTxs);
else
- return new GridCacheUpdateTxResult(false, null, logPtr);
+ return new GridCacheUpdateTxResult(false, logPtr);
}
/**
@@ -2176,7 +2598,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
", val=" + val + ']');
}
- removeValue();
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccRemoveAll(this);
+ else
+ removeValue();
}
finally {
unlockEntry();
@@ -2408,7 +2833,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
ver = newVer;
flags &= ~IS_EVICT_DISABLED;
- removeValue();
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccRemoveAll(this);
+ else
+ removeValue();
onInvalidate();
@@ -2673,7 +3101,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
long delta = expireTime - U.currentTimeMillis();
if (delta <= 0) {
- removeValue();
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccRemoveAll(this);
+ else
+ removeValue();
return true;
}
@@ -2738,6 +3169,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
@Override public boolean initialValue(
CacheObject val,
GridCacheVersion ver,
+ MvccVersion mvccVer,
+ MvccVersion newMvccVer,
+ byte mvccTxState,
+ byte newMvccTxState,
long ttl,
long expireTime,
boolean preload,
@@ -2812,11 +3247,63 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
}
}
- storeValue(val, expTime, ver);
+ if (cctx.mvccEnabled()) {
+ if (preload && mvccVer != null) {
+ cctx.offheap().mvccInitialValueIfAbsent(this,
+ val,
+ ver,
+ expTime,
+ mvccVer,
+ newMvccVer,
+ mvccTxState,
+ newMvccTxState);
+ }
+ else
+ cctx.offheap().mvccInitialValue(this, val, ver, expTime, mvccVer, newMvccVer);
+ }
+ else
+ storeValue(val, expTime, ver);
}
}
- else // Optimization to access storage only once.
- update = storeValue(val, expTime, ver, p);
+ else {
+ if (cctx.mvccEnabled()) {
+ // cannot identify whether the entry is exist on the fly
+ unswap(false);
+
+ if (update = p.apply(null)) {
+ // If entry is already unswapped and we are modifying it, we must run deletion callbacks for old value.
+ long oldExpTime = expireTimeUnlocked();
+ long delta = (oldExpTime == 0 ? 0 : oldExpTime - U.currentTimeMillis());
+
+ if (delta < 0) {
+ if (onExpired(this.val, null)) {
+ if (cctx.deferredDelete()) {
+ deferred = true;
+ oldVer = this.ver;
+ }
+ else if (val == null)
+ obsolete = true;
+ }
+ }
+
+ if (preload && mvccVer != null) {
+ cctx.offheap().mvccInitialValueIfAbsent(this,
+ val,
+ ver,
+ expTime,
+ mvccVer,
+ newMvccVer,
+ mvccTxState,
+ newMvccTxState);
+ }
+ else
+ cctx.offheap().mvccInitialValue(this, val, ver, expTime, mvccVer, newMvccVer);
+ }
+ }
+ else
+ // Optimization to access storage only once.
+ update = storeValue(val, expTime, ver, p);
+ }
if (update) {
update(val, expTime, ttl, ver, true);
@@ -2917,6 +3404,14 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
return 0;
}
+ /**
+ * @return Next mvcc update counter.
+ */
+ protected long nextMvccPartitionCounter() {
+ return 0;
+ }
+
+
/** {@inheritDoc} */
@Override public GridCacheVersionedEntryEx versionedEntry(final boolean keepBinary)
throws IgniteCheckedException, GridCacheEntryRemovedException {
@@ -3004,7 +3499,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
// Detach value before index update.
val = cctx.kernalContext().cacheObjects().prepareForCache(val, cctx);
- if (val != null) {
+ if (val != null) {
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccInitialValue(this, val, newVer, expTime);
+ else
storeValue(val, expTime, newVer);
if (deletedUnlocked())
@@ -3459,7 +3957,10 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
cctx.shared().database().checkpointReadLock();
try {
- removeValue();
+ if (cctx.mvccEnabled())
+ cctx.offheap().mvccRemoveAll(this);
+ else
+ removeValue();
}
finally {
cctx.shared().database().checkpointReadUnlock();
@@ -3634,8 +4135,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
}
/**
- * Stores value in offheap.
- *
+ * Stores value inoffheap.*
* @param val Value.
* @param expireTime Expire time.
* @param ver New entry version.
@@ -4375,6 +4875,450 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
}
}
+ /** */
+ private static class MvccRemoveLockListener implements IgniteInClosure<IgniteInternalFuture> {
+ /** */
+ private static final long serialVersionUID = -1578749008606139541L;
+
+ /** */
+ private final IgniteInternalTx tx;
+
+ /** */
+ private final AffinityTopologyVersion topVer;
+
+ /** */
+ private final UUID affNodeId;
+
+ /** */
+ private final MvccSnapshot mvccVer;
+
+ /** */
+ private final Long updateCntr;
+
+ /** */
+ private final boolean needHistory;
+
+ /** */
+ private final GridFutureAdapter<GridCacheUpdateTxResult> resFut;
+
+ /** */
+ private GridCacheMapEntry entry;
+
+ /** */
+ MvccRemoveLockListener(IgniteInternalTx tx,
+ GridCacheMapEntry entry,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ Long updateCntr,
+ MvccSnapshot mvccVer,
+ boolean needHistory,
+ GridFutureAdapter<GridCacheUpdateTxResult> resFut) {
+ this.tx = tx;
+ this.entry = entry;
+ this.topVer = topVer;
+ this.affNodeId = affNodeId;
+ this.mvccVer = mvccVer;
+ this.updateCntr = updateCntr;
+ this.needHistory = needHistory;
+ this.resFut = resFut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void apply(IgniteInternalFuture lockFut) {
+ WALPointer logPtr = null;
+ long updateCntr0;
+ boolean valid;
+
+ GridCacheContext cctx = entry.context();
+ GridCacheVersion newVer = tx.writeVersion();
+
+ MvccUpdateResult res;
+
+ try {
+ lockFut.get();
+
+ while (true) {
+ entry.lockEntry();
+
+ if (entry.obsoleteVersionExtras() == null)
+ break;
+
+ entry.unlockEntry();
+
+ entry = (GridCacheMapEntry)cctx.cache().entryEx(entry.key());
+ }
+
+ valid = entry.valid(tx.topologyVersion());
+
+ cctx.shared().database().checkpointReadLock();
+
+ try {
+ res = cctx.offheap().mvccRemove(entry, mvccVer, tx.local(), needHistory);
+ } finally {
+ cctx.shared().database().checkpointReadUnlock();
+ }
+
+ assert res != null;
+
+ if (res.resultType() == ResultType.VERSION_MISMATCH) {
+ resFut.onDone(new IgniteSQLException("Mvcc version mismatch.", CONCURRENT_UPDATE));
+
+ return;
+ }
+ else if (res.resultType() == ResultType.PREV_NULL) {
+ resFut.onDone(new GridCacheUpdateTxResult(false));
+
+ return;
+ }
+ else if (res.resultType() == ResultType.LOCKED) {
+ entry.unlockEntry();
+
+ IgniteInternalFuture<?> lockFuture = cctx.kernalContext().coordinators().waitFor(cctx, res.resultVersion());
+
+ lockFuture.listen(this);
+
+ return;
+ }
+
+ if (cctx.deferredDelete() && entry.deletedUnlocked() && !entry.detached())
+ entry.deletedUnlocked(false);
+
+ assert tx.local() && updateCntr == null || !tx.local() && updateCntr != null && updateCntr > 0;
+
+ updateCntr0 = tx.local() ? entry.nextMvccPartitionCounter() : updateCntr;
+
+ if (updateCntr != null && updateCntr != 0)
+ updateCntr0 = updateCntr;
+
+ if (cctx.group().persistenceEnabled() && cctx.group().walEnabled())
+ logPtr = cctx.shared().wal().log(new DataRecord(new DataEntry(
+ cctx.cacheId(),
+ entry.key(),
+ null,
+ DELETE,
+ tx.nearXidVersion(),
+ tx.writeVersion(),
+ 0,
+ entry.key().partition(),
+ updateCntr0)));
+
+ entry.update(null, 0, 0, newVer, true);
+
+ entry.mvccDrReplicate(tx.local() ? DR_PRIMARY : DR_BACKUP, null, newVer, topVer, mvccVer);
+
+ entry.recordNodeId(affNodeId, topVer);
+ }
+ catch (IgniteCheckedException e) {
+ resFut.onDone(e);
+
+ return;
+ }
+ finally {
+ if (entry.lockedByCurrentThread()) {
+ entry.unlockEntry();
+
+ cctx.evicts().touch(entry, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ entry.onUpdateFinished(updateCntr0);
+
+ GridCacheUpdateTxResult updRes = valid ? new GridCacheUpdateTxResult(true, updateCntr0, logPtr)
+ : new GridCacheUpdateTxResult(false, logPtr);
+
+ updRes.mvccHistory(res.history());
+
+ resFut.onDone(updRes);
+ }
+ }
+
+ /** */
+ private static class MvccAcquireLockListener implements IgniteInClosure<IgniteInternalFuture> {
+ /** */
+ private static final long serialVersionUID = -1578749008606139541L;
+
+ /** */
+ private final IgniteInternalTx tx;
+
+ /** */
+ private final MvccSnapshot mvccVer;
+
+ /** */
+ private final GridFutureAdapter<GridCacheUpdateTxResult> resFut;
+
+ /** */
+ private GridCacheMapEntry entry;
+
+ /** */
+ MvccAcquireLockListener(IgniteInternalTx tx,
+ GridCacheMapEntry entry,
+ MvccSnapshot mvccVer,
+ GridFutureAdapter<GridCacheUpdateTxResult> resFut) {
+ this.tx = tx;
+ this.entry = entry;
+ this.mvccVer = mvccVer;
+ this.resFut = resFut;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public void apply(IgniteInternalFuture lockFut) {
+ WALPointer logPtr = null;
+ boolean valid;
+
+ GridCacheContext cctx = entry.context();
+
+ try {
+ lockFut.get();
+
+ while (true) {
+ entry.lockEntry();
+
+ if (entry.obsoleteVersionExtras() == null)
+ break;
+
+ entry.unlockEntry();
+
+ entry = (GridCacheMapEntry)cctx.cache().entryEx(entry.key());
+ }
+
+ valid = entry.valid(tx.topologyVersion());
+
+ cctx.shared().database().checkpointReadLock();
+
+ MvccUpdateResult res;
+
+ try {
+ res = cctx.offheap().mvccLock(entry, mvccVer);
+ }
+ finally {
+ cctx.shared().database().checkpointReadUnlock();
+ }
+
+ assert res != null;
+
+ if (res.resultType() == ResultType.VERSION_MISMATCH) {
+ resFut.onDone(new IgniteSQLException("Mvcc version mismatch.", CONCURRENT_UPDATE));
+
+ return;
+ }
+ else if (res.resultType() == ResultType.LOCKED) {
+ entry.unlockEntry();
+
+ cctx.kernalContext().coordinators().waitFor(cctx, res.resultVersion()).listen(this);
+
+ return;
+ }
+ }
+ catch (IgniteCheckedException e) {
+ resFut.onDone(e);
+
+ return;
+ }
+ finally {
+ if (entry.lockedByCurrentThread()) {
+ entry.unlockEntry();
+
+ cctx.evicts().touch(entry, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ entry.onUpdateFinished(0L);
+
+ resFut.onDone(new GridCacheUpdateTxResult(valid, logPtr));
+ }
+ }
+
+ /** */
+ private static class MvccUpdateLockListener implements IgniteInClosure<IgniteInternalFuture> {
+ /** */
+ private static final long serialVersionUID = 8452738214760268397L;
+
+ /** */
+ private final IgniteInternalTx tx;
+
+ /** */
+ private final UUID affNodeId;
+
+ /** */
+ private final AffinityTopologyVersion topVer;
+
+ /** */
+ private final CacheObject val;
+
+ /** */
+ private final long ttl;
+
+ /** */
+ private final Long updateCntr;
+
+ /** */
+ private final MvccSnapshot mvccVer;
+
+ /** */
+ private final GridFutureAdapter<GridCacheUpdateTxResult> resFut;
+
+ /** */
+ private GridCacheMapEntry entry;
+
+ /** */
+ private GridCacheOperation op;
+
+ /** */
+ private final boolean needHistory;
+
+ /** */
+ private final boolean noCreate;
+
+ /** */
+ MvccUpdateLockListener(IgniteInternalTx tx,
+ GridCacheMapEntry entry,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ CacheObject val,
+ long ttl,
+ Long updateCntr,
+ MvccSnapshot mvccVer,
+ GridCacheOperation op,
+ boolean needHistory,
+ boolean noCreate,
+ GridFutureAdapter<GridCacheUpdateTxResult> resFut) {
+ this.tx = tx;
+ this.entry = entry;
+ this.affNodeId = affNodeId;
+ this.topVer = topVer;
+ this.val = val;
+ this.ttl = ttl;
+ this.updateCntr = updateCntr;
+ this.mvccVer = mvccVer;
+ this.op = op;
+ this.needHistory = needHistory;
+ this.noCreate = noCreate;
+ this.resFut = resFut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void apply(IgniteInternalFuture lockFut) {
+ WALPointer logPtr = null;
+ long updateCntr0;
+ boolean valid;
+
+ GridCacheContext cctx = entry.context();
+ GridCacheVersion newVer = tx.writeVersion();
+
+ MvccUpdateResult res;
+
+ try {
+ lockFut.get();
+
+ entry.ensureFreeSpace();
+
+ while (true) {
+ entry.lockEntry();
+
+ if (entry.obsoleteVersionExtras() == null)
+ break;
+
+ entry.unlockEntry();
+
+ entry = (GridCacheMapEntry)cctx.cache().entryEx(entry.key());
+ }
+
+ valid = entry.valid(tx.topologyVersion());
+
+ // Determine new ttl and expire time.
+ long expireTime, ttl = this.ttl;
+
+ if (ttl == -1L) {
+ ttl = entry.ttlExtras();
+ expireTime = entry.expireTimeExtras();
+ }
+ else
+ expireTime = CU.toExpireTime(ttl);
+
+ assert ttl >= 0 : ttl;
+ assert expireTime >= 0 : expireTime;
+
+ cctx.shared().database().checkpointReadLock();
+
+ try {
+ res = cctx.offheap().mvccUpdate(
+ entry, val, newVer, expireTime, mvccVer, tx.local(), needHistory, noCreate);
+ } finally {
+ cctx.shared().database().checkpointReadUnlock();
+ }
+
+ assert res != null;
+
+ if (res.resultType() == ResultType.VERSION_MISMATCH) {
+ resFut.onDone(new IgniteSQLException("Mvcc version mismatch.", CONCURRENT_UPDATE));
+
+ return;
+ }
+ else if (res.resultType() == ResultType.LOCKED) {
+ entry.unlockEntry();
+
+ cctx.kernalContext().coordinators().waitFor(cctx, res.resultVersion()).listen(this);
+
+ return;
+ }
+ else if (op == CREATE && tx.local() && (res.resultType() == ResultType.PREV_NOT_NULL ||
+ res.resultType() == ResultType.VERSION_FOUND)) {
+ resFut.onDone(new IgniteSQLException("Duplicate key during INSERT [key=" + entry.key() + ']',
+ DUPLICATE_KEY));
+
+ return;
+ }
+
+ if (cctx.deferredDelete() && entry.deletedUnlocked() && !entry.detached())
+ entry.deletedUnlocked(false);
+
+ assert tx.local() && updateCntr == null || !tx.local() && updateCntr != null && updateCntr > 0;
+
+ updateCntr0 = tx.local() ? entry.nextMvccPartitionCounter() : updateCntr;
+
+ if (cctx.group().persistenceEnabled() && cctx.group().walEnabled())
+ logPtr = cctx.shared().wal().log(new DataRecord(new DataEntry(
+ cctx.cacheId(),
+ entry.key(),
+ val,
+ res.resultType() == ResultType.PREV_NULL ? CREATE : UPDATE,
+ tx.nearXidVersion(),
+ newVer,
+ expireTime,
+ entry.key().partition(),
+ updateCntr0)));
+
+ entry.update(val, expireTime, ttl, newVer, true);
+
+ entry.mvccDrReplicate(tx.local() ? DR_PRIMARY : DR_BACKUP, val, newVer, topVer, mvccVer);
+
+ entry.recordNodeId(affNodeId, topVer);
+ }
+ catch (IgniteCheckedException e) {
+ resFut.onDone(e);
+
+ return;
+ }
+ finally {
+ if (entry.lockedByCurrentThread()) {
+ entry.unlockEntry();
+
+ cctx.evicts().touch(entry, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ entry.onUpdateFinished(updateCntr0);
+
+ GridCacheUpdateTxResult updRes = valid ? new GridCacheUpdateTxResult(true, updateCntr0, logPtr)
+ : new GridCacheUpdateTxResult(false, logPtr);
+
+ updRes.mvccHistory(res.history());
+
+ resFut.onDone(updRes);
+ }
+ }
+
/**
*
*/
@@ -5459,4 +6403,83 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
return S.toString(AtomicCacheUpdateClosure.class, this);
}
}
+
+ /** {@inheritDoc} */
+ @Override public GridCacheUpdateTxResult mvccUpdateRowsWithPreloadInfo(
+ IgniteInternalTx tx,
+ UUID affNodeId,
+ AffinityTopologyVersion topVer,
+ Long updateCntr,
+ List<GridCacheEntryInfo> entries,
+ GridCacheOperation op,
+ MvccSnapshot mvccVer)
+ throws IgniteCheckedException, GridCacheEntryRemovedException {
+ assert updateCntr != null && updateCntr > 0 && !tx.local();
+
+ WALPointer logPtr = null;
+
+ ensureFreeSpace();
+
+ lockEntry();
+
+ try {
+ checkObsolete();
+
+ CacheObject val = null;
+
+ for (int i = 0; i < entries.size(); i++) {
+ GridCacheMvccEntryInfo info = (GridCacheMvccEntryInfo)entries.get(i);
+
+ if (val == null && op != DELETE && MvccUtils.compare(info.mvccVersion(),
+ mvccVer.coordinatorVersion(),
+ mvccVer.counter(),
+ mvccVer.operationCounter()) == 0)
+ val = info.value();
+
+ cctx.offheap().mvccUpdateRowWithPreloadInfo(this,
+ info.value(),
+ info.version(),
+ info.expireTime(),
+ info.mvccVersion(),
+ info.newMvccVersion(),
+ info.mvccTxState(),
+ info.newMvccTxState());
+ }
+
+ if (cctx.deferredDelete() && deletedUnlocked() && !detached())
+ deletedUnlocked(false);
+
+ long expireTime = CU.EXPIRE_TIME_ETERNAL;
+ long ttl = CU.TTL_ETERNAL;
+
+ GridCacheVersion ver = tx.writeVersion();
+
+ if (cctx.group().persistenceEnabled() && cctx.group().walEnabled())
+ logPtr = cctx.shared().wal().log(new DataRecord(new DataEntry(
+ cctx.cacheId(),
+ key,
+ val,
+ op,
+ tx.nearXidVersion(),
+ ver,
+ CU.EXPIRE_TIME_ETERNAL,
+ key.partition(),
+ updateCntr)));
+
+ update(val, expireTime, ttl, ver, true);
+
+ mvccDrReplicate(DR_BACKUP, val, ver, topVer, mvccVer);
+
+ recordNodeId(affNodeId, topVer);
+ }
+ finally {
+ if (lockedByCurrentThread()) {
+ unlockEntry();
+
+ cctx.evicts().touch(this, AffinityTopologyVersion.NONE);
+ }
+ }
+
+ return new GridCacheUpdateTxResult(true, logPtr);
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccEntryInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccEntryInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccEntryInfo.java
new file mode 100644
index 0000000..8df78ed
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccEntryInfo.java
@@ -0,0 +1,257 @@
+/*
+ * 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;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_BIT_OFF;
+import static org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO.MVCC_HINTS_MASK;
+
+/**
+ *
+ */
+public class GridCacheMvccEntryInfo extends GridCacheEntryInfo implements MvccVersionAware, MvccUpdateVersionAware {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private long mvccCrdVer;
+
+ /** */
+ private long mvccCntr;
+
+ /** */
+ private int mvccOpCntr;
+
+ /** */
+ private long newMvccCrdVer;
+
+ /** */
+ private long newMvccCntr;
+
+ /** */
+ private int newMvccOpCntr;
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCoordinatorVersion() {
+ return newMvccCrdVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long newMvccCounter() {
+ return newMvccCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int newMvccOperationCounter() {
+ return newMvccOpCntr & ~MVCC_HINTS_MASK;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte newMvccTxState() {
+ return (byte)(newMvccOpCntr >>> MVCC_HINTS_BIT_OFF);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCoordinatorVersion() {
+ return mvccCrdVer;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long mvccCounter() {
+ return mvccCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int mvccOperationCounter() {
+ return mvccOpCntr & ~MVCC_HINTS_MASK;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte mvccTxState() {
+ return (byte)(mvccOpCntr >>> MVCC_HINTS_BIT_OFF);
+ }
+
+ /**
+ * @param mvccTxState Mvcc version Tx state hint.
+ */
+ public void mvccTxState(byte mvccTxState) {
+ mvccOpCntr = (mvccOpCntr & ~MVCC_HINTS_MASK) | ((int)mvccTxState << MVCC_HINTS_BIT_OFF);
+ }
+
+ /**
+ * @param newMvccTxState New mvcc version Tx state hint.
+ */
+ public void newMvccTxState(byte newMvccTxState) {
+ newMvccOpCntr = (newMvccOpCntr & ~MVCC_HINTS_MASK) | ((int)newMvccTxState << MVCC_HINTS_BIT_OFF);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void newMvccVersion(long crd, long cntr, int opCntr) {
+ newMvccCrdVer = crd;
+ newMvccCntr = cntr;
+ newMvccOpCntr = opCntr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void mvccVersion(long crd, long cntr, int opCntr) {
+ mvccCrdVer = crd;
+ mvccCntr = cntr;
+ mvccOpCntr = opCntr;
+ }
+
+ /** {@inheritDoc} */
+ public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!super.writeTo(buf, writer))
+ return false;
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 6:
+ if (!writer.writeLong("mvccCntr", mvccCntr))
+ return false;
+
+ writer.incrementState();
+
+ case 7:
+ if (!writer.writeLong("mvccCrdVer", mvccCrdVer))
+ return false;
+
+ writer.incrementState();
+
+ case 8:
+ if (!writer.writeInt("mvccOpCntr", mvccOpCntr))
+ return false;
+
+ writer.incrementState();
+
+ case 9:
+ if (!writer.writeLong("newMvccCntr", newMvccCntr))
+ return false;
+
+ writer.incrementState();
+
+ case 10:
+ if (!writer.writeLong("newMvccCrdVer", newMvccCrdVer))
+ return false;
+
+ writer.incrementState();
+
+ case 11:
+ if (!writer.writeInt("newMvccOpCntr", newMvccOpCntr))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ if (!super.readFrom(buf, reader))
+ return false;
+
+ switch (reader.state()) {
+ case 6:
+ mvccCntr = reader.readLong("mvccCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 7:
+ mvccCrdVer = reader.readLong("mvccCrdVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 8:
+ mvccOpCntr = reader.readInt("mvccOpCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 9:
+ newMvccCntr = reader.readLong("newMvccCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 10:
+ newMvccCrdVer = reader.readLong("newMvccCrdVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 11:
+ newMvccOpCntr = reader.readInt("newMvccOpCntr");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridCacheMvccEntryInfo.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 12;
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return 143;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(GridCacheMvccEntryInfo.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheOperation.java
index 555f825..377f95f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheOperation.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheOperation.java
@@ -17,7 +17,7 @@
package org.apache.ignite.internal.processors.cache;
-import org.jetbrains.annotations.*;
+import org.jetbrains.annotations.Nullable;
/**
* Cache value operations.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
index 9d88152..ca2d1c8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
@@ -894,7 +894,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
* @param ver Topology version.
* @return Future or {@code null} is future is already completed.
*/
- @Nullable public IgniteInternalFuture<?> affinityReadyFuture(AffinityTopologyVersion ver) {
+ @Nullable public IgniteInternalFuture<AffinityTopologyVersion> affinityReadyFuture(AffinityTopologyVersion ver) {
GridDhtPartitionsExchangeFuture lastInitializedFut0 = lastInitializedFut;
if (lastInitializedFut0 != null && lastInitializedFut0.initialVersion().compareTo(ver) == 0) {
@@ -1873,9 +1873,15 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
IgniteTxManager tm = cctx.tm();
if (tm != null) {
- U.warn(diagnosticLog, "Pending transactions:");
+ boolean first = true;
for (IgniteInternalTx tx : tm.activeTransactions()) {
+ if (first) {
+ U.warn(diagnosticLog, "Pending transactions:");
+
+ first = false;
+ }
+
if (exchTopVer != null) {
U.warn(diagnosticLog, ">>> [txVer=" + tx.topologyVersionSnapshot() +
", exchWait=" + tm.needWaitTransaction(tx, exchTopVer) +
@@ -1889,31 +1895,66 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
GridCacheMvccManager mvcc = cctx.mvcc();
if (mvcc != null) {
- U.warn(diagnosticLog, "Pending explicit locks:");
+ boolean first = true;
+
+ for (GridCacheExplicitLockSpan lockSpan : mvcc.activeExplicitLocks()) {
+ if (first) {
+ U.warn(diagnosticLog, "Pending explicit locks:");
+
+ first = false;
+ }
- for (GridCacheExplicitLockSpan lockSpan : mvcc.activeExplicitLocks())
U.warn(diagnosticLog, ">>> " + lockSpan);
+ }
- U.warn(diagnosticLog, "Pending cache futures:");
+ first = true;
+
+ for (GridCacheFuture<?> fut : mvcc.activeFutures()) {
+ if (first) {
+ U.warn(diagnosticLog, "Pending cache futures:");
+
+ first = false;
+ }
- for (GridCacheFuture<?> fut : mvcc.activeFutures())
dumpDiagnosticInfo(fut, diagCtx);
+ }
- U.warn(diagnosticLog, "Pending atomic cache futures:");
+ first = true;
+
+ for (GridCacheFuture<?> fut : mvcc.atomicFutures()) {
+ if (first) {
+ U.warn(diagnosticLog, "Pending atomic cache futures:");
+
+ first = false;
+ }
- for (GridCacheFuture<?> fut : mvcc.atomicFutures())
dumpDiagnosticInfo(fut, diagCtx);
+ }
+
+ first = true;
- U.warn(diagnosticLog, "Pending data streamer futures:");
+ for (IgniteInternalFuture<?> fut : mvcc.dataStreamerFutures()) {
+ if (first) {
+ U.warn(diagnosticLog, "Pending data streamer futures:");
+
+ first = false;
+ }
- for (IgniteInternalFuture<?> fut : mvcc.dataStreamerFutures())
dumpDiagnosticInfo(fut, diagCtx);
+ }
if (tm != null) {
- U.warn(diagnosticLog, "Pending transaction deadlock detection futures:");
+ first = true;
+
+ for (IgniteInternalFuture<?> fut : tm.deadlockDetectionFutures()) {
+ if (first) {
+ U.warn(diagnosticLog, "Pending transaction deadlock detection futures:");
+
+ first = false;
+ }
- for (IgniteInternalFuture<?> fut : tm.deadlockDetectionFutures())
dumpDiagnosticInfo(fut, diagCtx);
+ }
}
}
@@ -1935,6 +1976,8 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
affDumpCnt++;
}
}
+
+ cctx.kernalContext().coordinators().dumpDebugInfo(diagnosticLog, diagCtx);
}
/**
@@ -2070,12 +2113,21 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
ClusterNode node = evt.eventNode();
+ if ((evt.type() == EVT_NODE_FAILED || evt.type() == EVT_NODE_LEFT) &&
+ node.equals(cctx.coordinators().currentCoordinator())) {
+ if (log.isInfoEnabled())
+ log.info("Stop merge, need exchange for mvcc coordinator failure: " + node);
+
+ break;
+ }
+
if (!curFut.context().supportsMergeExchanges(node)) {
if (log.isInfoEnabled())
log.info("Stop merge, node does not support merge: " + node);
break;
}
+
if (evt.type() == EVT_NODE_JOINED && cctx.cache().hasCachesReceivedFromJoin(node)) {
if (log.isInfoEnabled())
log.info("Stop merge, received caches from node: " + node);
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index eb64d9c..e0a2420 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
@@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
+import javax.cache.expiry.EternalExpiryPolicy;
import javax.management.MBeanServer;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
@@ -97,7 +98,6 @@ import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabase
import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList;
-import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage;
import org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteCacheSnapshotManager;
import org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotDiscoveryMessage;
import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
@@ -165,6 +165,7 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_CACHE_REMOVED_ENTR
import static org.apache.ignite.IgniteSystemProperties.IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK;
import static org.apache.ignite.IgniteSystemProperties.getBoolean;
import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
import static org.apache.ignite.cache.CacheMode.LOCAL;
import static org.apache.ignite.cache.CacheMode.PARTITIONED;
import static org.apache.ignite.cache.CacheMode.REPLICATED;
@@ -542,6 +543,24 @@ public class GridCacheProcessor extends GridProcessorAdapter {
"other cache types [cacheName=" + cc.getName() + ", groupName=" + cc.getGroupName() +
", cacheType=" + cacheType + "]");
+ if (cc.getAtomicityMode() == TRANSACTIONAL && c.isMvccEnabled()) {
+ if (cc.getCacheStoreFactory() != null) {
+ throw new IgniteCheckedException("Transactional cache may not have a third party cache store when " +
+ "MVCC is enabled.");
+ }
+
+ if (cc.getExpiryPolicyFactory() != null && !(cc.getExpiryPolicyFactory().create() instanceof
+ EternalExpiryPolicy)) {
+ throw new IgniteCheckedException("Transactional cache may not have expiry policy when " +
+ "MVCC is enabled.");
+ }
+
+ if (cc.getInterceptor() != null) {
+ throw new IgniteCheckedException("Transactional cache may not have an interceptor when " +
+ "MVCC is enabled.");
+ }
+ }
+
// Make sure we do not use sql schema for system views.
if (ctx.query().moduleEnabled()) {
String schema = QueryUtils.normalizeSchemaName(cc.getName(), cc.getSqlSchema());
@@ -1224,7 +1243,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
", memoryPolicyName=" + memPlcName +
", mode=" + cfg.getCacheMode() +
", atomicity=" + cfg.getAtomicityMode() +
- ", backups=" + cfg.getBackups() + ']');
+ ", backups=" + cfg.getBackups() +
+ ", mvcc=" + cacheCtx.mvccEnabled() +']');
}
}
@@ -3408,7 +3428,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
CacheConfiguration ccfg = req.startCacheConfiguration();
try {
- cachesInfo.validateStartCacheConfiguration(ccfg);
+ cachesInfo.validateStartCacheConfiguration(ccfg, req.cacheType());
}
catch (IgniteCheckedException e) {
fut.onDone(e);
@@ -4464,7 +4484,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
if (ccfg != null) {
cloneCheckSerializable(ccfg);
- if (desc != null || MetaStorage.METASTORAGE_CACHE_NAME.equals(cacheName)) {
+ if (desc != null) {
if (failIfExists) {
throw new CacheExistsException("Failed to start cache " +
"(a cache with the same name is already started): " + cacheName);
[29/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessorImpl.java
new file mode 100644
index 0000000..31d3b61
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccProcessorImpl.java
@@ -0,0 +1,2187 @@
+/*
+ * 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.mvcc;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.events.DiscoveryEvent;
+import org.apache.ignite.events.Event;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteDiagnosticPrepareContext;
+import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.IgniteNodeAttributes;
+import org.apache.ignite.internal.NodeStoppingException;
+import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.managers.communication.GridMessageListener;
+import org.apache.ignite.internal.managers.discovery.DiscoCache;
+import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
+import org.apache.ignite.internal.processors.GridProcessorAdapter;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.ExchangeContext;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestQueryCntr;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestQueryId;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTx;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTxAndQueryCntr;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccAckRequestTxAndQueryId;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccActiveQueriesMessage;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccFutureResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccMessage;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccQuerySnapshotRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccSnapshotResponse;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccTxSnapshotRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.msg.MvccWaitTxsRequest;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxKey;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog;
+import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.DatabaseLifecycleListener;
+import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccDataRow;
+import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccLinkAwareSearchRow;
+import org.apache.ignite.internal.util.GridAtomicLong;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.future.GridCompoundFuture;
+import org.apache.ignite.internal.util.future.GridCompoundIdentityFuture;
+import org.apache.ignite.internal.util.future.GridFinishedFuture;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.lang.GridCursor;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.spi.IgniteNodeValidationResult;
+import org.apache.ignite.spi.discovery.DiscoveryDataBag;
+import org.apache.ignite.thread.IgniteThread;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.events.EventType.EVT_CLIENT_NODE_DISCONNECTED;
+import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
+import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
+import static org.apache.ignite.events.EventType.EVT_NODE_METRICS_UPDATED;
+import static org.apache.ignite.events.EventType.EVT_NODE_SEGMENTED;
+import static org.apache.ignite.internal.GridTopic.TOPIC_CACHE_COORDINATOR;
+import static org.apache.ignite.internal.events.DiscoveryCustomEvent.EVT_DISCOVERY_CUSTOM_EVT;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SYSTEM_POOL;
+import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker.MVCC_TRACKER_ID_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_CRD_COUNTER_NA;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_INITIAL_CNTR;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_READ_OP_CNTR;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_START_CNTR;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.MVCC_START_OP_CNTR;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.compare;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.hasNewVersion;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.isVisible;
+import static org.apache.ignite.internal.processors.cache.mvcc.MvccUtils.noCoordinatorError;
+import static org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog.TX_LOG_CACHE_ID;
+import static org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog.TX_LOG_CACHE_NAME;
+import static org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.RowData.KEY_ONLY;
+
+/**
+ * MVCC processor.
+ */
+@SuppressWarnings("serial")
+class MvccProcessorImpl extends GridProcessorAdapter implements MvccProcessor, DatabaseLifecycleListener {
+ /** */
+ private static final Waiter LOCAL_TRANSACTION_MARKER = new LocalTransactionMarker();
+
+ /** Dummy tx for vacuum. */
+ private static final IgniteInternalTx DUMMY_TX = new GridNearTxLocal();
+
+ /** For tests only. */
+ private static IgniteClosure<Collection<ClusterNode>, ClusterNode> crdC;
+
+ /**
+ * For testing only.
+ *
+ * @param crdC Closure assigning coordinator.
+ */
+ static void coordinatorAssignClosure(IgniteClosure<Collection<ClusterNode>, ClusterNode> crdC) {
+ MvccProcessorImpl.crdC = crdC;
+ }
+
+ /** Topology version when local node was assigned as coordinator. */
+ private long crdVer;
+
+ /** */
+ private volatile MvccCoordinator curCrd;
+
+ /** */
+ private TxLog txLog;
+
+ /** */
+ private List<GridWorker> vacuumWorkers;
+
+ /** */
+ private BlockingQueue<VacuumTask> cleanupQueue;
+
+ /**
+ * Vacuum mutex. Prevents concurrent vacuum while start/stop operations
+ */
+ private final Object mux = new Object();
+
+ /** For tests only. */
+ private volatile Throwable vacuumError;
+
+ /** */
+ private MvccDiscoveryData discoData = new MvccDiscoveryData(null);
+
+ /** */
+ private final GridAtomicLong futIdCntr = new GridAtomicLong(0);
+
+ /** */
+ private final GridAtomicLong mvccCntr = new GridAtomicLong(MVCC_START_CNTR);
+
+ /** */
+ private final GridAtomicLong committedCntr = new GridAtomicLong(MVCC_INITIAL_CNTR);
+
+ /** */
+ private final Map<Long, Long> activeTxs = new HashMap<>();
+
+ /** Active query trackers. */
+ private final Map<Long, MvccQueryTracker> activeTrackers = new ConcurrentHashMap<>();
+
+ /** */
+ private final Map<UUID, Map<Long, MvccSnapshotResponseListener>> snapLsnrs = new ConcurrentHashMap<>();
+
+ /** */
+ private final Map<Long, WaitAckFuture> ackFuts = new ConcurrentHashMap<>();
+
+ /** */
+ private final Map<Long, GridFutureAdapter> waitTxFuts = new ConcurrentHashMap<>();
+
+ /** */
+ private final Map<TxKey, Waiter> waitMap = new ConcurrentHashMap<>();
+
+ /** */
+ private final ActiveQueries activeQueries = new ActiveQueries();
+
+ /** */
+ private final MvccPreviousCoordinatorQueries prevCrdQueries = new MvccPreviousCoordinatorQueries();
+
+ /** */
+ private final GridFutureAdapter<Void> initFut = new GridFutureAdapter<>();
+
+ /**
+ * @param ctx Context.
+ */
+ public MvccProcessorImpl(GridKernalContext ctx) {
+ super(ctx);
+
+ ctx.internalSubscriptionProcessor().registerDatabaseListener(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void start() throws IgniteCheckedException {
+ ctx.addNodeAttribute(IgniteNodeAttributes.ATTR_MVCC_ENABLED, Boolean.TRUE);
+
+ ctx.event().addLocalEventListener(new CacheCoordinatorNodeFailListener(),
+ EVT_NODE_FAILED, EVT_NODE_LEFT);
+
+ ctx.io().addMessageListener(TOPIC_CACHE_COORDINATOR, new CoordinatorMessageListener());
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public IgniteNodeValidationResult validateNode(ClusterNode node) {
+ Boolean rmtEnabled = node.attribute(IgniteNodeAttributes.ATTR_MVCC_ENABLED);
+
+ if (rmtEnabled == null) {
+ String errMsg = "Failed to add node to topology because MVCC is enabled on cluster and " +
+ "the node doesn't support MVCC or MVCC is disabled for the node [nodeId=" + node.id() + ']';
+
+ return new IgniteNodeValidationResult(node.id(), errMsg, errMsg);
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void beforeStop(IgniteCacheDatabaseSharedManager mgr) {
+ stopVacuumWorkers();
+
+ txLog = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onInitDataRegions(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException {
+ DataStorageConfiguration dscfg = dataStorageConfiguration();
+
+ mgr.addDataRegion(
+ dscfg,
+ createTxLogRegion(dscfg),
+ CU.isPersistenceEnabled(ctx.config()));
+ }
+
+ /** {@inheritDoc} */
+ @Override public void afterInitialise(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException {
+ if (!CU.isPersistenceEnabled(ctx.config())) {
+ assert txLog == null;
+
+ txLog = new TxLog(ctx, mgr);
+
+ startVacuumWorkers();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("ConstantConditions")
+ @Override public void beforeMemoryRestore(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException {
+ assert CU.isPersistenceEnabled(ctx.config());
+ assert txLog == null;
+
+ ctx.cache().context().pageStore().initialize(TX_LOG_CACHE_ID, 1,
+ TX_LOG_CACHE_NAME, mgr.dataRegion(TX_LOG_CACHE_NAME).memoryMetrics());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void afterMemoryRestore(IgniteCacheDatabaseSharedManager mgr) throws IgniteCheckedException {
+ assert CU.isPersistenceEnabled(ctx.config());
+ assert txLog == null;
+
+ txLog = new TxLog(ctx, mgr);
+
+ startVacuumWorkers();
+ }
+
+ /** {@inheritDoc} */
+ @Override public DiscoveryDataExchangeType discoveryDataType() {
+ return DiscoveryDataExchangeType.CACHE_CRD_PROC;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("ConstantConditions")
+ @Override public void collectGridNodeData(DiscoveryDataBag dataBag) {
+ Integer cmpId = discoveryDataType().ordinal();
+
+ if (!dataBag.commonDataCollectedFor(cmpId))
+ dataBag.addGridCommonData(cmpId, discoData);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onGridDataReceived(DiscoveryDataBag.GridDiscoveryData data) {
+ MvccDiscoveryData discoData0 = (MvccDiscoveryData)data.commonData();
+
+ // Disco data might be null in case the first joined node is daemon.
+ if (discoData0 != null) {
+ discoData = discoData0;
+
+ log.info("Received mvcc coordinator on node join: " + discoData.coordinator());
+
+ assert discoData != null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onDiscoveryEvent(int evtType, Collection<ClusterNode> nodes, long topVer) {
+ if (evtType == EVT_NODE_METRICS_UPDATED || evtType == EVT_DISCOVERY_CUSTOM_EVT)
+ return;
+
+ MvccCoordinator crd;
+
+ if (evtType == EVT_NODE_SEGMENTED || evtType == EVT_CLIENT_NODE_DISCONNECTED)
+ crd = null;
+ else {
+ crd = discoData.coordinator();
+
+ if (crd == null ||
+ ((evtType == EVT_NODE_FAILED || evtType == EVT_NODE_LEFT) && !F.nodeIds(nodes).contains(crd.nodeId()))) {
+ ClusterNode crdNode = null;
+
+ if (crdC != null) {
+ crdNode = crdC.apply(nodes);
+
+ if (log.isInfoEnabled())
+ log.info("Assigned coordinator using test closure: " + crd);
+ }
+ else {
+ // Expect nodes are sorted by order.
+ for (ClusterNode node : nodes) {
+ if (!node.isClient()) {
+ crdNode = node;
+
+ break;
+ }
+ }
+ }
+
+ crd = crdNode != null ? new MvccCoordinator(crdNode.id(), coordinatorVersion(topVer), new AffinityTopologyVersion(topVer, 0)) : null;
+
+ if (crd != null) {
+ if (log.isInfoEnabled())
+ log.info("Assigned mvcc coordinator [crd=" + crd + ", crdNode=" + crdNode + ']');
+ }
+ else
+ U.warn(log, "New mvcc coordinator was not assigned [topVer=" + topVer + ']');
+ }
+ }
+
+ discoData = new MvccDiscoveryData(crd);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onExchangeStart(MvccCoordinator mvccCrd, ExchangeContext exchCtx, ClusterNode exchCrd) {
+ if (!exchCtx.newMvccCoordinator())
+ return;
+
+ GridLongList activeQryTrackers = collectActiveQueryTrackers();
+
+ exchCtx.addActiveQueries(ctx.localNodeId(), activeQryTrackers);
+
+ if (exchCrd == null || !mvccCrd.nodeId().equals(exchCrd.id())) {
+ try {
+ sendMessage(mvccCrd.nodeId(), new MvccActiveQueriesMessage(activeQryTrackers));
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send active queries to mvcc coordinator: " + e);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onExchangeDone(boolean newCoord, DiscoCache discoCache, Map<UUID, GridLongList> activeQueries) {
+ if (!newCoord)
+ return;
+
+ ctx.cache().context().tm().rollbackMvccTxOnCoordinatorChange();
+
+ if (ctx.localNodeId().equals(curCrd.nodeId())) {
+ assert ctx.localNodeId().equals(curCrd.nodeId());
+
+ MvccCoordinator crd = discoCache.mvccCoordinator();
+
+ assert crd != null;
+
+ // No need to re-initialize if coordinator version hasn't changed (e.g. it was cluster activation).
+ if (crdVer == crd.coordinatorVersion())
+ return;
+
+ crdVer = crd.coordinatorVersion();
+
+ log.info("Initialize local node as mvcc coordinator [node=" + ctx.localNodeId() +
+ ", crdVer=" + crdVer + ']');
+
+ prevCrdQueries.init(activeQueries, discoCache, ctx.discovery());
+
+ initFut.onDone();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void processClientActiveQueries(UUID nodeId, @Nullable GridLongList activeQueries) {
+ prevCrdQueries.addNodeActiveQueries(nodeId, activeQueries);
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable public MvccCoordinator currentCoordinator() {
+ return currentCoordinator(AffinityTopologyVersion.NONE);
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable public MvccCoordinator currentCoordinator(AffinityTopologyVersion topVer) {
+ MvccCoordinator crd = curCrd;
+
+ // Assert coordinator did not already change.
+ assert crd == null
+ || topVer == AffinityTopologyVersion.NONE
+ || crd.topologyVersion().compareTo(topVer) <= 0 : "Invalid coordinator [crd=" + crd + ", topVer=" + topVer + ']';
+
+ return crd;
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable public MvccCoordinator coordinatorFromDiscoveryEvent() {
+ return discoData != null ? discoData.coordinator() : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public UUID currentCoordinatorId() {
+ MvccCoordinator curCrd = this.curCrd;
+
+ return curCrd != null ? curCrd.nodeId() : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateCoordinator(MvccCoordinator curCrd) {
+ this.curCrd = curCrd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte state(long crdVer, long cntr) throws IgniteCheckedException {
+ return txLog.get(crdVer, cntr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte state(MvccVersion ver) throws IgniteCheckedException {
+ return txLog.get(ver.coordinatorVersion(), ver.counter());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateState(MvccVersion ver, byte state) throws IgniteCheckedException {
+ updateState(ver, state, true);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void updateState(MvccVersion ver, byte state, boolean primary) throws IgniteCheckedException {
+ TxKey key = new TxKey(ver.coordinatorVersion(), ver.counter());
+
+ txLog.put(key, state, primary);
+
+ Waiter waiter;
+
+ if (primary && (state == TxState.ABORTED || state == TxState.COMMITTED)
+ && (waiter = waitMap.remove(key)) != null)
+ waiter.run(ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void registerLocalTransaction(long crd, long cntr) {
+ Waiter old = waitMap.putIfAbsent(new TxKey(crd, cntr), LOCAL_TRANSACTION_MARKER);
+
+ assert old == null || old.hasLocalTransaction();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasLocalTransaction(long crd, long cntr) {
+ Waiter waiter = waitMap.get(new TxKey(crd, cntr));
+
+ return waiter != null && waiter.hasLocalTransaction();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> waitFor(GridCacheContext cctx, MvccVersion locked) throws IgniteCheckedException {
+ TxKey key = new TxKey(locked.coordinatorVersion(), locked.counter());
+
+ LockFuture fut = new LockFuture(cctx.ioPolicy());
+
+ Waiter waiter = waitMap.merge(key, fut, Waiter::concat);
+
+ byte state = txLog.get(key);
+
+ if ((state == TxState.ABORTED || state == TxState.COMMITTED)
+ && !waiter.hasLocalTransaction() && (waiter = waitMap.remove(key)) != null)
+ waiter.run(ctx);
+
+ return fut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void addQueryTracker(MvccQueryTracker tracker) {
+ assert tracker.id() != MVCC_TRACKER_ID_NA;
+
+ MvccQueryTracker tr = activeTrackers.put(tracker.id(), tracker);
+
+ assert tr == null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void removeQueryTracker(Long id) {
+ activeTrackers.remove(id);
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot tryRequestSnapshotLocal() throws ClusterTopologyCheckedException {
+ return tryRequestSnapshotLocal(null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public MvccSnapshot tryRequestSnapshotLocal(@Nullable IgniteInternalTx tx) throws ClusterTopologyCheckedException {
+ MvccCoordinator crd = currentCoordinator();
+
+ if (crd == null)
+ throw noCoordinatorError();
+
+ if (tx != null) {
+ AffinityTopologyVersion topVer = ctx.cache().context().lockedTopologyVersion(null);
+
+ if (topVer != null && topVer.compareTo(crd.topologyVersion()) < 0)
+ throw new ClusterTopologyCheckedException("Mvcc coordinator is outdated " +
+ "for the locked topology version. [crd=" + crd + ", tx=" + tx + ']');
+ }
+
+ if (!ctx.localNodeId().equals(crd.nodeId()) || !initFut.isDone())
+ return null;
+ else if (tx != null)
+ return assignTxSnapshot(0L);
+ else
+ return activeQueries.assignQueryCounter(ctx.localNodeId(), 0L);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshotAsync() {
+ return requestSnapshotAsync((IgniteInternalTx)null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<MvccSnapshot> requestSnapshotAsync(IgniteInternalTx tx) {
+ MvccSnapshotFuture fut = new MvccSnapshotFuture();
+
+ requestSnapshotAsync(tx, fut);
+
+ return fut;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void requestSnapshotAsync(MvccSnapshotResponseListener lsnr) {
+ requestSnapshotAsync(null, lsnr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void requestSnapshotAsync(IgniteInternalTx tx, MvccSnapshotResponseListener lsnr) {
+ MvccCoordinator crd = currentCoordinator();
+
+ if (crd == null) {
+ lsnr.onError(noCoordinatorError());
+
+ return;
+ }
+
+ if (tx != null) {
+ AffinityTopologyVersion topVer = ctx.cache().context().lockedTopologyVersion(null);
+
+ if (topVer != null && topVer.compareTo(crd.topologyVersion()) < 0) {
+ lsnr.onError(new ClusterTopologyCheckedException("Mvcc coordinator is outdated " +
+ "for the locked topology version. [crd=" + crd + ", tx=" + tx + ']'));
+
+ return;
+ }
+ }
+
+ if (ctx.localNodeId().equals(crd.nodeId())) {
+ if (!initFut.isDone()) {
+ // Wait for the local coordinator init.
+ initFut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ requestSnapshotAsync(tx, lsnr);
+ }
+ });
+ }
+ else if (tx != null)
+ lsnr.onResponse(assignTxSnapshot(0L));
+ else
+ lsnr.onResponse(activeQueries.assignQueryCounter(ctx.localNodeId(), 0L));
+
+ return;
+ }
+
+ // Send request to the remote coordinator.
+ UUID nodeId = crd.nodeId();
+
+ long id = futIdCntr.incrementAndGet();
+
+ Map<Long, MvccSnapshotResponseListener> map = snapLsnrs.get(nodeId), map0;
+
+ if (map == null && (map0 = snapLsnrs.putIfAbsent(nodeId, map = new ConcurrentHashMap<>())) != null)
+ map = map0;
+
+ map.put(id, lsnr);
+
+ try {
+ sendMessage(nodeId, tx != null ? new MvccTxSnapshotRequest(id) : new MvccQuerySnapshotRequest(id));
+ }
+ catch (IgniteCheckedException e) {
+ if (map.remove(id) != null)
+ lsnr.onError(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> ackTxCommit(MvccSnapshot updateVer) {
+ return ackTxCommit(updateVer, null, 0L);
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> ackTxCommit(MvccVersion updateVer, MvccSnapshot readSnapshot,
+ long qryId) {
+ assert updateVer != null;
+
+ MvccCoordinator crd = curCrd;
+
+ if (updateVer.coordinatorVersion() == crd.coordinatorVersion())
+ return sendTxCommit(crd, createTxAckMessage(futIdCntr.incrementAndGet(), updateVer, readSnapshot, qryId));
+ else if (readSnapshot != null)
+ ackQueryDone(readSnapshot, qryId);
+
+ return new GridFinishedFuture<>();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void ackTxRollback(MvccVersion updateVer) {
+ assert updateVer != null;
+
+ MvccCoordinator crd = curCrd;
+
+ if (crd.coordinatorVersion() != updateVer.coordinatorVersion())
+ return;
+
+ MvccAckRequestTx msg = createTxAckMessage(-1, updateVer, null, 0L);
+
+ msg.skipResponse(true);
+
+ try {
+ sendMessage(crd.nodeId(), msg);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send tx rollback ack, node left [msg=" + msg + ", node=" + crd.nodeId() + ']');
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send tx rollback ack [msg=" + msg + ", node=" + crd.nodeId() + ']', e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void ackTxRollback(MvccVersion updateVer, MvccSnapshot readSnapshot, long qryTrackerId) {
+ assert updateVer != null;
+
+ MvccCoordinator crd = curCrd;
+
+ if (crd.coordinatorVersion() != updateVer.coordinatorVersion()) {
+ if (readSnapshot != null)
+ ackQueryDone(readSnapshot, qryTrackerId);
+
+ return;
+ }
+
+ MvccAckRequestTx msg = createTxAckMessage(-1, updateVer, readSnapshot, qryTrackerId);
+
+ msg.skipResponse(true);
+
+ try {
+ sendMessage(crd.nodeId(), msg);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send tx rollback ack, node left [msg=" + msg + ", node=" + crd.nodeId() + ']');
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send tx rollback ack [msg=" + msg + ", node=" + crd.nodeId() + ']', e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void ackQueryDone(MvccSnapshot snapshot, long qryId) {
+ assert snapshot != null;
+
+ MvccCoordinator crd = currentCoordinator();
+
+ if (crd == null || crd.coordinatorVersion() == snapshot.coordinatorVersion()
+ && sendQueryDone(crd, new MvccAckRequestQueryCntr(queryTrackCounter(snapshot))))
+ return;
+
+ Message msg = new MvccAckRequestQueryId(qryId);
+
+ do {
+ crd = currentCoordinator();
+ }
+ while (!sendQueryDone(crd, msg));
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteInternalFuture<Void> waitTxsFuture(UUID crdId, GridLongList txs) {
+ assert crdId != null;
+ assert txs != null && txs.size() > 0;
+
+ WaitAckFuture fut = new WaitAckFuture(futIdCntr.incrementAndGet(), crdId, false);
+
+ ackFuts.put(fut.id, fut);
+
+ try {
+ sendMessage(crdId, new MvccWaitTxsRequest(fut.id, txs));
+ }
+ catch (IgniteCheckedException e) {
+ if (ackFuts.remove(fut.id) != null) {
+ if (e instanceof ClusterTopologyCheckedException)
+ fut.onDone(); // No need to wait, new coordinator will be assigned, finish without error.
+ else
+ fut.onDone(e);
+ }
+ }
+
+ return fut;
+ }
+
+ /** {@inheritDoc} */
+ // TODO: Proper use of diagnostic context.
+ @Override public void dumpDebugInfo(IgniteLogger log, @Nullable IgniteDiagnosticPrepareContext diagCtx) {
+ boolean first = true;
+
+ for (Map<Long, MvccSnapshotResponseListener> map : snapLsnrs.values()) {
+ if (first) {
+ U.warn(log, "Pending mvcc listener: ");
+
+ first = false;
+ }
+
+ for (MvccSnapshotResponseListener lsnr : map.values()) {
+ U.warn(log, ">>> " + lsnr.toString());
+ }
+ }
+
+ first = true;
+
+ for (WaitAckFuture waitAckFut : ackFuts.values()) {
+ if (first) {
+ U.warn(log, "Pending mvcc wait ack futures: ");
+
+ first = false;
+ }
+
+ U.warn(log, ">>> " + waitAckFut.toString());
+ }
+ }
+
+ /**
+ * Removes all less or equals to the given one records from Tx log.
+ *
+ * @param ver Version.
+ * @throws IgniteCheckedException If fails.
+ */
+ void removeUntil(MvccVersion ver) throws IgniteCheckedException {
+ txLog.removeUntil(ver.coordinatorVersion(), ver.counter());
+ }
+
+ /**
+ * TODO IGNITE-7966
+ *
+ * @return Data region configuration.
+ */
+ private DataRegionConfiguration createTxLogRegion(DataStorageConfiguration dscfg) {
+ DataRegionConfiguration cfg = new DataRegionConfiguration();
+
+ cfg.setName(TX_LOG_CACHE_NAME);
+ cfg.setInitialSize(dscfg.getSystemRegionInitialSize());
+ cfg.setMaxSize(dscfg.getSystemRegionMaxSize());
+ cfg.setPersistenceEnabled(CU.isPersistenceEnabled(dscfg));
+ return cfg;
+ }
+
+ /**
+ * @return Data storage configuration.
+ */
+ private DataStorageConfiguration dataStorageConfiguration() {
+ return ctx.config().getDataStorageConfiguration();
+ }
+
+ /**
+ * @param topVer Topology version.
+ * @return Coordinator version.
+ */
+ private long coordinatorVersion(long topVer) {
+ return topVer + ctx.discovery().gridStartTime();
+ }
+
+ /**
+ * @return Active queries list.
+ */
+ private GridLongList collectActiveQueryTrackers() {
+ assert curCrd != null;
+
+ GridLongList activeQryTrackers = new GridLongList();
+
+ for (MvccQueryTracker tracker : activeTrackers.values()) {
+ long trackerId = tracker.onMvccCoordinatorChange(curCrd);
+
+ if (trackerId != MVCC_TRACKER_ID_NA)
+ activeQryTrackers.add(trackerId);
+ }
+
+ return activeQryTrackers;
+ }
+
+ /**
+ * @return Counter.
+ */
+ private MvccSnapshotResponse assignTxSnapshot(long futId) {
+ assert initFut.isDone();
+ assert crdVer != 0;
+ assert ctx.localNodeId().equals(currentCoordinatorId());
+
+ MvccSnapshotResponse res = new MvccSnapshotResponse();
+
+ long ver, cleanup, tracking;
+
+ synchronized (this) {
+ ver = mvccCntr.incrementAndGet();
+ tracking = ver;
+ cleanup = committedCntr.get() + 1;
+
+ for (Map.Entry<Long, Long> txVer : activeTxs.entrySet()) {
+ cleanup = Math.min(txVer.getValue(), cleanup);
+ tracking = Math.min(txVer.getKey(), tracking);
+
+ res.addTx(txVer.getKey());
+ }
+
+ boolean add = activeTxs.put(ver, tracking) == null;
+
+ assert add : ver;
+ }
+
+ long minQry = activeQueries.minimalQueryCounter();
+
+ if (minQry != -1)
+ cleanup = Math.min(cleanup, minQry);
+
+ cleanup = prevCrdQueries.previousQueriesDone() ? cleanup - 1 : MVCC_COUNTER_NA;
+
+ res.init(futId, crdVer, ver, MVCC_START_OP_CNTR, cleanup, tracking);
+
+ return res;
+ }
+
+ /**
+ * @param txCntr Counter assigned to transaction.
+ */
+ private void onTxDone(Long txCntr, boolean committed) {
+ assert initFut.isDone();
+
+ GridFutureAdapter fut;
+
+ synchronized (this) {
+ activeTxs.remove(txCntr);
+
+ if (committed)
+ committedCntr.setIfGreater(txCntr);
+ }
+
+ fut = waitTxFuts.remove(txCntr);
+
+ if (fut != null)
+ fut.onDone();
+ }
+
+ /**
+ * @param mvccCntr Query counter.
+ */
+ private void onQueryDone(UUID nodeId, Long mvccCntr) {
+ activeQueries.onQueryDone(nodeId, mvccCntr);
+ }
+
+ /**
+ * @param futId Future ID.
+ * @param updateVer Update version.
+ * @param readSnapshot Optional read version.
+ * @param qryTrackerId Query tracker id.
+ * @return Message.
+ */
+ private MvccAckRequestTx createTxAckMessage(long futId, MvccVersion updateVer, MvccSnapshot readSnapshot,
+ long qryTrackerId) {
+ if (readSnapshot == null)
+ return new MvccAckRequestTx(futId, updateVer.counter());
+ else if (readSnapshot.coordinatorVersion() == updateVer.coordinatorVersion())
+ return new MvccAckRequestTxAndQueryCntr(futId, updateVer.counter(), queryTrackCounter(readSnapshot));
+ else
+ return new MvccAckRequestTxAndQueryId(futId, updateVer.counter(), qryTrackerId);
+ }
+
+ /**
+ * @param mvccVer Read version.
+ * @return Tracker counter.
+ */
+ private long queryTrackCounter(MvccSnapshot mvccVer) {
+ long trackCntr = mvccVer.counter();
+
+ MvccLongList txs = mvccVer.activeTransactions();
+
+ int size = txs.size();
+
+ for (int i = 0; i < size; i++) {
+ long txVer = txs.get(i);
+
+ if (txVer < trackCntr)
+ trackCntr = txVer;
+ }
+
+ return trackCntr;
+ }
+
+ /**
+ * Launches vacuum workers and scheduler.
+ */
+ void startVacuumWorkers() {
+ if (!ctx.clientNode()) {
+ synchronized (mux) {
+ if (vacuumWorkers == null) {
+ assert cleanupQueue == null;
+
+ cleanupQueue = new LinkedBlockingQueue<>();
+
+ vacuumWorkers = new ArrayList<>(ctx.config().getMvccVacuumThreadCnt() + 1);
+
+ vacuumWorkers.add(new VacuumScheduler(ctx, log, this));
+
+ for (int i = 0; i < ctx.config().getMvccVacuumThreadCnt(); i++) {
+ vacuumWorkers.add(new VacuumWorker(ctx, log, cleanupQueue));
+ }
+
+ for (GridWorker worker : vacuumWorkers) {
+ new IgniteThread(worker).start();
+ }
+
+ return;
+ }
+ }
+
+ U.warn(log, "Attempting to start active vacuum.");
+ }
+ }
+
+ /**
+ * Stops vacuum worker and scheduler.
+ */
+ void stopVacuumWorkers() {
+ if (!ctx.clientNode()) {
+ List<GridWorker> workers;
+ BlockingQueue<VacuumTask> queue;
+
+ synchronized (mux) {
+ workers = vacuumWorkers;
+ queue = cleanupQueue;
+
+ vacuumWorkers = null;
+ cleanupQueue = null;
+ }
+
+ if (workers == null) {
+ U.warn(log, "Attempting to stop inactive vacuum.");
+
+ return;
+ }
+
+ assert queue != null;
+
+ // Stop vacuum workers outside mutex to prevent deadlocks.
+ U.cancel(workers);
+ U.join(workers, log);
+
+ if (!queue.isEmpty()) {
+ IgniteCheckedException ex = vacuumCancelledException();
+
+ for (VacuumTask task : queue) {
+ task.onDone(ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * Runs vacuum process.
+ *
+ * @return {@code Future} with {@link VacuumMetrics}.
+ */
+ IgniteInternalFuture<VacuumMetrics> runVacuum() {
+ assert !ctx.clientNode();
+
+ MvccCoordinator crd0 = currentCoordinator();
+
+ if (Thread.currentThread().isInterrupted() ||
+ crd0 == null ||
+ crdVer == 0 && ctx.localNodeId().equals(crd0.nodeId()))
+ return new GridFinishedFuture<>(new VacuumMetrics());
+
+ final GridCompoundIdentityFuture<VacuumMetrics> res =
+ new GridCompoundIdentityFuture<>(new VacuumMetricsReducer());
+
+ MvccSnapshot snapshot;
+
+ try {
+ // TODO IGNITE-8974 create special method for getting cleanup version only.
+ snapshot = tryRequestSnapshotLocal(DUMMY_TX);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ throw new AssertionError(e);
+ }
+
+ if (snapshot != null)
+ continueRunVacuum(res, snapshot);
+ else
+ requestSnapshotAsync(DUMMY_TX, new MvccSnapshotResponseListener() {
+ @Override public void onResponse(MvccSnapshot s) {
+ continueRunVacuum(res, s);
+ }
+
+ @Override public void onError(IgniteCheckedException e) {
+ if (!(e instanceof ClusterTopologyCheckedException))
+ completeWithException(res, e);
+ else {
+ if (log.isDebugEnabled())
+ log.debug("Vacuum failed to receive an Mvcc snapshot. " +
+ "Need to retry on the stable topology. " + e.getMessage());
+
+ res.onDone(new VacuumMetrics());
+ }
+ }
+ });
+
+ return res;
+ }
+
+ /**
+ * For tests only.
+ *
+ * @return Vacuum error.
+ */
+ Throwable vacuumError() {
+ return vacuumError;
+ }
+
+ /**
+ * For tests only.
+ *
+ * @param e Vacuum error.
+ */
+ void vacuumError(Throwable e) {
+ this.vacuumError = e;
+ }
+
+ /**
+ * @param res Result.
+ * @param snapshot Snapshot.
+ */
+ private void continueRunVacuum(GridCompoundIdentityFuture<VacuumMetrics> res, MvccSnapshot snapshot) {
+ ackTxCommit(snapshot)
+ .listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ Throwable err;
+
+ if ((err = fut.error()) != null) {
+ U.error(log, "Vacuum error.", err);
+
+ res.onDone(err);
+ }
+ else if (snapshot.cleanupVersion() <= MVCC_COUNTER_NA)
+ res.onDone(new VacuumMetrics());
+ else {
+ try {
+ if (log.isDebugEnabled())
+ log.debug("Started vacuum with cleanup version=" + snapshot.cleanupVersion() + '.');
+
+ synchronized (mux) {
+ if (cleanupQueue == null) {
+ res.onDone(vacuumCancelledException());
+
+ return;
+ }
+
+ for (CacheGroupContext grp : ctx.cache().cacheGroups()) {
+ if (grp.mvccEnabled()) {
+ for (GridDhtLocalPartition part : grp.topology().localPartitions()) {
+ VacuumTask task = new VacuumTask(snapshot, part);
+
+ cleanupQueue.offer(task);
+
+ res.add(task);
+ }
+ }
+ }
+ }
+
+ res.listen(new CI1<IgniteInternalFuture<VacuumMetrics>>() {
+ @Override public void apply(IgniteInternalFuture<VacuumMetrics> fut) {
+ try {
+ VacuumMetrics metrics = fut.get();
+
+ if (U.assertionsEnabled()) {
+ MvccCoordinator crd = currentCoordinator();
+
+ assert crd != null
+ && crd.coordinatorVersion() >= snapshot.coordinatorVersion();
+
+ for (TxKey key : waitMap.keySet()) {
+ assert key.major() == snapshot.coordinatorVersion()
+ && key.minor() > snapshot.cleanupVersion()
+ || key.major() > snapshot.coordinatorVersion();
+ }
+ }
+
+ txLog.removeUntil(snapshot.coordinatorVersion(), snapshot.cleanupVersion());
+
+ if (log.isDebugEnabled())
+ log.debug("Vacuum completed. " + metrics);
+ }
+ catch (NodeStoppingException ignored) {
+ if (log.isDebugEnabled())
+ log.debug("Cannot complete vacuum (node is stopping).");
+ }
+ catch (Throwable e) {
+ U.error(log, "Vacuum error.", e);
+ }
+ }
+ });
+
+ res.markInitialized();
+ }
+ catch (Throwable e) {
+ completeWithException(res, e);
+ }
+ }
+ }
+ });
+ }
+
+ /** */
+ private void completeWithException(GridFutureAdapter fut, Throwable e) {
+ fut.onDone(e);
+
+ if (e instanceof Error)
+ throw (Error)e;
+ }
+
+ /** */
+ @NotNull private IgniteCheckedException vacuumCancelledException() {
+ return new NodeStoppingException("Operation has been cancelled (node is stopping).");
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param msg Message.
+ */
+ private void sendFutureResponse(UUID nodeId, MvccWaitTxsRequest msg) {
+ try {
+ sendMessage(nodeId, new MvccFutureResponse(msg.futureId()));
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send tx ack response, node left [msg=" + msg + ", node=" + nodeId + ']');
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send tx ack response [msg=" + msg + ", node=" + nodeId + ']', e);
+ }
+ }
+
+ /** */
+ @NotNull private IgniteInternalFuture<Void> sendTxCommit(MvccCoordinator crd, MvccAckRequestTx msg) {
+ WaitAckFuture fut = new WaitAckFuture(msg.futureId(), crd.nodeId(), true);
+
+ ackFuts.put(fut.id, fut);
+
+ try {
+ sendMessage(crd.nodeId(), msg);
+ }
+ catch (IgniteCheckedException e) {
+ if (ackFuts.remove(fut.id) != null) {
+ if (e instanceof ClusterTopologyCheckedException) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send tx ack, node left [crd=" + crd + ", msg=" + msg + ']');
+
+ fut.onDone(); // No need to ack, finish without error.
+ }
+ else
+
+ fut.onDone(e);
+ }
+ }
+
+ return fut;
+ }
+
+ /**
+ * @param crd Mvcc coordinator.
+ * @param msg Message.
+ * @return {@code True} if no need to resend the message to a new coordinator.
+ */
+ private boolean sendQueryDone(MvccCoordinator crd, Message msg) {
+ if (crd == null)
+ return true; // no need to send ack;
+
+ try {
+ sendMessage(crd.nodeId(), msg);
+
+ return true;
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send query ack, node left [crd=" + crd + ", msg=" + msg + ']');
+
+ MvccCoordinator crd0 = currentCoordinator();
+
+ // Coordinator is unassigned or still the same.
+ return crd0 == null || crd.coordinatorVersion() == crd0.coordinatorVersion();
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send query ack [crd=" + crd + ", msg=" + msg + ']', e);
+
+ return true;
+ }
+ }
+
+ /**
+ * Send IO message.
+ *
+ * @param nodeId Node ID.
+ * @param msg Message.
+ */
+ private void sendMessage(UUID nodeId, Message msg) throws IgniteCheckedException {
+ ctx.io().sendToGridTopic(nodeId, TOPIC_CACHE_COORDINATOR, msg, SYSTEM_POOL);
+ }
+
+ /**
+ * @param nodeId Sender node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorTxSnapshotRequest(UUID nodeId, MvccTxSnapshotRequest msg) {
+ ClusterNode node = ctx.discovery().node(nodeId);
+
+ if (node == null) {
+ if (log.isDebugEnabled())
+ log.debug("Ignore tx snapshot request processing, node left [msg=" + msg + ", node=" + nodeId + ']');
+
+ return;
+ }
+
+ MvccSnapshotResponse res = assignTxSnapshot(msg.futureId());
+
+ try {
+ sendMessage(node.id(), res);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send tx snapshot response, node left [msg=" + msg + ", node=" + nodeId + ']');
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send tx snapshot response [msg=" + msg + ", node=" + nodeId + ']', e);
+ }
+ }
+
+ /**
+ * @param nodeId Sender node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorQuerySnapshotRequest(UUID nodeId, MvccQuerySnapshotRequest msg) {
+ ClusterNode node = ctx.discovery().node(nodeId);
+
+ if (node == null) {
+ if (log.isDebugEnabled())
+ log.debug("Ignore query counter request processing, node left [msg=" + msg + ", node=" + nodeId + ']');
+
+ return;
+ }
+
+ MvccSnapshotResponse res = activeQueries.assignQueryCounter(nodeId, msg.futureId());
+
+ try {
+ sendMessage(node.id(), res);
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send query counter response, node left [msg=" + msg + ", node=" + nodeId + ']');
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send query counter response [msg=" + msg + ", node=" + nodeId + ']', e);
+
+ onQueryDone(nodeId, res.tracking());
+ }
+ }
+
+ /**
+ * @param nodeId Sender node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorSnapshotResponse(UUID nodeId, MvccSnapshotResponse msg) {
+ Map<Long, MvccSnapshotResponseListener> map = snapLsnrs.get(nodeId); MvccSnapshotResponseListener lsnr;
+
+ if (map != null && (lsnr = map.remove(msg.futureId())) != null)
+ lsnr.onResponse(msg);
+ else {
+ if (ctx.discovery().alive(nodeId))
+ U.warn(log, "Failed to find query version future [node=" + nodeId + ", msg=" + msg + ']');
+ else if (log.isDebugEnabled())
+ log.debug("Failed to find query version future [node=" + nodeId + ", msg=" + msg + ']');
+ }
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorQueryAckRequest(UUID nodeId, MvccAckRequestQueryCntr msg) {
+ onQueryDone(nodeId, msg.counter());
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param msg Message.
+ */
+ private void processNewCoordinatorQueryAckRequest(UUID nodeId, MvccAckRequestQueryId msg) {
+ prevCrdQueries.onQueryDone(nodeId, msg.queryTrackerId());
+ }
+
+ /**
+ * @param nodeId Sender node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorTxAckRequest(UUID nodeId, MvccAckRequestTx msg) {
+ onTxDone(msg.txCounter(), msg.futureId() >= 0);
+
+ if (msg.queryCounter() != MVCC_COUNTER_NA)
+ onQueryDone(nodeId, msg.queryCounter());
+ else if (msg.queryTrackerId() != MVCC_TRACKER_ID_NA)
+ prevCrdQueries.onQueryDone(nodeId, msg.queryTrackerId());
+
+ if (!msg.skipResponse()) {
+ try {
+ sendMessage(nodeId, new MvccFutureResponse(msg.futureId()));
+ }
+ catch (ClusterTopologyCheckedException e) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to send tx ack response, node left [msg=" + msg + ", node=" + nodeId + ']');
+ }
+ catch (IgniteCheckedException e) {
+ U.error(log, "Failed to send tx ack response [msg=" + msg + ", node=" + nodeId + ']', e);
+ }
+ }
+ }
+
+ /**
+ * @param nodeId Sender node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorAckResponse(UUID nodeId, MvccFutureResponse msg) {
+ WaitAckFuture fut = ackFuts.remove(msg.futureId());
+
+ if (fut != null)
+ fut.onResponse();
+ else {
+ if (ctx.discovery().alive(nodeId))
+ U.warn(log, "Failed to find tx ack future [node=" + nodeId + ", msg=" + msg + ']');
+ else if (log.isDebugEnabled())
+ log.debug("Failed to find tx ack future [node=" + nodeId + ", msg=" + msg + ']');
+ }
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param msg Message.
+ */
+ @SuppressWarnings("unchecked")
+ private void processCoordinatorWaitTxsRequest(final UUID nodeId, final MvccWaitTxsRequest msg) {
+ GridLongList txs = msg.transactions();
+
+ GridCompoundFuture resFut = null;
+
+ for (int i = 0; i < txs.size(); i++) {
+ Long txId = txs.get(i);
+
+ GridFutureAdapter fut = waitTxFuts.get(txId);
+
+ if (fut == null) {
+ GridFutureAdapter old = waitTxFuts.putIfAbsent(txId, fut = new GridFutureAdapter());
+
+ if (old != null)
+ fut = old;
+ }
+
+ boolean isDone;
+
+ synchronized (this) {
+ isDone = !activeTxs.containsKey(txId);
+ }
+
+ if (isDone)
+ fut.onDone();
+
+ if (!fut.isDone()) {
+ if (resFut == null)
+ resFut = new GridCompoundFuture();
+
+ resFut.add(fut);
+ }
+ }
+
+ if (resFut != null)
+ resFut.markInitialized();
+
+ if (resFut == null || resFut.isDone())
+ sendFutureResponse(nodeId, msg);
+ else {
+ resFut.listen(new IgniteInClosure<IgniteInternalFuture>() {
+ @Override public void apply(IgniteInternalFuture fut) {
+ sendFutureResponse(nodeId, msg);
+ }
+ });
+ }
+ }
+
+ /**
+ * @param nodeId Node ID.
+ * @param msg Message.
+ */
+ private void processCoordinatorActiveQueriesMessage(UUID nodeId, MvccActiveQueriesMessage msg) {
+ prevCrdQueries.addNodeActiveQueries(nodeId, msg.activeQueries());
+ }
+
+ /**
+ *
+ */
+ private class ActiveQueries {
+ /** */
+ private final Map<UUID, TreeMap<Long, AtomicInteger>> activeQueries = new HashMap<>();
+
+ /** */
+ private Long minQry;
+
+ /** */
+ private synchronized long minimalQueryCounter() {
+ return minQry == null ? -1 : minQry;
+ }
+
+ /** */
+ private synchronized MvccSnapshotResponse assignQueryCounter(UUID nodeId, long futId) {
+ MvccSnapshotResponse res = new MvccSnapshotResponse();
+
+ long ver, tracking;
+
+ synchronized (MvccProcessorImpl.this) {
+ ver = committedCntr.get();
+ tracking = ver;
+
+ for (Long txVer : activeTxs.keySet()) {
+ if (txVer < ver) {
+ tracking = Math.min(txVer, tracking);
+ res.addTx(txVer);
+ }
+ }
+ }
+
+ TreeMap<Long, AtomicInteger> nodeMap = activeQueries.get(nodeId);
+
+ if (nodeMap == null) {
+ activeQueries.put(nodeId, nodeMap = new TreeMap<>());
+
+ nodeMap.put(tracking, new AtomicInteger(1));
+ }
+ else {
+ AtomicInteger cntr = nodeMap.get(tracking);
+
+ if (cntr == null)
+ nodeMap.put(tracking, new AtomicInteger(1));
+ else
+ cntr.incrementAndGet();
+ }
+
+ if (minQry == null)
+ minQry = tracking;
+
+ res.init(futId, crdVer, ver, MVCC_READ_OP_CNTR, MVCC_COUNTER_NA, tracking);
+
+ return res;
+ }
+
+ /** */
+ private synchronized void onQueryDone(UUID nodeId, Long ver) {
+ TreeMap<Long, AtomicInteger> nodeMap = activeQueries.get(nodeId);
+
+ if (nodeMap == null)
+ return;
+
+ assert minQry != null;
+
+ AtomicInteger cntr = nodeMap.get(ver);
+
+ assert cntr != null && cntr.get() > 0 : "onQueryDone ver=" + ver;
+
+ if (cntr.decrementAndGet() == 0) {
+ nodeMap.remove(ver);
+
+ if (nodeMap.isEmpty())
+ activeQueries.remove(nodeId);
+
+ if (ver.equals(minQry))
+ minQry = activeMinimal();
+ }
+ }
+
+ /** */
+ private synchronized void onNodeFailed(UUID nodeId) {
+ activeQueries.remove(nodeId);
+
+ minQry = activeMinimal();
+ }
+
+ /** */
+ private Long activeMinimal() {
+ Long min = null;
+
+ for (TreeMap<Long, AtomicInteger> s : activeQueries.values()) {
+ Long first = s.firstKey();
+
+ if (min == null || first < min)
+ min = first;
+ }
+
+ return min;
+ }
+ }
+
+ /**
+ *
+ */
+ private class WaitAckFuture extends MvccFuture<Void> {
+ /** */
+ private final long id;
+
+ /** */
+ final boolean ackTx;
+
+ /**
+ * @param id Future ID.
+ * @param nodeId Coordinator node ID.
+ * @param ackTx {@code True} if ack tx commit, {@code false} if waits for previous txs.
+ */
+ WaitAckFuture(long id, UUID nodeId, boolean ackTx) {
+ super(nodeId);
+
+ this.id = id;
+ this.ackTx = ackTx;
+ }
+
+ /**
+ *
+ */
+ void onResponse() {
+ onDone();
+ }
+
+ /**
+ * @param nodeId Failed node ID.
+ */
+ void onNodeLeft(UUID nodeId) {
+ if (crdId.equals(nodeId) && ackFuts.remove(id) != null)
+ onDone();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(WaitAckFuture.class, this, super.toString());
+ }
+ }
+
+ /**
+ *
+ */
+ private class CacheCoordinatorNodeFailListener implements GridLocalEventListener {
+ /** {@inheritDoc} */
+ @Override public void onEvent(Event evt) {
+ assert evt instanceof DiscoveryEvent : evt;
+
+ DiscoveryEvent discoEvt = (DiscoveryEvent)evt;
+
+ UUID nodeId = discoEvt.eventNode().id();
+
+ Map<Long, MvccSnapshotResponseListener> map = snapLsnrs.remove(nodeId);
+
+ if (map != null) {
+ ClusterTopologyCheckedException ex = new ClusterTopologyCheckedException("Failed to request mvcc " +
+ "version, coordinator failed: " + nodeId);
+
+ MvccSnapshotResponseListener lsnr;
+
+ for (Long id : map.keySet()) {
+ if ((lsnr = map.remove(id)) != null)
+ lsnr.onError(ex);
+ }
+ }
+
+ for (WaitAckFuture fut : ackFuts.values())
+ fut.onNodeLeft(nodeId);
+
+ activeQueries.onNodeFailed(nodeId);
+
+ prevCrdQueries.onNodeFailed(nodeId);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "CacheCoordinatorDiscoveryListener[]";
+ }
+ }
+
+ /**
+ *
+ */
+ private class CoordinatorMessageListener implements GridMessageListener {
+ /** {@inheritDoc} */
+ @Override public void onMessage(UUID nodeId, Object msg, byte plc) {
+ MvccMessage msg0 = (MvccMessage)msg;
+
+ if (msg0.waitForCoordinatorInit() && !initFut.isDone()) {
+ initFut.listen(new IgniteInClosure<IgniteInternalFuture<Void>>() {
+ @Override public void apply(IgniteInternalFuture<Void> future) {
+ assert crdVer != 0L;
+
+ processMessage(nodeId, msg);
+ }
+ });
+ }
+ else
+ processMessage(nodeId, msg);
+ }
+
+ /**
+ * Processes mvcc message.
+ *
+ * @param nodeId Node id.
+ * @param msg Message.
+ */
+ private void processMessage(UUID nodeId, Object msg) {
+ if (msg instanceof MvccTxSnapshotRequest)
+ processCoordinatorTxSnapshotRequest(nodeId, (MvccTxSnapshotRequest)msg);
+ else if (msg instanceof MvccAckRequestTx)
+ processCoordinatorTxAckRequest(nodeId, (MvccAckRequestTx)msg);
+ else if (msg instanceof MvccFutureResponse)
+ processCoordinatorAckResponse(nodeId, (MvccFutureResponse)msg);
+ else if (msg instanceof MvccAckRequestQueryCntr)
+ processCoordinatorQueryAckRequest(nodeId, (MvccAckRequestQueryCntr)msg);
+ else if (msg instanceof MvccQuerySnapshotRequest)
+ processCoordinatorQuerySnapshotRequest(nodeId, (MvccQuerySnapshotRequest)msg);
+ else if (msg instanceof MvccSnapshotResponse)
+ processCoordinatorSnapshotResponse(nodeId, (MvccSnapshotResponse)msg);
+ else if (msg instanceof MvccWaitTxsRequest)
+ processCoordinatorWaitTxsRequest(nodeId, (MvccWaitTxsRequest)msg);
+ else if (msg instanceof MvccAckRequestQueryId)
+ processNewCoordinatorQueryAckRequest(nodeId, (MvccAckRequestQueryId)msg);
+ else if (msg instanceof MvccActiveQueriesMessage)
+ processCoordinatorActiveQueriesMessage(nodeId, (MvccActiveQueriesMessage)msg);
+ else
+ U.warn(log, "Unexpected message received [node=" + nodeId + ", msg=" + msg + ']');
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "CoordinatorMessageListener[]";
+ }
+ }
+
+ /** */
+ private interface Waiter {
+ /**
+ * @param ctx Grid kernal context.
+ */
+ void run(GridKernalContext ctx);
+
+ /**
+ * @param other Another waiter.
+ * @return New compound waiter.
+ */
+ Waiter concat(Waiter other);
+
+ /**
+ * @return {@code True} if there is an active local transaction
+ */
+ boolean hasLocalTransaction();
+
+ /**
+ * @return {@code True} if it is a compound waiter.
+ */
+ boolean compound();
+ }
+
+ /** */
+ private static class LockFuture extends GridFutureAdapter<Void> implements Waiter, Runnable {
+ /** */
+ private final byte plc;
+
+ /**
+ * @param plc Pool policy.
+ */
+ LockFuture(byte plc) {
+ this.plc = plc;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void run() {
+ onDone();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void run(GridKernalContext ctx) {
+ try {
+ ctx.pools().poolForPolicy(plc).execute(this);
+ }
+ catch (IgniteCheckedException e) {
+ U.error(ctx.log(LockFuture.class), e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Waiter concat(Waiter other) {
+ return new CompoundWaiterNoLocal(this, other);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasLocalTransaction() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean compound() {
+ return false;
+ }
+ }
+
+ /** */
+ private static class LocalTransactionMarker implements Waiter {
+ /** {@inheritDoc} */
+ @Override public void run(GridKernalContext ctx) {
+ // No-op
+ }
+
+ /** {@inheritDoc} */
+ @Override public Waiter concat(Waiter other) {
+ return new CompoundWaiter(other);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasLocalTransaction() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean compound() {
+ return false;
+ }
+ }
+
+ /** */
+ @SuppressWarnings("unchecked")
+ private static class CompoundWaiter implements Waiter {
+ /** */
+ private final Object inner;
+
+ /**
+ * @param waiter Waiter to wrap.
+ */
+ private CompoundWaiter(Waiter waiter) {
+ inner = waiter.compound() ? ((CompoundWaiter)waiter).inner : waiter;
+ }
+
+ /**
+ * @param first First waiter.
+ * @param second Second waiter.
+ */
+ private CompoundWaiter(Waiter first, Waiter second) {
+ ArrayList<Waiter> list = new ArrayList<>();
+
+ add(list, first);
+ add(list, second);
+
+ inner = list;
+ }
+
+ /** */
+ private void add(List<Waiter> to, Waiter waiter) {
+ if (!waiter.compound())
+ to.add(waiter);
+ else if (((CompoundWaiter)waiter).inner.getClass() == ArrayList.class)
+ to.addAll((List<Waiter>)((CompoundWaiter)waiter).inner);
+ else
+ to.add((Waiter)((CompoundWaiter)waiter).inner);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void run(GridKernalContext ctx) {
+ if (inner.getClass() == ArrayList.class) {
+ for (Waiter waiter : (List<Waiter>)inner) {
+ waiter.run(ctx);
+ }
+ }
+ else
+ ((Waiter)inner).run(ctx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Waiter concat(Waiter other) {
+ return new CompoundWaiter(this, other);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasLocalTransaction() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean compound() {
+ return true;
+ }
+ }
+
+ /** */
+ private static class CompoundWaiterNoLocal extends CompoundWaiter {
+ /**
+ * @param first First waiter.
+ * @param second Second waiter.
+ */
+ private CompoundWaiterNoLocal(Waiter first, Waiter second) {
+ super(first, second);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Waiter concat(Waiter other) {
+ return new CompoundWaiterNoLocal(this, other);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasLocalTransaction() {
+ return false;
+ }
+ }
+
+ /**
+ * Mvcc garbage collection scheduler.
+ */
+ private static class VacuumScheduler extends GridWorker {
+ /** */
+ private final static long VACUUM_TIMEOUT = 60_000;
+
+ /** */
+ private final long interval;
+
+ /** */
+ private final MvccProcessorImpl prc;
+
+ /**
+ * @param ctx Kernal context.
+ * @param log Logger.
+ * @param prc Mvcc processor.
+ */
+ VacuumScheduler(GridKernalContext ctx, IgniteLogger log, MvccProcessorImpl prc) {
+ super(ctx.igniteInstanceName(), "vacuum-scheduler", log);
+
+ this.interval = ctx.config().getMvccVacuumTimeInterval();
+ this.prc = prc;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void body() throws InterruptedException, IgniteInterruptedCheckedException {
+ U.sleep(interval); // initial delay
+
+ while (!isCancelled()) {
+ long nextScheduledTime = U.currentTimeMillis() + interval;
+
+ try {
+ IgniteInternalFuture<VacuumMetrics> fut = prc.runVacuum();
+
+ if (log.isDebugEnabled())
+ log.debug("Vacuum started by scheduler.");
+
+ while (true) {
+ try {
+ fut.get(VACUUM_TIMEOUT);
+
+ break;
+ }
+ catch (IgniteFutureTimeoutCheckedException e) {
+ U.warn(log, "Failed to wait for vacuum complete. Consider increasing vacuum workers count.");
+ }
+ }
+ }
+ catch (IgniteInterruptedCheckedException e) {
+ throw e;
+ }
+ catch (Throwable e) {
+ prc.vacuumError(e);
+
+ if (e instanceof Error)
+ throw (Error) e;
+ }
+
+ long delay = nextScheduledTime - U.currentTimeMillis();
+
+ if (delay > 0)
+ U.sleep(delay);
+ }
+ }
+ }
+
+ /**
+ * Vacuum worker.
+ */
+ private static class VacuumWorker extends GridWorker {
+ /** */
+ private final BlockingQueue<VacuumTask> cleanupQueue;
+
+ /**
+ * @param ctx Kernal context.
+ * @param log Logger.
+ * @param cleanupQueue Cleanup tasks queue.
+ */
+ VacuumWorker(GridKernalContext ctx, IgniteLogger log, BlockingQueue<VacuumTask> cleanupQueue) {
+ super(ctx.igniteInstanceName(), "vacuum-cleaner", log);
+
+ this.cleanupQueue = cleanupQueue;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void body() throws InterruptedException, IgniteInterruptedCheckedException {
+ while (!isCancelled()) {
+ VacuumTask task = cleanupQueue.take();
+
+ try {
+ if (task.part().state() != OWNING) {
+ task.part().group().preloader().rebalanceFuture()
+ .listen(new IgniteInClosure<IgniteInternalFuture<Boolean>>() {
+ @Override public void apply(IgniteInternalFuture<Boolean> future) {
+ cleanupQueue.add(task);
+ }
+ });
+
+ continue;
+ }
+
+ task.onDone(processPartition(task));
+ }
+ catch (IgniteInterruptedCheckedException e) {
+ throw e; // Cancelled.
+ }
+ catch (Throwable e) {
+ task.onDone(e);
+
+ if (e instanceof Error)
+ throw (Error) e;
+ }
+ }
+ }
+
+ /**
+ * Process partition.
+ *
+ * @param task VacuumTask.
+ * @throws IgniteCheckedException If failed.
+ */
+ private VacuumMetrics processPartition(VacuumTask task) throws IgniteCheckedException {
+ long startNanoTime = System.nanoTime();
+
+ GridDhtLocalPartition part = task.part();
+
+ VacuumMetrics metrics = new VacuumMetrics();
+
+ if (part == null || part.state() != OWNING || !part.reserve())
+ return metrics;
+
+ try {
+ GridCursor<? extends CacheDataRow> cursor = part.dataStore().cursor(KEY_ONLY);
+
+ KeyCacheObject prevKey = null; Object rest = null;
+
+ List<MvccLinkAwareSearchRow> cleanupRows = null;
+
+ MvccSnapshot snapshot = task.snapshot();
+
+ GridCacheContext cctx = null; int curCacheId = CU.UNDEFINED_CACHE_ID;
+
+ boolean shared = part.group().sharedGroup();
+
+ if (!shared)
+ cctx = part.group().singleCacheContext();
+
+ while (cursor.next()) {
+ if (isCancelled())
+ throw new IgniteInterruptedCheckedException("Operation has been cancelled.");
+
+ MvccDataRow row = (MvccDataRow)cursor.get();
+
+ if (prevKey == null)
+ prevKey = row.key();
+
+ if (cctx == null) {
+ assert shared;
+
+ curCacheId = row.cacheId();
+ cctx = part.group().shared().cacheContext(curCacheId);
+ }
+
+ if (!prevKey.equals(row.key()) || (shared && curCacheId != row.cacheId())) {
+ if (rest != null || !F.isEmpty(cleanupRows))
+ cleanup(part, prevKey, cleanupRows, rest, cctx, metrics);
+
+ cleanupRows = null; rest = null;
+
+ if (shared && curCacheId != row.cacheId())
+ cctx = part.group().shared().cacheContext(curCacheId = row.cacheId());
+
+ prevKey = row.key();
+ }
+
+ if (canClean(row, snapshot, cctx))
+ cleanupRows = addRow(cleanupRows, row);
+ else if (actualize(cctx, row, snapshot))
+ rest = addRest(rest, row);
+
+ metrics.addScannedRowsCount(1);
+ }
+
+ if (rest != null || !F.isEmpty(cleanupRows))
+ cleanup(part, prevKey, cleanupRows, rest, cctx, metrics);
+
+ metrics.addSearchNanoTime(System.nanoTime() - startNanoTime - metrics.cleanupNanoTime());
+
+ return metrics;
+ }
+ finally {
+ part.release();
+ }
+ }
+
+ /** */
+ @SuppressWarnings("unchecked")
+ @NotNull private Object addRest(@Nullable Object rest, MvccDataRow row) {
+ if (rest == null)
+ rest = row;
+ else if (rest.getClass() == ArrayList.class)
+ ((List)rest).add(row);
+ else {
+ ArrayList list = new ArrayList();
+
+ list.add(rest);
+ list.add(row);
+
+ rest = list;
+ }
+
+ return rest;
+ }
+
+ /**
+ * @param rows Collection of rows.
+ * @param row Row to add.
+ * @return Collection of rows.
+ */
+ @NotNull private List<MvccLinkAwareSearchRow> addRow(@Nullable List<MvccLinkAwareSearchRow> rows, MvccDataRow row) {
+ if (rows == null)
+ rows = new ArrayList<>();
+
+ rows.add(new MvccLinkAwareSearchRow(row.cacheId(), row.key(), row.mvccCoordinatorVersion(),
+ row.mvccCounter(), row.mvccOperationCounter(), row.link()));
+
+ return rows;
+ }
+
+ /**
+ * @param row Mvcc row to check.
+ * @param snapshot Cleanup version to compare with.
+ * @param cctx Cache context.
+ * @throws IgniteCheckedException If failed.
+ */
+ private boolean canClean(MvccDataRow row, MvccSnapshot snapshot,
+ GridCacheContext cctx) throws IgniteCheckedException {
+ // Row can be safely cleaned if it has ABORTED min version or COMMITTED and less than cleanup one max version.
+ return compare(row, snapshot.coordinatorVersion(), snapshot.cleanupVersion()) <= 0
+ && hasNewVersion(row) && MvccUtils.compareNewVersion(row, snapshot.coordinatorVersion(), snapshot.cleanupVersion()) <= 0
+ && MvccUtils.state(cctx, row.newMvccCoordinatorVersion(), row.newMvccCounter(),
+ row.newMvccOperationCounter() | (row.newMvccTxState() << PageIO.MVCC_HINTS_BIT_OFF)) == TxState.COMMITTED
+ || MvccUtils.state(cctx, row.mvccCoordinatorVersion(), row.mvccCounter(),
+ row.mvccOperationCounter() | (row.mvccTxState() << PageIO.MVCC_HINTS_BIT_OFF)) == TxState.ABORTED;
+ }
+
+ /** */
+ private boolean actualize(GridCacheContext cctx, MvccDataRow row,
+ MvccSnapshot snapshot) throws IgniteCheckedException {
+ return isVisible(cctx, snapshot, row.mvccCoordinatorVersion(), row.mvccCounter(), row.mvccOperationCounter(), false)
+ && (row.mvccTxState() == TxState.NA || (row.newMvccCoordinatorVersion() != MVCC_CRD_COUNTER_NA && row.newMvccTxState() == TxState.NA));
+ }
+
+ /**
+ *
+ * @param part Local partition.
+ * @param key Key.
+ * @param cleanupRows Cleanup rows.
+ * @param cctx Cache context.
+ * @param metrics Vacuum metrics.
+ * @throws IgniteCheckedException If failed.
+ */
+ @SuppressWarnings("unchecked")
+ private void cleanup(GridDhtLocalPartition part, KeyCacheObject key, List<MvccLinkAwareSearchRow> cleanupRows,
+ Object rest, GridCacheContext cctx, VacuumMetrics metrics) throws IgniteCheckedException {
+ assert key != null && cctx != null && (!F.isEmpty(cleanupRows) || rest != null);
+
+ long cleanupStartNanoTime = System.nanoTime();
+
+ GridCacheEntryEx entry = cctx.cache().entryEx(key);
+
+ while (true) {
+ entry.lockEntry();
+
+ if (!entry.obsolete())
+ break;
+
+ entry.unlockEntry();
+
+ entry = cctx.cache().entryEx(key);
+ }
+
+ cctx.shared().database().checkpointReadLock();
+
+ int cleaned = 0;
+
+ try {
+ if (cleanupRows != null)
+ cleaned = part.dataStore().cleanup(cctx, cleanupRows);
+
+ if (rest != null) {
+ if (rest.getClass() == ArrayList.class) {
+ for (MvccDataRow row : ((List<MvccDataRow>)rest)) {
+ part.dataStore().updateTxState(cctx, row);
+ }
+ }
+ else
+ part.dataStore().updateTxState(cctx, (MvccDataRow)rest);
+ }
+ }
+ finally {
+ cctx.shared().database().checkpointReadUnlock();
+
+ entry.unlockEntry();
+ cctx.evicts().touch(entry, AffinityTopologyVersion.NONE);
+
+ metrics.addCleanupNanoTime(System.nanoTime() - cleanupStartNanoTime);
+ metrics.addCleanupRowsCnt(cleaned);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTracker.java
new file mode 100644
index 0000000..f143a43
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/MvccQueryTracker.java
@@ -0,0 +1,102 @@
+/*
+ * 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.mvcc;
+
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Mvcc tracker.
+ */
+public interface MvccQueryTracker {
+ /** */
+ public static final AtomicLong ID_CNTR = new AtomicLong();
+
+ /** */
+ public static final long MVCC_TRACKER_ID_NA = -1;
+
+ /**
+ * @return Tracker id.
+ */
+ public long id();
+
+ /**
+ * @return Requested MVCC snapshot.
+ */
+ public MvccSnapshot snapshot();
+
+ /**
+ * @return Cache context.
+ */
+ public GridCacheContext context();
+
+ /**
+ * @return Topology version.
+ */
+ public AffinityTopologyVersion topologyVersion();
+
+ /**
+ * Requests version on coordinator.
+ *
+ * @return Future to wait for result.
+ */
+ public IgniteInternalFuture<MvccSnapshot> requestSnapshot();
+
+ /**
+ * Requests version on coordinator.
+ *
+ * @param topVer Topology version.
+ * @return Future to wait for result.
+ */
+ public IgniteInternalFuture<MvccSnapshot> requestSnapshot(@NotNull AffinityTopologyVersion topVer);
+
+ /**
+ * Requests version on coordinator.
+ *
+ * @param topVer Topology version.
+ * @param lsnr Response listener.
+ */
+ public void requestSnapshot(@NotNull AffinityTopologyVersion topVer, @NotNull MvccSnapshotResponseListener lsnr);
+
+ /**
+ * Marks tracker as done.
+ */
+ public void onDone();
+
+ /**
+ * Marks tracker as done.
+ *
+ * @param tx Transaction.
+ * @param commit Commit flag.
+ * @return Acknowledge future.
+ */
+ @Nullable public IgniteInternalFuture<Void> onDone(@NotNull GridNearTxLocal tx, boolean commit);
+
+ /**
+ * Mvcc coordinator change callback.
+ *
+ * @param newCrd New mvcc coordinator.
+ * @return Query id if exists.
+ */
+ long onMvccCoordinatorChange(MvccCoordinator newCrd);
+}
[11/45] ignite git commit: IGNITE-4191: MVCC and transactional SQL
support. Joint multi-man-years efforts of Semen Boikov, Igor Seliverstov,
Alexander Paschenko, Igor Sapego, Sergey Kalashnikov, Roman Kondakov,
Pavel Kuznetsov, Ivan Pavlukhin, Andrey Mas
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2SelectForUpdateTxDetails.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2SelectForUpdateTxDetails.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2SelectForUpdateTxDetails.java
new file mode 100644
index 0000000..a13a9c1
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2SelectForUpdateTxDetails.java
@@ -0,0 +1,299 @@
+/*
+ * 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.query.h2.twostep.msg;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * TX details holder for {@link GridH2QueryRequest}.
+ */
+public class GridH2SelectForUpdateTxDetails implements Message {
+ /** */
+ private static final long serialVersionUID = 8166491041528984454L;
+ /** */
+ private long threadId;
+
+ /** */
+ private IgniteUuid futId;
+
+ /** */
+ private int miniId;
+
+ /** */
+ private UUID subjId;
+
+ /** */
+ private GridCacheVersion lockVer;
+
+ /** */
+ private int taskNameHash;
+
+ /** */
+ private boolean clientFirst;
+
+ /** */
+ private long timeout;
+
+ /**
+ * Default constructor.
+ */
+ GridH2SelectForUpdateTxDetails() {
+ // No-op.
+ }
+
+ /**
+ * @param threadId Thread id.
+ * @param futId Future id.
+ * @param miniId Mini fture id.
+ * @param subjId Subject id.
+ * @param lockVer Lock version.
+ * @param taskNameHash Task name hash.
+ * @param clientFirst {@code True} if this is the first client request.
+ * @param timeout Tx timeout.
+ */
+ public GridH2SelectForUpdateTxDetails(long threadId, IgniteUuid futId, int miniId, UUID subjId,
+ GridCacheVersion lockVer, int taskNameHash, boolean clientFirst, long timeout) {
+ this.threadId = threadId;
+ this.futId = futId;
+ this.miniId = miniId;
+ this.subjId = subjId;
+ this.lockVer = lockVer;
+ this.taskNameHash = taskNameHash;
+ this.clientFirst = clientFirst;
+ this.timeout = timeout;
+ }
+
+ /**
+ * @return Thread id.
+ */
+ public long threadId() {
+ return threadId;
+ }
+
+ /**
+ * @return Future id.
+ */
+ public IgniteUuid futureId() {
+ return futId;
+ }
+
+ /**
+ * @return Mini fture id.
+ */
+ public int miniId() {
+ return miniId;
+ }
+
+ /**
+ * @return Subject id.
+ */
+ public UUID subjectId() {
+ return subjId;
+ }
+
+ /**
+ * @return Lock version.
+ */
+ public GridCacheVersion version() {
+ return lockVer;
+ }
+
+ /**
+ * @return Task name hash.
+ */
+ public int taskNameHash() {
+ return taskNameHash;
+ }
+
+ /**
+ * @return {@code True} if this is the first client request in transaction.
+ */
+ public boolean firstClientRequest() {
+ return clientFirst;
+ }
+
+ /**
+ * @return Tx timeout.
+ */
+ public long timeout() {
+ return timeout;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+ writer.setBuffer(buf);
+
+ if (!writer.isHeaderWritten()) {
+ if (!writer.writeHeader(directType(), fieldsCount()))
+ return false;
+
+ writer.onHeaderWritten();
+ }
+
+ switch (writer.state()) {
+ case 0:
+ if (!writer.writeBoolean("clientFirst", clientFirst))
+ return false;
+
+ writer.incrementState();
+
+ case 1:
+ if (!writer.writeIgniteUuid("futId", futId))
+ return false;
+
+ writer.incrementState();
+
+ case 2:
+ if (!writer.writeMessage("lockVer", lockVer))
+ return false;
+
+ writer.incrementState();
+
+ case 3:
+ if (!writer.writeInt("miniId", miniId))
+ return false;
+
+ writer.incrementState();
+
+ case 4:
+ if (!writer.writeUuid("subjId", subjId))
+ return false;
+
+ writer.incrementState();
+
+ case 5:
+ if (!writer.writeInt("taskNameHash", taskNameHash))
+ return false;
+
+ writer.incrementState();
+
+ case 6:
+ if (!writer.writeLong("threadId", threadId))
+ return false;
+
+ writer.incrementState();
+
+ case 7:
+ if (!writer.writeLong("timeout", timeout))
+ return false;
+
+ writer.incrementState();
+
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+ reader.setBuffer(buf);
+
+ if (!reader.beforeMessageRead())
+ return false;
+
+ switch (reader.state()) {
+ case 0:
+ clientFirst = reader.readBoolean("clientFirst");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 1:
+ futId = reader.readIgniteUuid("futId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 2:
+ lockVer = reader.readMessage("lockVer");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 3:
+ miniId = reader.readInt("miniId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 4:
+ subjId = reader.readUuid("subjId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 5:
+ taskNameHash = reader.readInt("taskNameHash");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 6:
+ threadId = reader.readLong("threadId");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ case 7:
+ timeout = reader.readLong("timeout");
+
+ if (!reader.isLastRead())
+ return false;
+
+ reader.incrementState();
+
+ }
+
+ return reader.afterMessageRead(GridH2SelectForUpdateTxDetails.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public short directType() {
+ return -57;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte fieldsCount() {
+ return 8;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onAckReceived() {
+ // No-op.
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
index 3c13392..c399d76 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/msg/GridH2ValueMessageFactory.java
@@ -118,6 +118,9 @@ public class GridH2ValueMessageFactory implements MessageFactory {
case -56:
return new GridH2DmlResponse();
+
+ case -57:
+ return new GridH2SelectForUpdateTxDetails();
}
return null;
@@ -126,14 +129,17 @@ public class GridH2ValueMessageFactory implements MessageFactory {
/**
* @param src Source values.
* @param dst Destination collection.
+ * @param cnt Number of columns to actually send.
* @return Destination collection.
* @throws IgniteCheckedException If failed.
*/
- public static Collection<Message> toMessages(Collection<Value[]> src, Collection<Message> dst)
+ public static Collection<Message> toMessages(Collection<Value[]> src, Collection<Message> dst, int cnt)
throws IgniteCheckedException {
for (Value[] row : src) {
- for (Value val : row)
- dst.add(toMessage(val));
+ assert row.length >= cnt;
+
+ for (int i = 0; i < cnt; i++)
+ dst.add(toMessage(row[i]));
}
return dst;
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/DdlTransactionSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/DdlTransactionSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/DdlTransactionSelfTest.java
new file mode 100644
index 0000000..6652559
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/DdlTransactionSelfTest.java
@@ -0,0 +1,335 @@
+/*
+ * 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;
+
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.FieldsQueryCursor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.TransactionConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.apache.ignite.transactions.TransactionState;
+
+/**
+ *
+ */
+public class DdlTransactionSelfTest extends GridCommonAbstractTest {
+ /** */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** */
+ private boolean client;
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ cfg.setTransactionConfiguration(new TransactionConfiguration()
+ .setDefaultTxIsolation(TransactionIsolation.REPEATABLE_READ)
+ .setDefaultTxConcurrency(TransactionConcurrency.PESSIMISTIC)
+ .setDefaultTxTimeout(5000));
+
+ TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
+
+ discoSpi.setIpFinder(IP_FINDER);
+
+ cfg.setDiscoverySpi(discoSpi);
+ cfg.setCacheConfiguration(getCacheConfiguration());
+ cfg.setClientMode(client);
+
+ return cfg;
+ }
+
+ /**
+ * @return Cache configuration.
+ */
+ private CacheConfiguration getCacheConfiguration() {
+ CacheConfiguration<?, ?> ccfg = defaultCacheConfiguration();
+
+ ccfg.setNearConfiguration(null);
+
+ return ccfg;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxIsCommittedOnDdlRequestMultinodeClient() throws Exception {
+ startGridsMultiThreaded(4, false);
+
+ client = true;
+
+ Ignite node = startGrid(4);
+
+ awaitPartitionMapExchange();
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = node.transactions().txStart()) {
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery(
+ "CREATE TABLE " +
+ " person (id int, name varchar, age int, company varchar, city varchar, primary key (id, name, city))" +
+ "WITH " +
+ " \"template=PARTITIONED,atomicity=TRANSACTIONAL,affinity_key=city\"").setSchema("PUBLIC"))) {
+
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(1, rows.size());
+
+ assertEquals(0L, rows.get(0).get(0));
+ }
+
+ assertTrue(tx.state() == TransactionState.COMMITTED);
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery("SELECT * FROM person").setSchema("PUBLIC"))) {
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(0, rows.size());
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxIsCommittedOnDdlRequestMultinode() throws Exception {
+ Ignite node = startGridsMultiThreaded(4);
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = node.transactions().txStart()) {
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery(
+ "CREATE TABLE " +
+ " person (id int, name varchar, age int, company varchar, city varchar, primary key (id, name, city))" +
+ "WITH " +
+ " \"template=PARTITIONED,atomicity=TRANSACTIONAL,affinity_key=city\"").setSchema("PUBLIC"))) {
+
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(1, rows.size());
+
+ assertEquals(0L, rows.get(0).get(0));
+ }
+
+ assertTrue(tx.state() == TransactionState.COMMITTED);
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery("SELECT * FROM person").setSchema("PUBLIC"))) {
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(0, rows.size());
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxIsCommittedOnDdlRequest() throws Exception {
+ Ignite node = startGrid();
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ try (Transaction tx = node.transactions().txStart()) {
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery(
+ "CREATE TABLE " +
+ " person (id int, name varchar, age int, company varchar, city varchar, primary key (id, name, city))" +
+ "WITH " +
+ " \"template=PARTITIONED,atomicity=TRANSACTIONAL,affinity_key=city\"").setSchema("PUBLIC"))) {
+
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(1, rows.size());
+
+ assertEquals(0L, rows.get(0).get(0));
+ }
+
+ assertTrue(tx.state() == TransactionState.COMMITTED);
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery("SELECT * FROM person").setSchema("PUBLIC"))) {
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(0, rows.size());
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testDdlRequestWithoutTxMultinodeClient() throws Exception {
+ startGridsMultiThreaded(4, false);
+
+ client = true;
+
+ Ignite node = startGrid(4);
+
+ awaitPartitionMapExchange();
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery(
+ "CREATE TABLE " +
+ " person (id int, name varchar, age int, company varchar, city varchar, primary key (id, name, city))" +
+ "WITH " +
+ " \"template=PARTITIONED,atomicity=TRANSACTIONAL,affinity_key=city\"").setSchema("PUBLIC"))) {
+
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(1, rows.size());
+
+ assertEquals(0L, rows.get(0).get(0));
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery("SELECT * FROM person").setSchema("PUBLIC"))) {
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(0, rows.size());
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testDdlRequestWithoutTxMultinode() throws Exception {
+ Ignite node = startGridsMultiThreaded(4);
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery(
+ "CREATE TABLE " +
+ " person (id int, name varchar, age int, company varchar, city varchar, primary key (id, name, city))" +
+ "WITH " +
+ " \"template=PARTITIONED,atomicity=TRANSACTIONAL,affinity_key=city\"").setSchema("PUBLIC"))) {
+
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(1, rows.size());
+
+ assertEquals(0L, rows.get(0).get(0));
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery("SELECT * FROM person").setSchema("PUBLIC"))) {
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(0, rows.size());
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testDdlRequestWithoutTx() throws Exception {
+ Ignite node = startGrid();
+
+ IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
+
+ cache.putAll(F.asMap(1, 1, 2, 2, 3, 3));
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery(
+ "CREATE TABLE " +
+ " person (id int, name varchar, age int, company varchar, city varchar, primary key (id, name, city))" +
+ "WITH " +
+ " \"template=PARTITIONED,atomicity=TRANSACTIONAL,affinity_key=city\"").setSchema("PUBLIC"))) {
+
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(1, rows.size());
+
+ assertEquals(0L, rows.get(0).get(0));
+ }
+
+ try (FieldsQueryCursor<List<?>> cur = cache.query(new SqlFieldsQuery("SELECT * FROM person").setSchema("PUBLIC"))) {
+ assertNotNull(cur);
+
+ List<List<?>> rows = cur.getAll();
+
+ assertEquals(0, rows.size());
+ }
+
+ assertEquals(1, cache.get(1));
+ assertEquals(2, cache.get(2));
+ assertEquals(3, cache.get(3));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryH2IndexingLeakTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryH2IndexingLeakTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryH2IndexingLeakTest.java
index 7212bf8..59be138 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryH2IndexingLeakTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheQueryH2IndexingLeakTest.java
@@ -27,6 +27,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
+import org.apache.ignite.internal.processors.query.h2.H2ConnectionWrapper;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
import org.apache.ignite.internal.util.lang.GridAbsPredicate;
import org.apache.ignite.internal.util.typedef.CAX;
@@ -120,9 +121,14 @@ public class IgniteCacheQueryH2IndexingLeakTest extends GridCommonAbstractTest {
private static int getStatementCacheSize(GridQueryProcessor qryProcessor) {
IgniteH2Indexing h2Idx = GridTestUtils.getFieldValue(qryProcessor, GridQueryProcessor.class, "idx");
- ConcurrentMap stmtCache = GridTestUtils.getFieldValue(h2Idx, IgniteH2Indexing.class, "stmtCache");
+ ConcurrentMap<Thread, H2ConnectionWrapper> conns = GridTestUtils.getFieldValue(h2Idx, IgniteH2Indexing.class, "conns");
- return stmtCache.size();
+ int cntr = 0;
+
+ for (H2ConnectionWrapper w : conns.values())
+ cntr += w.statementCacheSize();
+
+ return cntr;
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
index 0a0efc7..7f1e2e7 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/AbstractSchemaSelfTest.java
@@ -21,12 +21,15 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import javax.cache.CacheException;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteCheckedException;
@@ -36,12 +39,17 @@ import org.apache.ignite.cache.QueryIndexType;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.processors.odbc.ClientListenerProcessor;
import org.apache.ignite.internal.processors.port.GridPortRecord;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
import org.apache.ignite.internal.processors.query.QueryTypeDescriptorImpl;
import org.apache.ignite.internal.processors.query.QueryUtils;
@@ -51,6 +59,9 @@ import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.jetbrains.annotations.Nullable;
@@ -59,6 +70,9 @@ import org.jetbrains.annotations.Nullable;
*/
@SuppressWarnings("unchecked")
public abstract class AbstractSchemaSelfTest extends GridCommonAbstractTest {
+ /** IP finder. */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
/** Cache. */
protected static final String CACHE_NAME = "cache";
@@ -102,6 +116,59 @@ public abstract class AbstractSchemaSelfTest extends GridCommonAbstractTest {
protected static final String FIELD_NAME_2_ESCAPED = "field2";
/**
+ * Create common node configuration.
+ *
+ * @param idx Index.
+ * @return Configuration.
+ * @throws Exception If failed.
+ */
+ protected IgniteConfiguration commonConfiguration(int idx) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(getTestIgniteInstanceName(idx));
+
+ cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER));
+
+ cfg.setMarshaller(new BinaryMarshaller());
+
+ DataStorageConfiguration memCfg = new DataStorageConfiguration().setDefaultDataRegionConfiguration(
+ new DataRegionConfiguration().setMaxSize(128 * 1024 * 1024));
+
+ cfg.setDataStorageConfiguration(memCfg);
+
+ return optimize(cfg);
+ }
+
+ /**
+ * Ensure that SQL exception is thrown.
+ *
+ * @param r Runnable.
+ * @param expCode Error code.
+ */
+ static void assertSqlException(DynamicIndexAbstractBasicSelfTest.RunnableX r, int expCode) {
+ try {
+ try {
+ r.run();
+ }
+ catch (CacheException e) {
+ if (e.getCause() != null)
+ throw (Exception)e.getCause();
+ else
+ throw e;
+ }
+ }
+ catch (IgniteSQLException e) {
+ assertEquals("Unexpected error code [expected=" + expCode + ", actual=" + e.statusCode() + ']',
+ expCode, e.statusCode());
+
+ return;
+ }
+ catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ fail(IgniteSQLException.class.getSimpleName() + " is not thrown.");
+ }
+
+ /**
* Get type on the given node for the given cache and table name. Type must exist.
*
* @param node Node.
@@ -230,7 +297,7 @@ public abstract class AbstractSchemaSelfTest extends GridCommonAbstractTest {
* @param node Node to connect to.
* @return Thin JDBC connection to specified node.
*/
- static Connection connect(IgniteEx node) {
+ public static Connection connect(IgniteEx node) {
Collection<GridPortRecord> recs = node.context().ports().records();
GridPortRecord cliLsnrRec = null;
@@ -254,6 +321,17 @@ public abstract class AbstractSchemaSelfTest extends GridCommonAbstractTest {
}
/**
+ * @param conn Connection.
+ * @param sql Statement.
+ * @throws SQLException if failed.
+ */
+ public static void execute(Connection conn, String sql) throws SQLException {
+ try (Statement s = conn.createStatement()) {
+ s.execute(sql);
+ }
+ }
+
+ /**
* Assert index doesn't exist on all nodes.
*
* @param cacheName Cache name.
@@ -348,13 +426,33 @@ public abstract class AbstractSchemaSelfTest extends GridCommonAbstractTest {
}
/**
+ * Execute SQL statement on given node.
+ *
+ * @param node Node.
+ * @param sql Statement.
+ */
+ protected List<List<?>> execute(Ignite node, String sql) {
+ return queryProcessor(node).querySqlFields(new SqlFieldsQuery(sql).setSchema("PUBLIC"), true).getAll();
+ }
+
+ /**
* Get query processor.
*
* @param node Node.
* @return Query processor.
*/
static GridQueryProcessor queryProcessor(Ignite node) {
- return ((IgniteEx)node).context().query();
+ return queryProcessor((IgniteEx)node);
+ }
+
+ /**
+ * Get query processor.
+ *
+ * @param node Node.
+ * @return Query processor.
+ */
+ protected static GridQueryProcessor queryProcessor(IgniteEx node) {
+ return node.context().query();
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractConcurrentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractConcurrentSelfTest.java
index 3f09062..072f1ab 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractConcurrentSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractConcurrentSelfTest.java
@@ -380,9 +380,9 @@ public abstract class DynamicColumnsAbstractConcurrentSelfTest extends DynamicCo
IgniteCache<Object, BinaryObject> cache = node.cache(CACHE_NAME);
if (ThreadLocalRandom.current().nextBoolean())
- cache.put(key(node, key), val(node, val));
+ cache.put(key(key), val(node, val));
else
- cache.remove(key(node, key));
+ cache.remove(key(key));
}
return null;
@@ -416,7 +416,7 @@ public abstract class DynamicColumnsAbstractConcurrentSelfTest extends DynamicCo
IgniteCache<Object, BinaryObject> cache = srv1.cache(CACHE_NAME).withKeepBinary();
for (int i = 0; i < LARGE_CACHE_SIZE; i++) {
- Object key = key(srv1, i);
+ Object key = key(i);
BinaryObject val = cache.get(key);
@@ -430,7 +430,7 @@ public abstract class DynamicColumnsAbstractConcurrentSelfTest extends DynamicCo
}
}
- String valTypeName = ((IgniteEx)srv1).context().query().types(CACHE_NAME).iterator().next().valueTypeName();
+ String valTypeName = (srv1).context().query().types(CACHE_NAME).iterator().next().valueTypeName();
// Validate query result.
for (Ignite node : Ignition.allGrids()) {
@@ -469,11 +469,10 @@ public abstract class DynamicColumnsAbstractConcurrentSelfTest extends DynamicCo
}
/**
- * @param node Node.
* @param id Key.
* @return PERSON cache key (int or {@link BinaryObject}).
*/
- private Object key(Ignite node, int id) {
+ private Object key(int id) {
return id;
}
@@ -550,7 +549,7 @@ public abstract class DynamicColumnsAbstractConcurrentSelfTest extends DynamicCo
*/
private void put(Ignite node, int startIdx, int endIdx) {
for (int i = startIdx; i < endIdx; i++)
- node.cache(CACHE_NAME).put(key(node, i), val(node, i));
+ node.cache(CACHE_NAME).put(key(i), val(node, i));
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
index 4490fc7..b00d750 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractSelfTest.java
@@ -83,6 +83,13 @@ public abstract class DynamicIndexAbstractSelfTest extends AbstractSchemaSelfTes
/** Argument for simple SQL (2). */
protected static final int SQL_ARG_2 = 80;
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+
+ super.afterTestsStopped();
+ }
+
/**
* Create server configuration.
*
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionLeaksSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionLeaksSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionLeaksSelfTest.java
index 99661e4..7713004 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionLeaksSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionLeaksSelfTest.java
@@ -17,7 +17,6 @@
package org.apache.ignite.internal.processors.cache.index;
-import java.sql.Connection;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import org.apache.ignite.Ignite;
@@ -25,7 +24,6 @@ import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.IgniteInterruptedCheckedException;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
import org.apache.ignite.internal.util.lang.GridAbsPredicate;
import org.apache.ignite.testframework.GridTestUtils;
@@ -145,7 +143,7 @@ public class H2ConnectionLeaksSelfTest extends GridCommonAbstractTest {
boolean notLeak = GridTestUtils.waitForCondition(new GridAbsPredicate() {
@Override public boolean apply() {
for (int i = 0; i < NODE_CNT; i++) {
- Map<Thread, Connection> conns = perThreadConnections(i);
+ Map<Thread, ?> conns = perThreadConnections(i);
for(Thread t : conns.keySet()) {
if (!t.isAlive())
@@ -159,7 +157,7 @@ public class H2ConnectionLeaksSelfTest extends GridCommonAbstractTest {
if (!notLeak) {
for (int i = 0; i < NODE_CNT; i++) {
- Map<Thread, Connection> conns = perThreadConnections(i);
+ Map<Thread, ?> conns = perThreadConnections(i);
for(Thread t : conns.keySet())
log.error("+++ Connection is not closed for thread: " + t.getName());
@@ -173,7 +171,7 @@ public class H2ConnectionLeaksSelfTest extends GridCommonAbstractTest {
* @param nodeIdx Node index.
* @return Per-thread connections.
*/
- private Map<Thread, Connection> perThreadConnections(int nodeIdx) {
+ private Map<Thread, ?> perThreadConnections(int nodeIdx) {
return ((IgniteH2Indexing)grid(nodeIdx).context().query().getIndexing()).perThreadConnections();
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
index cf47774..9579b79 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicIndexAbstractSelfTest.java
@@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import javax.cache.CacheException;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;
@@ -33,9 +32,7 @@ import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.NearCacheConfiguration;
-import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
-import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.util.typedef.F;
/**
@@ -298,21 +295,6 @@ public abstract class H2DynamicIndexAbstractSelfTest extends AbstractSchemaSelfT
}
/**
- * Create common node configuration.
- *
- * @param idx Index.
- * @return Configuration.
- * @throws Exception If failed.
- */
- private IgniteConfiguration commonConfiguration(int idx) throws Exception {
- IgniteConfiguration cfg = super.getConfiguration(getTestIgniteInstanceName(idx));
-
- cfg.setMarshaller(new BinaryMarshaller());
-
- return optimize(cfg);
- }
-
- /**
* @return Default cache configuration.
*/
private CacheConfiguration cacheConfiguration() {
@@ -359,35 +341,4 @@ public abstract class H2DynamicIndexAbstractSelfTest extends AbstractSchemaSelfT
* @return Whether to use near cache.
*/
protected abstract boolean nearCache();
-
- /**
- * Ensure that SQL exception is thrown.
- *
- * @param r Runnable.
- * @param expCode Error code.
- */
- private static void assertSqlException(DynamicIndexAbstractBasicSelfTest.RunnableX r, int expCode) {
- try {
- try {
- r.run();
- }
- catch (CacheException e) {
- if (e.getCause() != null)
- throw (Exception)e.getCause();
- else
- throw e;
- }
- }
- catch (IgniteSQLException e) {
- assertEquals("Unexpected error code [expected=" + expCode + ", actual=" + e.statusCode() + ']',
- expCode, e.statusCode());
-
- return;
- }
- catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
-
- fail(IgniteSQLException.class.getSimpleName() + " is not thrown.");
- }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
index 82d10cd..6ed914c 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
@@ -50,7 +50,6 @@ import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
@@ -1609,12 +1608,11 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest {
* @return Configuration.
* @throws Exception If failed.
*/
- private IgniteConfiguration commonConfiguration(int idx) throws Exception {
- IgniteConfiguration cfg = super.getConfiguration(getTestIgniteInstanceName(idx));
+ protected IgniteConfiguration commonConfiguration(int idx) throws Exception {
+ IgniteConfiguration cfg = super.commonConfiguration(idx);
DataRegionConfiguration dataRegionCfg = new DataRegionConfiguration().setName(DATA_REGION_NAME);
- cfg.setMarshaller(new BinaryMarshaller());
cfg.setDataStorageConfiguration(new DataStorageConfiguration().setDataRegionConfigurations(dataRegionCfg));
return optimize(cfg);
@@ -1623,16 +1621,6 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest {
/**
* Execute DDL statement on given node.
*
- * @param node Node.
- * @param sql Statement.
- */
- private List<List<?>> execute(Ignite node, String sql) {
- return queryProcessor(node).querySqlFields(new SqlFieldsQuery(sql).setSchema("PUBLIC"), true).getAll();
- }
-
- /**
- * Execute DDL statement on given node.
- *
* @param sql Statement.
*/
private List<List<?>> executeLocal(GridCacheContext cctx, String sql) {
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsComandsWithMvccDisabledSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsComandsWithMvccDisabledSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsComandsWithMvccDisabledSelfTest.java
new file mode 100644
index 0000000..d2931ba
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsComandsWithMvccDisabledSelfTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.index;
+
+import java.util.concurrent.Callable;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.testframework.GridTestUtils;
+
+/**
+ *
+ */
+public class SqlTransactionsComandsWithMvccDisabledSelfTest extends AbstractSchemaSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGrid(commonConfiguration(0));
+
+ super.execute(grid(0), "CREATE TABLE INTS(k int primary key, v int) WITH \"wrap_value=false,cache_name=ints," +
+ "atomicity=transactional\"");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+
+ super.afterTestsStopped();
+ }
+
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testBeginWithMvccDisabledThrows() throws Exception {
+ checkMvccDisabledBehavior("BEGIN");
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testCommitWithMvccDisabledThrows() throws Exception {
+ checkMvccDisabledBehavior("COMMIT");
+ }
+
+ /**
+ * @throws Exception if failed.
+ */
+ public void testRollbackWithMvccDisabledThrows() throws Exception {
+ checkMvccDisabledBehavior("rollback");
+ }
+
+ /**
+ * @param sql Operation to test.
+ * @throws Exception if failed.
+ */
+ private void checkMvccDisabledBehavior(String sql) throws Exception {
+ try (IgniteEx node = startGrid(commonConfiguration(1))) {
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ execute(node, sql);
+
+ return null;
+ }
+ }, IgniteSQLException.class, "MVCC must be enabled in order to invoke transactional operation: " + sql);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsSelfTest.java
new file mode 100644
index 0000000..7081e34
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/SqlTransactionsSelfTest.java
@@ -0,0 +1,421 @@
+/*
+ * 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.index;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.GatewayProtectedCacheProxy;
+import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionState;
+
+/**
+ * Tests to check behavior regarding transactions started via SQL.
+ */
+public class SqlTransactionsSelfTest extends AbstractSchemaSelfTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ super.beforeTestsStarted();
+
+ startGrid(commonConfiguration(0).setMvccEnabled(true));
+
+ super.execute(node(), "CREATE TABLE INTS(k int primary key, v int) WITH \"wrap_value=false,cache_name=ints," +
+ "atomicity=transactional\"");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+
+ super.afterTestsStopped();
+ }
+
+ /**
+ * Test that BEGIN opens a transaction.
+ */
+ public void testBegin() {
+ execute(node(), "BEGIN");
+
+ assertTxPresent();
+
+ assertTxState(tx(), TransactionState.ACTIVE);
+ }
+
+ /**
+ * Test that COMMIT commits a transaction.
+ */
+ public void testCommit() {
+ execute(node(), "BEGIN WORK");
+
+ assertTxPresent();
+
+ Transaction tx = tx();
+
+ assertTxState(tx, TransactionState.ACTIVE);
+
+ execute(node(), "COMMIT TRANSACTION");
+
+ assertTxState(tx, TransactionState.COMMITTED);
+
+ assertSqlTxNotPresent();
+ }
+
+ /**
+ * Test that COMMIT without a transaction yields nothing.
+ */
+ public void testCommitNoTransaction() {
+ execute(node(), "COMMIT");
+ }
+
+ /**
+ * Test that ROLLBACK without a transaction yields nothing.
+ */
+ public void testRollbackNoTransaction() {
+ execute(node(), "ROLLBACK");
+ }
+
+ /**
+ * Test that ROLLBACK rolls back a transaction.
+ */
+ public void testRollback() {
+ execute(node(), "BEGIN TRANSACTION");
+
+ assertTxPresent();
+
+ Transaction tx = tx();
+
+ assertTxState(tx, TransactionState.ACTIVE);
+
+ execute(node(), "ROLLBACK TRANSACTION");
+
+ assertTxState(tx, TransactionState.ROLLED_BACK);
+
+ assertSqlTxNotPresent();
+ }
+
+ /**
+ * Test that attempting to perform various SQL operations within non SQL transaction yields an exception.
+ */
+ public void testSqlOperationsWithinNonSqlTransaction() {
+ assertSqlOperationWithinNonSqlTransactionThrows("COMMIT");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("ROLLBACK");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("SELECT * from ints");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("DELETE from ints");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("INSERT INTO ints(k, v) values(10, 15)");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("MERGE INTO ints(k, v) values(10, 15)");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("UPDATE ints SET v = 100 WHERE k = 5");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("create index idx on ints(v)");
+
+ assertSqlOperationWithinNonSqlTransactionThrows("CREATE TABLE T(k int primary key, v int)");
+ }
+
+ /**
+ * Check that trying to run given SQL statement both locally and in distributed mode yields an exception
+ * if transaction already has been marked as being of SQL type.
+ * @param sql SQL statement.
+ */
+ private void assertSqlOperationWithinNonSqlTransactionThrows(final String sql) {
+ try (Transaction ignored = node().transactions().txStart()) {
+ node().cache("ints").put(1, 1);
+
+ assertSqlException(new RunnableX() {
+ @Override public void run() throws Exception {
+ execute(node(), sql);
+ }
+ }, IgniteQueryErrorCode.TRANSACTION_TYPE_MISMATCH);
+ }
+
+ try (Transaction ignored = node().transactions().txStart()) {
+ node().cache("ints").put(1, 1);
+
+ assertSqlException(new RunnableX() {
+ @Override public void run() throws Exception {
+ node().cache("ints").query(new SqlFieldsQuery(sql).setLocal(true)).getAll();
+ }
+ }, IgniteQueryErrorCode.TRANSACTION_TYPE_MISMATCH);
+ }
+ }
+
+ /**
+ * Test that attempting to perform a cache API operation from within an SQL transaction fails.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ private void checkCacheOperationThrows(final String opName, final Object... args) {
+ execute(node(), "BEGIN");
+
+ try {
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ try {
+ // We need to detect types based on arguments due to multiple overloads.
+ Class[] types;
+
+ if (F.isEmpty(args))
+ types = (Class[]) X.EMPTY_OBJECT_ARRAY;
+ else {
+ types = new Class[args.length];
+
+ for (int i = 0; i < args.length; i++)
+ types[i] = argTypeForObject(args[i]);
+ }
+
+ Object res = U.invoke(GatewayProtectedCacheProxy.class, node().cache("ints"),
+ opName, types, args);
+
+ if (opName.endsWith("Async"))
+ ((IgniteFuture)res).get();
+ }
+ catch (IgniteCheckedException e) {
+ if (e.getCause() != null) {
+ try {
+ if (e.getCause().getCause() != null)
+ throw (Exception)e.getCause().getCause();
+ else
+ fail();
+ }
+ catch (IgniteException e1) {
+ // Some public API methods don't have IgniteCheckedException on their signature
+ // and thus may wrap it into an IgniteException.
+ if (e1.getCause() != null)
+ throw (Exception)e1.getCause();
+ else
+ fail();
+ }
+ }
+ else
+ fail();
+ }
+
+ return null;
+ }
+ }, IgniteCheckedException.class,
+ "SQL queries and cache operations may not be used in the same transaction.");
+ }
+ finally {
+ try {
+ execute(node(), "ROLLBACK");
+ }
+ catch (Throwable e) {
+ // No-op.
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private static Class<?> argTypeForObject(Object arg) {
+ if (arg instanceof Set)
+ return Set.class;
+ else if (arg instanceof Map)
+ return Map.class;
+ else if (arg.getClass().getName().startsWith("java.lang."))
+ return Object.class;
+ else if (arg instanceof CacheEntryProcessor)
+ return CacheEntryProcessor.class;
+ else if (arg instanceof EntryProcessor)
+ return EntryProcessor.class;
+ else
+ return arg.getClass();
+ }
+
+ /**
+ * Test that attempting to perform a cache PUT operation from within an SQL transaction fails.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testCacheOperationsFromSqlTransaction() {
+ checkCacheOperationThrows("get", 1);
+
+ checkCacheOperationThrows("getAsync", 1);
+
+ checkCacheOperationThrows("getEntry", 1);
+
+ checkCacheOperationThrows("getEntryAsync", 1);
+
+ checkCacheOperationThrows("getAndPut", 1, 1);
+
+ checkCacheOperationThrows("getAndPutAsync", 1, 1);
+
+ checkCacheOperationThrows("getAndPutIfAbsent", 1, 1);
+
+ checkCacheOperationThrows("getAndPutIfAbsentAsync", 1, 1);
+
+ checkCacheOperationThrows("getAndReplace", 1, 1);
+
+ checkCacheOperationThrows("getAndReplaceAsync", 1, 1);
+
+ checkCacheOperationThrows("getAndRemove", 1);
+
+ checkCacheOperationThrows("getAndRemoveAsync", 1);
+
+ checkCacheOperationThrows("containsKey", 1);
+
+ checkCacheOperationThrows("containsKeyAsync", 1);
+
+ checkCacheOperationThrows("put", 1, 1);
+
+ checkCacheOperationThrows("putAsync", 1, 1);
+
+ checkCacheOperationThrows("putIfAbsent", 1, 1);
+
+ checkCacheOperationThrows("putIfAbsentAsync", 1, 1);
+
+ checkCacheOperationThrows("remove", 1);
+
+ checkCacheOperationThrows("removeAsync", 1);
+
+ checkCacheOperationThrows("remove", 1, 1);
+
+ checkCacheOperationThrows("removeAsync", 1, 1);
+
+ checkCacheOperationThrows("replace", 1, 1);
+
+ checkCacheOperationThrows("replaceAsync", 1, 1);
+
+ checkCacheOperationThrows("replace", 1, 1, 1);
+
+ checkCacheOperationThrows("replaceAsync", 1, 1, 1);
+
+ checkCacheOperationThrows("getAll", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("containsKeys", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("getEntries", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("putAll", Collections.singletonMap(1, 1));
+
+ checkCacheOperationThrows("removeAll", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("getAllAsync", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("containsKeysAsync", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("getEntriesAsync", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("putAllAsync", Collections.singletonMap(1, 1));
+
+ checkCacheOperationThrows("removeAllAsync", new HashSet<>(Arrays.asList(1, 2)));
+
+ checkCacheOperationThrows("invoke", 1, ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invoke", 1, CACHE_ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAsync", 1, ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAsync", 1, CACHE_ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAll", Collections.singletonMap(1, CACHE_ENTRY_PROC), X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAll", Collections.singleton(1), CACHE_ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAll", Collections.singleton(1), ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAllAsync", Collections.singletonMap(1, CACHE_ENTRY_PROC),
+ X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAllAsync", Collections.singleton(1), CACHE_ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+
+ checkCacheOperationThrows("invokeAllAsync", Collections.singleton(1), ENTRY_PROC, X.EMPTY_OBJECT_ARRAY);
+ }
+
+ /** */
+ private final static EntryProcessor<Integer, Integer, Object> ENTRY_PROC =
+ new EntryProcessor<Integer, Integer, Object>() {
+ @Override public Object process(MutableEntry<Integer, Integer> entry, Object... arguments)
+ throws EntryProcessorException {
+ return null;
+ }
+ };
+
+ /** */
+ private final static CacheEntryProcessor<Integer, Integer, Object> CACHE_ENTRY_PROC =
+ new CacheEntryProcessor<Integer, Integer, Object>() {
+ @Override public Object process(MutableEntry<Integer, Integer> entry, Object... arguments)
+ throws EntryProcessorException {
+ return null;
+ }
+ };
+
+ /**
+ * @return Node.
+ */
+ private IgniteEx node() {
+ return grid(0);
+ }
+
+ /**
+ * @return Currently open transaction.
+ */
+ private Transaction tx() {
+ return node().transactions().tx();
+ }
+
+ /**
+ * Check that there's an open transaction with SQL flag.
+ */
+ private void assertTxPresent() {
+ assertNotNull(tx());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected List<List<?>> execute(Ignite node, String sql) {
+ return node.cache("ints").query(new SqlFieldsQuery(sql).setSchema(QueryUtils.DFLT_SCHEMA)).getAll();
+ }
+
+ /**
+ * Check that there's no open transaction.
+ */
+ private void assertSqlTxNotPresent() {
+ assertNull(tx());
+ }
+
+ /**
+ * Check transaction state.
+ */
+ private static void assertTxState(Transaction tx, TransactionState state) {
+ assertEquals(state, tx.state());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/00bb4d4d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractSqlCoordinatorFailoverTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractSqlCoordinatorFailoverTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractSqlCoordinatorFailoverTest.java
new file mode 100644
index 0000000..c449ee2
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/mvcc/CacheMvccAbstractSqlCoordinatorFailoverTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.mvcc;
+
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SCAN;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.ReadMode.SQL;
+import static org.apache.ignite.internal.processors.cache.mvcc.CacheMvccAbstractTest.WriteMode.DML;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/**
+ * Mvcc SQL API coordinator failover test.
+ */
+@SuppressWarnings("unchecked")
+public abstract class CacheMvccAbstractSqlCoordinatorFailoverTest extends CacheMvccAbstractBasicCoordinatorFailoverTest {
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_Server_Backups0_CoordinatorFails() throws Exception {
+ accountsTxReadAll(2, 1, 0, 64,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testAccountsTxSql_SingleNode_CoordinatorFails_Persistence() throws Exception {
+ persistence = true;
+
+ accountsTxReadAll(1, 0, 0, 1,
+ new InitIndexing(Integer.class, MvccTestAccount.class), true, SQL, DML, DFLT_TEST_TIME, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups0_RestartCoordinator_ScanDml() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD , 2, 1, 0, 64,
+ new InitIndexing(Integer.class, Integer.class), SCAN, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_RestartCoordinator_ScanDml_Persistence() throws Exception {
+ persistence = true;
+
+ putAllGetAll(RestartMode.RESTART_CRD , 1, 0, 0, 1,
+ new InitIndexing(Integer.class, Integer.class), SCAN, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_ClientServer_Backups0_RestartCoordinator_SqlDml() throws Exception {
+ putAllGetAll(RestartMode.RESTART_CRD, 2, 1, 0, DFLT_PARTITION_COUNT,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testPutAllGetAll_SingleNode_RestartCoordinator_SqlDml_Persistence() throws Exception {
+ persistence = true;
+
+ putAllGetAll(RestartMode.RESTART_CRD, 1, 0, 0, 1,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_ClientServer_Backups0_Sql_Persistence() throws Exception {
+ persistence = true;
+
+ updateNObjectsTest(5, 2, 0, 0, 64, DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testUpdate_N_Objects_SingleNode_Sql_Persistence() throws Exception {
+ updateNObjectsTest(3, 1, 0, 0, 1, DFLT_TEST_TIME,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML, RestartMode.RESTART_CRD);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorFailureSimplePessimisticTxSql() throws Exception {
+ coordinatorFailureSimple(PESSIMISTIC, REPEATABLE_READ, SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testTxInProgressCoordinatorChangeSimple_Readonly() throws Exception {
+ txInProgressCoordinatorChangeSimple(PESSIMISTIC, REPEATABLE_READ,
+ new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testReadInProgressCoordinatorFailsSimple_FromClient() throws Exception {
+ readInProgressCoordinatorFailsSimple(true, new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorChangeActiveQueryClientFails_Simple() throws Exception {
+ checkCoordinatorChangeActiveQueryClientFails_Simple(new InitIndexing(Integer.class, Integer.class), SQL, DML);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testCoordinatorChangeActiveQueryClientFails_SimpleScan() throws Exception {
+ checkCoordinatorChangeActiveQueryClientFails_Simple(new InitIndexing(Integer.class, Integer.class), SCAN, DML);
+ }
+}
[44/45] ignite git commit: IGNITE-6552 Added ability to set WAL
history size in bytes - Fixes #3559.
Posted by ag...@apache.org.
IGNITE-6552 Added ability to set WAL history size in bytes - Fixes #3559.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/3307a8b2
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/3307a8b2
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/3307a8b2
Branch: refs/heads/ignite-9273
Commit: 3307a8b26ccb5f0bb7e9c387c73fd221b98ab668
Parents: 00bb4d4
Author: Anton Kalashnikov <ka...@yandex.ru>
Authored: Thu Aug 30 15:02:50 2018 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Thu Aug 30 15:10:59 2018 +0300
----------------------------------------------------------------------
.../apache/ignite/IgniteSystemProperties.java | 11 +
.../configuration/DataStorageConfiguration.java | 42 +++
.../pagemem/wal/IgniteWriteAheadLogManager.java | 5 +
.../GridCacheDatabaseSharedManager.java | 16 +-
.../IgniteCacheDatabaseSharedManager.java | 34 +++
.../checkpoint/CheckpointHistory.java | 102 +++++--
.../cache/persistence/wal/FileDescriptor.java | 136 +++++++++
.../wal/FileWriteAheadLogManager.java | 185 ++++++-------
.../wal/FsyncModeFileWriteAheadLogManager.java | 214 ++++++--------
.../SingleSegmentLogicalRecordsIterator.java | 4 +-
.../wal/reader/IgniteWalIteratorFactory.java | 2 +-
.../reader/StandaloneWalRecordsIterator.java | 2 +-
.../utils/PlatformConfigurationUtils.java | 2 +
.../db/IgnitePdsReserveWalSegmentsTest.java | 188 +++++++++++++
.../db/IgnitePdsUnusedWalSegmentsTest.java | 198 -------------
...IsReachedDuringIterationOverArchiveTest.java | 2 +-
.../persistence/db/wal/WalCompactionTest.java | 8 +-
.../db/wal/WalDeletionArchiveAbstractTest.java | 277 +++++++++++++++++++
.../db/wal/WalDeletionArchiveFsyncTest.java | 31 +++
.../db/wal/WalDeletionArchiveLogOnlyTest.java | 31 +++
.../db/wal/WalRecoveryTxLogicalRecordsTest.java | 44 ---
.../db/wal/reader/MockWalIteratorFactory.java | 1 +
.../persistence/pagemem/NoOpWALManager.java | 6 +
.../ignite/testsuites/IgnitePdsTestSuite2.java | 14 +-
.../DataStorageConfigurationParityTest.cs | 3 +-
.../Configuration/DataStorageConfiguration.cs | 14 +
.../IgniteConfigurationSection.xsd | 5 +
27 files changed, 1067 insertions(+), 510 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 78842d6..00ab49a 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -875,6 +875,17 @@ public final class IgniteSystemProperties {
public static final String IGNITE_LOADED_PAGES_BACKWARD_SHIFT_MAP = "IGNITE_LOADED_PAGES_BACKWARD_SHIFT_MAP";
/**
+ * Property for setup percentage of archive size for checkpoint trigger. Default value is 0.25
+ */
+ public static final String IGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE = "IGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE";
+
+ /**
+ * Property for setup percentage of WAL archive size to calculate threshold since which removing of old archive should be started.
+ * Default value is 0.5
+ */
+ public static final String IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE = "IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE";
+
+ /**
* Whenever read load balancing is enabled, that means 'get' requests will be distributed between primary and backup
* nodes if it is possible and {@link CacheConfiguration#readFromBackup} is {@code true}.
*
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
index d8619aa..e7e8ca3 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
@@ -114,6 +114,9 @@ public class DataStorageConfiguration implements Serializable {
/** Default number of checkpoints to be kept in WAL after checkpoint is finished */
public static final int DFLT_WAL_HISTORY_SIZE = 20;
+ /** Default max size of WAL archive files, in bytes */
+ public static final long DFLT_WAL_ARCHIVE_MAX_SIZE = 1024 * 1024 * 1024;
+
/** */
public static final int DFLT_WAL_SEGMENTS = 10;
@@ -190,6 +193,9 @@ public class DataStorageConfiguration implements Serializable {
/** Number of checkpoints to keep */
private int walHistSize = DFLT_WAL_HISTORY_SIZE;
+ /** Maximum size of wal archive folder, in bytes */
+ private long maxWalArchiveSize = DFLT_WAL_ARCHIVE_MAX_SIZE;
+
/** Number of work WAL segments. */
private int walSegments = DFLT_WAL_SEGMENTS;
@@ -487,7 +493,10 @@ public class DataStorageConfiguration implements Serializable {
* Gets a total number of checkpoints to keep in the WAL history.
*
* @return Number of checkpoints to keep in WAL after a checkpoint is finished.
+ * @deprecated Instead of walHistorySize use maxWalArchiveSize for manage of archive size.
+ * @see DataStorageConfiguration#getMaxWalArchiveSize()
*/
+ @Deprecated
public int getWalHistorySize() {
return walHistSize <= 0 ? DFLT_WAL_HISTORY_SIZE : walHistSize;
}
@@ -497,7 +506,10 @@ public class DataStorageConfiguration implements Serializable {
*
* @param walHistSize Number of checkpoints to keep after a checkpoint is finished.
* @return {@code this} for chaining.
+ * @deprecated Instead of walHistorySize use maxWalArchiveSize for manage of archive size.
+ * @see DataStorageConfiguration#setMaxWalArchiveSize(long)
*/
+ @Deprecated
public DataStorageConfiguration setWalHistorySize(int walHistSize) {
this.walHistSize = walHistSize;
@@ -505,6 +517,36 @@ public class DataStorageConfiguration implements Serializable {
}
/**
+ * If WalHistorySize was set by user will use this parameter for compatibility.
+ *
+ * @return {@code true} if use WalHistorySize for compatibility.
+ */
+ public boolean isWalHistorySizeParameterUsed() {
+ return getWalHistorySize() != DFLT_WAL_HISTORY_SIZE && getWalHistorySize() != Integer.MAX_VALUE;
+ }
+
+ /**
+ * Gets a max allowed size of WAL archives. In bytes.
+ *
+ * @return max size of WAL archive directory.
+ */
+ public long getMaxWalArchiveSize() {
+ return maxWalArchiveSize <= 0 ? DFLT_WAL_ARCHIVE_MAX_SIZE : maxWalArchiveSize;
+ }
+
+ /**
+ * Sets a max allowed size of WAL archives. In bytes
+ *
+ * @param walArchiveMaxSize max size of WAL archive directory.
+ * @return {@code this} for chaining.
+ */
+ public DataStorageConfiguration setMaxWalArchiveSize(long walArchiveMaxSize) {
+ this.maxWalArchiveSize = walArchiveMaxSize;
+
+ return this;
+ }
+
+ /**
* Gets a number of WAL segments to work with.
*
* @return Number of work WAL segments.
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
index b5282ff..12fd3e9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
@@ -135,6 +135,11 @@ public interface IgniteWriteAheadLogManager extends GridCacheSharedManager, Igni
public long lastCompactedSegment();
/**
+ * @return Max allowed index of archived segment to delete or -1 if it does not exist.
+ */
+ public long maxArchivedSegmentToDelete();
+
+ /**
* Checks if WAL segment is under lock or reserved
*
* @param ptr Pointer to check.
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index 7d91645..7393c01 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -167,6 +167,7 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_WAL_REBALANCE_
import static org.apache.ignite.failure.FailureType.CRITICAL_ERROR;
import static org.apache.ignite.failure.FailureType.SYSTEM_WORKER_TERMINATION;
import static org.apache.ignite.internal.pagemem.wal.record.WALRecord.RecordType.CHECKPOINT_RECORD;
+import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_WAL_HISTORY_SIZE;
import static org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage.METASTORAGE_CACHE_ID;
/**
@@ -360,7 +361,9 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
checkpointFreq = persistenceCfg.getCheckpointFrequency();
- truncateWalOnCpFinish = persistenceCfg.getWalHistorySize() != Integer.MAX_VALUE;
+ truncateWalOnCpFinish = persistenceCfg.isWalHistorySizeParameterUsed()
+ ? persistenceCfg.getWalHistorySize() != Integer.MAX_VALUE
+ : persistenceCfg.getMaxWalArchiveSize() != Long.MAX_VALUE;
lockWaitTime = persistenceCfg.getLockWaitTime();
@@ -497,7 +500,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
/**
* Cleanup checkpoint directory from all temporary files {@link #FILE_TMP_SUFFIX}.
*/
- public void cleanupTempCheckpointDirectory() throws IgniteCheckedException {
+ @Override public void cleanupTempCheckpointDirectory() throws IgniteCheckedException {
try {
try (DirectoryStream<Path> files = Files.newDirectoryStream(
cpDir.toPath(),
@@ -515,7 +518,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
/**
* Cleanup checkpoint directory.
*/
- public void cleanupCheckpointDirectory() throws IgniteCheckedException {
+ @Override public void cleanupCheckpointDirectory() throws IgniteCheckedException {
try {
try (DirectoryStream<Path> files = Files.newDirectoryStream(cpDir.toPath())) {
for (Path path : files)
@@ -1780,6 +1783,13 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
return cp.wakeupForCheckpoint(0, reason);
}
+ /** {@inheritDoc} */
+ @Override public WALPointer lastCheckpointMarkWalPointer() {
+ CheckpointEntry lastCheckpointEntry = cpHistory == null ? null : cpHistory.lastCheckpoint();
+
+ return lastCheckpointEntry == null ? null : lastCheckpointEntry.checkpointMark();
+ }
+
/**
* @return Checkpoint directory.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
index 3ade265..737b02a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
@@ -42,6 +42,7 @@ import org.apache.ignite.internal.mem.file.MappedFileMemoryProvider;
import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl;
+import org.apache.ignite.internal.pagemem.wal.WALPointer;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
@@ -69,6 +70,8 @@ import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_DATA_REG_DEFAULT_NAME;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_PAGE_SIZE;
+import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_WAL_ARCHIVE_MAX_SIZE;
+import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_WAL_HISTORY_SIZE;
/**
*
@@ -388,6 +391,30 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
}
checkDataRegionConfiguration(memCfg, regNames, memCfg.getDefaultDataRegionConfiguration());
+
+ checkWalArchiveSizeConfiguration(memCfg);
+ }
+
+ /**
+ * Check wal archive size configuration for correctness.
+ *
+ * @param memCfg durable memory configuration for an Apache Ignite node.
+ */
+ private void checkWalArchiveSizeConfiguration(DataStorageConfiguration memCfg) throws IgniteCheckedException {
+ if (memCfg.getWalHistorySize() == DFLT_WAL_HISTORY_SIZE || memCfg.getWalHistorySize() == Integer.MAX_VALUE)
+ LT.warn(log, "DataRegionConfiguration.maxWalArchiveSize instead DataRegionConfiguration.walHistorySize " +
+ "would be used for removing old archive wal files");
+ else if(memCfg.getMaxWalArchiveSize() == DFLT_WAL_ARCHIVE_MAX_SIZE)
+ LT.warn(log, "walHistorySize was deprecated. maxWalArchiveSize should be used instead");
+ else
+ throw new IgniteCheckedException("Should be used only one of wal history size or max wal archive size." +
+ "(use DataRegionConfiguration.maxWalArchiveSize because DataRegionConfiguration.walHistorySize was deprecated)"
+ );
+
+ if(memCfg.getMaxWalArchiveSize() < memCfg.getWalSegmentSize())
+ throw new IgniteCheckedException(
+ "DataRegionConfiguration.maxWalArchiveSize should be greater than DataRegionConfiguration.walSegmentSize"
+ );
}
/**
@@ -748,6 +775,13 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
}
/**
+ * @return Last checkpoint mark WAL pointer.
+ */
+ public WALPointer lastCheckpointMarkWalPointer() {
+ return null;
+ }
+
+ /**
* Allows to wait checkpoint finished.
*
* @param reason Reason.
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointHistory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointHistory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointHistory.java
index 3fb8457..e64a53f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointHistory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointHistory.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.pagemem.wal.WALPointer;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager.Checkpoint;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -64,6 +65,9 @@ public class CheckpointHistory {
/** The maximal number of checkpoints hold in memory. */
private final int maxCpHistMemSize;
+ /** If WalHistorySize was setted by user will use old way for removing checkpoints. */
+ private final boolean isWalHistorySizeParameterEnabled;
+
/**
* Constructor.
*
@@ -77,6 +81,8 @@ public class CheckpointHistory {
maxCpHistMemSize = Math.min(dsCfg.getWalHistorySize(),
IgniteSystemProperties.getInteger(IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE, 100));
+
+ isWalHistorySizeParameterEnabled = dsCfg.isWalHistorySizeParameterUsed();
}
/**
@@ -198,36 +204,98 @@ public class CheckpointHistory {
*
* @return List of checkpoints removed from history.
*/
- public List<CheckpointEntry> onCheckpointFinished(GridCacheDatabaseSharedManager.Checkpoint chp, boolean truncateWal) {
- List<CheckpointEntry> rmv = new ArrayList<>();
-
+ public List<CheckpointEntry> onCheckpointFinished(Checkpoint chp, boolean truncateWal) {
chp.walSegsCoveredRange(calculateWalSegmentsCovered());
+ WALPointer checkpointMarkUntilDel = isWalHistorySizeParameterEnabled //check for compatibility mode.
+ ? checkpointMarkUntilDeleteByMemorySize()
+ : newerPointer(checkpointMarkUntilDeleteByMemorySize(), checkpointMarkUntilDeleteByArchiveSize());
+
+ if (checkpointMarkUntilDel == null)
+ return Collections.emptyList();
+
+ List<CheckpointEntry> deletedCheckpoints = onWalTruncated(checkpointMarkUntilDel);
+
int deleted = 0;
- while (histMap.size() > maxCpHistMemSize) {
- Map.Entry<Long, CheckpointEntry> entry = histMap.firstEntry();
+ if (truncateWal)
+ deleted += cctx.wal().truncate(null, firstCheckpointPointer());
- CheckpointEntry cpEntry = entry.getValue();
+ chp.walFilesDeleted(deleted);
- if (cctx.wal().reserved(cpEntry.checkpointMark())) {
- U.warn(log, "Could not clear historyMap due to WAL reservation on cpEntry " + cpEntry.checkpointId() +
- ", history map size is " + histMap.size());
+ return deletedCheckpoints;
+ }
- break;
- }
+ /**
+ * @param firstPointer One of pointers to choose the newest.
+ * @param secondPointer One of pointers to choose the newest.
+ * @return The newest pointer from input ones.
+ */
+ private FileWALPointer newerPointer(WALPointer firstPointer, WALPointer secondPointer) {
+ FileWALPointer first = (FileWALPointer)firstPointer;
+ FileWALPointer second = (FileWALPointer)secondPointer;
+
+ if (firstPointer == null)
+ return second;
+
+ if (secondPointer == null)
+ return first;
- if (truncateWal)
- deleted += cctx.wal().truncate(null, cpEntry.checkpointMark());
+ return first.index() > second.index() ? first : second;
+ }
- histMap.remove(entry.getKey());
+ /**
+ * Calculate mark until delete by maximum checkpoint history memory size.
+ *
+ * @return Checkpoint mark until which checkpoints can be deleted(not including this pointer).
+ */
+ private WALPointer checkpointMarkUntilDeleteByMemorySize() {
+ if (histMap.size() <= maxCpHistMemSize)
+ return null;
- rmv.add(cpEntry);
+ int calculatedCpHistSize = maxCpHistMemSize;
+
+ for (Map.Entry<Long, CheckpointEntry> entry : histMap.entrySet()) {
+ if (histMap.size() <= calculatedCpHistSize++)
+ return entry.getValue().checkpointMark();
}
- chp.walFilesDeleted(deleted);
+ return lastCheckpoint().checkpointMark();
+ }
+
+ /**
+ * Calculate mark until delete by maximum allowed archive size.
+ *
+ * @return Checkpoint mark until which checkpoints can be deleted(not including this pointer).
+ */
+ @Nullable private WALPointer checkpointMarkUntilDeleteByArchiveSize() {
+ long absFileIdxToDel = cctx.wal().maxArchivedSegmentToDelete();
+
+ if (absFileIdxToDel < 0)
+ return null;
+
+ long fileUntilDel = absFileIdxToDel + 1;
- return rmv;
+ long checkpointFileIdx = absFileIdx(lastCheckpoint());
+
+ for (CheckpointEntry cpEntry : histMap.values()) {
+ long currFileIdx = absFileIdx(cpEntry);
+
+ if (checkpointFileIdx <= currFileIdx || fileUntilDel <= currFileIdx)
+ return cpEntry.checkpointMark();
+ }
+
+ return lastCheckpoint().checkpointMark();
+ }
+
+ /**
+ * Retrieve absolute file index by checkpoint entry.
+ *
+ * @param pointer checkpoint entry for which need to calculate absolute file index.
+ * @return absolute file index for given checkpoint entry.
+ */
+ private long absFileIdx(CheckpointEntry pointer) {
+ return ((FileWALPointer)pointer.checkpointMark()).index();
}
/**
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileDescriptor.java
new file mode 100644
index 0000000..39052cc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileDescriptor.java
@@ -0,0 +1,136 @@
+/*
+ * 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.persistence.wal;
+
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * WAL file descriptor.
+ */
+public class FileDescriptor implements Comparable<FileDescriptor>, AbstractWalRecordsIterator.AbstractFileDescriptor {
+
+ /** file extension of WAL segment. */
+ private static final String WAL_SEGMENT_FILE_EXT = ".wal";
+
+ /** Length of WAL segment file name. */
+ private static final int WAL_SEGMENT_FILE_NAME_LENGTH = 16;
+
+ /** File represented by this class. */
+ protected final File file;
+
+ /** Absolute WAL segment file index. */
+ protected final long idx;
+
+ /**
+ * Creates file descriptor. Index is restored from file name.
+ *
+ * @param file WAL segment file.
+ */
+ public FileDescriptor(@NotNull File file) {
+ this(file, null);
+ }
+
+ /**
+ * @param file WAL segment file.
+ * @param idx Absolute WAL segment file index. For null value index is restored from file name.
+ */
+ public FileDescriptor(@NotNull File file, @Nullable Long idx) {
+ this.file = file;
+
+ String fileName = file.getName();
+
+ assert fileName.contains(WAL_SEGMENT_FILE_EXT);
+
+ this.idx = idx == null ? Long.parseLong(fileName.substring(0, WAL_SEGMENT_FILE_NAME_LENGTH)) : idx;
+ }
+
+ /**
+ * @param segment Segment index.
+ * @return Segment file name.
+ */
+ public static String fileName(long segment) {
+ SB b = new SB();
+
+ String segmentStr = Long.toString(segment);
+
+ for (int i = segmentStr.length(); i < WAL_SEGMENT_FILE_NAME_LENGTH; i++)
+ b.a('0');
+
+ b.a(segmentStr).a(WAL_SEGMENT_FILE_EXT);
+
+ return b.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int compareTo(@NotNull FileDescriptor o) {
+ return Long.compare(idx, o.idx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof FileDescriptor))
+ return false;
+
+ FileDescriptor that = (FileDescriptor)o;
+
+ return idx == that.idx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return (int)(idx ^ (idx >>> 32));
+ }
+
+ /**
+ * @return Absolute WAL segment file index
+ */
+ public long getIdx() {
+ return idx;
+ }
+
+ /**
+ * @return absolute pathname string of this file descriptor pathname.
+ */
+ public String getAbsolutePath() {
+ return file.getAbsolutePath();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isCompressed() {
+ return file.getName().endsWith(".zip");
+ }
+
+ /** {@inheritDoc} */
+ @Override public File file() {
+ return file;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long idx() {
+ return idx;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
index 3fa499b..61def1e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
@@ -59,6 +59,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -110,7 +111,6 @@ import org.apache.ignite.internal.util.typedef.CI1;
import org.apache.ignite.internal.util.typedef.CIX1;
import org.apache.ignite.internal.util.typedef.CO;
import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.util.worker.GridWorker;
import org.apache.ignite.lang.IgniteBiTuple;
@@ -124,6 +124,8 @@ import org.jetbrains.annotations.Nullable;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_WAL_MMAP;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_WAL_SEGMENT_SYNC_TIMEOUT;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_WAL_SERIALIZER_VERSION;
@@ -170,9 +172,6 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
/** */
private static final FileDescriptor[] EMPTY_DESCRIPTORS = new FileDescriptor[0];
- /** WAL segment file extension. */
- private static final String WAL_SEGMENT_FILE_EXT = ".wal";
-
/** */
private static final byte[] FILL_BUF = new byte[1024 * 1024];
@@ -237,6 +236,19 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
private static final AtomicLongFieldUpdater<FileWriteHandle> WRITTEN_UPD =
AtomicLongFieldUpdater.newUpdater(FileWriteHandle.class, "written");
+ /**
+ * Percentage of archive size for checkpoint trigger. Need for calculate max size of WAL after last checkpoint.
+ * Checkpoint should be triggered when max size of WAL after last checkpoint more than maxWallArchiveSize * thisValue
+ */
+ private static final double CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE =
+ IgniteSystemProperties.getDouble(IGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE, 0.25);
+
+ /**
+ * Percentage of WAL archive size to calculate threshold since which removing of old archive should be started.
+ */
+ private static final double THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE =
+ IgniteSystemProperties.getDouble(IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE, 0.5);
+
/** Interrupted flag. */
private final ThreadLocal<Boolean> interrupted = new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
@@ -250,6 +262,15 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
/** WAL segment size in bytes. . This is maximum value, actual segments may be shorter. */
private final long maxWalSegmentSize;
+ /**
+ * Maximum number of allowed segments without checkpoint. If we have their more checkpoint should be triggered.
+ * It is simple way to calculate WAL size without checkpoint instead fair WAL size calculating.
+ */
+ private final long maxSegCountWithoutCheckpoint;
+
+ /** Size of wal archive since which removing of old archive should be started */
+ private final long allowedThresholdWalArchiveSize;
+
/** */
private final WALMode mode;
@@ -304,8 +325,8 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
AtomicReferenceFieldUpdater.newUpdater(FileWriteAheadLogManager.class, FileWriteHandle.class, "currHnd");
/**
- * File archiver moves segments from work directory to archive. Locked segments may be kept not moved until
- * release. For mode archive and work folders set to equal value, archiver is not created.
+ * File archiver moves segments from work directory to archive. Locked segments may be kept not moved until release.
+ * For mode archive and work folders set to equal value, archiver is not created.
*/
@Nullable private volatile FileArchiver archiver;
@@ -379,6 +400,12 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
alwaysWriteFullPages = dsCfg.isAlwaysWriteFullPages();
ioFactory = new RandomAccessFileIOFactory();
walAutoArchiveAfterInactivity = dsCfg.getWalAutoArchiveAfterInactivity();
+
+ maxSegCountWithoutCheckpoint =
+ (long)((dsCfg.getMaxWalArchiveSize() * CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE) / dsCfg.getWalSegmentSize());
+
+ allowedThresholdWalArchiveSize = (long)(dsCfg.getMaxWalArchiveSize() * THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE);
+
evt = ctx.event();
failureProcessor = ctx.failure();
}
@@ -760,7 +787,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
rec.size(serializer.size(rec));
while (true) {
- if (rec.rollOver()){
+ if (rec.rollOver()) {
assert cctx.database().checkpointLockIsHeldByThread();
long idx = currWrHandle.idx;
@@ -1141,6 +1168,9 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
next.writeHeader();
+ if (next.idx - lashCheckpointFileIdx() >= maxSegCountWithoutCheckpoint)
+ cctx.database().forceCheckpoint("too big size of WAL without checkpoint");
+
boolean swapped = CURR_HND_UPD.compareAndSet(this, hnd, next);
assert swapped : "Concurrent updates on rollover are not allowed";
@@ -1158,6 +1188,15 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
}
/**
+ * Give last checkpoint file idx.
+ */
+ private long lashCheckpointFileIdx() {
+ WALPointer lastCheckpointMark = cctx.database().lastCheckpointMarkWalPointer();
+
+ return lastCheckpointMark == null ? 0 : ((FileWALPointer)lastCheckpointMark).index();
+ }
+
+ /**
* @param lastReadPtr Last read WAL file pointer.
* @return Initialized file write handle.
* @throws StorageException If failed to initialize WAL write handle.
@@ -1297,7 +1336,6 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
serializer,
rbuf);
-
if (interrupted)
Thread.currentThread().interrupt();
@@ -1367,7 +1405,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
File[] allFiles = walWorkDir.listFiles(WAL_SEGMENT_FILE_FILTER);
- if(isArchiverEnabled())
+ if (isArchiverEnabled())
if (allFiles.length != 0 && allFiles.length > dsCfg.getWalSegments())
throw new StorageException("Failed to initialize wal (work directory contains " +
"incorrect number of segments) [cur=" + allFiles.length + ", expected=" + dsCfg.getWalSegments() + ']');
@@ -1498,6 +1536,39 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
return new File(walWorkDir, FileDescriptor.fileName(segmentIdx));
}
+ /**
+ * Files from archive WAL directory.
+ */
+ private FileDescriptor[] walArchiveFiles() {
+ return scan(walArchiveDir.listFiles(WAL_SEGMENT_COMPACTED_OR_RAW_FILE_FILTER));
+ }
+
+ /** {@inheritDoc} */
+ @Override public long maxArchivedSegmentToDelete() {
+ //When maxWalArchiveSize==MAX_VALUE deleting files is not permit.
+ if (dsCfg.getMaxWalArchiveSize() == Long.MAX_VALUE)
+ return -1;
+
+ FileDescriptor[] archivedFiles = walArchiveFiles();
+
+ Long totalArchiveSize = Stream.of(archivedFiles)
+ .map(desc -> desc.file().length())
+ .reduce(0L, Long::sum);
+
+ if (archivedFiles.length == 0 || totalArchiveSize < allowedThresholdWalArchiveSize)
+ return -1;
+
+ long sizeOfOldestArchivedFiles = 0;
+
+ for (FileDescriptor desc : archivedFiles) {
+ sizeOfOldestArchivedFiles += desc.file().length();
+
+ if (totalArchiveSize - sizeOfOldestArchivedFiles < allowedThresholdWalArchiveSize)
+ return desc.getIdx();
+ }
+
+ return archivedFiles[archivedFiles.length - 1].getIdx();
+ }
/**
* @return Sorted WAL files descriptors.
@@ -2367,98 +2438,6 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
}
/**
- * WAL file descriptor.
- */
- public static class FileDescriptor implements
- Comparable<FileDescriptor>, AbstractFileDescriptor {
- /** */
- protected final File file;
-
- /** Absolute WAL segment file index */
- protected final long idx;
-
- /**
- * Creates file descriptor. Index is restored from file name
- *
- * @param file WAL segment file.
- */
- public FileDescriptor(@NotNull File file) {
- this(file, null);
- }
-
- /**
- * @param file WAL segment file.
- * @param idx Absolute WAL segment file index. For null value index is restored from file name
- */
- public FileDescriptor(@NotNull File file, @Nullable Long idx) {
- this.file = file;
-
- String fileName = file.getName();
-
- assert fileName.contains(WAL_SEGMENT_FILE_EXT);
-
- this.idx = idx == null ? Long.parseLong(fileName.substring(0, 16)) : idx;
- }
-
- /**
- * @param segment Segment index.
- * @return Segment file name.
- */
- public static String fileName(long segment) {
- SB b = new SB();
-
- String segmentStr = Long.toString(segment);
-
- for (int i = segmentStr.length(); i < 16; i++)
- b.a('0');
-
- b.a(segmentStr).a(WAL_SEGMENT_FILE_EXT);
-
- return b.toString();
- }
-
- /** {@inheritDoc} */
- @Override public int compareTo(@NotNull FileDescriptor o) {
- return Long.compare(idx, o.idx);
- }
-
- /** {@inheritDoc} */
- @Override public boolean equals(Object o) {
- if (this == o)
- return true;
-
- if (!(o instanceof FileDescriptor))
- return false;
-
- FileDescriptor that = (FileDescriptor)o;
-
- return idx == that.idx;
- }
-
- /** {@inheritDoc} */
- @Override public int hashCode() {
- return (int)(idx ^ (idx >>> 32));
- }
-
- /**
- * @return True if segment is ZIP compressed.
- */
- @Override public boolean isCompressed() {
- return file.getName().endsWith(".zip");
- }
-
- /** {@inheritDoc} */
- @Override public File file() {
- return file;
- }
-
- /** {@inheritDoc} */
- @Override public long idx() {
- return idx;
- }
- }
-
- /**
*
*/
private abstract static class FileHandle {
@@ -2615,7 +2594,6 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
this.buf = buf;
}
-
/**
* Write serializer version to current handle.
*/
@@ -3040,7 +3018,8 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
* @param serializerFactory Serializer factory.
* @param archiver File Archiver.
* @param decompressor Decompressor.
- *@param log Logger @throws IgniteCheckedException If failed to initialize WAL segment.
+ * @param log Logger
+ * @throws IgniteCheckedException If failed to initialize WAL segment.
*/
private RecordsIterator(
GridCacheSharedContext cctx,
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FsyncModeFileWriteAheadLogManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FsyncModeFileWriteAheadLogManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FsyncModeFileWriteAheadLogManager.java
index 4797f81..91d3218 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FsyncModeFileWriteAheadLogManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FsyncModeFileWriteAheadLogManager.java
@@ -55,6 +55,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -103,7 +104,6 @@ import org.apache.ignite.internal.util.typedef.CIX1;
import org.apache.ignite.internal.util.typedef.CO;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
-import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.util.worker.GridWorker;
import org.apache.ignite.lang.IgniteBiTuple;
@@ -117,6 +117,8 @@ import org.jetbrains.annotations.Nullable;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_WAL_SERIALIZER_VERSION;
import static org.apache.ignite.events.EventType.EVT_WAL_SEGMENT_COMPACTED;
import static org.apache.ignite.failure.FailureType.CRITICAL_ERROR;
@@ -131,9 +133,6 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
public static final FileDescriptor[] EMPTY_DESCRIPTORS = new FileDescriptor[0];
/** */
- public static final String WAL_SEGMENT_FILE_EXT = ".wal";
-
- /** */
private static final byte[] FILL_BUF = new byte[1024 * 1024];
/** Pattern for segment file names */
@@ -187,12 +186,34 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
/** Latest serializer version to use. */
private static final int LATEST_SERIALIZER_VERSION = 2;
+ /**
+ * Percentage of archive size for checkpoint trigger. Need for calculate max size of WAL after last checkpoint.
+ * Checkpoint should be triggered when max size of WAL after last checkpoint more than maxWallArchiveSize * thisValue
+ */
+ private static final double CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE =
+ IgniteSystemProperties.getDouble(IGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE, 0.25);
+
+ /**
+ * Percentage of WAL archive size to calculate threshold since which removing of old archive should be started.
+ */
+ private static final double THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE =
+ IgniteSystemProperties.getDouble(IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE, 0.5);
+
/** */
private final boolean alwaysWriteFullPages;
/** WAL segment size in bytes */
private final long maxWalSegmentSize;
+ /**
+ * Maximum number of allowed segments without checkpoint. If we have their more checkpoint should be triggered.
+ * It is simple way to calculate wal size without checkpoint instead fair wal size calculating.
+ */
+ private final long maxSegCountWithoutCheckpoint;
+
+ /** Size of wal archive since which removing of old archive should be started */
+ private final long allowedThresholdWalArchiveSize;
+
/** */
private final WALMode mode;
@@ -321,6 +342,11 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
walAutoArchiveAfterInactivity = dsCfg.getWalAutoArchiveAfterInactivity();
evt = ctx.event();
+ maxSegCountWithoutCheckpoint =
+ (long)((dsCfg.getMaxWalArchiveSize() * CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE) / dsCfg.getWalSegmentSize());
+
+ allowedThresholdWalArchiveSize = (long)(dsCfg.getMaxWalArchiveSize() * THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE);
+
assert mode == WALMode.FSYNC : dsCfg;
}
@@ -589,7 +615,7 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
List<File> res = new ArrayList<>();
for (long i = low.index(); i < high.index(); i++) {
- String segmentName = FileWriteAheadLogManager.FileDescriptor.fileName(i);
+ String segmentName = FileDescriptor.fileName(i);
File file = new File(walArchiveDir, segmentName);
File fileZip = new File(walArchiveDir, segmentName + ".zip");
@@ -869,6 +895,40 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
return res >= 0 ? res : 0;
}
+ /**
+ * Files from archive WAL directory.
+ */
+ private FileDescriptor[] walArchiveFiles() {
+ return scan(walArchiveDir.listFiles(WAL_SEGMENT_COMPACTED_OR_RAW_FILE_FILTER));
+ }
+
+ /** {@inheritDoc} */
+ @Override public long maxArchivedSegmentToDelete() {
+ //When maxWalArchiveSize==MAX_VALUE deleting files is not permit.
+ if (dsCfg.getMaxWalArchiveSize() == Long.MAX_VALUE)
+ return -1;
+
+ FileDescriptor[] archivedFiles = walArchiveFiles();
+
+ Long totalArchiveSize = Stream.of(archivedFiles)
+ .map(desc -> desc.file().length())
+ .reduce(0L, Long::sum);
+
+ if (archivedFiles.length == 0 || totalArchiveSize < allowedThresholdWalArchiveSize)
+ return -1;
+
+ long sizeOfOldestArchivedFiles = 0;
+
+ for (FileDescriptor desc : archivedFiles) {
+ sizeOfOldestArchivedFiles += desc.file().length();
+
+ if (totalArchiveSize - sizeOfOldestArchivedFiles < allowedThresholdWalArchiveSize)
+ return desc.getIdx();
+ }
+
+ return archivedFiles[archivedFiles.length - 1].getIdx();
+ }
+
/** {@inheritDoc} */
@Override public long lastArchivedSegment() {
return archiver.lastArchivedAbsoluteIndex();
@@ -1058,6 +1118,9 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
FileWriteHandle next = initNextWriteHandle(cur.idx);
+ if (next.idx - lashCheckpointFileIdx() >= maxSegCountWithoutCheckpoint)
+ cctx.database().forceCheckpoint("too big size of WAL without checkpoint");
+
boolean swapped = currentHndUpd.compareAndSet(this, hnd, next);
assert swapped : "Concurrent updates on rollover are not allowed";
@@ -1075,6 +1138,15 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
}
/**
+ * Give last checkpoint file idx
+ */
+ private long lashCheckpointFileIdx() {
+ WALPointer lastCheckpointMark = cctx.database().lastCheckpointMarkWalPointer();
+
+ return lastCheckpointMark == null ? 0 : ((FileWALPointer)lastCheckpointMark).index();
+ }
+
+ /**
* @param lastReadPtr Last read WAL file pointer.
* @return Initialized file write handle.
* @throws StorageException If failed to initialize WAL write handle.
@@ -1820,19 +1892,19 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
* Deletes raw WAL segments if they aren't locked and already have compressed copies of themselves.
*/
private void deleteObsoleteRawSegments() {
- FsyncModeFileWriteAheadLogManager.FileDescriptor[] descs = scan(walArchiveDir.listFiles(WAL_SEGMENT_COMPACTED_OR_RAW_FILE_FILTER));
+ FileDescriptor[] descs = scan(walArchiveDir.listFiles(WAL_SEGMENT_COMPACTED_OR_RAW_FILE_FILTER));
Set<Long> indices = new HashSet<>();
Set<Long> duplicateIndices = new HashSet<>();
- for (FsyncModeFileWriteAheadLogManager.FileDescriptor desc : descs) {
+ for (FileDescriptor desc : descs) {
if (!indices.add(desc.idx))
duplicateIndices.add(desc.idx);
}
FileArchiver archiver0 = archiver;
- for (FsyncModeFileWriteAheadLogManager.FileDescriptor desc : descs) {
+ for (FileDescriptor desc : descs) {
if (desc.isCompressed())
continue;
@@ -1862,11 +1934,11 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
if (currReservedSegment == -1)
continue;
- File tmpZip = new File(walArchiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(currReservedSegment) + ".zip" + ".tmp");
+ File tmpZip = new File(walArchiveDir, FileDescriptor.fileName(currReservedSegment) + ".zip" + ".tmp");
- File zip = new File(walArchiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(currReservedSegment) + ".zip");
+ File zip = new File(walArchiveDir, FileDescriptor.fileName(currReservedSegment) + ".zip");
- File raw = new File(walArchiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(currReservedSegment));
+ File raw = new File(walArchiveDir, FileDescriptor.fileName(currReservedSegment));
if (!Files.exists(raw.toPath()))
throw new IgniteCheckedException("WAL archive segment is missing: " + raw);
@@ -2194,126 +2266,6 @@ public class FsyncModeFileWriteAheadLogManager extends GridCacheSharedManagerAda
}
/**
- * WAL file descriptor.
- */
- public static class FileDescriptor implements Comparable<FileDescriptor>, AbstractWalRecordsIterator.AbstractFileDescriptor {
- /** */
- protected final File file;
-
- /** Absolute WAL segment file index */
- protected final long idx;
-
- /**
- * Creates file descriptor. Index is restored from file name
- *
- * @param file WAL segment file.
- */
- public FileDescriptor(@NotNull File file) {
- this(file, null);
- }
-
- /**
- * @param file WAL segment file.
- * @param idx Absolute WAL segment file index. For null value index is restored from file name
- */
- public FileDescriptor(@NotNull File file, @Nullable Long idx) {
- this.file = file;
-
- String fileName = file.getName();
-
- assert fileName.contains(WAL_SEGMENT_FILE_EXT);
-
- this.idx = idx == null ? Long.parseLong(fileName.substring(0, 16)) : idx;
- }
-
- /**
- * @param segment Segment index.
- * @return Segment file name.
- */
- public static String fileName(long segment) {
- SB b = new SB();
-
- String segmentStr = Long.toString(segment);
-
- for (int i = segmentStr.length(); i < 16; i++)
- b.a('0');
-
- b.a(segmentStr).a(WAL_SEGMENT_FILE_EXT);
-
- return b.toString();
- }
-
- /**
- * @param segment Segment number as integer.
- * @return Segment number as aligned string.
- */
- private static String segmentNumber(long segment) {
- SB b = new SB();
-
- String segmentStr = Long.toString(segment);
-
- for (int i = segmentStr.length(); i < 16; i++)
- b.a('0');
-
- b.a(segmentStr);
-
- return b.toString();
- }
-
- /** {@inheritDoc} */
- @Override public int compareTo(FileDescriptor o) {
- return Long.compare(idx, o.idx);
- }
-
- /** {@inheritDoc} */
- @Override public boolean equals(Object o) {
- if (this == o)
- return true;
-
- if (!(o instanceof FileDescriptor))
- return false;
-
- FileDescriptor that = (FileDescriptor)o;
-
- return idx == that.idx;
- }
-
- /** {@inheritDoc} */
- @Override public int hashCode() {
- return (int)(idx ^ (idx >>> 32));
- }
-
- /**
- * @return Absolute WAL segment file index
- */
- public long getIdx() {
- return idx;
- }
-
- /**
- * @return absolute pathname string of this file descriptor pathname.
- */
- public String getAbsolutePath() {
- return file.getAbsolutePath();
- }
-
- /** {@inheritDoc} */
- @Override public boolean isCompressed() {
- return file.getName().endsWith(".zip");
- }
-
- /** {@inheritDoc} */
- @Override public File file() {
- return file;
- }
-
- /** {@inheritDoc} */
- @Override public long idx() {
- return idx;
- }
- }
-
- /**
*
*/
private abstract static class FileHandle {
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/SingleSegmentLogicalRecordsIterator.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/SingleSegmentLogicalRecordsIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/SingleSegmentLogicalRecordsIterator.java
index f688bb4..a42eb89 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/SingleSegmentLogicalRecordsIterator.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/SingleSegmentLogicalRecordsIterator.java
@@ -100,8 +100,8 @@ public class SingleSegmentLogicalRecordsIterator extends AbstractWalRecordsItera
else {
segmentInitialized = true;
- FileWriteAheadLogManager.FileDescriptor fd = new FileWriteAheadLogManager.FileDescriptor(
- new File(archiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(curWalSegmIdx)));
+ FileDescriptor fd = new FileDescriptor(
+ new File(archiveDir, FileDescriptor.fileName(curWalSegmIdx)));
try {
return initReadHandle(fd, null);
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/IgniteWalIteratorFactory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/IgniteWalIteratorFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/IgniteWalIteratorFactory.java
index cda8d90..0e61fae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/IgniteWalIteratorFactory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/IgniteWalIteratorFactory.java
@@ -43,9 +43,9 @@ import org.apache.ignite.internal.processors.cache.persistence.file.FileIO;
import org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory;
import org.apache.ignite.internal.processors.cache.persistence.file.UnzipFileIO;
import org.apache.ignite.internal.processors.cache.persistence.wal.ByteBufferExpander;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileInput;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
-import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager.FileDescriptor;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.U;
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneWalRecordsIterator.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneWalRecordsIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneWalRecordsIterator.java
index e934f33..6729790 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneWalRecordsIterator.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/reader/StandaloneWalRecordsIterator.java
@@ -41,9 +41,9 @@ import org.apache.ignite.internal.processors.cache.persistence.file.FileIO;
import org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory;
import org.apache.ignite.internal.processors.cache.persistence.file.UnzipFileIO;
import org.apache.ignite.internal.processors.cache.persistence.wal.AbstractWalRecordsIterator;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileInput;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
-import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager.FileDescriptor;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager.ReadFileHandle;
import org.apache.ignite.internal.processors.cache.persistence.wal.WalSegmentTailReachedException;
import org.apache.ignite.internal.processors.cache.persistence.wal.serializer.RecordSerializer;
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
index 79780f7..cd67f15 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
@@ -1820,6 +1820,7 @@ public class PlatformConfigurationUtils {
.setCheckpointWriteOrder(CheckpointWriteOrder.fromOrdinal(in.readInt()))
.setWriteThrottlingEnabled(in.readBoolean())
.setWalCompactionEnabled(in.readBoolean())
+ .setMaxWalArchiveSize(in.readLong())
.setSystemRegionInitialSize(in.readLong())
.setSystemRegionMaxSize(in.readLong())
.setPageSize(in.readInt())
@@ -1947,6 +1948,7 @@ public class PlatformConfigurationUtils {
w.writeInt(cfg.getCheckpointWriteOrder().ordinal());
w.writeBoolean(cfg.isWriteThrottlingEnabled());
w.writeBoolean(cfg.isWalCompactionEnabled());
+ w.writeLong(cfg.getMaxWalArchiveSize());
w.writeLong(cfg.getSystemRegionInitialSize());
w.writeLong(cfg.getSystemRegionMaxSize());
w.writeInt(cfg.getPageSize());
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsReserveWalSegmentsTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsReserveWalSegmentsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsReserveWalSegmentsTest.java
new file mode 100644
index 0000000..5885b7a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsReserveWalSegmentsTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.persistence.db;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.WALMode;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointHistory;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE;
+
+/**
+ * Test correctness of truncating unused WAL segments.
+ */
+public class IgnitePdsReserveWalSegmentsTest extends GridCommonAbstractTest {
+ /** */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+ System.setProperty(IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE, "2");
+
+ IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+ cfg.setConsistentId(gridName);
+
+ cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER));
+
+ CacheConfiguration<Integer, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
+
+ ccfg.setAffinity(new RendezvousAffinityFunction(false, 32));
+
+ cfg.setCacheConfiguration(ccfg);
+
+ DataStorageConfiguration dbCfg = new DataStorageConfiguration();
+
+ dbCfg.setPageSize(4 * 1024);
+
+ cfg.setDataStorageConfiguration(dbCfg);
+
+ dbCfg.setWalSegmentSize(1024 * 1024)
+ .setMaxWalArchiveSize(Long.MAX_VALUE)
+ .setWalSegments(10)
+ .setWalMode(WALMode.LOG_ONLY)
+ .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+ .setMaxSize(100 * 1024 * 1024)
+ .setPersistenceEnabled(true));
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ stopAllGrids();
+
+ cleanPersistenceDir();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+
+ cleanPersistenceDir();
+ }
+
+ /**
+ * Tests that range reserved method return correct number of reserved WAL segments.
+ *
+ * @throws Exception if failed.
+ */
+ public void testWalManagerRangeReservation() throws Exception {
+ IgniteEx ig0 = prepareGrid(4);
+
+ GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager)ig0.context().cache().context()
+ .database();
+
+ IgniteWriteAheadLogManager wal = ig0.context().cache().context().wal();
+
+ long resIdx = getReservedWalSegmentIndex(dbMgr);
+
+ assertTrue("Expected that at least resIdx greater than 0, real is " + resIdx, resIdx > 0);
+
+ FileWALPointer lowPtr = (FileWALPointer)dbMgr.checkpointHistory().firstCheckpointPointer();
+
+ assertTrue("Expected that dbMbr returns valid resIdx", lowPtr.index() == resIdx);
+
+ // Reserve previous WAL segment.
+ wal.reserve(new FileWALPointer(resIdx - 1, 0, 0));
+
+ int resCnt = wal.reserved(new FileWALPointer(resIdx - 1, 0, 0), new FileWALPointer(resIdx, 0, 0));
+
+ assertTrue("Expected resCnt is 2, real is " + resCnt, resCnt == 2);
+ }
+
+ /**
+ * Tests that grid cache manager correctly truncates unused WAL segments;
+ *
+ * @throws Exception if failed.
+ */
+ public void testWalDoesNotTruncatedWhenSegmentReserved() throws Exception {
+ IgniteEx ig0 = prepareGrid(4);
+
+ GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager)ig0.context().cache().context()
+ .database();
+
+ IgniteWriteAheadLogManager wal = ig0.context().cache().context().wal();
+
+ long resIdx = getReservedWalSegmentIndex(dbMgr);
+
+ assertTrue("Expected that at least resIdx greater than 0, real is " + resIdx, resIdx > 0);
+
+ FileWALPointer lowPtr = (FileWALPointer) dbMgr.checkpointHistory().firstCheckpointPointer();
+
+ assertTrue("Expected that dbMbr returns valid resIdx", lowPtr.index() == resIdx);
+
+ // Reserve previous WAL segment.
+ wal.reserve(new FileWALPointer(resIdx - 1, 0, 0));
+
+ int numDel = wal.truncate(null, lowPtr);
+
+ int expNumDel = (int)resIdx - 1;
+
+ assertTrue("Expected del segments is " + expNumDel + ", real is " + numDel, expNumDel == numDel);
+ }
+
+ /**
+ * Starts grid and populates test data.
+ *
+ * @param cnt Grid count.
+ * @return First started grid.
+ * @throws Exception If failed.
+ */
+ private IgniteEx prepareGrid(int cnt) throws Exception {
+ IgniteEx ig0 = (IgniteEx)startGrids(cnt);
+
+ ig0.cluster().active(true);
+
+ IgniteCache<Object, Object> cache = ig0.cache(DEFAULT_CACHE_NAME);
+
+ for (int k = 0; k < 1_000; k++) {
+ cache.put(k, new byte[1024]);
+
+ if (k % 100 == 0)
+ forceCheckpoint();
+ }
+
+ return ig0;
+ }
+
+ /**
+ * Get index of reserved WAL segment by checkpointer.
+ *
+ * @param dbMgr Database shared manager.
+ */
+ private long getReservedWalSegmentIndex(GridCacheDatabaseSharedManager dbMgr) {
+ CheckpointHistory cpHist = dbMgr.checkpointHistory();
+
+ return ((FileWALPointer) cpHist.firstCheckpointPointer()).index();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsUnusedWalSegmentsTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsUnusedWalSegmentsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsUnusedWalSegmentsTest.java
deleted file mode 100644
index 06a9ec2..0000000
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsUnusedWalSegmentsTest.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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.persistence.db;
-
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
-import org.apache.ignite.configuration.DataStorageConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.configuration.WALMode;
-import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
-import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
-import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointHistory;
-import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
-import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
-import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
-import org.apache.ignite.testframework.GridTestUtils;
-import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
-
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE;
-
-/**
- * Test correctness of truncating unused WAL segments.
- */
-public class IgnitePdsUnusedWalSegmentsTest extends GridCommonAbstractTest {
- /** */
- private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
-
- /** {@inheritDoc} */
- @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
- System.setProperty(IGNITE_PDS_MAX_CHECKPOINT_MEMORY_HISTORY_SIZE, "2");
-
- IgniteConfiguration cfg = super.getConfiguration(gridName);
-
- cfg.setConsistentId(gridName);
-
- cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(IP_FINDER));
-
- CacheConfiguration<Integer, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
-
- ccfg.setAffinity(new RendezvousAffinityFunction(false, 32));
-
- cfg.setCacheConfiguration(ccfg);
-
- DataStorageConfiguration dbCfg = new DataStorageConfiguration();
-
- dbCfg.setPageSize(4 * 1024);
-
- cfg.setDataStorageConfiguration(dbCfg);
-
- dbCfg.setWalSegmentSize(1024 * 1024)
- .setWalHistorySize(Integer.MAX_VALUE)
- .setWalSegments(10)
- .setWalMode(WALMode.LOG_ONLY)
- .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
- .setMaxSize(100 * 1024 * 1024)
- .setPersistenceEnabled(true));
-
- return cfg;
- }
-
- /** {@inheritDoc} */
- @Override protected void beforeTest() throws Exception {
- cleanPersistenceDir();
- }
-
- /** {@inheritDoc} */
- @Override protected void afterTest() throws Exception {
- cleanPersistenceDir();
- }
-
- /**
- * Tests that range reserved method return correct number of reserved WAL segments.
- *
- * @throws Exception if failed.
- */
- public void testWalManagerRangeReservation() throws Exception {
- try{
- IgniteEx ig0 = prepareGrid(4);
-
- GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager) ig0.context().cache().context()
- .database();
-
- IgniteWriteAheadLogManager wal = ig0.context().cache().context().wal();
-
- long resIdx = getReservedWalSegmentIndex(dbMgr);
-
- assertTrue("Expected that at least resIdx greater than 0, real is " + resIdx, resIdx > 0);
-
- FileWALPointer lowPtr = (FileWALPointer)dbMgr.checkpointHistory().firstCheckpointPointer();
-
- assertTrue("Expected that dbMbr returns valid resIdx", lowPtr.index() == resIdx);
-
- // Reserve previous WAL segment.
- wal.reserve(new FileWALPointer(resIdx - 1, 0, 0));
-
- int resCnt = wal.reserved(new FileWALPointer(resIdx - 1, 0, 0), new FileWALPointer(resIdx, 0, 0));
-
- assertTrue("Expected resCnt is 2, real is " + resCnt, resCnt == 2);
- }
- finally {
- stopAllGrids();
- }
- }
-
- /**
- * Tests that grid cache manager correctly truncates unused WAL segments;
- *
- * @throws Exception if failed.
- */
- public void testUnusedWalTruncate() throws Exception {
- try{
- IgniteEx ig0 = prepareGrid(4);
-
- GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager) ig0.context().cache().context()
- .database();
-
- IgniteWriteAheadLogManager wal = ig0.context().cache().context().wal();
-
- long resIdx = getReservedWalSegmentIndex(dbMgr);
-
- assertTrue("Expected that at least resIdx greater than 0, real is " + resIdx, resIdx > 0);
-
- FileWALPointer lowPtr = (FileWALPointer) dbMgr.checkpointHistory().firstCheckpointPointer();
-
- assertTrue("Expected that dbMbr returns valid resIdx", lowPtr.index() == resIdx);
-
- // Reserve previous WAL segment.
- wal.reserve(new FileWALPointer(resIdx - 1, 0, 0));
-
- int numDel = wal.truncate(null, lowPtr);
-
- int expNumDel = (int)resIdx - 1;
-
- assertTrue("Expected del segments is " + expNumDel + ", real is " + numDel, expNumDel == numDel);
- }
- finally {
- stopAllGrids();
- }
- }
-
- /**
- * Starts grid and populates test data.
- *
- * @param cnt Grid count.
- * @return First started grid.
- * @throws Exception If failed.
- */
- private IgniteEx prepareGrid(int cnt) throws Exception {
- IgniteEx ig0 = (IgniteEx)startGrids(cnt);
-
- ig0.cluster().active(true);
-
- IgniteCache<Object, Object> cache = ig0.cache(DEFAULT_CACHE_NAME);
-
- for (int k = 0; k < 10_000; k++)
- cache.put(k, new byte[1024]);
-
- forceCheckpoint();
-
- for (int k = 0; k < 1_000; k++)
- cache.put(k, new byte[1024]);
-
- forceCheckpoint();
-
- return ig0;
- }
-
-
- /**
- * Get index of reserved WAL segment by checkpointer.
- *
- * @param dbMgr Database shared manager.
- */
- private long getReservedWalSegmentIndex(GridCacheDatabaseSharedManager dbMgr) {
- CheckpointHistory cpHist = dbMgr.checkpointHistory();
-
- return ((FileWALPointer) cpHist.firstCheckpointPointer()).index();
- }
-}
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWALTailIsReachedDuringIterationOverArchiveTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWALTailIsReachedDuringIterationOverArchiveTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWALTailIsReachedDuringIterationOverArchiveTest.java
index 5fd5a1b..e3c2c6c 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWALTailIsReachedDuringIterationOverArchiveTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWALTailIsReachedDuringIterationOverArchiveTest.java
@@ -38,8 +38,8 @@ import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
import org.apache.ignite.internal.processors.cache.persistence.file.FileIO;
import org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory;
import org.apache.ignite.internal.processors.cache.persistence.file.RandomAccessFileIOFactory;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
-import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager.FileDescriptor;
import org.apache.ignite.internal.processors.cache.persistence.wal.reader.IgniteWalIteratorFactory;
import org.apache.ignite.internal.processors.cache.persistence.wal.reader.IgniteWalIteratorFactory.IteratorParametersBuilder;
import org.apache.ignite.internal.util.typedef.internal.U;
http://git-wip-us.apache.org/repos/asf/ignite/blob/3307a8b2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalCompactionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalCompactionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalCompactionTest.java
index 938465c..f890d61 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalCompactionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalCompactionTest.java
@@ -33,7 +33,7 @@ import org.apache.ignite.configuration.WALMode;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.pagemem.FullPageId;
import org.apache.ignite.internal.pagemem.wal.record.PageSnapshot;
-import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
@@ -163,7 +163,7 @@ public class WalCompactionTest extends GridCommonAbstractTest {
File walDir = new File(dbDir, "wal");
File archiveDir = new File(walDir, "archive");
File nodeArchiveDir = new File(archiveDir, nodeFolderName);
- File walSegment = new File(nodeArchiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(0) + ".zip");
+ File walSegment = new File(nodeArchiveDir, FileDescriptor.fileName(0) + ".zip");
assertTrue(walSegment.exists());
assertTrue(walSegment.length() < WAL_SEGMENT_SIZE / 2); // Should be compressed at least in half.
@@ -268,7 +268,7 @@ public class WalCompactionTest extends GridCommonAbstractTest {
File walDir = new File(dbDir, "wal");
File archiveDir = new File(walDir, "archive");
File nodeArchiveDir = new File(archiveDir, nodeFolderName);
- File walSegment = new File(nodeArchiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(emptyIdx));
+ File walSegment = new File(nodeArchiveDir, FileDescriptor.fileName(emptyIdx));
try (RandomAccessFile raf = new RandomAccessFile(walSegment, "rw")) {
raf.setLength(0); // Clear wal segment, but don't delete.
@@ -358,7 +358,7 @@ public class WalCompactionTest extends GridCommonAbstractTest {
File walDir = new File(dbDir, "wal");
File archiveDir = new File(walDir, "archive");
File nodeArchiveDir = new File(archiveDir, nodeFolderName);
- File walSegment = new File(nodeArchiveDir, FileWriteAheadLogManager.FileDescriptor.fileName(0) + ".zip");
+ File walSegment = new File(nodeArchiveDir, FileDescriptor.fileName(0) + ".zip");
assertTrue(walSegment.exists());
assertTrue(walSegment.length() < WAL_SEGMENT_SIZE / 2); // Should be compressed at least in half.