From 1e08c3fb5c02ec8acafd71b50b6ad3b749259f1a Mon Sep 17 00:00:00 2001 From: "Andrey V. Mashenkov" Date: Mon, 31 Jul 2017 14:14:56 +0300 Subject: [PATCH 001/145] IGNITE-4800: Lucene query may fails with NPE. This closes #2315. --- .../query/h2/opt/GridLuceneDirectory.java | 64 ++++++++++--- .../query/h2/opt/GridLuceneFile.java | 91 ++++++++++++++----- .../query/h2/opt/GridLuceneIndex.java | 3 +- .../query/h2/opt/GridLuceneInputStream.java | 42 +++++++-- .../query/h2/opt/GridLuceneOutputStream.java | 18 +++- ...CacheFullTextQueryNodeJoiningSelfTest.java | 4 + .../IgniteCacheQuerySelfTestSuite.java | 2 + 7 files changed, 178 insertions(+), 46 deletions(-) diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java index ff20987b079f2..3ac9641f762db 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java @@ -21,22 +21,27 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; +import org.apache.ignite.IgniteException; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory; +import org.apache.ignite.internal.util.typedef.F; import org.apache.lucene.store.BaseDirectory; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.util.Accountable; +import org.apache.lucene.util.Accountables; /** * A memory-resident {@link Directory} implementation. */ -public class GridLuceneDirectory extends BaseDirectory { +public class GridLuceneDirectory extends BaseDirectory implements Accountable { /** */ protected final Map fileMap = new ConcurrentHashMap<>(); @@ -51,7 +56,7 @@ public class GridLuceneDirectory extends BaseDirectory { * * @param mem Memory. */ - public GridLuceneDirectory(GridUnsafeMemory mem) { + GridLuceneDirectory(GridUnsafeMemory mem) { super(new GridLuceneLockFactory()); this.mem = mem; @@ -64,10 +69,7 @@ public GridLuceneDirectory(GridUnsafeMemory mem) { // and the code below is resilient to map changes during the array population. Set fileNames = fileMap.keySet(); - List names = new ArrayList<>(fileNames.size()); - - for (String name : fileNames) - names.add(name); + List names = new ArrayList<>(fileNames); return names.toArray(new String[names.size()]); } @@ -82,6 +84,7 @@ public GridLuceneDirectory(GridUnsafeMemory mem) { throw new FileNotFoundException(source); fileMap.put(dest, file); + fileMap.remove(source); } @@ -101,21 +104,25 @@ public GridLuceneDirectory(GridUnsafeMemory mem) { @Override public void deleteFile(String name) throws IOException { ensureOpen(); - doDeleteFile(name); + doDeleteFile(name, false); } /** * Deletes file. * * @param name File name. + * @param onClose If on close directory; * @throws IOException If failed. */ - private void doDeleteFile(String name) throws IOException { + private void doDeleteFile(String name, boolean onClose) throws IOException { GridLuceneFile file = fileMap.remove(name); if (file != null) { file.delete(); + // All files should be closed when Directory is closing. + assert !onClose || !file.hasRefs() : "Possible memory leak, resource is not closed: " + file.toString(); + sizeInBytes.addAndGet(-file.getSizeInBytes()); } else @@ -128,7 +135,10 @@ private void doDeleteFile(String name) throws IOException { GridLuceneFile file = newRAMFile(); - GridLuceneFile existing = fileMap.remove(name); + // Lock for using in stream. Will be unlocked on stream closing. + file.lockRef(); + + GridLuceneFile existing = fileMap.put(name, file); if (existing != null) { sizeInBytes.addAndGet(-existing.getSizeInBytes()); @@ -136,8 +146,6 @@ private void doDeleteFile(String name) throws IOException { existing.delete(); } - fileMap.put(name, file); - return new GridLuceneOutputStream(file); } @@ -165,6 +173,16 @@ protected GridLuceneFile newRAMFile() { if (file == null) throw new FileNotFoundException(name); + // Lock for using in stream. Will be unlocked on stream closing. + file.lockRef(); + + if (!fileMap.containsKey(name)) { + // Unblock for deferred delete. + file.releaseRef(); + + throw new FileNotFoundException(name); + } + return new GridLuceneInputStream(name, file); } @@ -172,16 +190,36 @@ protected GridLuceneFile newRAMFile() { @Override public void close() { isOpen = false; + IgniteException errs = null; + for (String fileName : fileMap.keySet()) { try { - doDeleteFile(fileName); + doDeleteFile(fileName, true); } catch (IOException e) { - throw new IllegalStateException(e); + if (errs == null) + errs = new IgniteException("Error closing index directory."); + + errs.addSuppressed(e); } } assert fileMap.isEmpty(); + + if (errs != null && !F.isEmpty(errs.getSuppressed())) + throw errs; + } + + /** {@inheritDoc} */ + @Override public long ramBytesUsed() { + ensureOpen(); + + return sizeInBytes.get(); + } + + /** {@inheritDoc} */ + @Override public synchronized Collection getChildResources() { + return Accountables.namedAccountables("file", new HashMap<>(fileMap)); } /** diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java index 3985f0987b74f..d7ae132903b97 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java @@ -17,22 +17,19 @@ package org.apache.ignite.internal.processors.query.h2.opt; -import java.io.Serializable; import java.util.Arrays; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.lucene.util.Accountable; import static org.apache.ignite.internal.processors.query.h2.opt.GridLuceneOutputStream.BUFFER_SIZE; /** * Lucene file. */ -public class GridLuceneFile implements Serializable { - /** */ - private static final long serialVersionUID = 0L; - - /** */ - public static final AtomicInteger filesCnt = new AtomicInteger(); - +public class GridLuceneFile implements Accountable { /** */ private LongArray buffers = new LongArray(); @@ -45,6 +42,12 @@ public class GridLuceneFile implements Serializable { /** */ private volatile long sizeInBytes; + /** */ + private final AtomicLong refCnt = new AtomicLong(); + + /** */ + private final AtomicBoolean deleted = new AtomicBoolean(); + /** * File used as buffer, in no RAMDirectory * @@ -52,8 +55,6 @@ public class GridLuceneFile implements Serializable { */ GridLuceneFile(GridLuceneDirectory dir) { this.dir = dir; - - filesCnt.incrementAndGet(); } /** @@ -92,52 +93,90 @@ final long addBuffer() { return buf; } + /** + * Increment ref counter. + */ + void lockRef() { + refCnt.incrementAndGet(); + } + + /** + * Decrement ref counter. + */ + void releaseRef() { + refCnt.decrementAndGet(); + + deferredDelete(); + } + + /** + * Checks if there is file stream opened. + * + * @return {@code True} if file has external references. + */ + boolean hasRefs() { + long refs = refCnt.get(); + + assert refs >= 0; + + return refs != 0; + } + /** * Gets address of buffer. * * @param idx Index. * @return Pointer. */ - protected final synchronized long getBuffer(int idx) { + final synchronized long getBuffer(int idx) { return buffers.get(idx); } /** * @return Number of buffers. */ - protected final synchronized int numBuffers() { + final synchronized int numBuffers() { return buffers.size(); } /** - * Expert: allocate a new buffer. - * Subclasses can allocate differently. + * Expert: allocate a new buffer. Subclasses can allocate differently. * * @return allocated buffer. */ - protected long newBuffer() { + private long newBuffer() { return dir.memory().allocate(BUFFER_SIZE); } /** * Deletes file and deallocates memory.. */ - public synchronized void delete() { - if (buffers == null) + public void delete() { + if (!deleted.compareAndSet(false, true)) return; + deferredDelete(); + } + + /** + * Deferred delete. + */ + synchronized void deferredDelete() { + if (!deleted.get() || hasRefs()) + return; + + assert refCnt.get() == 0; + for (int i = 0; i < buffers.idx; i++) dir.memory().release(buffers.arr[i], BUFFER_SIZE); buffers = null; - - filesCnt.decrementAndGet(); } /** * @return Size in bytes. */ - public long getSizeInBytes() { + long getSizeInBytes() { return sizeInBytes; } @@ -148,6 +187,16 @@ public GridLuceneDirectory getDirectory() { return dir; } + /** {@inheritDoc} */ + @Override public long ramBytesUsed() { + return sizeInBytes; + } + + /** {@inheritDoc} */ + @Override public Collection getChildResources() { + return Collections.emptyList(); + } + /** * Simple expandable long[] wrapper. */ diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java index eed5ee4e36ae8..c51eb5d37cf54 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java @@ -296,7 +296,8 @@ public GridCloseableIterator> query(String qry, /** {@inheritDoc} */ @Override public void close() { U.closeQuiet(writer); - U.closeQuiet(dir); + + dir.close(); } /** diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneInputStream.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneInputStream.java index 4820af1b0967d..9b1bf0c2a9090 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneInputStream.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneInputStream.java @@ -20,6 +20,7 @@ import java.io.EOFException; import java.io.IOException; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory; +import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.IndexInput; import static org.apache.ignite.internal.processors.query.h2.opt.GridLuceneOutputStream.BUFFER_SIZE; @@ -27,7 +28,7 @@ /** * A memory-resident {@link IndexInput} implementation. */ -public class GridLuceneInputStream extends IndexInput { +public class GridLuceneInputStream extends IndexInput implements Cloneable { /** */ private GridLuceneFile file; @@ -52,6 +53,11 @@ public class GridLuceneInputStream extends IndexInput { /** */ private final GridUnsafeMemory mem; + /** */ + private volatile boolean closed; + + /** */ + private boolean isClone; /** * Constructor. * @@ -91,7 +97,24 @@ public GridLuceneInputStream(String name, GridLuceneFile f, final long length) t /** {@inheritDoc} */ @Override public void close() { - // nothing to do here + if (!isClone) { + closed = true; + + file.releaseRef(); + } + } + + /** {@inheritDoc} */ + @Override public IndexInput clone() { + GridLuceneInputStream clone = (GridLuceneInputStream) super.clone(); + + if(closed) + throw new AlreadyClosedException(toString()); + + clone.isClone = true; + + return clone; + } /** {@inheritDoc} */ @@ -222,14 +245,16 @@ private class SlicedInputStream extends GridLuceneInputStream { public SlicedInputStream(String newResourceDescription, long offset, long length) throws IOException { super(newResourceDescription, GridLuceneInputStream.this.file, offset + length); + // Avoid parent resource closing together with this. + super.isClone = true; + this.offset = offset; seek(0L); } /** {@inheritDoc} */ - @Override - public void seek(long pos) throws IOException { + @Override public void seek(long pos) throws IOException { if (pos < 0L) { throw new IllegalArgumentException("Seeking to negative position: " + this); } @@ -237,20 +262,17 @@ public void seek(long pos) throws IOException { } /** {@inheritDoc} */ - @Override - public long getFilePointer() { + @Override public long getFilePointer() { return super.getFilePointer() - offset; } /** {@inheritDoc} */ - @Override - public long length() { + @Override public long length() { return super.length() - offset; } /** {@inheritDoc} */ - @Override - public IndexInput slice(String sliceDescription, long ofs, long len) throws IOException { + @Override public IndexInput slice(String sliceDescription, long ofs, long len) throws IOException { return super.slice(sliceDescription, offset + ofs, len); } } diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneOutputStream.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneOutputStream.java index caea226022dc6..d8f09dfd0cf0f 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneOutputStream.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneOutputStream.java @@ -18,17 +18,21 @@ package org.apache.ignite.internal.processors.query.h2.opt; import java.io.IOException; +import java.util.Collection; +import java.util.Collections; import java.util.zip.CRC32; import java.util.zip.Checksum; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory; import org.apache.lucene.store.BufferedChecksum; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.util.Accountable; +import org.apache.lucene.util.Accountables; /** * A memory-resident {@link IndexOutput} implementation. */ -public class GridLuceneOutputStream extends IndexOutput { +public class GridLuceneOutputStream extends IndexOutput implements Accountable { /** Off-heap page size. */ static final int BUFFER_SIZE = 32 * 1024; @@ -93,6 +97,8 @@ public void reset() { /** {@inheritDoc} */ @Override public void close() throws IOException { flush(); + + file.releaseRef(); } /** {@inheritDoc} */ @@ -201,4 +207,14 @@ private void flush() throws IOException { bufPosition += toCp; } } + + /** {@inheritDoc} */ + @Override public long ramBytesUsed() { + return file.getSizeInBytes(); + } + + /** {@inheritDoc} */ + @Override public Collection getChildResources() { + return Collections.singleton(Accountables.namedAccountable("file", file)); + } } \ No newline at end of file diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java index ba0324fa25b77..ed092dfd1490b 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java @@ -119,6 +119,10 @@ public void testFullTextQueryNodeJoin() throws Exception { Ignite started = startGrid(GRID_CNT); + //TODO: remove next line when IGNITE-2229 issue will be fixed. + // see https://issues.apache.org/jira/browse/IGNITE-2229 + awaitPartitionMapExchange(); + for (int i = 0; i < 100; i++) { QueryCursor, IndexedEntity>> res = started.cache(DEFAULT_CACHE_NAME) .query(new TextQuery, IndexedEntity>(IndexedEntity.class, "indexed")); 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 258eed80811c4..1ad0d4bfb8ca2 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 @@ -47,6 +47,7 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheDeleteSqlQuerySelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheDuplicateEntityConfigurationSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheFieldsQueryNoDataSelfTest; +import org.apache.ignite.internal.processors.cache.IgniteCacheFullTextQueryNodeJoiningSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheInsertSqlQuerySelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheJoinPartitionedAndReplicatedTest; import org.apache.ignite.internal.processors.cache.IgniteCacheJoinQueryWithAffinityKeyTest; @@ -273,6 +274,7 @@ public static TestSuite suite() throws Exception { // Full text queries. suite.addTestSuite(GridCacheFullTextQuerySelfTest.class); + suite.addTestSuite(IgniteCacheFullTextQueryNodeJoiningSelfTest.class); // Ignite cache and H2 comparison. suite.addTestSuite(BaseH2CompareQueryTest.class); From 3fdf453e89a7bd76dff6b6d0646e3821ea3921d5 Mon Sep 17 00:00:00 2001 From: "Andrey V. Mashenkov" Date: Mon, 31 Jul 2017 17:32:12 +0300 Subject: [PATCH 002/145] IGNITE-4800: Lucene query may fails with NPE. Test fixed. --- .../cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java index ed092dfd1490b..162b1e5bacaaa 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheFullTextQueryNodeJoiningSelfTest.java @@ -42,7 +42,6 @@ import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; /** - * TODO https://issues.apache.org/jira/browse/IGNITE-2229 * Tests cache in-place modification logic with iterative value increment. */ public class IgniteCacheFullTextQueryNodeJoiningSelfTest extends GridCommonAbstractTest { @@ -107,6 +106,8 @@ protected CacheAtomicityMode atomicityMode() { * @throws Exception If failed. */ public void testFullTextQueryNodeJoin() throws Exception { + fail("https://issues.apache.org/jira/browse/IGNITE-2229"); + for (int r = 0; r < 5; r++) { startGrids(GRID_CNT); @@ -119,10 +120,6 @@ public void testFullTextQueryNodeJoin() throws Exception { Ignite started = startGrid(GRID_CNT); - //TODO: remove next line when IGNITE-2229 issue will be fixed. - // see https://issues.apache.org/jira/browse/IGNITE-2229 - awaitPartitionMapExchange(); - for (int i = 0; i < 100; i++) { QueryCursor, IndexedEntity>> res = started.cache(DEFAULT_CACHE_NAME) .query(new TextQuery, IndexedEntity>(IndexedEntity.class, "indexed")); From e255a564985a12113984ec02f15a4443495b8ffc Mon Sep 17 00:00:00 2001 From: Nikolay Izhikov Date: Wed, 2 Aug 2017 11:52:44 +0300 Subject: [PATCH 003/145] ignite-5712 Context switching for optimistic transactions --- .../ignite/tests/utils/TestTransaction.java | 10 + .../cache/GridCacheSharedContext.java | 24 + .../distributed/near/GridNearTxLocal.java | 51 ++ .../store/GridCacheStoreManagerAdapter.java | 10 + .../cache/transactions/IgniteTxAdapter.java | 20 +- .../cache/transactions/IgniteTxManager.java | 74 ++ .../cache/transactions/IgniteTxMap.java | 2 +- .../transactions/TransactionProxyImpl.java | 46 +- .../ignite/transactions/Transaction.java | 14 + .../ignite/transactions/TransactionState.java | 7 +- ...imisticTxSuspendResumeMultiServerTest.java | 30 + .../IgniteOptimisticTxSuspendResumeTest.java | 751 ++++++++++++++++++ .../IgnitePessimisticTxSuspendResumeTest.java | 91 +++ .../ignite/testframework/GridTestUtils.java | 26 + .../cache/GridAbstractCacheStoreSelfTest.java | 10 + .../testsuites/IgniteCacheTestSuite6.java | 42 + .../processors/cache/jta/CacheJtaManager.java | 5 +- .../cache/jta/CacheJtaResource.java | 28 +- .../GridJtaTransactionManagerSelfTest.java | 208 +++++ .../ignite/testsuites/IgniteJtaTestSuite.java | 5 +- 20 files changed, 1438 insertions(+), 16 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeMultiServerTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgnitePessimisticTxSuspendResumeTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java create mode 100644 modules/jta/src/test/java/org/apache/ignite/internal/processors/cache/GridJtaTransactionManagerSelfTest.java diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestTransaction.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestTransaction.java index 4a03d25a44b63..e587bd7862282 100644 --- a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestTransaction.java +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestTransaction.java @@ -140,4 +140,14 @@ public class TestTransaction implements Transaction { @Override public IgniteFuture rollbackAsync() throws IgniteException { return null; } + + /** {@inheritDoc} */ + @Override public void suspend() throws IgniteException{ + // No-op. + } + + /** {@inheritDoc} */ + @Override public void resume() throws IgniteException { + // No-op. + } } 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 5387cc8522790..3fb63dcd0d369 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 @@ -943,6 +943,30 @@ public IgniteInternalFuture rollbackTxAsync(GridNearTxLocal tx) throws IgniteChe return tx.rollbackNearTxLocalAsync(); } + /** + * Suspends transaction. It could be resume later. Supported only for optimistic transactions. + * + * @param tx Transaction to suspend. + * @throws IgniteCheckedException If suspension failed. + */ + public void suspendTx(GridNearTxLocal tx) throws IgniteCheckedException { + tx.txState().awaitLastFut(this); + + tx.suspend(); + } + + /** + * Resume transaction if it was previously suspended. + * + * @param tx Transaction to resume. + * @throws IgniteCheckedException If resume failed. + */ + public void resumeTx(GridNearTxLocal tx) throws IgniteCheckedException { + tx.txState().awaitLastFut(this); + + tx.resume(); + } + /** * @return Store session listeners. */ 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 81e5ca86ab643..2fb0ff32c180a 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 @@ -105,12 +105,14 @@ import static org.apache.ignite.internal.processors.cache.GridCacheOperation.UPDATE; import static org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry.SER_READ_EMPTY_ENTRY_VER; import static org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry.SER_READ_NOT_EMPTY_VER; +import static org.apache.ignite.transactions.TransactionState.ACTIVE; import static org.apache.ignite.transactions.TransactionState.COMMITTED; import static org.apache.ignite.transactions.TransactionState.COMMITTING; import static org.apache.ignite.transactions.TransactionState.PREPARED; import static org.apache.ignite.transactions.TransactionState.PREPARING; import static org.apache.ignite.transactions.TransactionState.ROLLED_BACK; import static org.apache.ignite.transactions.TransactionState.ROLLING_BACK; +import static org.apache.ignite.transactions.TransactionState.SUSPENDED; import static org.apache.ignite.transactions.TransactionState.UNKNOWN; /** @@ -2850,6 +2852,47 @@ public void addKeyMapping(IgniteTxKey key, ClusterNode node) { return txState.singleWrite(); } + /** + * Suspends transaction. It could be resumed later. Supported only for optimistic transactions. + * + * @throws IgniteCheckedException If the transaction is in an incorrect state, or timed out. + */ + public void suspend() throws IgniteCheckedException { + if (log.isDebugEnabled()) + log.debug("Suspend near local tx: " + this); + + if (pessimistic()) + throw new UnsupportedOperationException("Suspension is not supported for pessimistic transactions."); + + if (threadId() != Thread.currentThread().getId()) + throw new IgniteCheckedException("Only thread started transaction can suspend it."); + + synchronized (this) { + checkValid(); + + cctx.tm().suspendTx(this); + } + } + + /** + * Resumes transaction (possibly in another thread) if it was previously suspended. + * + * @throws IgniteCheckedException If the transaction is in an incorrect state, or timed out. + */ + public void resume() throws IgniteCheckedException { + if (log.isDebugEnabled()) + log.debug("Resume near local tx: " + this); + + if (pessimistic()) + throw new UnsupportedOperationException("Resume is not supported for pessimistic transactions."); + + synchronized (this) { + checkValid(); + + cctx.tm().resumeTx(this); + } + } + /** * @param maps Mappings. */ @@ -3951,6 +3994,14 @@ private IgniteInternalFuture nonInterruptable(IgniteInternalFuture fut return fut; } + /** + * @param threadId new owner of transaction. + * @throws IgniteCheckedException if method executed not in the middle of resume or suspend. + */ + public void threadId(long threadId) { + this.threadId = threadId; + } + /** * Post-lock closure. * diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheStoreManagerAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheStoreManagerAdapter.java index c02e2c73b2710..bb16ad1db2722 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheStoreManagerAdapter.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheStoreManagerAdapter.java @@ -1357,6 +1357,11 @@ private static class TxProxy implements Transaction { return tx.state(); } + /** {@inheritDoc} */ + @Override public void suspend() throws IgniteException { + throw new UnsupportedOperationException(); + } + /** {@inheritDoc} */ @Override public long timeout() { return tx.timeout(); @@ -1402,6 +1407,11 @@ private static class TxProxy implements Transaction { throw new UnsupportedOperationException(); } + /** {@inheritDoc} */ + @Override public void resume() throws IgniteException { + throw new UnsupportedOperationException(); + } + /** {@inheritDoc} */ @Override public IgniteAsyncSupport withAsync() { throw new UnsupportedOperationException(); 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 91ce3ceddf578..61ca78cbd570d 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 @@ -97,6 +97,7 @@ import static org.apache.ignite.transactions.TransactionState.PREPARING; import static org.apache.ignite.transactions.TransactionState.ROLLED_BACK; import static org.apache.ignite.transactions.TransactionState.ROLLING_BACK; +import static org.apache.ignite.transactions.TransactionState.SUSPENDED; /** * Managed transaction adapter. @@ -977,10 +978,10 @@ protected boolean state(TransactionState state, boolean timedOut) { switch (state) { case ACTIVE: { - valid = false; + valid = prev == SUSPENDED; break; - } // Active is initial state and cannot be transitioned to. + } case PREPARING: { valid = prev == ACTIVE; @@ -1025,15 +1026,20 @@ protected boolean state(TransactionState state, boolean timedOut) { } case MARKED_ROLLBACK: { - valid = prev == ACTIVE || prev == PREPARING || prev == PREPARED; + valid = prev == ACTIVE || prev == PREPARING || prev == PREPARED || prev == SUSPENDED; break; } case ROLLING_BACK: { - valid = - prev == ACTIVE || prev == MARKED_ROLLBACK || prev == PREPARING || - prev == PREPARED || (prev == COMMITTING && local() && !dht()); + valid = prev == ACTIVE || prev == MARKED_ROLLBACK || prev == PREPARING || + prev == PREPARED || prev == SUSPENDED || (prev == COMMITTING && local() && !dht()); + + break; + } + + case SUSPENDED: { + valid = prev == ACTIVE; break; } @@ -1064,7 +1070,7 @@ protected boolean state(TransactionState state, boolean timedOut) { if (valid) { // Seal transactions maps. - if (state != ACTIVE) + if (state != ACTIVE && state != SUSPENDED) seal(); } 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 7d612eceb8be3..a427da34f80d4 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 @@ -113,6 +113,7 @@ import static org.apache.ignite.transactions.TransactionState.MARKED_ROLLBACK; import static org.apache.ignite.transactions.TransactionState.PREPARED; import static org.apache.ignite.transactions.TransactionState.PREPARING; +import static org.apache.ignite.transactions.TransactionState.SUSPENDED; import static org.apache.ignite.transactions.TransactionState.UNKNOWN; import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q; @@ -2239,6 +2240,79 @@ public Collection> deadlockDetectionFutures() { return (Collection>)values; } + /** + * Suspends transaction. + * Should not be used directly. Use tx.suspend() instead. + * + * @param tx Transaction to be suspended. + * + * @see #resumeTx(GridNearTxLocal) + * @see GridNearTxLocal#suspend() + * @see GridNearTxLocal#resume() + */ + public void suspendTx(final GridNearTxLocal tx) throws IgniteCheckedException { + assert tx != null && !tx.system() : tx; + + if (!tx.state(SUSPENDED)) { + throw new IgniteCheckedException("Trying to suspend transaction with incorrect state " + + "[expected=" + ACTIVE + ", actual=" + tx.state() + ']'); + } + + clearThreadMap(tx); + + transactionMap(tx).remove(tx.xidVersion(), tx); + } + + /** + * Resume transaction in current thread. + * Please don't use directly. Use tx.resume() instead. + * + * @param tx Transaction to be resumed. + * + * @see #suspendTx(GridNearTxLocal) + * @see GridNearTxLocal#suspend() + * @see GridNearTxLocal#resume() + */ + public void resumeTx(GridNearTxLocal tx) throws IgniteCheckedException { + assert tx != null && !tx.system() : tx; + assert !threadMap.containsValue(tx) : tx; + assert !transactionMap(tx).containsValue(tx) : tx; + assert !haveSystemTxForThread(Thread.currentThread().getId()); + + if(!tx.state(ACTIVE)) { + throw new IgniteCheckedException("Trying to resume transaction with incorrect state " + + "[expected=" + SUSPENDED + ", actual=" + tx.state() + ']'); + } + + long threadId = Thread.currentThread().getId(); + + if (threadMap.putIfAbsent(threadId, tx) != null) + throw new IgniteCheckedException("Thread already start a transaction."); + + if (transactionMap(tx).putIfAbsent(tx.xidVersion(), tx) != null) + throw new IgniteCheckedException("Thread already start a transaction."); + + tx.threadId(threadId); + } + + /** + * @param threadId Thread id. + * @return True if thread have system transaction. False otherwise. + */ + private boolean haveSystemTxForThread(long threadId) { + if (!sysThreadMap.isEmpty()) { + for (GridCacheContext cacheCtx : cctx.cache().context().cacheContexts()) { + if (!cacheCtx.systemTx()) + continue; + + if (sysThreadMap.containsKey(new TxThreadKey(threadId, cacheCtx.cacheId()))) + return true; + } + } + + return false; + } + /** * Timeout object for node failure handler. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxMap.java index 429c995162e21..6b79550f0fb4b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxMap.java @@ -190,4 +190,4 @@ private void advance() { @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { throw new IllegalStateException("Transaction view map should never be serialized: " + this); } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionProxyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionProxyImpl.java index 8750cab00df4d..f25fc36b2b996 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionProxyImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TransactionProxyImpl.java @@ -44,6 +44,8 @@ import org.apache.ignite.transactions.TransactionIsolation; import org.apache.ignite.transactions.TransactionState; +import static org.apache.ignite.transactions.TransactionState.SUSPENDED; + /** * Cache transaction proxy. */ @@ -98,6 +100,18 @@ public GridNearTxLocal tx() { * Enters a call. */ private void enter() { + enter(false); + } + + /** + * Enters a call. + * + * @param resume Flag to indicate that resume operation in progress. + */ + private void enter(boolean resume) { + if (!resume && state() == SUSPENDED) + throw new IgniteException("Tx in SUSPENDED state. All operations except resume are prohibited."); + if (cctx.deploymentEnabled()) cctx.deploy().onEnter(); @@ -203,6 +217,21 @@ private void leave() { return tx.state(); } + /** {@inheritDoc} */ + @Override public void suspend() throws IgniteException { + enter(); + + try { + cctx.suspendTx(tx); + } + catch (IgniteCheckedException e) { + throw U.convertException(e); + } + finally { + leave(); + } + } + /** {@inheritDoc} */ @Override public long timeout(long timeout) { return tx.timeout(timeout); @@ -333,6 +362,21 @@ private void leave() { } } + /** {@inheritDoc} */ + @Override public void resume() throws IgniteException { + enter(true); + + try { + cctx.resumeTx(tx); + } + catch (IgniteCheckedException e) { + throw U.convertException(e); + } + finally { + leave(); + } + } + /** * @param res Result to convert to finished future. */ @@ -377,4 +421,4 @@ private IgniteFuture createFuture(IgniteInternalFuture fut) @Override public String toString() { return S.toString(TransactionProxyImpl.class, this); } -} \ No newline at end of file +} diff --git a/modules/core/src/main/java/org/apache/ignite/transactions/Transaction.java b/modules/core/src/main/java/org/apache/ignite/transactions/Transaction.java index 57a2b00eefa24..a1b4d78fc6444 100644 --- a/modules/core/src/main/java/org/apache/ignite/transactions/Transaction.java +++ b/modules/core/src/main/java/org/apache/ignite/transactions/Transaction.java @@ -272,4 +272,18 @@ public interface Transaction extends AutoCloseable, IgniteAsyncSupport { * @throws IgniteException If rollback failed. */ public IgniteFuture rollbackAsync() throws IgniteException; + + /** + * Resume transaction if it was previously suspended. Supported only for optimistic transactions. + * + * @throws IgniteException If resume failed. + */ + public void resume() throws IgniteException; + + /** + * Suspends transaction. It could be resumed later. Supported only for optimistic transactions. + * + * @throws IgniteException If suspension failed. + */ + public void suspend() throws IgniteException; } \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/transactions/TransactionState.java b/modules/core/src/main/java/org/apache/ignite/transactions/TransactionState.java index 19802421ba31a..d01c0fa928ee6 100644 --- a/modules/core/src/main/java/org/apache/ignite/transactions/TransactionState.java +++ b/modules/core/src/main/java/org/apache/ignite/transactions/TransactionState.java @@ -48,7 +48,10 @@ public enum TransactionState { ROLLED_BACK, /** Transaction rollback failed or is otherwise unknown state. */ - UNKNOWN; + UNKNOWN, + + /** Transaction has been suspended by user. */ + SUSPENDED; /** Enumerated values. */ private static final TransactionState[] VALS = values(); @@ -62,4 +65,4 @@ public enum TransactionState { @Nullable public static TransactionState fromOrdinal(int ord) { return ord >= 0 && ord < VALS.length ? VALS[ord] : null; } -} \ No newline at end of file +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeMultiServerTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeMultiServerTest.java new file mode 100644 index 0000000000000..a6318d45c185a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeMultiServerTest.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; + +/** + * + */ +public class IgniteOptimisticTxSuspendResumeMultiServerTest extends IgniteOptimisticTxSuspendResumeTest { + /** + * @return Number of server nodes. + */ + protected int serversNumber() { + return 4; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java new file mode 100644 index 0000000000000..d16aebd61ca9e --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java @@ -0,0 +1,751 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteException; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.NearCacheConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.util.typedef.CI1; +import org.apache.ignite.internal.util.typedef.CI2; +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.TransactionIsolation; +import org.apache.ignite.transactions.TransactionTimeoutException; + +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.transactions.TransactionConcurrency.OPTIMISTIC; +import static org.apache.ignite.transactions.TransactionState.ACTIVE; +import static org.apache.ignite.transactions.TransactionState.COMMITTED; +import static org.apache.ignite.transactions.TransactionState.MARKED_ROLLBACK; +import static org.apache.ignite.transactions.TransactionState.ROLLED_BACK; +import static org.apache.ignite.transactions.TransactionState.SUSPENDED; + +/** + * + */ +public class IgniteOptimisticTxSuspendResumeTest extends GridCommonAbstractTest { + /** Transaction timeout. */ + private static final long TX_TIMEOUT = 100; + + /** Future timeout */ + private static final int FUT_TIMEOUT = 5000; + + private boolean client = false; + + /** + * List of closures to execute transaction operation that prohibited in suspended state. + */ + private static final List> SUSPENDED_TX_PROHIBITED_OPS = Arrays.asList( + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.suspend(); + } + }, + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.close(); + } + }, + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.commit(); + } + }, + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.commitAsync().get(FUT_TIMEOUT); + } + }, + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.rollback(); + } + }, + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.rollbackAsync().get(FUT_TIMEOUT); + } + }, + new CI1Exc() { + @Override public void applyx(Transaction tx) throws Exception { + tx.setRollbackOnly(); + } + } + ); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.setClientMode(client); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + startGrids(serversNumber()); + + if (serversNumber() > 1) { + client = true; + + startGrid(serversNumber()); + + startGrid(serversNumber() + 1); + + client = false; + } + + awaitPartitionMapExchange(); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + super.afterTestsStopped(); + + stopAllGrids(true); + } + + /** + * @return Number of server nodes. + */ + protected int serversNumber() { + return 1; + } + + /** + * Test for transaction starting in one thread, continuing in another. + * + * @throws Exception If failed. + */ + public void testResumeTxInAnotherThread() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + final AtomicInteger cntr = new AtomicInteger(0); + + cache.put(-1, -1); + cache.put(cntr.get(), cntr.getAndIncrement()); + + tx.suspend(); + + assertEquals(SUSPENDED, tx.state()); + + assertNull("Thread already have tx", ignite.transactions().tx()); + + assertNull(cache.get(-1)); + assertNull(cache.get(cntr.get())); + + for (int i = 0; i < 10; i++) { + GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + assertEquals(SUSPENDED, tx.state()); + + tx.resume(); + + assertEquals(ACTIVE, tx.state()); + + cache.put(cntr.get(), cntr.getAndIncrement()); + + tx.suspend(); + } + }).get(FUT_TIMEOUT); + } + + tx.resume(); + + cache.remove(-1); + + tx.commit(); + + assertEquals(COMMITTED, tx.state()); + + for (int i = 0; i < cntr.get(); i++) + assertEquals(i, (int)cache.get(i)); + + assertFalse(cache.containsKey(-1)); + + cache.removeAll(); + } + } + }); + } + + /** + * Test for transaction starting in one thread, continuing in another, and resuming in initiating thread. + * Cache operations performed for a couple of caches. + * + * @throws Exception If failed. + */ + public void testCrossCacheTxInAnotherThread() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final IgniteCache otherCache = + ignite.getOrCreateCache(cacheConfiguration(PARTITIONED, 0, false).setName("otherCache")); + + final Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + final AtomicInteger cntr = new AtomicInteger(0); + + cache.put(-1, -1); + otherCache.put(-1, -1); + + tx.suspend(); + + for (int i = 0; i < 10; i++) { + GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + tx.resume(); + + assertEquals(ACTIVE, tx.state()); + + cache.put(cntr.get(), cntr.get()); + otherCache.put(cntr.get(), cntr.getAndIncrement()); + + tx.suspend(); + } + }).get(FUT_TIMEOUT); + } + + tx.resume(); + + cache.remove(-1); + otherCache.remove(-1); + + tx.commit(); + + assertEquals(COMMITTED, tx.state()); + + for (int i = 0; i < cntr.get(); i++) { + assertEquals(i, (int)cache.get(i)); + assertEquals(i, (int)otherCache.get(i)); + } + + assertFalse(cache.containsKey(-1)); + assertFalse(otherCache.containsKey(-1)); + + cache.removeAll(); + otherCache.removeAll(); + } + } + }); + } + + /** + * Test for transaction rollback. + * + * @throws Exception If failed. + */ + public void testTxRollback() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + cache.put(1, 1); + cache.put(2, 2); + + tx.suspend(); + + assertNull("There is no transaction for current thread", ignite.transactions().tx()); + + assertEquals(SUSPENDED, tx.state()); + + GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + tx.resume(); + + assertEquals(ACTIVE, tx.state()); + + cache.put(3, 3); + + tx.rollback(); + } + }).get(FUT_TIMEOUT); + + assertEquals(ROLLED_BACK, tx.state()); + + assertFalse(cache.containsKey(1)); + assertFalse(cache.containsKey(2)); + assertFalse(cache.containsKey(3)); + + cache.removeAll(); + } + } + }); + } + + /** + * Test for starting and suspending transactions, and then resuming and committing in another thread. + * + * @throws Exception If failed. + */ + public void testMultiTxSuspendResume() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final List clientTxs = new ArrayList<>(); + + for (int i = 0; i < 10; i++) { + Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + cache.put(i, i); + + tx.suspend(); + + clientTxs.add(tx); + } + + GridTestUtils.runMultiThreaded(new CI1Exc() { + public void applyx(Integer idx) throws Exception { + Transaction tx = clientTxs.get(idx); + + assertEquals(SUSPENDED, tx.state()); + + tx.resume(); + + assertEquals(ACTIVE, tx.state()); + + tx.commit(); + } + }, 10, "th-suspend"); + + for (int i = 0; i < 10; i++) + assertEquals(i, (int)cache.get(i)); + + cache.removeAll(); + } + } + }); + } + + /** + * Test checking all operations(exception resume) on suspended transaction from the other thread are prohibited. + * + * @throws Exception If failed. + */ + public void testOpsProhibitedOnSuspendedTxFromOtherThread() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (final CI1Exc txOperation : SUSPENDED_TX_PROHIBITED_OPS) { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + cache.put(1, 1); + + tx.suspend(); + + multithreaded(new RunnableX() { + @Override public void runx() throws Exception { + GridTestUtils.assertThrowsWithCause(txOperation, tx, IgniteException.class); + } + }, 1); + + tx.resume(); + tx.close(); + + assertNull(cache.get(1)); + } + } + } + }); + } + + /** + * Test checking all operations(exception resume) on suspended transaction are prohibited. + * + * @throws Exception If failed. + */ + public void testOpsProhibitedOnSuspendedTx() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (CI1Exc txOperation : SUSPENDED_TX_PROHIBITED_OPS) { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + cache.put(1, 1); + + tx.suspend(); + + GridTestUtils.assertThrowsWithCause(txOperation, tx, IgniteException.class); + + tx.resume(); + tx.close(); + + assertNull(cache.get(1)); + } + } + } + }); + } + + /** + * Test checking timeout on resumed transaction. + * + * @throws Exception If failed. + */ + public void testTxTimeoutOnResumed() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation, TX_TIMEOUT, 0); + + cache.put(1, 1); + + tx.suspend(); + + Thread.sleep(TX_TIMEOUT * 2); + + GridTestUtils.assertThrowsWithCause(new Callable() { + @Override public Object call() throws Exception { + tx.resume(); + + return null; + } + }, TransactionTimeoutException.class); + + assertEquals(MARKED_ROLLBACK, tx.state()); + + tx.close(); + } + } + }); + } + + /** + * Test checking timeout on suspended transaction. + * + * @throws Exception If failed. + */ + public void testTxTimeoutOnSuspend() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation, TX_TIMEOUT, 0); + + cache.put(1, 1); + + Thread.sleep(TX_TIMEOUT * 2); + + GridTestUtils.assertThrowsWithCause(new Callable() { + @Override public Object call() throws Exception { + tx.suspend(); + + return null; + } + }, TransactionTimeoutException.class); + + assertEquals(MARKED_ROLLBACK, tx.state()); + + tx.close(); + + assertNull(cache.get(1)); + } + } + }); + } + + /** + * Test start 1 transaction, suspendTx it. And then start another transaction, trying to write + * the same key and commit it. + * + * @throws Exception If failed. + */ + public void testSuspendTxAndStartNew() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation tx1Isolation : TransactionIsolation.values()) { + for (TransactionIsolation tx2Isolation : TransactionIsolation.values()) { + Transaction tx1 = ignite.transactions().txStart(OPTIMISTIC, tx1Isolation); + + cache.put(1, 1); + + tx1.suspend(); + + assertFalse(cache.containsKey(1)); + + Transaction tx2 = ignite.transactions().txStart(OPTIMISTIC, tx2Isolation); + + cache.put(1, 2); + + tx2.commit(); + + assertEquals(2, (int)cache.get(1)); + + tx1.resume(); + + assertEquals(1, (int)cache.get(1)); + + tx1.close(); + + cache.removeAll(); + } + } + } + }); + } + + /** + * Test start 1 transaction, suspendTx it. And then start another transaction, trying to write + * the same key. + * + * @throws Exception If failed. + */ + public void testSuspendTxAndStartNewWithoutCommit() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation tx1Isolation : TransactionIsolation.values()) { + for (TransactionIsolation tx2Isolation : TransactionIsolation.values()) { + Transaction tx1 = ignite.transactions().txStart(OPTIMISTIC, tx1Isolation); + + cache.put(1, 1); + + tx1.suspend(); + + assertFalse(cache.containsKey(1)); + + Transaction tx2 = ignite.transactions().txStart(OPTIMISTIC, tx2Isolation); + + cache.put(1, 2); + + tx2.suspend(); + + assertFalse(cache.containsKey(1)); + + tx1.resume(); + + assertEquals(1, (int)cache.get(1)); + + tx1.suspend(); + + tx2.resume(); + + assertEquals(2, (int)cache.get(1)); + + tx2.rollback(); + + tx1.resume(); + tx1.rollback(); + + cache.removeAll(); + } + } + } + }); + } + + /** + * Test we can resume and complete transaction if topology changed while transaction is suspended. + * + * @throws Exception If failed. + */ + public void testSuspendTxAndResumeAfterTopologyChange() throws Exception { + executeTestForAllCaches(new CI2Exc>() { + @Override public void applyx(Ignite ignite, final IgniteCache cache) throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + Transaction tx = ignite.transactions().txStart(OPTIMISTIC, isolation); + + cache.put(1, 1); + + tx.suspend(); + + assertEquals(SUSPENDED, tx.state()); + + try (IgniteEx g = startGrid(serversNumber() + 3)) { + tx.resume(); + + assertEquals(ACTIVE, tx.state()); + + assertEquals(1, (int)cache.get(1)); + + tx.commit(); + + assertEquals(1, (int)cache.get(1)); + } + + cache.removeAll(); + } + } + }); + } + + /** + * @return Cache configurations to test. + */ + private List> cacheConfigurations() { + List> cfgs = new ArrayList<>(); + + cfgs.add(cacheConfiguration(PARTITIONED, 0, false)); + cfgs.add(cacheConfiguration(PARTITIONED, 1, false)); + cfgs.add(cacheConfiguration(PARTITIONED, 1, true)); + cfgs.add(cacheConfiguration(REPLICATED, 0, false)); + + return cfgs; + } + + /** + * @param cacheMode Cache mode. + * @param backups Number of backups. + * @param nearCache If {@code true} near cache is enabled. + * @return Cache configuration. + */ + private CacheConfiguration cacheConfiguration( + CacheMode cacheMode, + int backups, + boolean nearCache) { + CacheConfiguration ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME); + + ccfg.setCacheMode(cacheMode); + ccfg.setAtomicityMode(TRANSACTIONAL); + ccfg.setWriteSynchronizationMode(FULL_SYNC); + + if (cacheMode == PARTITIONED) + ccfg.setBackups(backups); + + if (nearCache) + ccfg.setNearConfiguration(new NearCacheConfiguration()); + + return ccfg; + } + + /** + * @param c Closure. + * @throws Exception If failed. + */ + private void executeTestForAllCaches(CI2> c) throws Exception { + for (CacheConfiguration ccfg : cacheConfigurations()) { + ignite(0).createCache(ccfg); + + log.info("Run test for cache [cache=" + ccfg.getCacheMode() + + ", backups=" + ccfg.getBackups() + + ", near=" + (ccfg.getNearConfiguration() != null) + "]"); + + int srvNum = serversNumber(); + if (serversNumber() > 1) { + ignite(serversNumber() + 1).createNearCache(ccfg.getName(), new NearCacheConfiguration<>()); + srvNum += 2; + } + + try { + for (int i = 0; i < srvNum; i++) { + Ignite ignite = ignite(i); + + log.info("Run test for node [node=" + i + ", client=" + ignite.configuration().isClientMode() + ']'); + + c.apply(ignite, ignite.cache(ccfg.getName())); + } + } + finally { + ignite(0).destroyCache(ccfg.getName()); + } + } + } + + /** + * Closure with 2 parameters that can throw any exception. + * + * @param Type of first closure parameter. + * @param Type of second closure parameter. + */ + public static abstract class CI2Exc implements CI2 { + /** + * Closure body. + * + * @param e1 First closure argument. + * @param e2 Second closure argument. + * @throws Exception If failed. + */ + public abstract void applyx(E1 e1, E2 e2) throws Exception; + + /** {@inheritdoc} */ + @Override public void apply(E1 e1, E2 e2) { + try { + applyx(e1, e2); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * Closure that can throw any exception. + * + * @param Type of closure parameter. + */ + public static abstract class CI1Exc implements CI1 { + /** + * Closure body. + * + * @param o Closure argument. + * @throws Exception If failed. + */ + public abstract void applyx(T o) throws Exception; + + /** {@inheritdoc} */ + @Override public void apply(T o) { + try { + applyx(o); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * Runnable that can throw any exception. + */ + public static abstract class RunnableX implements Runnable { + /** + * Closure body. + * + * @throws Exception If failed. + */ + public abstract void runx() throws Exception; + + /** {@inheritdoc} */ + @Override public void run() { + try { + runx(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgnitePessimisticTxSuspendResumeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgnitePessimisticTxSuspendResumeTest.java new file mode 100644 index 0000000000000..57a1470010578 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgnitePessimisticTxSuspendResumeTest.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.cache.distributed; + +import java.util.concurrent.Callable; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteTransactions; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +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 static org.apache.ignite.cache.CacheMode.PARTITIONED; + +/** + * + */ +public class IgnitePessimisticTxSuspendResumeTest extends GridCommonAbstractTest { + /** + * Creates new cache configuration. + * + * @return CacheConfiguration New cache configuration. + */ + protected CacheConfiguration getCacheConfiguration() { + CacheConfiguration cacheCfg = defaultCacheConfiguration(); + + cacheCfg.setCacheMode(PARTITIONED); + + return cacheCfg; + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.setClientMode(false); + cfg.setCacheConfiguration(getCacheConfiguration()); + + return cfg; + } + + /** + * Test for suspension on pessimistic transaction. + * + * @throws Exception If failed. + */ + public void testSuspendPessimisticTx() throws Exception { + try (Ignite g = startGrid()) { + IgniteCache cache = jcache(); + + IgniteTransactions txs = g.transactions(); + + for (TransactionIsolation isolation : TransactionIsolation.values()) { + final Transaction tx = txs.txStart(TransactionConcurrency.PESSIMISTIC, isolation); + + cache.put(1, "1"); + + GridTestUtils.assertThrowsWithCause(new Callable() { + @Override public Object call() throws Exception { + tx.suspend(); + + return null; + } + }, UnsupportedOperationException.class); + + tx.close(); + + assertNull(cache.get(1)); + } + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java index cbcbaee53d47d..585c759ad5f00 100644 --- a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java @@ -425,6 +425,32 @@ public static Throwable assertThrowsAnyCause(@Nullable IgniteLogger log, Callabl throw new AssertionError("Exception has not been thrown."); } + /** + * Checks whether closure throws exception, which is itself of a specified + * class, or has a cause of the specified class. + * + * @param call Closure. + * @param p Parameter passed to closure. + * @param cls Expected class. + * @return Thrown throwable. + */ + public static

Throwable assertThrowsWithCause(IgniteInClosure

call, P p, Class cls) { + assert call != null; + assert cls != null; + + try { + call.apply(p); + } + catch (Throwable e) { + if (!X.hasCause(e, cls)) + fail("Exception is neither of a specified class, nor has a cause of the specified class: " + cls, e); + + return e; + } + + throw new AssertionError("Exception has not been thrown."); + } + /** * Throw assertion error with specified error message and initialized cause. * diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java index c5673b321fd23..f764212fada90 100644 --- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java @@ -578,6 +578,16 @@ public static class DummyTx extends GridMetadataAwareAdapter implements Transact // No-op. } + /** {@inheritDoc} */ + @Override public void suspend() { + // No-op. + } + + /** {@inheritDoc} */ + @Override public void resume() { + // No-op. + } + /** {@inheritDoc} */ @Override public IgniteFuture rollbackAsync() throws IgniteException { return null; diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java new file mode 100644 index 0000000000000..90190d064844b --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java @@ -0,0 +1,42 @@ +/* + * 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.distributed.IgniteOptimisticTxSuspendResumeMultiServerTest; +import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeTest; +import org.apache.ignite.internal.processors.cache.distributed.IgnitePessimisticTxSuspendResumeTest; + +/** + * Test suite. + */ +public class IgniteCacheTestSuite6 extends TestSuite { + /** + * @return IgniteCache test suite. + * @throws Exception Thrown in case of the failure. + */ + public static TestSuite suite() throws Exception { + TestSuite suite = new TestSuite("IgniteCache Test Suite part 6"); + + suite.addTestSuite(IgniteOptimisticTxSuspendResumeTest.class); + suite.addTestSuite(IgniteOptimisticTxSuspendResumeMultiServerTest.class); + suite.addTestSuite(IgnitePessimisticTxSuspendResumeTest.class); + + return suite; + } +} 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 5047491b9410f..dd5f6b7e628f1 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 @@ -28,10 +28,11 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.TransactionConfiguration; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; -import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; import org.apache.ignite.lifecycle.LifecycleAware; import org.jetbrains.annotations.Nullable; +import static org.apache.ignite.transactions.TransactionState.SUSPENDED; + /** * Implementation of {@link CacheJtaManagerAdapter}. */ @@ -147,7 +148,7 @@ private CacheTmLookup createTmLookup(String tmLookupClsName) throws IgniteChecke if (jtaTm != null) { CacheJtaResource rsrc = this.rsrc.get(); - if (rsrc == null || rsrc.isFinished()) { + if (rsrc == null || rsrc.isFinished() || rsrc.cacheTx().state() == SUSPENDED) { try { Transaction jtaTx = jtaTm.getTransaction(); diff --git a/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaResource.java b/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaResource.java index 649f7c41be7fd..e29c44bf34747 100644 --- a/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaResource.java +++ b/modules/jta/src/main/java/org/apache/ignite/internal/processors/cache/jta/CacheJtaResource.java @@ -71,12 +71,21 @@ final class CacheJtaResource implements XAResource, Synchronization { } /** {@inheritDoc} */ - @Override public void start(Xid xid, int flags) { + @Override public void start(Xid xid, int flags) throws XAException { if (log.isDebugEnabled()) log.debug("XA resource start(...) [xid=" + xid + ", flags=<" + flags(flags) + ">]"); // Simply save global transaction id. this.xid = xid; + + if ((flags & TMRESUME) == TMRESUME) { + try { + cacheTx.resume(); + } + catch (IgniteCheckedException e) { + throwException("Failed to resume cache transaction: " + e.getMessage(), e); + } + } } /** @@ -128,7 +137,7 @@ private void throwException(String msg, Throwable cause) throws XAException { } /** {@inheritDoc} */ - @Override public void end(Xid xid, int flags) { + @Override public void end(Xid xid, int flags) throws XAException { assert this.xid.equals(xid); if (log.isDebugEnabled()) @@ -136,6 +145,14 @@ private void throwException(String msg, Throwable cause) throws XAException { if ((flags & TMFAIL) > 0) cacheTx.setRollbackOnly(); + else if ((flags & TMSUSPEND) == TMSUSPEND) { + try { + cacheTx.suspend(); + } + catch (IgniteCheckedException e) { + throwException("Failed to suspend cache transaction: " + e.getMessage(), e); + } + } } /** {@inheritDoc} */ @@ -297,6 +314,13 @@ boolean isFinished() { return state == COMMITTED || state == ROLLED_BACK; } + /** + * @return Internal tx + */ + GridNearTxLocal cacheTx() { + return cacheTx; + } + /** {@inheritDoc} */ @Override public String toString() { return S.toString(CacheJtaResource.class, this); diff --git a/modules/jta/src/test/java/org/apache/ignite/internal/processors/cache/GridJtaTransactionManagerSelfTest.java b/modules/jta/src/test/java/org/apache/ignite/internal/processors/cache/GridJtaTransactionManagerSelfTest.java new file mode 100644 index 0000000000000..a181068e8ae1e --- /dev/null +++ b/modules/jta/src/test/java/org/apache/ignite/internal/processors/cache/GridJtaTransactionManagerSelfTest.java @@ -0,0 +1,208 @@ +/* + * 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 javax.cache.configuration.Factory; +import javax.transaction.Transaction; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.TransactionConfiguration; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.apache.ignite.transactions.TransactionConcurrency; +import org.apache.ignite.transactions.TransactionIsolation; +import org.objectweb.jotm.Current; +import org.objectweb.jotm.Jotm; +import org.objectweb.transaction.jta.TransactionManager; + +import static org.apache.ignite.cache.CacheMode.PARTITIONED; +import static org.apache.ignite.transactions.TransactionState.ACTIVE; + +/** + * JTA Tx Manager test. + */ +public class GridJtaTransactionManagerSelfTest extends GridCommonAbstractTest { + /** Java Open Transaction Manager facade. */ + private static Jotm jotm; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName). + setCacheConfiguration(defaultCacheConfiguration().setCacheMode(PARTITIONED)); + + cfg.getTransactionConfiguration().setTxManagerFactory(new Factory() { + private static final long serialVersionUID = 0L; + + @Override public TransactionManager create() { + return jotm.getTransactionManager(); + } + }); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + jotm = new Jotm(true, false); + + Current.setAppServer(false); + + startGrid(); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + super.afterTestsStopped(); + + stopAllGrids(); + + jotm.stop(); + } + + /** + * Test for switching tx context by JTA Manager. + * + * @throws Exception If failed. + */ + public void testJtaTxContextSwitch() throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + TransactionConfiguration cfg = grid().context().config().getTransactionConfiguration(); + + cfg.setDefaultTxConcurrency(TransactionConcurrency.OPTIMISTIC); + cfg.setDefaultTxIsolation(isolation); + + TransactionManager jtaTm = jotm.getTransactionManager(); + + IgniteCache cache = jcache(); + + assertNull(grid().transactions().tx()); + + jtaTm.begin(); + + Transaction tx1 = jtaTm.getTransaction(); + + cache.put(1, Integer.toString(1)); + + assertNotNull(grid().transactions().tx()); + + assertEquals(ACTIVE, grid().transactions().tx().state()); + + assertEquals(Integer.toString(1), cache.get(1)); + + jtaTm.suspend(); + + assertNull(grid().transactions().tx()); + + assertNull(cache.get(1)); + + jtaTm.begin(); + + Transaction tx2 = jtaTm.getTransaction(); + + assertNotSame(tx1, tx2); + + cache.put(2, Integer.toString(2)); + + assertNotNull(grid().transactions().tx()); + + assertEquals(ACTIVE, grid().transactions().tx().state()); + + assertEquals(Integer.toString(2), cache.get(2)); + + jtaTm.commit(); + + assertNull(grid().transactions().tx()); + + assertEquals(Integer.toString(2), cache.get(2)); + + jtaTm.resume(tx1); + + assertNotNull(grid().transactions().tx()); + + assertEquals(ACTIVE, grid().transactions().tx().state()); + + cache.put(3, Integer.toString(3)); + + jtaTm.commit(); + + assertEquals("1", cache.get(1)); + assertEquals("2", cache.get(2)); + assertEquals("3", cache.get(3)); + + assertNull(grid().transactions().tx()); + + cache.removeAll(); + } + } + + /** + * @throws Exception If failed. + */ + public void testJtaTxContextSwitchWithExistingTx() throws Exception { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + TransactionConfiguration cfg = grid().context().config().getTransactionConfiguration(); + + cfg.setDefaultTxConcurrency(TransactionConcurrency.OPTIMISTIC); + cfg.setDefaultTxIsolation(isolation); + + TransactionManager jtaTm = jotm.getTransactionManager(); + + IgniteCache cache = jcache(); + + jtaTm.begin(); + + Transaction tx1 = jtaTm.getTransaction(); + + cache.put(1, Integer.toString(1)); + + assertNotNull(grid().transactions().tx()); + + assertEquals(ACTIVE, grid().transactions().tx().state()); + + assertEquals(Integer.toString(1), cache.get(1)); + + jtaTm.suspend(); + + jtaTm.begin(); + + Transaction tx2 = jtaTm.getTransaction(); + + assertNotSame(tx1, tx2); + + cache.put(2, Integer.toString(2)); + + try { + jtaTm.resume(tx1); + + fail("jtaTm.resume shouldn't success."); + } + catch (IllegalStateException ignored) { + // No-op. + } + finally { + jtaTm.rollback(); //rolling back tx2 + } + + jtaTm.resume(tx1); + jtaTm.rollback(); + + cache.removeAll(); + } + } +} diff --git a/modules/jta/src/test/java/org/apache/ignite/testsuites/IgniteJtaTestSuite.java b/modules/jta/src/test/java/org/apache/ignite/testsuites/IgniteJtaTestSuite.java index 4ae5df06d54ec..0775c1a2fbbfd 100644 --- a/modules/jta/src/test/java/org/apache/ignite/testsuites/IgniteJtaTestSuite.java +++ b/modules/jta/src/test/java/org/apache/ignite/testsuites/IgniteJtaTestSuite.java @@ -21,13 +21,14 @@ import org.apache.ignite.internal.processors.cache.CacheJndiTmFactorySelfTest; import org.apache.ignite.internal.processors.cache.GridCacheJtaConfigurationValidationSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheJtaFactoryConfigValidationSelfTest; +import org.apache.ignite.internal.processors.cache.GridJtaLifecycleAwareSelfTest; +import org.apache.ignite.internal.processors.cache.GridJtaTransactionManagerSelfTest; import org.apache.ignite.internal.processors.cache.GridPartitionedCacheJtaFactorySelfTest; import org.apache.ignite.internal.processors.cache.GridPartitionedCacheJtaFactoryUseSyncSelfTest; import org.apache.ignite.internal.processors.cache.GridPartitionedCacheJtaLookupClassNameSelfTest; import org.apache.ignite.internal.processors.cache.GridReplicatedCacheJtaFactorySelfTest; import org.apache.ignite.internal.processors.cache.GridReplicatedCacheJtaFactoryUseSyncSelfTest; import org.apache.ignite.internal.processors.cache.GridReplicatedCacheJtaLookupClassNameSelfTest; -import org.apache.ignite.internal.processors.cache.GridJtaLifecycleAwareSelfTest; import org.apache.ignite.testframework.IgniteTestSuite; /** @@ -54,6 +55,8 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(GridCacheJtaConfigurationValidationSelfTest.class); suite.addTestSuite(GridCacheJtaFactoryConfigValidationSelfTest.class); + suite.addTestSuite(GridJtaTransactionManagerSelfTest.class); + // Factory suite.addTestSuite(CacheJndiTmFactorySelfTest.class); From 0f22223b7ca25313083e4dc35e7842931a655abd Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Fri, 4 Aug 2017 11:46:14 +0300 Subject: [PATCH 004/145] IGNITE-5126: Batch support for this JDBC driver. This closes #2162. --- .../jdbc/suite/IgniteJdbcDriverTestSuite.java | 2 + .../jdbc/thin/JdbcThinBatchSelfTest.java | 333 ++++++++++++++++++ .../jdbc/thin/JdbcThinPreparedStatement.java | 16 +- .../internal/jdbc/thin/JdbcThinStatement.java | 46 ++- .../internal/jdbc/thin/JdbcThinTcpIo.java | 20 ++ .../odbc/jdbc/JdbcBatchExecuteRequest.java | 109 ++++++ .../odbc/jdbc/JdbcBatchExecuteResult.java | 96 +++++ .../processors/odbc/jdbc/JdbcQuery.java | 95 +++++ .../processors/odbc/jdbc/JdbcRequest.java | 8 + .../odbc/jdbc/JdbcRequestHandler.java | 66 +++- .../processors/odbc/jdbc/JdbcResult.java | 11 + 11 files changed, 794 insertions(+), 8 deletions(-) create mode 100644 modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQuery.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 8ca3d45886343..cf7ee8fa54194 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 @@ -33,6 +33,7 @@ import org.apache.ignite.jdbc.JdbcResultSetSelfTest; import org.apache.ignite.jdbc.JdbcStatementSelfTest; import org.apache.ignite.jdbc.thin.JdbcThinAutoCloseServerCursorTest; +import org.apache.ignite.jdbc.thin.JdbcThinBatchSelfTest; import org.apache.ignite.jdbc.thin.JdbcThinComplexQuerySelfTest; import org.apache.ignite.jdbc.thin.JdbcThinConnectionSelfTest; import org.apache.ignite.jdbc.thin.JdbcThinDeleteStatementSelfTest; @@ -121,6 +122,7 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(JdbcThinMergeStatementSelfTest.class)); suite.addTest(new TestSuite(JdbcThinDeleteStatementSelfTest.class)); suite.addTest(new TestSuite(JdbcThinAutoCloseServerCursorTest.class)); + suite.addTest(new TestSuite(JdbcThinBatchSelfTest.class)); // New thin JDBC driver, DDL tests suite.addTest(new TestSuite(JdbcThinDynamicIndexAtomicPartitionedNearSelfTest.class)); diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java new file mode 100644 index 0000000000000..5781e0074e160 --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java @@ -0,0 +1,333 @@ +/* + * 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.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.Callable; +import org.apache.ignite.testframework.GridTestUtils; + +/** + * Statement test. + */ +public class JdbcThinBatchSelfTest extends JdbcThinAbstractDmlStatementSelfTest { + /** SQL query. */ + private static final String SQL_PREPARED = "insert into Person(_key, id, firstName, lastName, age) values " + + "(?, ?, ?, ?, ?)"; + + /** Statement. */ + private Statement stmt; + + /** Prepared statement. */ + private PreparedStatement pstmt; + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + stmt = conn.createStatement(); + + pstmt = conn.prepareStatement(SQL_PREPARED); + + assertNotNull(stmt); + assertFalse(stmt.isClosed()); + + assertNotNull(pstmt); + assertFalse(pstmt.isClosed()); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + if (stmt != null && !stmt.isClosed()) + stmt.close(); + + if (pstmt != null && !pstmt.isClosed()) + pstmt.close(); + + assertTrue(pstmt.isClosed()); + assertTrue(stmt.isClosed()); + + super.afterTest(); + } + + /** + * @throws SQLException If failed. + */ + public void testBatch() throws SQLException { + final int BATCH_SIZE = 10; + + for (int idx = 0, i = 0; i < BATCH_SIZE; ++i, idx += i) { + stmt.addBatch("insert into Person (_key, id, firstName, lastName, age) values " + + generateValues(idx, i + 1)); + } + + int [] updCnts = stmt.executeBatch(); + + assertEquals("Invalid update counts size", BATCH_SIZE, updCnts.length); + + for (int i = 0; i < BATCH_SIZE; ++i) + assertEquals("Invalid update count",i + 1, updCnts[i]); + } + + /** + * @throws SQLException If failed. + */ + public void testBatchOnClosedStatement() throws SQLException { + final Statement stmt2 = conn.createStatement(); + final PreparedStatement pstmt2 = conn.prepareStatement(""); + + stmt2.close(); + pstmt2.close(); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + stmt2.addBatch(""); + + return null; + } + }, SQLException.class, "Statement is closed."); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + stmt2.clearBatch(); + + return null; + } + }, SQLException.class, "Statement is closed."); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + stmt2.executeBatch(); + + return null; + } + }, SQLException.class, "Statement is closed."); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + pstmt2.addBatch(); + + return null; + } + }, SQLException.class, "Statement is closed."); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + pstmt2.clearBatch(); + + return null; + } + }, SQLException.class, "Statement is closed."); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + pstmt2.executeBatch(); + + return null; + } + }, SQLException.class, "Statement is closed."); + } + + /** + * @throws SQLException If failed. + */ + public void testBatchException() throws SQLException { + final int BATCH_SIZE = 7; + + for (int idx = 0, i = 0; i < BATCH_SIZE; ++i, idx += i) { + stmt.addBatch("insert into Person (_key, id, firstName, lastName, age) values " + + generateValues(idx, i + 1)); + } + + stmt.addBatch("select * from Person"); + + stmt.addBatch("insert into Person (_key, id, firstName, lastName, age) values " + + generateValues(100, 1)); + + try { + stmt.executeBatch(); + + fail("BatchUpdateException must be thrown"); + } catch(BatchUpdateException e) { + int [] updCnts = e.getUpdateCounts(); + + assertEquals("Invalid update counts size", BATCH_SIZE, updCnts.length); + + for (int i = 0; i < BATCH_SIZE; ++i) + assertEquals("Invalid update count",i + 1, updCnts[i]); + + if (!e.getMessage().contains("Query produced result set [qry=select * from Person, args=[]]")) { + log.error("Invalid exception: ", e); + + fail(); + } + } + } + + /** + * @throws SQLException If failed. + */ + public void testBatchClear() throws SQLException { + final int BATCH_SIZE = 7; + + for (int idx = 0, i = 0; i < BATCH_SIZE; ++i, idx += i) { + stmt.addBatch("insert into Person (_key, id, firstName, lastName, age) values " + + generateValues(idx, i + 1)); + } + + stmt.clearBatch(); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + stmt.executeBatch(); + + return null; + } + }, SQLException.class, "Batch is empty."); + } + + /** + * @throws SQLException If failed. + */ + public void testBatchPrepared() throws SQLException { + final int BATCH_SIZE = 10; + + for (int i = 0; i < BATCH_SIZE; ++i) { + int paramCnt = 1; + + pstmt.setString(paramCnt++, "p" + i); + pstmt.setInt(paramCnt++, i); + pstmt.setString(paramCnt++, "Name" + i); + pstmt.setString(paramCnt++, "Lastname" + i); + pstmt.setInt(paramCnt++, 20 + i); + + pstmt.addBatch(); + } + + int [] updCnts = pstmt.executeBatch(); + + assertEquals("Invalid update counts size", BATCH_SIZE, updCnts.length); + + for (int i = 0; i < BATCH_SIZE; ++i) + assertEquals("Invalid update count",1, updCnts[i]); + } + + /** + * @throws SQLException If failed. + */ + public void testBatchExceptionPrepared() throws SQLException { + final int BATCH_SIZE = 7; + + for (int i = 0; i < BATCH_SIZE; ++i) { + int paramCnt = 1; + + pstmt.setString(paramCnt++, "p" + i); + pstmt.setInt(paramCnt++, i); + pstmt.setString(paramCnt++, "Name" + i); + pstmt.setString(paramCnt++, "Lastname" + i); + pstmt.setInt(paramCnt++, 20 + i); + + pstmt.addBatch(); + } + + int paramCnt = 1; + pstmt.setString(paramCnt++, "p" + 100); + pstmt.setString(paramCnt++, "x"); + pstmt.setString(paramCnt++, "Name" + 100); + pstmt.setString(paramCnt++, "Lastname" + 100); + pstmt.setInt(paramCnt++, 20 + 100); + + pstmt.addBatch(); + + try { + pstmt.executeBatch(); + + fail("BatchUpdateException must be thrown"); + } catch(BatchUpdateException e) { + int [] updCnts = e.getUpdateCounts(); + + assertEquals("Invalid update counts size", BATCH_SIZE, updCnts.length); + + for (int i = 0; i < BATCH_SIZE; ++i) + assertEquals("Invalid update count",1, updCnts[i]); + + if (!e.getMessage().contains("Failed to execute SQL query.")) { + log.error("Invalid exception: ", e); + + fail(); + } + } + } + + /** + * @throws SQLException If failed. + */ + public void testBatchClearPrepared() throws SQLException { + final int BATCH_SIZE = 10; + + for (int i = 0; i < BATCH_SIZE; ++i) { + int paramCnt = 1; + + pstmt.setString(paramCnt++, "p" + i); + pstmt.setInt(paramCnt++, i); + pstmt.setString(paramCnt++, "Name" + i); + pstmt.setString(paramCnt++, "Lastname" + i); + pstmt.setInt(paramCnt++, 20 + i); + + pstmt.addBatch(); + } + + pstmt.clearBatch(); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + pstmt.executeBatch(); + + return null; + } + }, SQLException.class, "Batch is empty."); + } + + /** + * @param beginIndex Begin row index. + * @param cnt Count of rows. + * @return String contains values for 'cnt' rows. + */ + private String generateValues(int beginIndex, int cnt) { + StringBuilder sb = new StringBuilder(); + + int lastIdx = beginIndex + cnt - 1; + + for (int i = beginIndex; i < lastIdx; ++i) + sb.append(valuesRow(i)).append(','); + + sb.append(valuesRow(lastIdx)); + + return sb.toString(); + } + + /** + * @param idx Index of the row. + * @return String with row values. + */ + private String valuesRow(int idx) { + return String.format("('p%d', %d, 'Name%d', 'Lastname%d', %d)", idx, idx, idx, idx, 20 + idx); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java index 0c78a1365df11..455c80f78fbd7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.Calendar; import org.apache.ignite.internal.processors.odbc.SqlListenerUtils; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery; /** * JDBC prepared statement implementation. @@ -230,7 +231,20 @@ private void executeWithArguments() throws SQLException { @Override public void addBatch() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Updates are not supported."); + if (batch == null) { + batch = new ArrayList<>(); + + batch.add(new JdbcQuery(sql, args.toArray(new Object[args.size()]))); + } + else + batch.add(new JdbcQuery(null, args.toArray(new Object[args.size()]))); + + args = null; + } + + /** {@inheritDoc} */ + @Override public void addBatch(String sql) throws SQLException { + throw new SQLException("The method 'addBatch(String)' is called on PreparedStatement instance."); } /** {@inheritDoc} */ 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 2cad223365e24..b01350a0d6748 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 @@ -18,15 +18,20 @@ package org.apache.ignite.internal.jdbc.thin; import java.io.IOException; +import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLWarning; import java.sql.Statement; +import java.util.ArrayList; import java.util.List; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cache.query.SqlQuery; +import org.apache.ignite.internal.processors.odbc.SqlListenerResponse; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBatchExecuteResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteResult; import static java.sql.ResultSet.CONCUR_READ_ONLY; @@ -62,6 +67,9 @@ public class JdbcThinStatement implements Statement { /** */ private boolean alreadyRead; + /** Batch. */ + protected List batch; + /** * Creates new statement. * @@ -323,21 +331,53 @@ private JdbcThinResultSet lastResultSet() throws SQLException { @Override public void addBatch(String sql) throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Updates are not supported."); + if (batch == null) + batch = new ArrayList<>(); + + batch.add(new JdbcQuery(sql, null)); } /** {@inheritDoc} */ @Override public void clearBatch() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Updates are not supported."); + batch = null; } /** {@inheritDoc} */ @Override public int[] executeBatch() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Updates are not supported."); + if (rs != null) { + rs.close(); + + rs = null; + } + + alreadyRead = false; + + if (batch == null || batch.isEmpty()) + throw new SQLException("Batch is empty."); + + try { + JdbcBatchExecuteResult res = conn.io().batchExecute(conn.getSchema(), batch); + + if (res.errorCode() != SqlListenerResponse.STATUS_SUCCESS) + throw new BatchUpdateException(res.errorMessage(), null, res.errorCode(), res.updateCounts()); + + return res.updateCounts(); + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } + finally { + batch = null; + } } /** {@inheritDoc} */ 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 be62a8d4608aa..f54d5fdb8fda6 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 @@ -32,6 +32,9 @@ import org.apache.ignite.internal.processors.odbc.SqlListenerProtocolVersion; import org.apache.ignite.internal.processors.odbc.SqlListenerRequest; import org.apache.ignite.internal.processors.odbc.SqlListenerResponse; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBatchExecuteRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBatchExecuteResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryCloseRequest; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteRequest; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteResult; @@ -58,6 +61,9 @@ public class JdbcThinTcpIo { /** Initial output for query message. */ private static final int QUERY_EXEC_MSG_INIT_CAP = 256; + /** Maximum batch query count. */ + private static final int MAX_BATCH_QRY_CNT = 32; + /** Initial output for query fetch message. */ private static final int QUERY_FETCH_MSG_SIZE = 13; @@ -288,6 +294,20 @@ public void queryClose(long qryId) throws IOException, IgniteCheckedException { sendRequest(new JdbcQueryCloseRequest(qryId), QUERY_CLOSE_MSG_SIZE); } + /** + * @param schema Schema. + * @param batch Batch queries. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcBatchExecuteResult batchExecute(String schema, List batch) + throws IOException, IgniteCheckedException { + int cnt = Math.min(MAX_BATCH_QRY_CNT, batch.size()); + + return sendRequest(new JdbcBatchExecuteRequest(schema, batch), QUERY_EXEC_MSG_INIT_CAP * cnt); + } + /** * @param req ODBC request. * @throws IOException On error. 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 new file mode 100644 index 0000000000000..9f71bff7cc9dc --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.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.odbc.jdbc; + +import java.util.ArrayList; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.jetbrains.annotations.Nullable; + +/** + * JDBC batch execute request. + */ +public class JdbcBatchExecuteRequest extends JdbcRequest { + /** Cache name. */ + private String schema; + + /** Sql query. */ + @GridToStringInclude(sensitive = true) + private List queries; + + /** + * Default constructor. + */ + public JdbcBatchExecuteRequest() { + super(BATCH_EXEC); + } + + /** + * @param schema Schema. + * @param queries Queries. + */ + public JdbcBatchExecuteRequest(String schema, List queries) { + super(BATCH_EXEC); + + assert !F.isEmpty(queries); + + this.schema = schema; + this.queries = queries; + } + + /** + * @return Schema. + */ + @Nullable public String schema() { + return schema; + } + + /** + * @return Queries. + */ + public List queries() { + return queries; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schema); + writer.writeInt(queries.size()); + + for (JdbcQuery q : queries) + q.writeBinary(writer); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + schema = reader.readString(); + + int n = reader.readInt(); + + queries = new ArrayList<>(n); + + for (int i = 0; i < n; ++i) { + JdbcQuery qry = new JdbcQuery(); + + qry.readBinary(reader); + + queries.add(qry); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcBatchExecuteRequest.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java new file mode 100644 index 0000000000000..7977c224a782d --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.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.odbc.jdbc; + +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; + +/** + * JDBC batch execute result. + */ +public class JdbcBatchExecuteResult extends JdbcResult { + /** Update counts. */ + private int [] updateCnts; + + /** Batch update error code. */ + private int errCode; + + /** Batch update error message. */ + private String errMsg; + + /** + * Condtructor. + */ + public JdbcBatchExecuteResult() { + super(BATCH_EXEC); + } + + /** + * @param updateCnts Update counts for batch. + * @param errCode Error code. + * @param errMsg Error message. + */ + public JdbcBatchExecuteResult(int [] updateCnts, int errCode, String errMsg) { + super(BATCH_EXEC); + + this.updateCnts = updateCnts; + this.errCode = errCode; + this.errMsg = errMsg; + } + + /** + * @return Update count for DML queries. + */ + public int[] updateCounts() { + return updateCnts; + } + + /** + * @return Batch error code. + */ + public int errorCode() { + return errCode; + } + + /** + * @return Batch error message. + */ + public String errorMessage() { + return errMsg; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeInt(errCode); + writer.writeString(errMsg); + writer.writeIntArray(updateCnts); + } + + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + errCode = reader.readInt(); + errMsg = reader.readString(); + updateCnts = reader.readIntArray(); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQuery.java new file mode 100644 index 0000000000000..f7ffb994937c9 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQuery.java @@ -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. + */ + +package org.apache.ignite.internal.processors.odbc.jdbc; + +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.processors.odbc.SqlListenerUtils; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC SQL query with parameters. + */ +public class JdbcQuery implements JdbcRawBinarylizable { + /** Query SQL. */ + private String sql; + + /** Arguments. */ + private Object[] args; + + /** + * Default constructor is used for serialization. + */ + public JdbcQuery() { + // No-op. + } + + /** + * @param sql Query SQL. + * @param args Arguments. + */ + public JdbcQuery(String sql, Object[] args) { + this.sql = sql; + this.args = args; + } + + /** + * @return Query SQL string. + */ + public String sql() { + return sql; + } + + /** + * @return Query arguments. + */ + public Object[] args() { + return args; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) { + writer.writeString(sql); + + if (args == null || args.length == 0) + writer.writeInt(0); + else { + writer.writeInt(args.length); + + for (Object arg : args) + SqlListenerUtils.writeObject(writer, arg, false); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) { + sql = reader.readString(); + + int argsNum = reader.readInt(); + + args = new Object[argsNum]; + + for (int i = 0; i < argsNum; ++i) + args[i] = SqlListenerUtils.readObject(reader, false); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcQuery.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java index d6f8fd32bb376..0e144cc59d957 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java @@ -39,6 +39,9 @@ public class JdbcRequest extends SqlListenerRequest implements JdbcRawBinaryliza /** Get columns meta query. */ public static final byte QRY_META = 5; + /** Batch queries. */ + public static final byte BATCH_EXEC = 6; + /** Request type. */ private byte type; @@ -97,6 +100,11 @@ public static JdbcRequest readRequest(BinaryReaderExImpl reader) throws BinaryOb break; + case BATCH_EXEC: + req = new JdbcBatchExecuteRequest(); + + break; + default: throw new IgniteException("Unknown SQL listener request ID: [request ID=" + reqType + ']'); } 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 94ac433fcd699..60c08f9a20058 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 @@ -17,6 +17,11 @@ package org.apache.ignite.internal.processors.odbc.jdbc; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.cache.query.FieldsQueryCursor; import org.apache.ignite.cache.query.SqlFieldsQuery; @@ -31,10 +36,7 @@ import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; - +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.BATCH_EXEC; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_CLOSE; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_EXEC; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_FETCH; @@ -129,6 +131,9 @@ public JdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock, int case QRY_META: return getQueryMeta((JdbcQueryMetadataRequest)req); + + case BATCH_EXEC: + return executeBatch((JdbcBatchExecuteRequest)req); } return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, "Unsupported JDBC request [req=" + req + ']'); @@ -307,4 +312,57 @@ private JdbcResponse getQueryMeta(JdbcQueryMetadataRequest req) { return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); } } + + /** + * @param req Request. + * @return Response. + */ + private SqlListenerResponse executeBatch(JdbcBatchExecuteRequest req) { + String schemaName = req.schema(); + + if (F.isEmpty(schemaName)) + schemaName = QueryUtils.DFLT_SCHEMA; + + int successQueries = 0; + int updCnts[] = new int[req.queries().size()]; + + try { + String sql = null; + + for (JdbcQuery q : req.queries()) { + if (q.sql() != null) + sql = q.sql(); + + SqlFieldsQuery qry = new SqlFieldsQuery(sql); + + qry.setArgs(q.args()); + + qry.setDistributedJoins(distributedJoins); + qry.setEnforceJoinOrder(enforceJoinOrder); + qry.setCollocated(collocated); + qry.setReplicatedOnly(replicatedOnly); + + qry.setSchema(schemaName); + + QueryCursorImpl> qryCur = (QueryCursorImpl>)ctx.query() + .querySqlFieldsNoCache(qry, true); + + if (qryCur.isQuery()) + throw new IgniteCheckedException("Query produced result set [qry=" + q.sql() + ", args=" + + Arrays.toString(q.args()) + ']'); + + List> items = qryCur.getAll(); + + updCnts[successQueries++] = ((Long)items.get(0).get(0)).intValue(); + } + + return new JdbcResponse(new JdbcBatchExecuteResult(updCnts, SqlListenerResponse.STATUS_SUCCESS, null)); + } + catch (Exception e) { + U.error(log, "Failed to execute batch query [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(new JdbcBatchExecuteResult(Arrays.copyOf(updCnts, successQueries), + SqlListenerResponse.STATUS_FAILED, e.toString())); + } + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java index 2d7666e14f60d..48affe96602ba 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java @@ -35,6 +35,9 @@ public class JdbcResult implements JdbcRawBinarylizable { /** Get columns meta query result. */ public static final byte QRY_META = 4; + /** Batch queries. */ + public static final byte BATCH_EXEC = 6; + /** Success status. */ private byte type; @@ -70,14 +73,22 @@ public static JdbcResult readResult(BinaryReaderExImpl reader) throws BinaryObje switch(resId) { case QRY_EXEC: res = new JdbcQueryExecuteResult(); + break; case QRY_FETCH: res = new JdbcQueryFetchResult(); + break; case QRY_META: res = new JdbcQueryMetadataResult(); + + break; + + case BATCH_EXEC: + res = new JdbcBatchExecuteResult(); + break; default: From 0b3a9a7176f5ae44a96ecf700c8147193dfbf064 Mon Sep 17 00:00:00 2001 From: Igor Sapego Date: Fri, 4 Aug 2017 13:18:00 +0300 Subject: [PATCH 005/145] IGNITE-5923: ODBC: SQLGetTypeInfo now works with SQL_ALL_TYPES (cherry picked from commit 48c914d) --- modules/platforms/cpp/odbc-test/Makefile.am | 1 + .../cpp/odbc-test/include/complex_type.h | 271 +++++++++--------- .../odbc-test/project/vs/odbc-test.vcxproj | 1 + .../project/vs/odbc-test.vcxproj.filters | 3 + .../cpp/odbc-test/src/meta_queries_test.cpp | 189 ++++++++++++ modules/platforms/cpp/odbc/src/odbc.cpp | 2 +- .../cpp/odbc/src/query/type_info_query.cpp | 2 +- modules/platforms/cpp/odbc/src/statement.cpp | 2 +- 8 files changed, 332 insertions(+), 139 deletions(-) create mode 100644 modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp diff --git a/modules/platforms/cpp/odbc-test/Makefile.am b/modules/platforms/cpp/odbc-test/Makefile.am index 56ae56a397a1c..1d6546839ee51 100644 --- a/modules/platforms/cpp/odbc-test/Makefile.am +++ b/modules/platforms/cpp/odbc-test/Makefile.am @@ -61,6 +61,7 @@ ignite_odbc_tests_SOURCES = \ src/column_test.cpp \ src/configuration_test.cpp \ src/row_test.cpp \ + src/meta_queries_test.cpp \ src/utility_test.cpp \ src/queries_test.cpp \ src/test_utils.cpp \ diff --git a/modules/platforms/cpp/odbc-test/include/complex_type.h b/modules/platforms/cpp/odbc-test/include/complex_type.h index 8a1bd59575f63..ea01554914332 100644 --- a/modules/platforms/cpp/odbc-test/include/complex_type.h +++ b/modules/platforms/cpp/odbc-test/include/complex_type.h @@ -1,137 +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. - */ - -#ifndef _IGNITE_ODBC_TEST_COMPLEX_TYPE -#define _IGNITE_ODBC_TEST_COMPLEX_TYPE - -#include - -#include "ignite/ignite.h" -#include "ignite/ignition.h" - -namespace ignite -{ - struct TestObject - { - TestObject() : - f1(412), - f2("Lorem ipsum") - { - // No-op. - } - - int32_t f1; - std::string f2; - }; - - struct ComplexType - { - ComplexType() : - i32Field(0) - { - // No-op. - } - - int32_t i32Field; - TestObject objField; - std::string strField; - }; - - bool operator==(TestObject const& lhs, TestObject const& rhs) - { - return lhs.f1 == rhs.f1 && lhs.f2 == rhs.f2; - } - - bool operator==(ComplexType const& lhs, ComplexType const& rhs) - { - return lhs.i32Field == rhs.i32Field && lhs.objField == rhs.objField && lhs.strField == rhs.strField; - } - - std::ostream& operator<<(std::ostream& str, TestObject const& obj) - { - str << "TestObject::f1: " << obj.f1 - << "TestObject::f2: " << obj.f2; - return str; - } - - std::ostream& operator<<(std::ostream& str, ComplexType const& obj) +/* + * 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_TEST_COMPLEX_TYPE +#define _IGNITE_ODBC_TEST_COMPLEX_TYPE + +#include + +#include "ignite/ignite.h" + +namespace ignite +{ + struct TestObject { - str << "ComplexType::i32Field: " << obj.i32Field - << "ComplexType::objField: " << obj.objField - << "ComplexType::strField: " << obj.strField; - return str; - } -} - -namespace ignite -{ - namespace binary - { - - IGNITE_BINARY_TYPE_START(ignite::TestObject) - - typedef ignite::TestObject TestObject; - - IGNITE_BINARY_GET_TYPE_ID_AS_HASH(TestObject) - IGNITE_BINARY_GET_TYPE_NAME_AS_IS(TestObject) - IGNITE_BINARY_GET_FIELD_ID_AS_HASH - IGNITE_BINARY_IS_NULL_FALSE(TestObject) - IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(TestObject) - - static void Write(BinaryWriter& writer, const TestObject& obj) - { - writer.WriteInt32("f1", obj.f1); - writer.WriteString("f2", obj.f2); - } - - static void Read(BinaryReader& reader, TestObject& dst) - { - dst.f1 = reader.ReadInt32("f1"); - dst.f2 = reader.ReadString("f2"); - } - - IGNITE_BINARY_TYPE_END - - IGNITE_BINARY_TYPE_START(ignite::ComplexType) - - typedef ignite::ComplexType ComplexType; - - IGNITE_BINARY_GET_TYPE_ID_AS_HASH(ComplexType) - IGNITE_BINARY_GET_TYPE_NAME_AS_IS(ComplexType) - IGNITE_BINARY_GET_FIELD_ID_AS_HASH - IGNITE_BINARY_IS_NULL_FALSE(ComplexType) - IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(ComplexType) - - static void Write(BinaryWriter& writer, const ComplexType& obj) - { - writer.WriteInt32("i32Field", obj.i32Field); - writer.WriteObject("objField", obj.objField); - writer.WriteString("strField", obj.strField); - } - - static void Read(BinaryReader& reader, ComplexType& dst) - { - dst.i32Field = reader.ReadInt32("i32Field"); - dst.objField = reader.ReadObject("objField"); - dst.strField = reader.ReadString("strField"); - } - - IGNITE_BINARY_TYPE_END - } -}; - -#endif // _IGNITE_ODBC_TEST_COMPLEX_TYPE + TestObject() : + f1(412), + f2("Lorem ipsum") + { + // No-op. + } + + friend bool operator==(TestObject const& lhs, TestObject const& rhs) + { + return lhs.f1 == rhs.f1 && lhs.f2 == rhs.f2; + } + + friend std::ostream& operator<<(std::ostream& str, TestObject const& obj) + { + str << "TestObject::f1: " << obj.f1 + << "TestObject::f2: " << obj.f2; + return str; + } + + int32_t f1; + std::string f2; + }; + + struct ComplexType + { + ComplexType() : + i32Field(0) + { + // No-op. + } + + friend bool operator==(ComplexType const& lhs, ComplexType const& rhs) + { + return lhs.i32Field == rhs.i32Field && lhs.objField == rhs.objField && lhs.strField == rhs.strField; + } + + friend std::ostream& operator<<(std::ostream& str, ComplexType const& obj) + { + str << "ComplexType::i32Field: " << obj.i32Field + << "ComplexType::objField: " << obj.objField + << "ComplexType::strField: " << obj.strField; + return str; + } + + int32_t i32Field; + TestObject objField; + std::string strField; + }; +} + +namespace ignite +{ + namespace binary + { + + IGNITE_BINARY_TYPE_START(ignite::TestObject) + + typedef ignite::TestObject TestObject; + + IGNITE_BINARY_GET_TYPE_ID_AS_HASH(TestObject) + IGNITE_BINARY_GET_TYPE_NAME_AS_IS(TestObject) + IGNITE_BINARY_GET_FIELD_ID_AS_HASH + IGNITE_BINARY_IS_NULL_FALSE(TestObject) + IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(TestObject) + + static void Write(BinaryWriter& writer, const TestObject& obj) + { + writer.WriteInt32("f1", obj.f1); + writer.WriteString("f2", obj.f2); + } + + static void Read(BinaryReader& reader, TestObject& dst) + { + dst.f1 = reader.ReadInt32("f1"); + dst.f2 = reader.ReadString("f2"); + } + + IGNITE_BINARY_TYPE_END + + IGNITE_BINARY_TYPE_START(ignite::ComplexType) + + typedef ignite::ComplexType ComplexType; + + IGNITE_BINARY_GET_TYPE_ID_AS_HASH(ComplexType) + IGNITE_BINARY_GET_TYPE_NAME_AS_IS(ComplexType) + IGNITE_BINARY_GET_FIELD_ID_AS_HASH + IGNITE_BINARY_IS_NULL_FALSE(ComplexType) + IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(ComplexType) + + static void Write(BinaryWriter& writer, const ComplexType& obj) + { + writer.WriteInt32("i32Field", obj.i32Field); + writer.WriteObject("objField", obj.objField); + writer.WriteString("strField", obj.strField); + } + + static void Read(BinaryReader& reader, ComplexType& dst) + { + dst.i32Field = reader.ReadInt32("i32Field"); + dst.objField = reader.ReadObject("objField"); + dst.strField = reader.ReadString("strField"); + } + + IGNITE_BINARY_TYPE_END + } +}; + +#endif // _IGNITE_ODBC_TEST_COMPLEX_TYPE 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 c332aad51c3c9..ceecb3d785d8b 100644 --- a/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj +++ b/modules/platforms/cpp/odbc-test/project/vs/odbc-test.vcxproj @@ -168,6 +168,7 @@ + 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 65f2ebf3621a1..91c029e0a5d2d 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 @@ -121,6 +121,9 @@ Externals + + Code + diff --git a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp new file mode 100644 index 0000000000000..5b7ae593555bf --- /dev/null +++ b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp @@ -0,0 +1,189 @@ +/* + * 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 +#endif + +#include +#include + +#include +#include +#include + +#ifndef _MSC_VER +# define BOOST_TEST_DYN_LINK +#endif + +#include + +#include "ignite/ignition.h" + +#include "ignite/common/fixed_size_array.h" +#include "ignite/impl/binary/binary_utils.h" + +#include "test_type.h" +#include "complex_type.h" +#include "test_utils.h" + +using namespace ignite; +using namespace ignite::cache; +using namespace ignite::cache::query; +using namespace ignite::common; +using namespace ignite_test; +using namespace ignite::binary; +using namespace ignite::impl::binary; +using namespace ignite::impl::interop; + +using namespace boost::unit_test; + +/** + * Test setup fixture. + */ +struct MetaQueriesTestSuiteFixture +{ + /** + * Establish connection to node. + * + * @param connectStr Connection string. + */ + void Connect(const std::string& connectStr) + { + // Allocate an environment handle + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); + + BOOST_REQUIRE(env != NULL); + + // We want ODBC 3 support + SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, reinterpret_cast(SQL_OV_ODBC3), 0); + + // Allocate a connection handle + SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); + + BOOST_REQUIRE(dbc != NULL); + + // Connect string + std::vector connectStr0; + + connectStr0.reserve(connectStr.size() + 1); + std::copy(connectStr.begin(), connectStr.end(), std::back_inserter(connectStr0)); + + SQLCHAR outstr[ODBC_BUFFER_SIZE]; + SQLSMALLINT outstrlen; + + // Connecting to ODBC server. + SQLRETURN ret = SQLDriverConnect(dbc, NULL, &connectStr0[0], static_cast(connectStr0.size()), + outstr, sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE); + + if (!SQL_SUCCEEDED(ret)) + { + Ignition::Stop(grid.GetName(), true); + + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_DBC, dbc)); + } + + // Allocate a statement handle + SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); + + BOOST_REQUIRE(stmt != NULL); + } + + void Disconnect() + { + // Releasing statement handle. + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + + // Disconneting from the server. + SQLDisconnect(dbc); + + // Releasing allocated handles. + SQLFreeHandle(SQL_HANDLE_DBC, dbc); + SQLFreeHandle(SQL_HANDLE_ENV, env); + } + + static Ignite StartAdditionalNode(const char* name) + { +#ifdef IGNITE_TESTS_32 + return StartNode("queries-test-noodbc-32.xml", name); +#else + return StartNode("queries-test-noodbc.xml", name); +#endif + } + + /** + * Constructor. + */ + MetaQueriesTestSuiteFixture() : + cache1(0), + cache2(0), + env(NULL), + dbc(NULL), + stmt(NULL) + { +#ifdef IGNITE_TESTS_32 + grid = StartNode("queries-test-32.xml", "NodeMain"); +#else + grid = StartNode("queries-test.xml", "NodeMain"); +#endif + + cache1 = grid.GetCache("cache"); + cache2 = grid.GetCache("cache2"); + } + + /** + * Destructor. + */ + ~MetaQueriesTestSuiteFixture() + { + Disconnect(); + + Ignition::StopAll(true); + } + + /** Node started during the test. */ + Ignite grid; + + /** Frist cache instance. */ + Cache cache1; + + /** Second cache instance. */ + Cache cache2; + + /** ODBC Environment. */ + SQLHENV env; + + /** ODBC Connect. */ + SQLHDBC dbc; + + /** ODBC Statement. */ + SQLHSTMT stmt; +}; + +BOOST_FIXTURE_TEST_SUITE(MetaQueriesTestSuite, MetaQueriesTestSuiteFixture) + +BOOST_AUTO_TEST_CASE(TestGetTypeInfoAllTypes) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLRETURN ret = SQLGetTypeInfo(stmt, SQL_ALL_TYPES); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/modules/platforms/cpp/odbc/src/odbc.cpp b/modules/platforms/cpp/odbc/src/odbc.cpp index b450903517fd1..1862465a7d8e2 100644 --- a/modules/platforms/cpp/odbc/src/odbc.cpp +++ b/modules/platforms/cpp/odbc/src/odbc.cpp @@ -1019,7 +1019,7 @@ namespace ignite { using odbc::Statement; - LOG_MSG("SQLGetTypeInfo called"); + LOG_MSG("SQLGetTypeInfo called: [type=" << type << ']'); Statement *statement = reinterpret_cast(stmt); diff --git a/modules/platforms/cpp/odbc/src/query/type_info_query.cpp b/modules/platforms/cpp/odbc/src/query/type_info_query.cpp index 280477b4ff804..f6b399023ac54 100644 --- a/modules/platforms/cpp/odbc/src/query/type_info_query.cpp +++ b/modules/platforms/cpp/odbc/src/query/type_info_query.cpp @@ -155,7 +155,7 @@ namespace ignite columnsMeta.push_back(ColumnMeta(sch, tbl, "NUM_PREC_RADIX", IGNITE_TYPE_INT)); columnsMeta.push_back(ColumnMeta(sch, tbl, "INTERVAL_PRECISION", IGNITE_TYPE_SHORT)); - assert(IsSqlTypeSupported(sqlType)); + assert(IsSqlTypeSupported(sqlType) || sqlType == SQL_ALL_TYPES); if (sqlType == SQL_ALL_TYPES) { diff --git a/modules/platforms/cpp/odbc/src/statement.cpp b/modules/platforms/cpp/odbc/src/statement.cpp index adc7d6b964c9f..38a1e2e61ab49 100644 --- a/modules/platforms/cpp/odbc/src/statement.cpp +++ b/modules/platforms/cpp/odbc/src/statement.cpp @@ -671,7 +671,7 @@ namespace ignite SqlResult::Type Statement::InternalExecuteGetTypeInfoQuery(int16_t sqlType) { - if (!type_traits::IsSqlTypeSupported(sqlType)) + if (sqlType != SQL_ALL_TYPES && !type_traits::IsSqlTypeSupported(sqlType)) { std::stringstream builder; builder << "Data type is not supported. [typeId=" << sqlType << ']'; From 4e0385fbc0f50548f2da3407fdfdfe939b463c67 Mon Sep 17 00:00:00 2001 From: Igor Sapego Date: Fri, 4 Aug 2017 18:34:27 +0300 Subject: [PATCH 006/145] IGNITE-5939: ODBC: SQLColAttributes now works with legacy attribute codes. (cherry picked from commit 70ffa2c) --- .../cpp/odbc-test/src/meta_queries_test.cpp | 51 +++++++++++++++++++ .../cpp/odbc/src/meta/column_meta.cpp | 3 ++ 2 files changed, 54 insertions(+) diff --git a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp index 5b7ae593555bf..454a989eb0f7e 100644 --- a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp +++ b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp @@ -186,4 +186,55 @@ BOOST_AUTO_TEST_CASE(TestGetTypeInfoAllTypes) BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); } +BOOST_AUTO_TEST_CASE(TestColAttributesColumnLength) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR req[] = "select strField from TestType"; + SQLExecDirect(stmt, req, SQL_NTS); + + SQLLEN intVal; + SQLCHAR strBuf[1024]; + SQLSMALLINT strLen; + + SQLRETURN ret = SQLColAttribute(stmt, 1, SQL_COLUMN_LENGTH, strBuf, sizeof(strBuf), &strLen, &intVal); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); +} + +BOOST_AUTO_TEST_CASE(TestColAttributesColumnPresicion) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR req[] = "select strField from TestType"; + SQLExecDirect(stmt, req, SQL_NTS); + + SQLLEN intVal; + SQLCHAR strBuf[1024]; + SQLSMALLINT strLen; + + SQLRETURN ret = SQLColAttribute(stmt, 1, SQL_COLUMN_PRECISION, strBuf, sizeof(strBuf), &strLen, &intVal); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); +} + +BOOST_AUTO_TEST_CASE(TestColAttributesColumnScale) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR req[] = "select strField from TestType"; + SQLExecDirect(stmt, req, SQL_NTS); + + SQLLEN intVal; + SQLCHAR strBuf[1024]; + SQLSMALLINT strLen; + + SQLRETURN ret = SQLColAttribute(stmt, 1, SQL_COLUMN_SCALE, strBuf, sizeof(strBuf), &strLen, &intVal); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/modules/platforms/cpp/odbc/src/meta/column_meta.cpp b/modules/platforms/cpp/odbc/src/meta/column_meta.cpp index 12dbfc11f80da..97fdf807101c8 100644 --- a/modules/platforms/cpp/odbc/src/meta/column_meta.cpp +++ b/modules/platforms/cpp/odbc/src/meta/column_meta.cpp @@ -179,6 +179,7 @@ namespace ignite case SQL_DESC_LENGTH: case SQL_DESC_OCTET_LENGTH: + case SQL_COLUMN_LENGTH: { value = type_traits::BinaryTypeTransferLength(dataType); @@ -200,6 +201,7 @@ namespace ignite } case SQL_DESC_PRECISION: + case SQL_COLUMN_PRECISION: { value = type_traits::BinaryTypeColumnSize(dataType); @@ -207,6 +209,7 @@ namespace ignite } case SQL_DESC_SCALE: + case SQL_COLUMN_SCALE: { value = type_traits::BinaryTypeDecimalDigits(dataType); From b093afb8231135a4904f5fffd62f5b4c332f1d47 Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Tue, 8 Aug 2017 12:05:36 +0300 Subject: [PATCH 007/145] IGNITE-5211: Added new constructor: QueryEntity(Class keyCls, Class valCls). This closes #2371. This closes #2388. This closes #2407. --- .../org/apache/ignite/cache/QueryEntity.java | 229 +++++++ .../configuration/CacheConfiguration.java | 620 +----------------- .../cache/query/QueryEntityClassProperty.java | 116 ++++ .../query/QueryEntityIndexDescriptor.java | 121 ++++ .../query/QueryEntityTypeDescriptor.java | 219 +++++++ .../h2/sql/AbstractH2CompareQueryTest.java | 4 +- 6 files changed, 693 insertions(+), 616 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java index 955e7d2dcb036..5180100fe4c24 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java @@ -18,7 +18,9 @@ package org.apache.ignite.cache; import java.io.Serializable; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -27,10 +29,20 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import javax.cache.CacheException; +import org.apache.ignite.cache.query.annotations.QueryGroupIndex; +import org.apache.ignite.cache.query.annotations.QuerySqlField; +import org.apache.ignite.cache.query.annotations.QueryTextField; +import org.apache.ignite.internal.processors.cache.query.QueryEntityClassProperty; +import org.apache.ignite.internal.processors.cache.query.QueryEntityTypeDescriptor; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.processors.query.QueryUtils; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.jetbrains.annotations.Nullable; /** * Query entity is a description of {@link org.apache.ignite.IgniteCache cache} entry (composed of key and value) @@ -110,6 +122,16 @@ public QueryEntity(String keyType, String valType) { this.valType = valType; } + /** + * Creates a query entity with the given key and value types. + * + * @param keyCls Key type. + * @param valCls Value type. + */ + public QueryEntity(Class keyCls, Class valCls) { + this(convert(processKeyAndValueClasses(keyCls,valCls))); + } + /** * Gets key type for this query pair. * @@ -351,6 +373,213 @@ public QueryEntity addQueryField(String fullName, String type, String alias) { return this; } + /** + * @param desc Type descriptor. + * @return Type metadata. + */ + private static QueryEntity convert(QueryEntityTypeDescriptor desc) { + QueryEntity entity = new QueryEntity(); + + // Key and val types. + entity.setKeyType(desc.keyClass().getName()); + entity.setValueType(desc.valueClass().getName()); + + for (QueryEntityClassProperty prop : desc.properties().values()) + entity.addQueryField(prop.fullName(), U.box(prop.type()).getName(), prop.alias()); + + entity.setKeyFields(desc.keyProperties()); + + QueryIndex txtIdx = null; + + Collection idxs = new ArrayList<>(); + + for (Map.Entry idxEntry : desc.indexes().entrySet()) { + GridQueryIndexDescriptor idx = idxEntry.getValue(); + + if (idx.type() == QueryIndexType.FULLTEXT) { + assert txtIdx == null; + + txtIdx = new QueryIndex(); + + txtIdx.setIndexType(QueryIndexType.FULLTEXT); + + txtIdx.setFieldNames(idx.fields(), true); + txtIdx.setName(idxEntry.getKey()); + } + else { + QueryIndex sortedIdx = new QueryIndex(); + + sortedIdx.setIndexType(idx.type()); + + LinkedHashMap fields = new LinkedHashMap<>(); + + for (String f : idx.fields()) + fields.put(f, !idx.descending(f)); + + sortedIdx.setFields(fields); + + sortedIdx.setName(idxEntry.getKey()); + + idxs.add(sortedIdx); + } + } + + if (desc.valueTextIndex()) { + if (txtIdx == null) { + txtIdx = new QueryIndex(); + + txtIdx.setIndexType(QueryIndexType.FULLTEXT); + + txtIdx.setFieldNames(Arrays.asList(QueryUtils.VAL_FIELD_NAME), true); + } + else + txtIdx.getFields().put(QueryUtils.VAL_FIELD_NAME, true); + } + + if (txtIdx != null) + idxs.add(txtIdx); + + if (!F.isEmpty(idxs)) + entity.setIndexes(idxs); + + return entity; + } + + /** + * @param keyCls Key class. + * @param valCls Value class. + * @return Type descriptor. + */ + private static QueryEntityTypeDescriptor processKeyAndValueClasses( + Class keyCls, + Class valCls + ) { + QueryEntityTypeDescriptor d = new QueryEntityTypeDescriptor(); + + d.keyClass(keyCls); + d.valueClass(valCls); + + processAnnotationsInClass(true, d.keyClass(), d, null); + processAnnotationsInClass(false, d.valueClass(), d, null); + + return d; + } + + /** + * Process annotations for class. + * + * @param key If given class relates to key. + * @param cls Class. + * @param type Type descriptor. + * @param parent Parent in case of embeddable. + */ + private static void processAnnotationsInClass(boolean key, Class cls, QueryEntityTypeDescriptor type, + @Nullable QueryEntityClassProperty parent) { + if (U.isJdk(cls) || QueryUtils.isGeometryClass(cls)) { + if (parent == null && !key && QueryUtils.isSqlType(cls)) { // We have to index primitive _val. + String idxName = cls.getSimpleName() + "_" + QueryUtils.VAL_FIELD_NAME + "_idx"; + + type.addIndex(idxName, QueryUtils.isGeometryClass(cls) ? + QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); + + type.addFieldToIndex(idxName, QueryUtils.VAL_FIELD_NAME, 0, false); + } + + return; + } + + if (parent != null && parent.knowsClass(cls)) + throw new CacheException("Recursive reference found in type: " + cls.getName()); + + if (parent == null) { // Check class annotation at top level only. + QueryTextField txtAnnCls = cls.getAnnotation(QueryTextField.class); + + if (txtAnnCls != null) + type.valueTextIndex(true); + + QueryGroupIndex grpIdx = cls.getAnnotation(QueryGroupIndex.class); + + if (grpIdx != null) + type.addIndex(grpIdx.name(), QueryIndexType.SORTED); + + QueryGroupIndex.List grpIdxList = cls.getAnnotation(QueryGroupIndex.List.class); + + if (grpIdxList != null && !F.isEmpty(grpIdxList.value())) { + for (QueryGroupIndex idx : grpIdxList.value()) + type.addIndex(idx.name(), QueryIndexType.SORTED); + } + } + + for (Class c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) { + for (Field field : c.getDeclaredFields()) { + QuerySqlField sqlAnn = field.getAnnotation(QuerySqlField.class); + QueryTextField txtAnn = field.getAnnotation(QueryTextField.class); + + if (sqlAnn != null || txtAnn != null) { + QueryEntityClassProperty prop = new QueryEntityClassProperty(field); + + prop.parent(parent); + + // Add parent property before its possible nested properties so that + // resulting parent column comes before columns corresponding to those + // nested properties in the resulting table - that way nested + // properties override will happen properly (first parent, then children). + type.addProperty(prop, key, true); + + processAnnotation(key, sqlAnn, txtAnn, cls, c, field.getType(), prop, type); + } + } + } + } + + /** + * Processes annotation at field or method. + * + * @param key If given class relates to key. + * @param sqlAnn SQL annotation, can be {@code null}. + * @param txtAnn H2 text annotation, can be {@code null}. + * @param cls Entity class. + * @param curCls Current entity class. + * @param fldCls Class of field or return type for method. + * @param prop Current property. + * @param desc Class description. + */ + private static void processAnnotation(boolean key, QuerySqlField sqlAnn, QueryTextField txtAnn, + Class cls, Class curCls, Class fldCls, QueryEntityClassProperty prop, QueryEntityTypeDescriptor desc) { + if (sqlAnn != null) { + processAnnotationsInClass(key, fldCls, desc, prop); + + if (!sqlAnn.name().isEmpty()) + prop.alias(sqlAnn.name()); + + if (sqlAnn.index()) { + String idxName = curCls.getSimpleName() + "_" + prop.alias() + "_idx"; + + if (cls != curCls) + idxName = cls.getSimpleName() + "_" + idxName; + + desc.addIndex(idxName, QueryUtils.isGeometryClass(prop.type()) ? + QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); + + desc.addFieldToIndex(idxName, prop.fullName(), 0, sqlAnn.descending()); + } + + if (!F.isEmpty(sqlAnn.groups())) { + for (String group : sqlAnn.groups()) + desc.addFieldToIndex(group, prop.fullName(), 0, false); + } + + if (!F.isEmpty(sqlAnn.orderedGroups())) { + for (QuerySqlField.Group idx : sqlAnn.orderedGroups()) + desc.addFieldToIndex(idx.name(), prop.fullName(), idx.order(), idx.descending()); + } + } + + if (txtAnn != null) + desc.addFieldToTextIndex(prop.fullName()); + } + + /** {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) 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 670046f508436..2b4ec1d86e304 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 @@ -18,21 +18,10 @@ package org.apache.ignite.configuration; import java.io.Serializable; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; import javax.cache.Cache; import javax.cache.CacheException; import javax.cache.configuration.CacheEntryListenerConfiguration; @@ -53,26 +42,17 @@ import org.apache.ignite.cache.CacheWriteSynchronizationMode; import org.apache.ignite.cache.PartitionLossPolicy; import org.apache.ignite.cache.QueryEntity; -import org.apache.ignite.cache.QueryIndex; -import org.apache.ignite.cache.QueryIndexType; import org.apache.ignite.cache.affinity.AffinityFunction; import org.apache.ignite.cache.affinity.AffinityKeyMapper; import org.apache.ignite.cache.eviction.EvictionFilter; import org.apache.ignite.cache.eviction.EvictionPolicy; -import org.apache.ignite.cache.query.annotations.QueryGroupIndex; -import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.cache.query.annotations.QuerySqlFunction; -import org.apache.ignite.cache.query.annotations.QueryTextField; import org.apache.ignite.cache.store.CacheStore; import org.apache.ignite.cache.store.CacheStoreSessionListener; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.internal.binary.BinaryContext; -import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; import org.apache.ignite.internal.processors.query.QueryUtils; -import org.apache.ignite.internal.util.tostring.GridToStringExclude; -import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; -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.S; import org.apache.ignite.internal.util.typedef.internal.U; @@ -1768,14 +1748,12 @@ public CacheConfiguration setIndexedTypes(Class... indexedTypes) { Class keyCls = newIndexedTypes[i]; Class valCls = newIndexedTypes[i + 1]; - TypeDescriptor desc = processKeyAndValueClasses(keyCls, valCls); - - QueryEntity converted = convert(desc); + QueryEntity newEntity = new QueryEntity(keyCls, valCls); boolean dup = false; for (QueryEntity entity : qryEntities) { - if (F.eq(entity.findValueType(), converted.findValueType())) { + if (F.eq(entity.findValueType(), newEntity.findValueType())) { dup = true; break; @@ -1783,13 +1761,13 @@ public CacheConfiguration setIndexedTypes(Class... indexedTypes) { } if (!dup) - qryEntities.add(converted); + qryEntities.add(newEntity); // Set key configuration if needed. - String affFieldName = desc.affinityFieldName(); + String affFieldName = BinaryContext.affinityFieldName(keyCls); if (affFieldName != null) { - CacheKeyConfiguration newKeyCfg = new CacheKeyConfiguration(converted.getKeyType(), affFieldName); + CacheKeyConfiguration newKeyCfg = new CacheKeyConfiguration(newEntity.getKeyType(), affFieldName); if (F.isEmpty(keyCfg)) keyCfg = new CacheKeyConfiguration[] { newKeyCfg }; @@ -2041,82 +2019,6 @@ protected Object writeReplace() { return cfg; } - /** - * @param desc Type descriptor. - * @return Type metadata. - */ - private static QueryEntity convert(TypeDescriptor desc) { - QueryEntity entity = new QueryEntity(); - - // Key and val types. - entity.setKeyType(desc.keyClass().getName()); - entity.setValueType(desc.valueClass().getName()); - - for (ClassProperty prop : desc.props.values()) - entity.addQueryField(prop.fullName(), U.box(prop.type()).getName(), prop.alias()); - - entity.setKeyFields(desc.keyProps); - - QueryIndex txtIdx = null; - - Collection idxs = new ArrayList<>(); - - for (Map.Entry idxEntry : desc.indexes().entrySet()) { - GridQueryIndexDescriptor idx = idxEntry.getValue(); - - if (idx.type() == QueryIndexType.FULLTEXT) { - assert txtIdx == null; - - txtIdx = new QueryIndex(); - - txtIdx.setIndexType(QueryIndexType.FULLTEXT); - - txtIdx.setFieldNames(idx.fields(), true); - txtIdx.setName(idxEntry.getKey()); - } - else { - Collection grp = new ArrayList<>(); - - for (String fieldName : idx.fields()) - grp.add(idx.descending(fieldName) ? fieldName + " desc" : fieldName); - - QueryIndex sortedIdx = new QueryIndex(); - - sortedIdx.setIndexType(idx.type()); - - LinkedHashMap fields = new LinkedHashMap<>(); - - for (String f : idx.fields()) - fields.put(f, !idx.descending(f)); - - sortedIdx.setFields(fields); - - sortedIdx.setName(idxEntry.getKey()); - - idxs.add(sortedIdx); - } - } - - if (desc.valueTextIndex()) { - if (txtIdx == null) { - txtIdx = new QueryIndex(); - - txtIdx.setIndexType(QueryIndexType.FULLTEXT); - - txtIdx.setFieldNames(Arrays.asList(QueryUtils.VAL_FIELD_NAME), true); - } - else - txtIdx.getFields().put(QueryUtils.VAL_FIELD_NAME, true); - } - - if (txtIdx != null) - idxs.add(txtIdx); - - if (!F.isEmpty(idxs)) - entity.setIndexes(idxs); - - return entity; - } /** * @param cls Class. @@ -2128,141 +2030,6 @@ private static Class mask(Class cls) { return QueryUtils.isSqlType(cls) ? cls : Object.class; } - /** - * @param keyCls Key class. - * @param valCls Value class. - * @return Type descriptor. - */ - static TypeDescriptor processKeyAndValueClasses( - Class keyCls, - Class valCls - ) { - TypeDescriptor d = new TypeDescriptor(); - - d.keyClass(keyCls); - d.valueClass(valCls); - - d.affinityFieldName(BinaryContext.affinityFieldName(keyCls)); - - processAnnotationsInClass(true, d.keyCls, d, null); - processAnnotationsInClass(false, d.valCls, d, null); - - return d; - } - - /** - * Process annotations for class. - * - * @param key If given class relates to key. - * @param cls Class. - * @param type Type descriptor. - * @param parent Parent in case of embeddable. - */ - private static void processAnnotationsInClass(boolean key, Class cls, TypeDescriptor type, - @Nullable ClassProperty parent) { - if (U.isJdk(cls) || QueryUtils.isGeometryClass(cls)) { - if (parent == null && !key && QueryUtils.isSqlType(cls)) { // We have to index primitive _val. - String idxName = cls.getSimpleName() + "_" + QueryUtils.VAL_FIELD_NAME + "_idx"; - - type.addIndex(idxName, QueryUtils.isGeometryClass(cls) ? - QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); - - type.addFieldToIndex(idxName, QueryUtils.VAL_FIELD_NAME, 0, false); - } - - return; - } - - if (parent != null && parent.knowsClass(cls)) - throw new CacheException("Recursive reference found in type: " + cls.getName()); - - if (parent == null) { // Check class annotation at top level only. - QueryTextField txtAnnCls = cls.getAnnotation(QueryTextField.class); - - if (txtAnnCls != null) - type.valueTextIndex(true); - - QueryGroupIndex grpIdx = cls.getAnnotation(QueryGroupIndex.class); - - if (grpIdx != null) - type.addIndex(grpIdx.name(), QueryIndexType.SORTED); - - QueryGroupIndex.List grpIdxList = cls.getAnnotation(QueryGroupIndex.List.class); - - if (grpIdxList != null && !F.isEmpty(grpIdxList.value())) { - for (QueryGroupIndex idx : grpIdxList.value()) - type.addIndex(idx.name(), QueryIndexType.SORTED); - } - } - - for (Class c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) { - for (Field field : c.getDeclaredFields()) { - QuerySqlField sqlAnn = field.getAnnotation(QuerySqlField.class); - QueryTextField txtAnn = field.getAnnotation(QueryTextField.class); - - if (sqlAnn != null || txtAnn != null) { - ClassProperty prop = new ClassProperty(field); - - prop.parent(parent); - - // Add parent property before its possible nested properties so that - // resulting parent column comes before columns corresponding to those - // nested properties in the resulting table - that way nested - // properties override will happen properly (first parent, then children). - type.addProperty(prop, key, true); - - processAnnotation(key, sqlAnn, txtAnn, cls, c, field.getType(), prop, type); - } - } - } - } - - /** - * Processes annotation at field or method. - * - * @param key If given class relates to key. - * @param sqlAnn SQL annotation, can be {@code null}. - * @param txtAnn H2 text annotation, can be {@code null}. - * @param cls Entity class. - * @param curCls Current entity class. - * @param fldCls Class of field or return type for method. - * @param prop Current property. - * @param desc Class description. - */ - private static void processAnnotation(boolean key, QuerySqlField sqlAnn, QueryTextField txtAnn, - Class cls, Class curCls, Class fldCls, ClassProperty prop, TypeDescriptor desc) { - if (sqlAnn != null) { - processAnnotationsInClass(key, fldCls, desc, prop); - - if (!sqlAnn.name().isEmpty()) - prop.alias(sqlAnn.name()); - - if (sqlAnn.index()) { - String idxName = curCls.getSimpleName() + "_" + prop.alias() + "_idx"; - - if (cls != curCls) - idxName = cls.getSimpleName() + "_" + idxName; - - desc.addIndex(idxName, QueryUtils.isGeometryClass(prop.type()) ? - QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); - - desc.addFieldToIndex(idxName, prop.fullName(), 0, sqlAnn.descending()); - } - - if (!F.isEmpty(sqlAnn.groups())) { - for (String group : sqlAnn.groups()) - desc.addFieldToIndex(group, prop.fullName(), 0, false); - } - - if (!F.isEmpty(sqlAnn.orderedGroups())) { - for (QuerySqlField.Group idx : sqlAnn.orderedGroups()) - desc.addFieldToIndex(idx.name(), prop.fullName(), idx.order(), idx.descending()); - } - } - - if (txtAnn != null) - desc.addFieldToTextIndex(prop.fullName()); - } /** {@inheritDoc} */ @Override public CacheConfiguration setStatisticsEnabled(boolean enabled) { @@ -2375,381 +2142,4 @@ public static class IgniteAllNodesPredicate implements IgnitePredicate> fields = new LinkedHashMap<>(); - - /** */ - @GridToStringExclude - private final Map props = new LinkedHashMap<>(); - - /** */ - @GridToStringInclude - private final Set keyProps = new HashSet<>(); - - /** */ - @GridToStringInclude - private final Map indexes = new HashMap<>(); - - /** */ - private IndexDescriptor fullTextIdx; - - /** */ - private Class keyCls; - - /** */ - private Class valCls; - - /** */ - private boolean valTextIdx; - - /** Affinity field name. */ - private String affFieldName; - - /** - * @return Indexes. - */ - public Map indexes() { - return Collections.unmodifiableMap(indexes); - } - - /** - * Adds index. - * - * @param idxName Index name. - * @param type Index type. - * @param inlineSize Inline size. - * @return Index descriptor. - */ - public IndexDescriptor addIndex(String idxName, QueryIndexType type, int inlineSize) { - IndexDescriptor idx = new IndexDescriptor(type, inlineSize); - - if (indexes.put(idxName, idx) != null) - throw new CacheException("Index with name '" + idxName + "' already exists."); - - return idx; - } - - /** - * Adds index. - * - * @param idxName Index name. - * @param type Index type. - * @return Index descriptor. - */ - public IndexDescriptor addIndex(String idxName, QueryIndexType type) { - return addIndex(idxName, type, -1); - } - - /** - * Adds field to index. - * - * @param idxName Index name. - * @param field Field name. - * @param orderNum Fields order number in index. - * @param descending Sorting order. - */ - public void addFieldToIndex(String idxName, String field, int orderNum, - boolean descending) { - IndexDescriptor desc = indexes.get(idxName); - - if (desc == null) - desc = addIndex(idxName, QueryIndexType.SORTED); - - desc.addField(field, orderNum, descending); - } - - /** - * Adds field to text index. - * - * @param field Field name. - */ - public void addFieldToTextIndex(String field) { - if (fullTextIdx == null) { - fullTextIdx = new IndexDescriptor(QueryIndexType.FULLTEXT); - - indexes.put(null, fullTextIdx); - } - - fullTextIdx.addField(field, 0, false); - } - - /** - * @return Value class. - */ - public Class valueClass() { - return valCls; - } - - /** - * Sets value class. - * - * @param valCls Value class. - */ - void valueClass(Class valCls) { - this.valCls = valCls; - } - - /** - * @return Key class. - */ - public Class keyClass() { - return keyCls; - } - - /** - * Set key class. - * - * @param keyCls Key class. - */ - void keyClass(Class keyCls) { - this.keyCls = keyCls; - } - - /** - * @return Affinity field name. - */ - @Nullable public String affinityFieldName() { - return affFieldName; - } - - /** - * @param affFieldName Affinity field name. - */ - private void affinityFieldName(@Nullable String affFieldName) { - this.affFieldName = affFieldName; - } - - /** - * Adds property to the type descriptor. - * - * @param prop Property. - * @param key Property ownership flag (key or not). - * @param failOnDuplicate Fail on duplicate flag. - */ - void addProperty(ClassProperty prop, boolean key, boolean failOnDuplicate) { - String name = prop.fullName(); - - if (props.put(name, prop) != null && failOnDuplicate) - throw new CacheException("Property with name '" + name + "' already exists."); - - fields.put(name, prop.type()); - - if (key) - keyProps.add(name); - } - - /** - * @return {@code true} If we need to have a fulltext index on value. - */ - public boolean valueTextIndex() { - return valTextIdx; - } - - /** - * Sets if this value should be text indexed. - * - * @param valTextIdx Flag value. - */ - public void valueTextIndex(boolean valTextIdx) { - this.valTextIdx = valTextIdx; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(TypeDescriptor.class, this); - } - } - - /** - * Index descriptor. - */ - private static class IndexDescriptor implements GridQueryIndexDescriptor { - /** Fields sorted by order number. */ - private final Collection> fields = new TreeSet<>( - new Comparator>() { - @Override public int compare(T2 o1, T2 o2) { - if (o1.get2().equals(o2.get2())) // Order is equal, compare field names to avoid replace in Set. - return o1.get1().compareTo(o2.get1()); - - return o1.get2() < o2.get2() ? -1 : 1; - } - }); - - /** Fields which should be indexed in descending order. */ - private Collection descendings; - - /** */ - private final QueryIndexType type; - - /** */ - private final int inlineSize; - - /** - * @param type Type. - * @param inlineSize Inline size. - */ - private IndexDescriptor(QueryIndexType type, int inlineSize) { - assert type != null; - - this.type = type; - this.inlineSize = inlineSize; - } - - /** - * @param type Type. - */ - private IndexDescriptor(QueryIndexType type) { - this(type, -1); - } - - /** {@inheritDoc} */ - @Override public String name() { - return null; - } - - /** {@inheritDoc} */ - @Override public Collection fields() { - Collection res = new ArrayList<>(fields.size()); - - for (T2 t : fields) - res.add(t.get1()); - - return res; - } - - /** {@inheritDoc} */ - @Override public boolean descending(String field) { - return descendings != null && descendings.contains(field); - } - - /** - * Adds field to this index. - * - * @param field Field name. - * @param orderNum Field order number in this index. - * @param descending Sort order. - */ - public void addField(String field, int orderNum, boolean descending) { - fields.add(new T2<>(field, orderNum)); - - if (descending) { - if (descendings == null) - descendings = new HashSet<>(); - - descendings.add(field); - } - } - - /** {@inheritDoc} */ - @Override public QueryIndexType type() { - return type; - } - - /** {@inheritDoc} */ - @Override public int inlineSize() { - return inlineSize; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(IndexDescriptor.class, this); - } - } - - /** - * Description of type property. - */ - private static class ClassProperty { - /** */ - private final Member member; - - /** */ - private ClassProperty parent; - - /** */ - private String name; - - /** */ - private String alias; - - /** - * Constructor. - * - * @param member Element. - */ - ClassProperty(Member member) { - this.member = member; - - name = member.getName(); - - if (member instanceof Method) { - if (member.getName().startsWith("get") && member.getName().length() > 3) - name = member.getName().substring(3); - - if (member.getName().startsWith("is") && member.getName().length() > 2) - name = member.getName().substring(2); - } - - ((AccessibleObject)member).setAccessible(true); - } - - /** - * @param alias Alias. - */ - public void alias(String alias) { - this.alias = alias; - } - - /** - * @return Alias. - */ - String alias() { - return F.isEmpty(alias) ? name : alias; - } - - /** - * @return Type. - */ - public Class type() { - return member instanceof Field ? ((Field)member).getType() : ((Method)member).getReturnType(); - } - - /** - * @param parent Parent property if this is embeddable element. - */ - public void parent(ClassProperty parent) { - this.parent = parent; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(ClassProperty.class, this); - } - - /** - * @param cls Class. - * @return {@code true} If this property or some parent relates to member of the given class. - */ - public boolean knowsClass(Class cls) { - return member.getDeclaringClass() == cls || (parent != null && parent.knowsClass(cls)); - } - - /** - * @return Full name with all parents in dot notation. - */ - public String fullName() { - assert name != null; - - if (parent == null) - return name; - - return parent.fullName() + '.' + name; - } - } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java new file mode 100644 index 0000000000000..e21b73c0e479a --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.cache.query; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Description of type property. + */ +public class QueryEntityClassProperty { + /** */ + private final Member member; + + /** */ + private QueryEntityClassProperty parent; + + /** */ + private String name; + + /** */ + private String alias; + + /** + * Constructor. + * + * @param member Element. + */ + public QueryEntityClassProperty(Member member) { + this.member = member; + + name = member.getName(); + + if (member instanceof Method) { + if (member.getName().startsWith("get") && member.getName().length() > 3) + name = member.getName().substring(3); + + if (member.getName().startsWith("is") && member.getName().length() > 2) + name = member.getName().substring(2); + } + + ((AccessibleObject)member).setAccessible(true); + } + + /** + * @param alias Alias. + */ + public void alias(String alias) { + this.alias = alias; + } + + /** + * @return Alias. + */ + public String alias() { + return F.isEmpty(alias) ? name : alias; + } + + /** + * @return Type. + */ + public Class type() { + return member instanceof Field ? ((Field)member).getType() : ((Method)member).getReturnType(); + } + + /** + * @param parent Parent property if this is embeddable element. + */ + public void parent(QueryEntityClassProperty parent) { + this.parent = parent; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(QueryEntityClassProperty.class, this); + } + + /** + * @param cls Class. + * @return {@code true} If this property or some parent relates to member of the given class. + */ + public boolean knowsClass(Class cls) { + return member.getDeclaringClass() == cls || (parent != null && parent.knowsClass(cls)); + } + + /** + * @return Full name with all parents in dot notation. + */ + public String fullName() { + assert name != null; + + if (parent == null) + return name; + + return parent.fullName() + '.' + name; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java new file mode 100644 index 0000000000000..2fb583788ac92 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.cache.query; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.TreeSet; +import org.apache.ignite.cache.QueryIndexType; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Index descriptor. + */ +public class QueryEntityIndexDescriptor implements GridQueryIndexDescriptor { + /** Fields sorted by order number. */ + private final Collection> fields = new TreeSet<>( + new Comparator>() { + @Override public int compare(T2 o1, T2 o2) { + if (o1.get2().equals(o2.get2())) // Order is equal, compare field names to avoid replace in Set. + return o1.get1().compareTo(o2.get1()); + + return o1.get2() < o2.get2() ? -1 : 1; + } + }); + /** */ + private final QueryIndexType type; + /** */ + private final int inlineSize; + /** Fields which should be indexed in descending order. */ + private Collection descendings; + + /** + * @param type Type. + * @param inlineSize Inline size. + */ + QueryEntityIndexDescriptor(QueryIndexType type, int inlineSize) { + assert type != null; + + this.type = type; + this.inlineSize = inlineSize; + } + + /** + * @param type Type. + */ + QueryEntityIndexDescriptor(QueryIndexType type) { + this(type, -1); + } + + /** {@inheritDoc} */ + @Override public String name() { + return null; + } + + /** {@inheritDoc} */ + @Override public Collection fields() { + Collection res = new ArrayList<>(fields.size()); + + for (T2 t : fields) + res.add(t.get1()); + + return res; + } + + /** {@inheritDoc} */ + @Override public boolean descending(String field) { + return descendings != null && descendings.contains(field); + } + + /** + * Adds field to this index. + * + * @param field Field name. + * @param orderNum Field order number in this index. + * @param descending Sort order. + */ + public void addField(String field, int orderNum, boolean descending) { + fields.add(new T2<>(field, orderNum)); + + if (descending) { + if (descendings == null) + descendings = new HashSet<>(); + + descendings.add(field); + } + } + + /** {@inheritDoc} */ + @Override public QueryIndexType type() { + return type; + } + + /** {@inheritDoc} */ + @Override public int inlineSize() { + return inlineSize; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(QueryEntityIndexDescriptor.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java new file mode 100644 index 0000000000000..47ab263eb216a --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.cache.query; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import javax.cache.CacheException; +import org.apache.ignite.cache.QueryIndexType; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +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; + +/** + * Descriptor of type. + */ +public class QueryEntityTypeDescriptor { + /** Value field names and types with preserved order. */ + @GridToStringInclude + private final Map> fields = new LinkedHashMap<>(); + + /** */ + @GridToStringExclude + private final Map props = new LinkedHashMap<>(); + + /** */ + @GridToStringInclude + private final Set keyProps = new HashSet<>(); + + /** */ + @GridToStringInclude + private final Map indexes = new HashMap<>(); + + /** */ + private QueryEntityIndexDescriptor fullTextIdx; + + /** */ + private Class keyCls; + + /** */ + private Class valCls; + + /** */ + private boolean valTextIdx; + + /** + * @return Indexes. + */ + public Map indexes() { + return Collections.unmodifiableMap(indexes); + } + + /** + * Adds index. + * + * @param idxName Index name. + * @param type Index type. + * @param inlineSize Inline size. + * @return Index descriptor. + */ + public QueryEntityIndexDescriptor addIndex(String idxName, QueryIndexType type, int inlineSize) { + QueryEntityIndexDescriptor idx = new QueryEntityIndexDescriptor(type, inlineSize); + + if (indexes.put(idxName, idx) != null) + throw new CacheException("Index with name '" + idxName + "' already exists."); + + return idx; + } + + /** + * Adds index. + * + * @param idxName Index name. + * @param type Index type. + * @return Index descriptor. + */ + public QueryEntityIndexDescriptor addIndex(String idxName, QueryIndexType type) { + return addIndex(idxName, type, -1); + } + + /** + * Adds field to index. + * + * @param idxName Index name. + * @param field Field name. + * @param orderNum Fields order number in index. + * @param descending Sorting order. + */ + public void addFieldToIndex(String idxName, String field, int orderNum, + boolean descending) { + QueryEntityIndexDescriptor desc = indexes.get(idxName); + + if (desc == null) + desc = addIndex(idxName, QueryIndexType.SORTED); + + desc.addField(field, orderNum, descending); + } + + /** + * Adds field to text index. + * + * @param field Field name. + */ + public void addFieldToTextIndex(String field) { + if (fullTextIdx == null) { + fullTextIdx = new QueryEntityIndexDescriptor(QueryIndexType.FULLTEXT); + + indexes.put(null, fullTextIdx); + } + + fullTextIdx.addField(field, 0, false); + } + + /** + * @return Value class. + */ + public Class valueClass() { + return valCls; + } + + /** + * Sets value class. + * + * @param valCls Value class. + */ + public void valueClass(Class valCls) { + this.valCls = valCls; + } + + /** + * @return Key class. + */ + public Class keyClass() { + return keyCls; + } + + /** + * Set key class. + * + * @param keyCls Key class. + */ + public void keyClass(Class keyCls) { + this.keyCls = keyCls; + } + + /** + * Adds property to the type descriptor. + * + * @param prop Property. + * @param key Property ownership flag (key or not). + * @param failOnDuplicate Fail on duplicate flag. + */ + public void addProperty(QueryEntityClassProperty prop, boolean key, boolean failOnDuplicate) { + String name = prop.fullName(); + + if (props.put(name, prop) != null && failOnDuplicate) + throw new CacheException("Property with name '" + name + "' already exists."); + + fields.put(name, prop.type()); + + if (key) + keyProps.add(name); + } + + /** + * @return Class properties. + */ + public Map properties() { + return props; + } + + /** + * @return Properties keys. + */ + public Set keyProperties() { + return keyProps; + } + + /** + * @return {@code true} If we need to have a fulltext index on value. + */ + public boolean valueTextIndex() { + return valTextIdx; + } + + /** + * Sets if this value should be text indexed. + * + * @param valTextIdx Flag value. + */ + public void valueTextIndex(boolean valTextIdx) { + this.valTextIdx = valTextIdx; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(QueryEntityTypeDescriptor.class, this); + } +} + diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java index 547531156fc95..1ddd252bb8120 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java @@ -25,6 +25,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -34,6 +35,7 @@ import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.CacheWriteSynchronizationMode; +import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; @@ -94,7 +96,7 @@ protected CacheConfiguration cacheConfiguration(String name, CacheMode mode, Cla cc.setCacheMode(mode); cc.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); cc.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); - cc.setIndexedTypes(clsK, clsV); + cc.setQueryEntities(Collections.singleton(new QueryEntity(clsK, clsV))); return cc; } From 879f19106b22e66d5f6ea94424d961d049397410 Mon Sep 17 00:00:00 2001 From: devozerov Date: Tue, 8 Aug 2017 15:16:58 +0300 Subject: [PATCH 008/145] IGNITE-5982: GridMapQueryExecutor was split into several pieces. --- .../h2/twostep/GridMapQueryExecutor.java | 501 ++---------------- .../query/h2/twostep/MapNodeResults.java | 108 ++++ .../query/h2/twostep/MapQueryResult.java | 258 +++++++++ .../query/h2/twostep/MapQueryResults.java | 155 ++++++ .../h2/twostep/MapReplicatedReservation.java | 38 ++ .../query/h2/twostep/MapRequestKey.java | 65 +++ .../query/h2/twostep/MapReservationKey.java | 73 +++ 7 files changed, 730 insertions(+), 468 deletions(-) create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.java create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReplicatedReservation.java create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReservationKey.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 fcf5f10959e89..19b628b07a719 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 @@ -17,7 +17,6 @@ package org.apache.ignite.internal.processors.query.h2.twostep; -import java.lang.reflect.Field; import java.sql.Connection; import java.sql.ResultSet; import java.util.AbstractCollection; @@ -31,7 +30,6 @@ import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicReferenceArray; import javax.cache.CacheException; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; @@ -39,7 +37,6 @@ import org.apache.ignite.cache.query.QueryCancelledException; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.events.CacheQueryExecutedEvent; -import org.apache.ignite.events.CacheQueryReadEvent; import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.events.Event; import org.apache.ignite.events.EventType; @@ -57,35 +54,29 @@ 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; -import org.apache.ignite.internal.processors.query.GridQueryCancel; 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.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.opt.GridH2Table; -import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject; 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; import org.apache.ignite.internal.processors.query.h2.twostep.messages.GridQueryNextPageResponse; import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest; -import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap; 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.T2; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.Message; import org.h2.jdbc.JdbcResultSet; -import org.h2.result.ResultInterface; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_EXECUTED; -import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_OBJECT_READ; import static org.apache.ignite.internal.managers.communication.GridIoPolicy.QUERY_POOL; import static org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion.NONE; import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING; @@ -94,29 +85,12 @@ import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.MAP; import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.REPLICATED; import static org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2ValueMessageFactory.toMessages; -import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q; /** * Map query executor. */ +@SuppressWarnings("ForLoopReplaceableByForEach") public class GridMapQueryExecutor { - /** */ - private static final Field RESULT_FIELD; - - /* - * Initialize. - */ - static { - try { - RESULT_FIELD = JdbcResultSet.class.getDeclaredField("result"); - - RESULT_FIELD.setAccessible(true); - } - catch (NoSuchFieldException e) { - throw new IllegalStateException("Check H2 version in classpath.", e); - } - } - /** */ private IgniteLogger log; @@ -127,14 +101,13 @@ public class GridMapQueryExecutor { private IgniteH2Indexing h2; /** */ - private ConcurrentMap qryRess = new ConcurrentHashMap8<>(); + private ConcurrentMap qryRess = new ConcurrentHashMap8<>(); /** */ private final GridSpinBusyLock busyLock; /** */ - private final ConcurrentMap, GridReservable> reservations = - new ConcurrentHashMap8<>(); + private final ConcurrentMap reservations = new ConcurrentHashMap8<>(); /** * @param busyLock Busy lock. @@ -162,7 +135,7 @@ public void start(final GridKernalContext ctx, IgniteH2Indexing h2) throws Ignit GridH2QueryContext.clearAfterDeadNode(locNodeId, nodeId); - NodeResults nodeRess = qryRess.remove(nodeId); + MapNodeResults nodeRess = qryRess.remove(nodeId); if (nodeRess == null) return; @@ -172,6 +145,7 @@ public void start(final GridKernalContext ctx, IgniteH2Indexing h2) throws Ignit }, EventType.EVT_NODE_FAILED, EventType.EVT_NODE_LEFT); ctx.io().addMessageListener(GridTopic.TOPIC_QUERY, new GridMessageListener() { + @SuppressWarnings("deprecation") @Override public void onMessage(UUID nodeId, Object msg, byte plc) { if (!busyLock.enterBusy()) return; @@ -228,7 +202,7 @@ else if (msg instanceof GridQueryCancelRequest) private void onCancel(ClusterNode node, GridQueryCancelRequest msg) { long qryReqId = msg.queryRequestId(); - NodeResults nodeRess = resultsForNode(node.id()); + MapNodeResults nodeRess = resultsForNode(node.id()); boolean clear = GridH2QueryContext.clear(ctx.localNodeId(), node.id(), qryReqId, MAP); @@ -245,13 +219,13 @@ private void onCancel(ClusterNode node, GridQueryCancelRequest msg) { * @param nodeId Node ID. * @return Results for node. */ - private NodeResults resultsForNode(UUID nodeId) { - NodeResults nodeRess = qryRess.get(nodeId); + private MapNodeResults resultsForNode(UUID nodeId) { + MapNodeResults nodeRess = qryRess.get(nodeId); if (nodeRess == null) { - nodeRess = new NodeResults(); + nodeRess = new MapNodeResults(); - NodeResults old = qryRess.putIfAbsent(nodeId, nodeRess); + MapNodeResults old = qryRess.putIfAbsent(nodeId, nodeRess); if (old != null) nodeRess = old; @@ -300,13 +274,12 @@ private boolean reservePartitions( continue; // For replicated cache topology version does not make sense. - final T2 grpKey = - new T2<>(cctx.name(), cctx.isReplicated() ? null : topVer); + final MapReservationKey grpKey = new MapReservationKey(cctx.name(), cctx.isReplicated() ? null : topVer); GridReservable r = reservations.get(grpKey); if (explicitParts == null && r != null) { // Try to reserve group partition if any and no explicits. - if (r != ReplicatedReservation.INSTANCE) { + if (r != MapReplicatedReservation.INSTANCE) { if (!r.reserve()) return false; // We need explicit partitions here -> retry. @@ -327,7 +300,7 @@ private boolean reservePartitions( } // Mark that we checked this replicated cache. - reservations.putIfAbsent(grpKey, ReplicatedReservation.INSTANCE); + reservations.putIfAbsent(grpKey, MapReplicatedReservation.INSTANCE); } } else { // Reserve primary partitions for partitioned cache (if no explicit given). @@ -381,6 +354,7 @@ private static Collection wrap(final int[] ints) { return Collections.emptySet(); return new AbstractCollection() { + @SuppressWarnings("NullableProblems") @Override public Iterator iterator() { return new Iterator() { /** */ @@ -537,9 +511,9 @@ private void onQueryRequest0( GridCacheContext mainCctx = !F.isEmpty(cacheIds) ? ctx.cache().context().cacheContext(cacheIds.get(0)) : null; - NodeResults nodeRess = resultsForNode(node.id()); + MapNodeResults nodeRess = resultsForNode(node.id()); - QueryResults qr = null; + MapQueryResults qr = null; List reserved = new ArrayList<>(); @@ -553,7 +527,7 @@ private void onQueryRequest0( } } - qr = new QueryResults(reqId, qrys.size(), mainCctx != null ? mainCctx.name() : null); + qr = new MapQueryResults(h2, reqId, qrys.size(), mainCctx != null ? mainCctx.name() : null); if (nodeRess.put(reqId, segmentId, qr) != null) throw new IllegalStateException(); @@ -619,7 +593,7 @@ private void onQueryRequest0( rs = h2.executeSqlQueryWithTimer(conn, qry.query(), F.asList(qry.parameters(params)), true, timeout, - qr.cancels[qryIdx]); + qr.queryCancel(qryIdx)); if (evt) { assert mainCctx != null; @@ -644,7 +618,7 @@ private void onQueryRequest0( qr.addResult(qryIdx, qry, node.id(), rs, params); - if (qr.canceled) { + if (qr.cancelled()) { qr.result(qryIdx).close(); throw new QueryCancelledException(); @@ -724,7 +698,7 @@ private void sendError(ClusterNode node, long qryReqId, Throwable err) { * @param req Request. */ private void onNextPageRequest(ClusterNode node, GridQueryNextPageRequest req) { - NodeResults nodeRess = qryRess.get(node.id()); + MapNodeResults nodeRess = qryRess.get(node.id()); if (nodeRess == null) { sendError(node, req.queryRequestId(), new CacheException("No node result found for request: " + req)); @@ -736,11 +710,11 @@ private void onNextPageRequest(ClusterNode node, GridQueryNextPageRequest req) { return; } - QueryResults qr = nodeRess.get(req.queryRequestId(), req.segmentId()); + MapQueryResults qr = nodeRess.get(req.queryRequestId(), req.segmentId()); if (qr == null) sendError(node, req.queryRequestId(), new CacheException("No query result found for request: " + req)); - else if (qr.canceled) + else if (qr.cancelled()) sendError(node, req.queryRequestId(), new QueryCancelledException()); else sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize()); @@ -754,16 +728,16 @@ else if (qr.canceled) * @param segmentId Index segment ID. * @param pageSize Page size. */ - private void sendNextPage(NodeResults nodeRess, ClusterNode node, QueryResults qr, int qry, int segmentId, + private void sendNextPage(MapNodeResults nodeRess, ClusterNode node, MapQueryResults qr, int qry, int segmentId, int pageSize) { - QueryResult res = qr.result(qry); + MapQueryResult res = qr.result(qry); assert res != null; - if (res.closed) + if (res.closed()) return; - int page = res.page; + int page = res.page(); List rows = new ArrayList<>(Math.min(64, pageSize)); @@ -773,16 +747,16 @@ private void sendNextPage(NodeResults nodeRess, ClusterNode node, QueryResults q res.close(); if (qr.isAllClosed()) - nodeRess.remove(qr.qryReqId, segmentId, qr); + nodeRess.remove(qr.queryRequestId(), segmentId, qr); } try { boolean loc = node.isLocal(); - GridQueryNextPageResponse msg = new GridQueryNextPageResponse(qr.qryReqId, segmentId, qry, page, - page == 0 ? res.rowCnt : -1, - res.cols, - loc ? null : toMessages(rows, new ArrayList(res.cols)), + GridQueryNextPageResponse msg = new GridQueryNextPageResponse(qr.queryRequestId(), segmentId, qry, page, + page == 0 ? res.rowCount() : -1, + res.columnCount(), + loc ? null : toMessages(rows, new ArrayList(res.columnCount())), loc ? rows : null); if (loc) @@ -828,418 +802,9 @@ private void sendRetry(ClusterNode node, long reqId, int segmentId) { */ public void onCacheStop(String cacheName) { // Drop group reservations. - for (T2 grpKey : reservations.keySet()) { - if (F.eq(grpKey.get1(), cacheName)) + for (MapReservationKey grpKey : reservations.keySet()) { + if (F.eq(grpKey.cacheName(), cacheName)) reservations.remove(grpKey); } } - - - /** - * - */ - private static class NodeResults { - /** */ - private final ConcurrentMap res = new ConcurrentHashMap8<>(); - - /** */ - private final GridBoundedConcurrentLinkedHashMap qryHist = - new GridBoundedConcurrentLinkedHashMap<>(1024, 1024, 0.75f, 64, PER_SEGMENT_Q); - - /** - * @param reqId Query Request ID. - * @return {@code False} if query was already cancelled. - */ - boolean cancelled(long reqId) { - return qryHist.get(reqId) != null; - } - - /** - * @param reqId Query Request ID. - * @return {@code True} if cancelled. - */ - boolean onCancel(long reqId) { - Boolean old = qryHist.putIfAbsent(reqId, Boolean.FALSE); - - return old == null; - } - - /** - * @param reqId Query Request ID. - * @param segmentId Index segment ID. - * @return query partial results. - */ - public QueryResults get(long reqId, int segmentId) { - return res.get(new RequestKey(reqId, segmentId)); - } - - /** - * Cancel all thread of given request. - * @param reqID Request ID. - */ - public void cancelRequest(long reqID) { - for (RequestKey key : res.keySet()) { - if (key.reqId == reqID) { - QueryResults removed = res.remove(key); - - if (removed != null) - removed.cancel(true); - } - - } - } - - /** - * @param reqId Query Request ID. - * @param segmentId Index segment ID. - * @param qr Query Results. - * @return {@code True} if removed. - */ - public boolean remove(long reqId, int segmentId, QueryResults qr) { - return res.remove(new RequestKey(reqId, segmentId), qr); - } - - /** - * @param reqId Query Request ID. - * @param segmentId Index segment ID. - * @param qr Query Results. - * @return previous value. - */ - public QueryResults put(long reqId, int segmentId, QueryResults qr) { - return res.put(new RequestKey(reqId, segmentId), qr); - } - - /** - * Cancel all node queries. - */ - public void cancelAll() { - for (QueryResults ress : res.values()) - ress.cancel(true); - } - - /** - * - */ - private static class RequestKey { - /** */ - private long reqId; - - /** */ - private int segmentId; - - /** Constructor */ - RequestKey(long reqId, int segmentId) { - this.reqId = reqId; - this.segmentId = segmentId; - } - - /** {@inheritDoc} */ - @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - RequestKey other = (RequestKey)o; - - return reqId == other.reqId && segmentId == other.segmentId; - - } - - /** {@inheritDoc} */ - @Override public int hashCode() { - int result = (int)(reqId ^ (reqId >>> 32)); - result = 31 * result + segmentId; - return result; - } - } - } - - /** - * - */ - private class QueryResults { - /** */ - private final long qryReqId; - - /** */ - private final AtomicReferenceArray results; - - /** */ - private final GridQueryCancel[] cancels; - - /** */ - private final String cacheName; - - /** */ - private volatile boolean canceled; - - /** - * @param qryReqId Query request ID. - * @param qrys Number of queries. - * @param cacheName Cache name. - */ - @SuppressWarnings("unchecked") - private QueryResults(long qryReqId, int qrys, @Nullable String cacheName) { - this.qryReqId = qryReqId; - this.cacheName = cacheName; - - results = new AtomicReferenceArray<>(qrys); - cancels = new GridQueryCancel[qrys]; - - for (int i = 0; i < cancels.length; i++) - cancels[i] = new GridQueryCancel(); - } - - /** - * @param qry Query result index. - * @return Query result. - */ - QueryResult result(int qry) { - return results.get(qry); - } - - /** - * @param qry Query result index. - * @param q Query object. - * @param qrySrcNodeId Query source node. - * @param rs Result set. - */ - void addResult(int qry, GridCacheSqlQuery q, UUID qrySrcNodeId, ResultSet rs, Object[] params) { - if (!results.compareAndSet(qry, null, new QueryResult(rs, ctx, cacheName, qrySrcNodeId, q, params))) - throw new IllegalStateException(); - } - - /** - * @return {@code true} If all results are closed. - */ - boolean isAllClosed() { - for (int i = 0; i < results.length(); i++) { - QueryResult res = results.get(i); - - if (res == null || !res.closed) - return false; - } - - return true; - } - - /** - * Cancels the query. - */ - void cancel(boolean forceQryCancel) { - if (canceled) - return; - - canceled = true; - - for (int i = 0; i < results.length(); i++) { - QueryResult res = results.get(i); - - if (res != null) { - res.close(); - - continue; - } - - if (forceQryCancel) { - GridQueryCancel cancel = cancels[i]; - - if (cancel != null) - cancel.cancel(); - } - } - } - } - - /** - * Result for a single part of the query. - */ - private class QueryResult implements AutoCloseable { - /** */ - private final ResultInterface res; - - /** */ - private final ResultSet rs; - - /** Kernal context. */ - private final GridKernalContext ctx; - - /** */ - private final String cacheName; - - /** */ - private final GridCacheSqlQuery qry; - - /** */ - private final UUID qrySrcNodeId; - - /** */ - private final int cols; - - /** */ - private int page; - - /** */ - private final int rowCnt; - - /** */ - private boolean cpNeeded; - - /** */ - private volatile boolean closed; - - /** */ - private final Object[] params; - - /** - * @param rs Result set. - * @param ctx Kernal context. - * @param cacheName Cache name. - * @param qrySrcNodeId Query source node. - * @param qry Query. - * @param params Query params. - */ - private QueryResult(ResultSet rs, GridKernalContext ctx, @Nullable String cacheName, - UUID qrySrcNodeId, GridCacheSqlQuery qry, Object[] params) { - this.ctx = ctx; - this.cacheName = cacheName; - this.qry = qry; - this.params = params; - this.qrySrcNodeId = qrySrcNodeId; - this.cpNeeded = F.eq(ctx.localNodeId(), qrySrcNodeId); - - if (rs != null) { - this.rs = rs; - try { - res = (ResultInterface)RESULT_FIELD.get(rs); - } - catch (IllegalAccessException e) { - throw new IllegalStateException(e); // Must not happen. - } - - rowCnt = res.getRowCount(); - cols = res.getVisibleColumnCount(); - } - else { - this.rs = null; - this.res = null; - this.cols = -1; - this.rowCnt = -1; - - closed = true; - } - } - - /** - * @param rows Collection to fetch into. - * @param pageSize Page size. - * @return {@code true} If there are no more rows available. - */ - synchronized boolean fetchNextPage(List rows, int pageSize) { - if (closed) - return true; - - boolean readEvt = cacheName != null && ctx.event().isRecordable(EVT_CACHE_QUERY_OBJECT_READ); - - page++; - - for (int i = 0 ; i < pageSize; i++) { - if (!res.next()) - return true; - - Value[] row = res.currentRow(); - - if (cpNeeded) { - boolean copied = false; - - for (int j = 0; j < row.length; j++) { - Value val = row[j]; - - if (val instanceof GridH2ValueCacheObject) { - GridH2ValueCacheObject valCacheObj = (GridH2ValueCacheObject)val; - - row[j] = new GridH2ValueCacheObject(valCacheObj.getCacheObject(), h2.objectContext()) { - @Override public Object getObject() { - return getObject(true); - } - }; - - copied = true; - } - } - - if (i == 0 && !copied) - cpNeeded = false; // No copy on read caches, skip next checks. - } - - assert row != null; - - if (readEvt) { - ctx.event().record(new CacheQueryReadEvent<>( - ctx.discovery().localNode(), - "SQL fields query result set row read.", - EVT_CACHE_QUERY_OBJECT_READ, - CacheQueryType.SQL.name(), - cacheName, - null, - qry.query(), - null, - null, - params, - qrySrcNodeId, - null, - null, - null, - null, - row(row))); - } - - rows.add(res.currentRow()); - } - - return false; - } - - /** - * @param row Values array row. - * @return Objects list row. - */ - private List row(Value[] row) { - List res = new ArrayList<>(row.length); - - for (Value v : row) - res.add(v.getObject()); - - return res; - } - - /** {@inheritDoc} */ - @Override public synchronized void close() { - if (closed) - return; - - closed = true; - - U.close(rs, log); - } - } - - /** - * Fake reservation object for replicated caches. - */ - private static class ReplicatedReservation implements GridReservable { - /** */ - static final ReplicatedReservation INSTANCE = new ReplicatedReservation(); - - /** {@inheritDoc} */ - @Override public boolean reserve() { - throw new IllegalStateException(); - } - - /** {@inheritDoc} */ - @Override public void release() { - throw new IllegalStateException(); - } - } } \ No newline at end of file diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java new file mode 100644 index 0000000000000..d5ea357a5232c --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java @@ -0,0 +1,108 @@ +/* + * 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; + +import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap; +import org.jsr166.ConcurrentHashMap8; + +import java.util.concurrent.ConcurrentMap; + +import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q; + +/** + * Mapper node results. + */ +class MapNodeResults { + /** */ + private final ConcurrentMap res = new ConcurrentHashMap8<>(); + + /** */ + private final GridBoundedConcurrentLinkedHashMap qryHist = + new GridBoundedConcurrentLinkedHashMap<>(1024, 1024, 0.75f, 64, PER_SEGMENT_Q); + + /** + * @param reqId Query Request ID. + * @return {@code False} if query was already cancelled. + */ + boolean cancelled(long reqId) { + return qryHist.get(reqId) != null; + } + + /** + * @param reqId Query Request ID. + * @return {@code True} if cancelled. + */ + boolean onCancel(long reqId) { + Boolean old = qryHist.putIfAbsent(reqId, Boolean.FALSE); + + return old == null; + } + + /** + * @param reqId Query Request ID. + * @param segmentId Index segment ID. + * @return query partial results. + */ + public MapQueryResults get(long reqId, int segmentId) { + return res.get(new MapRequestKey(reqId, segmentId)); + } + + /** + * Cancel all thread of given request. + * @param reqId Request ID. + */ + public void cancelRequest(long reqId) { + for (MapRequestKey key : res.keySet()) { + if (key.requestId() == reqId) { + MapQueryResults removed = res.remove(key); + + if (removed != null) + removed.cancel(true); + } + } + } + + /** + * @param reqId Query Request ID. + * @param segmentId Index segment ID. + * @param qr Query Results. + * @return {@code True} if removed. + */ + public boolean remove(long reqId, int segmentId, MapQueryResults qr) { + return res.remove(new MapRequestKey(reqId, segmentId), qr); + } + + /** + * @param reqId Query Request ID. + * @param segmentId Index segment ID. + * @param qr Query Results. + * @return previous value. + */ + public MapQueryResults put(long reqId, int segmentId, MapQueryResults qr) { + return res.put(new MapRequestKey(reqId, segmentId), qr); + } + + /** + * Cancel all node queries. + */ + public void cancelAll() { + for (MapQueryResults ress : res.values()) + ress.cancel(true); + } + +} diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java new file mode 100644 index 0000000000000..4799e03c05b94 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java @@ -0,0 +1,258 @@ +/* + * 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; + +import org.apache.ignite.events.CacheQueryReadEvent; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.processors.cache.query.CacheQueryType; +import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery; +import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; +import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.h2.jdbc.JdbcResultSet; +import org.h2.result.ResultInterface; +import org.h2.value.Value; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_OBJECT_READ; + +/** + * Mapper result for a single part of the query. + */ +class MapQueryResult implements AutoCloseable { + /** */ + private static final Field RESULT_FIELD; + + /* + * Initialize. + */ + static { + try { + RESULT_FIELD = JdbcResultSet.class.getDeclaredField("result"); + + RESULT_FIELD.setAccessible(true); + } + catch (NoSuchFieldException e) { + throw new IllegalStateException("Check H2 version in classpath.", e); + } + } + + /** Indexing. */ + private final IgniteH2Indexing h2; + + /** */ + private final ResultInterface res; + + /** */ + private final ResultSet rs; + + /** */ + private final String cacheName; + + /** */ + private final GridCacheSqlQuery qry; + + /** */ + private final UUID qrySrcNodeId; + + /** */ + private final int cols; + + /** */ + private int page; + + /** */ + private final int rowCnt; + + /** */ + private boolean cpNeeded; + + /** */ + private volatile boolean closed; + + /** */ + private final Object[] params; + + /** + * @param rs Result set. + * @param cacheName Cache name. + * @param qrySrcNodeId Query source node. + * @param qry Query. + * @param params Query params. + */ + MapQueryResult(IgniteH2Indexing h2, ResultSet rs, @Nullable String cacheName, + UUID qrySrcNodeId, GridCacheSqlQuery qry, Object[] params) { + this.h2 = h2; + this.cacheName = cacheName; + this.qry = qry; + this.params = params; + this.qrySrcNodeId = qrySrcNodeId; + this.cpNeeded = F.eq(h2.kernalContext().localNodeId(), qrySrcNodeId); + + if (rs != null) { + this.rs = rs; + try { + res = (ResultInterface)RESULT_FIELD.get(rs); + } + catch (IllegalAccessException e) { + throw new IllegalStateException(e); // Must not happen. + } + + rowCnt = res.getRowCount(); + cols = res.getVisibleColumnCount(); + } + else { + this.rs = null; + this.res = null; + this.cols = -1; + this.rowCnt = -1; + + closed = true; + } + } + + /** + * @return Page number. + */ + int page() { + return page; + } + + /** + * @return Row count. + */ + int rowCount() { + return rowCnt; + } + + /** + * @return Column ocunt. + */ + int columnCount() { + return cols; + } + + /** + * @return Closed flag. + */ + boolean closed() { + return closed; + } + + /** + * @param rows Collection to fetch into. + * @param pageSize Page size. + * @return {@code true} If there are no more rows available. + */ + synchronized boolean fetchNextPage(List rows, int pageSize) { + if (closed) + return true; + + boolean readEvt = cacheName != null && h2.kernalContext().event().isRecordable(EVT_CACHE_QUERY_OBJECT_READ); + + page++; + + for (int i = 0 ; i < pageSize; i++) { + if (!res.next()) + return true; + + Value[] row = res.currentRow(); + + if (cpNeeded) { + boolean copied = false; + + for (int j = 0; j < row.length; j++) { + Value val = row[j]; + + if (val instanceof GridH2ValueCacheObject) { + GridH2ValueCacheObject valCacheObj = (GridH2ValueCacheObject)val; + + row[j] = new GridH2ValueCacheObject(valCacheObj.getCacheObject(), h2.objectContext()) { + @Override public Object getObject() { + return getObject(true); + } + }; + + copied = true; + } + } + + if (i == 0 && !copied) + cpNeeded = false; // No copy on read caches, skip next checks. + } + + assert row != null; + + if (readEvt) { + GridKernalContext ctx = h2.kernalContext(); + + ctx.event().record(new CacheQueryReadEvent<>( + ctx.discovery().localNode(), + "SQL fields query result set row read.", + EVT_CACHE_QUERY_OBJECT_READ, + CacheQueryType.SQL.name(), + cacheName, + null, + qry.query(), + null, + null, + params, + qrySrcNodeId, + null, + null, + null, + null, + row(row))); + } + + rows.add(res.currentRow()); + } + + return false; + } + + /** + * @param row Values array row. + * @return Objects list row. + */ + private List row(Value[] row) { + List res = new ArrayList<>(row.length); + + for (Value v : row) + res.add(v.getObject()); + + return res; + } + + /** {@inheritDoc} */ + @Override public synchronized void close() { + if (closed) + return; + + closed = true; + + U.closeQuiet(rs); + } +} 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 new file mode 100644 index 0000000000000..7ad1d14fa9fc7 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResults.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.query.h2.twostep; + +import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery; +import org.apache.ignite.internal.processors.query.GridQueryCancel; +import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; +import org.jetbrains.annotations.Nullable; + +import java.sql.ResultSet; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * Mapper query results. + */ +class MapQueryResults { + /** H@ indexing. */ + private final IgniteH2Indexing h2; + + /** */ + private final long qryReqId; + + /** */ + private final AtomicReferenceArray results; + + /** */ + private final GridQueryCancel[] cancels; + + /** */ + private final String cacheName; + + /** */ + private volatile boolean cancelled; + + /** + * @param qryReqId Query request ID. + * @param qrys Number of queries. + * @param cacheName Cache name. + */ + @SuppressWarnings("unchecked") + MapQueryResults(IgniteH2Indexing h2, long qryReqId, int qrys, + @Nullable String cacheName) { + this.h2 = h2; + this.qryReqId = qryReqId; + this.cacheName = cacheName; + + results = new AtomicReferenceArray<>(qrys); + cancels = new GridQueryCancel[qrys]; + + for (int i = 0; i < cancels.length; i++) + cancels[i] = new GridQueryCancel(); + } + + /** + * @param qry Query result index. + * @return Query result. + */ + MapQueryResult result(int qry) { + return results.get(qry); + } + + /** + * Get cancel token for query. + * + * @param qryIdx Query index. + * @return Cancel token. + */ + GridQueryCancel queryCancel(int qryIdx) { + return cancels[qryIdx]; + } + + /** + * @param qry Query result index. + * @param q Query object. + * @param qrySrcNodeId Query source node. + * @param rs Result set. + */ + void addResult(int qry, GridCacheSqlQuery q, UUID qrySrcNodeId, ResultSet rs, Object[] params) { + MapQueryResult res = new MapQueryResult(h2, rs, cacheName, qrySrcNodeId, q, params); + + if (!results.compareAndSet(qry, null, res)) + throw new IllegalStateException(); + } + + /** + * @return {@code true} If all results are closed. + */ + boolean isAllClosed() { + for (int i = 0; i < results.length(); i++) { + MapQueryResult res = results.get(i); + + if (res == null || !res.closed()) + return false; + } + + return true; + } + + /** + * Cancels the query. + */ + void cancel(boolean forceQryCancel) { + if (cancelled) + return; + + cancelled = true; + + for (int i = 0; i < results.length(); i++) { + MapQueryResult res = results.get(i); + + if (res != null) { + res.close(); + + continue; + } + + if (forceQryCancel) { + GridQueryCancel cancel = cancels[i]; + + if (cancel != null) + cancel.cancel(); + } + } + } + + /** + * @return Cancel flag. + */ + boolean cancelled() { + return cancelled; + } + + /** + * @return Query request ID. + */ + long queryRequestId() { + return qryReqId; + } +} diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReplicatedReservation.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReplicatedReservation.java new file mode 100644 index 0000000000000..dd8237b6f946d --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReplicatedReservation.java @@ -0,0 +1,38 @@ +/* + * 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; + +import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservable; + +/** + * Mapper fake reservation object for replicated caches. + */ +class MapReplicatedReservation implements GridReservable { + /** */ + static final MapReplicatedReservation INSTANCE = new MapReplicatedReservation(); + + /** {@inheritDoc} */ + @Override public boolean reserve() { + throw new IllegalStateException(); + } + + /** {@inheritDoc} */ + @Override public void release() { + throw new IllegalStateException(); + } +} diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java new file mode 100644 index 0000000000000..6feb8ea68da8c --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.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.query.h2.twostep; + +/** + * Mapper request key. + */ +class MapRequestKey { + /** */ + private long reqId; + + /** */ + private int segmentId; + + /** Constructor */ + MapRequestKey(long reqId, int segmentId) { + this.reqId = reqId; + this.segmentId = segmentId; + } + + /** + * @return Request ID. + */ + public long requestId() { + return reqId; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + MapRequestKey other = (MapRequestKey)o; + + return reqId == other.reqId && segmentId == other.segmentId; + + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int res = (int)(reqId ^ (reqId >>> 32)); + + res = 31 * res + segmentId; + + return res; + } +} diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReservationKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReservationKey.java new file mode 100644 index 0000000000000..9d2d7ba696849 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapReservationKey.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.query.h2.twostep; + +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.util.typedef.F; + +/** + * Mapper reservation key. + */ +public class MapReservationKey { + /** Cache name. */ + private final String cacheName; + + /** Topology version. */ + private final AffinityTopologyVersion topVer; + + /** + * Constructor. + * + * @param cacheName Cache name. + * @param topVer Topology version. + */ + public MapReservationKey(String cacheName, AffinityTopologyVersion topVer) { + this.cacheName = cacheName; + this.topVer = topVer; + } + + /** + * @return Cache name. + */ + public String cacheName() { + return cacheName; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + MapReservationKey other = (MapReservationKey)o; + + return F.eq(cacheName, other.cacheName) && F.eq(topVer, other.topVer); + + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int res = cacheName != null ? cacheName.hashCode() : 0; + + res = 31 * res + (topVer != null ? topVer.hashCode() : 0); + + return res; + } +} From 7c77b869bc3efdf19e53cc2b064f4290fd73e2b2 Mon Sep 17 00:00:00 2001 From: devozerov Date: Wed, 9 Aug 2017 11:47:58 +0300 Subject: [PATCH 009/145] IGNITE-5993: Removed unused SQL-related classes and methods (old tree index, snapshots, etc). This closes #2414. --- .../query/h2/opt/GridH2SpatialIndex.java | 7 - .../processors/query/h2/H2RowDescriptor.java | 11 - .../processors/query/h2/H2TableEngine.java | 4 +- .../query/h2/database/H2PkHashIndex.java | 7 - .../query/h2/database/H2TreeIndex.java | 26 - .../query/h2/opt/GridH2IndexBase.java | 93 +-- .../query/h2/opt/GridH2QueryContext.java | 59 -- .../query/h2/opt/GridH2RowDescriptor.java | 5 - .../processors/query/h2/opt/GridH2Table.java | 186 +----- .../query/h2/opt/GridH2TreeIndex.java | 602 ------------------ .../h2/twostep/GridMapQueryExecutor.java | 25 - .../query/h2/opt/GridH2TableSelfTest.java | 172 ----- 12 files changed, 7 insertions(+), 1190 deletions(-) delete mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.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 9389290db9280..d83e860def35c 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 @@ -31,7 +31,6 @@ import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.query.h2.H2Cursor; import org.apache.ignite.internal.util.GridCursorIteratorWrapper; -import org.apache.ignite.internal.util.IgniteTree; import org.apache.ignite.internal.util.lang.GridCursor; import org.h2.engine.Session; import org.h2.index.Cursor; @@ -51,7 +50,6 @@ import org.h2.table.TableFilter; import org.h2.value.Value; import org.h2.value.ValueGeometry; -import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL; @@ -157,11 +155,6 @@ private void checkClosed() { return segments.length; } - /** {@inheritDoc} */ - @Nullable @Override protected IgniteTree doTakeSnapshot() { - return null; // TODO We do not support snapshots, but probably this is possible. - } - /** {@inheritDoc} */ @Override public GridH2Row put(GridH2Row row) { assert row instanceof GridH2AbstractKeyValueRow : "requires key to be at 0"; diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java index dab83d187181e..31f0e69a25a9b 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java @@ -106,9 +106,6 @@ public class H2RowDescriptor implements GridH2RowDescriptor { /** */ private final GridUnsafeGuard guard; - /** */ - private final boolean snapshotableIdx; - /** */ private final GridQueryProperty[] props; @@ -170,9 +167,6 @@ public class H2RowDescriptor implements GridH2RowDescriptor { valueAliasColumnId = (type.valueFieldName() != null) ? DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.valueFieldAlias()) : -1; - - // Index is not snapshotable in db-x. - snapshotableIdx = false; } /** {@inheritDoc} */ @@ -381,11 +375,6 @@ public class H2RowDescriptor implements GridH2RowDescriptor { return rowCache().get(link); } - /** {@inheritDoc} */ - @Override public boolean snapshotableIndex() { - return snapshotableIdx; - } - /** {@inheritDoc} */ @Override public boolean isKeyColumn(int columnId) { assert columnId >= 0; diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java index 57b7ba0bdae96..6bdcc30331d65 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java @@ -56,8 +56,8 @@ public class H2TableEngine implements TableEngine { * @throws SQLException If failed. * @return Created table. */ - public static synchronized GridH2Table createTable(Connection conn, String sql, - @Nullable GridH2RowDescriptor rowDesc, H2RowFactory rowFactory, H2TableDescriptor tblDesc) + public static synchronized GridH2Table createTable(Connection conn, String sql, GridH2RowDescriptor rowDesc, + H2RowFactory rowFactory, H2TableDescriptor tblDesc) throws SQLException { rowDesc0 = rowDesc; rowFactory0 = rowFactory; 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 2ae5868a97afb..1937a4b8e8cd2 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 @@ -30,7 +30,6 @@ import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase; import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row; 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.lang.IgniteBiPredicate; import org.apache.ignite.spi.indexing.IndexingQueryFilter; @@ -44,7 +43,6 @@ import org.h2.table.Column; import org.h2.table.IndexColumn; import org.h2.table.TableFilter; -import org.jetbrains.annotations.Nullable; /** * @@ -199,11 +197,6 @@ public H2PkHashIndex( throw new UnsupportedOperationException(); } - /** {@inheritDoc} */ - @Nullable @Override protected IgniteTree doTakeSnapshot() { - throw new AssertionError("This method must not be called for PK index"); - } - /** * Cursor. */ 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 e02510f053a96..eb579c34c1a44 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 @@ -214,25 +214,6 @@ private List getAvailableInlineColumns(IndexColumn[] cols) { } } - /** {@inheritDoc} */ - @Override public boolean putx(GridH2Row row) { - try { - InlineIndexHelper.setCurrentInlineIndexes(inlineIdxs); - - int seg = segmentForRow(row); - - H2Tree tree = treeForRead(seg); - - return tree.putx(row); - } - catch (IgniteCheckedException e) { - throw DbException.convert(e); - } - finally { - InlineIndexHelper.clearCurrentInlineIndexes(); - } - } - /** {@inheritDoc} */ @Override public GridH2Row remove(SearchRow row) { try { @@ -343,13 +324,6 @@ private List getAvailableInlineColumns(IndexColumn[] cols) { } } - /** {@inheritDoc} */ - @Nullable @Override protected IgniteTree doTakeSnapshot() { - int seg = threadLocalSegment(); - - return treeForRead(seg); - } - /** {@inheritDoc} */ @Override protected H2Tree treeForRead(int segment) { return segments[segment]; 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 542adf06e72f7..5d5fb56341d66 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 @@ -22,7 +22,6 @@ import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import javax.cache.CacheException; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteInterruptedException; @@ -35,7 +34,6 @@ 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.distributed.dht.GridReservable; import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeRequest; import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2IndexRangeResponse; import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2RowMessage; @@ -92,15 +90,6 @@ public abstract class GridH2IndexBase extends BaseIndex { /** */ private static final Object EXPLICIT_NULL = new Object(); - /** */ - private static final AtomicLong idxIdGen = new AtomicLong(); - - /** */ - protected final long idxId = idxIdGen.incrementAndGet(); - - /** */ - private final ThreadLocal snapshot = new ThreadLocal<>(); - /** */ private Object msgTopic; @@ -197,16 +186,6 @@ protected int threadLocalSegment() { */ public abstract GridH2Row put(GridH2Row row); - /** - * Puts row. - * - * @param row Row. - * @return {@code True} if replaced existing row. - */ - public boolean putx(GridH2Row row) { - return put(row) != null; - } - /** * Remove row from index. * @@ -232,32 +211,6 @@ public void removex(SearchRow row) { */ public abstract GridH2Row findOne(GridH2Row row); - /** - * Takes or sets existing snapshot to be used in current thread. - * - * @param s Optional existing snapshot to use. - * @param qctx Query context. - * @return Snapshot. - */ - public final Object takeSnapshot(@Nullable Object s, GridH2QueryContext qctx) { - assert snapshot.get() == null; - - if (s == null) - s = doTakeSnapshot(); - - if (s != null) { - if (s instanceof GridReservable && !((GridReservable)s).reserve()) - return null; - - snapshot.set(s); - - if (qctx != null) - qctx.putSnapshot(idxId, s); - } - - return s; - } - /** * @param ses Session. */ @@ -302,38 +255,6 @@ public final int getDistributedMultiplier(Session ses, TableFilter[] filters, in return (GridH2Table)super.getTable(); } - /** - * Takes and returns actual snapshot or {@code null} if snapshots are not supported. - * - * @return Snapshot or {@code null}. - */ - @Nullable protected abstract IgniteTree doTakeSnapshot(); - - /** - * @return Thread local snapshot. - */ - @SuppressWarnings("unchecked") - protected T threadLocalSnapshot() { - return (T)snapshot.get(); - } - - /** - * Releases snapshot for current thread. - */ - public void releaseSnapshot() { - Object s = snapshot.get(); - - assert s != null; - - snapshot.remove(); - - if (s instanceof GridReservable) - ((GridReservable)s).release(); - - if (s instanceof AutoCloseable) - U.closeQuiet((AutoCloseable)s); - } - /** * Filters rows from expired ones and using predicate. * @@ -498,11 +419,9 @@ private void onIndexRangeRequest(final ClusterNode node, final GridH2IndexRangeR if (msg.bounds() != null) { // This is the first request containing all the search rows. - IgniteTree snapshotTree = qctx.getSnapshot(idxId); - assert !msg.bounds().isEmpty() : "empty bounds"; - src = new RangeSource(msg.bounds(), msg.segment(), snapshotTree, qctx.filter()); + src = new RangeSource(msg.bounds(), msg.segment(), qctx.filter()); } else { // This is request to fetch next portion of data. @@ -619,7 +538,7 @@ private List broadcastSegments(GridH2QueryContext qctx, GridCacheCon if (isLocalQry) { if (partMap != null && !partMap.containsKey(cctx.localNodeId())) - return Collections.emptyList(); // Prevent remote index call for local queries. + return Collections.emptyList(); // Prevent remote index call for local queries. nodes = Collections.singletonList(cctx.localNode()); } @@ -1542,9 +1461,6 @@ private class RangeSource { /** */ int curRangeId = -1; - /** */ - final IgniteTree tree; - /** */ private final int segment; @@ -1556,18 +1472,15 @@ private class RangeSource { /** * @param bounds Bounds. - * @param tree Snapshot. * @param filter Filter. */ RangeSource( Iterable bounds, int segment, - IgniteTree tree, IndexingQueryFilter filter ) { this.segment = segment; this.filter = filter; - this.tree = tree; boundsIter = bounds.iterator(); } @@ -1623,7 +1536,7 @@ public GridH2RowRange next(int maxRows) { SearchRow first = toSearchRow(bounds.first()); SearchRow last = toSearchRow(bounds.last()); - IgniteTree t = tree != null ? tree : treeForRead(segment); + IgniteTree t = treeForRead(segment); iter = new CursorIteratorWrapper(doFind0(t, first, true, last, filter)); 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 a7ee0dc398641..2b4e1802587e6 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 @@ -25,7 +25,6 @@ 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.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.spi.indexing.IndexingQueryFilter; @@ -51,10 +50,6 @@ public class GridH2QueryContext { /** */ private volatile boolean cleared; - /** Index snapshots. */ - @GridToStringInclude - private Map snapshots; - /** */ private List reservations; @@ -247,51 +242,6 @@ public int segment() { return key.segmentId; } - /** - * @param idxId Index ID. - * @param snapshot Index snapshot. - */ - public void putSnapshot(long idxId, Object snapshot) { - assert snapshot != null; - assert get() == null : "need to snapshot indexes before setting query context for correct visibility"; - - if (snapshot instanceof GridReservable && !((GridReservable)snapshot).reserve()) - throw new IllegalStateException("Must be already reserved before."); - - if (snapshots == null) - snapshots = new HashMap<>(); - - if (snapshots.put(idxId, snapshot) != null) - throw new IllegalStateException("Index already snapshoted."); - } - - /** - * Clear taken snapshots. - */ - public void clearSnapshots() { - if (F.isEmpty(snapshots)) - return; - - for (Object snapshot : snapshots.values()) { - if (snapshot instanceof GridReservable) - ((GridReservable)snapshot).release(); - } - - snapshots = null; - } - - /** - * @param idxId Index ID. - * @return Index snapshot or {@code null} if none. - */ - @SuppressWarnings("unchecked") - public T getSnapshot(long idxId) { - if (snapshots == null) - return null; - - return (T)snapshots.get(idxId); - } - /** * @param batchLookupId Batch lookup ID. * @param streams Range streams. @@ -362,13 +312,6 @@ public int nextBatchLookupId() { return ++batchLookupIdGen; } - /** - * @return If indexes were snapshotted before query execution. - */ - public boolean hasIndexSnapshots() { - return snapshots != null; - } - /** * Sets current thread local context. This method must be called when all the non-volatile properties are * already set to ensure visibility for other threads. @@ -440,8 +383,6 @@ private static boolean doClear(Key key, boolean nodeStop) { public void clearContext(boolean nodeStop) { cleared = true; - clearSnapshots(); - List r = reservations; if (!nodeStop && !F.isEmpty(r)) { 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 ce7301032868b..d273e168b7fa2 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 @@ -153,11 +153,6 @@ public GridH2Row createRow(KeyCacheObject key, int part, @Nullable CacheObject v */ public Value wrap(Object o, int type) throws IgniteCheckedException; - /** - * @return {@code True} if index should support snapshots. - */ - public boolean snapshotableIndex(); - /** * Checks if provided column id matches key column or key alias. * 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 76d025811bd32..f76cb5fd42393 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 @@ -22,8 +22,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -61,7 +59,6 @@ import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL; -import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.MAP; /** * H2 Table implementation. @@ -94,18 +91,12 @@ public class GridH2Table extends TableBase { /** */ private final ConcurrentMap sessions = new ConcurrentHashMap8<>(); - /** */ - private final AtomicReferenceArray actualSnapshot; - /** */ private IndexColumn affKeyCol; /** */ private final LongAdder8 size = new LongAdder8(); - /** */ - private final boolean snapshotEnabled; - /** */ private final H2RowFactory rowFactory; @@ -127,7 +118,7 @@ public class GridH2Table extends TableBase { * @param idxsFactory Indexes factory. * @param cctx Cache context. */ - public GridH2Table(CreateTableData createTblData, @Nullable GridH2RowDescriptor desc, H2RowFactory rowFactory, + public GridH2Table(CreateTableData createTblData, GridH2RowDescriptor desc, H2RowFactory rowFactory, GridH2SystemIndexFactory idxsFactory, GridCacheContext cctx) { super(createTblData); @@ -136,7 +127,7 @@ public GridH2Table(CreateTableData createTblData, @Nullable GridH2RowDescriptor this.desc = desc; this.cctx = cctx; - if (desc != null && desc.context() != null && !desc.context().customAffinityMapper()) { + if (desc.context() != null && !desc.context().customAffinityMapper()) { boolean affinityColExists = true; String affKey = desc.type().affinityKey(); @@ -186,18 +177,10 @@ public GridH2Table(CreateTableData createTblData, @Nullable GridH2RowDescriptor else idxs.add(0, new GridH2PrimaryScanIndex(this, index(0), null)); - snapshotEnabled = desc == null || desc.snapshotableIndex(); - pkIndexPos = hasHashIndex ? 2 : 1; sysIdxsCnt = idxs.size(); - final int segments = desc != null ? desc.context().config().getQueryParallelism() : - // Get index segments count from PK index. Null desc can be passed from tests. - index(pkIndexPos).segmentsCount(); - - actualSnapshot = snapshotEnabled ? new AtomicReferenceArray(Math.max(segments, 1)) : null; - lock = new ReentrantReadWriteLock(); } @@ -259,78 +242,9 @@ public GridCacheContext cache() { throw new IllegalStateException("Table " + identifierString() + " already destroyed."); } - if (snapshotInLock()) { - final GridH2QueryContext qctx = GridH2QueryContext.get(); - - assert qctx != null; - - snapshotIndexes(null, qctx.segment()); - } - return false; } - /** - * @return {@code True} If we must snapshot and release index snapshots in {@link #lock(Session, boolean, boolean)} - * and {@link #unlock(Session)} methods. - */ - private boolean snapshotInLock() { - if (!snapshotEnabled) - return false; - - GridH2QueryContext qctx = GridH2QueryContext.get(); - - // On MAP queries with distributed joins we lock tables before the queries. - return qctx == null || qctx.type() != MAP || !qctx.hasIndexSnapshots(); - } - - /** - * @param qctx Query context. - * @param segment id of index segment to be snapshoted. - */ - public void snapshotIndexes(GridH2QueryContext qctx, int segment) { - if (!snapshotEnabled) - return; - - Object[] segmentSnapshot; - - // Try to reuse existing snapshots outside of the lock. - for (long waitTime = 200; ; waitTime *= 2) { // Increase wait time to avoid starvation. - segmentSnapshot = actualSnapshot.get(segment); - - if (segmentSnapshot != null) { // Reuse existing snapshot without locking. - segmentSnapshot = doSnapshotIndexes(segment, segmentSnapshot, qctx); - - if (segmentSnapshot != null) - return; // Reused successfully. - } - - if (tryLock(true, waitTime)) - break; - } - - try { - ensureNotDestroyed(); - - // Try again inside of the lock. - segmentSnapshot = actualSnapshot.get(segment); - - if (segmentSnapshot != null) // Try reusing. - segmentSnapshot = doSnapshotIndexes(segment, segmentSnapshot, qctx); - - if (segmentSnapshot == null) { // Reuse failed, produce new snapshots. - segmentSnapshot = doSnapshotIndexes(segment,null, qctx); - - assert segmentSnapshot != null; - - actualSnapshot.set(segment, segmentSnapshot); - } - } - finally { - unlock(true); - } - } - /** * @return Table identifier. */ @@ -363,27 +277,6 @@ private void lock(boolean exclusive) { } } - /** - * @param exclusive Exclusive lock. - * @param waitMillis Milliseconds to wait for the lock. - * @return Whether lock was acquired. - */ - private boolean tryLock(boolean exclusive, long waitMillis) { - Lock l = exclusive ? lock.writeLock() : lock.readLock(); - - try { - if (!l.tryLock(waitMillis, TimeUnit.MILLISECONDS)) - return false; - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - - throw new IgniteInterruptedException("Thread got interrupted while trying to acquire table lock.", e); - } - - return true; - } - /** * Release table lock. * @@ -403,55 +296,6 @@ private void ensureNotDestroyed() { throw new IllegalStateException("Table " + identifierString() + " already destroyed."); } - /** - * Must be called inside of write lock because when using multiple indexes we have to ensure that all of them have - * the same contents at snapshot taking time. - * - * @param segment id of index segment snapshot. - * @param segmentSnapshot snapshot to be reused. - * @param qctx Query context. - * @return New indexes data snapshot. - */ - @SuppressWarnings("unchecked") - private Object[] doSnapshotIndexes(int segment, Object[] segmentSnapshot, GridH2QueryContext qctx) { - assert snapshotEnabled; - - //TODO: make HashIndex snapshotable or remove it at all? - if (segmentSnapshot == null) // Nothing to reuse, create new snapshots. - segmentSnapshot = new Object[idxs.size() - pkIndexPos]; - - // Take snapshots on all except first which is scan. - for (int i = pkIndexPos, len = idxs.size(); i < len; i++) { - Object s = segmentSnapshot[i - pkIndexPos]; - - boolean reuseExisting = s != null; - - if (!(idxs.get(i) instanceof GridH2IndexBase)) - continue; - - s = index(i).takeSnapshot(s, qctx); - - if (reuseExisting && s == null) { // Existing snapshot was invalidated before we were able to reserve it. - // Release already taken snapshots. - if (qctx != null) - qctx.clearSnapshots(); - - for (int j = pkIndexPos; j < i; j++) - if ((idxs.get(j) instanceof GridH2IndexBase)) - index(j).releaseSnapshot(); - - // Drop invalidated snapshot. - actualSnapshot.compareAndSet(segment, segmentSnapshot, null); - - return null; - } - - segmentSnapshot[i - pkIndexPos] = s; - } - - return segmentSnapshot; - } - /** {@inheritDoc} */ @Override public void close(Session ses) { // No-op. @@ -525,31 +369,9 @@ public void destroy() { if (exclusive == null) return; - if (snapshotInLock()) - releaseSnapshots(); - unlock(exclusive); } - /** - * Releases snapshots. - */ - public void releaseSnapshots() { - if (!snapshotEnabled) - return; - - releaseSnapshots0(idxs); - } - - /** - * @param idxs Indexes. - */ - private void releaseSnapshots0(ArrayList idxs) { - // Release snapshots on all except first which is scan and second which is hash. - for (int i = 2, len = idxs.size(); i < len; i++) - ((GridH2IndexBase)idxs.get(i)).releaseSnapshot(); - } - /** * Updates table for given key. If value is null then row with given key will be removed from table, * otherwise value and expiration time will be updated or new row will be added. @@ -700,10 +522,6 @@ boolean doUpdate(final GridH2Row row, boolean del) throws IgniteCheckedException return false; } - // The snapshot is not actual after update. - if (actualSnapshot != null) - actualSnapshot.set(pk.segmentForRow(row), null); - return true; } finally { diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java deleted file mode 100644 index 03fedcbfad728..0000000000000 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java +++ /dev/null @@ -1,602 +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.query.h2.opt; - -import java.util.Collection; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; -import org.apache.ignite.IgniteCheckedException; -import org.apache.ignite.internal.processors.query.h2.H2Cursor; -import org.apache.ignite.internal.util.GridCursorIteratorWrapper; -import org.apache.ignite.internal.util.IgniteTree; -import org.apache.ignite.internal.util.lang.GridCursor; -import org.apache.ignite.internal.util.offheap.unsafe.GridOffHeapSnapTreeMap; -import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard; -import org.apache.ignite.internal.util.snaptree.SnapTreeMap; -import org.apache.ignite.internal.util.typedef.internal.SB; -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.SearchRow; -import org.h2.result.SortOrder; -import org.h2.table.Column; -import org.h2.table.IndexColumn; -import org.h2.table.TableFilter; -import org.h2.value.Value; -import org.jetbrains.annotations.Nullable; - -/** - * Base class for snapshotable segmented tree indexes. - */ -@SuppressWarnings("ComparatorNotSerializable") -public class GridH2TreeIndex extends GridH2IndexBase implements Comparator { - /** */ - private final IgniteNavigableMapTree[] segments; - - /** */ - private final boolean snapshotEnabled; - - /** - * Constructor with index initialization. Creates index with single segment. - * - * @param name Index name. - * @param tbl Table. - * @param pk If this index is primary key. - * @param colsList Index columns list. - */ - @SuppressWarnings("unchecked") - public GridH2TreeIndex(String name, GridH2Table tbl, boolean pk, List colsList) { - this(name, tbl, pk, colsList, 1); - } - - /** - * Constructor with index initialization. - * - * @param name Index name. - * @param tbl Table. - * @param pk If this index is primary key. - * @param colsList Index columns list. - * @param segmentsCnt Number of segments. - */ - @SuppressWarnings("unchecked") - public GridH2TreeIndex(String name, GridH2Table tbl, boolean pk, List colsList, int segmentsCnt) { - assert segmentsCnt > 0 : segmentsCnt; - - IndexColumn[] cols = colsList.toArray(new IndexColumn[colsList.size()]); - - IndexColumn.mapColumns(cols, tbl); - - initBaseIndex(tbl, 0, name, cols, - pk ? IndexType.createPrimaryKey(false, false) : IndexType.createNonUnique(false, false, false)); - - segments = new IgniteNavigableMapTree[segmentsCnt]; - - final GridH2RowDescriptor desc = tbl.rowDescriptor(); - - if (desc == null || desc.memory() == null) { - snapshotEnabled = desc == null || desc.snapshotableIndex(); - - if (snapshotEnabled) { - for (int i = 0; i < segmentsCnt; i++) { - segments[i] = new IgniteNavigableMapTree(new SnapTreeMap(this) { - @Override protected void afterNodeUpdate_nl(Node node, Object val) { - if (val != null) - node.key = (GridSearchRowPointer)val; - } - - @Override protected Comparable comparable(Object key) { - if (key instanceof ComparableRow) - return (Comparable)key; - - return super.comparable(key); - } - }); - } - } - else { - for (int i = 0; i < segmentsCnt; i++) { - segments[i] = new IgniteNavigableMapTree( - new ConcurrentSkipListMap( - new Comparator() { - @Override public int compare(GridSearchRowPointer o1, GridSearchRowPointer o2) { - if (o1 instanceof ComparableRow) - return ((ComparableRow)o1).compareTo(o2); - - if (o2 instanceof ComparableRow) - return -((ComparableRow)o2).compareTo(o1); - - return compareRows(o1, o2); - } - } - )); - } - } - } - else { - assert desc.snapshotableIndex() : desc; - - snapshotEnabled = true; - - for (int i = 0; i < segmentsCnt; i++) { - segments[i] = new IgniteNavigableMapTree(new GridOffHeapSnapTreeMap(desc, desc, desc.memory(), desc.guard(), this) { - @Override protected void afterNodeUpdate_nl(long node, GridH2Row val) { - final long oldKey = keyPtr(node); - - if (val != null) { - key(node, val); - - guard.finalizeLater(new Runnable() { - @Override public void run() { - desc.createPointer(oldKey).decrementRefCount(); - } - }); - } - } - - @Override protected Comparable comparable(Object key) { - if (key instanceof ComparableRow) - return (Comparable)key; - - return super.comparable(key); - } - }); - } - } - - initDistributedJoinMessaging(tbl); - } - - /** {@inheritDoc} */ - @Override protected IgniteTree doTakeSnapshot() { - assert snapshotEnabled; - - int seg = threadLocalSegment(); - - IgniteNavigableMapTree tree = segments[seg]; - - return tree.clone(); - } - - /** {@inheritDoc} */ - @Override protected final IgniteTree treeForRead(int seg) { - if (!snapshotEnabled) - return segments[seg]; - - IgniteTree res = threadLocalSnapshot(); - - if (res == null) - return segments[seg]; - - return res; - } - - /** {@inheritDoc} */ - @Override public void destroy() { - assert threadLocalSnapshot() == null; - - super.destroy(); - } - - /** {@inheritDoc} */ - @Override public long getRowCount(@Nullable Session ses) { - IndexingQueryFilter f = threadLocalFilter(); - - int seg = threadLocalSegment(); - - // Fast path if we don't need to perform any filtering. - if (f == null || f.forCache((getTable()).cacheName()) == null) - try { - return treeForRead(seg).size(); - } catch (IgniteCheckedException e) { - throw DbException.convert(e); - } - - GridCursor cursor = doFind(null, false, null); - - long size = 0; - - try { - while (cursor.next()) - size++; - } - catch (IgniteCheckedException e) { - throw DbException.convert(e); - } - - return size; - } - - /** {@inheritDoc} */ - @Override public long getRowCountApproximation() { - return table.getRowCountApproximation(); - } - - /** {@inheritDoc} */ - @Override public int compare(GridSearchRowPointer r1, GridSearchRowPointer r2) { - // Second row here must be data row if first is a search row. - return -compareRows(r2, r1); - } - - /** {@inheritDoc} */ - @Override public String toString() { - SB sb = new SB((indexType.isUnique() ? "Unique index '" : "Index '") + getName() + "' ["); - - boolean first = true; - - for (IndexColumn col : getIndexColumns()) { - if (first) - first = false; - else - sb.a(", "); - - sb.a(col.getSQL()); - } - - sb.a(" ]"); - - return sb.toString(); - } - - /** {@inheritDoc} */ - @Override public double getCost(Session ses, int[] masks, TableFilter[] filters, int filter, - SortOrder sortOrder, HashSet cols) { - long rowCnt = getRowCountApproximation(); - double baseCost = getCostRangeIndex(masks, rowCnt, filters, filter, sortOrder, false, cols); - int mul = getDistributedMultiplier(ses, filters, filter); - - return mul * baseCost; - } - - /** {@inheritDoc} */ - @Override public boolean canFindNext() { - return false; - } - - /** {@inheritDoc} */ - @Override public Cursor find(Session ses, @Nullable SearchRow first, @Nullable SearchRow last) { - return new H2Cursor(doFind(first, true, last), null); - } - - /** {@inheritDoc} */ - @Override public Cursor findNext(Session ses, SearchRow higherThan, SearchRow last) { - return new H2Cursor(doFind(higherThan, false, last), null); - } - - /** - * Finds row with key equal one in given search row. - * WARNING!! Method call must be protected by {@link GridUnsafeGuard#begin()} - * {@link GridUnsafeGuard#end()} block. - * - * @param row Search row. - * @return Row. - */ - @Override public GridH2Row findOne(GridH2Row row) { - int seg = segmentForRow(row); - - return segments[seg].findOne(row); - } - - /** - * Returns sub-tree bounded by given values. - * - * @param first Lower bound. - * @param includeFirst Whether lower bound should be inclusive. - * @param last Upper bound always inclusive. - * @return Iterator over rows in given range. - */ - @SuppressWarnings("unchecked") - private GridCursor doFind(@Nullable SearchRow first, boolean includeFirst, @Nullable SearchRow last) { - int seg = threadLocalSegment(); - - IgniteTree t = treeForRead(seg); - - return doFind0(t, first, includeFirst, last, threadLocalFilter()); - } - - /** {@inheritDoc} */ - @Override protected final GridCursor doFind0( - IgniteTree t, - @Nullable SearchRow first, - boolean includeFirst, - @Nullable SearchRow last, - IndexingQueryFilter filter - ) { - includeFirst &= first != null; - - GridCursor range = subTree(t, comparable(first, includeFirst ? -1 : 1), - comparable(last, 1)); - - if (range == null) - return EMPTY_CURSOR; - - return filter(range, filter); - } - - /** - * @param row Row. - * @param bias Bias. - * @return Comparable row. - */ - private GridSearchRowPointer comparable(SearchRow row, int bias) { - if (row == null) - return null; - - if (bias == 0 && row instanceof GridH2Row) - return (GridSearchRowPointer)row; - - return new ComparableRow(row, bias); - } - - /** - * Takes sup-map from given one. - * - * @param tree Tree. - * @param first Lower bound. - * @param last Upper bound. - * @return Sub-map. - */ - @SuppressWarnings({"IfMayBeConditional", "TypeMayBeWeakened"}) - private GridCursor subTree(IgniteTree tree, - @Nullable GridSearchRowPointer first, @Nullable GridSearchRowPointer last) { - - if (first != null && last != null && compare(first, last) > 0) - return null; - - try { - // We take exclusive bounds because it is possible that one search row will be equal to multiple key rows - // in tree and we must return them all. - return tree.find(first, last); - } - catch (IgniteCheckedException e) { - throw DbException.convert(e); - } - } - - /** - * Gets iterator over all rows in this index. - * - * @return Rows iterator. - */ - GridCursor rows() { - return doFind(null, false, null); - } - - /** {@inheritDoc} */ - @Override public boolean canGetFirstOrLast() { - return true; - } - - /** {@inheritDoc} */ - @Override public Cursor findFirstOrLast(Session ses, boolean first) { - try { - int seg = threadLocalSegment(); - - IgniteTree t = treeForRead(seg); - - GridH2Row row = (GridH2Row)(first ? t.findFirst() : t.findLast()); - - return new SingleRowCursor(row); - } - catch (IgniteCheckedException e) { - throw DbException.convert(e); - } - } - - /** {@inheritDoc} */ - @Override public GridH2Row put(GridH2Row row) { - int seg = segmentForRow(row); - - return segments[seg].put(row); - } - - /** {@inheritDoc} */ - @Override public GridH2Row remove(SearchRow row) { - GridSearchRowPointer comparable = comparable(row, 0); - - int seg = segmentForRow(row); - - return segments[seg].remove(comparable); - } - - /** {@inheritDoc} */ - @Override protected int segmentsCount() { - return segments.length; - } - - /** - * Comparable row with bias. Will be used for queries to have correct bounds (in case of multicolumn index - * and query on few first columns we will multiple equal entries in tree). - */ - private final class ComparableRow implements GridSearchRowPointer, Comparable { - /** */ - private final SearchRow row; - - /** */ - private final int bias; - - /** - * @param row Row. - * @param bias Bias. - */ - private ComparableRow(SearchRow row, int bias) { - this.row = row; - this.bias = bias; - } - - /** {@inheritDoc} */ - @Override public int compareTo(SearchRow o) { - int res = compareRows(o, row); - - if (res == 0) - return bias; - - return -res; - } - - /** {@inheritDoc} */ - @Override public boolean equals(Object obj) { - throw new IllegalStateException("Should never be called."); - } - - /** {@inheritDoc} */ - @Override public int getColumnCount() { - return row.getColumnCount(); - } - - /** {@inheritDoc} */ - @Override public Value getValue(int idx) { - return row.getValue(idx); - } - - /** {@inheritDoc} */ - @Override public void setValue(int idx, Value v) { - row.setValue(idx, v); - } - - /** {@inheritDoc} */ - @Override public void setKeyAndVersion(SearchRow old) { - row.setKeyAndVersion(old); - } - - /** {@inheritDoc} */ - @Override public int getVersion() { - return row.getVersion(); - } - - /** {@inheritDoc} */ - @Override public void setKey(long key) { - row.setKey(key); - } - - /** {@inheritDoc} */ - @Override public long getKey() { - return row.getKey(); - } - - /** {@inheritDoc} */ - @Override public int getMemory() { - return row.getMemory(); - } - - /** {@inheritDoc} */ - @Override public long pointer() { - throw new IllegalStateException(); - } - - /** {@inheritDoc} */ - @Override public void incrementRefCount() { - throw new IllegalStateException(); - } - - /** {@inheritDoc} */ - @Override public void decrementRefCount() { - throw new IllegalStateException(); - } - } - - /** - * Adapter from {@link NavigableMap} to {@link IgniteTree}. - */ - private static final class IgniteNavigableMapTree implements IgniteTree, Cloneable { - /** Tree. */ - private final NavigableMap tree; - - /** - * @param tree Tree. - */ - private IgniteNavigableMapTree(NavigableMap tree) { - this.tree = tree; - } - - /** {@inheritDoc} */ - @Override public void invoke(GridSearchRowPointer key, Object x, InvokeClosure c) { - throw new UnsupportedOperationException(); - } - - /** {@inheritDoc} */ - @Override public GridH2Row put(GridH2Row val) { - return tree.put(val, val); - } - - /** {@inheritDoc} */ - @Override public GridH2Row findOne(GridSearchRowPointer key) { - return tree.get(key); - } - - /** {@inheritDoc} */ - @Override public GridCursor find(GridSearchRowPointer lower, GridSearchRowPointer upper) - throws IgniteCheckedException { - - Collection rows; - - if (lower == null && upper == null) - rows = tree.values(); - else if (lower != null && upper == null) - rows = tree.tailMap(lower).values(); - else if (lower == null) - rows = tree.headMap(upper).values(); - else - rows = tree.subMap(lower, false, upper, false).values(); - - return new GridCursorIteratorWrapper<>(rows.iterator()); - } - - /** {@inheritDoc} */ - @Override public GridH2Row findFirst() throws IgniteCheckedException { - Map.Entry first = tree.firstEntry(); - return (first == null) ? null : first.getValue(); - } - - /** {@inheritDoc} */ - @Override public GridH2Row findLast() throws IgniteCheckedException { - Map.Entry last = tree.lastEntry(); - return (last == null) ? null : last.getValue(); - } - - /** {@inheritDoc} */ - @Override public GridH2Row remove(GridSearchRowPointer key) { - return tree.remove(key); - } - - /** {@inheritDoc} */ - @Override public long size() { - return tree.size(); - } - - /** {@inheritDoc} */ - @Override public IgniteNavigableMapTree clone() { - IgniteNavigableMapTree cp; - - try { - cp = (IgniteNavigableMapTree)super.clone(); - } - catch (final CloneNotSupportedException e) { - throw DbException.convert(e); - } - - return new IgniteNavigableMapTree(cp.tree); - } - } -} \ No newline at end of file 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 19b628b07a719..e717367c642e4 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 @@ -26,7 +26,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; @@ -59,7 +58,6 @@ 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.opt.GridH2Table; 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; @@ -545,22 +543,6 @@ private void onQueryRequest0( .topologyVersion(topVer) .reservations(reserved); - List snapshotedTbls = null; - - if (!F.isEmpty(tbls)) { - snapshotedTbls = new ArrayList<>(tbls.size()); - - for (QueryTable tbl : tbls) { - GridH2Table h2Tbl = h2.dataTable(tbl); - - Objects.requireNonNull(h2Tbl, tbl.toString()); - - h2Tbl.snapshotIndexes(qctx, segmentId); - - snapshotedTbls.add(h2Tbl); - } - } - Connection conn = h2.connectionForSchema(schemaName); H2Utils.setupConnection(conn, distributedJoinMode != OFF, enforceJoinOrder); @@ -596,8 +578,6 @@ private void onQueryRequest0( qr.queryCancel(qryIdx)); if (evt) { - assert mainCctx != null; - ctx.event().record(new CacheQueryExecutedEvent<>( node, "SQL query executed.", @@ -635,11 +615,6 @@ private void onQueryRequest0( if (distributedJoinMode == OFF) qctx.clearContext(false); - - if (!F.isEmpty(snapshotedTbls)) { - for (GridH2Table dataTbl : snapshotedTbls) - dataTbl.releaseSnapshots(); - } } } catch (Throwable e) { diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java index 88ff61edf3ab0..a1a64e8f5d5ea 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java @@ -18,38 +18,20 @@ package org.apache.ignite.internal.processors.query.h2.opt; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; import java.util.Random; -import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; -import org.apache.ignite.IgniteCheckedException; -import org.apache.ignite.internal.processors.query.h2.database.H2PkHashIndex; -import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory; -import org.apache.ignite.internal.util.lang.GridCursor; -import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; -import org.h2.Driver; -import org.h2.index.Cursor; -import org.h2.index.Index; -import org.h2.result.Row; -import org.h2.result.SearchRow; -import org.h2.result.SortOrder; -import org.h2.table.IndexColumn; import org.h2.value.ValueLong; import org.h2.value.ValueString; import org.h2.value.ValueTimestamp; import org.h2.value.ValueUuid; -import org.jetbrains.annotations.Nullable; /** * Tests H2 Table. @@ -173,47 +155,6 @@ public void testTable() throws Exception { assertEquals(MAX_X, idx.getRowCount(null)); } - // Check correct rows order. - checkOrdered((GridH2TreeIndex)tbl.indexes().get(0), new Comparator() { - @Override public int compare(SearchRow o1, SearchRow o2) { - UUID id1 = (UUID)o1.getValue(0).getObject(); - UUID id2 = (UUID)o2.getValue(0).getObject(); - - return id1.compareTo(id2); - } - }); - - checkOrdered((GridH2TreeIndex)tbl.indexes().get(1), new Comparator() { - @Override public int compare(SearchRow o1, SearchRow o2) { - Long x1 = (Long)o1.getValue(3).getObject(); - Long x2 = (Long)o2.getValue(3).getObject(); - - int c = x2.compareTo(x1); - - if (c != 0) - return c; - - Timestamp t1 = (Timestamp)o1.getValue(1).getObject(); - Timestamp t2 = (Timestamp)o2.getValue(1).getObject(); - - return t1.compareTo(t2); - } - }); - - checkOrdered((GridH2TreeIndex)tbl.indexes().get(2), new Comparator() { - @Override public int compare(SearchRow o1, SearchRow o2) { - String s1 = (String)o1.getValue(2).getObject(); - String s2 = (String)o2.getValue(2).getObject(); - - return s2.compareTo(s1); - } - }); - - // Indexes data consistency. - ArrayList idxs = tbl.indexes(); - - checkIndexesConsistent((ArrayList)idxs, null); - // Check unique index. UUID id = UUID.randomUUID(); UUID id2 = UUID.randomUUID(); @@ -404,54 +345,6 @@ public void testDataLoss() throws Exception { } - /** - * @throws Exception If failed. - */ - public void testIndexFindFirstOrLast() throws Exception { - Index index = tbl.getIndexes().get(2); - assertTrue(index instanceof GridH2TreeIndex); - assertTrue(index.canGetFirstOrLast()); - - //find first on empty data - Cursor cursor = index.findFirstOrLast(null, true); - assertFalse(cursor.next()); - assertNull(cursor.get()); - - //find last on empty data - cursor = index.findFirstOrLast(null, false); - assertFalse(cursor.next()); - assertNull(cursor.get()); - - //fill with data - int rows = 100; - long t = System.currentTimeMillis(); - Random rnd = new Random(); - UUID min = null; - UUID max = null; - - for (int i = 0 ; i < rows; i++) { - UUID id = UUID.randomUUID(); - if (min == null || id.compareTo(min) < 0) - min = id; - if (max == null || id.compareTo(max) > 0) - max = id; - GridH2Row row = row(id, t++, id.toString(), rnd.nextInt(100)); - ((GridH2TreeIndex)index).put(row); - } - - //find first - cursor = index.findFirstOrLast(null, true); - assertTrue(cursor.next()); - assertEquals(min, cursor.get().getValue(0).getObject()); - assertFalse(cursor.next()); - - //find last - cursor = index.findFirstOrLast(null, false); - assertTrue(cursor.next()); - assertEquals(max, cursor.get().getValue(0).getObject()); - assertFalse(cursor.next()); - } - /** * Check query plan to correctly select index. * @@ -473,69 +366,4 @@ private void checkQueryPlan(Connection conn, String sql, String search) throws S } } } - - /** - * @param idxs Indexes. - * @param rowSet Rows. - * @return Rows. - */ - private Set checkIndexesConsistent(ArrayList idxs, @Nullable Set rowSet) throws IgniteCheckedException { - for (Index idx : idxs) { - if (!(idx instanceof GridH2TreeIndex)) - continue; - - Set set = new HashSet<>(); - - GridCursor cursor = ((GridH2TreeIndex)idx).rows(); - - while(cursor.next()) - assertTrue(set.add(cursor.get())); - - //((GridH2SnapTreeSet)((GridH2Index)idx).tree).print(); - - if (rowSet == null || rowSet.isEmpty()) - rowSet = set; - else - assertEquals(rowSet, set); - } - - return rowSet; - } - - /** - * @param idxs Indexes list. - */ - private void checkOrdered(ArrayList idxs) throws IgniteCheckedException { - for (Index idx : idxs) { - if (!(idx instanceof GridH2TreeIndex)) - continue; - - GridH2TreeIndex h2Idx = (GridH2TreeIndex)idx; - - checkOrdered(h2Idx, h2Idx); - } - } - - /** - * @param idx Index. - * @param cmp Comparator. - */ - private void checkOrdered(GridH2TreeIndex idx, Comparator cmp) throws IgniteCheckedException { - GridCursor cursor = idx.rows(); - - GridH2Row min = null; - - while (cursor.next()) { - GridH2Row row = cursor.get(); - - System.out.println(row); - - assertNotNull(row); - - assertFalse("Incorrect row order in index: " + idx + "\n min: " + min + "\n row: " + row, - min != null && cmp.compare(min, row) > 0); - - min = row; - } - } } \ No newline at end of file From ab18fdfcc4f6db1e54fb1f3b68ba7fbc31a7f6e7 Mon Sep 17 00:00:00 2001 From: "Andrey V. Mashenkov" Date: Fri, 14 Jul 2017 20:14:47 +0300 Subject: [PATCH 010/145] IGNITE-5452: GridTimeoutProcessor can hang on stop. This closes #2279. --- .../timeout/GridTimeoutProcessor.java | 18 +- .../IgniteTxRemoveTimeoutObjectsTest.java | 194 ++++++++++++++++++ .../timeout/GridTimeoutProcessorSelfTest.java | 68 ++++-- .../testsuites/IgniteCacheTestSuite3.java | 4 +- 4 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteTxRemoveTimeoutObjectsTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java index 9deca9a229d5b..8c71f76e0caa5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java @@ -37,7 +37,7 @@ */ public class GridTimeoutProcessor extends GridProcessorAdapter { /** */ - private final IgniteThread timeoutWorker; + private final TimeoutWorker timeoutWorker; /** Time-based sorted set for timeout objects. */ private final GridConcurrentSkipListSet timeoutObjs = @@ -62,13 +62,12 @@ public class GridTimeoutProcessor extends GridProcessorAdapter { public GridTimeoutProcessor(GridKernalContext ctx) { super(ctx); - timeoutWorker = new IgniteThread(ctx.config().getIgniteInstanceName(), "grid-timeout-worker", - new TimeoutWorker()); + timeoutWorker = new TimeoutWorker(); } /** {@inheritDoc} */ @Override public void start() { - timeoutWorker.start(); + new IgniteThread(timeoutWorker).start(); if (log.isDebugEnabled()) log.debug("Timeout processor started."); @@ -76,7 +75,7 @@ public GridTimeoutProcessor(GridKernalContext ctx) { /** {@inheritDoc} */ @Override public void stop(boolean cancel) throws IgniteCheckedException { - U.interrupt(timeoutWorker); + timeoutWorker.cancel(); U.join(timeoutWorker); if (log.isDebugEnabled()) @@ -159,6 +158,13 @@ private class TimeoutWorker extends GridWorker { timeoutObj.onTimeout(); } catch (Throwable e) { + if (isCancelled() && !(e instanceof Error)){ + if (log.isDebugEnabled()) + log.debug("Error when executing timeout callback: " + timeoutObj); + + return; + } + U.error(log, "Error when executing timeout callback: " + timeoutObj, e); if (e instanceof Error) @@ -170,7 +176,7 @@ private class TimeoutWorker extends GridWorker { } synchronized (mux) { - while (true) { + while (!isCancelled()) { // Access of the first element must be inside of // synchronization block, so we don't miss out // on thread notification events sent from diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteTxRemoveTimeoutObjectsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteTxRemoveTimeoutObjectsTest.java new file mode 100644 index 0000000000000..c0f9940a6eb3a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteTxRemoveTimeoutObjectsTest.java @@ -0,0 +1,194 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.processors.cache.GridCacheAbstractSelfTest; +import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; +import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor; +import org.apache.ignite.internal.util.typedef.G; +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.TransactionTimeoutException; + +import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC; +import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE; + +/** + * Test correctness of rollback a transaction with timeout during the grid stop. + */ +public class IgniteTxRemoveTimeoutObjectsTest extends GridCacheAbstractSelfTest { + /** */ + private static final int PUT_CNT = 1000; + + /** {@inheritDoc} */ + @Override protected int gridCount() { + return 3; + } + + /** {@inheritDoc} */ + @Override protected long getTestTimeout() { + return 60_000; + } + + /** + * @throws Exception If failed. + */ + public void testTxRemoveTimeoutObjects() throws Exception { + IgniteCache cache0 = grid(0).cache(DEFAULT_CACHE_NAME); + IgniteCache cache1 = grid(1).cache(DEFAULT_CACHE_NAME); + + // start additional grid to be closed. + IgniteCache cacheAdditional = startGrid(gridCount()).cache(DEFAULT_CACHE_NAME); + + for (int i = 0; i < PUT_CNT; i++) + cache0.put(i, Integer.MAX_VALUE); + + logTimeoutObjectsFrequency(); + + info("Tx1 started"); + try (Transaction tx = grid(gridCount()).transactions().txStart(PESSIMISTIC, SERIALIZABLE, 100, PUT_CNT)) { + try { + for (int i = 0; i < PUT_CNT; i++) { + cacheAdditional.put(i, Integer.MIN_VALUE); + + if (i % 100 == 0) + logTimeoutObjectsFrequency(); + } + + U.sleep(200); + + tx.commit(); + + fail("A timeout should have happened."); + } + catch (Exception e) { + assertTrue(X.hasCause(e, TransactionTimeoutException.class)); + } + } + + assertDoesNotContainLockTimeoutObjects(); + + logTimeoutObjectsFrequency(); + + stopGrid(gridCount()); + + awaitPartitionMapExchange(); + + info("Grid2 closed."); + + assertDoesNotContainLockTimeoutObjects(); + + logTimeoutObjectsFrequency(); + + // Check that the values have not changed and lock can be acquired. + try (Transaction tx2 = grid(1).transactions().txStart(PESSIMISTIC, SERIALIZABLE)) { + info("Tx2 started"); + + for (int i = 0; i < PUT_CNT; i++) { + assertEquals(cache1.get(i).intValue(), Integer.MAX_VALUE); + cache1.put(i, i); + + if (i % (PUT_CNT / 5) == 0) + logTimeoutObjectsFrequency(); + } + + tx2.commit(); + } + + info("Tx2 stopped"); + + // Check that that changes committed. + for (int i = 0; i < PUT_CNT; i++) + assertEquals(cache0.get(i).intValue(), i); + } + + /** + * Fails if at least one grid contains LockTimeoutObjects. + */ + private void assertDoesNotContainLockTimeoutObjects() { + for (Ignite ignite : G.allGrids()) { + for (GridTimeoutObject object : getTimeoutObjects((IgniteEx)ignite)) { + if (object.getClass().getSimpleName().equals("LockTimeoutObject")) + fail("Grids contain LockTimeoutObjects."); + } + } + } + + /** + * Print the number of each timeout object type on each grid to the log. + */ + private void logTimeoutObjectsFrequency() { + StringBuilder sb = new StringBuilder("Timeout objects frequency ["); + + for (Ignite ignite : G.allGrids()) { + IgniteEx igniteEx = (IgniteEx)ignite; + + Map objFreqMap = new HashMap<>(); + + Set objs = getTimeoutObjects(igniteEx); + + for (GridTimeoutObject obj : objs) { + String clsName = obj.getClass().getSimpleName(); + + Integer cnt = objFreqMap.get(clsName); + + if (cnt == null) + objFreqMap.put(clsName, 1); + else + objFreqMap.put(clsName, cnt + 1); + } + + sb.append("[") + .append(igniteEx.name()).append(": size=") + .append(objs.size()).append(", "); + + for (Map.Entry entry : objFreqMap.entrySet()) { + sb.append(entry.getKey()).append("=") + .append(entry.getValue()) + .append(", "); + } + + sb.delete(sb.length() - 2, sb.length()) + .append("]; "); + } + + sb.delete(sb.length() - 2, sb.length()) + .append("]"); + + info(sb.toString() + .replaceAll("distributed.IgniteTxRollbackOnStopTest", "Grid")); + } + + /** + * @param igniteEx IgniteEx. + * @return Set of timeout objects that process on current IgniteEx. + */ + private Set getTimeoutObjects(IgniteEx igniteEx) { + GridTimeoutProcessor timeout = igniteEx.context().timeout(); + + return GridTestUtils.getFieldValue(timeout, timeout.getClass(), "timeoutObjs"); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java index eb248cfd5dced..606b10252b19a 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessorSelfTest.java @@ -40,6 +40,11 @@ public class GridTimeoutProcessorSelfTest extends GridCommonAbstractTest { /** Kernal context. */ private GridTestKernalContext ctx; + /** {@inheritDoc} */ + @Override protected long getTestTimeout() { + return 60_000; + } + /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { ctx = newContext(); @@ -84,7 +89,9 @@ public void testTimeouts() throws Exception { } /** {@inheritDoc} */ - @Override public long endTime() { return endTime; } + @Override public long endTime() { + return endTime; + } /** {@inheritDoc} */ @Override public void onTimeout() { @@ -152,10 +159,14 @@ public void testTimeoutsMultithreaded() throws Exception { private final long endTime = System.currentTimeMillis() + RAND.nextInt(1000) + 500; /** {@inheritDoc} */ - @Override public IgniteUuid timeoutId() { return id; } + @Override public IgniteUuid timeoutId() { + return id; + } /** {@inheritDoc} */ - @Override public long endTime() { return endTime; } + @Override public long endTime() { + return endTime; + } /** {@inheritDoc} */ @Override public void onTimeout() { @@ -307,9 +318,8 @@ public void testTimeoutNeverCalled() throws Exception { assert timeObjs.size() == max; // Remove timeout objects so that they aren't able to times out (supposing the cycle takes less than 500 ms). - for (GridTimeoutObject obj : timeObjs) { + for (GridTimeoutObject obj : timeObjs) ctx.timeout().removeTimeoutObject(obj); - } Thread.sleep(1000); @@ -350,7 +360,9 @@ public void testTimeoutNeverCalledMultithreaded() throws Exception { } /** {@inheritDoc} */ - @Override public long endTime() { return endTime; } + @Override public long endTime() { + return endTime; + } /** {@inheritDoc} */ @Override public void onTimeout() { @@ -370,9 +382,8 @@ public void testTimeoutNeverCalledMultithreaded() throws Exception { // Remove timeout objects so that they aren't able to times out // (supposing the cycle takes less than 500 ms). - for (GridTimeoutObject obj : timeObjs) { + for (GridTimeoutObject obj : timeObjs) ctx.timeout().removeTimeoutObject(obj); - } } }, threads, "timeout-test-worker"); @@ -381,6 +392,9 @@ public void testTimeoutNeverCalledMultithreaded() throws Exception { assert callCnt.get() == 0; } + /** + * @throws Exception If test failed. + */ public void testAddRemoveInterleaving() throws Exception { final AtomicInteger callCnt = new AtomicInteger(0); @@ -430,9 +444,8 @@ public void testAddRemoveInterleaving() throws Exception { // Remove timeout objects so that they aren't able to times out // (supposing the cycle takes less than 500 ms). - for (GridTimeoutObject obj : timeObjs) { + for (GridTimeoutObject obj : timeObjs) ctx.timeout().removeTimeoutObject(obj); - } } }, 100, "timeout-test-worker"); @@ -516,10 +529,14 @@ public void testTimeoutCallOnce() throws Exception { private int cnt; /** {@inheritDoc} */ - @Override public IgniteUuid timeoutId() { return id; } + @Override public IgniteUuid timeoutId() { + return id; + } /** {@inheritDoc} */ - @Override public long endTime() { return endTime; } + @Override public long endTime() { + return endTime; + } /** {@inheritDoc} */ @Override public void onTimeout() { @@ -608,4 +625,31 @@ public void testTimeoutSameEndTime() throws Exception { assert latch.await(3000, MILLISECONDS); } + + /** + * Test that eaten {@link InterruptedException} will not hang on the closing of the grid. + * + * @throws Exception If test failed. + */ + public void testCancelingWithClearedInterruptedFlag() throws Exception { + final CountDownLatch onTimeoutCalled = new CountDownLatch(1); + + ctx.timeout().addTimeoutObject(new GridTimeoutObjectAdapter(10) { + /** {@inheritDoc} */ + @Override public void onTimeout() { + try { + onTimeoutCalled.countDown(); + + // Wait for CacheProcessor has stopped and cause InterruptedException + // which clears interrupted flag. + Thread.sleep(Long.MAX_VALUE); + } + catch (InterruptedException ignore) { + // No-op. + } + } + }); + + onTimeoutCalled.await(); + } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java index 58e9dc320305a..a6be07ea07ee0 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java @@ -36,9 +36,9 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheInterceptorSelfTestSuite; import org.apache.ignite.internal.processors.cache.IgniteCacheScanPredicateDeploymentSelfTest; import org.apache.ignite.internal.processors.cache.distributed.CacheAsyncOperationsTest; -import org.apache.ignite.internal.processors.cache.distributed.CacheGroupsPreloadTest; import org.apache.ignite.internal.processors.cache.distributed.GridCacheMixedModeSelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteTxGetAfterStopTest; +import org.apache.ignite.internal.processors.cache.distributed.IgniteTxRemoveTimeoutObjectsTest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridCacheDaemonNodePartitionedSelfTest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedOnlyP2PDisabledByteArrayValuesSelfTest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedOnlyP2PEnabledByteArrayValuesSelfTest; @@ -199,6 +199,8 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(CacheAsyncOperationsTest.class); + suite.addTestSuite(IgniteTxRemoveTimeoutObjectsTest.class); + return suite; } } From 580b6aa8e5a8a887397eab5c4c830ec28f45cd30 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Wed, 9 Aug 2017 17:22:54 +0700 Subject: [PATCH 011/145] IGNITE-5734 Web Console: Fixed npm dependencies. (cherry picked from commit aeafbf1) --- modules/web-console/frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web-console/frontend/package.json b/modules/web-console/frontend/package.json index 6b049ff91ae63..f96c322da1447 100644 --- a/modules/web-console/frontend/package.json +++ b/modules/web-console/frontend/package.json @@ -57,7 +57,7 @@ "babel-polyfill": "6.23.0", "babel-preset-es2015": "6.24.1", "babel-preset-stage-1": "6.24.1", - "babel-runtime": "6.23.0", + "babel-runtime": "6.25.0", "bootstrap-sass": "3.3.7", "brace": "0.10.0", "copy-webpack-plugin": "4.0.1", From 841db65e56063605475710bc170de4aea672c31d Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Wed, 9 Aug 2017 18:55:04 +0700 Subject: [PATCH 012/145] IGNITE-5987 Added -nq (visor will not quit in batch mode) option for Visor Cmd. (cherry picked from commit 8d6e842) --- .../apache/ignite/visor/commands/VisorConsole.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala index 19d130e625db9..d53a0d51f293c 100644 --- a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala +++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/VisorConsole.scala @@ -92,6 +92,7 @@ class VisorConsole { println(" -cfg= - connect with specified configuration.") println(" -b= - batch mode with file.") println(" -e=cmd1;cmd2;... - batch mode with commands.") + println(" -nq - batch mode will not quit after execution (useful for alerts monitoring).") visor.quit() } @@ -103,6 +104,10 @@ class VisorConsole { val cfgFile = argValue("cfg", argLst) val batchFile = argValue("b", argLst) val batchCommand = argValue("e", argLst) + val noBatchQuit = hasArgName("nq", argLst) + + if (noBatchQuit && batchFile.isEmpty && batchCommand.isEmpty) + visor.warn("Option \"-nq\" will be ignored because batch mode options \"-b\" or \"-e\" were not specified.") cfgFile.foreach(cfg => { if (cfg.trim.isEmpty) { @@ -149,7 +154,10 @@ class VisorConsole { case Some(cmd) => visor.batchMode = true - new ByteArrayInputStream((cmd + "\nquit\n").getBytes("UTF-8")) + val script = if (noBatchQuit) cmd else cmd + "\nquit\n" + + new ByteArrayInputStream(script.getBytes("UTF-8")) + case None => new FileInputStream(FileDescriptor.in) } @@ -159,7 +167,7 @@ class VisorConsole { new TerminalSupport(false) {} } catch { - case ignored: ClassNotFoundException => null + case _: ClassNotFoundException => null } val reader = new ConsoleReader(inputStream, System.out, term) From 5c2097856714a7803956d754735c68b21156019c Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 11 Aug 2017 10:25:36 +0700 Subject: [PATCH 013/145] IGNITE-5902 Implemented stop caches at once. (cherry picked from commit ebb8765) --- .../visor/cache/VisorCacheStopTask.java | 22 ++++++++------- .../visor/cache/VisorCacheStopTaskArg.java | 27 +++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTask.java index 9f7c018b9fa89..df95c5ea73488 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTask.java @@ -17,8 +17,10 @@ package org.apache.ignite.internal.visor.cache; -import org.apache.ignite.IgniteCache; +import java.util.Collection; +import java.util.HashSet; import org.apache.ignite.internal.processors.task.GridInternal; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.visor.VisorJob; import org.apache.ignite.internal.visor.VisorOneNodeTask; @@ -46,23 +48,23 @@ private static class VisorCacheStopJob extends VisorJob cacheNames = F.isEmpty(arg.getCacheNames()) + ? F.asList(arg.getCacheName()) + : new HashSet<>(arg.getCacheNames()); - IgniteCache cache = ignite.cache(cacheName); + if (F.isEmpty(cacheNames)) + throw new IllegalStateException("Cache names was not specified."); - if (cache == null) - throw new IllegalStateException("Failed to find cache for name: " + cacheName); - - cache.destroy(); + ignite.destroyCaches(cacheNames); return null; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTaskArg.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTaskArg.java index 4976036cef283..77156ab25a9b3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTaskArg.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCacheStopTaskArg.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; +import java.util.List; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.internal.visor.VisorDataTransferObject; @@ -34,6 +35,9 @@ public class VisorCacheStopTaskArg extends VisorDataTransferObject { /** Cache name. */ private String cacheName; + /** Cache names. */ + private List cacheNames; + /** * Default constructor. */ @@ -48,6 +52,13 @@ public VisorCacheStopTaskArg(String cacheName) { this.cacheName = cacheName; } + /** + * @param cacheNames Cache names. + */ + public VisorCacheStopTaskArg(List cacheNames) { + this.cacheNames = cacheNames; + } + /** * @return Cache name. */ @@ -55,14 +66,30 @@ public String getCacheName() { return cacheName; } + /** + * @return Cache names. + */ + public List getCacheNames() { + return cacheNames; + } + + /** {@inheritDoc} */ + @Override public byte getProtocolVersion() { + return V2; + } + /** {@inheritDoc} */ @Override protected void writeExternalData(ObjectOutput out) throws IOException { U.writeString(out, cacheName); + U.writeCollection(out, cacheNames); } /** {@inheritDoc} */ @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException { cacheName = U.readString(in); + + if (protoVer > V1) + cacheNames = U.readList(in); } /** {@inheritDoc} */ From 8b2461942c18f228c0107020aa28c03711bdceda Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 11 Aug 2017 11:07:26 +0700 Subject: [PATCH 014/145] IGNITE-6012 Refactored GridJettyRestHandler.processRequest(): replace mapper.writeValueAsString with writeValue(stream, v). (cherry picked from commit 3a390c8) --- .../http/jetty/GridJettyRestHandler.java | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java index c864a105d805b..327c13a330280 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.UUID; import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -356,41 +357,29 @@ private void processRequest(String act, HttpServletRequest req, HttpServletRespo U.error(log, "Failed to process HTTP request [action=" + act + ", req=" + req + ']', e); - cmdRes = new GridRestResponse(STATUS_FAILED, e.getMessage()); - if (e instanceof Error) throw (Error)e; - } - - String json; - try { - json = jsonMapper.writeValueAsString(cmdRes); + cmdRes = new GridRestResponse(STATUS_FAILED, e.getMessage()); } - catch (JsonProcessingException e1) { - U.error(log, "Failed to convert response to JSON: " + cmdRes, e1); - GridRestResponse resFailed = new GridRestResponse(STATUS_FAILED, e1.getMessage()); + try { + ServletOutputStream os = res.getOutputStream(); try { - json = jsonMapper.writeValueAsString(resFailed); + jsonMapper.writeValue(os, cmdRes); } - catch (JsonProcessingException e2) { - json = "{\"successStatus\": \"1\", \"error:\" \"" + e2.getMessage() + "\"}}"; - } - } - - try { - if (log.isDebugEnabled()) - log.debug("Parsed command response into JSON object: " + json); + catch (JsonProcessingException e) { + U.error(log, "Failed to convert response to JSON: " + cmdRes, e); - res.getWriter().write(json); + jsonMapper.writeValue(os, F.asMap("successStatus", STATUS_FAILED, "error", e.getMessage())); + } if (log.isDebugEnabled()) log.debug("Processed HTTP request [action=" + act + ", jsonRes=" + cmdRes + ", req=" + req + ']'); } catch (IOException e) { - U.error(log, "Failed to send HTTP response: " + json, e); + U.error(log, "Failed to send HTTP response: " + cmdRes, e); } } From 3a7d4f4a79e7c0a23244387e3a68535375a66a87 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 11 Aug 2017 11:18:42 +0700 Subject: [PATCH 015/145] IGNITE-6013 Optimized processing response from cluster. (cherry picked from commit b02c481) --- .../agent/handlers/ClusterListener.java | 13 +- .../console/agent/rest/RestExecutor.java | 201 ++++++++++++++++-- 2 files changed, 194 insertions(+), 20 deletions(-) diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java index b811a2de4929c..435ce74dd06a3 100644 --- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java +++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java @@ -46,7 +46,7 @@ import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SUCCESS; /** - * API to retranslate topology from Ignite cluster available by node-uri. + * API to transfer topology from Ignite cluster available by node-uri. */ public class ClusterListener { /** */ @@ -65,7 +65,7 @@ public class ClusterListener { private static final long DFLT_TIMEOUT = 3000L; /** JSON object mapper. */ - private static final ObjectMapper mapper = new GridJettyObjectMapper(); + private static final ObjectMapper MAPPER = new GridJettyObjectMapper(); /** Latest topology snapshot. */ private TopologySnapshot top; @@ -234,10 +234,7 @@ Collection nid8() { /** */ boolean differentCluster(TopologySnapshot old) { - if (old == null || F.isEmpty(old.nids)) - return true; - - return Collections.disjoint(nids, old.nids); + return old == null || F.isEmpty(old.nids) || Collections.disjoint(nids, old.nids); } } @@ -250,7 +247,7 @@ private class WatchTask implements Runnable { switch (res.getStatus()) { case STATUS_SUCCESS: - List nodes = mapper.readValue(res.getData(), + List nodes = MAPPER.readValue(res.getData(), new TypeReference>() {}); TopologySnapshot newTop = new TopologySnapshot(nodes); @@ -290,7 +287,7 @@ private class BroadcastTask implements Runnable { switch (res.getStatus()) { case STATUS_SUCCESS: - List nodes = mapper.readValue(res.getData(), + List nodes = MAPPER.readValue(res.getData(), new TypeReference>() {}); TopologySnapshot newTop = new TopologySnapshot(nodes); diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java index 03eca4e557173..13989b46d46de 100644 --- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java +++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java @@ -17,9 +17,16 @@ package org.apache.ignite.console.agent.rest; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.io.IOException; +import java.io.StringWriter; import java.net.ConnectException; import java.util.HashMap; import java.util.Map; @@ -40,6 +47,9 @@ import org.apache.ignite.logger.slf4j.Slf4jLogger; import org.slf4j.LoggerFactory; +import static com.fasterxml.jackson.core.JsonToken.END_ARRAY; +import static com.fasterxml.jackson.core.JsonToken.END_OBJECT; +import static com.fasterxml.jackson.core.JsonToken.START_ARRAY; import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_AUTH_FAILED; import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_FAILED; import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SUCCESS; @@ -52,7 +62,7 @@ public class RestExecutor { private static final IgniteLogger log = new Slf4jLogger(LoggerFactory.getLogger(RestExecutor.class)); /** JSON object mapper. */ - private static final ObjectMapper mapper = new GridJettyObjectMapper(); + private static final ObjectMapper MAPPER = new GridJettyObjectMapper(); /** */ private final OkHttpClient httpClient; @@ -141,34 +151,35 @@ private RestResult sendRequest(boolean demo, String path, Map pa reqBuilder.url(urlBuilder.build()); try (Response resp = httpClient.newCall(reqBuilder.build()).execute()) { - String content = resp.body().string(); - if (resp.isSuccessful()) { - JsonNode node = mapper.readTree(content); + RestResponseHolder res = MAPPER.readValue(resp.body().byteStream(), RestResponseHolder.class); - int status = node.get("successStatus").asInt(); + int status = res.getSuccessStatus(); switch (status) { case STATUS_SUCCESS: - return RestResult.success(node.get("response").toString()); + return RestResult.success(res.getResponse()); default: - return RestResult.fail(status, node.get("error").asText()); + return RestResult.fail(status, res.getError()); } } if (resp.code() == 401) - return RestResult.fail(STATUS_AUTH_FAILED, "Failed to authenticate in grid. " + + return RestResult.fail(STATUS_AUTH_FAILED, "Failed to authenticate in cluster. " + "Please check agent\'s login and password or node port."); - return RestResult.fail(STATUS_FAILED, "Failed connect to node and execute REST command."); + if (resp.code() == 404) + return RestResult.fail(STATUS_FAILED, "Failed connect to cluster."); + + return RestResult.fail(STATUS_FAILED, "Failed to execute REST command: " + resp.message()); } catch (ConnectException ignored) { - LT.warn(log, "Failed connect to node and execute REST command. " + + LT.warn(log, "Failed connect to cluster. " + "Please ensure that nodes have [ignite-rest-http] module in classpath " + "(was copied from libs/optional to libs folder)."); - throw new ConnectException("Failed connect to node and execute REST command [url=" + urlBuilder + ", parameters=" + params + "]"); + throw new ConnectException("Failed connect to cluster [url=" + urlBuilder + ", parameters=" + params + "]"); } } @@ -208,4 +219,170 @@ public RestResult topology(boolean demo, boolean full) throws IOException { return sendRequest(demo, "ignite", params, null, null); } + + /** + * REST response holder Java bean. + */ + private static class RestResponseHolder { + /** Success flag */ + private int successStatus; + + /** Error. */ + private String err; + + /** Response. */ + private String res; + + /** Session token string representation. */ + private String sesTokStr; + + /** + * @return {@code True} if this request was successful. + */ + public int getSuccessStatus() { + return successStatus; + } + + /** + * @param successStatus Whether request was successful. + */ + public void setSuccessStatus(int successStatus) { + this.successStatus = successStatus; + } + + /** + * @return Error. + */ + public String getError() { + return err; + } + + /** + * @param err Error. + */ + public void setError(String err) { + this.err = err; + } + + /** + * @return Response object. + */ + public String getResponse() { + return res; + } + + /** + * @param res Response object. + */ + @JsonDeserialize(using = RawContentDeserializer.class) + public void setResponse(String res) { + this.res = res; + } + + /** + * @return String representation of session token. + */ + public String getSessionToken() { + return sesTokStr; + } + + /** + * @param sesTokStr String representation of session token. + */ + public void setSessionToken(String sesTokStr) { + this.sesTokStr = sesTokStr; + } + } + + /** + * Raw content deserializer that will deserialize any data as string. + */ + private static class RawContentDeserializer extends JsonDeserializer { + /** */ + private final JsonFactory factory = new JsonFactory(); + + /** + * @param tok Token to process. + * @param p Parser. + * @param gen Generator. + */ + private void writeToken(JsonToken tok, JsonParser p, JsonGenerator gen) throws IOException { + switch (tok) { + case FIELD_NAME: + gen.writeFieldName(p.getText()); + break; + + case START_ARRAY: + gen.writeStartArray(); + break; + + case END_ARRAY: + gen.writeEndArray(); + break; + + case START_OBJECT: + gen.writeStartObject(); + break; + + case END_OBJECT: + gen.writeEndObject(); + break; + + case VALUE_NUMBER_INT: + gen.writeNumber(p.getLongValue()); + break; + + case VALUE_NUMBER_FLOAT: + gen.writeNumber(p.getDoubleValue()); + break; + + case VALUE_TRUE: + gen.writeBoolean(true); + break; + + case VALUE_FALSE: + gen.writeBoolean(false); + break; + + case VALUE_NULL: + gen.writeNull(); + break; + + default: + gen.writeString(p.getText()); + } + } + + /** {@inheritDoc} */ + @Override public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonToken startTok = p.getCurrentToken(); + + if (startTok.isStructStart()) { + StringWriter wrt = new StringWriter(4096); + + JsonGenerator gen = factory.createGenerator(wrt); + + JsonToken tok = startTok, endTok = startTok == START_ARRAY ? END_ARRAY : END_OBJECT; + + int cnt = 1; + + while (cnt > 0) { + writeToken(tok, p, gen); + + tok = p.nextToken(); + + if (tok == startTok) + cnt++; + else if (tok == endTok) + cnt--; + } + + gen.close(); + + return wrt.toString(); + } + + return p.getValueAsString(); + } + } } From fde550bac56fd0cc7c51c62a9c291dd4c3f3030c Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Mon, 14 Aug 2017 11:32:11 +0300 Subject: [PATCH 016/145] IGNITE-5941 - Fixed index name length restrictions. This closes #2408 --- .../ignite/internal/pagemem/PageUtils.java | 26 +++ .../cache/persistence/MetadataStorage.java | 12 +- .../cache/index/LongIndexNameTest.java | 212 ++++++++++++++++++ .../IgniteCacheQuerySelfTestSuite.java | 2 + 4 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java index 3fa5954e88afd..362ac5469cd4b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java @@ -36,6 +36,19 @@ public static byte getByte(long addr, int off) { return GridUnsafe.getByte(addr + off); } + /** + * + * @param addr Start address. + * @param off Offset. + * @return Byte value from given address. + */ + public static int getUnsignedByte(long addr, int off) { + assert addr > 0 : addr; + assert off >= 0; + + return GridUnsafe.getByte(addr + off) & 0xFF; + } + /** * @param addr Start address. * @param off Offset. @@ -163,6 +176,19 @@ public static void putByte(long addr, int off, byte v) { GridUnsafe.putByte(addr + off, v); } + /** + * @param addr Address. + * @param off Offset. + * @param v Value. + */ + public static void putUnsignedByte(long addr, int off, int v) { + assert addr > 0 : addr; + assert off >= 0; + assert v >= 0 && v <= 255; + + GridUnsafe.putByte(addr + off, (byte) v); + } + /** * @param addr Address. * @param off Offset. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java index 743f3b9099aee..498ecdd3b14e3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java @@ -39,7 +39,7 @@ */ public class MetadataStorage implements MetaStore { /** Max index name length (bytes num) */ - public static final int MAX_IDX_NAME_LEN = 768; + public static final int MAX_IDX_NAME_LEN = 255; /** Bytes in byte. */ private static final int BYTE_LEN = 1; @@ -197,7 +197,7 @@ private MetaTree( int shift = 0; // Compare index names. - final byte len = PageUtils.getByte(pageAddr, off + shift); + final int len = PageUtils.getUnsignedByte(pageAddr, off + shift); shift += BYTE_LEN; @@ -256,7 +256,7 @@ private static void storeRow( final IndexItem row ) { // Index name length. - PageUtils.putByte(pageAddr, off, (byte)row.idxName.length); + PageUtils.putUnsignedByte(pageAddr, off, row.idxName.length); off++; // Index name. @@ -282,10 +282,10 @@ private static void storeRow( int srcOff ) { // Index name length. - final byte len = PageUtils.getByte(srcPageAddr, srcOff); + final int len = PageUtils.getUnsignedByte(srcPageAddr, srcOff); srcOff++; - PageUtils.putByte(dstPageAddr, dstOff, len); + PageUtils.putUnsignedByte(dstPageAddr, dstOff, len); dstOff++; PageHandler.copyMemory(srcPageAddr, dstPageAddr, srcOff, dstOff, len); @@ -305,7 +305,7 @@ private static void storeRow( */ private static IndexItem readRow(final long pageAddr, int off) { // Index name length. - final int len = PageUtils.getByte(pageAddr, off) & 0xFF; + final int len = PageUtils.getUnsignedByte(pageAddr, off) & 0xFF; off++; // Index name. diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java new file mode 100644 index 0000000000000..92883a488405c --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java @@ -0,0 +1,212 @@ +/* + * 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 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.QueryIndex; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.cache.query.QueryCursor; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * + */ +public class LongIndexNameTest extends GridCommonAbstractTest { + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + return super.getConfiguration(igniteInstanceName) + .setPersistentStoreConfiguration(new PersistentStoreConfiguration()) + .setCacheConfiguration(new CacheConfiguration("cache") + .setQueryEntities(getIndexCfg()) + .setAffinity(new RendezvousAffinityFunction(false, 16))); + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + deleteWorkFiles(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + deleteWorkFiles(); + } + + /** + * @throws Exception If failed. + */ + public void testLongIndexNames() throws Exception { + try { + Ignite ignite = startGrid(0); + + IgniteCache cache = insertSomeData(ignite); + + QueryCursor cursor1 = cache.query(new SqlFieldsQuery("SELECT * FROM Person where name like '%Name 0'")); + QueryCursor cursor1Idx = cache.query(new SqlFieldsQuery("SELECT * FROM Person where name = 'Name 0'")); + + QueryCursor cursor2 = cache.query(new SqlFieldsQuery("SELECT * FROM Person where age like '%0'")); + QueryCursor cursor2Idx = cache.query(new SqlFieldsQuery("SELECT * FROM Person where age = 0")); + + assertEquals(cursor1.getAll().size(), cursor1Idx.getAll().size()); + assertEquals(cursor2.getAll().size(), cursor2Idx.getAll().size()); + + ignite.close(); + + Thread.sleep(2_000); + + ignite = startGrid(0); + + cache = insertSomeData(ignite); + + cursor1 = cache.query(new SqlFieldsQuery("SELECT * FROM Person where name like '%Name 0'")); + cursor1Idx = cache.query(new SqlFieldsQuery("SELECT * FROM Person where name = 'Name 0'")); + + cursor2 = cache.query(new SqlFieldsQuery("SELECT * FROM Person where age like '%0'")); + cursor2Idx = cache.query(new SqlFieldsQuery("SELECT * FROM Person where age = 0")); + + assertEquals(cursor1.getAll().size(), cursor1Idx.getAll().size()); + assertEquals(cursor2.getAll().size(), cursor2Idx.getAll().size()); + + + } + finally { + stopAllGrids(); + } + } + + /** + * + */ + @NotNull private IgniteCache insertSomeData(Ignite ignite) { + if (!ignite.active()) + ignite.active(true); + + IgniteCache cache = ignite.cache("cache"); + + for (int i=0; i<10; i++) + cache.put(String.valueOf(System.currentTimeMillis()), new Person("Name " + i, i)); + + return cache; + } + + /** + * + */ + public static List getIndexCfg() { + ArrayList entities = new ArrayList<>(); + + QueryEntity qe = new QueryEntity(String.class.getName(), Person.class.getName()); + + LinkedHashMap fieldsMap = new LinkedHashMap<>(); + fieldsMap.put("name", String.class.getName()); + fieldsMap.put("age", Integer.class.getName()); + + qe.setFields(fieldsMap); + + ArrayList indices = new ArrayList<>(); + QueryIndex index = new QueryIndex("name", true, "LONG_NAME_123456789012345678901234567890" + + "12345678901234567890123456789012345678901234567890123456789012345678901234567890"); + + QueryIndex index2 = new QueryIndex("age", true, "AGE_IDX"); + indices.add(index); + indices.add(index2); + + qe.setIndexes(indices); + + entities.add(qe); + + return entities; + } + + /** + * @throws IgniteCheckedException If failed. + */ + private void deleteWorkFiles() throws IgniteCheckedException { + deleteRecursively(U.resolveWorkDirectory(U.defaultWorkDirectory(), "db", false)); + } + + /** + * + */ + private static class Person { + /** */ + private String name; + + /** */ + private int age; + + /** + * + */ + public Person() { + // No-op. + } + + /** + * @param name Name. + * @param age Age. + */ + public Person(String name, int age) { + this.name = name; + this.age = age; + } + + /** + * @return Name. + */ + public String getName() { + return name; + } + + /** + * @param name Name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return Age. + */ + public int getAge() { + return age; + } + + /** + * @param age Age. + */ + public void setAge(int age) { + this.age = age; + } + } +} 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 1ad0d4bfb8ca2..decc7d5cd4396 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 @@ -108,6 +108,7 @@ import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerTransactionalPartitionedTest; import org.apache.ignite.internal.processors.cache.index.H2DynamicIndexingComplexServerTransactionalReplicatedTest; import org.apache.ignite.internal.processors.cache.index.H2DynamicTableSelfTest; +import org.apache.ignite.internal.processors.cache.index.LongIndexNameTest; import org.apache.ignite.internal.processors.cache.index.SchemaExchangeSelfTest; import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalAtomicQuerySelfTest; import org.apache.ignite.internal.processors.cache.local.IgniteCacheLocalFieldsQuerySelfTest; @@ -317,6 +318,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteCacheDistributedPartitionQueryConfigurationSelfTest.class); suite.addTestSuite(IgniteSqlKeyValueFieldsTest.class); suite.addTestSuite(IgniteSqlRoutingTest.class); + suite.addTestSuite(LongIndexNameTest.class); return suite; } From 13f38d79b57b395e43d42a8f3c278cf48336d7c5 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Mon, 14 Aug 2017 12:12:46 +0300 Subject: [PATCH 017/145] IGNITE-5890 Added estimated time to rebalance completion and time to rebalance start to MXBean - Fixes #2386. --- .../org/apache/ignite/cache/CacheMetrics.java | 10 ++ .../cache/CacheClusterMetricsMXBeanImpl.java | 10 ++ .../cache/CacheLocalMetricsMXBeanImpl.java | 10 ++ .../processors/cache/CacheMetricsImpl.java | 36 +++++- .../cache/CacheMetricsSnapshot.java | 18 +++ .../preloader/GridDhtPartitionDemander.java | 22 +++- .../dht/preloader/GridDhtPreloader.java | 18 +-- .../CacheGroupsMetricsRebalanceTest.java | 118 ++++++++++++++++++ .../PlatformCacheWriteMetricsTask.java | 10 ++ 9 files changed, 234 insertions(+), 18 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/cache/CacheMetrics.java b/modules/core/src/main/java/org/apache/ignite/cache/CacheMetrics.java index 0cff4a8537567..20ea692f33198 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/CacheMetrics.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/CacheMetrics.java @@ -505,6 +505,16 @@ public interface CacheMetrics { */ public long getRebalancingBytesRate(); + /** + * @return Estimated rebalancing finished time. + */ + public long estimateRebalancingFinishTime(); + + /** + * @return Rebalancing start time. + */ + public long rebalancingStartTime(); + /** * Checks whether statistics collection is enabled in this cache. *

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClusterMetricsMXBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClusterMetricsMXBeanImpl.java index 266c577219ac1..df4a6ab7d9b22 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClusterMetricsMXBeanImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClusterMetricsMXBeanImpl.java @@ -382,4 +382,14 @@ class CacheClusterMetricsMXBeanImpl implements CacheMetricsMXBean { @Override public long getRebalancingBytesRate() { return cache.clusterMetrics().getRebalancingBytesRate(); } + + /** {@inheritDoc} */ + @Override public long estimateRebalancingFinishTime() { + return cache.clusterMetrics().estimateRebalancingFinishTime(); + } + + /** {@inheritDoc} */ + @Override public long rebalancingStartTime() { + return cache.clusterMetrics().rebalancingStartTime(); + } } \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheLocalMetricsMXBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheLocalMetricsMXBeanImpl.java index f363bfefbc4bf..a7671931c40d7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheLocalMetricsMXBeanImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheLocalMetricsMXBeanImpl.java @@ -382,4 +382,14 @@ class CacheLocalMetricsMXBeanImpl implements CacheMetricsMXBean { @Override public long getRebalancingBytesRate() { return cache.metrics0().getRebalancingBytesRate(); } + + /** {@inheritDoc} */ + @Override public long estimateRebalancingFinishTime() { + return cache.metrics0().estimateRebalancingFinishTime(); + } + + /** {@inheritDoc} */ + @Override public long rebalancingStartTime() { + return cache.metrics0().rebalancingStartTime(); + } } \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java index 6a8ae0b26f344..d03a6f8e952f1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java @@ -29,6 +29,7 @@ import org.apache.ignite.internal.processors.cache.store.GridCacheWriteBehindStore; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.internal.util.typedef.internal.U; /** * Adapter for cache metrics. @@ -108,6 +109,9 @@ public class CacheMetricsImpl implements CacheMetrics { /** Total rebalanced bytes count. */ private AtomicLong totalRebalancedBytes = new AtomicLong(); + /** Rebalanced start time. */ + private AtomicLong rebalanceStartTime = new AtomicLong(-1L); + /** Estimated rebalancing keys count. */ private AtomicLong estimatedRebalancingKeys = new AtomicLong(); @@ -734,7 +738,7 @@ public void addPutAndGetTimeNanos(long duration) { } /** {@inheritDoc} */ - public int getTotalPartitionsCount() { + @Override public int getTotalPartitionsCount() { int res = 0; if (cctx.isLocal()) @@ -749,7 +753,7 @@ public int getTotalPartitionsCount() { } /** {@inheritDoc} */ - public int getRebalancingPartitionsCount() { + @Override public int getRebalancingPartitionsCount() { int res = 0; if (cctx.isLocal()) @@ -764,17 +768,17 @@ public int getRebalancingPartitionsCount() { } /** {@inheritDoc} */ - public long getKeysToRebalanceLeft() { + @Override public long getKeysToRebalanceLeft() { return Math.max(0, estimatedRebalancingKeys.get() - rebalancedKeys.get()); } /** {@inheritDoc} */ - public long getRebalancingKeysRate() { + @Override public long getRebalancingKeysRate() { return rebalancingKeysRate.getRate(); } /** {@inheritDoc} */ - public long getRebalancingBytesRate() { + @Override public long getRebalancingBytesRate() { return rebalancingBytesRate.getRate(); } @@ -791,6 +795,28 @@ public void clearRebalanceCounters() { rebalancingBytesRate.clear(); rebalancingKeysRate.clear(); + + rebalanceStartTime.set(-1L); + } + + /** + * + */ + public void startRebalance(long delay){ + rebalanceStartTime.addAndGet(delay + U.currentTimeMillis()); + } + + /** {@inheritDoc} */ + @Override public long estimateRebalancingFinishTime() { + long rate = rebalancingKeysRate.getRate(); + + return rate <= 0 ? -1L : + ((getKeysToRebalanceLeft() / rate) * REBALANCE_RATE_INTERVAL) + U.currentTimeMillis(); + } + + /** {@inheritDoc} */ + @Override public long rebalancingStartTime() { + return rebalanceStartTime.get(); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsSnapshot.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsSnapshot.java index e9141c6406342..2d38db8a02648 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsSnapshot.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsSnapshot.java @@ -203,6 +203,12 @@ public class CacheMetricsSnapshot implements CacheMetrics, Externalizable { /** Get rebalancing bytes rate. */ private long rebalancingBytesRate; + /** Start rebalance time. */ + private long rebalanceStartTime; + + /** Estimate rebalance finish time. */ + private long rebalanceFinishTime; + /** */ private String keyType; @@ -307,6 +313,8 @@ public CacheMetricsSnapshot(CacheMetrics m) { keysToRebalanceLeft = m.getKeysToRebalanceLeft(); rebalancingBytesRate = m.getRebalancingBytesRate(); rebalancingKeysRate = m.getRebalancingKeysRate(); + rebalanceStartTime = m.rebalancingStartTime(); + rebalanceFinishTime = m.estimateRebalancingFinishTime(); } /** @@ -715,6 +723,16 @@ public CacheMetricsSnapshot(CacheMetrics loc, Collection metrics) return rebalancingBytesRate; } + /** {@inheritDoc} */ + @Override public long estimateRebalancingFinishTime() { + return rebalanceFinishTime; + } + + /** {@inheritDoc} */ + @Override public long rebalancingStartTime() { + return rebalanceStartTime; + } + /** {@inheritDoc} */ @Override public boolean isWriteBehindEnabled() { return isWriteBehindEnabled; 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 248b739acd207..2258187e4a4e1 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 @@ -257,11 +257,13 @@ void onTopologyChanged(GridDhtPartitionsExchangeFuture lastFut) { * @param forcedRebFut External future for forced rebalance. * @return Rebalancing runnable. */ - Runnable addAssignments(final GridDhtPreloaderAssignments assigns, + Runnable addAssignments( + final GridDhtPreloaderAssignments assigns, boolean force, int cnt, final Runnable next, - @Nullable final GridCompoundFuture forcedRebFut) { + @Nullable final GridCompoundFuture forcedRebFut + ) { if (log.isDebugEnabled()) log.debug("Adding partition assignments: " + assigns); @@ -289,14 +291,14 @@ Runnable addAssignments(final GridDhtPreloaderAssignments assigns, rebalanceFut = fut; - fut.sendRebalanceStartedEvent(); - - for (GridCacheContext cctx : grp.caches()) { + for (final GridCacheContext cctx : grp.caches()) { if (cctx.config().isStatisticsEnabled()) { final CacheMetricsImpl metrics = cctx.cache().metrics0(); metrics.clearRebalanceCounters(); + metrics.startRebalance(0); + rebalanceFut.listen(new IgniteInClosure>() { @Override public void apply(IgniteInternalFuture fut) { metrics.clearRebalanceCounters(); @@ -305,6 +307,8 @@ Runnable addAssignments(final GridDhtPreloaderAssignments assigns, } } + fut.sendRebalanceStartedEvent(); + if (assigns.cancelled()) { // Pending exchange. if (log.isDebugEnabled()) log.debug("Rebalancing skipped due to cancelled assignments."); @@ -350,6 +354,14 @@ Runnable addAssignments(final GridDhtPreloaderAssignments assigns, }; } else if (delay > 0) { + for (GridCacheContext cctx : grp.caches()) { + if (cctx.config().isStatisticsEnabled()) { + final CacheMetricsImpl metrics = cctx.cache().metrics0(); + + metrics.startRebalance(delay); + } + } + GridTimeoutObject obj = lastTimeoutObj.get(); if (obj != null) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java index 7efd4aa2dccdf..305da92e86b7b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java @@ -306,12 +306,12 @@ private IgniteCheckedException stopError() { GridDhtPartitionDemandMessage msg = assigns.get(n); - if (msg == null) { - assigns.put(n, msg = new GridDhtPartitionDemandMessage( - top.updateSequence(), - exchId.topologyVersion(), - grp.groupId())); - } + if (msg == null) { + assigns.put(n, msg = new GridDhtPartitionDemandMessage( + top.updateSequence(), + exchId.topologyVersion(), + grp.groupId())); + } msg.addPartition(p, false); } @@ -396,11 +396,13 @@ private Collection remoteOwners(int p, AffinityTopologyVersion topV } /** {@inheritDoc} */ - @Override public Runnable addAssignments(GridDhtPreloaderAssignments assignments, + @Override public Runnable addAssignments( + GridDhtPreloaderAssignments assignments, boolean forceRebalance, int cnt, Runnable next, - @Nullable GridCompoundFuture forcedRebFut) { + @Nullable GridCompoundFuture forcedRebFut + ) { return demander.addAssignments(assignments, forceRebalance, cnt, next, forcedRebFut); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java index c15fa5f50df86..a1a855a584f25 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java @@ -21,20 +21,28 @@ import java.util.concurrent.TimeUnit; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteDataStreamer; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMetrics; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.events.CacheRebalancingEvent; import org.apache.ignite.events.Event; import org.apache.ignite.events.EventType; +import org.apache.ignite.internal.util.typedef.PA; +import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgnitePredicate; 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 static org.apache.ignite.IgniteSystemProperties.IGNITE_REBALANCE_STATISTICS_TIME_INTERVAL; +import static org.apache.ignite.testframework.GridTestUtils.runAsync; +import static org.apache.ignite.testframework.GridTestUtils.waitForCondition; + /** * */ @@ -71,6 +79,7 @@ public class CacheGroupsMetricsRebalanceTest extends GridCommonAbstractTest { .setCacheMode(CacheMode.PARTITIONED) .setAtomicityMode(CacheAtomicityMode.ATOMIC) .setRebalanceMode(CacheRebalanceMode.ASYNC) + .setRebalanceBatchSize(100) .setStatisticsEnabled(true); CacheConfiguration cfg2 = new CacheConfiguration(cfg1) @@ -137,4 +146,113 @@ public void testRebalance() throws Exception { assertTrue(ratio > 40 && ratio < 60); } + + /** + * @throws Exception If failed. + */ + public void testRebalanceEstimateFinishTime() throws Exception { + System.setProperty(IGNITE_REBALANCE_STATISTICS_TIME_INTERVAL, String.valueOf(1000)); + + Ignite ig1 = startGrid(1); + + final int KEYS = 4_000_000; + + IgniteCache cache1 = ig1.cache(CACHE1); + + try (IgniteDataStreamer st = ig1.dataStreamer(CACHE1)) { + for (int i = 0; i < KEYS; i++) + st.addData(i, CACHE1 + "-" + i); + } + + final CountDownLatch finishRebalanceLatch = new CountDownLatch(1); + + final Ignite ig2 = startGrid(2); + + ig2.events().localListen(new IgnitePredicate() { + @Override public boolean apply(Event evt) { + CacheRebalancingEvent rebEvent = (CacheRebalancingEvent)evt; + + if (rebEvent.cacheName().equals(CACHE1)) { + System.out.println("CountDown rebalance stop latch:" + rebEvent.cacheName()); + + finishRebalanceLatch.countDown(); + } + + return false; + } + }, EventType.EVT_CACHE_REBALANCE_STOPPED); + + waitForCondition(new PA() { + @Override public boolean apply() { + return ig2.cache(CACHE1).localMetrics().rebalancingStartTime() != -1L; + } + }, 5_000); + + CacheMetrics metrics = ig2.cache(CACHE1).localMetrics(); + + long startTime = metrics.rebalancingStartTime(); + + assertTrue(startTime > 0); + assertTrue((U.currentTimeMillis() - startTime) < 5000); + + final CountDownLatch latch = new CountDownLatch(1); + + runAsync(new Runnable() { + @Override public void run() { + // Waiting 25% keys will be rebalanced. + int partKeys = KEYS / 2; + + final long keysLine = (long)(partKeys - (partKeys * 0.25)); + + System.out.println("Wait until keys left will be less " + keysLine); + + while (finishRebalanceLatch.getCount() != 0) { + CacheMetrics m = ig2.cache(CACHE1).localMetrics(); + + long keyLeft = m.getKeysToRebalanceLeft(); + + if (keyLeft > 0 && keyLeft < keysLine) + latch.countDown(); + + System.out.println("Keys left: " + m.getKeysToRebalanceLeft()); + + try { + Thread.sleep(1_000); + } + catch (InterruptedException e) { + System.out.println("Interrupt thread: " + e.getMessage()); + + Thread.currentThread().interrupt(); + } + } + } + }); + + latch.await(); + + long finishTime = ig2.cache(CACHE1).localMetrics().estimateRebalancingFinishTime(); + + assertTrue(finishTime > 0); + + long timePassed = U.currentTimeMillis() - startTime; + long timeLeft = finishTime - System.currentTimeMillis(); + + assertTrue(finishRebalanceLatch.await(timeLeft + 2_000, TimeUnit.SECONDS)); + + System.out.println( + "TimePassed:" + timePassed + + "\nTimeLeft:" + timeLeft + + "\nTime to rebalance: " + (finishTime - startTime) + + "\nStartTime: " + startTime + + "\nFinishTime: " + finishTime + ); + + System.clearProperty(IGNITE_REBALANCE_STATISTICS_TIME_INTERVAL); + + System.out.println("Rebalance time:" + (U.currentTimeMillis() - startTime)); + + long diff = finishTime - U.currentTimeMillis(); + + assertTrue("Expected less 5000, Actual:" + diff, Math.abs(diff) < 10_000); + } } diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformCacheWriteMetricsTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformCacheWriteMetricsTask.java index f1d811461bf21..64ff0bc7a6ddc 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformCacheWriteMetricsTask.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformCacheWriteMetricsTask.java @@ -433,6 +433,16 @@ private static class TestCacheMetrics implements CacheMetrics { @Override public long getHeapEntriesCount() { return 59; } + + /** {@inheritDoc} */ + @Override public long estimateRebalancingFinishTime() { + return 60; + } + + /** {@inheritDoc} */ + @Override public long rebalancingStartTime() { + return 61; + } } } From c23a2dcfb1395e87cb4e14457a053c6b4727b318 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Mon, 14 Aug 2017 16:33:12 +0300 Subject: [PATCH 018/145] IGNITE-5741 - Replaced HeapByteBuffer with DirectByteBuffer in WAL records iterator - Fixes #2329. Signed-off-by: Alexey Goncharuk --- .../wal/AbstractWalRecordsIterator.java | 11 +++- .../persistence/wal/ByteBufferExpander.java | 22 +++++--- .../wal/FileWriteAheadLogManager.java | 11 ++-- .../reader/StandaloneWalRecordsIterator.java | 9 ++-- .../ignite/internal/util/GridUnsafe.java | 14 +++++ .../db/wal/crc/IgniteDataIntegrityTests.java | 52 ++++++++++++++++++- 6 files changed, 100 insertions(+), 19 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java index beed90b1c4996..db949c32e271a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java @@ -95,7 +95,6 @@ protected AbstractWalRecordsIterator( this.serializer = serializer; this.ioFactory = ioFactory; - // Do not allocate direct buffer for iterator. buf = new ByteBufferExpander(bufSize, ByteOrder.nativeOrder()); } @@ -128,6 +127,16 @@ protected static FileWriteAheadLogManager.FileDescriptor[] loadFileDescriptors(@ return curRec != null; } + /** {@inheritDoc} */ + @Override protected void onClose() throws IgniteCheckedException { + try { + buf.close(); + } + catch (Exception ex) { + throw new IgniteCheckedException(ex); + } + } + /** * Switches records iterator to the next record. *

    diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java index 829cd5c25b278..cf1db84c3feac 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java @@ -19,19 +19,24 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; +import org.apache.ignite.internal.util.GridUnsafe; /** * ByteBuffer wrapper for dynamically expand buffer size. */ -public class ByteBufferExpander { +public class ByteBufferExpander implements AutoCloseable { /** Byte buffer */ private ByteBuffer buf; + /** + * @param initSize Initial size. + * @param order Byte order. + */ public ByteBufferExpander(int initSize, ByteOrder order) { - ByteBuffer buffer = ByteBuffer.allocate(initSize); + ByteBuffer buffer = GridUnsafe.allocateBuffer(initSize); buffer.order(order); - this.buf = buffer; + buf = buffer; } /** @@ -49,16 +54,17 @@ public ByteBuffer buffer() { * @return ByteBuffer with requested size. */ public ByteBuffer expand(int size) { - ByteBuffer newBuf = ByteBuffer.allocate(size); + ByteBuffer newBuf = GridUnsafe.reallocateBuffer(buf, size); newBuf.order(buf.order()); - newBuf.put(buf); - - newBuf.flip(); - buf = newBuf; return newBuf; } + + /** {@inheritDoc} */ + @Override public void close() { + GridUnsafe.freeBuffer(buf); + } } 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 17db8f8fd8825..bb1f910189736 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 @@ -1430,12 +1430,8 @@ else if (create) */ private int readSerializerVersion(FileIO io, File file, long idx) throws IOException, IgniteCheckedException { - try { - ByteBuffer buf = ByteBuffer.allocate(RecordV1Serializer.HEADER_RECORD_SIZE); - buf.order(ByteOrder.nativeOrder()); - - FileInput in = new FileInput(io, - new ByteBufferExpander(RecordV1Serializer.HEADER_RECORD_SIZE, ByteOrder.nativeOrder())); + try (ByteBufferExpander buf = new ByteBufferExpander(RecordV1Serializer.HEADER_RECORD_SIZE, ByteOrder.nativeOrder())){ + FileInput in = new FileInput(io, buf); // Header record must be agnostic to the serializer version. WALRecord rec = serializer.readRecord(in, new FileWALPointer(idx, 0, 0)); @@ -2402,9 +2398,12 @@ private RecordsIterator( /** {@inheritDoc} */ @Override protected void onClose() throws IgniteCheckedException { + super.onClose(); + curRec = null; final ReadFileHandle handle = closeCurrentWalSegment(); + if (handle != null && handle.workDir) releaseWorkSegment(curWalSegmIdx); 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 85022ad7fd6e2..cd0f8ab3651b8 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 @@ -180,9 +180,11 @@ private List scanIndexesFromFileHeaders FileWALPointer ptr; - try (FileIO fileIO = ioFactory.create(file, "r")) { - final DataInput in = new FileInput(fileIO, - new ByteBufferExpander(HEADER_RECORD_SIZE, ByteOrder.nativeOrder())); + try ( + FileIO fileIO = ioFactory.create(file, "r"); + ByteBufferExpander buf = new ByteBufferExpander(HEADER_RECORD_SIZE, ByteOrder.nativeOrder()) + ) { + final DataInput in = new FileInput(fileIO, buf); // Header record must be agnostic to the serializer version. final int type = in.readUnsignedByte(); @@ -256,6 +258,7 @@ private List scanIndexesFromFileHeaders /** {@inheritDoc} */ @Override protected void onClose() throws IgniteCheckedException { super.onClose(); + curRec = null; closeCurrentWalSegment(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java b/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java index 0add64d9c9bdc..15e6f2c87520b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java @@ -138,6 +138,20 @@ public static void freeBuffer(ByteBuffer buf) { freeMemory(ptr); } + /** + * + * @param buf Buffer. + * @param len New length. + * @return Reallocated direct buffer. + */ + public static ByteBuffer reallocateBuffer(ByteBuffer buf, int len) { + long ptr = bufferAddress(buf); + + long newPtr = reallocateMemory(ptr, len); + + return wrapPointer(newPtr, len); + } + /** * Gets boolean value from object field. * diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java index b93c74db14760..270c560bfcef6 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java @@ -41,6 +41,9 @@ public class IgniteDataIntegrityTests extends TestCase { /** Random access file. */ private RandomAccessFile randomAccessFile; + /** Buffer expander. */ + private ByteBufferExpander expBuf; + /** {@inheritDoc} */ @Override protected void setUp() throws Exception { super.setUp(); @@ -50,9 +53,11 @@ public class IgniteDataIntegrityTests extends TestCase { randomAccessFile = new RandomAccessFile(file, "rw"); + expBuf = new ByteBufferExpander(1024, ByteOrder.BIG_ENDIAN); + fileInput = new FileInput( new RandomAccessFileIO(randomAccessFile), - new ByteBufferExpander(1024, ByteOrder.BIG_ENDIAN) + expBuf ); ByteBuffer buf = ByteBuffer.allocate(1024); @@ -70,6 +75,12 @@ public class IgniteDataIntegrityTests extends TestCase { randomAccessFile.getFD().sync(); } + /** {@inheritDoc} */ + @Override protected void tearDown() throws Exception { + randomAccessFile.close(); + expBuf.close(); + } + /** * */ @@ -107,6 +118,45 @@ public void testSkipingLastCorruptedEntry() throws Exception { } } + /** + * + */ + public void testExpandBuffer() { + ByteBufferExpander expBuf = new ByteBufferExpander(16, ByteOrder.nativeOrder()); + + ByteBuffer b1 = expBuf.buffer(); + + b1.put((byte)1); + b1.putInt(2); + b1.putLong(3L); + + assertEquals(13, b1.position()); + assertEquals(16, b1.limit()); + + ByteBuffer b2 = expBuf.expand(32); + + assertEquals(0, b2.position()); + assertEquals((byte)1, b2.get()); + assertEquals(2, b2.getInt()); + assertEquals(3L, b2.getLong()); + assertEquals(13, b2.position()); + assertEquals(32, b2.limit()); + + b2.putInt(4); + + assertEquals(17, b2.position()); + assertEquals(32, b2.limit()); + + b2.flip(); + + assertEquals(0, b2.position()); + assertEquals((byte)1, b2.get()); + assertEquals(2, b2.getInt()); + assertEquals(3L, b2.getLong()); + assertEquals(4, b2.getInt()); + assertEquals(17, b2.limit()); + } + /** * @param rangeFrom Range from. * @param rangeTo Range to. From 2f38065cd10fd61d51771d14188380dc7cc74ed7 Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Mon, 14 Aug 2017 16:44:50 +0300 Subject: [PATCH 019/145] GG-12629 Backport IGNITE-5961 to 8.1.4 --- .../internal/pagemem/store/PageStore.java | 5 + .../cache/persistence/file/FilePageStore.java | 56 ++++++--- .../file/FilePageStoreFactory.java | 35 ++++++ .../file/FilePageStoreManager.java | 17 +-- .../persistence/file/FilePageStoreV2.java | 53 ++++++++ .../file/FileVersionCheckingFactory.java | 116 ++++++++++++++++++ ...itePdsRecoveryAfterFileCorruptionTest.java | 2 +- 7 files changed, 252 insertions(+), 32 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreFactory.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreV2.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/PageStore.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/PageStore.java index 4698a6b116fb0..f6e577ce3916e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/PageStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/PageStore.java @@ -95,4 +95,9 @@ public interface PageStore { * @throws IgniteCheckedException If sync failed (IO error occurred). */ public void ensure() throws IgniteCheckedException; + + /** + * @return Page store version. + */ + public int version(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java index a7ca13c2d1e62..e6c5379a4b5b9 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java @@ -45,10 +45,10 @@ public class FilePageStore implements PageStore { private static final long SIGNATURE = 0xF19AC4FE60C530B8L; /** File version. */ - private static final int VERSION = 1; + public static final int VERSION = 1; /** Allocated field offset. */ - public static final int HEADER_SIZE = 8/*SIGNATURE*/ + 4/*VERSION*/ + 1/*type*/ + 4/*page size*/; + static final int HEADER_SIZE = 8/*SIGNATURE*/ + 4/*VERSION*/ + 1/*type*/ + 4/*page size*/; /** */ private final File cfgFile; @@ -57,7 +57,7 @@ public class FilePageStore implements PageStore { private final byte type; /** Database configuration. */ - private final MemoryConfiguration dbCfg; + protected final MemoryConfiguration dbCfg; /** Factory to provide I/O interfaces for read/write operations with files */ private final FileIOFactory ioFactory; @@ -103,20 +103,36 @@ public FilePageStore(byte type, File file, FileIOFactory factory, MemoryConfigur /** {@inheritDoc} */ @Override public boolean exists() { - return cfgFile.exists() && cfgFile.length() > HEADER_SIZE; + return cfgFile.exists() && cfgFile.length() > headerSize(); } /** + * Size of page store header. + */ + public int headerSize() { + return HEADER_SIZE; + } + + /** + * Page store version. + */ + public int version() { + return VERSION; + } + + /** + * Creates header for current version file store. Doesn't init the store. + * * @param type Type. * @param pageSize Page size. * @return Byte buffer instance. */ - public static ByteBuffer header(byte type, int pageSize) { - ByteBuffer hdr = ByteBuffer.allocate(HEADER_SIZE).order(ByteOrder.LITTLE_ENDIAN); + public ByteBuffer header(byte type, int pageSize) { + ByteBuffer hdr = ByteBuffer.allocate(headerSize()).order(ByteOrder.LITTLE_ENDIAN); hdr.putLong(SIGNATURE); - hdr.putInt(VERSION); + hdr.putInt(version()); hdr.put(type); @@ -142,7 +158,7 @@ private long initFile() { } //there is 'super' page in every file - return HEADER_SIZE + dbCfg.getPageSize(); + return headerSize() + dbCfg.getPageSize(); } /** @@ -150,7 +166,7 @@ private long initFile() { */ private long checkFile() throws IgniteCheckedException { try { - ByteBuffer hdr = ByteBuffer.allocate(HEADER_SIZE).order(ByteOrder.LITTLE_ENDIAN); + ByteBuffer hdr = ByteBuffer.allocate(headerSize()).order(ByteOrder.LITTLE_ENDIAN); while (hdr.remaining() > 0) fileIO.read(hdr); @@ -166,9 +182,9 @@ private long checkFile() throws IgniteCheckedException { int ver = hdr.getInt(); - if (VERSION != ver) + if (version() != ver) throw new IgniteCheckedException("Failed to verify store file (invalid file version)" + - " [expectedVersion=" + VERSION + + " [expectedVersion=" + version() + ", fileVersion=" + ver + "]"); byte type = hdr.get(); @@ -187,10 +203,10 @@ private long checkFile() throws IgniteCheckedException { long fileSize = cfgFile.length(); - if (fileSize == HEADER_SIZE) // Every file has a special meta page. - fileSize = pageSize + HEADER_SIZE; + if (fileSize == headerSize()) // Every file has a special meta page. + fileSize = pageSize + headerSize(); - if ((fileSize - HEADER_SIZE) % pageSize != 0) + if ((fileSize - headerSize()) % pageSize != 0) throw new IgniteCheckedException("Failed to verify store file (invalid file size)" + " [fileSize=" + U.hexLong(fileSize) + ", pageSize=" + U.hexLong(pageSize) + ']'); @@ -346,9 +362,9 @@ public void finishRecover() { init(); try { - assert buf.remaining() == HEADER_SIZE; + assert buf.remaining() == headerSize(); - int len = HEADER_SIZE; + int len = headerSize(); long off = 0; @@ -425,7 +441,7 @@ private void init() throws IgniteCheckedException { long off = pageOffset(pageId); - assert (off >= 0 && off + pageSize <= allocated.get() + HEADER_SIZE) || recover : + assert (off >= 0 && off + pageSize <= allocated.get() + headerSize()) || recover : "off=" + U.hexLong(off) + ", allocated=" + U.hexLong(allocated.get()) + ", pageId=" + U.hexLong(pageId); assert pageBuf.capacity() == pageSize; @@ -463,7 +479,7 @@ private void init() throws IgniteCheckedException { /** {@inheritDoc} */ @Override public long pageOffset(long pageId) { - return (long) PageIdUtils.pageIndex(pageId) * pageSize + HEADER_SIZE; + return (long) PageIdUtils.pageIndex(pageId) * pageSize + headerSize(); } /** {@inheritDoc} */ @@ -494,7 +510,7 @@ private void init() throws IgniteCheckedException { long off = allocPage(); - return off / pageSize; + return (off - headerSize()) / pageSize; } /** @@ -519,6 +535,6 @@ private long allocPage() { if (!inited) return 0; - return (int)(allocated.get() / pageSize); + return (int)(allocated.get() - headerSize()) / pageSize; } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreFactory.java new file mode 100644 index 0000000000000..d97ab26397a03 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreFactory.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.persistence.file; + +import java.io.File; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.pagemem.PageIdAllocator; + +/** + * + */ +public interface FilePageStoreFactory { + /** + * Creates instance of FilePageStore based on given file. + * + * @param type Data type, can be {@link PageIdAllocator#FLAG_IDX} or {@link PageIdAllocator#FLAG_DATA}. + * @param file File Page store file. + */ + public FilePageStore createPageStore(byte type, File file) throws IgniteCheckedException; +} 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 e2ad070741e6f..0041ea61d4991 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 @@ -365,21 +365,16 @@ private CacheStoreHolder initForCache(CacheGroupDescriptor grpDesc, CacheConfigu if (dirExisted && !idxFile.exists()) grpsWithoutIdx.add(grpDesc.groupId()); - FilePageStore idxStore = new FilePageStore( - PageMemory.FLAG_IDX, - idxFile, - pstCfg.getFileIOFactory(), - cctx.kernalContext().config().getMemoryConfiguration()); + FileVersionCheckingFactory pageStoreFactory = new FileVersionCheckingFactory( + pstCfg.getFileIOFactory(), igniteCfg.getMemoryConfiguration()); + + FilePageStore idxStore = pageStoreFactory.createPageStore(PageMemory.FLAG_IDX, idxFile); FilePageStore[] partStores = new FilePageStore[grpDesc.config().getAffinity().partitions()]; for (int partId = 0; partId < partStores.length; partId++) { - FilePageStore partStore = new FilePageStore( - PageMemory.FLAG_DATA, - new File(cacheWorkDir, String.format(PART_FILE_TEMPLATE, partId)), - pstCfg.getFileIOFactory(), - cctx.kernalContext().config().getMemoryConfiguration() - ); + FilePageStore partStore = pageStoreFactory.createPageStore( + PageMemory.FLAG_DATA, new File(cacheWorkDir, String.format(PART_FILE_TEMPLATE, partId))); partStores[partId] = partStore; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreV2.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreV2.java new file mode 100644 index 0000000000000..5d044ec6b60e5 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStoreV2.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.persistence.file; + +import java.io.File; +import org.apache.ignite.configuration.MemoryConfiguration; + +/** + * + */ +public class FilePageStoreV2 extends FilePageStore { + /** File version. */ + public static final int VERSION = 2; + + /** Header size. */ + private final int hdrSize; + + /** + * @param type Type. + * @param file File. + * @param factory Factory. + * @param cfg Config. + */ + public FilePageStoreV2(byte type, File file, FileIOFactory factory, MemoryConfiguration cfg) { + super(type, file, factory, cfg); + + hdrSize = cfg.getPageSize(); + } + + /** {@inheritDoc} */ + @Override public int headerSize() { + return hdrSize; + } + + /** {@inheritDoc} */ + @Override public int version() { + return VERSION; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java new file mode 100644 index 0000000000000..53bd802c77aae --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java @@ -0,0 +1,116 @@ +/* +* 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.file; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.configuration.MemoryConfiguration; + +/** + * Checks version in files if it's present on the disk, creates store with latest version otherwise. + */ +public class FileVersionCheckingFactory implements FilePageStoreFactory { + /** Property to override latest version. Should be used only in tests. */ + public static final String LATEST_VERSION_OVERRIDE_PROPERTY = "file.page.store.latest.version.override"; + + /** Latest page store version. */ + public final static int LATEST_VERSION = 2; + + /** Factory to provide I/O interfaces for read/write operations with files. */ + private final FileIOFactory fileIOFactory; + + /** Memory configuration. */ + private final MemoryConfiguration memCfg; + + /** + * @param fileIOFactory File io factory. + * @param memCfg Memory configuration. + */ + public FileVersionCheckingFactory( + FileIOFactory fileIOFactory, MemoryConfiguration memCfg) { + this.fileIOFactory = fileIOFactory; + this.memCfg = memCfg; + } + + /** {@inheritDoc} */ + @Override public FilePageStore createPageStore(byte type, File file) throws IgniteCheckedException { + if (!file.exists()) + return createPageStore(type, file, latestVersion()); + + try (FileIO fileIO = fileIOFactory.create(file, "r")) { + int minHdr = FilePageStore.HEADER_SIZE; + + if (fileIO.size() < minHdr) + return createPageStore(type, file, latestVersion()); + + ByteBuffer hdr = ByteBuffer.allocate(minHdr).order(ByteOrder.LITTLE_ENDIAN); + + while (hdr.remaining() > 0) + fileIO.read(hdr); + + hdr.rewind(); + + hdr.getLong(); // Read signature + + int ver = hdr.getInt(); + + return createPageStore(type, file, ver); + } + catch (IOException e) { + throw new IgniteCheckedException("Error while creating file page store [file=" + file + "]:", e); + } + } + + /** + * Resolves latest page store version. + */ + public int latestVersion() { + int latestVer = LATEST_VERSION; + + try { + latestVer = Integer.parseInt(System.getProperty(LATEST_VERSION_OVERRIDE_PROPERTY)); + } catch (NumberFormatException e) { + // No override. + } + + return latestVer; + } + + /** + * Instantiates specific version of FilePageStore. + * + * @param type Type. + * @param file File. + * @param ver Version. + */ + public FilePageStore createPageStore(byte type, File file, int ver) throws IgniteCheckedException { + switch (ver) { + case FilePageStore.VERSION: + return new FilePageStore(type, file, fileIOFactory, memCfg); + + case FilePageStoreV2.VERSION: + return new FilePageStoreV2(type, file, fileIOFactory, memCfg); + + default: + throw new IllegalArgumentException("Unknown version of file page store: " + ver); + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsRecoveryAfterFileCorruptionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsRecoveryAfterFileCorruptionTest.java index c248c35d19020..11d5eef9926ba 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsRecoveryAfterFileCorruptionTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsRecoveryAfterFileCorruptionTest.java @@ -194,7 +194,7 @@ private void eraseDataFromDisk( long size = fileIO.size(); - fileIO.write(ByteBuffer.allocate((int)size - FilePageStore.HEADER_SIZE), FilePageStore.HEADER_SIZE); + fileIO.write(ByteBuffer.allocate((int)size - filePageStore.headerSize()), filePageStore.headerSize()); fileIO.force(); } From cdac5a87cb1432ffd0ec32a2888505805e7348da Mon Sep 17 00:00:00 2001 From: EdShangGG Date: Mon, 14 Aug 2017 16:56:11 +0300 Subject: [PATCH 020/145] IGNITE-5843 Persist cache configuration received on node join - Fixes #2347. --- .../cache/CacheAffinitySharedManager.java | 56 ++++++++++++++----- .../GridCacheDatabaseSharedManager.java | 15 +++-- .../file/FilePageStoreManager.java | 1 - .../processors/query/GridQueryProcessor.java | 15 ----- .../IgnitePdsDynamicCacheTest.java | 43 ++++++++++++++ 5 files changed, 92 insertions(+), 38 deletions(-) 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 d251d52a9a7bf..9fc791e561870 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 @@ -720,13 +720,6 @@ public void onCacheChangeRequest( } try { - // Save configuration before cache started. - if (cctx.pageStore() != null && !cctx.kernalContext().clientNode()) { - cctx.pageStore().storeCacheData( - new StoredCacheData(req.startCacheConfiguration()) - ); - } - if (startCache) { cctx.cache().prepareCacheStart(req.startCacheConfiguration(), cacheDesc, @@ -2157,7 +2150,7 @@ void add(Integer grpId, Integer part, UUID waitNode, List assignmen /** * */ - static class CachesInfo { + class CachesInfo { /** Registered cache groups (updated from exchange thread). */ private final ConcurrentHashMap registeredGrps = new ConcurrentHashMap<>(); @@ -2170,10 +2163,29 @@ static class CachesInfo { */ void init(Map grps, Map caches) { for (CacheGroupDescriptor grpDesc : grps.values()) - registeredGrps.put(grpDesc.groupId(), grpDesc); + registerGroup(grpDesc); for (DynamicCacheDescriptor cacheDesc : caches.values()) - registeredCaches.put(cacheDesc.cacheId(), cacheDesc); + registerCache(cacheDesc); + } + + + /** + * @param desc Description. + */ + private DynamicCacheDescriptor registerCache(DynamicCacheDescriptor desc) { + saveCacheConfiguration(desc.cacheConfiguration()); + + return registeredCaches.put(desc.cacheId(), desc); + } + + /** + * @param grpDesc Group description. + */ + private CacheGroupDescriptor registerGroup(CacheGroupDescriptor grpDesc) { + saveCacheConfiguration(grpDesc.config()); + + return registeredGrps.put(grpDesc.groupId(), grpDesc); } /** @@ -2203,10 +2215,10 @@ void initStartedCaches(Collection descs) { CacheGroupDescriptor grpDesc = desc.groupDescriptor(); if (!registeredGrps.containsKey(grpDesc.groupId())) - registeredGrps.put(grpDesc.groupId(), grpDesc); + registerGroup(grpDesc); if (!registeredCaches.containsKey(desc.cacheId())) - registeredCaches.put(desc.cacheId(), desc); + registerCache(desc); } } @@ -2221,7 +2233,7 @@ void updateCachesInfo(ExchangeActions exchActions) { } for (ExchangeActions.CacheGroupActionData startAction : exchActions.cacheGroupsToStart()) { - CacheGroupDescriptor old = registeredGrps.put(startAction.descriptor().groupId(), startAction.descriptor()); + CacheGroupDescriptor old = registerGroup(startAction.descriptor()); assert old == null : old; } @@ -2230,7 +2242,7 @@ void updateCachesInfo(ExchangeActions exchActions) { registeredCaches.remove(req.descriptor().cacheId()); for (ExchangeActions.CacheActionData req : exchActions.cacheStartRequests()) - registeredCaches.put(req.descriptor().cacheId(), req.descriptor()); + registerCache(req.descriptor()); } /** @@ -2250,4 +2262,20 @@ void clear() { registeredCaches.clear(); } } + + /** + * @param cfg cache configuration + */ + private void saveCacheConfiguration(CacheConfiguration cfg) { + if (cctx.pageStore() != null && cctx.database().persistenceEnabled() && !cctx.kernalContext().clientNode()) { + try { + cctx.pageStore().storeCacheData( + new StoredCacheData(cfg) + ); + } + catch (IgniteCheckedException e) { + U.error(log(), "Error while saving cache configuration on disk, cfg = " + cfg, e); + } + } + } } 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 a1eb4af96037b..56dcac042fab2 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 @@ -56,9 +56,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.management.InstanceNotFoundException; -import javax.management.JMException; -import javax.management.MBeanRegistrationException; import javax.management.ObjectName; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; @@ -98,6 +95,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionMetaStateRecord; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheGroupContext; +import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor; import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor; import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; @@ -1453,11 +1451,12 @@ private PageMemoryEx getPageMemoryForCacheGroup(int grpId) throws IgniteCheckedE // TODO IGNITE-5075: cache descriptor can be removed. GridCacheSharedContext sharedCtx = context(); - String memPlcName = sharedCtx - .cache() - .cacheGroupDescriptors().get(grpId) - .config() - .getMemoryPolicyName(); + CacheGroupDescriptor desc = sharedCtx.cache().cacheGroupDescriptors().get(grpId); + + if (desc == null) + throw new IgniteCheckedException("Failed to find cache group descriptor [grpId=" + grpId + ']'); + + String memPlcName = desc.config().getMemoryPolicyName(); return (PageMemoryEx)sharedCtx.database().memoryPolicy(memPlcName).pageMemory(); } 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 0041ea61d4991..13bcd2a9d9df1 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 @@ -210,7 +210,6 @@ public FilePageStoreManager(GridKernalContext ctx) { @Override public void storeCacheData( StoredCacheData cacheData ) throws IgniteCheckedException { - File cacheWorkDir = cacheWorkDirectory(cacheData.config()); File file; 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 d44be2c1df07f..baafb1e8d5f02 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 @@ -504,21 +504,6 @@ private void onSchemaFinishDiscovery(SchemaFinishDiscoveryMessage msg) { if (cacheDesc != null && F.eq(cacheDesc.deploymentId(), proposeMsg.deploymentId())) cacheDesc.schemaChangeFinish(msg); - - if (ctx.cache().context().pageStore() != null && - ctx.cache().context().database().persistenceEnabled()) { - - StoredCacheData cacheData = new StoredCacheData(cacheDesc.cacheConfiguration()); - - cacheData.queryEntities(cacheDesc.schema().entities()); - - try { - ctx.cache().context().pageStore().storeCacheData(cacheData); - } - catch (IgniteCheckedException e) { - throw new IllegalStateException("Failed to persist cache data: " + cacheData.config().getName(), e); - } - } } // Propose message will be used from exchange thread to diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDynamicCacheTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDynamicCacheTest.java index 189b86620bb81..5873a355822e4 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDynamicCacheTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDynamicCacheTest.java @@ -19,6 +19,7 @@ import java.io.Serializable; import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMode; @@ -159,6 +160,48 @@ public void testRestartAndCreate() throws Exception { } } + /** + * @throws Exception If failed. + */ + public void testDynamicCacheSavingOnNewNode() throws Exception { + Ignite ignite = startGrid(0); + + ignite.active(true); + + CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME); + + ccfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); + ccfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); + ccfg.setRebalanceMode(CacheRebalanceMode.SYNC); + ccfg.setAffinity(new RendezvousAffinityFunction(false, 32)); + + IgniteCache cache = ignite.getOrCreateCache(ccfg); + + for (int i = 0; i < 160; i++) + cache.put(i, i); + + ignite = startGrid(1); + + awaitPartitionMapExchange(); + + cache = ignite.cache(DEFAULT_CACHE_NAME); + + for (int i = 0; i < 160; i++) + assertEquals(i, cache.get(i)); + + stopAllGrids(true); + + startGrid(0); + ignite = startGrid(1); + + ignite.active(true); + + cache = ignite.cache(DEFAULT_CACHE_NAME); + + for (int i = 0; i < 160; i++) + assertEquals(i, cache.get(i)); + } + /** * @throws IgniteCheckedException If failed. */ From 305c0f4ffb745bdc04cd0a6f3b45dbd9ff5da302 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Mon, 14 Aug 2017 17:47:11 +0300 Subject: [PATCH 021/145] IGNITE-6052 request cluster state from daemon node via compute grid - Fixes #2439. Signed-off-by: Alexey Goncharuk --- .../cluster/GridClusterStateProcessor.java | 64 ++++++++++++++++--- .../IgniteStandByClusterTest.java | 48 +++++++++++--- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java index 283a58fd4272b..13a889cd68991 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java @@ -55,6 +55,7 @@ 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.IgniteCallable; import org.apache.ignite.lang.IgniteFuture; import org.apache.ignite.lang.IgniteRunnable; import org.apache.ignite.resources.IgniteInstanceResource; @@ -119,6 +120,9 @@ public GridClusterStateProcessor(GridKernalContext ctx) { * @return Cluster state to be used on public API. */ public boolean publicApiActiveState() { + if (ctx.isDaemon()) + return sendComputeCheckGlobalState(); + DiscoveryDataClusterState globalState = this.globalState; assert globalState != null; @@ -407,7 +411,7 @@ public IgniteInternalFuture changeGlobalState(final boolean activate) { if (ctx.isDaemon() || ctx.clientNode()) { GridFutureAdapter fut = new GridFutureAdapter<>(); - sendCompute(activate, fut); + sendComputeChangeGlobalState(activate, fut); return fut; } @@ -490,11 +494,9 @@ public IgniteInternalFuture changeGlobalState(final boolean activate) { * @param activate New cluster state. * @param resFut State change future. */ - private void sendCompute(boolean activate, final GridFutureAdapter resFut) { + private void sendComputeChangeGlobalState(boolean activate, final GridFutureAdapter resFut) { AffinityTopologyVersion topVer = ctx.discovery().topologyVersionEx(); - IgniteCompute comp = ((ClusterGroupAdapter)ctx.cluster().get().forServers()).compute(); - if (log.isInfoEnabled()) { log.info("Sending " + prettyStr(activate) + " request from node [id=" + ctx.localNodeId() + ", topVer=" + topVer + @@ -502,7 +504,9 @@ private void sendCompute(boolean activate, final GridFutureAdapter resFut) ", daemon" + ctx.isDaemon() + "]"); } - IgniteFuture fut = comp.runAsync(new ClientChangeGlobalStateComputeRequest(activate)); + IgniteCompute comp = ((ClusterGroupAdapter)ctx.cluster().get().forServers()).compute(); + + IgniteFuture fut = comp.runAsync(new ChangeGlobalStateComputeRequest(activate)); fut.listen(new CI1() { @Override public void apply(IgniteFuture fut) { @@ -518,6 +522,32 @@ private void sendCompute(boolean activate, final GridFutureAdapter resFut) }); } + /** + * Check cluster state. + * + * @return Cluster state, {@code True} if cluster active, {@code False} if inactive. + */ + private boolean sendComputeCheckGlobalState() { + AffinityTopologyVersion topVer = ctx.discovery().topologyVersionEx(); + + if (log.isInfoEnabled()) { + log.info("Sending check cluster state request from node [id=" + ctx.localNodeId() + + ", topVer=" + topVer + + ", client=" + ctx.clientNode() + + ", daemon" + ctx.isDaemon() + "]"); + } + IgniteCompute comp = ((ClusterGroupAdapter)ctx.cluster().get().forServers()).compute(); + + return comp.call(new IgniteCallable() { + @IgniteInstanceResource + private Ignite ig; + + @Override public Boolean call() throws Exception { + return ig.active(); + } + }); + } + /** * @param errs Errors. * @param req State change request. @@ -854,7 +884,7 @@ private void onAllReceived() { /** * */ - private static class ClientChangeGlobalStateComputeRequest implements IgniteRunnable { + private static class ChangeGlobalStateComputeRequest implements IgniteRunnable { /** */ private static final long serialVersionUID = 0L; @@ -863,18 +893,34 @@ private static class ClientChangeGlobalStateComputeRequest implements IgniteRunn /** Ignite. */ @IgniteInstanceResource - private Ignite ignite; + private Ignite ig; /** * @param activate New cluster state. */ - private ClientChangeGlobalStateComputeRequest(boolean activate) { + private ChangeGlobalStateComputeRequest(boolean activate) { this.activate = activate; } /** {@inheritDoc} */ @Override public void run() { - ignite.active(activate); + ig.active(activate); + } + } + + /** + * + */ + private static class CheckGlobalStateComputeRequest implements IgniteCallable { + /** */ + private static final long serialVersionUID = 0L; + + /** Ignite. */ + @IgniteInstanceResource + private Ignite ig; + + @Override public Boolean call() throws Exception { + return ig.active(); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteStandByClusterTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteStandByClusterTest.java index 30fff0843a18e..c2bece0d0355f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteStandByClusterTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteStandByClusterTest.java @@ -249,6 +249,36 @@ public void testJoinDaemonAndDaemonStop() throws Exception { daemon.close(); } + /** + * @throws Exception if fail. + */ + public void testCheckStatusFromDaemon() throws Exception { + IgniteEx ig = startGrid(0); + + assertFalse(ig.active()); + + ig.active(true); + + IgniteEx daemon = startGrid( + getConfiguration("daemon") + .setDaemon(true) + .setClientMode(true) + ); + + assertTrue(ig.active()); + assertTrue(daemon.active()); + + daemon.active(false); + + assertFalse(ig.active()); + assertFalse(daemon.active()); + + daemon.active(true); + + assertTrue(ig.active()); + assertTrue(daemon.active()); + } + /** * @throws Exception if fail. */ @@ -298,25 +328,25 @@ public void testActivateDeActivateCallbackForPluginProviders() throws Exception ig1.active(true); - checkPlugin(ig1,1,0); - checkPlugin(ig2,1,0); - checkPlugin(ig3,1,0); + checkPlugin(ig1, 1, 0); + checkPlugin(ig2, 1, 0); + checkPlugin(ig3, 1, 0); ig2.active(false); ig3.active(true); - checkPlugin(ig1,2,1); - checkPlugin(ig2,2,1); - checkPlugin(ig3,2,1); + checkPlugin(ig1, 2, 1); + checkPlugin(ig2, 2, 1); + checkPlugin(ig3, 2, 1); ig1.active(false); ig2.active(true); - checkPlugin(ig1,3,2); - checkPlugin(ig2,3,2); - checkPlugin(ig3,3,2); + checkPlugin(ig1, 3, 2); + checkPlugin(ig2, 3, 2); + checkPlugin(ig3, 3, 2); } From 621380dbec5d1b9dcff626bb1686ad75361f7ed8 Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Tue, 15 Aug 2017 11:19:37 +0300 Subject: [PATCH 022/145] IGNITE-6004: Added QuerySqlField.inlineSize property. This closes #2420. --- .../org/apache/ignite/cache/QueryEntity.java | 15 +- .../org/apache/ignite/cache/QueryIndex.java | 5 +- .../query/annotations/QueryGroupIndex.java | 20 +++ .../query/annotations/QuerySqlField.java | 23 +++ .../query/QueryEntityTypeDescriptor.java | 17 +- ...dCacheQuerySqlFieldInlineSizeSelfTest.java | 160 ++++++++++++++++++ .../IgniteCacheQuerySelfTestSuite.java | 3 + 7 files changed, 226 insertions(+), 17 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheQuerySqlFieldInlineSizeSelfTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java index 5180100fe4c24..6440b4825a621 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java @@ -419,6 +419,7 @@ private static QueryEntity convert(QueryEntityTypeDescriptor desc) { sortedIdx.setFields(fields); sortedIdx.setName(idxEntry.getKey()); + sortedIdx.setInlineSize(idx.inlineSize()); idxs.add(sortedIdx); } @@ -480,7 +481,7 @@ private static void processAnnotationsInClass(boolean key, Class cls, QueryEn String idxName = cls.getSimpleName() + "_" + QueryUtils.VAL_FIELD_NAME + "_idx"; type.addIndex(idxName, QueryUtils.isGeometryClass(cls) ? - QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); + QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED, QueryIndex.DFLT_INLINE_SIZE); type.addFieldToIndex(idxName, QueryUtils.VAL_FIELD_NAME, 0, false); } @@ -500,13 +501,13 @@ private static void processAnnotationsInClass(boolean key, Class cls, QueryEn QueryGroupIndex grpIdx = cls.getAnnotation(QueryGroupIndex.class); if (grpIdx != null) - type.addIndex(grpIdx.name(), QueryIndexType.SORTED); + type.addIndex(grpIdx.name(), QueryIndexType.SORTED, grpIdx.inlineSize()); QueryGroupIndex.List grpIdxList = cls.getAnnotation(QueryGroupIndex.List.class); if (grpIdxList != null && !F.isEmpty(grpIdxList.value())) { for (QueryGroupIndex idx : grpIdxList.value()) - type.addIndex(idx.name(), QueryIndexType.SORTED); + type.addIndex(idx.name(), QueryIndexType.SORTED, idx.inlineSize()); } } @@ -559,11 +560,17 @@ private static void processAnnotation(boolean key, QuerySqlField sqlAnn, QueryTe idxName = cls.getSimpleName() + "_" + idxName; desc.addIndex(idxName, QueryUtils.isGeometryClass(prop.type()) ? - QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); + QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED, sqlAnn.inlineSize()); desc.addFieldToIndex(idxName, prop.fullName(), 0, sqlAnn.descending()); } + if ((!F.isEmpty(sqlAnn.groups()) || !F.isEmpty(sqlAnn.orderedGroups())) + && sqlAnn.inlineSize() != QueryIndex.DFLT_INLINE_SIZE) { + throw new CacheException("Inline size cannot be set on a field with group index [" + + "type=" + cls.getName() + ", property=" + prop.fullName() + ']'); + } + if (!F.isEmpty(sqlAnn.groups())) { for (String group : sqlAnn.groups()) desc.addFieldToIndex(group, prop.fullName(), 0, false); diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java index 6e73049ada3ff..ac7dd8eb81ed6 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryIndex.java @@ -38,6 +38,9 @@ public class QueryIndex implements Serializable { /** */ private static final QueryIndexType DFLT_IDX_TYP = QueryIndexType.SORTED; + /** Default index inline size. */ + public static final int DFLT_INLINE_SIZE = -1; + /** Index name. */ private String name; @@ -49,7 +52,7 @@ public class QueryIndex implements Serializable { private QueryIndexType type = DFLT_IDX_TYP; /** */ - private int inlineSize = -1; + private int inlineSize = DFLT_INLINE_SIZE; /** * Creates an empty index. Should be populated via setters. diff --git a/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QueryGroupIndex.java b/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QueryGroupIndex.java index b1fefaeb94563..a947d0f2f8da4 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QueryGroupIndex.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QueryGroupIndex.java @@ -21,6 +21,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.apache.ignite.cache.QueryIndex; +import org.apache.ignite.configuration.CacheConfiguration; /** * Describes group index. @@ -35,6 +37,24 @@ */ String name(); + /** + * Index inline size in bytes. When enabled part of indexed value will be placed directly to index pages, + * thus minimizing data page accesses, thus incraesing query performance. + *

    + * Allowed values: + *

      + *
    • {@code -1} (default) - determine inline size automatically (see below)
    • + *
    • {@code 0} - index inline is disabled (not recommended)
    • + *
    • positive value - fixed index inline
    • + *
    + * When set to {@code -1}, Ignite will try to detect inline size automatically. It will be no more than + * {@link CacheConfiguration#getSqlIndexMaxInlineSize()}. Index inline will be enabled for all fixed-length types, + * but will not be enabled for {@code String}. + * + * @return Index inline size in bytes. + */ + int inlineSize() default QueryIndex.DFLT_INLINE_SIZE; + /** * List of group indexes for type. */ diff --git a/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QuerySqlField.java b/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QuerySqlField.java index 94dbea1013e5d..64aaa3a5c4398 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QuerySqlField.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/query/annotations/QuerySqlField.java @@ -22,6 +22,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.apache.ignite.cache.QueryIndex; +import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.internal.processors.cache.query.CacheQuery; /** @@ -87,6 +89,27 @@ */ String name() default ""; + /** + * Index inline size in bytes. When enabled part of indexed value will be placed directly to index pages, + * thus minimizing data page accesses, thus incraesing query performance. + *

    + * Allowed values: + *

      + *
    • {@code -1} (default) - determine inline size automatically (see below)
    • + *
    • {@code 0} - index inline is disabled (not recommended)
    • + *
    • positive value - fixed index inline
    • + *
    + * When set to {@code -1}, Ignite will try to detect inline size automatically. It will be no more than + * {@link CacheConfiguration#getSqlIndexMaxInlineSize()}. Index inline will be enabled for all fixed-length types, + * but will not be enabled for {@code String}. + *

    + * When index group is used, inline size must be defined in {@link QueryGroupIndex#inlineSize()}. Any value + * except of {@code -1} defined on a specific column will lead to exception. + * + * @return Index inline size in bytes. + */ + int inlineSize() default QueryIndex.DFLT_INLINE_SIZE; + /** * Describes group of index and position of field in this group. *

    diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java index 47ab263eb216a..837a08f7d260d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Set; import javax.cache.CacheException; +import org.apache.ignite.cache.QueryIndex; import org.apache.ignite.cache.QueryIndexType; import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; import org.apache.ignite.internal.util.tostring.GridToStringExclude; @@ -78,6 +79,9 @@ public Map indexes() { * @return Index descriptor. */ public QueryEntityIndexDescriptor addIndex(String idxName, QueryIndexType type, int inlineSize) { + if (inlineSize < 0 && inlineSize != QueryIndex.DFLT_INLINE_SIZE) + throw new CacheException("Illegal inline size [idxName=" + idxName + ", inlineSize=" + inlineSize + ']'); + QueryEntityIndexDescriptor idx = new QueryEntityIndexDescriptor(type, inlineSize); if (indexes.put(idxName, idx) != null) @@ -86,17 +90,6 @@ public QueryEntityIndexDescriptor addIndex(String idxName, QueryIndexType type, return idx; } - /** - * Adds index. - * - * @param idxName Index name. - * @param type Index type. - * @return Index descriptor. - */ - public QueryEntityIndexDescriptor addIndex(String idxName, QueryIndexType type) { - return addIndex(idxName, type, -1); - } - /** * Adds field to index. * @@ -110,7 +103,7 @@ public void addFieldToIndex(String idxName, String field, int orderNum, QueryEntityIndexDescriptor desc = indexes.get(idxName); if (desc == null) - desc = addIndex(idxName, QueryIndexType.SORTED); + desc = addIndex(idxName, QueryIndexType.SORTED, QueryIndex.DFLT_INLINE_SIZE); desc.addField(field, orderNum, descending); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheQuerySqlFieldInlineSizeSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheQuerySqlFieldInlineSizeSelfTest.java new file mode 100644 index 0000000000000..a2571602471d4 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheQuerySqlFieldInlineSizeSelfTest.java @@ -0,0 +1,160 @@ +/* + * 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.concurrent.Callable; +import javax.cache.CacheException; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.QueryIndex; +import org.apache.ignite.cache.query.annotations.QueryGroupIndex; +import org.apache.ignite.cache.query.annotations.QuerySqlField; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * Tests cache configuration with inlineSize property of the QuerySqlField annotation. + */ +@SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "unchecked", "unused"}) +public class GridCacheQuerySqlFieldInlineSizeSelfTest extends GridCommonAbstractTest { + /** + * @throws Exception If failed. + */ + public void testSingleFieldIndexes() throws Exception { + CacheConfiguration ccfg = defaultCacheConfiguration(); + + ccfg.setIndexedTypes(Integer.class, TestValueSingleFieldIndexes.class); + + assertEquals(1, ccfg.getQueryEntities().size()); + + QueryEntity ent = (QueryEntity)ccfg.getQueryEntities().iterator().next(); + + assertEquals(2, ent.getIndexes().size()); + + for (QueryIndex idx : ent.getIndexes()) { + if(idx.getFields().containsKey("val0")) + assertEquals(10, idx.getInlineSize()); + else if(idx.getFields().containsKey("val1")) + assertEquals(20, idx.getInlineSize()); + } + } + + /** + * @throws Exception If failed. + */ + public void testGroupIndex() throws Exception { + CacheConfiguration ccfg = defaultCacheConfiguration(); + + ccfg.setIndexedTypes(Integer.class, TestValueGroupIndex.class); + + assertEquals(1, ccfg.getQueryEntities().size()); + + QueryEntity ent = (QueryEntity)ccfg.getQueryEntities().iterator().next(); + + assertEquals(1, ent.getIndexes().size()); + + QueryIndex idx = ent.getIndexes().iterator().next(); + + assertEquals(10, idx.getInlineSize()); + } + + /** + * @throws Exception If failed. + */ + public void testGroupIndexInvalidAnnotaion() throws Exception { + final CacheConfiguration ccfg = defaultCacheConfiguration(); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + ccfg.setIndexedTypes(Integer.class, TestValueGroupIndexInvalidAnnotation.class); + + return null; + } + }, CacheException.class, "Inline size cannot be set on a field with group index"); + } + + /** + * @throws Exception If failed. + */ + public void testNegativeInlineSize() throws Exception { + final CacheConfiguration ccfg = defaultCacheConfiguration(); + + GridTestUtils.assertThrows( + log, new Callable() { + + @Override public Object call() throws Exception { + ccfg.setIndexedTypes(Integer.class, TestValueNegativeInlineSize.class); + + return null; + } + }, + CacheException.class, + "Illegal inline size [idxName=TestValueNegativeInlineSize_val_idx, inlineSize=-10]" + ); + } + + /** + * + */ + static class TestValueSingleFieldIndexes { + /** */ + @QuerySqlField(index = true, inlineSize = 10) + String val0; + + /** */ + @QuerySqlField(index = true, inlineSize = 20) + String val1; + } + + /** + * + */ + @QueryGroupIndex(name="idx", inlineSize = 10) + static class TestValueGroupIndex { + /** */ + @QuerySqlField(orderedGroups = @QuerySqlField.Group(name = "idx", order = 0)) + String val0; + + /** */ + @QuerySqlField(orderedGroups = @QuerySqlField.Group(name = "idx", order = 1)) + String val1; + } + + /** + * + */ + @QueryGroupIndex(name="idx") + static class TestValueGroupIndexInvalidAnnotation { + /** */ + @QuerySqlField(orderedGroups = @QuerySqlField.Group(name = "idx", order = 0)) + String val0; + + /** */ + @QuerySqlField(orderedGroups = @QuerySqlField.Group(name = "idx", order = 1), inlineSize = 10) + String val1; + } + + /** + * + */ + static class TestValueNegativeInlineSize { + /** */ + @QuerySqlField(index = true, inlineSize = -10) + String val; + } +} \ No newline at end of file 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 decc7d5cd4396..99b03707c2e2d 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 @@ -39,6 +39,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheQueryIndexingDisabledSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheQueryInternalKeysSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheQuerySerializationSelfTest; +import org.apache.ignite.internal.processors.cache.GridCacheQuerySqlFieldInlineSizeSelfTest; import org.apache.ignite.internal.processors.cache.IgniteBinaryObjectFieldsQuerySelfTest; import org.apache.ignite.internal.processors.cache.IgniteBinaryObjectLocalQueryArgumentsTest; import org.apache.ignite.internal.processors.cache.IgniteBinaryObjectQueryArgumentsTest; @@ -320,6 +321,8 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteSqlRoutingTest.class); suite.addTestSuite(LongIndexNameTest.class); + suite.addTestSuite(GridCacheQuerySqlFieldInlineSizeSelfTest.class); + return suite; } } From 276e84a91bcea9c6b076ff674cdd16d93fbbde4d Mon Sep 17 00:00:00 2001 From: Igor Sapego Date: Thu, 10 Aug 2017 15:31:44 +0300 Subject: [PATCH 023/145] IGNITE-5995: ODBC fix for SQLGetData. (cherry picked from commit 0d8d166) --- .../impl/cache/query/query_fields_row_impl.h | 2 +- .../cpp/odbc-test/include/test_utils.h | 9 ++ .../cpp/odbc-test/src/meta_queries_test.cpp | 100 ++++++++++++++++++ .../cpp/odbc-test/src/test_utils.cpp | 13 +++ .../ignite/odbc/query/column_metadata_query.h | 3 + .../ignite/odbc/query/table_metadata_query.h | 3 + .../ignite/odbc/query/type_info_query.h | 3 + .../cpp/odbc/src/query/batch_query.cpp | 7 +- .../odbc/src/query/column_metadata_query.cpp | 16 ++- .../cpp/odbc/src/query/data_query.cpp | 7 +- .../odbc/src/query/table_metadata_query.cpp | 16 ++- .../cpp/odbc/src/query/type_info_query.cpp | 16 ++- 12 files changed, 183 insertions(+), 12 deletions(-) diff --git a/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h b/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h index 63e0523b1a27f..29436251c348c 100644 --- a/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h +++ b/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h @@ -143,7 +143,7 @@ namespace ignite int32_t actualLen = reader.ReadInt8Array(dst, len); - if (actualLen == 0 || dst && len >= actualLen) + if (actualLen == 0 || (dst && len >= actualLen)) ++processed; return actualLen; diff --git a/modules/platforms/cpp/odbc-test/include/test_utils.h b/modules/platforms/cpp/odbc-test/include/test_utils.h index 6a58e54a908b9..5dc6d6e25bc35 100644 --- a/modules/platforms/cpp/odbc-test/include/test_utils.h +++ b/modules/platforms/cpp/odbc-test/include/test_utils.h @@ -42,6 +42,15 @@ namespace ignite_test /** Read buffer size. */ enum { ODBC_BUFFER_SIZE = 1024 }; + /** + * Extract error state. + * + * @param handleType Type of the handle. + * @param handle Handle. + * @return Error state. + */ + std::string GetOdbcErrorState(SQLSMALLINT handleType, SQLHANDLE handle); + /** * Extract error message. * diff --git a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp index 454a989eb0f7e..ff3695db29748 100644 --- a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp +++ b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp @@ -103,6 +103,9 @@ struct MetaQueriesTestSuiteFixture BOOST_REQUIRE(stmt != NULL); } + /** + * Disconnect. + */ void Disconnect() { // Releasing statement handle. @@ -116,6 +119,11 @@ struct MetaQueriesTestSuiteFixture SQLFreeHandle(SQL_HANDLE_ENV, env); } + /** + * Start additional node with the specified name. + * + * @param name Node name. + */ static Ignite StartAdditionalNode(const char* name) { #ifdef IGNITE_TESTS_32 @@ -125,6 +133,36 @@ struct MetaQueriesTestSuiteFixture #endif } + /** + * Checks single row result set for correct work with SQLGetData. + * + * @param stmt Statement. + */ + void CheckSingleRowResultSetWithGetData(SQLHSTMT stmt) + { + SQLRETURN ret = SQLFetch(stmt); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + char buf[1024]; + SQLLEN bufLen = sizeof(buf); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, buf, sizeof(buf), &bufLen); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + ret = SQLFetch(stmt); + + BOOST_REQUIRE_EQUAL(ret, SQL_NO_DATA); + + ret = SQLGetData(stmt, 1, SQL_C_CHAR, buf, sizeof(buf), &bufLen); + + BOOST_REQUIRE_EQUAL(ret, SQL_ERROR); + BOOST_CHECK_EQUAL(GetOdbcErrorState(SQL_HANDLE_STMT, stmt), "24000"); + } + /** * Constructor. */ @@ -237,4 +275,66 @@ BOOST_AUTO_TEST_CASE(TestColAttributesColumnScale) BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); } +BOOST_AUTO_TEST_CASE(TestGetDataWithGetTypeInfo) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLRETURN ret = SQLGetTypeInfo(stmt, SQL_VARCHAR); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + CheckSingleRowResultSetWithGetData(stmt); +} + +BOOST_AUTO_TEST_CASE(TestGetDataWithTables) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR empty[] = ""; + SQLCHAR table[] = "TestType"; + + SQLRETURN ret = SQLTables(stmt, empty, SQL_NTS, empty, SQL_NTS, table, SQL_NTS, empty, SQL_NTS); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + CheckSingleRowResultSetWithGetData(stmt); +} + +BOOST_AUTO_TEST_CASE(TestGetDataWithColumns) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR empty[] = ""; + SQLCHAR table[] = "TestType"; + SQLCHAR column[] = "strField"; + + SQLRETURN ret = SQLColumns(stmt, empty, SQL_NTS, empty, SQL_NTS, table, SQL_NTS, column, SQL_NTS); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + CheckSingleRowResultSetWithGetData(stmt); +} + +BOOST_AUTO_TEST_CASE(TestGetDataWithSelectQuery) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR insertReq[] = "insert into TestType(_key, strField) VALUES(1, 'Lorem ipsum')"; + SQLRETURN ret = SQLExecDirect(stmt, insertReq, SQL_NTS); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + SQLCHAR selectReq[] = "select strField from TestType"; + ret = SQLExecDirect(stmt, selectReq, SQL_NTS); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + CheckSingleRowResultSetWithGetData(stmt); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/modules/platforms/cpp/odbc-test/src/test_utils.cpp b/modules/platforms/cpp/odbc-test/src/test_utils.cpp index c7aa70ac7c98a..6e8fe6a31d59f 100644 --- a/modules/platforms/cpp/odbc-test/src/test_utils.cpp +++ b/modules/platforms/cpp/odbc-test/src/test_utils.cpp @@ -21,6 +21,19 @@ namespace ignite_test { + std::string GetOdbcErrorState(SQLSMALLINT handleType, SQLHANDLE handle) + { + SQLCHAR sqlstate[7] = {}; + SQLINTEGER nativeCode; + + SQLCHAR message[ODBC_BUFFER_SIZE]; + SQLSMALLINT reallen = 0; + + SQLGetDiagRec(handleType, handle, 1, sqlstate, &nativeCode, message, ODBC_BUFFER_SIZE, &reallen); + + return std::string(reinterpret_cast(sqlstate)); + } + std::string GetOdbcErrorMessage(SQLSMALLINT handleType, SQLHANDLE handle) { SQLCHAR sqlstate[7] = {}; diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/query/column_metadata_query.h b/modules/platforms/cpp/odbc/include/ignite/odbc/query/column_metadata_query.h index 878a4befde5bf..875b1ce170045 100644 --- a/modules/platforms/cpp/odbc/include/ignite/odbc/query/column_metadata_query.h +++ b/modules/platforms/cpp/odbc/include/ignite/odbc/query/column_metadata_query.h @@ -130,6 +130,9 @@ namespace ignite /** Query executed. */ bool executed; + /** Fetched flag. */ + bool fetched; + /** Fetched metadata. */ meta::ColumnMetaVector meta; diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/query/table_metadata_query.h b/modules/platforms/cpp/odbc/include/ignite/odbc/query/table_metadata_query.h index cef963c848447..acd3f498a4270 100644 --- a/modules/platforms/cpp/odbc/include/ignite/odbc/query/table_metadata_query.h +++ b/modules/platforms/cpp/odbc/include/ignite/odbc/query/table_metadata_query.h @@ -134,6 +134,9 @@ namespace ignite /** Query executed. */ bool executed; + /** Fetched flag. */ + bool fetched; + /** Fetched metadata. */ meta::TableMetaVector meta; diff --git a/modules/platforms/cpp/odbc/include/ignite/odbc/query/type_info_query.h b/modules/platforms/cpp/odbc/include/ignite/odbc/query/type_info_query.h index a7cee9212df35..00cca08735095 100644 --- a/modules/platforms/cpp/odbc/include/ignite/odbc/query/type_info_query.h +++ b/modules/platforms/cpp/odbc/include/ignite/odbc/query/type_info_query.h @@ -105,6 +105,9 @@ namespace ignite /** Executed flag. */ bool executed; + /** Fetched flag. */ + bool fetched; + /** Requested types. */ std::vector types; diff --git a/modules/platforms/cpp/odbc/src/query/batch_query.cpp b/modules/platforms/cpp/odbc/src/query/batch_query.cpp index df9fb05008627..46447c074371f 100644 --- a/modules/platforms/cpp/odbc/src/query/batch_query.cpp +++ b/modules/platforms/cpp/odbc/src/query/batch_query.cpp @@ -112,7 +112,12 @@ namespace ignite } if (dataRetrieved) - return SqlResult::AI_NO_DATA; + { + diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, + "Cursor has reached end of the result set."); + + return SqlResult::AI_ERROR; + } if (columnIdx != 1) { diff --git a/modules/platforms/cpp/odbc/src/query/column_metadata_query.cpp b/modules/platforms/cpp/odbc/src/query/column_metadata_query.cpp index 8ba9323e1e2c4..b9c08f5dd044a 100644 --- a/modules/platforms/cpp/odbc/src/query/column_metadata_query.cpp +++ b/modules/platforms/cpp/odbc/src/query/column_metadata_query.cpp @@ -83,6 +83,7 @@ namespace ignite table(table), column(column), executed(false), + fetched(false), meta(), columnsMeta() { @@ -125,6 +126,7 @@ namespace ignite if (result == SqlResult::AI_SUCCESS) { executed = true; + fetched = false; cursor = meta.begin(); } @@ -146,6 +148,11 @@ namespace ignite return SqlResult::AI_ERROR; } + if (!fetched) + fetched = true; + else + ++cursor; + if (cursor == meta.end()) return SqlResult::AI_NO_DATA; @@ -154,8 +161,6 @@ namespace ignite for (it = columnBindings.begin(); it != columnBindings.end(); ++it) GetColumn(it->first, it->second); - ++cursor; - return SqlResult::AI_SUCCESS; } @@ -169,7 +174,12 @@ namespace ignite } if (cursor == meta.end()) - return SqlResult::AI_NO_DATA; + { + diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, + "Cursor has reached end of the result set."); + + return SqlResult::AI_ERROR; + } const meta::ColumnMeta& currentColumn = *cursor; uint8_t columnType = currentColumn.GetDataType(); diff --git a/modules/platforms/cpp/odbc/src/query/data_query.cpp b/modules/platforms/cpp/odbc/src/query/data_query.cpp index 23d524047eb84..f14d00433f84b 100644 --- a/modules/platforms/cpp/odbc/src/query/data_query.cpp +++ b/modules/platforms/cpp/odbc/src/query/data_query.cpp @@ -125,7 +125,12 @@ namespace ignite Row* row = cursor->GetRow(); if (!row) - return SqlResult::AI_NO_DATA; + { + diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, + "Cursor has reached end of the result set."); + + return SqlResult::AI_ERROR; + } SqlResult::Type result = row->ReadColumnToBuffer(columnIdx, buffer); diff --git a/modules/platforms/cpp/odbc/src/query/table_metadata_query.cpp b/modules/platforms/cpp/odbc/src/query/table_metadata_query.cpp index 401a1d24d4c15..e66b281c2c231 100644 --- a/modules/platforms/cpp/odbc/src/query/table_metadata_query.cpp +++ b/modules/platforms/cpp/odbc/src/query/table_metadata_query.cpp @@ -63,6 +63,7 @@ namespace ignite table(table), tableType(tableType), executed(false), + fetched(false), meta(), columnsMeta() { @@ -98,6 +99,7 @@ namespace ignite if (result == SqlResult::AI_SUCCESS) { executed = true; + fetched = false; cursor = meta.begin(); } @@ -119,6 +121,11 @@ namespace ignite return SqlResult::AI_ERROR; } + if (!fetched) + fetched = true; + else + ++cursor; + if (cursor == meta.end()) return SqlResult::AI_NO_DATA; @@ -127,8 +134,6 @@ namespace ignite for (it = columnBindings.begin(); it != columnBindings.end(); ++it) GetColumn(it->first, it->second); - ++cursor; - return SqlResult::AI_SUCCESS; } @@ -142,7 +147,12 @@ namespace ignite } if (cursor == meta.end()) - return SqlResult::AI_NO_DATA; + { + diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, + "Cursor has reached end of the result set."); + + return SqlResult::AI_ERROR; + } const meta::TableMeta& currentColumn = *cursor; diff --git a/modules/platforms/cpp/odbc/src/query/type_info_query.cpp b/modules/platforms/cpp/odbc/src/query/type_info_query.cpp index f6b399023ac54..b4efca0b9b668 100644 --- a/modules/platforms/cpp/odbc/src/query/type_info_query.cpp +++ b/modules/platforms/cpp/odbc/src/query/type_info_query.cpp @@ -122,6 +122,7 @@ namespace ignite Query(diag, QueryType::TYPE_INFO), columnsMeta(), executed(false), + fetched(false), types(), cursor(types.end()) { @@ -185,6 +186,7 @@ namespace ignite cursor = types.begin(); executed = true; + fetched = false; return SqlResult::AI_SUCCESS; } @@ -203,6 +205,11 @@ namespace ignite return SqlResult::AI_ERROR; } + if (!fetched) + fetched = true; + else + ++cursor; + if (cursor == types.end()) return SqlResult::AI_NO_DATA; @@ -211,8 +218,6 @@ namespace ignite for (it = columnBindings.begin(); it != columnBindings.end(); ++it) GetColumn(it->first, it->second); - ++cursor; - return SqlResult::AI_SUCCESS; } @@ -228,7 +233,12 @@ namespace ignite } if (cursor == types.end()) - return SqlResult::AI_NO_DATA; + { + diag.AddStatusRecord(SqlState::S24000_INVALID_CURSOR_STATE, + "Cursor has reached end of the result set."); + + return SqlResult::AI_ERROR; + } int8_t currentType = *cursor; From 020ff3608d07fc584effe0826d72eb5edac0e1d6 Mon Sep 17 00:00:00 2001 From: Igor Sapego Date: Tue, 15 Aug 2017 16:55:31 +0300 Subject: [PATCH 024/145] IGNITE-6032: ODBC: Added SQL_SCROLL_OPTIONS support for SQLGetInfo (cherry picked from commit f3d3d1bd718068c941e14b4e5949573b0a5d0c6a) --- .../cpp/odbc-test/src/meta_queries_test.cpp | 13 +++++++++++++ .../cpp/odbc/src/config/connection_info.cpp | 19 ++++++++++++++++++- .../cpp/odbc/src/meta/column_meta.cpp | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp index ff3695db29748..5d4e22fa5ae58 100644 --- a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp +++ b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp @@ -337,4 +337,17 @@ BOOST_AUTO_TEST_CASE(TestGetDataWithSelectQuery) CheckSingleRowResultSetWithGetData(stmt); } +BOOST_AUTO_TEST_CASE(TestGetInfoScrollOptions) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLUINTEGER val = 0; + SQLRETURN ret = SQLGetInfo(dbc, SQL_SCROLL_OPTIONS, &val, 0, 0); + + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_DBC, dbc)); + + BOOST_CHECK_NE(val, 0); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/modules/platforms/cpp/odbc/src/config/connection_info.cpp b/modules/platforms/cpp/odbc/src/config/connection_info.cpp index 4e7cc3cfbb7d8..49259570c19dc 100644 --- a/modules/platforms/cpp/odbc/src/config/connection_info.cpp +++ b/modules/platforms/cpp/odbc/src/config/connection_info.cpp @@ -118,6 +118,7 @@ namespace ignite DBG_STR_CASE(SQL_CONVERT_WLONGVARCHAR); DBG_STR_CASE(SQL_CONVERT_WVARCHAR); DBG_STR_CASE(SQL_CONVERT_GUID); + DBG_STR_CASE(SQL_SCROLL_OPTIONS); DBG_STR_CASE(SQL_PARAM_ARRAY_ROW_COUNTS); DBG_STR_CASE(SQL_PARAM_ARRAY_SELECTS); default: @@ -628,6 +629,19 @@ namespace ignite SQL_CVT_BINARY | SQL_CVT_VARBINARY | SQL_CVT_LONGVARBINARY | SQL_CVT_GUID; #endif //SQL_CONVERT_GUID +#ifdef SQL_SCROLL_OPTIONS + // Bitmask enumerating the scroll options supported for scrollable cursors + // SQL_SO_FORWARD_ONLY = The cursor only scrolls forward. (ODBC 1.0) + // SQL_SO_STATIC = The data in the result set is static. (ODBC 2.0) + // SQL_SO_KEYSET_DRIVEN = The driver saves and uses the keys for every row in the result set. (ODBC 1.0) + // SQL_SO_DYNAMIC = The driver keeps the keys for every row in the rowset(the keyset size is the same + // as the rowset size). (ODBC 1.0) + // SQL_SO_MIXED = The driver keeps the keys for every row in the keyset, and the keyset size is greater + // than the rowset size.The cursor is keyset - driven inside the keyset and dynamic outside the + // keyset. (ODBC 1.0) + intParams[SQL_SCROLL_OPTIONS] = SQL_SO_FORWARD_ONLY | SQL_SO_STATIC; +#endif //SQL_SCROLL_OPTIONS + //======================= Short Params ======================== #ifdef SQL_MAX_CONCURRENT_ACTIVITIES // The maximum number of active statements that the driver can @@ -666,13 +680,16 @@ namespace ignite SqlResult::Type ConnectionInfo::GetInfo(InfoType type, void* buf, short buflen, short* reslen) const { - if (!buf || !buflen) + if (!buf) return SqlResult::AI_ERROR; StringInfoMap::const_iterator itStr = strParams.find(type); if (itStr != strParams.end()) { + if (!buflen) + return SqlResult::AI_ERROR; + unsigned short strlen = static_cast( utility::CopyStringToBuffer(itStr->second, reinterpret_cast(buf), buflen)); diff --git a/modules/platforms/cpp/odbc/src/meta/column_meta.cpp b/modules/platforms/cpp/odbc/src/meta/column_meta.cpp index 97fdf807101c8..f1bd9a1b17057 100644 --- a/modules/platforms/cpp/odbc/src/meta/column_meta.cpp +++ b/modules/platforms/cpp/odbc/src/meta/column_meta.cpp @@ -60,6 +60,9 @@ namespace ignite DBG_STR_CASE(SQL_DESC_UNNAMED); DBG_STR_CASE(SQL_DESC_UNSIGNED); DBG_STR_CASE(SQL_DESC_UPDATABLE); + DBG_STR_CASE(SQL_COLUMN_LENGTH); + DBG_STR_CASE(SQL_COLUMN_PRECISION); + DBG_STR_CASE(SQL_COLUMN_SCALE); default: break; } From b094cb277152be41d8d3aa467167403838f69e87 Mon Sep 17 00:00:00 2001 From: Alexander Paschenko Date: Wed, 16 Aug 2017 09:41:54 +0300 Subject: [PATCH 025/145] IGNITE-6019: SQL: do not pull the whole result set immediately to the client when there is no merge table. This closes #2430. --- .../h2/twostep/GridMergeIndexIterator.java | 165 ++++++++++++++++++ .../h2/twostep/GridReduceQueryExecutor.java | 61 +++---- 2 files changed, 188 insertions(+), 38 deletions(-) create mode 100644 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 new file mode 100644 index 0000000000000..1c0efb39ac6d1 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexIterator.java @@ -0,0 +1,165 @@ +/* + * 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; + +import java.util.ArrayList; +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.h2.index.Cursor; +import org.h2.result.Row; + +/** + * Iterator that transparently and sequentially traverses a bunch of {@link GridMergeIndex} objects. + */ +class GridMergeIndexIterator implements Iterator>, AutoCloseable { + /** Reduce query executor. */ + private final GridReduceQueryExecutor rdcExec; + + /** Participating nodes. */ + private final Collection nodes; + + /** Query run. */ + private final ReduceQueryRun run; + + /** Query request ID. */ + private final long qryReqId; + + /** Distributed joins. */ + private final boolean distributedJoins; + + /** Iterator over indexes. */ + private final Iterator idxIter; + + /** Current cursor. */ + private Cursor cursor; + + /** Next row to return. */ + private List next; + + /** Whether remote resources were released. */ + private boolean released; + + /** + * Constructor. + * + * @param rdcExec Reduce query executor. + * @param nodes Participating nodes. + * @param run Query run. + * @param qryReqId Query request ID. + * @param distributedJoins Distributed joins. + * @throws IgniteCheckedException if failed. + */ + GridMergeIndexIterator(GridReduceQueryExecutor rdcExec, Collection nodes, ReduceQueryRun run, + long qryReqId, boolean distributedJoins) + throws IgniteCheckedException { + this.rdcExec = rdcExec; + this.nodes = nodes; + this.run = run; + this.qryReqId = qryReqId; + this.distributedJoins = distributedJoins; + + this.idxIter = run.indexes().iterator(); + + advance(); + } + + /** {@inheritDoc} */ + @Override public boolean hasNext() { + return next != null; + } + + /** {@inheritDoc} */ + @Override public List next() { + List res = next; + + if (res == null) + throw new NoSuchElementException(); + + advance(); + + return res; + } + + /** {@inheritDoc} */ + @Override public void remove() { + throw new UnsupportedOperationException("Remove is not supported"); + } + + /** {@inheritDoc} */ + @Override public void close() throws Exception { + releaseIfNeeded(); + } + + /** + * Advance iterator. + */ + private void advance() { + next = null; + + try { + boolean hasNext = false; + + while (cursor == null || !(hasNext = cursor.next())) { + if (idxIter.hasNext()) + cursor = idxIter.next().findInStream(null, null); + else { + releaseIfNeeded(); + + break; + } + } + + if (hasNext) { + Row row = cursor.get(); + + int cols = row.getColumnCount(); + + List res = new ArrayList<>(cols); + + for (int c = 0; c < cols; c++) + res.add(row.getValue(c).getObject()); + + next = res; + } + } + catch (Exception e) { + releaseIfNeeded(); + + throw e; + } + } + + /** + * Close routine. + */ + private void releaseIfNeeded() { + if (!released) { + try { + rdcExec.releaseRemoteResources(nodes, run, qryReqId, distributedJoins); + } + finally { + released = true; + } + } + } +} 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 85a7e0b21d968..0e9d1a239b74f 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 @@ -87,10 +87,8 @@ import org.apache.ignite.plugin.extensions.communication.Message; import org.h2.command.ddl.CreateTableData; import org.h2.engine.Session; -import org.h2.index.Cursor; import org.h2.index.Index; import org.h2.jdbc.JdbcConnection; -import org.h2.result.Row; import org.h2.table.Column; import org.h2.util.IntArray; import org.h2.value.Value; @@ -169,6 +167,7 @@ public void start(final GridKernalContext ctx, final IgniteH2Indexing h2) throws log = ctx.log(GridReduceQueryExecutor.class); ctx.io().addMessageListener(GridTopic.TOPIC_QUERY, new GridMessageListener() { + @SuppressWarnings("deprecation") @Override public void onMessage(UUID nodeId, Object msg, byte plc) { if (!busyLock.enterBusy()) return; @@ -511,7 +510,7 @@ else if (!isReplicatedOnly && !extraCctx.isReplicated()) { */ public Iterator> query( String schemaName, - GridCacheTwoStepQuery qry, + final GridCacheTwoStepQuery qry, boolean keepBinary, boolean enforceJoinOrder, int timeoutMillis, @@ -622,6 +621,8 @@ public Iterator> query( int replicatedQrysCnt = 0; + final Collection finalNodes = nodes; + for (GridCacheSqlQuery mapQry : qry.mapQueries()) { GridMergeIndex idx; @@ -665,6 +666,8 @@ public Iterator> query( runs.put(qryReqId, r); + boolean release = true; + try { cancel.checkCancelled(); @@ -686,8 +689,6 @@ public Iterator> query( final boolean distributedJoins = qry.distributedJoins(); - final Collection finalNodes = nodes; - cancel.set(new Runnable() { @Override public void run() { send(finalNodes, new GridQueryCancelRequest(qryReqId), null, false); @@ -757,27 +758,9 @@ public Iterator> query( if (!retry) { if (skipMergeTbl) { - List> res = new ArrayList<>(); - - // Simple UNION ALL can have multiple indexes. - for (GridMergeIndex idx : r.indexes()) { - Cursor cur = idx.findInStream(null, null); - - while (cur.next()) { - Row row = cur.get(); - - int cols = row.getColumnCount(); - - List resRow = new ArrayList<>(cols); - - for (int c = 0; c < cols; c++) - resRow.add(row.getValue(c).getObject()); + resIter = new GridMergeIndexIterator(this, finalNodes, r, qryReqId, qry.distributedJoins()); - res.add(resRow); - } - } - - resIter = res.iterator(); + release = false; } else { cancel.checkCancelled(); @@ -820,6 +803,8 @@ public Iterator> query( return new GridQueryCacheObjectsIterator(resIter, h2.objectContext(), keepBinary); } catch (IgniteCheckedException | RuntimeException e) { + release = true; + U.closeQuiet(r.connection()); if (e instanceof CacheException) { @@ -842,15 +827,13 @@ public Iterator> query( throw new CacheException("Failed to run reduce query locally.", cause); } finally { - // Make sure any activity related to current attempt is cancelled. - cancelRemoteQueriesIfNeeded(nodes, r, qryReqId, qry.distributedJoins()); - - if (!runs.remove(qryReqId, r)) - U.warn(log, "Query run was already removed: " + qryReqId); + if (release) { + releaseRemoteResources(finalNodes, r, qryReqId, qry.distributedJoins()); - if (!skipMergeTbl) { - for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++) - fakeTable(null, i).innerTable(null); // Drop all merge tables. + if (!skipMergeTbl) { + for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++) + fakeTable(null, i).innerTable(null); // Drop all merge tables. + } } } } @@ -885,16 +868,15 @@ private boolean wasCancelled(CacheException e) { } /** + * Release remote resources if needed. + * * @param nodes Query nodes. * @param r Query run. * @param qryReqId Query id. * @param distributedJoins Distributed join flag. */ - private void cancelRemoteQueriesIfNeeded(Collection nodes, - ReduceQueryRun r, - long qryReqId, - boolean distributedJoins) - { + public void releaseRemoteResources(Collection nodes, ReduceQueryRun r, long qryReqId, + boolean distributedJoins) { // For distributedJoins need always send cancel request to cleanup resources. if (distributedJoins) send(nodes, new GridQueryCancelRequest(qryReqId), null, false); @@ -907,6 +889,9 @@ private void cancelRemoteQueriesIfNeeded(Collection nodes, } } } + + if (!runs.remove(qryReqId, r)) + U.warn(log, "Query run was already removed: " + qryReqId); } /** From 56d8baa3744992a77ee5eb9bea8f78bf50667ffa Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Wed, 16 Aug 2017 14:06:58 +0300 Subject: [PATCH 026/145] IGNITE-6074 Fixed test fail IgniteWalRecoveryTest#testWalBigObjectNodeCancel (reset position and limit after expand buffer) - Fixes #2452. Signed-off-by: Alexey Goncharuk --- .../cache/persistence/wal/ByteBufferExpander.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java index cf1db84c3feac..e7130a4d16a29 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/ByteBufferExpander.java @@ -54,9 +54,16 @@ public ByteBuffer buffer() { * @return ByteBuffer with requested size. */ public ByteBuffer expand(int size) { + assert buf.capacity() < size; + + int pos = buf.position(); + int lim = buf.limit(); + ByteBuffer newBuf = GridUnsafe.reallocateBuffer(buf, size); newBuf.order(buf.order()); + newBuf.position(pos); + newBuf.limit(lim); buf = newBuf; From 3d696da0714577dff82229db3c3aed87d43f1f10 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Wed, 16 Aug 2017 17:01:57 +0300 Subject: [PATCH 027/145] IGNITE-6082 Fixed DynamicIndexPartitionedTransactionalConcurrentSelfTest.testConcurrentPutRemove (use correct IP finder in test SPI) --- .../index/DynamicIndexAbstractConcurrentSelfTest.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java index 913d724ceb5c7..00fd4135a6bff 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java @@ -49,6 +49,7 @@ import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitor; import org.apache.ignite.internal.processors.query.schema.SchemaOperationException; import org.apache.ignite.internal.util.typedef.T3; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; import org.jetbrains.annotations.NotNull; import static org.apache.ignite.internal.IgniteClientReconnectAbstractTest.TestTcpDiscoverySpi; @@ -120,7 +121,15 @@ public abstract class DynamicIndexAbstractConcurrentSelfTest extends DynamicInde /** {@inheritDoc} */ @Override protected IgniteConfiguration commonConfiguration(int idx) throws Exception { - return super.commonConfiguration(idx).setDiscoverySpi(new TestTcpDiscoverySpi()); + IgniteConfiguration cfg = super.commonConfiguration(idx); + + TestTcpDiscoverySpi testSpi = new TestTcpDiscoverySpi(); + + if (cfg.getDiscoverySpi() instanceof TcpDiscoverySpi && + ((TcpDiscoverySpi)cfg.getDiscoverySpi()).getIpFinder() != null) + testSpi.setIpFinder(((TcpDiscoverySpi)cfg.getDiscoverySpi()).getIpFinder()); + + return cfg.setDiscoverySpi(testSpi); } /** From 5819acd8c829c4d65bf53c6bef39e3dfb9fc938b Mon Sep 17 00:00:00 2001 From: Sergey Chugunov Date: Wed, 16 Aug 2017 17:12:27 +0300 Subject: [PATCH 028/145] IGNITE-6073 Handy API to add binary metadata locally --- .../binary/CacheObjectBinaryProcessor.java | 20 +++++++++++++++---- .../CacheObjectBinaryProcessorImpl.java | 16 +++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessor.java index 4d285ab8b1d5f..14dd5cbc9a35b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessor.java @@ -19,13 +19,13 @@ import java.util.Collection; import java.util.Map; -import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteBinary; -import org.apache.ignite.internal.binary.BinaryFieldMetadata; -import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor; +import org.apache.ignite.IgniteException; +import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.binary.BinaryObjectBuilder; import org.apache.ignite.binary.BinaryType; -import org.apache.ignite.binary.BinaryObject; +import org.apache.ignite.internal.binary.BinaryFieldMetadata; +import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor; import org.jetbrains.annotations.Nullable; /** @@ -53,6 +53,18 @@ public interface CacheObjectBinaryProcessor extends IgniteCacheObjectProcessor { */ public void addMeta(int typeId, final BinaryType newMeta) throws IgniteException; + /** + * Adds metadata locally without triggering discovery exchange. + * + * Must be used only during startup and only if it is guaranteed that all nodes have the same copy + * of BinaryType. + * + * @param typeId Type ID. + * @param newMeta New meta data. + * @throws IgniteException In case of error. + */ + public void addMetaLocally(int typeId, final BinaryType newMeta) throws IgniteException; + /** * @param typeId Type ID. * @param typeName Type name. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java index 0065e4156b51a..28ed156b6f10d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/binary/CacheObjectBinaryProcessorImpl.java @@ -441,6 +441,22 @@ public GridBinaryMarshaller marshaller() { } } + /** {@inheritDoc} */ + @Override public void addMetaLocally(int typeId, BinaryType newMeta) throws BinaryObjectException { + assert newMeta != null; + assert newMeta instanceof BinaryTypeImpl; + + BinaryMetadata newMeta0 = ((BinaryTypeImpl)newMeta).metadata(); + + BinaryMetadataHolder metaHolder = metadataLocCache.get(typeId); + + BinaryMetadata oldMeta = metaHolder != null ? metaHolder.metadata() : null; + + BinaryMetadata mergedMeta = BinaryUtils.mergeMetadata(oldMeta, newMeta0); + + metadataLocCache.put(typeId, new BinaryMetadataHolder(mergedMeta, 0, 0)); + } + /** {@inheritDoc} */ @Nullable @Override public BinaryType metadata(final int typeId) { BinaryMetadata meta = metadata0(typeId); From 6528622444d389f4876195b35cadc6cb68f36cc4 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Wed, 16 Aug 2017 17:59:11 +0300 Subject: [PATCH 029/145] IGNITE-6068 Fixed InvalidPartitionException during index visit --- .../dht/GridDhtLocalPartition.java | 38 ++++++------- .../schema/SchemaIndexCacheVisitorImpl.java | 7 +++ ...IgnitePdsCacheRebalancingAbstractTest.java | 53 ++++++++++--------- 3 files changed, 55 insertions(+), 43 deletions(-) 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 725822dd58cdf..dbfb426d717d1 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 @@ -953,24 +953,26 @@ public void clearAll() throws NodeStoppingException { ctx.database().checkpointReadLock(); - try {if (cached instanceof GridDhtCacheEntry && ((GridDhtCacheEntry)cached).clearInternal(clearVer, extras)) { - removeEntry(cached); - - if (rec) { - hld.cctx.events().addEvent(cached.partition(), - cached.key(), - ctx.localNodeId(), - (IgniteUuid)null, - null, - EVT_CACHE_REBALANCE_OBJECT_UNLOADED, - null, - false, - cached.rawGet(), - cached.hasValue(), - null, - null, - null, - false);} + try { + if (cached instanceof GridDhtCacheEntry && ((GridDhtCacheEntry)cached).clearInternal(clearVer, extras)) { + removeEntry(cached); + + if (rec) { + hld.cctx.events().addEvent(cached.partition(), + cached.key(), + ctx.localNodeId(), + (IgniteUuid)null, + null, + EVT_CACHE_REBALANCE_OBJECT_UNLOADED, + null, + false, + cached.rawGet(), + cached.hasValue(), + null, + null, + null, + false); + } } } finally { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java index 3a445bf79e72e..4e50f64418266 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaIndexCacheVisitorImpl.java @@ -24,6 +24,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheEntryEx; import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException; import org.apache.ignite.internal.processors.cache.KeyCacheObject; +import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter; @@ -118,6 +119,9 @@ private void processPartition(GridDhtLocalPartition part, FilteringVisitorClosur KeyCacheObject key = row.key(); processKey(key, row.link(), clo); + + if (part.state() == RENTING) + break; } } finally { @@ -149,6 +153,9 @@ private void processKey(KeyCacheObject key, long link, FilteringVisitorClosure c break; } + catch (GridDhtInvalidPartitionException ignore) { + break; + } catch (GridCacheEntryRemovedException ignored) { // No-op. } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java index c463871691846..9ceb87c31285c 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java @@ -37,8 +37,8 @@ import org.apache.ignite.cache.QueryIndex; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; -import org.apache.ignite.configuration.MemoryPolicyConfiguration; import org.apache.ignite.configuration.MemoryConfiguration; +import org.apache.ignite.configuration.MemoryPolicyConfiguration; import org.apache.ignite.configuration.PersistentStoreConfiguration; import org.apache.ignite.configuration.WALMode; import org.apache.ignite.internal.IgniteEx; @@ -63,7 +63,7 @@ public abstract class IgnitePdsCacheRebalancingAbstractTest extends GridCommonAb private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); /** Cache name. */ - private final String cacheName = "cache"; + private static final String cacheName = "cache"; /** */ protected boolean explicitTx; @@ -288,10 +288,10 @@ public void testDataCorrectnessAfterRestart() throws Exception { IgniteEx ignite3 = (IgniteEx)G.start(getConfiguration("test3")); IgniteEx ignite4 = (IgniteEx)G.start(getConfiguration("test4")); - awaitPartitionMapExchange(); - ignite1.active(true); + awaitPartitionMapExchange(); + IgniteCache cache1 = ignite1.cache(cacheName); for (int i = 0; i < 100; i++) @@ -307,10 +307,10 @@ public void testDataCorrectnessAfterRestart() throws Exception { ignite3 = (IgniteEx)G.start(getConfiguration("test3")); ignite4 = (IgniteEx)G.start(getConfiguration("test4")); - awaitPartitionMapExchange(); - ignite1.active(true); + awaitPartitionMapExchange(); + cache1 = ignite1.cache(cacheName); IgniteCache cache2 = ignite2.cache(cacheName); IgniteCache cache3 = ignite3.cache(cacheName); @@ -459,32 +459,35 @@ public void testTopologyChangesWithConstantLoad() throws Exception { } }, 1, "load-runner"); - for (int i = 0; i < topChanges; i++) { - if (U.currentTimeMillis() > timeOut) - break; + try { + for (int i = 0; i < topChanges; i++) { + if (U.currentTimeMillis() > timeOut) + break; - U.sleep(3_000); + U.sleep(3_000); - boolean add; + boolean add; - if (nodesCnt.get() <= maxNodesCount / 2) - add = true; - else if (nodesCnt.get() > maxNodesCount) - add = false; - else // More chance that node will be added - add = ThreadLocalRandom.current().nextInt(3) <= 1; + if (nodesCnt.get() <= maxNodesCount / 2) + add = true; + else if (nodesCnt.get() > maxNodesCount) + add = false; + else // More chance that node will be added + add = ThreadLocalRandom.current().nextInt(3) <= 1; - if (add) - startGrid(nodesCnt.incrementAndGet()); - else - stopGrid(nodesCnt.getAndDecrement()); + if (add) + startGrid(nodesCnt.incrementAndGet()); + else + stopGrid(nodesCnt.getAndDecrement()); - awaitPartitionMapExchange(); + awaitPartitionMapExchange(); - cache.rebalance().get(); + cache.rebalance().get(); + } + } + finally { + stop.set(true); } - - stop.set(true); fut.get(); From 30de686fbc206f2d105f17eee4f31b6bdc1c0a14 Mon Sep 17 00:00:00 2001 From: Konstantin Dudkov Date: Tue, 15 Aug 2017 19:28:05 +0700 Subject: [PATCH 030/145] IGNITE-6067 move initialize() to GridCacheUtils - Fixes #2445. (cherry picked from commit 24cfc2a) --- .../processors/cache/GridCacheProcessor.java | 87 +---------------- .../processors/cache/GridCacheUtils.java | 97 +++++++++++++++++++ 2 files changed, 99 insertions(+), 85 deletions(-) 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 3406f48d842c9..ef27a14db4e47 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 @@ -34,9 +34,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; -import javax.cache.configuration.Factory; -import javax.cache.integration.CacheLoader; -import javax.cache.integration.CacheWriter; import javax.management.MBeanServer; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; @@ -156,12 +153,9 @@ import static org.apache.ignite.cache.CacheMode.LOCAL; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheMode.REPLICATED; -import static org.apache.ignite.cache.CacheRebalanceMode.ASYNC; import static org.apache.ignite.cache.CacheRebalanceMode.SYNC; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_ASYNC; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; -import static org.apache.ignite.cache.CacheWriteSynchronizationMode.PRIMARY_SYNC; -import static org.apache.ignite.configuration.CacheConfiguration.DFLT_CACHE_MODE; import static org.apache.ignite.configuration.DeploymentMode.CONTINUOUS; import static org.apache.ignite.configuration.DeploymentMode.ISOLATED; import static org.apache.ignite.configuration.DeploymentMode.PRIVATE; @@ -244,86 +238,9 @@ public GridCacheProcessor(GridKernalContext ctx) { */ private void initialize(CacheConfiguration cfg, CacheObjectContext cacheObjCtx) throws IgniteCheckedException { - if (cfg.getCacheMode() == null) - cfg.setCacheMode(DFLT_CACHE_MODE); - - if (cfg.getNodeFilter() == null) - cfg.setNodeFilter(CacheConfiguration.ALL_NODES); - - if (cfg.getAffinity() == null) { - if (cfg.getCacheMode() == PARTITIONED) { - RendezvousAffinityFunction aff = new RendezvousAffinityFunction(); - - cfg.setAffinity(aff); - } - else if (cfg.getCacheMode() == REPLICATED) { - RendezvousAffinityFunction aff = new RendezvousAffinityFunction(false, 512); - - cfg.setAffinity(aff); - - cfg.setBackups(Integer.MAX_VALUE); - } - else - cfg.setAffinity(new LocalAffinityFunction()); - } - else { - if (cfg.getCacheMode() == LOCAL && !(cfg.getAffinity() instanceof LocalAffinityFunction)) { - cfg.setAffinity(new LocalAffinityFunction()); - - U.warn(log, "AffinityFunction configuration parameter will be ignored for local cache" + - " [cacheName=" + U.maskName(cfg.getName()) + ']'); - } - } - - if (cfg.getCacheMode() == REPLICATED) - cfg.setBackups(Integer.MAX_VALUE); - - if (cfg.getQueryParallelism() > 1 && cfg.getCacheMode() != PARTITIONED) - throw new IgniteCheckedException("Segmented indices are supported for PARTITIONED mode only."); - - if (cfg.getAffinityMapper() == null) - cfg.setAffinityMapper(cacheObjCtx.defaultAffMapper()); + CU.initializeConfigDefaults(log, cfg, cacheObjCtx); ctx.igfsHelper().preProcessCacheConfiguration(cfg); - - if (cfg.getRebalanceMode() == null) - cfg.setRebalanceMode(ASYNC); - - if (cfg.getAtomicityMode() == null) - cfg.setAtomicityMode(CacheConfiguration.DFLT_CACHE_ATOMICITY_MODE); - - if (cfg.getWriteSynchronizationMode() == null) - cfg.setWriteSynchronizationMode(PRIMARY_SYNC); - - assert cfg.getWriteSynchronizationMode() != null; - - if (cfg.getCacheStoreFactory() == null) { - Factory ldrFactory = cfg.getCacheLoaderFactory(); - Factory writerFactory = cfg.isWriteThrough() ? cfg.getCacheWriterFactory() : null; - - if (ldrFactory != null || writerFactory != null) - cfg.setCacheStoreFactory(new GridCacheLoaderWriterStoreFactory(ldrFactory, writerFactory)); - } - else { - if (cfg.getCacheLoaderFactory() != null) - throw new IgniteCheckedException("Cannot set both cache loaded factory and cache store factory " + - "for cache: " + U.maskName(cfg.getName())); - - if (cfg.getCacheWriterFactory() != null) - throw new IgniteCheckedException("Cannot set both cache writer factory and cache store factory " + - "for cache: " + U.maskName(cfg.getName())); - } - - Collection entities = cfg.getQueryEntities(); - - if (!F.isEmpty(entities)) { - Collection normalEntities = new ArrayList<>(entities.size()); - - for (QueryEntity entity : entities) - normalEntities.add(QueryUtils.normalizeQueryEntity(entity, cfg.isSqlEscapeAll())); - - cfg.clearQueryEntities().setQueryEntities(normalEntities); - } } /** @@ -3961,7 +3878,7 @@ public IgniteUuid deploymentId() { /** * */ - private static class LocalAffinityFunction implements AffinityFunction { + static class LocalAffinityFunction implements AffinityFunction { /** */ private static final long serialVersionUID = 0L; 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 a07b1669a8139..4aec0c8b5ade9 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 @@ -36,6 +36,8 @@ import javax.cache.configuration.Factory; import javax.cache.expiry.Duration; import javax.cache.expiry.ExpiryPolicy; +import javax.cache.integration.CacheLoader; +import javax.cache.integration.CacheWriter; import javax.cache.integration.CacheWriterException; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; @@ -46,6 +48,8 @@ import org.apache.ignite.cache.CacheAtomicUpdateTimeoutException; import org.apache.ignite.cache.CachePartialUpdateException; import org.apache.ignite.cache.CacheServerNotFoundException; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; import org.apache.ignite.cache.store.CacheStoreSessionListener; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; @@ -65,6 +69,7 @@ import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.igfs.IgfsUtils; +import org.apache.ignite.internal.processors.query.QueryUtils; import org.apache.ignite.internal.processors.query.schema.SchemaOperationException; import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException; import org.apache.ignite.internal.util.lang.IgniteInClosureX; @@ -95,9 +100,13 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK; 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; +import static org.apache.ignite.cache.CacheRebalanceMode.ASYNC; import static org.apache.ignite.cache.CacheRebalanceMode.SYNC; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; +import static org.apache.ignite.cache.CacheWriteSynchronizationMode.PRIMARY_SYNC; +import static org.apache.ignite.configuration.CacheConfiguration.DFLT_CACHE_MODE; import static org.apache.ignite.internal.GridTopic.TOPIC_REPLICATION; import static org.apache.ignite.internal.processors.cache.GridCacheOperation.READ; @@ -1613,4 +1622,92 @@ public static void validateConfigurationCacheNames(Collection ldrFactory = cfg.getCacheLoaderFactory(); + Factory writerFactory = cfg.isWriteThrough() ? cfg.getCacheWriterFactory() : null; + + if (ldrFactory != null || writerFactory != null) + cfg.setCacheStoreFactory(new GridCacheLoaderWriterStoreFactory(ldrFactory, writerFactory)); + } + else { + if (cfg.getCacheLoaderFactory() != null) + throw new IgniteCheckedException("Cannot set both cache loaded factory and cache store factory " + + "for cache: " + U.maskName(cfg.getName())); + + if (cfg.getCacheWriterFactory() != null) + throw new IgniteCheckedException("Cannot set both cache writer factory and cache store factory " + + "for cache: " + U.maskName(cfg.getName())); + } + + Collection entities = cfg.getQueryEntities(); + + if (!F.isEmpty(entities)) { + Collection normalEntities = new ArrayList<>(entities.size()); + + for (QueryEntity entity : entities) + normalEntities.add(QueryUtils.normalizeQueryEntity(entity, cfg.isSqlEscapeAll())); + + cfg.clearQueryEntities().setQueryEntities(normalEntities); + } + } } From 8a5e3dd24a924b0d81a3771a0e71fd28c6ca8953 Mon Sep 17 00:00:00 2001 From: vsisko Date: Thu, 17 Aug 2017 14:55:08 +0700 Subject: [PATCH 031/145] IGNITE-5586 Toggle cluster active state. (cherry picked from commit 5283e19) --- .../commands/top/VisorTopologyCommand.scala | 111 +++++++++++------- .../ignite/visor/VisorRuntimeBaseSpec.scala | 9 +- .../top/VisorActivationCommandSpec.scala | 67 +++++++++++ .../VisorConsoleSelfTestSuite.scala | 3 +- 4 files changed, 146 insertions(+), 44 deletions(-) create mode 100644 modules/visor-console/src/test/scala/org/apache/ignite/visor/commands/top/VisorActivationCommandSpec.scala diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/top/VisorTopologyCommand.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/top/VisorTopologyCommand.scala index 63b6d2bf45eb9..b75afc67897aa 100644 --- a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/top/VisorTopologyCommand.scala +++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/top/VisorTopologyCommand.scala @@ -48,6 +48,8 @@ import scala.util.control.Breaks._ * ====Specification==== * {{{ * top "{-c1=e1 -c2=e2 ... -ck=ek} {-h= ... -h=} {-a}" + * top -activate + * top -deactivate * }}} * * ====Arguments==== @@ -87,6 +89,10 @@ import scala.util.control.Breaks._ * -a * This defines whether to show a separate table of nodes * with detail per-node information. + * -activate + * Activate cluster. + * -deactivate + * Deactivate cluster. * }}} * * ====Examples==== @@ -99,6 +105,10 @@ import scala.util.control.Breaks._ * Prints topology for provided hosts. * top * Prints full topology. + * top -activate + * Activate cluster. + * top -deactivate + * Deactivate cluster. * }}} */ class VisorTopologyCommand extends VisorConsoleCommand { @@ -140,47 +150,53 @@ class VisorTopologyCommand extends VisorConsoleCommand { else { val argLst = parseArgs(args) - val hosts = argLst.filter(_._1 == "h").map((a: Arg) => - try - InetAddress.getByName(a._2).getHostAddress + if (hasArgFlag("activate", argLst)) + ignite.active(true) + else if (hasArgFlag("deactivate", argLst)) + ignite.active(false) + else { + val hosts = argLst.filter(_._1 == "h").map((a: Arg) => + try + InetAddress.getByName(a._2).getHostAddress + catch { + case e: UnknownHostException => scold("Unknown host: " + a._2).^^ + + "" // Never happens. + } + ).filter(!_.isEmpty).toSet + + val all = hasArgFlag("a", argLst) + + var f: NodeFilter = (ClusterNode) => true + + try { + argLst foreach (arg => { + val (n, v) = arg + + n match { + case "cc" if v != null => f = make(v, f, _.metrics.getTotalCpus) + case "cl" if v != null => f = make(v, f, (n: ClusterNode) => + (n.metrics.getCurrentCpuLoad * 100).toLong) + case "aj" if v != null => f = make(v, f, _.metrics.getCurrentActiveJobs) + case "cj" if v != null => f = make(v, f, _.metrics.getCurrentCancelledJobs) + case "tc" if v != null => f = make(v, f, _.metrics.getCurrentThreadCount) + case "ut" if v != null => f = make(v, f, _.metrics.getUpTime) + case "je" if v != null => f = make(v, f, _.metrics.getCurrentJobExecuteTime) + case "jw" if v != null => f = make(v, f, _.metrics.getCurrentJobWaitTime) + case "wj" if v != null => f = make(v, f, _.metrics.getCurrentWaitingJobs) + case "rj" if v != null => f = make(v, f, _.metrics.getCurrentRejectedJobs) + case "hu" if v != null => f = make(v, f, _.metrics.getHeapMemoryUsed) + case "hm" if v != null => f = make(v, f, _.metrics.getHeapMemoryMaximum) + case _ => () + } + }) + + show(n => f(n), hosts, all) + } catch { - case e: UnknownHostException => scold("Unknown host: " + a._2).^^ - - "" // Never happens. + case e: NumberFormatException => scold(e) + case e: IgniteException => scold(e) } - ).filter(!_.isEmpty).toSet - - val all = hasArgFlag("a", argLst) - - var f: NodeFilter = (ClusterNode) => true - - try { - argLst foreach (arg => { - val (n, v) = arg - - n match { - case "cc" if v != null => f = make(v, f, _.metrics.getTotalCpus) - case "cl" if v != null => f = make(v, f, (n: ClusterNode) => - (n.metrics.getCurrentCpuLoad * 100).toLong) - case "aj" if v != null => f = make(v, f, _.metrics.getCurrentActiveJobs) - case "cj" if v != null => f = make(v, f, _.metrics.getCurrentCancelledJobs) - case "tc" if v != null => f = make(v, f, _.metrics.getCurrentThreadCount) - case "ut" if v != null => f = make(v, f, _.metrics.getUpTime) - case "je" if v != null => f = make(v, f, _.metrics.getCurrentJobExecuteTime) - case "jw" if v != null => f = make(v, f, _.metrics.getCurrentJobWaitTime) - case "wj" if v != null => f = make(v, f, _.metrics.getCurrentWaitingJobs) - case "rj" if v != null => f = make(v, f, _.metrics.getCurrentRejectedJobs) - case "hu" if v != null => f = make(v, f, _.metrics.getHeapMemoryUsed) - case "hm" if v != null => f = make(v, f, _.metrics.getHeapMemoryMaximum) - case _ => () - } - }) - - show(n => f(n), hosts, all) - } - catch { - case e: NumberFormatException => scold(e) - case e: IgniteException => scold(e) } } } @@ -322,6 +338,7 @@ class VisorTopologyCommand extends VisorConsoleCommand { val sumT = VisorTextTable() + sumT += ("Active", ignite.active) sumT += ("Total hosts", U.neighborhood(nodes).size) sumT += ("Total nodes", m.getTotalNodes) sumT += ("Total CPUs", m.getTotalCpus) @@ -348,7 +365,9 @@ object VisorTopologyCommand { name = "top", shortInfo = "Prints current topology.", spec = List( - "top {-c1=e1 -c2=e2 ... -ck=ek} {-h= ... -h=} {-a}" + "top {-c1=e1 -c2=e2 ... -ck=ek} {-h= ... -h=} {-a}", + "top -activate", + "top -deactivate" ), args = List( "-ck=ek" -> List( @@ -388,6 +407,12 @@ object VisorTopologyCommand { "-a" -> List( "This defines whether to show a separate table of nodes", "with detail per-node information." + ), + "-activate" -> List( + "Activate cluster." + ), + "-deactivate" -> List( + "Deactivate cluster." ) ), examples = List( @@ -398,7 +423,11 @@ object VisorTopologyCommand { "top -h=10.34.2.122 -h=10.65.3.11" -> "Prints topology for provided hosts.", "top" -> - "Prints full topology." + "Prints full topology.", + "top -activate" -> + "Activate cluster.", + "top -deactivate" -> + "Deactivate cluster." ), emptyArgs = cmd.top, withArgs = cmd.top diff --git a/modules/visor-console/src/test/scala/org/apache/ignite/visor/VisorRuntimeBaseSpec.scala b/modules/visor-console/src/test/scala/org/apache/ignite/visor/VisorRuntimeBaseSpec.scala index 38db76074b39b..f5d6e561bb236 100644 --- a/modules/visor-console/src/test/scala/org/apache/ignite/visor/VisorRuntimeBaseSpec.scala +++ b/modules/visor-console/src/test/scala/org/apache/ignite/visor/VisorRuntimeBaseSpec.scala @@ -20,8 +20,8 @@ package org.apache.ignite.visor import org.apache.ignite.Ignition import org.apache.ignite.configuration.IgniteConfiguration import org.apache.ignite.visor.commands.open.VisorOpenCommand._ - import org.scalatest._ +import VisorRuntimeBaseSpec._ /** * Base abstract class for unit tests requiring Visor runtime. @@ -45,7 +45,7 @@ abstract class VisorRuntimeBaseSpec(private[this] val num: Int) extends FunSpec } protected def openVisor() { - visor.open(config("visor-demo-node"), "n/a") + visor.open(config(VISOR_INSTANCE_NAME), "n/a") } protected def closeVisorQuiet() { @@ -75,3 +75,8 @@ abstract class VisorRuntimeBaseSpec(private[this] val num: Int) extends FunSpec closeVisorQuiet() } } + +/** Singleton companion object. */ +object VisorRuntimeBaseSpec { + val VISOR_INSTANCE_NAME = "visor-demo-node" +} diff --git a/modules/visor-console/src/test/scala/org/apache/ignite/visor/commands/top/VisorActivationCommandSpec.scala b/modules/visor-console/src/test/scala/org/apache/ignite/visor/commands/top/VisorActivationCommandSpec.scala new file mode 100644 index 0000000000000..fb2a7f439c816 --- /dev/null +++ b/modules/visor-console/src/test/scala/org/apache/ignite/visor/commands/top/VisorActivationCommandSpec.scala @@ -0,0 +1,67 @@ +/* + * 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.visor.commands.top + +import org.apache.ignite.Ignition +import org.apache.ignite.configuration.{IgniteConfiguration, MemoryConfiguration, PersistentStoreConfiguration} +import org.apache.ignite.visor.commands.top.VisorTopologyCommand._ +import org.apache.ignite.visor.{VisorRuntimeBaseSpec, visor} +import VisorRuntimeBaseSpec._ + +/** + * Unit test for cluster activation commands. + */ +class VisorActivationCommandSpec extends VisorRuntimeBaseSpec(2) { + override protected def config(name: String): IgniteConfiguration = { + val cfg = super.config(name) + + val memCfg = new MemoryConfiguration + memCfg.setDefaultMemoryPolicySize(10 * 1024 * 1024) + + cfg.setMemoryConfiguration(memCfg) + cfg.setPersistentStoreConfiguration(new PersistentStoreConfiguration) + + cfg + } + + describe("A 'top' visor command for cluster activation") { + it("should activate cluster") { + assert(!Ignition.ignite(VISOR_INSTANCE_NAME).active()) + + visor.top() + + visor.top("-activate") + + visor.top() + + assert(Ignition.ignite(VISOR_INSTANCE_NAME).active()) + } + + it("should deactivate cluster") { + assert(Ignition.ignite(VISOR_INSTANCE_NAME).active()) + + visor.top() + + visor.top("-deactivate") + + visor.top() + + assert(!Ignition.ignite(VISOR_INSTANCE_NAME).active()) + } + } +} diff --git a/modules/visor-console/src/test/scala/org/apache/ignite/visor/testsuites/VisorConsoleSelfTestSuite.scala b/modules/visor-console/src/test/scala/org/apache/ignite/visor/testsuites/VisorConsoleSelfTestSuite.scala index f23a7c9ac18f9..5fbc8a09719e7 100644 --- a/modules/visor-console/src/test/scala/org/apache/ignite/visor/testsuites/VisorConsoleSelfTestSuite.scala +++ b/modules/visor-console/src/test/scala/org/apache/ignite/visor/testsuites/VisorConsoleSelfTestSuite.scala @@ -39,7 +39,7 @@ import org.apache.ignite.visor.commands.open.VisorOpenCommandSpec import org.apache.ignite.visor.commands.ping.VisorPingCommandSpec import org.apache.ignite.visor.commands.start.VisorStartCommandSpec import org.apache.ignite.visor.commands.tasks.VisorTasksCommandSpec -import org.apache.ignite.visor.commands.top.VisorTopologyCommandSpec +import org.apache.ignite.visor.commands.top.{VisorActivationCommandSpec, VisorTopologyCommandSpec} import org.junit.runner.RunWith import org.scalatest.Suites import org.scalatest.junit.JUnitRunner @@ -69,6 +69,7 @@ class VisorConsoleSelfTestSuite extends Suites ( new VisorStartCommandSpec, new VisorTasksCommandSpec, new VisorTopologyCommandSpec, + new VisorActivationCommandSpec, new VisorArgListSpec ) { // Mimic GridTestUtils.getNextMulticastGroup behavior because it can't be imported here From 5cefdef0ccf2f24053068e529d4eeb3e0eddf1d6 Mon Sep 17 00:00:00 2001 From: devozerov Date: Thu, 17 Aug 2017 11:24:06 +0300 Subject: [PATCH 032/145] IGNITE-6080: DML batches are now grouped by affinity. This closes #2454. --- .../query/h2/DmlStatementsProcessor.java | 274 +++++++++++------- 1 file changed, 163 insertions(+), 111 deletions(-) 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 4f7c288f86c22..0ff9cfef6b60a 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 @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import javax.cache.processor.EntryProcessor; @@ -46,7 +47,9 @@ import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.binary.BinaryObjectBuilder; import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheOperationContext; import org.apache.ignite.internal.processors.cache.GridCacheAdapter; import org.apache.ignite.internal.processors.cache.GridCacheContext; @@ -83,7 +86,6 @@ import org.h2.table.Column; import org.h2.util.DateTimeUtils; import org.h2.util.LocalDateTimeUtils; -import org.h2.value.DataType; import org.h2.value.Value; import org.h2.value.ValueDate; import org.h2.value.ValueTime; @@ -365,7 +367,7 @@ private UpdateResult executeUpdateStatement(String schemaName, final GridCacheCo QueryCursorImpl> cur; // Do a two-step query only if locality flag is not set AND if plan's SELECT corresponds to an actual - // subquery and not some dummy stuff like "select 1, 2, 3;" + // sub-query and not some dummy stuff like "select 1, 2, 3;" if (!loc && !plan.isLocSubqry) { SqlFieldsQuery newFieldsQry = new SqlFieldsQuery(plan.selectQry, fieldsQry.isCollocated()) .setArgs(fieldsQry.getArgs()) @@ -493,52 +495,28 @@ private static UpdateResult doFastUpdate(UpdatePlan plan, Object[] args) throws @SuppressWarnings({"unchecked", "ConstantConditions", "ThrowableResultOfMethodCallIgnored"}) private UpdateResult doDelete(GridCacheContext cctx, Iterable> cursor, int pageSize) throws IgniteCheckedException { - // With DELETE, we have only two columns - key and value. - long res = 0; + BatchSender sender = new BatchSender(cctx, pageSize); - // Keys that failed to DELETE due to concurrent updates. - List failedKeys = new ArrayList<>(); + for (List row : cursor) { + if (row.size() != 2) { + U.warn(log, "Invalid row size on DELETE - expected 2, got " + row.size()); - SQLException resEx = null; - - - Iterator> it = cursor.iterator(); - Map> rows = new LinkedHashMap<>(); - - while (it.hasNext()) { - List e = it.next(); - if (e.size() != 2) { - U.warn(log, "Invalid row size on DELETE - expected 2, got " + e.size()); continue; } - rows.put(e.get(0), new ModifyingEntryProcessor(e.get(1), RMV)); - - if ((pageSize > 0 && rows.size() == pageSize) || (!it.hasNext())) { - PageProcessingResult pageRes = processPage(cctx, rows); - - res += pageRes.cnt; - - failedKeys.addAll(F.asList(pageRes.errKeys)); + sender.add(row.get(0), new ModifyingEntryProcessor(row.get(1), RMV)); + } - if (pageRes.ex != null) { - if (resEx == null) - resEx = pageRes.ex; - else - resEx.setNextException(pageRes.ex); - } + sender.flush(); - if (it.hasNext()) - rows.clear(); // No need to clear after the last batch. - } - } + SQLException resEx = sender.error(); if (resEx != null) { - if (!F.isEmpty(failedKeys)) { + if (!F.isEmpty(sender.failedKeys())) { // Don't go for a re-run if processing of some keys yielded exceptions and report keys that // had been modified concurrently right away. String msg = "Failed to DELETE some keys because they had been modified concurrently " + - "[keys=" + failedKeys + ']'; + "[keys=" + sender.failedKeys() + ']'; SQLException conEx = createJdbcSqlException(msg, IgniteQueryErrorCode.CONCURRENT_UPDATE); @@ -550,7 +528,7 @@ private UpdateResult doDelete(GridCacheContext cctx, Iterable> cursor, i throw new IgniteSQLException(resEx); } - return new UpdateResult(res, failedKeys.toArray()); + return new UpdateResult(sender.updateCount(), sender.failedKeys().toArray()); } /** @@ -579,20 +557,10 @@ private UpdateResult doUpdate(UpdatePlan plan, Iterable> cursor, int pag // or if its list of updated columns includes only _val, i.e. is single element. boolean hasProps = !hasNewVal || updatedColNames.length > 1; - long res = 0; - - Map> rows = new LinkedHashMap<>(); - - // Keys that failed to UPDATE due to concurrent updates. - List failedKeys = new ArrayList<>(); - - SQLException resEx = null; - - Iterator> it = cursor.iterator(); + BatchSender sender = new BatchSender(cctx, pageSize); - while (it.hasNext()) { - List e = it.next(); - Object key = e.get(0); + for (List row : cursor) { + Object key = row.get(0); Object newVal; @@ -606,10 +574,10 @@ private UpdateResult doUpdate(UpdatePlan plan, Iterable> cursor, int pag assert prop != null; - newColVals.put(plan.colNames[i], convert(e.get(i + 2), desc, prop.type(), plan.colTypes[i])); + newColVals.put(plan.colNames[i], convert(row.get(i + 2), desc, prop.type(), plan.colTypes[i])); } - newVal = plan.valSupplier.apply(e); + newVal = plan.valSupplier.apply(row); if (newVal == null) throw new IgniteSQLException("New value for UPDATE must not be null", IgniteQueryErrorCode.NULL_VALUE); @@ -643,38 +611,24 @@ private UpdateResult doUpdate(UpdatePlan plan, Iterable> cursor, int pag newVal = ((BinaryObjectBuilder) newVal).build(); } - Object srcVal = e.get(1); + Object srcVal = row.get(1); if (bin && !(srcVal instanceof BinaryObject)) srcVal = cctx.grid().binary().toBinary(srcVal); - rows.put(key, new ModifyingEntryProcessor(srcVal, new EntryValueUpdater(newVal))); - - if ((pageSize > 0 && rows.size() == pageSize) || (!it.hasNext())) { - PageProcessingResult pageRes = processPage(cctx, rows); - - res += pageRes.cnt; - - failedKeys.addAll(F.asList(pageRes.errKeys)); + sender.add(key, new ModifyingEntryProcessor(srcVal, new EntryValueUpdater(newVal))); + } - if (pageRes.ex != null) { - if (resEx == null) - resEx = pageRes.ex; - else - resEx.setNextException(pageRes.ex); - } + sender.flush(); - if (it.hasNext()) - rows.clear(); // No need to clear after the last batch. - } - } + SQLException resEx = sender.error(); if (resEx != null) { - if (!F.isEmpty(failedKeys)) { + if (!F.isEmpty(sender.failedKeys())) { // Don't go for a re-run if processing of some keys yielded exceptions and report keys that // had been modified concurrently right away. String msg = "Failed to UPDATE some keys because they had been modified concurrently " + - "[keys=" + failedKeys + ']'; + "[keys=" + sender.failedKeys() + ']'; SQLException dupEx = createJdbcSqlException(msg, IgniteQueryErrorCode.CONCURRENT_UPDATE); @@ -686,7 +640,7 @@ private UpdateResult doUpdate(UpdatePlan plan, Iterable> cursor, int pag throw new IgniteSQLException(resEx); } - return new UpdateResult(res, failedKeys.toArray()); + return new UpdateResult(sender.updateCount(), sender.failedKeys().toArray()); } /** @@ -864,47 +818,22 @@ private long doInsert(UpdatePlan plan, Iterable> cursor, int pageSize) t IgniteQueryErrorCode.DUPLICATE_KEY); } else { - Map> rows = plan.isLocSubqry ? - new LinkedHashMap>(plan.rowsNum) : - new LinkedHashMap>(); - // Keys that failed to INSERT due to duplication. - List duplicateKeys = new ArrayList<>(); - - int resCnt = 0; - - SQLException resEx = null; - - Iterator> it = cursor.iterator(); - - while (it.hasNext()) { - List row = it.next(); - - final IgniteBiTuple t = rowToKeyValue(cctx, row, plan); - - rows.put(t.getKey(), new InsertEntryProcessor(t.getValue())); - - if (!it.hasNext() || (pageSize > 0 && rows.size() == pageSize)) { - PageProcessingResult pageRes = processPage(cctx, rows); + BatchSender sender = new BatchSender(cctx, pageSize); - resCnt += pageRes.cnt; + for (List row : cursor) { + final IgniteBiTuple keyValPair = rowToKeyValue(cctx, row, plan); - duplicateKeys.addAll(F.asList(pageRes.errKeys)); + sender.add(keyValPair.getKey(), new InsertEntryProcessor(keyValPair.getValue())); + } - if (pageRes.ex != null) { - if (resEx == null) - resEx = pageRes.ex; - else - resEx.setNextException(pageRes.ex); - } + sender.flush(); - rows.clear(); - } - } + SQLException resEx = sender.error(); - if (!F.isEmpty(duplicateKeys)) { + if (!F.isEmpty(sender.failedKeys())) { String msg = "Failed to INSERT some keys because they are already in cache " + - "[keys=" + duplicateKeys + ']'; + "[keys=" + sender.failedKeys() + ']'; SQLException dupEx = new SQLException(msg, null, IgniteQueryErrorCode.DUPLICATE_KEY); @@ -917,7 +846,7 @@ private long doInsert(UpdatePlan plan, Iterable> cursor, int pageSize) t if (resEx != null) throw new IgniteSQLException(resEx); - return resCnt; + return sender.updateCount(); } } @@ -1133,7 +1062,7 @@ private final static class UpdateResult { /** Number of processed items. */ final long cnt; - /** Keys that failed to be UPDATEd or DELETEd due to concurrent modification of values. */ + /** Keys that failed to be updated or deleted due to concurrent modification of values. */ @NotNull final Object[] errKeys; @@ -1150,7 +1079,7 @@ private final static class PageProcessingResult { /** Number of successfully processed items. */ final long cnt; - /** Keys that failed to be UPDATEd or DELETEd due to concurrent modification of values. */ + /** Keys that failed to be updated or deleted due to concurrent modification of values. */ @NotNull final Object[] errKeys; @@ -1193,4 +1122,127 @@ private PageProcessingErrorResult(@NotNull Object[] errKeys, SQLException ex, in this.ex = ex; } } + + /** + * Batch sender class. + */ + private static class BatchSender { + /** Cache context. */ + private final GridCacheContext cctx; + + /** Batch size. */ + private final int size; + + /** Batches. */ + private final Map>> batches = new HashMap<>(); + + /** Result count. */ + private long updateCnt; + + /** Failed keys. */ + private List failedKeys; + + /** Exception. */ + private SQLException err; + + /** + * Constructor. + * + * @param cctx Cache context. + * @param size Batch. + */ + public BatchSender(GridCacheContext cctx, int size) { + this.cctx = cctx; + this.size = size; + } + + /** + * Add entry to batch. + * + * @param key Key. + * @param proc Processor. + */ + public void add(Object key, EntryProcessor proc) throws IgniteCheckedException { + ClusterNode node = cctx.affinity().primaryByKey(key, AffinityTopologyVersion.NONE); + + if (node == null) + throw new IgniteCheckedException("Failed to map key to node."); + + UUID nodeId = node.id(); + + Map> batch = batches.get(nodeId); + + if (batch == null) { + batch = new HashMap<>(); + + batches.put(nodeId, batch); + } + + batch.put(key, proc); + + if (batch.size() >= size) { + sendBatch(batch); + + batch.clear(); + } + } + + /** + * Flush any remaining entries. + * + * @throws IgniteCheckedException If failed. + */ + public void flush() throws IgniteCheckedException { + for (Map> batch : batches.values()) { + if (!batch.isEmpty()) + sendBatch(batch); + } + } + + /** + * @return Update count. + */ + public long updateCount() { + return updateCnt; + } + + /** + * @return Failed keys. + */ + public List failedKeys() { + return failedKeys != null ? failedKeys : Collections.emptyList(); + } + + /** + * @return Error. + */ + public SQLException error() { + return err; + } + + /** + * Send the batch. + * + * @param batch Batch. + * @throws IgniteCheckedException If failed. + */ + private void sendBatch(Map> batch) + throws IgniteCheckedException { + PageProcessingResult pageRes = processPage(cctx, batch); + + updateCnt += pageRes.cnt; + + if (failedKeys == null) + failedKeys = new ArrayList<>(); + + failedKeys.addAll(F.asList(pageRes.errKeys)); + + if (pageRes.ex != null) { + if (err == null) + err = pageRes.ex; + else + err.setNextException(pageRes.ex); + } + } + } } From 50a42280a08b71ff023f06262a19011f4c2bef37 Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Thu, 17 Aug 2017 13:41:37 +0300 Subject: [PATCH 033/145] IGNITE-5233: JDBC thind driver: implemented metadata methods. This closes #2079. --- .../jdbc/thin/JdbcThinMetadataSelfTest.java | 337 +++- .../jdbc/thin/JdbcThinConnection.java | 20 +- .../jdbc/thin/JdbcThinDatabaseMetadata.java | 1587 +++++++++++++++++ .../jdbc/thin/JdbcThinParameterMetadata.java | 115 ++ .../jdbc/thin/JdbcThinPreparedStatement.java | 27 +- .../internal/jdbc/thin/JdbcThinResultSet.java | 47 +- .../internal/jdbc/thin/JdbcThinStatement.java | 4 +- .../internal/jdbc/thin/JdbcThinTcpIo.java | 117 +- .../odbc/SqlListenerNioListener.java | 9 +- .../odbc/SqlListenerRequestHandler.java | 9 + .../odbc/jdbc/JdbcBatchExecuteRequest.java | 20 +- .../odbc/jdbc/JdbcBatchExecuteResult.java | 6 + .../processors/odbc/jdbc/JdbcColumnMeta.java | 75 +- .../processors/odbc/jdbc/JdbcIndexMeta.java | 192 ++ .../odbc/jdbc/JdbcMetaColumnsRequest.java | 102 ++ .../odbc/jdbc/JdbcMetaColumnsResult.java | 99 + .../odbc/jdbc/JdbcMetaIndexesRequest.java | 88 + .../odbc/jdbc/JdbcMetaIndexesResult.java | 98 + .../odbc/jdbc/JdbcMetaParamsRequest.java | 87 + .../odbc/jdbc/JdbcMetaParamsResult.java | 97 + .../odbc/jdbc/JdbcMetaPrimaryKeysRequest.java | 88 + .../odbc/jdbc/JdbcMetaPrimaryKeysResult.java | 99 + .../odbc/jdbc/JdbcMetaSchemasRequest.java | 73 + .../odbc/jdbc/JdbcMetaSchemasResult.java | 73 + .../odbc/jdbc/JdbcMetaTablesRequest.java | 87 + .../odbc/jdbc/JdbcMetaTablesResult.java | 97 + .../odbc/jdbc/JdbcParameterMeta.java | 165 ++ .../odbc/jdbc/JdbcPrimaryKeyMeta.java | 131 ++ .../odbc/jdbc/JdbcQueryCloseRequest.java | 4 +- .../odbc/jdbc/JdbcQueryExecuteRequest.java | 8 +- .../odbc/jdbc/JdbcQueryExecuteResult.java | 12 +- .../odbc/jdbc/JdbcQueryFetchRequest.java | 4 +- .../odbc/jdbc/JdbcQueryFetchResult.java | 12 +- .../odbc/jdbc/JdbcQueryMetadataRequest.java | 18 +- .../odbc/jdbc/JdbcQueryMetadataResult.java | 14 +- .../processors/odbc/jdbc/JdbcRequest.java | 67 +- .../odbc/jdbc/JdbcRequestHandler.java | 273 ++- .../processors/odbc/jdbc/JdbcResult.java | 58 +- .../processors/odbc/jdbc/JdbcTableMeta.java | 82 + .../processors/odbc/jdbc/JdbcUtils.java | 37 +- .../odbc/odbc/OdbcRequestHandler.java | 6 + .../processors/query/GridQueryProcessor.java | 2 +- .../query/GridQueryTypeDescriptor.java | 7 + .../query/QueryTypeDescriptorImpl.java | 15 + .../internal/processors/query/QueryUtils.java | 7 +- .../h2/GridIndexingSpiAbstractSelfTest.java | 18 +- 46 files changed, 4472 insertions(+), 121 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinParameterMetadata.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcIndexMeta.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesRequest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesResult.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcPrimaryKeyMeta.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcTableMeta.java diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java index 2dae107798e1e..01b2e8a6e97a9 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataSelfTest.java @@ -21,16 +21,27 @@ import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; +import java.sql.Types; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Set; import org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.QueryIndex; import org.apache.ignite.cache.affinity.AffinityKey; -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.IgniteVersionUtils; +import org.apache.ignite.internal.binary.BinaryMarshaller; 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; @@ -66,15 +77,18 @@ public class JdbcThinMetadataSelfTest extends JdbcThinAbstractSelfTest { } /** + * @param qryEntity Query entity. * @return Cache configuration. */ - protected CacheConfiguration cacheConfiguration() { + protected CacheConfiguration cacheConfiguration(QueryEntity qryEntity) { CacheConfiguration cache = defaultCacheConfiguration(); cache.setCacheMode(PARTITIONED); cache.setBackups(1); cache.setWriteSynchronizationMode(FULL_SYNC); + cache.setQueryEntities(Collections.singletonList(qryEntity)); + return cache; } @@ -84,22 +98,49 @@ protected CacheConfiguration cacheConfiguration() { startGridsMultiThreaded(3); - IgniteCache orgCache = jcache(grid(0), cacheConfiguration(), "org", - String.class, Organization.class); + IgniteCache orgCache = jcache(grid(0), + cacheConfiguration(new QueryEntity(String.class.getName(), Organization.class.getName()) + .addQueryField("id", Integer.class.getName(), null) + .addQueryField("name", String.class.getName(), null) + .setIndexes(Arrays.asList( + new QueryIndex("id"), + new QueryIndex("name", false, "org_name_index") + ))), "org"); assert orgCache != null; orgCache.put("o1", new Organization(1, "A")); orgCache.put("o2", new Organization(2, "B")); - IgniteCache personCache = jcache(grid(0), cacheConfiguration(), "pers", - AffinityKey.class, Person.class); + LinkedHashMap persFields = new LinkedHashMap<>(); + + persFields.put("name", true); + persFields.put("age", false); + + IgniteCache personCache = jcache(grid(0), cacheConfiguration( + new QueryEntity(AffinityKey.class.getName(), Person.class.getName()) + .addQueryField("name", String.class.getName(), null) + .addQueryField("age", Integer.class.getName(), null) + .addQueryField("orgId", Integer.class.getName(), null) + .setIndexes(Arrays.asList( + new QueryIndex("orgId"), + new QueryIndex().setFields(persFields))) + ), "pers"); assert personCache != null; personCache.put(new AffinityKey<>("p1", "o1"), new Person("John White", 25, 1)); personCache.put(new AffinityKey<>("p2", "o1"), new Person("Joe Black", 35, 1)); personCache.put(new AffinityKey<>("p3", "o2"), new Person("Mike Green", 40, 2)); + + try (Connection conn = DriverManager.getConnection(URL)) { + Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE TEST (ID INT, NAME VARCHAR(50), VAL VARCHAR(50), PRIMARY KEY (ID, NAME))"); + stmt.execute("CREATE TABLE \"Quoted\" (\"Id\" INT primary key, \"Name\" VARCHAR(50))"); + stmt.execute("CREATE INDEX \"MyTestIndex quoted\" on \"Quoted\" (\"Id\" DESC)"); + stmt.execute("CREATE INDEX IDX ON TEST (ID ASC)"); + } } /** {@inheritDoc} */ @@ -147,8 +188,6 @@ public void testResultSetMetaData() throws Exception { * @throws Exception If failed. */ public void testGetTables() throws Exception { - fail("https://issues.apache.org/jira/browse/IGNITE-5233"); - try (Connection conn = DriverManager.getConnection(URL)) { DatabaseMetaData meta = conn.getMetaData(); @@ -181,18 +220,46 @@ public void testGetTables() throws Exception { } } + /** + * @throws Exception If failed. + */ + public void testGetAllTables() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + DatabaseMetaData meta = conn.getMetaData(); + + ResultSet rs = meta.getTables(null, null, null, null); + + Set expectedTbls = new HashSet<>(Arrays.asList( + "org.ORGANIZATION", + "pers.PERSON", + "PUBLIC.TEST", + "PUBLIC.Quoted")); + + Set actualTbls = new HashSet<>(expectedTbls.size()); + + while(rs.next()) { + actualTbls.add(rs.getString("TABLE_SCHEM") + '.' + + rs.getString("TABLE_NAME")); + } + + assert expectedTbls.equals(actualTbls) : "expectedTbls=" + expectedTbls + + ", actualTbls" + actualTbls; + } + } + /** * @throws Exception If failed. */ public void testGetColumns() throws Exception { - fail("https://issues.apache.org/jira/browse/IGNITE-5233"); + final boolean primitivesInformationIsLostAfterStore = ignite(0).configuration().getMarshaller() + instanceof BinaryMarshaller; try (Connection conn = DriverManager.getConnection(URL)) { conn.setSchema("pers"); DatabaseMetaData meta = conn.getMetaData(); - ResultSet rs = meta.getColumns("", "pers", "Person", "%"); + ResultSet rs = meta.getColumns("", "pers", "PERSON", "%"); assert rs != null; @@ -216,7 +283,7 @@ public void testGetColumns() throws Exception { } else if ("AGE".equals(name) || "ORGID".equals(name)) { assert rs.getInt("DATA_TYPE") == INTEGER; assert "INTEGER".equals(rs.getString("TYPE_NAME")); - assert rs.getInt("NULLABLE") == 0; + assertEquals(primitivesInformationIsLostAfterStore ? 1 : 0, rs.getInt("NULLABLE")); } if ("_KEY".equals(name)) { assert rs.getInt("DATA_TYPE") == OTHER; @@ -235,7 +302,7 @@ public void testGetColumns() throws Exception { assert names.isEmpty(); assert cnt == 3; - rs = meta.getColumns("", "org", "Organization", "%"); + rs = meta.getColumns("", "org", "ORGANIZATION", "%"); assert rs != null; @@ -280,22 +347,243 @@ public void testGetColumns() throws Exception { /** * @throws Exception If failed. */ - public void testMetadataResultSetClose() throws Exception { - fail("https://issues.apache.org/jira/browse/IGNITE-5233"); + public void testGetAllColumns() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + DatabaseMetaData meta = conn.getMetaData(); + + ResultSet rs = meta.getColumns(null, null, null, null); + + Set expectedCols = new HashSet<>(Arrays.asList( + "org.ORGANIZATION.ID", + "org.ORGANIZATION.NAME", + "pers.PERSON.ORGID", + "pers.PERSON.AGE", + "pers.PERSON.NAME", + "PUBLIC.TEST.ID", + "PUBLIC.TEST.NAME", + "PUBLIC.TEST.VAL", + "PUBLIC.Quoted.Id", + "PUBLIC.Quoted.Name")); + + Set actualCols = new HashSet<>(expectedCols.size()); + + while(rs.next()) { + actualCols.add(rs.getString("TABLE_SCHEM") + '.' + + rs.getString("TABLE_NAME") + "." + + rs.getString("COLUMN_NAME")); + } + + assert expectedCols.equals(actualCols) : "expectedCols=" + expectedCols + + ", actualCols" + actualCols; + } + } + + /** + * @throws Exception If failed. + */ + public void testInvalidCatalog() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + DatabaseMetaData meta = conn.getMetaData(); + + ResultSet rs = meta.getSchemas("q", null); + + assert !rs.next() : "Results must be empty"; + + rs = meta.getTables("q", null, null, null); + + assert !rs.next() : "Results must be empty"; + + rs = meta.getColumns("q", null, null, null); + + assert !rs.next() : "Results must be empty"; + + rs = meta.getIndexInfo("q", null, null, false, false); + + assert !rs.next() : "Results must be empty"; + + rs = meta.getPrimaryKeys("q", null, null); + + assert !rs.next() : "Results must be empty"; + } + } + + /** + * @throws Exception If failed. + */ + public void testIndexMetadata() throws Exception { + try (Connection conn = DriverManager.getConnection(URL); + ResultSet rs = conn.getMetaData().getIndexInfo(null, "pers", "PERSON", false, false)) { + + int cnt = 0; + + while (rs.next()) { + String idxName = rs.getString("INDEX_NAME"); + String field = rs.getString("COLUMN_NAME"); + String ascOrDesc = rs.getString("ASC_OR_DESC"); + + assert rs.getShort("TYPE") == DatabaseMetaData.tableIndexOther; + + if ("PERSON_ORGID_ASC_IDX".equals(idxName)) { + assert "ORGID".equals(field); + assert "A".equals(ascOrDesc); + } + else if ("PERSON_NAME_ASC_AGE_DESC_IDX".equals(idxName)) { + if ("NAME".equals(field)) + assert "A".equals(ascOrDesc); + else if ("AGE".equals(field)) + assert "D".equals(ascOrDesc); + else + fail("Unexpected field: " + field); + } + else + fail("Unexpected index: " + idxName); + + cnt++; + } + + assert cnt == 3; + } + } + + /** + * @throws Exception If failed. + */ + public void testGetAllIndexes() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + ResultSet rs = conn.getMetaData().getIndexInfo(null, null, null, false, false); + + Set expectedIdxs = new HashSet<>(Arrays.asList( + "org.ORGANIZATION.ORGANIZATION_ID_ASC_IDX", + "org.ORGANIZATION.ORG_NAME_INDEX", + "pers.PERSON.PERSON_ORGID_ASC_IDX", + "pers.PERSON.PERSON_NAME_ASC_AGE_DESC_IDX", + "PUBLIC.TEST.IDX", + "PUBLIC.Quoted.MyTestIndex quoted")); + + Set actualIdxs = new HashSet<>(expectedIdxs.size()); + + while(rs.next()) { + actualIdxs.add(rs.getString("TABLE_SCHEM") + + '.' + rs.getString("TABLE_NAME") + + '.' + rs.getString("INDEX_NAME")); + } + + assert expectedIdxs.equals(actualIdxs) : "expectedIdxs=" + expectedIdxs + + ", actualIdxs" + actualIdxs; + } + } + /** + * @throws Exception If failed. + */ + public void testPrimaryKeyMetadata() throws Exception { try (Connection conn = DriverManager.getConnection(URL); - ResultSet tbls = conn.getMetaData().getTables(null, null, "%", null)) { - int colCnt = tbls.getMetaData().getColumnCount(); + ResultSet rs = conn.getMetaData().getPrimaryKeys(null, "pers", "PERSON")) { + + int cnt = 0; + + while (rs.next()) { + assert "_KEY".equals(rs.getString("COLUMN_NAME")); + + cnt++; + } + + assert cnt == 1; + } + } + + /** + * @throws Exception If failed. + */ + public void testGetAllPrimaryKeys() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + ResultSet rs = conn.getMetaData().getPrimaryKeys(null, null, null); + + Set expectedPks = new HashSet<>(Arrays.asList( + "org.ORGANIZATION.PK_org_ORGANIZATION._KEY", + "pers.PERSON.PK_pers_PERSON._KEY", + "PUBLIC.TEST.PK_PUBLIC_TEST.ID", + "PUBLIC.TEST.PK_PUBLIC_TEST.NAME", + "PUBLIC.Quoted.PK_PUBLIC_Quoted.Id")); + + Set actualPks = new HashSet<>(expectedPks.size()); + + while(rs.next()) { + actualPks.add(rs.getString("TABLE_SCHEM") + + '.' + rs.getString("TABLE_NAME") + + '.' + rs.getString("PK_NAME") + + '.' + rs.getString("COLUMN_NAME")); + } + + assert expectedPks.equals(actualPks) : "expectedPks=" + expectedPks + + ", actualPks" + actualPks; + } + } + + /** + * @throws Exception If failed. + */ + public void testParametersMetadata() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + conn.setSchema("pers"); + + PreparedStatement stmt = conn.prepareStatement("select orgId from Person p where p.name > ? and p.orgId > ?"); + + ParameterMetaData meta = stmt.getParameterMetaData(); + + assert meta != null; + + assert meta.getParameterCount() == 2; + + assert meta.getParameterType(1) == Types.VARCHAR; + assert meta.isNullable(1) == ParameterMetaData.parameterNullableUnknown; + assert meta.getPrecision(1) == Integer.MAX_VALUE; + + assert meta.getParameterType(2) == Types.INTEGER; + assert meta.isNullable(2) == ParameterMetaData.parameterNullableUnknown; + } + } + + /** + * @throws Exception If failed. + */ + public void testSchemasMetadata() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + ResultSet rs = conn.getMetaData().getSchemas(); + + Set expectedSchemas = new HashSet<>(Arrays.asList("PUBLIC", "pers", "org")); - while (tbls.next()) { - for (int i = 0; i < colCnt; i++) - tbls.getObject(i + 1); + Set schemas = new HashSet<>(); + + while (rs.next()) { + schemas.add(rs.getString(1)); + + assert rs.getString(2) == null; } + + assert expectedSchemas.equals(schemas) : "Unexpected schemas: " + schemas + + ". Expected schemas: " + expectedSchemas; } - catch (Exception e) { - log.error("Unexpected exception", e); + } - fail(); + /** + * @throws Exception If failed. + */ + public void testEmptySchemasMetadata() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + ResultSet rs = conn.getMetaData().getSchemas(null, "qqq"); + + assert !rs.next() : "Empty result set is expected"; + } + } + + /** + * @throws Exception If failed. + */ + public void testVersions() throws Exception { + try (Connection conn = DriverManager.getConnection(URL)) { + assert conn.getMetaData().getDatabaseProductVersion().equals(IgniteVersionUtils.VER.toString()); + assert conn.getMetaData().getDriverVersion().equals(IgniteVersionUtils.VER.toString()); } } @@ -305,15 +593,12 @@ public void testMetadataResultSetClose() throws Exception { @SuppressWarnings("UnusedDeclaration") private static class Person implements Serializable { /** Name. */ - @QuerySqlField(index = false) private final String name; /** Age. */ - @QuerySqlField private final int age; /** Organization ID. */ - @QuerySqlField private final int orgId; /** @@ -338,11 +623,9 @@ private Person(String name, int age, int orgId) { @SuppressWarnings("UnusedDeclaration") private static class Organization implements Serializable { /** ID. */ - @QuerySqlField private final int id; /** Name. */ - @QuerySqlField(index = false) private final String name; /** 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 89ef2fca0ed8a..8836cd51e5e9b 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 @@ -64,6 +64,9 @@ public class JdbcThinConnection implements Connection { /** Logger. */ private static final Logger LOG = Logger.getLogger(JdbcThinConnection.class.getName()); + /** Connection URL. */ + private String url; + /** Schema name. */ private String schemaName; @@ -88,6 +91,9 @@ public class JdbcThinConnection implements Connection { /** Ignite endpoint. */ private JdbcThinTcpIo cliIo; + /** Jdbc metadata. Cache the JDBC object on the first access */ + private JdbcThinDatabaseMetadata metadata; + /** * Creates new connection. * @@ -99,6 +105,8 @@ public JdbcThinConnection(String url, Properties props) throws SQLException { assert url != null; assert props != null; + this.url = url; + holdability = HOLD_CURSORS_OVER_COMMIT; autoCommit = true; txIsolation = Connection.TRANSACTION_NONE; @@ -274,7 +282,10 @@ private void checkCursorOptions(int resSetType, int resSetConcurrency, @Override public DatabaseMetaData getMetaData() throws SQLException { ensureNotClosed(); - return null; + if (metadata == null) + metadata = new JdbcThinDatabaseMetadata(this); + + return metadata; } /** {@inheritDoc} */ @@ -665,4 +676,11 @@ private static int extractInt(Properties props, String propName, int dfltVal) th ", value=" + strVal + ']'); } } + + /** + * @return Connection URL. + */ + public String url() { + return url; + } } \ No newline at end of file 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 new file mode 100644 index 0000000000000..583bcec53148d --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinDatabaseMetadata.java @@ -0,0 +1,1587 @@ +/* + * 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.jdbc.thin; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.IgniteVersionUtils; +import org.apache.ignite.internal.jdbc2.JdbcUtils; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcColumnMeta; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcIndexMeta; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaColumnsResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaIndexesResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaPrimaryKeysResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaSchemasResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaTablesResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcPrimaryKeyMeta; +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.ResultSet.CONCUR_READ_ONLY; +import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT; +import static java.sql.ResultSet.TYPE_FORWARD_ONLY; +import static java.sql.RowIdLifetime.ROWID_UNSUPPORTED; + +/** + * JDBC database metadata implementation. + */ +@SuppressWarnings("RedundantCast") +public class JdbcThinDatabaseMetadata implements DatabaseMetaData { + /** Connection. */ + private final JdbcThinConnection conn; + + /** + * @param conn Connection. + */ + JdbcThinDatabaseMetadata(JdbcThinConnection conn) { + this.conn = conn; + } + + /** {@inheritDoc} */ + @Override public boolean allProceduresAreCallable() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean allTablesAreSelectable() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public String getURL() throws SQLException { + return conn.url(); + } + + /** {@inheritDoc} */ + @Override public String getUserName() throws SQLException { + return ""; + } + + /** {@inheritDoc} */ + @Override public boolean isReadOnly() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean nullsAreSortedHigh() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean nullsAreSortedLow() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean nullsAreSortedAtStart() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean nullsAreSortedAtEnd() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public String getDatabaseProductName() throws SQLException { + return "Ignite"; + } + + /** {@inheritDoc} */ + @Override public String getDatabaseProductVersion() throws SQLException { + return conn.io().igniteVersion().toString(); + } + + /** {@inheritDoc} */ + @Override public String getDriverName() throws SQLException { + return "Ignite JDBC Thin Driver"; + } + + /** {@inheritDoc} */ + @Override public String getDriverVersion() throws SQLException { + return IgniteVersionUtils.VER.toString(); + } + + /** {@inheritDoc} */ + @Override public int getDriverMajorVersion() { + return IgniteVersionUtils.VER.major(); + } + + /** {@inheritDoc} */ + @Override public int getDriverMinorVersion() { + return IgniteVersionUtils.VER.minor(); + } + + /** {@inheritDoc} */ + @Override public boolean usesLocalFiles() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean usesLocalFilePerTable() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsMixedCaseIdentifiers() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean storesUpperCaseIdentifiers() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean storesLowerCaseIdentifiers() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean storesMixedCaseIdentifiers() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public String getIdentifierQuoteString() throws SQLException { + return "\""; + } + + /** {@inheritDoc} */ + @Override public String getSQLKeywords() throws SQLException { + return "LIMIT,MINUS,ROWNUM,SYSDATE,SYSTIME,SYSTIMESTAMP,TODAY"; + } + + /** {@inheritDoc} */ + @Override public String getNumericFunctions() throws SQLException { + // TODO: IGNITE-6028 + return ""; + } + + /** {@inheritDoc} */ + @Override public String getStringFunctions() throws SQLException { + // TODO: IGNITE-6028 + return ""; + } + + /** {@inheritDoc} */ + @Override public String getSystemFunctions() throws SQLException { + // TODO: IGNITE-6028 + return ""; + } + + /** {@inheritDoc} */ + @Override public String getTimeDateFunctions() throws SQLException { + // TODO: IGNITE-6028 + return ""; + } + + /** {@inheritDoc} */ + @Override public String getSearchStringEscape() throws SQLException { + return "\\"; + } + + /** {@inheritDoc} */ + @Override public String getExtraNameCharacters() throws SQLException { + return ""; + } + + /** {@inheritDoc} */ + @Override public boolean supportsAlterTableWithAddColumn() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsAlterTableWithDropColumn() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsColumnAliasing() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean nullPlusNonNullIsNull() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsConvert() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsConvert(int fromType, int toType) throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsTableCorrelationNames() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsDifferentTableCorrelationNames() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsExpressionsInOrderBy() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsOrderByUnrelated() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsGroupBy() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsGroupByUnrelated() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsGroupByBeyondSelect() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsLikeEscapeClause() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsMultipleResultSets() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsMultipleTransactions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsNonNullableColumns() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsMinimumSQLGrammar() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCoreSQLGrammar() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsExtendedSQLGrammar() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsANSI92EntryLevelSQL() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsANSI92IntermediateSQL() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsANSI92FullSQL() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsIntegrityEnhancementFacility() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsOuterJoins() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsFullOuterJoins() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsLimitedOuterJoins() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public String getSchemaTerm() throws SQLException { + return ""; + } + + /** {@inheritDoc} */ + @Override public String getProcedureTerm() throws SQLException { + return ""; + } + + /** {@inheritDoc} */ + @Override public String getCatalogTerm() throws SQLException { + return ""; + } + + /** {@inheritDoc} */ + @Override public boolean isCatalogAtStart() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public String getCatalogSeparator() throws SQLException { + return ""; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSchemasInDataManipulation() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSchemasInProcedureCalls() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSchemasInTableDefinitions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSchemasInIndexDefinitions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCatalogsInDataManipulation() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCatalogsInProcedureCalls() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCatalogsInTableDefinitions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsPositionedDelete() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsPositionedUpdate() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSelectForUpdate() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsStoredProcedures() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSubqueriesInComparisons() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSubqueriesInExists() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSubqueriesInIns() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSubqueriesInQuantifieds() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsCorrelatedSubqueries() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsUnion() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsUnionAll() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public int getMaxBinaryLiteralLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxCharLiteralLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxColumnNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxColumnsInGroupBy() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxColumnsInIndex() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxColumnsInOrderBy() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxColumnsInSelect() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxColumnsInTable() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxConnections() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxCursorNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxIndexLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxSchemaNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxProcedureNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxCatalogNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxRowSize() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public int getMaxStatementLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxStatements() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxTableNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxTablesInSelect() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getMaxUserNameLength() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getDefaultTransactionIsolation() throws SQLException { + return TRANSACTION_NONE; + } + + /** {@inheritDoc} */ + @Override public boolean supportsTransactions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public ResultSet getProcedures(String catalog, String schemaPtrn, + String procedureNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "PROCEDURE_CAT", String.class), + new JdbcColumnMeta(null, null, "PROCEDURE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "PROCEDURE_NAME", String.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "PROCEDURE_TYPE", String.class), + new JdbcColumnMeta(null, null, "SPECIFIC_NAME", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getProcedureColumns(String catalog, String schemaPtrn, String procedureNamePtrn, + String colNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "PROCEDURE_CAT", String.class), + new JdbcColumnMeta(null, null, "PROCEDURE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "PROCEDURE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_TYPE", Short.class), + new JdbcColumnMeta(null, null, "COLUMN_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "PRECISION", Integer.class), + new JdbcColumnMeta(null, null, "LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "SCALE", Short.class), + new JdbcColumnMeta(null, null, "RADIX", Short.class), + new JdbcColumnMeta(null, null, "NULLABLE", Short.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "COLUMN_DEF", String.class), + new JdbcColumnMeta(null, null, "SQL_DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "SQL_DATETIME_SUB", Integer.class), + new JdbcColumnMeta(null, null, "CHAR_OCTET_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "ORDINAL_POSITION", Integer.class), + new JdbcColumnMeta(null, null, "IS_NULLABLE", String.class), + new JdbcColumnMeta(null, null, "SPECIFIC_NAME", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getTables(String catalog, String schemaPtrn, String tblNamePtrn, + String[] tblTypes) throws SQLException { + if (conn.isClosed()) + throw new SQLException("Connection is closed."); + + final List meta = Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "TABLE_TYPE", String.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "TYPE_CAT", String.class), + new JdbcColumnMeta(null, null, "TYPE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "SELF_REFERENCING_COL_NAME", String.class), + new JdbcColumnMeta(null, null, "REF_GENERATION", String.class)); + + boolean tblTypeMatch = false; + + if (tblTypes == null) + tblTypeMatch = true; + else { + for (String type : tblTypes) { + if ("TABLE".equals(type)) { + tblTypeMatch = true; + + break; + } + } + } + + if (!validCatalogPattern(catalog) || !tblTypeMatch) + return new JdbcThinResultSet(Collections.>emptyList(), meta); + + try { + JdbcMetaTablesResult res = conn.io().tablesMeta(schemaPtrn, tblNamePtrn); + + List> rows = new LinkedList<>(); + + for (JdbcTableMeta tblMeta : res.meta()) + rows.add(tableRow(tblMeta)); + + return new JdbcThinResultSet(rows, meta); + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } + } + + /** + * @param tblMeta Table metadata. + * @return Table metadata row. + */ + private List tableRow(JdbcTableMeta tblMeta) { + List row = new ArrayList<>(10); + + row.add(null); + row.add(tblMeta.schemaName()); + row.add(tblMeta.tableName()); + row.add("TABLE"); + row.add(null); + row.add(null); + row.add(null); + row.add(null); + row.add(null); + row.add(null); + + return row; + } + + /** {@inheritDoc} */ + @Override public ResultSet getSchemas() throws SQLException { + return getSchemas(null, "%"); + } + + /** {@inheritDoc} */ + @SuppressWarnings("ArraysAsListWithZeroOrOneArgument") + @Override public ResultSet getCatalogs() throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), + Arrays.asList(new JdbcColumnMeta(null, null, "TABLE_CAT", String.class))); + } + + /** {@inheritDoc} */ + @SuppressWarnings("ArraysAsListWithZeroOrOneArgument") + @Override public ResultSet getTableTypes() throws SQLException { + return new JdbcThinResultSet(Collections.singletonList(Collections.singletonList("TABLE")), + Arrays.asList(new JdbcColumnMeta(null, null, "TABLE_TYPE", String.class))); + } + + /** {@inheritDoc} */ + @Override public ResultSet getColumns(String catalog, String schemaPtrn, String tblNamePtrn, + String colNamePtrn) throws SQLException { + if (conn.isClosed()) + throw new SQLException("Connection is closed."); + + final List meta = Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Short.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_SIZE", Integer.class), + new JdbcColumnMeta(null, null, "DECIMAL_DIGITS", Integer.class), + new JdbcColumnMeta(null, null, "NUM_PREC_RADIX", Short.class), + new JdbcColumnMeta(null, null, "NULLABLE", Short.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "COLUMN_DEF", String.class), + new JdbcColumnMeta(null, null, "CHAR_OCTET_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "ORDINAL_POSITION", Integer.class), + new JdbcColumnMeta(null, null, "IS_NULLABLE", String.class), + new JdbcColumnMeta(null, null, "SCOPE_CATLOG", String.class), + new JdbcColumnMeta(null, null, "SCOPE_SCHEMA", String.class), + new JdbcColumnMeta(null, null, "SCOPE_TABLE", String.class), + new JdbcColumnMeta(null, null, "SOURCE_DATA_TYPE", Short.class), + new JdbcColumnMeta(null, null, "IS_AUTOINCREMENT", String.class)); + + if (!validCatalogPattern(catalog)) + return new JdbcThinResultSet(Collections.>emptyList(), meta); + + try { + JdbcMetaColumnsResult res = conn.io().columnsMeta(schemaPtrn, tblNamePtrn, colNamePtrn); + + List> rows = new LinkedList<>(); + + for (int i = 0; i < res.meta().size(); ++i) + rows.add(columnRow(res.meta().get(i), i + 1)); + + return new JdbcThinResultSet(rows, meta); + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } + } + + /** + * @param colMeta Column metadata. + * @param pos Ordinal position. + * @return Column metadata row. + */ + private List columnRow(JdbcColumnMeta colMeta, int pos) { + List row = new ArrayList<>(20); + + row.add((String)null); + row.add(colMeta.schemaName()); + row.add(colMeta.tableName()); + row.add(colMeta.columnName()); + row.add(colMeta.dataType()); + row.add(colMeta.dataTypeName()); + row.add((Integer)null); + row.add((Integer)null); + row.add(10); + row.add(JdbcUtils.nullable(colMeta.columnName(), colMeta.dataTypeClass()) ? 1 : 0 ); + row.add((String)null); + row.add((String)null); + row.add(Integer.MAX_VALUE); + row.add(pos); + row.add("YES"); + row.add((String)null); + row.add((String)null); + row.add((String)null); + row.add((Short)null); + row.add("NO"); + + return row; + } + + /** {@inheritDoc} */ + @Override public ResultSet getColumnPrivileges(String catalog, String schema, String tbl, + String colNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "GRANTOR", String.class), + new JdbcColumnMeta(null, null, "GRANTEE", String.class), + new JdbcColumnMeta(null, null, "PRIVILEGE", String.class), + new JdbcColumnMeta(null, null, "IS_GRANTABLE", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getTablePrivileges(String catalog, String schemaPtrn, + String tblNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "GRANTOR", String.class), + new JdbcColumnMeta(null, null, "GRANTEE", String.class), + new JdbcColumnMeta(null, null, "PRIVILEGE", String.class), + new JdbcColumnMeta(null, null, "IS_GRANTABLE", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getBestRowIdentifier(String catalog, String schema, String tbl, int scope, + boolean nullable) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "SCOPE", Short.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_SIZE", Integer.class), + new JdbcColumnMeta(null, null, "BUFFER_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "DECIMAL_DIGITS", Short.class), + new JdbcColumnMeta(null, null, "PSEUDO_COLUMN", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getVersionColumns(String catalog, String schema, String tbl) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "SCOPE", Short.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_SIZE", Integer.class), + new JdbcColumnMeta(null, null, "BUFFER_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "DECIMAL_DIGITS", Short.class), + new JdbcColumnMeta(null, null, "PSEUDO_COLUMN", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getPrimaryKeys(String catalog, String schema, String tbl) throws SQLException { + if (conn.isClosed()) + throw new SQLException("Connection is closed."); + + final List meta = Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "KEY_SEQ", Short.class), + new JdbcColumnMeta(null, null, "PK_NAME", String.class)); + + if (!validCatalogPattern(catalog)) + return new JdbcThinResultSet(Collections.>emptyList(), meta); + + try { + JdbcMetaPrimaryKeysResult res = conn.io().primaryKeysMeta(schema, tbl); + + List> rows = new LinkedList<>(); + + for (JdbcPrimaryKeyMeta pkMeta : res.meta()) + rows.addAll(primaryKeyRows(pkMeta)); + + return new JdbcThinResultSet(rows, meta); + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } + } + + /** + * @param pkMeta Primary key metadata. + * @return Result set rows for primary key. + */ + private List> primaryKeyRows(JdbcPrimaryKeyMeta pkMeta) { + List> rows = new ArrayList<>(pkMeta.fields().size()); + + for (int i = 0; i < pkMeta.fields().size(); ++i) { + List row = new ArrayList<>(6); + + row.add((String)null); // table catalog + row.add(pkMeta.schemaName()); + row.add(pkMeta.tableName()); + row.add(pkMeta.fields().get(i)); + row.add((Integer)i + 1); // sequence number + row.add(pkMeta.name()); + + rows.add(row); + } + + return rows; + } + + /** {@inheritDoc} */ + @Override public ResultSet getImportedKeys(String catalog, String schema, String tbl) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "PKTABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "PKTABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "PKTABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "PKCOLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "FKCOLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "KEY_SEQ", Short.class), + new JdbcColumnMeta(null, null, "UPDATE_RULE", Short.class), + new JdbcColumnMeta(null, null, "DELETE_RULE", Short.class), + new JdbcColumnMeta(null, null, "FK_NAME", String.class), + new JdbcColumnMeta(null, null, "PK_NAME", String.class), + new JdbcColumnMeta(null, null, "DEFERRABILITY", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getExportedKeys(String catalog, String schema, String tbl) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "PKTABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "PKTABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "PKTABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "PKCOLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "FKCOLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "KEY_SEQ", Short.class), + new JdbcColumnMeta(null, null, "UPDATE_RULE", Short.class), + new JdbcColumnMeta(null, null, "DELETE_RULE", Short.class), + new JdbcColumnMeta(null, null, "FK_NAME", String.class), + new JdbcColumnMeta(null, null, "PK_NAME", String.class), + new JdbcColumnMeta(null, null, "DEFERRABILITY", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTbl, + String foreignCatalog, String foreignSchema, String foreignTbl) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "PKTABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "PKTABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "PKTABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "PKCOLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "FKTABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "FKCOLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "KEY_SEQ", Short.class), + new JdbcColumnMeta(null, null, "UPDATE_RULE", Short.class), + new JdbcColumnMeta(null, null, "DELETE_RULE", Short.class), + new JdbcColumnMeta(null, null, "FK_NAME", String.class), + new JdbcColumnMeta(null, null, "PK_NAME", String.class), + new JdbcColumnMeta(null, null, "DEFERRABILITY", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getTypeInfo() throws SQLException { + List> types = new ArrayList<>(21); + + types.add(Arrays.asList("BOOLEAN", Types.BOOLEAN, 1, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "BOOLEAN", 0, 0, + Types.BOOLEAN, 0, 10)); + + types.add(Arrays.asList("TINYINT", Types.TINYINT, 3, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "TINYINT", 0, 0, + Types.TINYINT, 0, 10)); + + types.add(Arrays.asList("SMALLINT", Types.SMALLINT, 5, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "SMALLINT", 0, 0, + Types.SMALLINT, 0, 10)); + + types.add(Arrays.asList("INTEGER", Types.INTEGER, 10, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "INTEGER", 0, 0, + Types.INTEGER, 0, 10)); + + types.add(Arrays.asList("BIGINT", Types.BIGINT, 19, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "BIGINT", 0, 0, + Types.BIGINT, 0, 10)); + + types.add(Arrays.asList("FLOAT", Types.FLOAT, 17, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "FLOAT", 0, 0, + Types.FLOAT, 0, 10)); + + types.add(Arrays.asList("REAL", Types.REAL, 7, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "REAL", 0, 0, + Types.REAL, 0, 10)); + + types.add(Arrays.asList("DOUBLE", Types.DOUBLE, 17, null, null, null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "DOUBLE", 0, 0, + Types.DOUBLE, 0, 10)); + + types.add(Arrays.asList("NUMERIC", Types.NUMERIC, Integer.MAX_VALUE, null, null, "PRECISION,SCALE", + (short)typeNullable, false, (short)typeSearchable, false, false, false, "NUMERIC", 0, 0, + Types.NUMERIC, 0, 10)); + + types.add(Arrays.asList("DECIMAL", Types.DECIMAL, Integer.MAX_VALUE, null, null, "PRECISION,SCALE", + (short)typeNullable, false, (short)typeSearchable, false, false, false, "DECIMAL", 0, 0, + Types.DECIMAL, 0, 10)); + + types.add(Arrays.asList("DATE", Types.DATE, 8, "DATE '", "'", null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "DATE", 0, 0, + Types.DATE, 0, null)); + + types.add(Arrays.asList("TIME", Types.TIME, 6, "TIME '", "'", null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "TIME", 0, 0, + Types.TIME, 0, null)); + + types.add(Arrays.asList("TIMESTAMP", Types.TIMESTAMP, 23, "TIMESTAMP '", "'", null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "TIMESTAMP", 0, 10, + Types.TIMESTAMP, 0, null)); + + types.add(Arrays.asList("CHAR", Types.CHAR, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, true, (short)typeSearchable, false, false, false, "CHAR", 0, 0, + Types.CHAR, 0, null)); + + types.add(Arrays.asList("VARCHAR", Types.VARCHAR, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, true, (short)typeSearchable, false, false, false, "VARCHAR", 0, 0, + Types.VARCHAR, 0, null)); + + types.add(Arrays.asList("LONGVARCHAR", Types.LONGVARCHAR, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, true, (short)typeSearchable, false, false, false, "LONGVARCHAR", 0, 0, + Types.LONGVARCHAR, 0, null)); + + types.add(Arrays.asList("BINARY", Types.BINARY, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, false, (short)typeSearchable, false, false, false, "BINARY", 0, 0, + Types.BINARY, 0, null)); + + types.add(Arrays.asList("VARBINARY", Types.VARBINARY, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, false, (short)typeSearchable, false, false, false, "VARBINARY", 0, 0, + Types.VARBINARY, 0, null)); + + types.add(Arrays.asList("LONGVARBINARY", Types.LONGVARBINARY, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, false, (short)typeSearchable, false, false, false, "LONGVARBINARY", 0, 0, + Types.LONGVARBINARY, 0, null)); + + types.add(Arrays.asList("OTHER", Types.OTHER, Integer.MAX_VALUE, "'", "'", "LENGTH", + (short)typeNullable, false, (short)typeSearchable, false, false, false, "OTHER", 0, 0, + Types.OTHER, 0, null)); + + types.add(Arrays.asList("ARRAY", Types.ARRAY, 0, "(", "')", null, + (short)typeNullable, false, (short)typeSearchable, false, false, false, "ARRAY", 0, 0, + Types.ARRAY, 0, null)); + + return new JdbcThinResultSet(types, Arrays.asList( + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "PRECISION", Integer.class), + new JdbcColumnMeta(null, null, "LITERAL_PREFIX", String.class), + new JdbcColumnMeta(null, null, "LITERAL_SUFFIX", String.class), + new JdbcColumnMeta(null, null, "CREATE_PARAMS", String.class), + new JdbcColumnMeta(null, null, "NULLABLE", Short.class), + new JdbcColumnMeta(null, null, "CASE_SENSITIVE", Boolean.class), + new JdbcColumnMeta(null, null, "SEARCHABLE", Short.class), + new JdbcColumnMeta(null, null, "UNSIGNED_ATTRIBUTE", Boolean.class), + new JdbcColumnMeta(null, null, "FIXED_PREC_SCALE", Boolean.class), + new JdbcColumnMeta(null, null, "AUTO_INCREMENT", Boolean.class), + new JdbcColumnMeta(null, null, "LOCAL_TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "MINIMUM_SCALE", Short.class), + new JdbcColumnMeta(null, null, "MAXIMUM_SCALE", Short.class), + new JdbcColumnMeta(null, null, "SQL_DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "SQL_DATETIME_SUB", Integer.class), + new JdbcColumnMeta(null, null, "NUM_PREC_RADIX", Integer.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getIndexInfo(String catalog, String schema, String tbl, boolean unique, + boolean approximate) throws SQLException { + if (conn.isClosed()) + throw new SQLException("Connection is closed."); + + final List meta = Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "NON_UNIQUE", Boolean.class), + new JdbcColumnMeta(null, null, "INDEX_QUALIFIER", String.class), + new JdbcColumnMeta(null, null, "INDEX_NAME", String.class), + new JdbcColumnMeta(null, null, "TYPE", Short.class), + new JdbcColumnMeta(null, null, "ORDINAL_POSITION", Short.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "ASC_OR_DESC", String.class), + new JdbcColumnMeta(null, null, "CARDINALITY", Integer.class), + new JdbcColumnMeta(null, null, "PAGES", Integer.class), + new JdbcColumnMeta(null, null, "FILTER_CONDITION", String.class)); + + if (!validCatalogPattern(catalog)) + return new JdbcThinResultSet(Collections.>emptyList(), meta); + + try { + JdbcMetaIndexesResult res = conn.io().indexMeta(schema, tbl); + + List> rows = new LinkedList<>(); + + for (JdbcIndexMeta idxMeta : res.meta()) + rows.addAll(indexRows(idxMeta)); + + return new JdbcThinResultSet(rows, meta); + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } + } + + /** + * @param idxMeta Index metadata. + * @return List of result rows correspond to index. + */ + private List> indexRows(JdbcIndexMeta idxMeta) { + List> rows = new ArrayList<>(idxMeta.fields().size()); + + for (int i = 0; i < idxMeta.fields().size(); ++i) { + List row = new ArrayList<>(13); + + row.add((String)null); // table catalog + row.add(idxMeta.schemaName()); + row.add(idxMeta.tableName()); + row.add(true); // non unique + row.add(null); // index qualifier (index catalog) + row.add(idxMeta.indexName()); + row.add((short)tableIndexOther); // type + row.add((Integer)i); // field ordinal position in index + row.add(idxMeta.fields().get(i)); + row.add(idxMeta.fieldsAsc().get(i) ? "A" : "D"); + row.add((Integer)0); // cardinality + row.add((Integer)0); // pages + row.add((String)null); // filer condition + + rows.add(row); + } + + return rows; + } + + /** {@inheritDoc} */ + @Override public boolean supportsResultSetType(int type) throws SQLException { + return type == TYPE_FORWARD_ONLY; + } + + /** {@inheritDoc} */ + @Override public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + return supportsResultSetType(type) && concurrency == CONCUR_READ_ONLY; + } + + /** {@inheritDoc} */ + @Override public boolean ownUpdatesAreVisible(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean ownDeletesAreVisible(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean ownInsertsAreVisible(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean othersUpdatesAreVisible(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean othersDeletesAreVisible(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean othersInsertsAreVisible(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean updatesAreDetected(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean deletesAreDetected(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean insertsAreDetected(int type) throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsBatchUpdates() throws SQLException { + return true; + } + + /** {@inheritDoc} */ + @Override public ResultSet getUDTs(String catalog, String schemaPtrn, String typeNamePtrn, + int[] types) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TYPE_CAT", String.class), + new JdbcColumnMeta(null, null, "TYPE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "CLASS_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "BASE_TYPE", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public Connection getConnection() throws SQLException { + return conn; + } + + /** {@inheritDoc} */ + @Override public boolean supportsSavepoints() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsNamedParameters() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsMultipleOpenResults() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsGetGeneratedKeys() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public ResultSet getSuperTypes(String catalog, String schemaPtrn, + String typeNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TYPE_CAT", String.class), + new JdbcColumnMeta(null, null, "TYPE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "SUPERTYPE_CAT", String.class), + new JdbcColumnMeta(null, null, "SUPERTYPE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "SUPERTYPE_NAME", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getSuperTables(String catalog, String schemaPtrn, + String tblNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "SUPERTABLE_NAME", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getAttributes(String catalog, String schemaPtrn, String typeNamePtrn, + String attributeNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TYPE_CAT", String.class), + new JdbcColumnMeta(null, null, "TYPE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "ATTR_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "ATTR_TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "ATTR_SIZE", Integer.class), + new JdbcColumnMeta(null, null, "DECIMAL_DIGITS", Integer.class), + new JdbcColumnMeta(null, null, "NUM_PREC_RADIX", Integer.class), + new JdbcColumnMeta(null, null, "NULLABLE", Integer.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "ATTR_DEF", String.class), + new JdbcColumnMeta(null, null, "SQL_DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "SQL_DATETIME_SUB", Integer.class), + new JdbcColumnMeta(null, null, "CHAR_OCTET_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "ORDINAL_POSITION", Integer.class), + new JdbcColumnMeta(null, null, "IS_NULLABLE", String.class), + new JdbcColumnMeta(null, null, "SCOPE_CATALOG", String.class), + new JdbcColumnMeta(null, null, "SCOPE_SCHEMA", String.class), + new JdbcColumnMeta(null, null, "SCOPE_TABLE", String.class), + new JdbcColumnMeta(null, null, "SOURCE_DATA_TYPE", Short.class) + )); + } + + /** {@inheritDoc} */ + @Override public boolean supportsResultSetHoldability(int holdability) throws SQLException { + return holdability == HOLD_CURSORS_OVER_COMMIT; + } + + /** {@inheritDoc} */ + @Override public int getResultSetHoldability() throws SQLException { + return HOLD_CURSORS_OVER_COMMIT; + } + + /** {@inheritDoc} */ + @Override public int getDatabaseMajorVersion() throws SQLException { + return conn.io().igniteVersion().major(); + } + + /** {@inheritDoc} */ + @Override public int getDatabaseMinorVersion() throws SQLException { + return conn.io().igniteVersion().minor(); + } + + /** {@inheritDoc} */ + @Override public int getJDBCMajorVersion() throws SQLException { + return 4; + } + + /** {@inheritDoc} */ + @Override public int getJDBCMinorVersion() throws SQLException { + return 0; + } + + /** {@inheritDoc} */ + @Override public int getSQLStateType() throws SQLException { + return DatabaseMetaData.sqlStateSQL99; + } + + /** {@inheritDoc} */ + @Override public boolean locatorsUpdateCopy() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean supportsStatementPooling() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public RowIdLifetime getRowIdLifetime() throws SQLException { + return ROWID_UNSUPPORTED; + } + + /** {@inheritDoc} */ + @Override public ResultSet getSchemas(String catalog, String schemaPtrn) throws SQLException { + if (conn.isClosed()) + throw new SQLException("Connection is closed."); + + final List meta = Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_CATALOG", String.class) + ); + + if (!validCatalogPattern(catalog)) + return new JdbcThinResultSet(Collections.>emptyList(), meta); + + try { + JdbcMetaSchemasResult res = conn.io().schemasMeta(schemaPtrn); + + List> rows = new LinkedList<>(); + + for (String schema : res.schemas()) { + List row = new ArrayList<>(2); + + row.add(schema); + row.add(null); + + rows.add(row); + } + + return new JdbcThinResultSet(rows, meta); + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } + } + + /** {@inheritDoc} */ + @Override public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + return false; + } + + /** {@inheritDoc} */ + @Override public ResultSet getClientInfoProperties() throws SQLException { + // TODO: IGNITE-5425. + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "NAME", String.class), + new JdbcColumnMeta(null, null, "MAX_LEN", Integer.class), + new JdbcColumnMeta(null, null, "DEFAULT_VALUE", String.class), + new JdbcColumnMeta(null, null, "DESCRIPTION", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getFunctions(String catalog, String schemaPtrn, + String functionNamePtrn) throws SQLException { + // TODO: IGNITE-6028 + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "FUNCTION_CAT", String.class), + new JdbcColumnMeta(null, null, "FUNCTION_SCHEM", String.class), + new JdbcColumnMeta(null, null, "FUNCTION_NAME", String.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "FUNCTION_TYPE", String.class), + new JdbcColumnMeta(null, null, "SPECIFIC_NAME", String.class) + )); + } + + /** {@inheritDoc} */ + @Override public ResultSet getFunctionColumns(String catalog, String schemaPtrn, String functionNamePtrn, + String colNamePtrn) throws SQLException { + // TODO: IGNITE-6028 + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "FUNCTION_CAT", String.class), + new JdbcColumnMeta(null, null, "FUNCTION_SCHEM", String.class), + new JdbcColumnMeta(null, null, "FUNCTION_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_TYPE", Short.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "TYPE_NAME", String.class), + new JdbcColumnMeta(null, null, "PRECISION", Integer.class), + new JdbcColumnMeta(null, null, "LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "SCALE", Short.class), + new JdbcColumnMeta(null, null, "RADIX", Short.class), + new JdbcColumnMeta(null, null, "NULLABLE", Short.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "CHAR_OCTET_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "ORDINAL_POSITION", Integer.class), + new JdbcColumnMeta(null, null, "IS_NULLABLE", String.class), + new JdbcColumnMeta(null, null, "SPECIFIC_NAME", String.class) + )); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public T unwrap(Class iface) throws SQLException { + if (!isWrapperFor(iface)) + throw new SQLException("Database meta data is not a wrapper for " + iface.getName()); + + return (T)this; + } + + /** {@inheritDoc} */ + @Override public boolean isWrapperFor(Class iface) throws SQLException { + return iface != null && iface.isAssignableFrom(JdbcThinDatabaseMetadata.class); + } + + /** {@inheritDoc} */ + @Override public ResultSet getPseudoColumns(String catalog, String schemaPtrn, String tblNamePtrn, + String colNamePtrn) throws SQLException { + return new JdbcThinResultSet(Collections.>emptyList(), Arrays.asList( + new JdbcColumnMeta(null, null, "TABLE_CAT", String.class), + new JdbcColumnMeta(null, null, "TABLE_SCHEM", String.class), + new JdbcColumnMeta(null, null, "TABLE_NAME", String.class), + new JdbcColumnMeta(null, null, "COLUMN_NAME", String.class), + new JdbcColumnMeta(null, null, "DATA_TYPE", Integer.class), + new JdbcColumnMeta(null, null, "COLUMN_SIZE", Integer.class), + new JdbcColumnMeta(null, null, "DECIMAL_DIGITS", Integer.class), + new JdbcColumnMeta(null, null, "NUM_PREC_RADIX", Integer.class), + new JdbcColumnMeta(null, null, "COLUMN_USAGE", Integer.class), + new JdbcColumnMeta(null, null, "REMARKS", String.class), + new JdbcColumnMeta(null, null, "CHAR_OCTET_LENGTH", Integer.class), + new JdbcColumnMeta(null, null, "IS_NULLABLE", String.class) + )); + } + + /** + * @param catalog Catalog pattern. + * @return {@code true} If patter is valid for Ignite (null, empty, or '%' wildcard). + * Otherwise returns {@code false}. + */ + private static boolean validCatalogPattern(String catalog) { + return F.isEmpty(catalog) || "%".equals(catalog); + } + + /** {@inheritDoc} */ + @Override public boolean generatedKeyAlwaysReturned() throws SQLException { + return false; + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinParameterMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinParameterMetadata.java new file mode 100644 index 0000000000000..8647258f88867 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinParameterMetadata.java @@ -0,0 +1,115 @@ +/* + * 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.jdbc.thin; + +import java.sql.ParameterMetaData; +import java.sql.SQLException; +import java.util.List; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcParameterMeta; + +/** + * JDBC SQL query's parameters metadata. + */ +public class JdbcThinParameterMetadata implements ParameterMetaData { + /** Parameters metadata. */ + private final List meta; + + /** + * @param meta Parameters metadata. + */ + public JdbcThinParameterMetadata(List meta) { + assert meta != null; + + this.meta = meta; + } + + /** {@inheritDoc} */ + @Override public int getParameterCount() throws SQLException { + return meta.size(); + } + + /** {@inheritDoc} */ + @SuppressWarnings("MagicConstant") + @Override public int isNullable(int param) throws SQLException { + return parameter(param).isNullable(); + } + + /** {@inheritDoc} */ + @Override public boolean isSigned(int param) throws SQLException { + return parameter(param).isSigned(); + } + + /** {@inheritDoc} */ + @Override public int getPrecision(int param) throws SQLException { + return parameter(param).precision(); + } + + /** {@inheritDoc} */ + @Override public int getScale(int param) throws SQLException { + return parameter(param).scale(); + } + + /** {@inheritDoc} */ + @Override public int getParameterType(int param) throws SQLException { + return parameter(param).type(); + } + + /** {@inheritDoc} */ + @Override public String getParameterTypeName(int param) throws SQLException { + return parameter(param).typeName(); + } + + /** {@inheritDoc} */ + @Override public String getParameterClassName(int param) throws SQLException { + return parameter(param).typeClass(); + } + + /** {@inheritDoc} */ + @SuppressWarnings("MagicConstant") + @Override public int getParameterMode(int param) throws SQLException { + return parameter(param).mode(); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public T unwrap(Class iface) throws SQLException { + if (!isWrapperFor(iface)) + throw new SQLException("Parameters metadata is not a wrapper for " + iface.getName()); + + return (T)this; + } + + /** {@inheritDoc} */ + @Override public boolean isWrapperFor(Class iface) throws SQLException { + return iface != null && iface.isAssignableFrom(JdbcThinParameterMetadata.class); + } + + /** + * Bounds checks the parameter index. + * + * @param param Parameter index. + * @return Parameter. + * @throws SQLException If failed. + */ + private JdbcParameterMeta parameter(int param) throws SQLException { + if (param <= 0 || param > meta.size()) + throw new SQLException("Invalid parameter number"); + + return meta.get(param - 1); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java index 455c80f78fbd7..e6dfa59690af4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinPreparedStatement.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.jdbc.thin; +import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; @@ -39,7 +40,9 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; +import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.odbc.SqlListenerUtils; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaParamsResult; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery; /** @@ -52,6 +55,9 @@ public class JdbcThinPreparedStatement extends JdbcThinStatement implements Prep /** Query arguments. */ protected ArrayList args; + /** Parameters metadata. */ + private JdbcThinParameterMetadata metaData; + /** * Creates new prepared statement. * @@ -322,8 +328,27 @@ private void executeWithArguments() throws SQLException { /** {@inheritDoc} */ @Override public ParameterMetaData getParameterMetaData() throws SQLException { ensureNotClosed(); + try { + if (conn.isClosed()) + throw new SQLException("Connection is closed."); - throw new SQLFeatureNotSupportedException("Meta data for prepared statement is not supported."); + if (metaData != null) + return metaData; + + JdbcMetaParamsResult res = conn.io().parametersMeta(conn.getSchema(), sql); + + metaData = new JdbcThinParameterMetadata(res.meta()); + + return metaData; + } + catch (IOException e) { + conn.close(); + + throw new SQLException("Failed to query Ignite.", e); + } + catch (IgniteCheckedException e) { + throw new SQLException("Failed to query Ignite.", e); + } } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java index 5c61e23d77409..c4be5bcf6caa2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinResultSet.java @@ -100,6 +100,34 @@ public class JdbcThinResultSet implements ResultSet { /** Update count. */ private long updCnt; + /** Jdbc metadata. Cache the JDBC object on the first access */ + private JdbcThinResultSetMetadata jdbcMeta; + + /** + * Constructs static result set. + * + * @param fields Fields. + * @param meta Columns metadata. + */ + JdbcThinResultSet(List> fields, List meta) { + stmt = null; + fetchSize = 0; + qryId = -1L; + finished = true; + isQuery = true; + updCnt = -1; + + this.rows = fields; + + rowsIter = fields.iterator(); + + this.meta = meta; + + metaInit = true; + + initColumnOrder(); + } + /** * Creates new result set. * @@ -180,7 +208,7 @@ public class JdbcThinResultSet implements ResultSet { /** {@inheritDoc} */ @Override public void close() throws SQLException { - if (closed || stmt.connection().isClosed()) + if (closed || stmt == null || stmt.connection().isClosed()) return; try { @@ -497,7 +525,10 @@ else if (val.getClass() == java.util.Date.class) @Override public ResultSetMetaData getMetaData() throws SQLException { ensureNotClosed(); - return new JdbcThinResultSetMetadata(meta()); + if (jdbcMeta == null) + jdbcMeta = new JdbcThinResultSetMetadata(meta()); + + return jdbcMeta; } /** {@inheritDoc} */ @@ -1648,7 +1679,6 @@ private List meta() throws SQLException { } /** - * Init column order map. * @throws SQLException On error. * @return Column order map. */ @@ -1659,6 +1689,15 @@ private Map columnOrder() throws SQLException { if(!metaInit) meta(); + initColumnOrder(); + + return colOrder; + } + + /** + * Init column order map. + */ + private void initColumnOrder() { colOrder = new HashMap<>(meta.size()); for (int i = 0; i < meta.size(); ++i) { @@ -1667,8 +1706,6 @@ private Map columnOrder() throws SQLException { if(!colOrder.containsKey(colName)) colOrder.put(colName, i); } - - return colOrder; } /** 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 b01350a0d6748..3772b83075095 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,8 +46,8 @@ public class JdbcThinStatement implements Statement { /** Default queryPage size. */ private static final int DFLT_PAGE_SIZE = SqlQuery.DFLT_PAGE_SIZE; - /** Ignite endpoint and I/O protocol implementation. */ - private JdbcThinConnection conn; + /** JDBC Connection implementation. */ + protected JdbcThinConnection conn; /** Closed flag. */ private boolean closed; 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 f54d5fdb8fda6..e124921d0b5a3 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 @@ -34,6 +34,18 @@ import org.apache.ignite.internal.processors.odbc.SqlListenerResponse; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBatchExecuteRequest; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBatchExecuteResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaColumnsRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaColumnsResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaIndexesRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaIndexesResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaParamsRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaParamsResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaPrimaryKeysRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaPrimaryKeysResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaSchemasRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaSchemasResult; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaTablesRequest; +import org.apache.ignite.internal.processors.odbc.jdbc.JdbcMetaTablesResult; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryCloseRequest; import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQueryExecuteRequest; @@ -47,6 +59,7 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcResult; import org.apache.ignite.internal.util.ipc.loopback.IpcClientTcpEndpoint; import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; /** * JDBC IO layer implementation based on blocking IPC streams. @@ -59,7 +72,7 @@ public class JdbcThinTcpIo { private static final int HANDSHAKE_MSG_SIZE = 13; /** Initial output for query message. */ - private static final int QUERY_EXEC_MSG_INIT_CAP = 256; + private static final int DYNAMIC_SIZE_MSG_CAP = 256; /** Maximum batch query count. */ private static final int MAX_BATCH_QRY_CNT = 32; @@ -115,6 +128,9 @@ public class JdbcThinTcpIo { /** Closed flag. */ private boolean closed; + /** Ignite server version. */ + private IgniteProductVersion igniteVer; + /** * Constructor. * @@ -202,8 +218,20 @@ public void handshake() throws IOException, IgniteCheckedException { boolean accepted = reader.readBoolean(); - if (accepted) + if (accepted) { + byte maj = reader.readByte(); + byte min = reader.readByte(); + byte maintenance = reader.readByte(); + + String stage = reader.readString(); + + long ts = reader.readLong(); + byte[] hash = reader.readByteArray(); + + igniteVer = new IgniteProductVersion(maj, min, maintenance, stage, ts, hash); + return; + } short maj = reader.readShort(); short min = reader.readShort(); @@ -231,7 +259,7 @@ public JdbcQueryExecuteResult queryExecute(String cache, int fetchSize, int maxR String sql, List args) throws IOException, IgniteCheckedException { return sendRequest(new JdbcQueryExecuteRequest(cache, fetchSize, maxRows, sql, - args == null ? null : args.toArray(new Object[args.size()])), QUERY_EXEC_MSG_INIT_CAP); + args == null ? null : args.toArray(new Object[args.size()])), DYNAMIC_SIZE_MSG_CAP); } /** @@ -295,21 +323,89 @@ public void queryClose(long qryId) throws IOException, IgniteCheckedException { } /** - * @param schema Schema. + * @param schemaName Schema. * @param batch Batch queries. * @return Result. * @throws IOException On error. * @throws IgniteCheckedException On error. */ - public JdbcBatchExecuteResult batchExecute(String schema, List batch) + public JdbcBatchExecuteResult batchExecute(String schemaName, List batch) throws IOException, IgniteCheckedException { int cnt = Math.min(MAX_BATCH_QRY_CNT, batch.size()); - return sendRequest(new JdbcBatchExecuteRequest(schema, batch), QUERY_EXEC_MSG_INIT_CAP * cnt); + return sendRequest(new JdbcBatchExecuteRequest(schemaName, batch), DYNAMIC_SIZE_MSG_CAP * cnt); + } + + /** + * @param schemaPtrn Schema name pattern. + * @param tablePtrn Table name pattern. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcMetaTablesResult tablesMeta(String schemaPtrn, String tablePtrn) + throws IOException, IgniteCheckedException { + return sendRequest(new JdbcMetaTablesRequest(schemaPtrn, tablePtrn), DYNAMIC_SIZE_MSG_CAP); + } + + /** + * @param schemaPtrn Schema name pattern. + * @param tablePtrn Table name pattern. + * @param columnPtrn Column name pattern. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcMetaColumnsResult columnsMeta(String schemaPtrn, String tablePtrn, String columnPtrn) + throws IOException, IgniteCheckedException { + return sendRequest(new JdbcMetaColumnsRequest(schemaPtrn, tablePtrn, columnPtrn), DYNAMIC_SIZE_MSG_CAP); + } + + /** + * @param schemaPtrn Schema name pattern. + * @param tablePtrn Table name pattern. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcMetaIndexesResult indexMeta(String schemaPtrn, String tablePtrn) throws IOException, IgniteCheckedException { + return sendRequest(new JdbcMetaIndexesRequest(schemaPtrn, tablePtrn), DYNAMIC_SIZE_MSG_CAP); + } + + /** + * @param schemaPtrn Schema name pattern. + * @param sql SQL query. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcMetaParamsResult parametersMeta(String schemaPtrn, String sql) throws IOException, IgniteCheckedException { + return sendRequest(new JdbcMetaParamsRequest(schemaPtrn, sql), DYNAMIC_SIZE_MSG_CAP); } /** - * @param req ODBC request. + * @param schemaPtrn Schema name pattern. + * @param tablePtrn Table name pattern. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcMetaPrimaryKeysResult primaryKeysMeta(String schemaPtrn, String tablePtrn) throws IOException, IgniteCheckedException { + return sendRequest(new JdbcMetaPrimaryKeysRequest(schemaPtrn, tablePtrn), DYNAMIC_SIZE_MSG_CAP); + } + + /** + * @param schemaPtrn Schema name pattern. + * @return Result. + * @throws IOException On error. + * @throws IgniteCheckedException On error. + */ + public JdbcMetaSchemasResult schemasMeta(String schemaPtrn) throws IOException, IgniteCheckedException { + return sendRequest(new JdbcMetaSchemasRequest(schemaPtrn), DYNAMIC_SIZE_MSG_CAP); + } + + /** + * @param req JDBC request bytes. * @throws IOException On error. */ private void send(byte[] req) throws IOException { @@ -434,4 +530,11 @@ public int socketReceiveBuffer() { public boolean tcpNoDelay() { return tcpNoDelay; } + + /** + * @return Ignnite server version. + */ + IgniteProductVersion igniteVersion() { + return igniteVer; + } } \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerNioListener.java index a879796dbdf12..5a49e3faf1650 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerNioListener.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerNioListener.java @@ -198,9 +198,11 @@ private void onHandshake(GridNioSession ses, byte[] msg) { String errMsg = null; + SqlListenerConnectionContext connCtx = null; + if (SUPPORTED_VERS.contains(ver)) { // Prepare context. - SqlListenerConnectionContext connCtx = prepareContext(ver, reader); + connCtx = prepareContext(ver, reader); ses.addMeta(CONN_CTX_META_KEY, connCtx); } @@ -213,9 +215,10 @@ private void onHandshake(GridNioSession ses, byte[] msg) { // Send response. BinaryWriterExImpl writer = new BinaryWriterExImpl(null, new BinaryHeapOutputStream(8), null, null); - if (errMsg == null) - writer.writeBoolean(true); + if (connCtx != null) + connCtx.handler().writeHandshake(writer); else { + // Failed handshake response writer.writeBoolean(false); writer.writeShort(CURRENT_VER.major()); writer.writeShort(CURRENT_VER.minor()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerRequestHandler.java index 98dc03932ee5b..348054f34708b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerRequestHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerRequestHandler.java @@ -17,6 +17,8 @@ package org.apache.ignite.internal.processors.odbc; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; + /** * SQL listener request handler. */ @@ -36,4 +38,11 @@ public interface SqlListenerRequestHandler { * @return Error response. */ public SqlListenerResponse handleException(Exception e); + + /** + * Write successful handshake response. + * + * @param writer Binary writer. + */ + public void writeHandshake(BinaryWriterExImpl writer); } 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 9f71bff7cc9dc..25e1049820b88 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 @@ -31,8 +31,8 @@ * JDBC batch execute request. */ public class JdbcBatchExecuteRequest extends JdbcRequest { - /** Cache name. */ - private String schema; + /** Schema name. */ + private String schemaName; /** Sql query. */ @GridToStringInclude(sensitive = true) @@ -46,23 +46,23 @@ public JdbcBatchExecuteRequest() { } /** - * @param schema Schema. + * @param schemaName Schema name. * @param queries Queries. */ - public JdbcBatchExecuteRequest(String schema, List queries) { + public JdbcBatchExecuteRequest(String schemaName, List queries) { super(BATCH_EXEC); assert !F.isEmpty(queries); - this.schema = schema; + this.schemaName = schemaName; this.queries = queries; } /** - * @return Schema. + * @return Schema name. */ - @Nullable public String schema() { - return schema; + @Nullable public String schemaName() { + return schemaName; } /** @@ -76,7 +76,7 @@ public List queries() { @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { super.writeBinary(writer); - writer.writeString(schema); + writer.writeString(schemaName); writer.writeInt(queries.size()); for (JdbcQuery q : queries) @@ -87,7 +87,7 @@ public List queries() { @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { super.readBinary(reader); - schema = reader.readString(); + schemaName = reader.readString(); int n = reader.readInt(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java index 7977c224a782d..917e60ae75ccc 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteResult.java @@ -20,6 +20,7 @@ import org.apache.ignite.binary.BinaryObjectException; import org.apache.ignite.internal.binary.BinaryReaderExImpl; import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; /** * JDBC batch execute result. @@ -93,4 +94,9 @@ public String errorMessage() { errMsg = reader.readString(); updateCnts = reader.readIntArray(); } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcBatchExecuteResult.class, this); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcColumnMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcColumnMeta.java index 07cbabef7ef5c..9f145e0ec73ef 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcColumnMeta.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcColumnMeta.java @@ -21,19 +21,21 @@ import org.apache.ignite.internal.binary.BinaryWriterExImpl; import org.apache.ignite.internal.jdbc.thin.JdbcThinUtils; import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener column metadata. + * JDBC column metadata. */ public class JdbcColumnMeta implements JdbcRawBinarylizable { /** Cache name. */ private String schemaName; /** Table name. */ - private String tableName; + private String tblName; /** Column name. */ - private String columnName; + private String colName; /** Data type. */ private int dataType; @@ -47,22 +49,41 @@ public class JdbcColumnMeta implements JdbcRawBinarylizable { /** * Default constructor is used for serialization. */ - public JdbcColumnMeta() { + JdbcColumnMeta() { + // No-op. } /** * @param info Field metadata. */ - public JdbcColumnMeta(GridQueryFieldMetadata info) { + JdbcColumnMeta(GridQueryFieldMetadata info) { this.schemaName = info.schemaName(); - this.tableName = info.typeName(); - this.columnName = info.fieldName(); + this.tblName = info.typeName(); + this.colName = info.fieldName(); dataType = JdbcThinUtils.type(info.fieldTypeName()); dataTypeName = JdbcThinUtils.typeName(info.fieldTypeName()); dataTypeClass = info.fieldTypeName(); } + /** + * @param schemaName Schema. + * @param tblName Table. + * @param colName Column. + * @param cls Type. + */ + public JdbcColumnMeta(String schemaName, String tblName, String colName, Class cls) { + this.schemaName = schemaName; + this.tblName = tblName; + this.colName = colName; + + String type = cls.getName(); + + dataType = JdbcThinUtils.type(type); + dataTypeName = JdbcThinUtils.typeName(type); + dataTypeClass = type; + } + /** * @return Schema name. */ @@ -74,14 +95,14 @@ public String schemaName() { * @return Table name. */ public String tableName() { - return tableName; + return tblName; } /** * @return Column name. */ public String columnName() { - return columnName; + return colName; } /** @@ -108,8 +129,8 @@ public String dataTypeClass() { /** {@inheritDoc} */ @Override public void writeBinary(BinaryWriterExImpl writer) { writer.writeString(schemaName); - writer.writeString(tableName); - writer.writeString(columnName); + writer.writeString(tblName); + writer.writeString(colName); writer.writeInt(dataType); writer.writeString(dataTypeName); @@ -119,11 +140,39 @@ public String dataTypeClass() { /** {@inheritDoc} */ @Override public void readBinary(BinaryReaderExImpl reader) { schemaName = reader.readString(); - tableName = reader.readString(); - columnName = reader.readString(); + tblName = reader.readString(); + colName = reader.readString(); dataType = reader.readInt(); dataTypeName = reader.readString(); dataTypeClass = reader.readString(); } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + JdbcColumnMeta meta = (JdbcColumnMeta)o; + + return F.eq(schemaName, meta.schemaName) && F.eq(tblName, meta.tblName) && F.eq(colName, meta.colName); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int result = schemaName != null ? schemaName.hashCode() : 0; + + result = 31 * result + (tblName != null ? tblName.hashCode() : 0); + result = 31 * result + colName.hashCode(); + + return result; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcColumnMeta.class, this); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcIndexMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcIndexMeta.java new file mode 100644 index 0000000000000..d33f8871b7292 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcIndexMeta.java @@ -0,0 +1,192 @@ +/* + * 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.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.cache.QueryIndexType; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC index metadata. + */ +public class JdbcIndexMeta implements JdbcRawBinarylizable { + /** Index schema name. */ + private String schemaName; + + /** Index table name. */ + private String tblName; + + /** Index name. */ + private String idxName; + + /** Index type. */ + private QueryIndexType type; + + /** Index fields */ + private List fields; + + /** Index fields is ascending. */ + private List fieldsAsc; + + /** + * Default constructor is used for binary serialization. + */ + JdbcIndexMeta() { + // No-op. + } + + /** + * @param schemaName Schema name. + * @param tblName Table name. + * @param idx Index info. + */ + JdbcIndexMeta(String schemaName, String tblName, GridQueryIndexDescriptor idx) { + assert tblName != null; + assert idx != null; + assert idx.fields() != null; + + this.schemaName = schemaName; + this.tblName = tblName; + + idxName = idx.name(); + type = idx.type(); + fields = new ArrayList(idx.fields()); + + fieldsAsc = new ArrayList<>(fields.size()); + + for (int i = 0; i < fields.size(); ++i) + fieldsAsc.add(!idx.descending(fields.get(i))); + } + + /** + * @return Schema name. + */ + public String schemaName() { + return schemaName; + } + + /** + * @return Table name. + */ + public String tableName() { + return tblName; + } + + /** + * @return Index name. + */ + public String indexName() { + return idxName; + } + + /** + * @return Index type. + */ + public QueryIndexType type() { + return type; + } + + /** + * @return Index fields + */ + public List fields() { + return fields; + } + + /** + * @return Index fields is ascending. + */ + public List fieldsAsc() { + return fieldsAsc; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + writer.writeString(schemaName); + writer.writeString(tblName); + writer.writeString(idxName); + writer.writeByte((byte)type.ordinal()); + + JdbcUtils.writeStringCollection(writer, fields); + + if (fieldsAsc == null) + writer.writeInt(0); + else { + writer.writeInt(fieldsAsc.size()); + + for (Boolean b : fieldsAsc) + writer.writeBoolean(b.booleanValue()); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + schemaName = reader.readString(); + tblName = reader.readString(); + idxName = reader.readString(); + type = QueryIndexType.fromOrdinal(reader.readByte()); + + fields = JdbcUtils.readStringList(reader); + + int size = reader.readInt(); + + if (size > 0) { + fieldsAsc = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) + fieldsAsc .add(reader.readBoolean()); + } + else + fieldsAsc = Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + JdbcIndexMeta meta = (JdbcIndexMeta)o; + + return F.eq(schemaName, meta.schemaName) && F.eq(tblName, meta.tblName) && F.eq(idxName, meta.idxName); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int result = schemaName != null ? schemaName.hashCode() : 0; + + result = 31 * result + tblName.hashCode(); + result = 31 * result + idxName.hashCode(); + + return result; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcIndexMeta.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsRequest.java new file mode 100644 index 0000000000000..fca1bf70d2b8e --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsRequest.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.odbc.jdbc; + +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.jetbrains.annotations.Nullable; + +/** + * JDBC get columns metadata request. + */ +public class JdbcMetaColumnsRequest extends JdbcRequest { + /** Schema name pattern. */ + private String schemaName; + + /** Table name pattern. */ + private String tblName; + + /** Column name pattern. */ + private String colName; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaColumnsRequest() { + super(META_COLUMNS); + } + + /** + * @param schemaName Schema name. + * @param tblName Table name. + * @param colName Column name. + */ + public JdbcMetaColumnsRequest(String schemaName, String tblName, String colName) { + super(META_COLUMNS); + + this.schemaName = schemaName; + this.tblName = tblName; + this.colName = colName; + } + + /** + * @return Schema name pattern. + */ + @Nullable public String schemaName() { + return schemaName; + } + + /** + * @return Table name pattern. + */ + public String tableName() { + return tblName; + } + + /** + * @return Column name pattern. + */ + public String columnName() { + return colName; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schemaName); + writer.writeString(tblName); + writer.writeString(colName); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + schemaName = reader.readString(); + tblName = reader.readString(); + colName = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaColumnsRequest.class, this); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsResult.java new file mode 100644 index 0000000000000..da270de71d56a --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaColumnsResult.java @@ -0,0 +1,99 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC columns metadata result. + */ +public class JdbcMetaColumnsResult extends JdbcResult { + /** Columns metadata. */ + private List meta; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaColumnsResult() { + super(META_COLUMNS); + } + + /** + * @param meta Columns metadata. + */ + JdbcMetaColumnsResult(Collection meta) { + super(META_COLUMNS); + + this.meta = new ArrayList<>(meta); + } + + /** + * @return Columns metadata. + */ + public List meta() { + return meta; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + if (F.isEmpty(meta)) + writer.writeInt(0); + else { + writer.writeInt(meta.size()); + + for(JdbcColumnMeta m : meta) + m.writeBinary(writer); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + int size = reader.readInt(); + + if (size == 0) + meta = Collections.emptyList(); + else { + meta = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) { + JdbcColumnMeta m = new JdbcColumnMeta(); + + m.readBinary(reader); + + meta.add(m); + } + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaColumnsResult.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesRequest.java new file mode 100644 index 0000000000000..d4a53d8e79399 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesRequest.java @@ -0,0 +1,88 @@ +/* + * 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 org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.jetbrains.annotations.Nullable; + +/** + * JDBC indexes metadata request. + */ +public class JdbcMetaIndexesRequest extends JdbcRequest { + /** Schema name pattern. */ + private String schemaName; + + /** Table name pattern. */ + private String tblName; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaIndexesRequest() { + super(META_INDEXES); + } + + /** + * @param schemaName Cache name. + * @param tblName Table name. + */ + public JdbcMetaIndexesRequest(String schemaName, String tblName) { + super(META_INDEXES); + + this.schemaName = schemaName; + this.tblName = tblName; + } + + /** + * @return Schema name. + */ + @Nullable public String schemaName() { + return schemaName; + } + + /** + * @return Table name. + */ + public String tableName() { + return tblName; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schemaName); + writer.writeString(tblName); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + schemaName = reader.readString(); + tblName = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaIndexesRequest.class, this); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesResult.java new file mode 100644 index 0000000000000..2316dfceac416 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaIndexesResult.java @@ -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. + */ + +package org.apache.ignite.internal.processors.odbc.jdbc; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC indexes metadata result. + */ +public class JdbcMetaIndexesResult extends JdbcResult { + /** Indexes metadata. */ + private List meta; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaIndexesResult() { + super(META_INDEXES); + } + + /** + * @param meta Indexes metadata. + */ + JdbcMetaIndexesResult(Collection meta) { + super(META_INDEXES); + this.meta = new ArrayList<>(meta); + } + + /** + * @return Indexes metadata. + */ + public List meta() { + return meta; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + if (F.isEmpty(meta)) + writer.writeInt(0); + else { + writer.writeInt(meta.size()); + + for(JdbcIndexMeta m : meta) + m.writeBinary(writer); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + int size = reader.readInt(); + + if (size == 0) + meta = Collections.emptyList(); + else { + meta = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) { + JdbcIndexMeta m = new JdbcIndexMeta(); + + m.readBinary(reader); + + meta.add(m); + } + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaIndexesResult.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsRequest.java new file mode 100644 index 0000000000000..6b955f9cd4845 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsRequest.java @@ -0,0 +1,87 @@ +/* + * 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 org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC SQL query parameters metadata request. + */ +public class JdbcMetaParamsRequest extends JdbcRequest { + /** Schema name. */ + private String schemaName; + + /** Query. */ + private String sql; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaParamsRequest() { + super(META_PARAMS); + } + + /** + * @param schemaName Schema name. + * @param sql SQL Query. + */ + public JdbcMetaParamsRequest(String schemaName, String sql) { + super(META_PARAMS); + + this.schemaName = schemaName; + this.sql = sql; + } + + /** + * @return Schema name. + */ + public String schemaName() { + return schemaName; + } + + /** + * @return SQL Query. + */ + public String sql() { + return sql; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schemaName); + writer.writeString(sql); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + schemaName = reader.readString(); + sql = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaParamsRequest.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsResult.java new file mode 100644 index 0000000000000..7563e014934cd --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaParamsResult.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.odbc.jdbc; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC SQL query parameters metadata result. + */ +public class JdbcMetaParamsResult extends JdbcResult { + /** Parameters meta results. */ + private List meta; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaParamsResult() { + super(META_PARAMS); + } + + /** + * @param meta Column metadata. + */ + JdbcMetaParamsResult(List meta) { + super(META_PARAMS); + this.meta = meta; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + if (F.isEmpty(meta)) + writer.writeInt(0); + else { + writer.writeInt(meta.size()); + + for(JdbcParameterMeta m : meta) + m.writeBinary(writer); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + int size = reader.readInt(); + + if (size == 0) + meta = Collections.emptyList(); + else { + meta = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) { + JdbcParameterMeta m = new JdbcParameterMeta(); + + m.readBinary(reader); + + meta.add(m); + } + } + } + + /** + * @return SQL query parameters metadata. + */ + public List meta() { + return meta; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaParamsResult.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysRequest.java new file mode 100644 index 0000000000000..957225a380ef7 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysRequest.java @@ -0,0 +1,88 @@ +/* + * 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 org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.jetbrains.annotations.Nullable; + +/** + * JDBC get primary keys metadata request. + */ +public class JdbcMetaPrimaryKeysRequest extends JdbcRequest { + /** Schema name pattern. */ + private String schemaName; + + /** Table name pattern. */ + private String tblName; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaPrimaryKeysRequest() { + super(META_PRIMARY_KEYS); + } + + /** + * @param schemaName Cache name. + * @param tblName Table name. + */ + public JdbcMetaPrimaryKeysRequest(String schemaName, String tblName) { + super(META_PRIMARY_KEYS); + + this.schemaName = schemaName; + this.tblName = tblName; + } + + /** + * @return Schema name pattern. + */ + @Nullable public String schemaName() { + return schemaName; + } + + /** + * @return Table name pattern. + */ + public String tableName() { + return tblName; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schemaName); + writer.writeString(tblName); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + schemaName = reader.readString(); + tblName = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaPrimaryKeysRequest.class, this); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysResult.java new file mode 100644 index 0000000000000..bd0dd90857240 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaPrimaryKeysResult.java @@ -0,0 +1,99 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC primary keys metadata result. + */ +public class JdbcMetaPrimaryKeysResult extends JdbcResult { + /** Query result rows. */ + private List meta; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaPrimaryKeysResult() { + super(META_PRIMARY_KEYS); + } + + /** + * @param meta Column metadata. + */ + JdbcMetaPrimaryKeysResult(Collection meta) { + super(META_PRIMARY_KEYS); + + this.meta = new ArrayList<>(meta); + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + if (F.isEmpty(meta)) + writer.writeInt(0); + else { + writer.writeInt(meta.size()); + + for(JdbcPrimaryKeyMeta m : meta) + m.writeBinary(writer); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + int size = reader.readInt(); + + if (size == 0) + meta = Collections.emptyList(); + else { + meta = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) { + JdbcPrimaryKeyMeta m = new JdbcPrimaryKeyMeta(); + + m.readBinary(reader); + + meta.add(m); + } + } + } + + /** + * @return Primary keys metadata. + */ + public List meta() { + return meta; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaPrimaryKeysResult.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasRequest.java new file mode 100644 index 0000000000000..43bbe5dbbea11 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasRequest.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.odbc.jdbc; + +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC tables metadata request. + */ +public class JdbcMetaSchemasRequest extends JdbcRequest { + /** Schema search pattern. */ + private String schemaName; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaSchemasRequest() { + super(META_SCHEMAS); + } + + /** + * @param schemaName Schema search pattern. + */ + public JdbcMetaSchemasRequest(String schemaName) { + super(META_SCHEMAS); + + this.schemaName = schemaName; + } + + /** + * @return Schema search pattern. + */ + public String schemaName() { + return schemaName; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schemaName); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + this.schemaName = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaSchemasRequest.class, this); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasResult.java new file mode 100644 index 0000000000000..48b6aae387171 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaSchemasResult.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.odbc.jdbc; + +import java.util.Collection; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC tables metadata result. + */ +public class JdbcMetaSchemasResult extends JdbcResult { + /** Found schemas. */ + private Collection schemas; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaSchemasResult() { + super(META_SCHEMAS); + } + + /** + * @param schemas Found schemas. + */ + JdbcMetaSchemasResult(Collection schemas) { + super(META_SCHEMAS); + this.schemas = schemas; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + JdbcUtils.writeStringCollection(writer, schemas); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + schemas = JdbcUtils.readStringList(reader); + } + + /** + * @return Found schemas. + */ + public Collection schemas() { + return schemas; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaSchemasResult.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesRequest.java new file mode 100644 index 0000000000000..740b6561600c5 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesRequest.java @@ -0,0 +1,87 @@ +/* + * 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 org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC tables metadata request. + */ +public class JdbcMetaTablesRequest extends JdbcRequest { + /** Schema search pattern. */ + private String schemaName; + + /** Table search pattern. */ + private String tblName; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaTablesRequest() { + super(META_TABLES); + } + + /** + * @param schemaName Schema search pattern. + * @param tblName Table search pattern. + */ + public JdbcMetaTablesRequest(String schemaName, String tblName) { + super(META_TABLES); + + this.schemaName = schemaName; + this.tblName = tblName; + } + + /** + * @return Schema search pattern. + */ + public String schemaName() { + return schemaName; + } + + /** + * @return Table search pattern. + */ + public String tableName() { + return tblName; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + writer.writeString(schemaName); + writer.writeString(tblName); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + this.schemaName = reader.readString(); + this.tblName = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaTablesRequest.class, this); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesResult.java new file mode 100644 index 0000000000000..585667ef4cf2f --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcMetaTablesResult.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.odbc.jdbc; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC tables metadata result. + */ +public class JdbcMetaTablesResult extends JdbcResult { + /** Tables metadata. */ + private List meta; + + /** + * Default constructor is used for deserialization. + */ + JdbcMetaTablesResult() { + super(META_TABLES); + } + + /** + * @param meta Tables metadata. + */ + JdbcMetaTablesResult(List meta) { + super(META_TABLES); + this.meta = meta; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + super.writeBinary(writer); + + if (F.isEmpty(meta)) + writer.writeInt(0); + else { + writer.writeInt(meta.size()); + + for(JdbcTableMeta m : meta) + m.writeBinary(writer); + } + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + super.readBinary(reader); + + int size = reader.readInt(); + + if (size == 0) + meta = Collections.emptyList(); + else { + meta = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) { + JdbcTableMeta m = new JdbcTableMeta(); + + m.readBinary(reader); + + meta.add(m); + } + } + } + + /** + * @return Tables metadata. + */ + public List meta() { + return meta; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcMetaTablesResult.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java new file mode 100644 index 0000000000000..dd3b18b67c3c1 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java @@ -0,0 +1,165 @@ +/* + * 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.sql.ParameterMetaData; +import java.sql.SQLException; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC SQL query parameter metadata. + * + * {@see java.sql.ParameterMetaData}. + */ +public class JdbcParameterMeta implements JdbcRawBinarylizable { + /** Null value is allow for the param. */ + private int isNullable; + + /** Signed flag. */ + private boolean signed; + + /** Precision. */ + private int precision; + + /** Scale. */ + private int scale; + + /** SQL type ID. */ + private int type; + + /** SQL type name. */ + private String typeName; + + /** Java type class name. */ + private String typeClass; + + /** Mode. */ + private int mode; + + + /** + * Default constructor is used for binary serialization. + */ + public JdbcParameterMeta() { + // No-op. + } + + /** + * @param meta Param metadata. + * @param order Param order. + * @throws SQLException On errror. + */ + public JdbcParameterMeta(ParameterMetaData meta, int order) throws SQLException { + isNullable = meta.isNullable(order); + signed = meta.isSigned(order); + precision = meta.getPrecision(order); + scale = meta.getScale(order); + type = meta.getParameterType(order); + typeName = meta.getParameterTypeName(order); + typeClass = meta.getParameterClassName(order); + mode = meta.getParameterMode(order); + } + + /** + * @return Nullable mode. + */ + public int isNullable() { + return isNullable; + } + + /** + * @return Signed flag. + */ + public boolean isSigned() { + return signed; + } + + /** + * @return Precision. + */ + public int precision() { + return precision; + } + + /** + * @return Scale. + */ + public int scale() { + return scale; + } + + /** + * @return SQL type. + */ + public int type() { + return type; + } + + /** + * @return SQL type name. + */ + public String typeName() { + return typeName; + } + + /** + * @return Java type class name. + */ + public String typeClass() { + return typeClass; + } + + /** + * @return Mode. + */ + public int mode() { + return mode; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + writer.writeInt(isNullable); + writer.writeBoolean(signed); + writer.writeInt(precision); + writer.writeInt(scale); + writer.writeInt(type); + writer.writeString(typeName); + writer.writeString(typeClass); + writer.writeInt(mode); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + isNullable = reader.readInt(); + signed = reader.readBoolean(); + precision = reader.readInt(); + scale = reader.readInt(); + type = reader.readInt(); + typeName = reader.readString(); + typeClass = reader.readString(); + mode = reader.readInt(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcParameterMeta.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcPrimaryKeyMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcPrimaryKeyMeta.java new file mode 100644 index 0000000000000..6b9bf70f9fcca --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcPrimaryKeyMeta.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.List; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.F; + +/** + * JDBC primary key metadata. + */ +public class JdbcPrimaryKeyMeta implements JdbcRawBinarylizable { + /** Schema name. */ + private String schemaName; + + /** Table name. */ + private String tblName; + + /** Primary key name. */ + private String name; + + /** Primary key fields. */ + private List fields; + + /** + * Default constructor is used for binary serialization. + */ + JdbcPrimaryKeyMeta() { + // No-op. + } + + /** + * @param schemaName Schema. + * @param tblName Table. + * @param name Name. + * @param fields Primary key fields. + */ + JdbcPrimaryKeyMeta(String schemaName, String tblName, String name, List fields) { + this.schemaName = schemaName; + this.tblName = tblName; + this.name = name; + this.fields = fields; + } + + /** + * @return Schema name. + */ + public String schemaName() { + return schemaName; + } + + /** + * @return Table name. + */ + public String tableName() { + return tblName; + } + + /** + * @return Primary key name. + */ + public String name() { + return name; + } + + /** + * @return Key fields. + */ + public List fields() { + return fields; + } + + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + writer.writeString(schemaName); + writer.writeString(tblName); + writer.writeString(name); + + JdbcUtils.writeStringCollection(writer, fields); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + schemaName = reader.readString(); + tblName = reader.readString(); + name = reader.readString(); + + fields = JdbcUtils.readStringList(reader); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + JdbcPrimaryKeyMeta meta = (JdbcPrimaryKeyMeta)o; + + return F.eq(schemaName, meta.schemaName) && F.eq(tblName, meta.tblName) && F.eq(name, meta.name); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int result = schemaName != null ? schemaName.hashCode() : 0; + + result = 31 * result + tblName.hashCode(); + result = 31 * result + name.hashCode(); + + return result; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryCloseRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryCloseRequest.java index 411d1e098ed84..872889ca9b775 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryCloseRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryCloseRequest.java @@ -23,7 +23,7 @@ import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener query close request. + * JDBC query close request. */ public class JdbcQueryCloseRequest extends JdbcRequest { /** Query ID. */ @@ -31,7 +31,7 @@ public class JdbcQueryCloseRequest extends JdbcRequest { /** */ - public JdbcQueryCloseRequest() { + JdbcQueryCloseRequest() { super(QRY_CLOSE); } 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 f1323663b754f..0b26dce0ecc1d 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 @@ -27,10 +27,10 @@ import org.jetbrains.annotations.Nullable; /** - * SQL listener query execute request. + * JDBC query execute request. */ public class JdbcQueryExecuteRequest extends JdbcRequest { - /** Cache name. */ + /** Schema name. */ private String schemaName; /** Fetch size. */ @@ -49,7 +49,7 @@ public class JdbcQueryExecuteRequest extends JdbcRequest { /** */ - public JdbcQueryExecuteRequest() { + JdbcQueryExecuteRequest() { super(QRY_EXEC); } @@ -100,7 +100,7 @@ public Object[] arguments() { } /** - * @return Cache name. + * @return Schema name. */ @Nullable public String schemaName() { return schemaName; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteResult.java index a9352158abc65..fdebdb8c3c450 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteResult.java @@ -21,9 +21,10 @@ import org.apache.ignite.binary.BinaryObjectException; import org.apache.ignite.internal.binary.BinaryReaderExImpl; import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener query execute result. + * JDBC query execute result. */ public class JdbcQueryExecuteResult extends JdbcResult { /** Query ID. */ @@ -44,7 +45,7 @@ public class JdbcQueryExecuteResult extends JdbcResult { /** * Condtructor. */ - public JdbcQueryExecuteResult() { + JdbcQueryExecuteResult() { super(QRY_EXEC); } @@ -53,7 +54,7 @@ public JdbcQueryExecuteResult() { * @param items Query result rows. * @param last Flag indicates the query has no unfetched results. */ - public JdbcQueryExecuteResult(long queryId, List> items, boolean last) { + JdbcQueryExecuteResult(long queryId, List> items, boolean last) { super(QRY_EXEC); this.queryId = queryId; @@ -147,4 +148,9 @@ public long updateCount() { updateCnt = reader.readLong(); } } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcQueryExecuteResult.class, this); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchRequest.java index 2e1f551a74522..776c3bfa85438 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchRequest.java @@ -23,7 +23,7 @@ import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener query fetch request. + * JDBC query fetch request. */ public class JdbcQueryFetchRequest extends JdbcRequest { /** Query ID. */ @@ -35,7 +35,7 @@ public class JdbcQueryFetchRequest extends JdbcRequest { /** * Constructor. */ - public JdbcQueryFetchRequest() { + JdbcQueryFetchRequest() { super(QRY_FETCH); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchResult.java index 6735c6bdd0c4b..ac4a6035e6ab6 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryFetchResult.java @@ -21,9 +21,10 @@ import org.apache.ignite.binary.BinaryObjectException; import org.apache.ignite.internal.binary.BinaryReaderExImpl; import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener query fetch result. + * JDBC query fetch result. */ public class JdbcQueryFetchResult extends JdbcResult { /** Query result rows. */ @@ -35,7 +36,7 @@ public class JdbcQueryFetchResult extends JdbcResult { /** * Default constructor is used for deserialization. */ - public JdbcQueryFetchResult() { + JdbcQueryFetchResult() { super(QRY_FETCH); } @@ -43,7 +44,7 @@ public JdbcQueryFetchResult() { * @param items Query result rows. * @param last Flag indicating the query has no unfetched results. */ - public JdbcQueryFetchResult(List> items, boolean last){ + JdbcQueryFetchResult(List> items, boolean last){ super(QRY_FETCH); this.items = items; @@ -81,4 +82,9 @@ public boolean last() { items = JdbcUtils.readItems(reader); } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcQueryFetchResult.class, this); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataRequest.java index d14c9dffd49b2..bdef321abe678 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataRequest.java @@ -23,47 +23,47 @@ import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener query metadata request. + * JDBC query metadata request. */ public class JdbcQueryMetadataRequest extends JdbcRequest { /** Query ID. */ - private long queryId; + private long qryId; /** * Constructor. */ - public JdbcQueryMetadataRequest() { + JdbcQueryMetadataRequest() { super(QRY_META); } /** - * @param queryId Query ID. + * @param qryId Query ID. */ - public JdbcQueryMetadataRequest(long queryId) { + public JdbcQueryMetadataRequest(long qryId) { super(QRY_META); - this.queryId = queryId; + this.qryId = qryId; } /** * @return Query ID. */ public long queryId() { - return queryId; + return qryId; } /** {@inheritDoc} */ @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { super.writeBinary(writer); - writer.writeLong(queryId); + writer.writeLong(qryId); } /** {@inheritDoc} */ @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { super.readBinary(reader); - queryId = reader.readLong(); + qryId = reader.readLong(); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataResult.java index cc193e391530e..c8c0991e344bb 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryMetadataResult.java @@ -24,9 +24,10 @@ import org.apache.ignite.internal.binary.BinaryReaderExImpl; import org.apache.ignite.internal.binary.BinaryWriterExImpl; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; /** - * SQL listener query metadata result. + * JDBC query metadata result. */ public class JdbcQueryMetadataResult extends JdbcResult { /** Fields metadata. */ @@ -35,7 +36,7 @@ public class JdbcQueryMetadataResult extends JdbcResult { /** * Default constructor is used for deserialization. */ - public JdbcQueryMetadataResult() { + JdbcQueryMetadataResult() { super(QRY_META); } @@ -43,14 +44,14 @@ public JdbcQueryMetadataResult() { * @param queryId Query ID. * @param meta Query metadata. */ - public JdbcQueryMetadataResult(long queryId, List meta){ + JdbcQueryMetadataResult(long queryId, List meta){ super(QRY_META); this.meta = meta; } /** - * @return Query result rows. + * @return Query result metadata. */ public List meta() { return meta; @@ -90,4 +91,9 @@ public List meta() { } } } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcQueryMetadataResult.class, this); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java index 0e144cc59d957..4ef75f6e4ffba 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequest.java @@ -24,24 +24,43 @@ import org.apache.ignite.internal.processors.odbc.SqlListenerRequest; /** - * SQL listener command request. + * JDBC request. */ public class JdbcRequest extends SqlListenerRequest implements JdbcRawBinarylizable { - /** Execute sql query. */ - public static final byte QRY_EXEC = 2; + /** Execute sql query request. */ + static final byte QRY_EXEC = 2; - /** Fetch query results. */ - public static final byte QRY_FETCH = 3; + /** Fetch query results request. */ + static final byte QRY_FETCH = 3; - /** Close query. */ - public static final byte QRY_CLOSE = 4; + /** Close query request. */ + static final byte QRY_CLOSE = 4; - /** Get columns meta query. */ - public static final byte QRY_META = 5; + /** Get query columns metadata request. */ + static final byte QRY_META = 5; /** Batch queries. */ public static final byte BATCH_EXEC = 6; + /** Get tables metadata request. */ + static final byte META_TABLES = 7; + + /** Get columns metadata request. */ + static final byte META_COLUMNS = 8; + + /** Get indexes metadata request. */ + static final byte META_INDEXES = 9; + + /** Get SQL query parameters metadata request. */ + static final byte META_PARAMS = 10; + + /** Get primary keys metadata request. */ + static final byte META_PRIMARY_KEYS = 11; + + /** Get schemas metadata request. */ + static final byte META_SCHEMAS = 12; + + /** Request type. */ private byte type; @@ -105,6 +124,36 @@ public static JdbcRequest readRequest(BinaryReaderExImpl reader) throws BinaryOb break; + case META_TABLES: + req = new JdbcMetaTablesRequest(); + + break; + + case META_COLUMNS: + req = new JdbcMetaColumnsRequest(); + + break; + + case META_INDEXES: + req = new JdbcMetaIndexesRequest(); + + break; + + case META_PARAMS: + req = new JdbcMetaParamsRequest(); + + break; + + case META_PRIMARY_KEYS: + req = new JdbcMetaPrimaryKeysRequest(); + + break; + + case META_SCHEMAS: + req = new JdbcMetaSchemasRequest(); + + break; + default: throw new IgniteException("Unknown SQL listener request ID: [request ID=" + reqType + ']'); } 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 60c08f9a20058..7e58f99568faf 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 @@ -17,8 +17,15 @@ package org.apache.ignite.internal.processors.odbc.jdbc; +import java.sql.ParameterMetaData; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.ignite.IgniteCheckedException; @@ -26,10 +33,15 @@ 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.IgniteVersionUtils; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; import org.apache.ignite.internal.processors.cache.QueryCursorImpl; import org.apache.ignite.internal.processors.odbc.SqlListenerRequest; import org.apache.ignite.internal.processors.odbc.SqlListenerRequestHandler; import org.apache.ignite.internal.processors.odbc.SqlListenerResponse; +import org.apache.ignite.internal.processors.odbc.odbc.OdbcQueryGetColumnsMetaRequest; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor; import org.apache.ignite.internal.processors.query.QueryUtils; import org.apache.ignite.internal.util.GridSpinBusyLock; import org.apache.ignite.internal.util.typedef.F; @@ -37,13 +49,19 @@ import org.apache.ignite.internal.util.typedef.internal.U; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.BATCH_EXEC; +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.META_COLUMNS; +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.META_INDEXES; +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.META_PARAMS; +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.META_PRIMARY_KEYS; +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.META_SCHEMAS; +import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.META_TABLES; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_CLOSE; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_EXEC; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_FETCH; import static org.apache.ignite.internal.processors.odbc.jdbc.JdbcRequest.QRY_META; /** - * SQL query handler. + * JDBC request handler. */ public class JdbcRequestHandler implements SqlListenerRequestHandler { /** Query ID sequence. */ @@ -92,7 +110,7 @@ public class JdbcRequestHandler implements SqlListenerRequestHandler { * @param autoCloseCursors Flag to automatically close server cursors. */ public JdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock, int maxCursors, - boolean distributedJoins, boolean enforceJoinOrder, boolean collocated, boolean replicatedOnly, + boolean distributedJoins, boolean enforceJoinOrder, boolean collocated, boolean replicatedOnly, boolean autoCloseCursors) { this.ctx = ctx; this.busyLock = busyLock; @@ -134,6 +152,24 @@ public JdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock, int case BATCH_EXEC: return executeBatch((JdbcBatchExecuteRequest)req); + + case META_TABLES: + return getTablesMeta((JdbcMetaTablesRequest)req); + + case META_COLUMNS: + return getColumnsMeta((JdbcMetaColumnsRequest)req); + + case META_INDEXES: + return getIndexesMeta((JdbcMetaIndexesRequest)req); + + case META_PARAMS: + return getParametersMeta((JdbcMetaParamsRequest)req); + + case META_PRIMARY_KEYS: + return getPrimaryKeys((JdbcMetaPrimaryKeysRequest)req); + + case META_SCHEMAS: + return getSchemas((JdbcMetaSchemasRequest)req); } return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, "Unsupported JDBC request [req=" + req + ']'); @@ -148,6 +184,20 @@ public JdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock, int return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); } + /** {@inheritDoc} */ + @Override public void writeHandshake(BinaryWriterExImpl writer) { + // Handshake OK. + writer.writeBoolean(true); + + // Write server version. + writer.writeByte(IgniteVersionUtils.VER.major()); + writer.writeByte(IgniteVersionUtils.VER.minor()); + writer.writeByte(IgniteVersionUtils.VER.maintenance()); + writer.writeString(IgniteVersionUtils.VER.stage()); + writer.writeLong(IgniteVersionUtils.VER.revisionTimestamp()); + writer.writeByteArray(IgniteVersionUtils.VER.revisionHash()); + } + /** * {@link JdbcQueryExecuteRequest} command handler. * @@ -318,7 +368,7 @@ private JdbcResponse getQueryMeta(JdbcQueryMetadataRequest req) { * @return Response. */ private SqlListenerResponse executeBatch(JdbcBatchExecuteRequest req) { - String schemaName = req.schema(); + String schemaName = req.schemaName(); if (F.isEmpty(schemaName)) schemaName = QueryUtils.DFLT_SCHEMA; @@ -365,4 +415,221 @@ private SqlListenerResponse executeBatch(JdbcBatchExecuteRequest req) { SqlListenerResponse.STATUS_FAILED, e.toString())); } } + + /** + * @param req Get tables metadata request. + * @return Response. + */ + private JdbcResponse getTablesMeta(JdbcMetaTablesRequest req) { + try { + List meta = new ArrayList<>(); + + for (String cacheName : ctx.cache().publicCacheNames()) { + for (GridQueryTypeDescriptor table : ctx.query().types(cacheName)) { + if (!matches(table.schemaName(), req.schemaName())) + continue; + + if (!matches(table.tableName(), req.tableName())) + continue; + + JdbcTableMeta tableMeta = new JdbcTableMeta(table.schemaName(), table.tableName(), "TABLE"); + + if (!meta.contains(tableMeta)) + meta.add(tableMeta); + } + } + + JdbcMetaTablesResult res = new JdbcMetaTablesResult(meta); + + return new JdbcResponse(res); + } + catch (Exception e) { + U.error(log, "Failed to get tables metadata [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); + } + } + + /** + * {@link OdbcQueryGetColumnsMetaRequest} command handler. + * + * @param req Get columns metadata request. + * @return Response. + */ + private JdbcResponse getColumnsMeta(JdbcMetaColumnsRequest req) { + try { + Collection meta = new HashSet<>(); + + for (String cacheName : ctx.cache().publicCacheNames()) { + for (GridQueryTypeDescriptor table : ctx.query().types(cacheName)) { + if (!matches(table.schemaName(), req.schemaName())) + continue; + + if (!matches(table.tableName(), req.tableName())) + continue; + + for (Map.Entry> field : table.fields().entrySet()) { + if (!matches(field.getKey(), req.columnName())) + continue; + + JdbcColumnMeta columnMeta = new JdbcColumnMeta(table.schemaName(), table.tableName(), + field.getKey(), field.getValue()); + + if (!meta.contains(columnMeta)) + meta.add(columnMeta); + } + } + } + + JdbcMetaColumnsResult res = new JdbcMetaColumnsResult(meta); + + return new JdbcResponse(res); + } + catch (Exception e) { + U.error(log, "Failed to get columns metadata [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); + } + } + + /** + * @param req Request. + * @return Response. + */ + private SqlListenerResponse getIndexesMeta(JdbcMetaIndexesRequest req) { + try { + Collection meta = new HashSet<>(); + + for (String cacheName : ctx.cache().publicCacheNames()) { + for (GridQueryTypeDescriptor table : ctx.query().types(cacheName)) { + if (!matches(table.schemaName(), req.schemaName())) + continue; + + if (!matches(table.tableName(), req.tableName())) + continue; + + for (GridQueryIndexDescriptor idxDesc : table.indexes().values()) + meta.add(new JdbcIndexMeta(table.schemaName(), table.tableName(), idxDesc)); + } + } + + return new JdbcResponse(new JdbcMetaIndexesResult(meta)); + } + catch (Exception e) { + U.error(log, "Failed to get parameters metadata [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); + } + } + + /** + * @param req Request. + * @return Response. + */ + private SqlListenerResponse getParametersMeta(JdbcMetaParamsRequest req) { + try { + ParameterMetaData paramMeta = ctx.query().prepareNativeStatement(req.schemaName(), req.sql()) + .getParameterMetaData(); + + int size = paramMeta.getParameterCount(); + + List meta = new ArrayList<>(size); + + for (int i = 0; i < size; i++) + meta.add(new JdbcParameterMeta(paramMeta, i + 1)); + + JdbcMetaParamsResult res = new JdbcMetaParamsResult(meta); + + return new JdbcResponse(res); + } + catch (Exception e) { + U.error(log, "Failed to get parameters metadata [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); + } + } + + /** + * @param req Request. + * @return Response. + */ + private SqlListenerResponse getPrimaryKeys(JdbcMetaPrimaryKeysRequest req) { + try { + Collection meta = new HashSet<>(); + + for (String cacheName : ctx.cache().publicCacheNames()) { + for (GridQueryTypeDescriptor table : ctx.query().types(cacheName)) { + if (!matches(table.schemaName(), req.schemaName())) + continue; + + if (!matches(table.tableName(), req.tableName())) + continue; + + List fields = new ArrayList<>(); + + for (String field : table.fields().keySet()) { + if (table.property(field).key()) + fields.add(field); + } + + + final String keyName = table.keyFieldName() == null ? + "PK_" + table.schemaName() + "_" + table.tableName() : + table.keyFieldName(); + + if (fields.isEmpty()) { + meta.add(new JdbcPrimaryKeyMeta(table.schemaName(), table.tableName(), keyName, + Collections.singletonList("_KEY"))); + } + else + meta.add(new JdbcPrimaryKeyMeta(table.schemaName(), table.tableName(), keyName, fields)); + } + } + + return new JdbcResponse(new JdbcMetaPrimaryKeysResult(meta)); + } + catch (Exception e) { + U.error(log, "Failed to get parameters metadata [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); + } + } + + /** + * @param req Request. + * @return Response. + */ + private SqlListenerResponse getSchemas(JdbcMetaSchemasRequest req) { + try { + String schemaPtrn = req.schemaName(); + + Set schemas = new HashSet<>(); + + for (String cacheName : ctx.cache().publicCacheNames()) { + for (GridQueryTypeDescriptor table : ctx.query().types(cacheName)) { + if (matches(table.schemaName(), schemaPtrn)) + schemas.add(table.schemaName()); + } + } + + return new JdbcResponse(new JdbcMetaSchemasResult(schemas)); + } + catch (Exception e) { + U.error(log, "Failed to get schemas metadata [reqId=" + req.requestId() + ", req=" + req + ']', e); + + return new JdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); + } + } + + /** + * Checks whether string matches SQL pattern. + * + * @param str String. + * @param ptrn Pattern. + * @return Whether string matches pattern. + */ + private static boolean matches(String str, String ptrn) { + return str != null && (F.isEmpty(ptrn) || + str.matches(ptrn.replace("%", ".*").replace("_", "."))); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java index 48affe96602ba..202905bbc9e4d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcResult.java @@ -23,21 +23,39 @@ import org.apache.ignite.internal.binary.BinaryWriterExImpl; /** - * SQL listener response. + * JDBC response result. */ public class JdbcResult implements JdbcRawBinarylizable { /** Execute sql result. */ - public static final byte QRY_EXEC = 2; + static final byte QRY_EXEC = 2; /** Fetch query results. */ - public static final byte QRY_FETCH = 3; + static final byte QRY_FETCH = 3; - /** Get columns meta query result. */ - public static final byte QRY_META = 4; + /** Query result's columns metadata result. */ + static final byte QRY_META = 5; /** Batch queries. */ public static final byte BATCH_EXEC = 6; + /** Tables metadata result. */ + static final byte META_TABLES = 7; + + /** Columns metadata result. */ + static final byte META_COLUMNS = 8; + + /** Indexes metadata result. */ + static final byte META_INDEXES = 9; + + /** SQL query parameters metadata result. */ + static final byte META_PARAMS = 10; + + /** Primary keys metadata result. */ + static final byte META_PRIMARY_KEYS = 11; + + /** Database schemas metadata result. */ + static final byte META_SCHEMAS = 12; + /** Success status. */ private byte type; @@ -91,6 +109,36 @@ public static JdbcResult readResult(BinaryReaderExImpl reader) throws BinaryObje break; + case META_TABLES: + res = new JdbcMetaTablesResult(); + + break; + + case META_COLUMNS: + res = new JdbcMetaColumnsResult(); + + break; + + case META_INDEXES: + res = new JdbcMetaIndexesResult(); + + break; + + case META_PARAMS: + res = new JdbcMetaParamsResult(); + + break; + + case META_PRIMARY_KEYS: + res = new JdbcMetaPrimaryKeysResult(); + + break; + + case META_SCHEMAS: + res = new JdbcMetaSchemasResult(); + + break; + default: throw new IgniteException("Unknown SQL listener request ID: [request ID=" + resId + ']'); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcTableMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcTableMeta.java new file mode 100644 index 0000000000000..b954e9706b47a --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcTableMeta.java @@ -0,0 +1,82 @@ +/* + * 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 org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * JDBC table metadata. + */ +public class JdbcTableMeta implements JdbcRawBinarylizable { + /** Schema name. */ + private String schemaName; + + /** Table name. */ + private String tblName; + + /** + * Default constructor is used for deserialization. + */ + JdbcTableMeta() { + // No-op. + } + + /** + * @param schemaName Schema name. + * @param tblName Table name. + * @param tblType Table type. + */ + JdbcTableMeta(String schemaName, String tblName, String tblType) { + this.schemaName = schemaName; + this.tblName = tblName; + } + + /** + * @return Schema name. + */ + public String schemaName() { + return schemaName; + } + + /** + * @return Table name. + */ + public String tableName() { + return tblName; + } + + /** {@inheritDoc} */ + @Override public void writeBinary(BinaryWriterExImpl writer) throws BinaryObjectException { + writer.writeString(schemaName); + writer.writeString(tblName); + } + + /** {@inheritDoc} */ + @Override public void readBinary(BinaryReaderExImpl reader) throws BinaryObjectException { + schemaName = reader.readString(); + tblName = reader.readString(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(JdbcTableMeta.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcUtils.java index 65efbf5300090..d556419efbb8c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcUtils.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.odbc.jdbc; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.ignite.internal.binary.BinaryReaderExImpl; @@ -29,7 +30,7 @@ */ public class JdbcUtils { /** - * @param writer Binari writer. + * @param writer Binary writer. * @param items Query results items. */ public static void writeItems(BinaryWriterExImpl writer, List> items) { @@ -70,4 +71,38 @@ public static List> readItems(BinaryReaderExImpl reader) { } else return Collections.emptyList(); } + + /** + * @param writer Binary writer. + * @param lst List to write. + */ + public static void writeStringCollection(BinaryWriterExImpl writer, Collection lst) { + if (lst == null) + writer.writeInt(0); + else { + writer.writeInt(lst.size()); + + for (String s : lst) + writer.writeString(s); + } + } + + /** + * @param reader Binary reader. + * @return List of string. + */ + public static List readStringList(BinaryReaderExImpl reader) { + int size = reader.readInt(); + + if (size > 0) { + List lst = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) + lst.add(reader.readString()); + + return lst; + } + else + return Collections.emptyList(); + } } 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 bb54b59daac81..692043c1abbf0 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 @@ -33,6 +33,7 @@ import org.apache.ignite.cache.query.QueryCursor; import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; import org.apache.ignite.internal.binary.GridBinaryMarshaller; import org.apache.ignite.internal.processors.cache.QueryCursorImpl; import org.apache.ignite.internal.processors.odbc.SqlListenerRequest; @@ -156,6 +157,11 @@ public OdbcRequestHandler(GridKernalContext ctx, GridSpinBusyLock busyLock, int return new OdbcResponse(SqlListenerResponse.STATUS_FAILED, e.toString()); } + /** {@inheritDoc} */ + @Override public void writeHandshake(BinaryWriterExImpl writer) { + writer.writeBoolean(true); + } + /** * {@link OdbcQueryExecuteRequest} command handler. * 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 baafb1e8d5f02..1d154d39e69b9 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 @@ -688,7 +688,7 @@ public void onCacheStart0(GridCacheContext cctx, QuerySchema schema) if (!F.isEmpty(qryEntities)) { for (QueryEntity qryEntity : qryEntities) { - QueryTypeCandidate cand = QueryUtils.typeForQueryEntity(cacheName, cctx, qryEntity, + QueryTypeCandidate cand = QueryUtils.typeForQueryEntity(cacheName, schemaName, cctx, qryEntity, mustDeserializeClss, escape); cands.add(cand); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java index c1a9e1e918d61..c149335373f65 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java @@ -32,6 +32,13 @@ public interface GridQueryTypeDescriptor { */ public String name(); + /** + * Gets schema name for type (database schema means here). + * + * @return Schema name. + */ + public String schemaName(); + /** * Gets table name for type. * diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java index c0da83fc3948c..79b90e51c06c8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java @@ -42,6 +42,9 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { /** */ private String name; + /** Schema name. */ + private String schemaName; + /** */ private String tblName; @@ -120,6 +123,11 @@ public String cacheName() { return name; } + /** {@inheritDoc} */ + @Override public String schemaName() { + return schemaName; + } + /** * Sets type name. * @@ -363,6 +371,13 @@ public void addProperty(GridQueryProperty prop, boolean failOnDuplicate) throws fields.put(name, prop.type()); } + /** + * @param schemaName Schema name. + */ + public void schemaName(String schemaName) { + this.schemaName = schemaName; + } + /** {@inheritDoc} */ @Override public boolean valueTextIndex() { return valTextIdx; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java index 320b25a2f770a..26fc776ee42ab 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java @@ -343,6 +343,7 @@ private static String aliasForFieldName(String fieldName) { * Create type candidate for query entity. * * @param cacheName Cache name. + * @param schemaName Schema name. * @param cctx Cache context. * @param qryEntity Query entity. * @param mustDeserializeClss Classes which must be deserialized. @@ -350,8 +351,8 @@ private static String aliasForFieldName(String fieldName) { * @return Type candidate. * @throws IgniteCheckedException If failed. */ - public static QueryTypeCandidate typeForQueryEntity(String cacheName, GridCacheContext cctx, QueryEntity qryEntity, - List> mustDeserializeClss, boolean escape) throws IgniteCheckedException { + public static QueryTypeCandidate typeForQueryEntity(String cacheName, String schemaName, GridCacheContext cctx, + QueryEntity qryEntity, List> mustDeserializeClss, boolean escape) throws IgniteCheckedException { GridKernalContext ctx = cctx.kernalContext(); CacheConfiguration ccfg = cctx.config(); @@ -361,6 +362,8 @@ public static QueryTypeCandidate typeForQueryEntity(String cacheName, GridCacheC QueryTypeDescriptorImpl desc = new QueryTypeDescriptorImpl(cacheName); + desc.schemaName(schemaName); + desc.aliases(qryEntity.getAliases()); // Key and value classes still can be available if they are primitive or JDK part. 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 99246ebc14279..e1ab8e629c6df 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 @@ -104,13 +104,13 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract } /** */ - private static TypeDesc typeAA = new TypeDesc("A", "A", Collections.>emptyMap(), null); + private static TypeDesc typeAA = new TypeDesc("A", "A", "A", Collections.>emptyMap(), null); /** */ - private static TypeDesc typeAB = new TypeDesc("A", "B", Collections.>emptyMap(), textIdx); + private static TypeDesc typeAB = new TypeDesc("A", "A", "B", Collections.>emptyMap(), textIdx); /** */ - private static TypeDesc typeBA = new TypeDesc("B", "A", Collections.>emptyMap(), null); + private static TypeDesc typeBA = new TypeDesc("B", "B", "A", Collections.>emptyMap(), null); /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { @@ -466,6 +466,9 @@ private static class TypeDesc implements GridQueryTypeDescriptor { /** */ private final String cacheName; + /** */ + private final String schemaName; + /** */ private final Map> valFields; @@ -474,13 +477,15 @@ private static class TypeDesc implements GridQueryTypeDescriptor { /** * @param cacheName Cache name. + * @param schemaName Schema name. * @param name Type name. * @param valFields Fields. * @param textIdx Fulltext index. */ - private TypeDesc(String cacheName, String name, Map> valFields, GridQueryIndexDescriptor textIdx) { + private TypeDesc(String cacheName, String schemaName, String name, Map> valFields, GridQueryIndexDescriptor textIdx) { this.name = name; this.cacheName = cacheName; + this.schemaName = schemaName; this.valFields = Collections.unmodifiableMap(valFields); this.textIdx = textIdx; } @@ -495,6 +500,11 @@ private TypeDesc(String cacheName, String name, Map> valFields, return name; } + /** {@inheritDoc} */ + @Override public String schemaName() { + return schemaName; + } + /** {@inheritDoc} */ @Override public String tableName() { return null; From 44c1d5b9ff20a768c66c37520949073b5bb7c489 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Thu, 17 Aug 2017 13:46:03 +0300 Subject: [PATCH 034/145] IGNITE-6091 Fixed flaky test CacheLateAffinityAssignmentTest.testRandomOperations - Fixes #2462. Signed-off-by: Alexey Goncharuk --- .../cache/transactions/IgniteTxImplicitSingleStateImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 7610d50845f14..b48e0b41ac409 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 @@ -115,8 +115,9 @@ public class IgniteTxImplicitSingleStateImpl extends IgniteTxLocalStateAdapter { Throwable err = topFut.validateCache(cacheCtx, recovery, read, null, entry); if (err != null) { - return new IgniteCheckedException("Failed to perform cache operation (cache topology is not valid): " + - U.maskName(cacheCtx.name())); + return new IgniteCheckedException( + "Failed to perform cache operation (cache topology is not valid): " + + U.maskName(cacheCtx.name()), err); } if (CU.affinityNodes(cacheCtx, topFut.topologyVersion()).isEmpty()) { From 8486b98c4542291644cb0f2b0ac53934f5da4d32 Mon Sep 17 00:00:00 2001 From: nikolay_tikhonov Date: Thu, 17 Aug 2017 14:41:38 +0300 Subject: [PATCH 035/145] IGNITE-5947 Fixed "ClassCastException when two-dimensional array is fetched from cache". Signed-off-by: nikolay_tikhonov --- .../processors/cache/CacheObjectUtils.java | 27 +- .../cache/CacheTwoDimensionalArrayTest.java | 309 ++++++++++++++++++ .../testsuites/IgniteCacheTestSuite.java | 2 + 3 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java index f9c76df5814bd..6a6376803d9fa 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache; +import java.lang.reflect.Array; import org.apache.ignite.internal.binary.BinaryUtils; import org.apache.ignite.internal.util.typedef.F; @@ -123,8 +124,30 @@ private static Object[] unwrapBinariesInArrayIfNeeded(CacheObjectValueContext ct Object[] res = new Object[arr.length]; - for (int i = 0; i < arr.length; i++) - res[i] = unwrapBinary(ctx, arr[i], keepBinary, cpy); + boolean canCastArray = true; + Class cls = null; + + for (int i = 0; i < arr.length; i++) { + Object obj = unwrapBinary(ctx, arr[i], keepBinary, cpy); + + res[i] = obj; + + if (canCastArray && obj != null) { + if (cls == null) + cls = obj.getClass(); + else if (cls != obj.getClass()) + canCastArray = false; + } + } + + // If array contains all element the same type then will create typed array. + if (canCastArray && cls != null) { + Object[] res0 = (Object[])Array.newInstance(cls, res.length); + + System.arraycopy(res, 0, res0, 0, res.length); + + res = res0; + } return res; } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java new file mode 100644 index 0000000000000..edba357c8142e --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java @@ -0,0 +1,309 @@ +/* + * 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 org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.jetbrains.annotations.NotNull; + +import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; +import static org.apache.ignite.cache.CacheMode.PARTITIONED; +import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; +import static org.junit.Assert.assertArrayEquals; + +/** + * + */ +public class CacheTwoDimensionalArrayTest extends GridCommonAbstractTest { + /** */ + private static int NODES = 3; + + /** */ + private static int KEYS = 100; + + /** */ + private static int size = 5; + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + startGridsMultiThreaded(NODES); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + super.afterTestsStopped(); + + stopAllGrids(); + } + + /** + * @throws Exception If failed. + */ + public void testSimpleModel() throws Exception { + doTestSimpleModel(ATOMIC, PARTITIONED); + } + + /** + * @param atomicityMode Cache atomicity mode. + * @param cacheMode Cache mode. + * + * @throws Exception If failed. + */ + private void doTestSimpleModel(CacheAtomicityMode atomicityMode, CacheMode cacheMode) throws Exception { + CacheConfiguration ccfg = getConfiguration(atomicityMode, cacheMode); + + ignite(0).getOrCreateCache(ccfg); + + int n = size, m = size -1; + + // Object array with primitives. + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, new Object[]{1}); + + for (int key = 0; key < KEYS; key++) { + Object[] exp = new Object[]{1}; + + Object[] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + // Primitive empty array. + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, new int[n][m]); + + for (int key = 0; key < KEYS; key++) { + int[][] exp = new int[n][m]; + + int[][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + // Object empty array. + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, new Object[n][m]); + + for (int key = 0; key < KEYS; key++) { + Object[][] exp = new Object[n][m]; + + Object[][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, intArray(n, m, key)); + + for (int key = 0; key < KEYS; key++) { + int[][] exp = intArray(n, m, key); + + int[][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, new int[5][6][7]); + + for (int key = 0; key < KEYS; key++) { + int[][][] exp = new int[5][6][7]; + + int[][][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, objectArray(n, m, key)); + + for (int key = 0; key < KEYS; key++) { + Object[][] exp = objectArray(n, m, key); + + Object[][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, testObjectArray(n, m, key)); + + for (int key = 0; key < KEYS; key++) { + TestObject[][] exp = testObjectArray(n, m, key); + + TestObject[][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + + { + IgniteCache cache = ignite(0).cache(ccfg.getName()); + + for (int key = 0; key < KEYS; key++) + cache.put(key, testObjectArray(n, m, key)); + + for (int key = 0; key < KEYS; key++) { + TestObject[][] exp = testObjectArray(n, m, key); + + TestObject[][] act = cache.get(key); + + assertArrayEquals(exp, act); + } + + cache.removeAll(); + } + } + + /** + * @return Array. + */ + private int[][] intArray(int n, int m,int K) { + int[][] arr = new int[n][m]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) + arr[i][j] = (i + j) * K; + } + + return arr; + } + + /** + * @return Array. + */ + private Object[][] objectArray(int n, int m, int K) { + Object[][] arr = new Object[n][m]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) + arr[i][j] = ((n + m) % 2 == 0) ? (i + j) * K : new TestObject((i + j) * K); + } + + return arr; + } + + /** + * @return Array. + */ + private TestObject[][] testObjectArray(int n, int m, int K) { + TestObject[][] arr = new TestObject[n][m]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) + arr[i][j] = new TestObject((i + j) * K); + } + + return arr; + } + + /** + * @param atomicityMode Atomicity mode. + * @param cacheMode Cache mode. + * + * @return Cache configuration. + */ + @NotNull private CacheConfiguration getConfiguration(CacheAtomicityMode atomicityMode, + CacheMode cacheMode) { + CacheConfiguration ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME); + + ccfg.setCacheMode(cacheMode); + ccfg.setAtomicityMode(atomicityMode); + ccfg.setWriteSynchronizationMode(FULL_SYNC); + ccfg.setBackups(1); + + return ccfg; + } + + /** + * + */ + private static class TestObject { + /** */ + private int val; + + /** + * @param val Value. + */ + public TestObject(int val) { + this.val = val; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + TestObject object = (TestObject)o; + + return val == object.val; + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return val; + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java index 42d953419866f..ce1e3860c9989 100755 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java @@ -46,6 +46,7 @@ import org.apache.ignite.internal.processors.cache.CacheNamesSelfTest; import org.apache.ignite.internal.processors.cache.CacheNamesWithSpecialCharactersTest; import org.apache.ignite.internal.processors.cache.CachePutEventListenerErrorSelfTest; +import org.apache.ignite.internal.processors.cache.CacheTwoDimensionalArrayTest; import org.apache.ignite.internal.processors.cache.CacheTxFastFinishTest; import org.apache.ignite.internal.processors.cache.GridCacheAffinityApiSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheAffinityMapperSelfTest; @@ -311,6 +312,7 @@ public static TestSuite suite(Set ignoredTests) throws Exception { suite.addTestSuite(IgniteDiagnosticMessagesMultipleConnectionsTest.class); suite.addTestSuite(IgniteIncompleteCacheObjectSelfTest.class); + suite.addTestSuite(CacheTwoDimensionalArrayTest.class); return suite; } From 38cccd3ca31a4d1d5c9a6500eda203285603bb86 Mon Sep 17 00:00:00 2001 From: Sergey Kalashnikov Date: Thu, 17 Aug 2017 14:58:38 +0300 Subject: [PATCH 036/145] IGNITE-5738: JDBC: add batch support. This closes #2393. --- .../jdbc2/JdbcDeleteStatementSelfTest.java | 22 ++ .../jdbc2/JdbcInsertStatementSelfTest.java | 159 +++++++++++++ .../jdbc2/JdbcMergeStatementSelfTest.java | 41 ++++ .../jdbc2/JdbcStatementBatchingSelfTest.java | 133 +++++++++++ .../jdbc2/JdbcUpdateStatementSelfTest.java | 24 ++ .../jdbc/suite/IgniteJdbcDriverTestSuite.java | 3 + .../internal/jdbc2/JdbcBatchUpdateTask.java | 215 ++++++++++++++++++ .../internal/jdbc2/JdbcDatabaseMetadata.java | 2 +- .../internal/jdbc2/JdbcPreparedStatement.java | 25 +- .../ignite/internal/jdbc2/JdbcStatement.java | 59 ++++- 10 files changed, 675 insertions(+), 8 deletions(-) create mode 100644 modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementBatchingSelfTest.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBatchUpdateTask.java diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDeleteStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDeleteStatementSelfTest.java index d55c979f7e52b..3eec5a025d947 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDeleteStatementSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcDeleteStatementSelfTest.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.jdbc2; +import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Arrays; import java.util.HashSet; @@ -46,4 +47,25 @@ public void testExecuteUpdate() throws SQLException { assertFalse(jcache(0).containsKey("p2")); assertTrue(jcache(0).containsKeys(new HashSet(Arrays.asList("p1", "p3")))); } + + /** + * + */ + public void testBatch() throws SQLException { + PreparedStatement ps = conn.prepareStatement("delete from Person where firstName = ?"); + + ps.setString(1, "John"); + + ps.addBatch(); + + ps.setString(1, "Harry"); + + ps.addBatch(); + + int[] res = ps.executeBatch(); + + assertFalse(jcache(0).containsKey("p1")); + assertTrue(jcache(0).containsKeys(new HashSet(Arrays.asList("p2", "p3")))); + assertTrue(Arrays.equals(new int[] {1, 0}, res)); + } } diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java index 0e7539fd1cc0a..407d6e2c05895 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcInsertStatementSelfTest.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.jdbc2; +import java.sql.BatchUpdateException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -26,6 +27,7 @@ import java.util.concurrent.Callable; import org.apache.ignite.cache.CachePeekMode; import org.apache.ignite.internal.processors.query.IgniteSQLException; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.testframework.GridTestUtils; /** @@ -174,4 +176,161 @@ public void testDuplicateKeys() { assertEquals(3, jcache(0).withKeepBinary().getAll(new HashSet<>(Arrays.asList("p1", "p2", "p3"))).size()); } + + /** + * @throws SQLException if failed. + */ + public void testBatch() throws SQLException { + formBatch(1, 2); + formBatch(3, 4); + + int[] res = prepStmt.executeBatch(); + + assertTrue(Arrays.equals(new int[] {2, 2}, res)); + } + + /** + * @throws SQLException if failed. + */ + public void testSingleItemBatch() throws SQLException { + formBatch(1, 2); + + int[] res = prepStmt.executeBatch(); + + assertTrue(Arrays.equals(new int[] {2}, res)); + } + + /** + * @throws SQLException if failed. + */ + public void testSingleItemBatchError() throws SQLException { + formBatch(1, 2); + + prepStmt.executeBatch(); + + formBatch(1, 2); // Duplicate key + + BatchUpdateException reason = (BatchUpdateException) + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + return prepStmt.executeBatch(); + } + }, + BatchUpdateException.class, + "Failed to INSERT some keys because they are already in cache"); + + // Check update counts in the exception. + assertTrue(F.isEmpty(reason.getUpdateCounts())); + } + + /** + * @throws SQLException if failed. + */ + public void testErrorAmidstBatch() throws SQLException { + formBatch(1, 2); + formBatch(3, 1); // Duplicate key + + BatchUpdateException reason = (BatchUpdateException) + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws Exception { + return prepStmt.executeBatch(); + } + }, + BatchUpdateException.class, + "Failed to INSERT some keys because they are already in cache"); + + // Check update counts in the exception. + int[] counts = reason.getUpdateCounts(); + + assertNotNull(counts); + + assertEquals(1, counts.length); + assertEquals(2, counts[0]); + } + + /** + * @throws Exception If failed. + */ + public void testClearBatch() throws Exception { + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws SQLException { + return prepStmt.executeBatch(); + } + }, SQLException.class, "Batch is empty"); + + formBatch(1, 2); + + prepStmt.clearBatch(); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws SQLException { + return prepStmt.executeBatch(); + } + }, SQLException.class, "Batch is empty"); + } + + /** + * Form batch on prepared statement. + * + * @param id1 id for first row. + * @param id2 id for second row. + * @throws SQLException if failed. + */ + private void formBatch(int id1, int id2) throws SQLException { + int[] ids = new int[] { id1, id2 }; + + int arg = 0; + for (int id: ids) { + String key = "p" + id; + + switch (id) { + case 1: + prepStmt.setString(arg + 1, key); + prepStmt.setInt(arg + 2, 1); + prepStmt.setString(arg + 3, "John"); + prepStmt.setString(arg + 4, "White"); + prepStmt.setInt(arg + 5, 25); + prepStmt.setBytes(arg + 6, getBytes("White")); + + break; + + case 2: + prepStmt.setString(arg + 1, key); + prepStmt.setInt(arg + 2, 2); + prepStmt.setString(arg + 3, "Joe"); + prepStmt.setString(arg + 4, "Black"); + prepStmt.setInt(arg + 5, 35); + prepStmt.setBytes(arg + 6, getBytes("Black")); + + break; + + case 3: + prepStmt.setString(arg + 1, key); + prepStmt.setInt(arg + 2, 3); + prepStmt.setString(arg + 3, "Mike"); + prepStmt.setString(arg + 4, "Green"); + prepStmt.setInt(arg + 5, 40); + prepStmt.setBytes(arg + 6, getBytes("Green")); + + break; + + case 4: + prepStmt.setString(arg + 1, key); + prepStmt.setInt(arg + 2, 4); + prepStmt.setString(arg + 3, "Leah"); + prepStmt.setString(arg + 4, "Grey"); + prepStmt.setInt(arg + 5, 22); + prepStmt.setBytes(arg + 6, getBytes("Grey")); + + break; + + default: + assert false; + } + + arg += 6; + } + + prepStmt.addBatch(); + } } diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java index 1432a78c7bbc2..489bacd8ea906 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMergeStatementSelfTest.java @@ -21,6 +21,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.Arrays; import org.apache.ignite.cache.CachePeekMode; /** @@ -143,4 +144,44 @@ public void testExecute() throws SQLException { assertEquals(false, res); } + + /** + * @throws SQLException if failed. + */ + public void testBatch() throws SQLException { + prepStmt.setString(1, "p1"); + prepStmt.setInt(2, 1); + prepStmt.setString(3, "John"); + prepStmt.setString(4, "White"); + prepStmt.setInt(5, 25); + prepStmt.setBytes(6, getBytes("White")); + + prepStmt.setString(7, "p2"); + prepStmt.setInt(8, 2); + prepStmt.setString(9, "Joe"); + prepStmt.setString(10, "Black"); + prepStmt.setInt(11, 35); + prepStmt.setBytes(12, getBytes("Black")); + prepStmt.addBatch(); + + prepStmt.setString(1, "p3"); + prepStmt.setInt(2, 3); + prepStmt.setString(3, "Mike"); + prepStmt.setString(4, "Green"); + prepStmt.setInt(5, 40); + prepStmt.setBytes(6, getBytes("Green")); + + prepStmt.setString(7, "p4"); + prepStmt.setInt(8, 4); + prepStmt.setString(9, "Leah"); + prepStmt.setString(10, "Grey"); + prepStmt.setInt(11, 22); + prepStmt.setBytes(12, getBytes("Grey")); + + prepStmt.addBatch(); + + int[] res = prepStmt.executeBatch(); + + assertTrue(Arrays.equals(new int[] {2, 2}, res)); + } } diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementBatchingSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementBatchingSelfTest.java new file mode 100644 index 0000000000000..c9169b98a6bd7 --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementBatchingSelfTest.java @@ -0,0 +1,133 @@ +/* + * 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.jdbc2; + +import java.sql.BatchUpdateException; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.Callable; +import org.apache.ignite.testframework.GridTestUtils; + +/** + * Statement batch test. + */ +public class JdbcStatementBatchingSelfTest extends JdbcAbstractDmlStatementSelfTest { + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + jcache(0).clear(); + } + + /** + * @throws SQLException If failed. + */ + public void testDatabaseMetadataBatchSupportFlag() throws SQLException { + DatabaseMetaData meta = conn.getMetaData(); + + assertNotNull(meta); + + assertTrue(meta.supportsBatchUpdates()); + } + + /** + * @throws SQLException If failed. + */ + public void testBatch() throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.addBatch("INSERT INTO Person(_key, id, firstName, lastName, age, data) " + + "VALUES ('p1', 0, 'J', 'W', 250, RAWTOHEX('W'))"); + + stmt.addBatch("MERGE INTO Person(_key, id, firstName, lastName, age, data) VALUES " + + "('p1', 1, 'John', 'White', 25, RAWTOHEX('White')), " + + "('p2', 2, 'Joe', 'Black', 35, RAWTOHEX('Black')), " + + "('p3', 0, 'M', 'G', 4, RAWTOHEX('G'))"); + + stmt.addBatch("UPDATE Person SET id = 3, firstName = 'Mike', lastName = 'Green', " + + "age = 40, data = RAWTOHEX('Green') WHERE _key = 'p3'"); + + stmt.addBatch("DELETE FROM Person WHERE _key = 'p1'"); + + int[] res = stmt.executeBatch(); + + assertEquals(4, res.length); + assertEquals(1, res[0]); + assertEquals(3, res[1]); + assertEquals(1, res[2]); + assertEquals(1, res[3]); + } + } + + /** + * @throws SQLException If failed. + */ + public void testErrorAmidstBatch() throws SQLException { + BatchUpdateException reason = (BatchUpdateException) + GridTestUtils.assertThrows(log, + new Callable() { + @Override public Object call() throws Exception { + try (Statement stmt = conn.createStatement()) { + stmt.addBatch("INSERT INTO Person(_key, id, firstName, lastName, age, data) " + + "VALUES ('p1', 0, 'J', 'W', 250, RAWTOHEX('W'))"); + + stmt.addBatch("UPDATE Person SET id = 3, firstName = 'Mike', lastName = 'Green', " + + "age = 40, data = RAWTOHEX('Green') WHERE _key = 'p3'"); + + stmt.addBatch("SELECT id FROM Person WHERE _key = 'p1'"); + + return stmt.executeBatch(); + } + } + }, + BatchUpdateException.class, + "Given statement type does not match that declared by JDBC driver"); + + // Check update counts in the exception. + int[] counts = reason.getUpdateCounts(); + + assertEquals(2, counts.length); + assertEquals(1, counts[0]); + assertEquals(0, counts[1]); + } + + /** + * @throws Exception If failed. + */ + public void testClearBatch() throws Exception { + try (Statement stmt = conn.createStatement()) { + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws SQLException { + return stmt.executeBatch(); + } + }, SQLException.class, "Batch is empty"); + + stmt.addBatch("INSERT INTO Person(_key, id, firstName, lastName, age, data) " + + "VALUES ('p1', 0, 'J', 'W', 250, RAWTOHEX('W'))"); + + stmt.clearBatch(); + + GridTestUtils.assertThrows(log, new Callable() { + @Override public Object call() throws SQLException { + return stmt.executeBatch(); + } + }, SQLException.class, "Batch is empty"); + } + } +} diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcUpdateStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcUpdateStatementSelfTest.java index 8ae0e906acd69..07b5587ffedde 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcUpdateStatementSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcUpdateStatementSelfTest.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.jdbc2; +import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Arrays; import org.apache.ignite.cache.query.SqlFieldsQuery; @@ -47,4 +48,27 @@ public void testExecuteUpdate() throws SQLException { assertEquals(Arrays.asList(F.asList("John"), F.asList("Jack"), F.asList("Mike")), jcache(0).query(new SqlFieldsQuery("select firstName from Person order by _key")).getAll()); } + + /** + * @throws SQLException If failed. + */ + public void testBatch() throws SQLException { + PreparedStatement ps = conn.prepareStatement("update Person set lastName = concat(firstName, 'son') " + + "where firstName = ?"); + + ps.setString(1, "John"); + + ps.addBatch(); + + ps.setString(1, "Harry"); + + ps.addBatch(); + + int[] res = ps.executeBatch(); + + assertEquals(Arrays.asList(F.asList("Johnson"), F.asList("Black"), F.asList("Green")), + jcache(0).query(new SqlFieldsQuery("select lastName from Person order by _key")).getAll()); + + assertTrue(Arrays.equals(new int[] {1, 0}, res)); + } } 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 cf7ee8fa54194..a20002bd74721 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 @@ -93,9 +93,12 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDefaultNoOpCacheTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcMergeStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcBinaryMarshallerMergeStatementSelfTest.class)); + suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcUpdateStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcInsertStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcBinaryMarshallerInsertStatementSelfTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcDeleteStatementSelfTest.class)); + suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcStatementBatchingSelfTest.class)); + suite.addTest(new TestSuite(JdbcBlobTest.class)); suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcStreamingSelfTest.class)); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBatchUpdateTask.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBatchUpdateTask.java new file mode 100644 index 0000000000000..7b4846c939cd3 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcBatchUpdateTask.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.jdbc2; + +import java.sql.BatchUpdateException; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteJdbcDriver; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.processors.cache.QueryCursorImpl; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.lang.IgniteCallable; +import org.apache.ignite.resources.IgniteInstanceResource; + +import static java.sql.Statement.SUCCESS_NO_INFO; + +/** + * Task for SQL batched update statements execution through {@link IgniteJdbcDriver}. + */ +class JdbcBatchUpdateTask implements IgniteCallable { + /** Serial version uid. */ + private static final long serialVersionUID = 0L; + + /** Ignite. */ + @IgniteInstanceResource + private Ignite ignite; + + /** Cache name. */ + private final String cacheName; + + /** Schema name. */ + private final String schemaName; + + /** SQL command for argument batching. */ + private final String sql; + + /** Batch of statements. */ + private final List sqlBatch; + + /** Batch of arguments. */ + private final List> batchArgs; + + /** Fetch size. */ + private final int fetchSize; + + /** Local execution flag. */ + private final boolean loc; + + /** Local query flag. */ + private final boolean locQry; + + /** Collocated query flag. */ + private final boolean collocatedQry; + + /** Distributed joins flag. */ + private final boolean distributedJoins; + + /** + * @param ignite Ignite. + * @param cacheName Cache name. + * @param schemaName Schema name. + * @param sql SQL query. {@code null} in case of statement batching. + * @param sqlBatch Batch of SQL statements. {@code null} in case of parameter batching. + * @param batchArgs Batch of SQL parameters. {@code null} in case of statement batching. + * @param loc Local execution flag. + * @param fetchSize Fetch size. + * @param locQry Local query flag. + * @param collocatedQry Collocated query flag. + * @param distributedJoins Distributed joins flag. + */ + public JdbcBatchUpdateTask(Ignite ignite, String cacheName, String schemaName, String sql, + List sqlBatch, List> batchArgs, boolean loc, int fetchSize, + boolean locQry, boolean collocatedQry, boolean distributedJoins) { + this.ignite = ignite; + this.cacheName = cacheName; + this.schemaName = schemaName; + this.sql = sql; + this.sqlBatch = sqlBatch; + this.batchArgs = batchArgs; + this.fetchSize = fetchSize; + this.loc = loc; + this.locQry = locQry; + this.collocatedQry = collocatedQry; + this.distributedJoins = distributedJoins; + + assert (!F.isEmpty(sql) && !F.isEmpty(batchArgs)) ^ !F.isEmpty(sqlBatch); + } + + /** {@inheritDoc} */ + @Override public int[] call() throws Exception { + IgniteCache cache = ignite.cache(cacheName); + + // Don't create caches on server nodes in order to avoid of data rebalancing. + boolean start = ignite.configuration().isClientMode(); + + if (cache == null && cacheName == null) + cache = ((IgniteKernal)ignite).context().cache().getOrStartPublicCache(start, !loc && locQry); + + if (cache == null) { + if (cacheName == null) + throw new SQLException("Failed to execute query. No suitable caches found."); + else + throw new SQLException("Cache not found [cacheName=" + cacheName + ']'); + } + + int batchSize = F.isEmpty(sql) ? sqlBatch.size() : batchArgs.size(); + + int[] updCntrs = new int[batchSize]; + + int idx = 0; + + try { + if (F.isEmpty(sql)) { + for (; idx < batchSize; idx++) + updCntrs[idx] = doSingleUpdate(cache, sqlBatch.get(idx), null); + } + else { + for (; idx < batchSize; idx++) + updCntrs[idx] = doSingleUpdate(cache, sql, batchArgs.get(idx)); + } + } + catch (Exception ex) { + throw new BatchUpdateException(Arrays.copyOf(updCntrs, idx), ex); + } + + return updCntrs; + } + + /** + * Performs update. + * + * @param cache Cache. + * @param sqlText SQL text. + * @param args Parameters. + * @return Update counter. + * @throws SQLException If failed. + */ + private Integer doSingleUpdate(IgniteCache cache, String sqlText, List args) throws SQLException { + SqlFieldsQuery qry = new JdbcSqlFieldsQuery(sqlText, false); + + qry.setPageSize(fetchSize); + qry.setLocal(locQry); + qry.setCollocated(collocatedQry); + qry.setDistributedJoins(distributedJoins); + qry.setSchema(schemaName); + qry.setArgs(args == null ? null : args.toArray()); + + QueryCursorImpl> qryCursor = (QueryCursorImpl>)cache.withKeepBinary().query(qry); + + if (qryCursor.isQuery()) + throw new SQLException(getError("Query produced result set", qry)); + + List> rows = qryCursor.getAll(); + + if (F.isEmpty(rows)) + return SUCCESS_NO_INFO; + + if (rows.size() != 1) + throw new SQLException(getError("Expected single row for update operation result", qry)); + + List row = rows.get(0); + + if (F.isEmpty(row) || row.size() != 1) + throw new SQLException(getError("Expected row size of 1 for update operation", qry)); + + Object objRes = row.get(0); + + if (!(objRes instanceof Long)) + throw new SQLException(getError("Unexpected update result type", qry)); + + Long longRes = (Long)objRes; + + if (longRes > Integer.MAX_VALUE) { + IgniteLogger log = ignite.log(); + + if (log != null) + log.warning(getError("Query updated row counter (" + longRes + ") exceeds integer range", qry)); + + return Integer.MAX_VALUE; + } + + return longRes.intValue(); + } + + /** + * Formats error message with query details. + * + * @param msg Error message. + * @param qry Query. + * @return Result. + */ + private String getError(String msg, SqlFieldsQuery qry) { + return msg + " [qry='" + qry.getSql() + "', params=" + Arrays.deepToString(qry.getArgs()) + ']'; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcDatabaseMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcDatabaseMetadata.java index 98a25637be2c5..b369b0b6f5f86 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcDatabaseMetadata.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcDatabaseMetadata.java @@ -1063,7 +1063,7 @@ private List columnRow(String schema, String tbl, String col, int type, /** {@inheritDoc} */ @Override public boolean supportsBatchUpdates() throws SQLException { - return false; + return true; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java index 16030f7c5c984..38dfe02c19ece 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcPreparedStatement.java @@ -39,6 +39,7 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; +import java.util.List; /** * JDBC prepared statement implementation. @@ -50,6 +51,9 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat /** H2's parsed statement to retrieve metadata from. */ PreparedStatement nativeStatement; + /** Batch arguments. */ + private List> batchArgs; + /** * Creates new prepared statement. * @@ -66,7 +70,8 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat @Override public void addBatch(String sql) throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Adding new SQL command to batch not supported for prepared statement."); + throw new SQLFeatureNotSupportedException("Adding new SQL command to batch is not supported for prepared " + + "statement (use addBatch() to add new set of arguments)"); } /** {@inheritDoc} */ @@ -185,7 +190,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat @Override public void clearBatch() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Batch statements are not supported yet."); + batchArgs = null; } /** {@inheritDoc} */ @@ -207,14 +212,26 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat @Override public void addBatch() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Batch statements are not supported yet."); + if (batchArgs == null) + batchArgs = new ArrayList<>(); + + batchArgs.add(args); + + args = null; } /** {@inheritDoc} */ @Override public int[] executeBatch() throws SQLException { - throw new SQLFeatureNotSupportedException("Batch statements are not supported yet."); + ensureNotClosed(); + + List> batchArgs = this.batchArgs; + + this.batchArgs = null; + + return doBatchUpdate(sql, null, batchArgs); } + /** {@inheritDoc} */ @Override public void setCharacterStream(int paramIdx, Reader x, int len) throws SQLException { ensureNotClosed(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java index 89a80ca18ac4c..19c20a39d7412 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcStatement.java @@ -74,7 +74,7 @@ public class JdbcStatement implements Statement { /** Current updated items count. */ long updateCnt = -1; - /** Batch statements. */ + /** Batch of statements. */ private List batch; /** @@ -187,7 +187,7 @@ long doUpdate(String sql, Object[] args) throws SQLException { /** * @param rows query result. - * @return update counter, if found + * @return update counter, if found. * @throws SQLException if getting an update counter from result proved to be impossible. */ private static long updateCounterFromQueryResult(List> rows) throws SQLException { @@ -461,7 +461,60 @@ void closeInternal() throws SQLException { @Override public int[] executeBatch() throws SQLException { ensureNotClosed(); - throw new SQLFeatureNotSupportedException("Batch statements are not supported yet."); + List batch = this.batch; + + this.batch = null; + + return doBatchUpdate(null, batch, null); + } + + /** + * Runs batch of update commands. + * + * @param command SQL command. + * @param batch Batch of SQL commands. + * @param batchArgs Batch of SQL parameters. + * @return Number of affected rows. + * @throws SQLException If failed. + */ + protected int[] doBatchUpdate(String command, List batch, List> batchArgs) + throws SQLException { + rs = null; + + updateCnt = -1; + + if ((F.isEmpty(command) || F.isEmpty(batchArgs)) && F.isEmpty(batch)) + throw new SQLException("Batch is empty."); + + Ignite ignite = conn.ignite(); + + UUID nodeId = conn.nodeId(); + + boolean loc = nodeId == null; + + if (!conn.isDmlSupported()) + throw new SQLException("Failed to query Ignite: DML operations are supported in versions 1.8.0 and newer"); + + JdbcBatchUpdateTask task = new JdbcBatchUpdateTask(loc ? ignite : null, conn.cacheName(), + conn.schemaName(), command, batch, batchArgs, loc, getFetchSize(), conn.isLocalQuery(), + conn.isCollocatedQuery(), conn.isDistributedJoins()); + + try { + int[] res = loc ? task.call() : ignite.compute(ignite.cluster().forNodeId(nodeId)).call(task); + + updateCnt = F.isEmpty(res)? -1 : res[res.length - 1]; + + return res; + } + catch (IgniteSQLException e) { + throw e.toJdbcException(); + } + catch (SQLException e) { + throw e; + } + catch (Exception e) { + throw new SQLException("Failed to query Ignite.", e); + } } /** {@inheritDoc} */ From 621f62a41a7186d891ee2d743d911bfafa3dc617 Mon Sep 17 00:00:00 2001 From: devozerov Date: Thu, 17 Aug 2017 15:39:27 +0300 Subject: [PATCH 037/145] IGNITE-5837: Minor fix to DynamicIndexAbstractConcurrentSelfTest. This closes #2345. --- ...ynamicIndexAbstractConcurrentSelfTest.java | 115 ++++++++++-------- 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java index 00fd4135a6bff..7b53f7309362a 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicIndexAbstractConcurrentSelfTest.java @@ -66,7 +66,8 @@ public abstract class DynamicIndexAbstractConcurrentSelfTest extends DynamicInde private static final int LARGE_CACHE_SIZE = 100_000; /** Latches to block certain index operations. */ - private static final ConcurrentHashMap> BLOCKS = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap> BLOCKS = + new ConcurrentHashMap<>(); /** Cache mode. */ private final CacheMode cacheMode; @@ -88,8 +89,6 @@ public abstract class DynamicIndexAbstractConcurrentSelfTest extends DynamicInde /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { super.beforeTest(); - - GridQueryProcessor.idxCls = BlockingIndexing.class; } /** {@inheritDoc} */ @@ -139,16 +138,16 @@ public abstract class DynamicIndexAbstractConcurrentSelfTest extends DynamicInde */ public void testCoordinatorChange() throws Exception { // Start servers. - Ignite srv1 = Ignition.start(serverConfiguration(1)); - Ignite srv2 = Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); - Ignition.start(serverConfiguration(4)); + Ignite srv1 = ignitionStart(serverConfiguration(1)); + Ignite srv2 = ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); + ignitionStart(serverConfiguration(4)); UUID srv1Id = srv1.cluster().localNode().id(); UUID srv2Id = srv2.cluster().localNode().id(); // Start client which will execute operations. - Ignite cli = Ignition.start(clientConfiguration(5)); + Ignite cli = ignitionStart(clientConfiguration(5)); createSqlCache(cli); @@ -203,11 +202,11 @@ public void testCoordinatorChange() throws Exception { * @throws Exception If failed. */ public void testOperationChaining() throws Exception { - Ignite srv1 = Ignition.start(serverConfiguration(1)); + Ignite srv1 = ignitionStart(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); - Ignition.start(clientConfiguration(4)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); + ignitionStart(clientConfiguration(4)); createSqlCache(srv1); @@ -223,9 +222,9 @@ public void testOperationChaining() throws Exception { queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, CACHE_NAME, TBL_NAME, idx2, false); // Start even more nodes of different flavors - Ignition.start(serverConfiguration(5)); - Ignition.start(serverConfiguration(6, true)); - Ignition.start(clientConfiguration(7)); + ignitionStart(serverConfiguration(5)); + ignitionStart(serverConfiguration(6, true)); + ignitionStart(clientConfiguration(7)); assert !idxFut1.isDone(); assert !idxFut2.isDone(); @@ -255,7 +254,7 @@ public void testOperationChaining() throws Exception { * @throws Exception If failed. */ public void testNodeJoinOnPendingOperation() throws Exception { - Ignite srv1 = Ignition.start(serverConfiguration(1)); + Ignite srv1 = ignitionStart(serverConfiguration(1)); createSqlCache(srv1); @@ -266,9 +265,9 @@ public void testNodeJoinOnPendingOperation() throws Exception { IgniteInternalFuture idxFut = queryProcessor(srv1).dynamicIndexCreate(CACHE_NAME, CACHE_NAME, TBL_NAME, idx, false); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); - Ignition.start(clientConfiguration(4)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); + ignitionStart(clientConfiguration(4)); assert !idxFut.isDone(); @@ -293,10 +292,10 @@ public void testNodeJoinOnPendingOperation() throws Exception { */ public void testConcurrentPutRemove() throws Exception { // Start several nodes. - Ignite srv1 = Ignition.start(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3)); - Ignition.start(serverConfiguration(4)); + Ignite srv1 = ignitionStart(serverConfiguration(1)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3)); + ignitionStart(serverConfiguration(4)); awaitPartitionMapExchange(); @@ -390,8 +389,8 @@ public void testConcurrentPutRemove() throws Exception { */ public void testConcurrentRebalance() throws Exception { // Start cache and populate it with data. - Ignite srv1 = Ignition.start(serverConfiguration(1)); - Ignite srv2 = Ignition.start(serverConfiguration(2)); + Ignite srv1 = ignitionStart(serverConfiguration(1)); + Ignite srv2 = ignitionStart(serverConfiguration(2)); createSqlCache(srv1); @@ -412,12 +411,12 @@ public void testConcurrentRebalance() throws Exception { idxLatch2.countDown(); // Start two more nodes and unblock index operation in the middle. - Ignition.start(serverConfiguration(3)); + ignitionStart(serverConfiguration(3)); unblockIndexing(srv1); unblockIndexing(srv2); - Ignition.start(serverConfiguration(4)); + ignitionStart(serverConfiguration(4)); awaitPartitionMapExchange(); @@ -437,12 +436,12 @@ public void testConcurrentRebalance() throws Exception { */ public void testConcurrentCacheDestroy() throws Exception { // Start complex topology. - Ignite srv1 = Ignition.start(serverConfiguration(1)); + Ignite srv1 = ignitionStart(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); - Ignite cli = Ignition.start(clientConfiguration(4)); + Ignite cli = ignitionStart(clientConfiguration(4)); // Start cache and populate it with data. createSqlCache(cli); @@ -482,11 +481,11 @@ public void testConcurrentCacheDestroy() throws Exception { */ public void testConcurrentOperationsMultithreaded() throws Exception { // Start complex topology. - Ignition.start(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); + ignitionStart(serverConfiguration(1)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); - Ignite cli = Ignition.start(clientConfiguration(4)); + Ignite cli = ignitionStart(clientConfiguration(4)); createSqlCache(cli); @@ -556,11 +555,11 @@ public void testConcurrentOperationsMultithreaded() throws Exception { */ public void testQueryConsistencyMultithreaded() throws Exception { // Start complex topology. - Ignition.start(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); + ignitionStart(serverConfiguration(1)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); - Ignite cli = Ignition.start(clientConfiguration(4)); + Ignite cli = ignitionStart(clientConfiguration(4)); createSqlCache(cli); @@ -654,11 +653,11 @@ public void testClientReconnectWithCacheRestart() throws Exception { */ private void checkClientReconnect(final boolean restartCache) throws Exception { // Start complex topology. - final Ignite srv = Ignition.start(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); + final Ignite srv = ignitionStart(serverConfiguration(1)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); - final Ignite cli = Ignition.start(clientConfiguration(4)); + final Ignite cli = ignitionStart(clientConfiguration(4)); createSqlCache(cli); @@ -752,11 +751,11 @@ private void reconnectClientNode(final Ignite srvNode, final Ignite cliNode, fin */ public void testConcurrentOperationsAndNodeStartStopMultithreaded() throws Exception { // Start several stable nodes. - Ignition.start(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); + ignitionStart(serverConfiguration(1)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); - final Ignite cli = Ignition.start(clientConfiguration(4)); + final Ignite cli = ignitionStart(clientConfiguration(4)); createSqlCache(cli); @@ -798,7 +797,7 @@ public void testConcurrentOperationsAndNodeStartStopMultithreaded() throws Excep cfg = clientConfiguration(lastIdx); } - Ignition.start(cfg); + ignitionStart(cfg); exists = true; } @@ -877,11 +876,11 @@ public void testConcurrentOperationsAndNodeStartStopMultithreaded() throws Excep */ public void testConcurrentOperationsAndCacheStartStopMultithreaded() throws Exception { // Start complex topology. - Ignition.start(serverConfiguration(1)); - Ignition.start(serverConfiguration(2)); - Ignition.start(serverConfiguration(3, true)); + ignitionStart(serverConfiguration(1)); + ignitionStart(serverConfiguration(2)); + ignitionStart(serverConfiguration(3, true)); - Ignite cli = Ignition.start(clientConfiguration(4)); + Ignite cli = ignitionStart(clientConfiguration(4)); final AtomicBoolean stopped = new AtomicBoolean(); @@ -1091,4 +1090,16 @@ private static class BlockingIndexing extends IgniteH2Indexing { private IgniteCache createSqlCache(Ignite node) throws IgniteCheckedException { return createSqlCache(node, cacheConfiguration()); } + + /** + * Start a node. + * + * @param cfg Configuration. + * @return Ignite instance. + */ + private static Ignite ignitionStart(IgniteConfiguration cfg) { + GridQueryProcessor.idxCls = BlockingIndexing.class; + + return Ignition.start(cfg); + } } From a2ae057ac57cace756334044c35ac08e38d50eb6 Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Thu, 17 Aug 2017 15:54:21 +0300 Subject: [PATCH 038/145] IGNITE-6033 Added sorted and multithreaded modes in checkpointing algorithm - Fixes #2441. --- .../configuration/CheckpointWriteOrder.java | 33 ++++++++ .../PersistentStoreConfiguration.java | 26 ++++++ .../GridCacheDatabaseSharedManager.java | 81 ++++++++++++++----- ...tePersistenceSequentialCheckpointTest.java | 44 ++++++++++ .../IgnitePersistentStoreCacheGroupsTest.java | 31 ++++--- 5 files changed, 183 insertions(+), 32 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/configuration/CheckpointWriteOrder.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistenceSequentialCheckpointTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CheckpointWriteOrder.java b/modules/core/src/main/java/org/apache/ignite/configuration/CheckpointWriteOrder.java new file mode 100644 index 0000000000000..31feaf6f88d5c --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/configuration/CheckpointWriteOrder.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.configuration; + +/** + * This enum defines order of writing pages to disk storage during checkpoint. + */ +public enum CheckpointWriteOrder { + /** + * Pages are written in order provided by checkpoint pages collection iterator (which is basically a hashtable). + */ + RANDOM, + + /** + * All checkpoint pages are collected into single list and sorted by page index. + * Provides almost sequential disk writes, which can be much faster on some SSD models. + */ + SEQUENTIAL +} diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java index e8a0ff47e7015..5b902ac1e3adc 100644 --- a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java +++ b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java @@ -47,6 +47,9 @@ public class PersistentStoreConfiguration implements Serializable { /** Default number of checkpointing threads. */ public static final int DFLT_CHECKPOINTING_THREADS = 1; + /** Default checkpoint write order. */ + public static final CheckpointWriteOrder DFLT_CHECKPOINT_WRITE_ORDER = CheckpointWriteOrder.RANDOM; + /** Default number of checkpoints to be kept in WAL after checkpoint is finished */ public static final int DFLT_WAL_HISTORY_SIZE = 20; @@ -95,6 +98,9 @@ public class PersistentStoreConfiguration implements Serializable { /** */ private int checkpointingThreads = DFLT_CHECKPOINTING_THREADS; + /** Checkpoint write order. */ + private CheckpointWriteOrder checkpointWriteOrder = DFLT_CHECKPOINT_WRITE_ORDER; + /** Number of checkpoints to keep */ private int walHistSize = DFLT_WAL_HISTORY_SIZE; @@ -587,6 +593,26 @@ public long getWalAutoArchiveAfterInactivity() { return walAutoArchiveAfterInactivity; } + /** + * This property defines order of writing pages to disk storage during checkpoint. + * + * @return Checkpoint write order. + */ + public CheckpointWriteOrder getCheckpointWriteOrder() { + return checkpointWriteOrder; + } + + /** + * This property defines order of writing pages to disk storage during checkpoint. + * + * @param checkpointWriteOrder Checkpoint write order. + */ + public PersistentStoreConfiguration setCheckpointWriteOrder(CheckpointWriteOrder checkpointWriteOrder) { + this.checkpointWriteOrder = checkpointWriteOrder; + + return this; + } + /** {@inheritDoc} */ @Override public String toString() { return S.toString(PersistentStoreConfiguration.class, this); 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 56dcac042fab2..a55ba8688062b 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 @@ -49,7 +49,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -62,6 +61,7 @@ import org.apache.ignite.IgniteLogger; import org.apache.ignite.IgniteSystemProperties; import org.apache.ignite.PersistenceMetrics; +import org.apache.ignite.configuration.CheckpointWriteOrder; import org.apache.ignite.configuration.DataPageEvictionMode; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.configuration.MemoryConfiguration; @@ -135,6 +135,7 @@ import org.apache.ignite.lang.IgniteBiTuple; import org.apache.ignite.mxbean.PersistenceMetricsMXBean; import org.apache.ignite.thread.IgniteThread; +import org.apache.ignite.thread.IgniteThreadPoolExecutor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -382,11 +383,12 @@ private void initDataBase() { long cpBufSize = persistenceCfg.getCheckpointingPageBufferSize(); if (persistenceCfg.getCheckpointingThreads() > 1) - asyncRunner = new ThreadPoolExecutor( + asyncRunner = new IgniteThreadPoolExecutor( + "checkpoint-runner", + cctx.igniteInstanceName(), persistenceCfg.getCheckpointingThreads(), persistenceCfg.getCheckpointingThreads(), - 30L, - TimeUnit.SECONDS, + 30_000, new LinkedBlockingQueue() ); @@ -2086,10 +2088,10 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws WALPointer cpPtr = null; - GridMultiCollectionWrapper cpPages; - final CheckpointProgress curr; + IgniteBiTuple>, Integer> cpPagesTuple; + tracker.onLockWaitStart(); checkpointLock.writeLock().lock(); @@ -2154,19 +2156,9 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws if (curr.nextSnapshot) snapshotMgr.onMarkCheckPointBegin(curr.snapshotOperation, map); - IgniteBiTuple>, Integer> tup = beginAllCheckpoints(); + cpPagesTuple = beginAllCheckpoints(); - // Todo it maybe more optimally - Collection cpPagesList = new ArrayList<>(tup.get2()); - - for (GridMultiCollectionWrapper col : tup.get1()) { - for (int i = 0; i < col.collectionsSize(); i++) - cpPagesList.addAll(col.innerCollection(i)); - } - - cpPages = new GridMultiCollectionWrapper<>(cpPagesList); - - if (!F.isEmpty(cpPages)) { + if (!F.isEmpty(cpPagesTuple.get1())) { // No page updates for this checkpoint are allowed from now on. cpPtr = cctx.wal().log(cpRec); @@ -2182,7 +2174,7 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws curr.cpBeginFut.onDone(); - if (!F.isEmpty(cpPages)) { + if (!F.isEmpty(cpPagesTuple.get1())) { assert cpPtr != null; // Sync log outside the checkpoint write lock. @@ -2200,6 +2192,8 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws checkpointHist.addCheckpointEntry(cpEntry); + GridMultiCollectionWrapper cpPages = splitAndSortCpPagesIfNeeded(cpPagesTuple); + if (printCheckpointStats) if (log.isInfoEnabled()) log.info(String.format("Checkpoint started [checkpointId=%s, startPtr=%s, checkpointLockWait=%dms, " + @@ -2297,6 +2291,55 @@ public void shutdownNow() { } } + /** + * Reorders list of checkpoint pages and splits them into needed number of sublists according to + * {@link PersistentStoreConfiguration#getCheckpointingThreads()} and + * {@link PersistentStoreConfiguration#getCheckpointWriteOrder()}. + * + * @param cpPagesTuple Checkpoint pages tuple. + */ + private GridMultiCollectionWrapper splitAndSortCpPagesIfNeeded( + IgniteBiTuple>, Integer> cpPagesTuple) { + List cpPagesList = new ArrayList<>(cpPagesTuple.get2()); + + for (GridMultiCollectionWrapper col : cpPagesTuple.get1()) { + for (int i = 0; i < col.collectionsSize(); i++) + cpPagesList.addAll(col.innerCollection(i)); + } + + if (persistenceCfg.getCheckpointWriteOrder() == CheckpointWriteOrder.SEQUENTIAL) { + Collections.sort(cpPagesList, new Comparator() { + @Override public int compare(FullPageId o1, FullPageId o2) { + int cmp = Long.compare(o1.groupId(), o2.groupId()); + if (cmp != 0) + return cmp; + + return Long.compare(PageIdUtils.effectivePageId(o1.pageId()), + PageIdUtils.effectivePageId(o2.pageId())); + } + }); + } + + int cpThreads = persistenceCfg.getCheckpointingThreads(); + + int pagesSubLists = cpThreads == 1 ? 1 : cpThreads * 4; + // Splitting pages to (threads * 4) subtasks. If any thread will be faster, it will help slower threads. + + Collection[] pagesSubListArr = new Collection[pagesSubLists]; + + for (int i = 0; i < pagesSubLists; i++) { + int totalSize = cpPagesList.size(); + + int from = totalSize * i / (pagesSubLists); + + int to = totalSize * (i + 1) / (pagesSubLists); + + pagesSubListArr[i] = cpPagesList.subList(from, to); + } + + return new GridMultiCollectionWrapper(pagesSubListArr); + } + /** Pages write task */ private class WriteCheckpointPages implements Runnable { /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistenceSequentialCheckpointTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistenceSequentialCheckpointTest.java new file mode 100644 index 0000000000000..92950002d5f77 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistenceSequentialCheckpointTest.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.persistence; + +import org.apache.ignite.configuration.CheckpointWriteOrder; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +import org.apache.ignite.configuration.WALMode; + +/** + * + */ +public class IgnitePersistenceSequentialCheckpointTest extends IgnitePersistentStoreCacheGroupsTest { + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setPersistentStoreConfiguration(new PersistentStoreConfiguration() + .setWalMode(WALMode.LOG_ONLY) + .setCheckpointingThreads(4) + .setCheckpointWriteOrder(CheckpointWriteOrder.SEQUENTIAL)); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected int entriesCount() { + return 1000; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java index a945c73ed347f..b39b8cb6b6f9b 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java @@ -87,7 +87,7 @@ public class IgnitePersistentStoreCacheGroupsTest extends GridCommonAbstractTest MemoryConfiguration memCfg = new MemoryConfiguration(); memCfg.setPageSize(1024); - memCfg.setDefaultMemoryPolicySize(10 * 1024 * 1024); + memCfg.setDefaultMemoryPolicySize(100 * 1024 * 1024); cfg.setMemoryConfiguration(memCfg); @@ -115,6 +115,11 @@ public class IgnitePersistentStoreCacheGroupsTest extends GridCommonAbstractTest super.afterTest(); } + /** Entries count. */ + protected int entriesCount() { + return 10; + } + /** * @throws Exception If failed. */ @@ -236,7 +241,7 @@ public void _testExpiryPolicy() throws Exception { for (String cacheName : caches) { IgniteCache cache = node.cache(cacheName).withExpiryPolicy(plc); - for (int i = 0; i < 10; i++) + for (int i = 0; i < entriesCount(); i++) cache.put(i, cacheName + i); } @@ -253,10 +258,10 @@ public void _testExpiryPolicy() throws Exception { for (String cacheName : caches) { IgniteCache cache = node.cache(cacheName); - for (int i = 0; i < 10; i++) + for (int i = 0; i < entriesCount(); i++) assertEquals(cacheName + i, cache.get(i)); - assertEquals(10, cache.size()); + assertEquals(entriesCount(), cache.size()); } // Wait for expiration. @@ -340,7 +345,7 @@ private void putPersons(String[] caches, Ignite node) { for (String cacheName : caches) { IgniteCache cache = node.cache(cacheName); - for (int i = 0; i < 10; i++) + for (int i = 0; i < entriesCount(); i++) cache.put(i, new Person("" + i, cacheName)); } } @@ -353,10 +358,10 @@ private void checkPersons(String[] caches, Ignite node) { for (String cacheName : caches) { IgniteCache cache = node.cache(cacheName); - for (int i = 0; i < 10; i++) + for (int i = 0; i < entriesCount(); i++) assertEquals(new Person("" + i, cacheName), cache.get(i)); - assertEquals(10, cache.size()); + assertEquals(entriesCount(), cache.size()); } } @@ -373,10 +378,10 @@ private void checkPersonsQuery(String[] caches, Ignite node) { List> persons = cache.query(qry.setArgs(cacheName)).getAll(); - for (int i = 0; i < 10; i++) + for (int i = 0; i < entriesCount(); i++) assertEquals(new Person("" + i, cacheName), persons.get(i).getValue()); - assertEquals(10, persons.size()); + assertEquals(entriesCount(), persons.size()); } } @@ -413,13 +418,13 @@ private void clusterRestart(int nodes, boolean staticCaches) throws Exception { for (String cacheName : caches) { IgniteCache cache = node.cache(cacheName); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < entriesCount(); i++) { cache.put(i, cacheName + i); assertEquals(cacheName + i, cache.get(i)); } - assertEquals(10, cache.size()); + assertEquals(entriesCount(), cache.size()); } stopAllGrids(); @@ -433,10 +438,10 @@ private void clusterRestart(int nodes, boolean staticCaches) throws Exception { for (String cacheName : caches) { IgniteCache cache = node.cache(cacheName); - for (int i = 0; i < 10; i++) + for (int i = 0; i < entriesCount(); i++) assertEquals(cacheName + i, cache.get(i)); - assertEquals(10, cache.size()); + assertEquals(entriesCount(), cache.size()); } } From 752167976af6d361feb38c4940b1916da014dc74 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Thu, 17 Aug 2017 17:05:50 +0300 Subject: [PATCH 039/145] IGNITE-6098 Fixed IgniteDataIntegrityTests.testExpandBuffer - Fixes #2465. Signed-off-by: Alexey Goncharuk --- .../db/wal/crc/IgniteDataIntegrityTests.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java index 270c560bfcef6..e4874d97b47e3 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java @@ -122,7 +122,7 @@ public void testSkipingLastCorruptedEntry() throws Exception { * */ public void testExpandBuffer() { - ByteBufferExpander expBuf = new ByteBufferExpander(16, ByteOrder.nativeOrder()); + ByteBufferExpander expBuf = new ByteBufferExpander(24, ByteOrder.nativeOrder()); ByteBuffer b1 = expBuf.buffer(); @@ -131,21 +131,32 @@ public void testExpandBuffer() { b1.putLong(3L); assertEquals(13, b1.position()); - assertEquals(16, b1.limit()); + assertEquals(24, b1.limit()); ByteBuffer b2 = expBuf.expand(32); + assertEquals(13, b2.position()); + assertEquals(24, b2.limit()); + + b2.rewind(); + assertEquals(0, b2.position()); assertEquals((byte)1, b2.get()); assertEquals(2, b2.getInt()); assertEquals(3L, b2.getLong()); assertEquals(13, b2.position()); - assertEquals(32, b2.limit()); + assertEquals(24, b2.limit()); + assertEquals(32, b2.capacity()); + + b2.limit(b2.capacity()); b2.putInt(4); + b2.putInt(5); + b2.putInt(6); - assertEquals(17, b2.position()); + assertEquals(25, b2.position()); assertEquals(32, b2.limit()); + assertEquals(32, b2.capacity()); b2.flip(); @@ -154,7 +165,10 @@ public void testExpandBuffer() { assertEquals(2, b2.getInt()); assertEquals(3L, b2.getLong()); assertEquals(4, b2.getInt()); - assertEquals(17, b2.limit()); + assertEquals(5, b2.getInt()); + assertEquals(6, b2.getInt()); + assertEquals(25, b2.limit()); + assertEquals(32, b2.capacity()); } /** From a787eee339190a0d4110850fd876ea315f53ed53 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Thu, 17 Aug 2017 17:07:29 +0300 Subject: [PATCH 040/145] IGNITE-6100 Fixed memory leak, IgnitePdsRecoveryAfterFileCorruptionTest.testPageRecoveryAfterFileCorruption - Fixes #2466. Signed-off-by: Alexey Goncharuk --- .../persistence/pagemem/PageMemoryImpl.java | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java index 1b4cf81c9f2c2..b6e5f4653e944 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java @@ -47,6 +47,7 @@ import org.apache.ignite.internal.pagemem.PageUtils; import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager; import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager; +import org.apache.ignite.internal.pagemem.wal.WALIterator; import org.apache.ignite.internal.pagemem.wal.WALPointer; import org.apache.ignite.internal.pagemem.wal.record.CheckpointRecord; import org.apache.ignite.internal.pagemem.wal.record.PageSnapshot; @@ -670,55 +671,57 @@ private void tryToRestorePage(FullPageId fullId, long absPtr) throws IgniteCheck ByteBuffer curPage = null; ByteBuffer lastValidPage = null; - for (IgniteBiTuple tuple : walMgr.replay(null)) { - switch (tuple.getValue().type()) { - case PAGE_RECORD: - PageSnapshot snapshot = (PageSnapshot)tuple.getValue(); + try (WALIterator it = walMgr.replay(null)) { + for (IgniteBiTuple tuple : it) { + switch (tuple.getValue().type()) { + case PAGE_RECORD: + PageSnapshot snapshot = (PageSnapshot)tuple.getValue(); - if (snapshot.fullPageId().equals(fullId)) { - if (tmpAddr == null) { - assert snapshot.pageData().length <= pageSize() : snapshot.pageData().length; + if (snapshot.fullPageId().equals(fullId)) { + if (tmpAddr == null) { + assert snapshot.pageData().length <= pageSize() : snapshot.pageData().length; - tmpAddr = GridUnsafe.allocateMemory(pageSize()); - } + tmpAddr = GridUnsafe.allocateMemory(pageSize()); + } - if (curPage == null) - curPage = wrapPointer(tmpAddr, pageSize()); + if (curPage == null) + curPage = wrapPointer(tmpAddr, pageSize()); - PageUtils.putBytes(tmpAddr, 0, snapshot.pageData()); - } + PageUtils.putBytes(tmpAddr, 0, snapshot.pageData()); + } - break; + break; - case CHECKPOINT_RECORD: - CheckpointRecord rec = (CheckpointRecord)tuple.getValue(); + case CHECKPOINT_RECORD: + CheckpointRecord rec = (CheckpointRecord)tuple.getValue(); - assert !rec.end(); + assert !rec.end(); - if (curPage != null) { - lastValidPage = curPage; - curPage = null; - } + if (curPage != null) { + lastValidPage = curPage; + curPage = null; + } - break; + break; - case MEMORY_RECOVERY: // It means that previous checkpoint was broken. - curPage = null; + case MEMORY_RECOVERY: // It means that previous checkpoint was broken. + curPage = null; - break; + break; - default: - if (tuple.getValue() instanceof PageDeltaRecord) { - PageDeltaRecord deltaRecord = (PageDeltaRecord)tuple.getValue(); + default: + if (tuple.getValue() instanceof PageDeltaRecord) { + PageDeltaRecord deltaRecord = (PageDeltaRecord)tuple.getValue(); - if (curPage != null - && deltaRecord.pageId() == fullId.pageId() - && deltaRecord.groupId() == fullId.groupId()) { - assert tmpAddr != null; + if (curPage != null + && deltaRecord.pageId() == fullId.pageId() + && deltaRecord.groupId() == fullId.groupId()) { + assert tmpAddr != null; - deltaRecord.applyDelta(this, tmpAddr); + deltaRecord.applyDelta(this, tmpAddr); + } } - } + } } } From 9bca772d70efdbfa779c5f9dca26845626fcea43 Mon Sep 17 00:00:00 2001 From: devozerov Date: Thu, 17 Aug 2017 17:56:02 +0300 Subject: [PATCH 041/145] IGNITE-5233: Fixed JavaDocs. --- .../ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java index dd3b18b67c3c1..c0cfc9e1d4a5a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcParameterMeta.java @@ -26,8 +26,6 @@ /** * JDBC SQL query parameter metadata. - * - * {@see java.sql.ParameterMetaData}. */ public class JdbcParameterMeta implements JdbcRawBinarylizable { /** Null value is allow for the param. */ From 718f4a6395a02549d180a08ba23ac447bcd0cd78 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Thu, 17 Aug 2017 18:09:50 +0300 Subject: [PATCH 042/145] IGNITE-6103 - Handle missed partition ID during WAL replay --- .../GridCacheDatabaseSharedManager.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 a55ba8688062b..cc1599b10ad31 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 @@ -1623,8 +1623,12 @@ private boolean updateState(GridDhtLocalPartition part, int stateId) { * @param dataEntry Data entry to apply. */ private void applyUpdate(GridCacheContext cacheCtx, DataEntry dataEntry) throws IgniteCheckedException { - GridDhtLocalPartition locPart = cacheCtx.topology() - .localPartition(dataEntry.partitionId(), AffinityTopologyVersion.NONE, true); + int partId = dataEntry.partitionId(); + + if (partId == -1) + partId = cacheCtx.affinity().partition(dataEntry.key()); + + GridDhtLocalPartition locPart = cacheCtx.topology().localPartition(partId); switch (dataEntry.op()) { case CREATE: @@ -1639,15 +1643,15 @@ private void applyUpdate(GridCacheContext cacheCtx, DataEntry dataEntry) throws null); if (dataEntry.partitionCounter() != 0) - cacheCtx.offheap().onPartitionInitialCounterUpdated(dataEntry.partitionId(), dataEntry.partitionCounter()); + cacheCtx.offheap().onPartitionInitialCounterUpdated(partId, dataEntry.partitionCounter()); break; case DELETE: - cacheCtx.offheap().remove(cacheCtx, dataEntry.key(), dataEntry.partitionId(), locPart); + cacheCtx.offheap().remove(cacheCtx, dataEntry.key(), partId, locPart); if (dataEntry.partitionCounter() != 0) - cacheCtx.offheap().onPartitionInitialCounterUpdated(dataEntry.partitionId(), dataEntry.partitionCounter()); + cacheCtx.offheap().onPartitionInitialCounterUpdated(partId, dataEntry.partitionCounter()); break; From 2f2b180b0aae90d8eff706f868931343623150ab Mon Sep 17 00:00:00 2001 From: devozerov Date: Thu, 17 Aug 2017 18:24:34 +0300 Subject: [PATCH 043/145] IGNITE-5991: SQL: Lazy query execution. This closes #2437. --- .../ignite/cache/query/SqlFieldsQuery.java | 38 +- .../processors/query/h2/IgniteH2Indexing.java | 57 ++- .../h2/twostep/GridMapQueryExecutor.java | 237 ++++++++--- .../h2/twostep/GridReduceQueryExecutor.java | 7 +- .../query/h2/twostep/MapNodeResults.java | 19 +- .../query/h2/twostep/MapQueryLazyWorker.java | 176 ++++++++ .../h2/twostep/MapQueryLazyWorkerKey.java | 97 +++++ .../query/h2/twostep/MapQueryResult.java | 46 ++- .../query/h2/twostep/MapQueryResults.java | 26 +- .../query/h2/twostep/MapRequestKey.java | 23 +- .../h2/twostep/msg/GridH2QueryRequest.java | 9 + .../processors/query/LazyQuerySelfTest.java | 389 ++++++++++++++++++ .../IgniteCacheQuerySelfTestSuite.java | 2 + 13 files changed, 1041 insertions(+), 85 deletions(-) create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorker.java create mode 100644 modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorkerKey.java create mode 100644 modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/LazyQuerySelfTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java b/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java index 2838fe3e9b7ef..54f839691b448 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/query/SqlFieldsQuery.java @@ -71,6 +71,9 @@ public class SqlFieldsQuery extends Query> { /** */ private boolean replicatedOnly; + /** */ + private boolean lazy; + /** Partitions for query */ private int[] parts; @@ -230,7 +233,7 @@ public SqlFieldsQuery setDistributedJoins(boolean distributedJoins) { /** * Check if distributed joins are enabled for this query. * - * @return {@code true} If distributed joind enabled. + * @return {@code true} If distributed joins enabled. */ public boolean isDistributedJoins() { return distributedJoins; @@ -268,6 +271,39 @@ public boolean isReplicatedOnly() { return replicatedOnly; } + /** + * Sets lazy query execution flag. + *

    + * By default Ignite attempts to fetch the whole query result set to memory and send it to the client. For small + * and medium result sets this provides optimal performance and minimize duration of internal database locks, thus + * increasing concurrency. + *

    + * If result set is too big to fit in available memory this could lead to excessive GC pauses and even + * OutOfMemoryError. Use this flag as a hint for Ignite to fetch result set lazily, thus minimizing memory + * consumption at the cost of moderate performance hit. + *

    + * Defaults to {@code false}, meaning that the whole result set is fetched to memory eagerly. + * + * @param lazy Lazy query execution flag. + * @return {@code this} For chaining. + */ + public SqlFieldsQuery setLazy(boolean lazy) { + this.lazy = lazy; + + return this; + } + + /** + * Gets lazy query execution flag. + *

    + * See {@link #setLazy(boolean)} for more information. + * + * @return Lazy flag. + */ + public boolean isLazy() { + return lazy; + } + /** * Gets partitions for query, in ascending order. */ 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 007eeb1bdf751..6896f18d6a175 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 @@ -111,6 +111,7 @@ import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter; 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.schema.SchemaIndexCacheVisitor; import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure; import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor; @@ -139,6 +140,7 @@ import org.h2.api.JavaObjectSerializer; import org.h2.command.Prepared; import org.h2.command.dml.Insert; +import org.h2.engine.Session; import org.h2.engine.SysProperties; import org.h2.index.Index; import org.h2.jdbc.JdbcPreparedStatement; @@ -905,24 +907,32 @@ private PreparedStatement preparedStatementWithParams(Connection conn, String sq * @throws IgniteCheckedException If failed. */ private ResultSet executeSqlQuery(final Connection conn, final PreparedStatement stmt, - int timeoutMillis, @Nullable GridQueryCancel cancel) - throws IgniteCheckedException { + int timeoutMillis, @Nullable GridQueryCancel cancel) throws IgniteCheckedException { + final MapQueryLazyWorker lazyWorker = MapQueryLazyWorker.currentWorker(); if (cancel != null) { cancel.set(new Runnable() { @Override public void run() { - try { - stmt.cancel(); - } - catch (SQLException ignored) { - // No-op. + if (lazyWorker != null) { + lazyWorker.submit(new Runnable() { + @Override public void run() { + cancelStatement(stmt); + } + }); } + else + cancelStatement(stmt); } }); } + Session ses = H2Utils.session(conn); + if (timeoutMillis > 0) - H2Utils.session(conn).setQueryTimeout(timeoutMillis); + ses.setQueryTimeout(timeoutMillis); + + if (lazyWorker != null) + ses.setLazyQueryExecution(true); try { return stmt.executeQuery(); @@ -936,7 +946,24 @@ private ResultSet executeSqlQuery(final Connection conn, final PreparedStatement } finally { if (timeoutMillis > 0) - H2Utils.session(conn).setQueryTimeout(0); + ses.setQueryTimeout(0); + + if (lazyWorker != null) + ses.setLazyQueryExecution(false); + } + } + + /** + * Cancel prepared statement. + * + * @param stmt Statement. + */ + private static void cancelStatement(PreparedStatement stmt) { + try { + stmt.cancel(); + } + catch (SQLException ignored) { + // No-op. } } @@ -1143,6 +1170,7 @@ public GridCloseableIterator> queryLocalSql(String sc * @param keepCacheObj Flag to keep cache object. * @param enforceJoinOrder Enforce join order of tables. * @param parts Partitions. + * @param lazy Lazy query execution flag. * @return Iterable result. */ private Iterable> runQueryTwoStep( @@ -1153,12 +1181,13 @@ private Iterable> runQueryTwoStep( final int timeoutMillis, final GridQueryCancel cancel, final Object[] params, - final int[] parts + final int[] parts, + final boolean lazy ) { return new Iterable>() { @Override public Iterator> iterator() { return rdcQryExec.query(schemaName, qry, keepCacheObj, enforceJoinOrder, timeoutMillis, cancel, params, - parts); + parts, lazy); } }; } @@ -1402,7 +1431,7 @@ private Iterable> runQueryTwoStep( QueryCursorImpl> cursor = new QueryCursorImpl<>( runQueryTwoStep(schemaName, twoStepQry, keepBinary, enforceJoinOrder, qry.getTimeout(), cancel, - qry.getArgs(), partitions), cancel); + qry.getArgs(), partitions, qry.isLazy()), cancel); cursor.fieldsMeta(meta); @@ -2070,6 +2099,8 @@ private void createSqlFunctions(String schema, Class[] clss) throws IgniteChe if (log.isDebugEnabled()) log.debug("Stopping cache query index..."); + mapQryExec.cancelLazyWorkers(); + // unregisterMBean(); TODO https://issues.apache.org/jira/browse/IGNITE-2139 if (ctx != null && !ctx.cache().context().database().persistenceEnabled()) { for (H2Schema schema : schemas.values()) @@ -2355,6 +2386,8 @@ private int bindPartitionInfoParameter(CacheQueryPartitionInfo partInfo, Object[ /** {@inheritDoc} */ @Override public void cancelAllQueries() { + mapQryExec.cancelLazyWorkers(); + for (Connection conn : conns) U.close(conn, log); } 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 e717367c642e4..37e144efd0b38 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 @@ -28,7 +28,9 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; import javax.cache.CacheException; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; @@ -52,7 +54,6 @@ 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.cache.query.QueryTable; 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.opt.DistributedJoinMode; @@ -69,6 +70,7 @@ import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.thread.IgniteThread; import org.h2.jdbc.JdbcResultSet; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; @@ -107,6 +109,15 @@ public class GridMapQueryExecutor { /** */ private final ConcurrentMap reservations = new ConcurrentHashMap8<>(); + /** Lazy workers. */ + private final ConcurrentHashMap lazyWorkers = new ConcurrentHashMap<>(); + + /** Busy lock for lazy workers. */ + private final GridSpinBusyLock lazyWorkerBusyLock = new GridSpinBusyLock(); + + /** Lazy worker stop guard. */ + private final AtomicBoolean lazyWorkerStopGuard = new AtomicBoolean(); + /** * @param busyLock Busy lock. */ @@ -161,6 +172,21 @@ public void start(final GridKernalContext ctx, IgniteH2Indexing h2) throws Ignit }); } + /** + * Cancel active lazy queries and prevent submit of new queries. + */ + public void cancelLazyWorkers() { + if (!lazyWorkerStopGuard.compareAndSet(false, true)) + return; + + lazyWorkerBusyLock.block(); + + for (MapQueryLazyWorker worker : lazyWorkers.values()) + worker.stop(); + + lazyWorkers.clear(); + } + /** * @param nodeId Node ID. * @param msg Message. @@ -221,7 +247,7 @@ private MapNodeResults resultsForNode(UUID nodeId) { MapNodeResults nodeRess = qryRess.get(nodeId); if (nodeRess == null) { - nodeRess = new MapNodeResults(); + nodeRess = new MapNodeResults(nodeId); MapNodeResults old = qryRess.putIfAbsent(nodeId, nodeRess); @@ -416,6 +442,7 @@ private void onQueryRequest(final ClusterNode node, final GridH2QueryRequest req final boolean enforceJoinOrder = req.isFlagSet(GridH2QueryRequest.FLAG_ENFORCE_JOIN_ORDER); final boolean explain = req.isFlagSet(GridH2QueryRequest.FLAG_EXPLAIN); final boolean replicated = req.isFlagSet(GridH2QueryRequest.FLAG_REPLICATED); + final boolean lazy = req.isFlagSet(GridH2QueryRequest.FLAG_LAZY); final List cacheIds = req.caches(); @@ -429,30 +456,51 @@ private void onQueryRequest(final ClusterNode node, final GridH2QueryRequest req final int segment = i; - ctx.closure().callLocal( - new Callable() { - @Override public Void call() throws Exception { - onQueryRequest0(node, - req.requestId(), - segment, - req.schemaName(), - req.queries(), - cacheIds, - req.topologyVersion(), - partsMap, - parts, - req.tables(), - req.pageSize(), - joinMode, - enforceJoinOrder, - false, - req.timeout(), - params); - - return null; + if (lazy) { + onQueryRequest0(node, + req.requestId(), + segment, + req.schemaName(), + req.queries(), + cacheIds, + req.topologyVersion(), + partsMap, + parts, + req.pageSize(), + joinMode, + enforceJoinOrder, + false, // Replicated is always false here (see condition above). + req.timeout(), + params, + true); // Lazy = true. + } + else { + ctx.closure().callLocal( + new Callable() { + @Override + public Void call() throws Exception { + onQueryRequest0(node, + req.requestId(), + segment, + req.schemaName(), + req.queries(), + cacheIds, + req.topologyVersion(), + partsMap, + parts, + req.pageSize(), + joinMode, + enforceJoinOrder, + false, + req.timeout(), + params, + false); // Lazy = false. + + return null; + } } - } - , QUERY_POOL); + , QUERY_POOL); + } } onQueryRequest0(node, @@ -464,13 +512,13 @@ private void onQueryRequest(final ClusterNode node, final GridH2QueryRequest req req.topologyVersion(), partsMap, parts, - req.tables(), req.pageSize(), joinMode, enforceJoinOrder, replicated, req.timeout(), - params); + params, + lazy); } /** @@ -483,28 +531,61 @@ private void onQueryRequest(final ClusterNode node, final GridH2QueryRequest req * @param topVer Topology version. * @param partsMap Partitions map for unstable topology. * @param parts Explicit partitions for current node. - * @param tbls Tables. * @param pageSize Page size. * @param distributedJoinMode Query distributed join mode. + * @param lazy Streaming flag. */ private void onQueryRequest0( - ClusterNode node, - long reqId, - int segmentId, - String schemaName, - Collection qrys, - List cacheIds, - AffinityTopologyVersion topVer, - Map partsMap, - int[] parts, - Collection tbls, - int pageSize, - DistributedJoinMode distributedJoinMode, - boolean enforceJoinOrder, - boolean replicated, - int timeout, - Object[] params + final ClusterNode node, + final long reqId, + final int segmentId, + final String schemaName, + final Collection qrys, + final List cacheIds, + final AffinityTopologyVersion topVer, + final Map partsMap, + final int[] parts, + final int pageSize, + final DistributedJoinMode distributedJoinMode, + final boolean enforceJoinOrder, + final boolean replicated, + final int timeout, + final Object[] params, + boolean lazy ) { + if (lazy && MapQueryLazyWorker.currentWorker() == null) { + // Lazy queries must be re-submitted to dedicated workers. + MapQueryLazyWorkerKey key = new MapQueryLazyWorkerKey(node.id(), reqId, segmentId); + MapQueryLazyWorker worker = new MapQueryLazyWorker(ctx.igniteInstanceName(), key, log, this); + + 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); + } + }); + + if (lazyWorkerBusyLock.enterBusy()) { + try { + MapQueryLazyWorker oldWorker = lazyWorkers.put(key, worker); + + if (oldWorker != null) + oldWorker.stop(); + + IgniteThread thread = new IgniteThread(worker); + + thread.start(); + } + finally { + lazyWorkerBusyLock.leaveBusy(); + } + } + else + log.info("Ignored query request (node is stopping) [nodeId=" + node.id() + ", reqId=" + reqId + ']'); + + return; + } + // Prepare to run queries. GridCacheContext mainCctx = !F.isEmpty(cacheIds) ? ctx.cache().context().cacheContext(cacheIds.get(0)) : null; @@ -519,13 +600,18 @@ private void onQueryRequest0( if (topVer != null) { // Reserve primary for topology version or explicit partitions. if (!reservePartitions(cacheIds, topVer, parts, reserved)) { + // Unregister lazy worker because re-try may never reach this node again. + if (lazy) + stopAndUnregisterCurrentLazyWorker(); + sendRetry(node, reqId, segmentId); return; } } - qr = new MapQueryResults(h2, reqId, qrys.size(), mainCctx != null ? mainCctx.name() : null); + qr = new MapQueryResults(h2, reqId, qrys.size(), mainCctx != null ? mainCctx.name() : null, + MapQueryLazyWorker.currentWorker()); if (nodeRess.put(reqId, segmentId, qr) != null) throw new IllegalStateException(); @@ -570,8 +656,7 @@ private void onQueryRequest0( ResultSet rs = null; // 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()))) { + if (qry.node() == null || (segmentId == 0 && qry.node().equals(ctx.localNodeId()))) { rs = h2.executeSqlQueryWithTimer(conn, qry.query(), F.asList(qry.parameters(params)), true, timeout, @@ -624,6 +709,10 @@ private void onQueryRequest0( qr.cancel(false); } + // Unregister worker after possible cancellation. + if (lazy) + stopAndUnregisterCurrentLazyWorker(); + if (X.hasCause(e, GridH2RetryException.class)) sendRetry(node, reqId, segmentId); else { @@ -672,27 +761,39 @@ private void sendError(ClusterNode node, long qryReqId, Throwable err) { * @param node Node. * @param req Request. */ - private void onNextPageRequest(ClusterNode node, GridQueryNextPageRequest req) { - MapNodeResults nodeRess = qryRess.get(node.id()); + private void onNextPageRequest(final ClusterNode node, final GridQueryNextPageRequest req) { + final MapNodeResults nodeRess = qryRess.get(node.id()); if (nodeRess == null) { sendError(node, req.queryRequestId(), new CacheException("No node result found for request: " + req)); return; - } else if (nodeRess.cancelled(req.queryRequestId())) { + } + else if (nodeRess.cancelled(req.queryRequestId())) { sendError(node, req.queryRequestId(), new QueryCancelledException()); return; } - MapQueryResults qr = nodeRess.get(req.queryRequestId(), req.segmentId()); + final MapQueryResults qr = nodeRess.get(req.queryRequestId(), req.segmentId()); if (qr == null) sendError(node, req.queryRequestId(), new CacheException("No query result found for request: " + req)); else if (qr.cancelled()) sendError(node, req.queryRequestId(), new QueryCancelledException()); - else - sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize()); + else { + MapQueryLazyWorker lazyWorker = qr.lazyWorker(); + + if (lazyWorker != null) { + lazyWorker.submit(new Runnable() { + @Override public void run() { + sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize()); + } + }); + } + else + sendNextPage(nodeRess, node, qr, req.query(), req.segmentId(), req.pageSize()); + } } /** @@ -782,4 +883,34 @@ public void onCacheStop(String cacheName) { reservations.remove(grpKey); } } + + /** + * Unregister lazy worker if needed (i.e. if we are currently in lazy worker thread). + */ + public void stopAndUnregisterCurrentLazyWorker() { + MapQueryLazyWorker worker = MapQueryLazyWorker.currentWorker(); + + if (worker != null) { + worker.stop(); + + // Just stop is not enough as worker may be registered, but not started due to exception. + unregisterLazyWorker(worker); + } + } + + /** + * Unregister lazy worker. + * + * @param worker Worker. + */ + public void unregisterLazyWorker(MapQueryLazyWorker worker) { + lazyWorkers.remove(worker.key(), worker); + } + + /** + * @return Number of registered lazy workers. + */ + public int registeredLazyWorkers() { + return lazyWorkers.size(); + } } \ No newline at end of file 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 0e9d1a239b74f..8638794f8785e 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 @@ -506,6 +506,7 @@ else if (!isReplicatedOnly && !extraCctx.isReplicated()) { * @param cancel Query cancel. * @param params Query parameters. * @param parts Partitions. + * @param lazy Lazy execution flag. * @return Rows iterator. */ public Iterator> query( @@ -516,7 +517,8 @@ public Iterator> query( int timeoutMillis, GridQueryCancel cancel, Object[] params, - final int[] parts + final int[] parts, + boolean lazy ) { if (F.isEmpty(params)) params = EMPTY_PARAMS; @@ -712,6 +714,9 @@ public Iterator> query( if (isReplicatedOnly) flags |= GridH2QueryRequest.FLAG_REPLICATED; + if (lazy && mapQrys.size() == 1) + flags |= GridH2QueryRequest.FLAG_LAZY; + GridH2QueryRequest req = new GridH2QueryRequest() .requestId(qryReqId) .topologyVersion(topVer) diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java index d5ea357a5232c..2d20c8d52593e 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapNodeResults.java @@ -20,6 +20,7 @@ import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap; import org.jsr166.ConcurrentHashMap8; +import java.util.UUID; import java.util.concurrent.ConcurrentMap; import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q; @@ -35,6 +36,18 @@ class MapNodeResults { private final GridBoundedConcurrentLinkedHashMap qryHist = new GridBoundedConcurrentLinkedHashMap<>(1024, 1024, 0.75f, 64, PER_SEGMENT_Q); + /** Node ID. */ + private final UUID nodeId; + + /** + * Constructor. + * + * @param nodeId Node ID. + */ + public MapNodeResults(UUID nodeId) { + this.nodeId = nodeId; + } + /** * @param reqId Query Request ID. * @return {@code False} if query was already cancelled. @@ -59,7 +72,7 @@ boolean onCancel(long reqId) { * @return query partial results. */ public MapQueryResults get(long reqId, int segmentId) { - return res.get(new MapRequestKey(reqId, segmentId)); + return res.get(new MapRequestKey(nodeId, reqId, segmentId)); } /** @@ -84,7 +97,7 @@ public void cancelRequest(long reqId) { * @return {@code True} if removed. */ public boolean remove(long reqId, int segmentId, MapQueryResults qr) { - return res.remove(new MapRequestKey(reqId, segmentId), qr); + return res.remove(new MapRequestKey(nodeId, reqId, segmentId), qr); } /** @@ -94,7 +107,7 @@ public boolean remove(long reqId, int segmentId, MapQueryResults qr) { * @return previous value. */ public MapQueryResults put(long reqId, int segmentId, MapQueryResults qr) { - return res.put(new MapRequestKey(reqId, segmentId), qr); + return res.put(new MapRequestKey(nodeId, reqId, segmentId), qr); } /** diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorker.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorker.java new file mode 100644 index 0000000000000..51580353c94d0 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorker.java @@ -0,0 +1,176 @@ +/* + * 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; + +import org.apache.ignite.IgniteException; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.internal.IgniteInterruptedCheckedException; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.internal.util.worker.GridWorker; +import org.jetbrains.annotations.Nullable; +import org.jsr166.LongAdder8; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingDeque; + +/** + * Worker for lazy query execution. + */ +public class MapQueryLazyWorker extends GridWorker { + /** Lazy thread flag. */ + private static final ThreadLocal LAZY_WORKER = new ThreadLocal<>(); + + /** Active lazy worker count (for testing purposes). */ + private static final LongAdder8 ACTIVE_CNT = new LongAdder8(); + + /** Task to be executed. */ + private final BlockingQueue tasks = new LinkedBlockingDeque<>(); + + /** Key. */ + private final MapQueryLazyWorkerKey key; + + /** Map query executor. */ + private final GridMapQueryExecutor exec; + + /** Latch decremented when worker finishes. */ + private final CountDownLatch stopLatch = new CountDownLatch(1); + + /** Map query result. */ + private volatile MapQueryResult res; + + /** + * Constructor. + * + * @param instanceName Instance name. + * @param key Lazy worker key. + * @param log Logger. + * @param exec Map query executor. + */ + public MapQueryLazyWorker(@Nullable String instanceName, MapQueryLazyWorkerKey key, IgniteLogger log, + GridMapQueryExecutor exec) { + super(instanceName, workerName(instanceName, key), log); + + this.key = key; + this.exec = exec; + } + + /** {@inheritDoc} */ + @Override protected void body() throws InterruptedException, IgniteInterruptedCheckedException { + LAZY_WORKER.set(this); + + ACTIVE_CNT.increment(); + + try { + while (!isCancelled()) { + Runnable task = tasks.take(); + + if (task != null) + task.run(); + } + } + finally { + if (res != null) + res.close(); + + LAZY_WORKER.set(null); + + ACTIVE_CNT.decrement(); + + exec.unregisterLazyWorker(this); + } + } + + /** + * Submit task to worker. + * + * @param task Task to be executed. + */ + public void submit(Runnable task) { + tasks.add(task); + } + + /** + * @return Worker key. + */ + public MapQueryLazyWorkerKey key() { + return key; + } + + /** + * Stop the worker. + */ + public void stop() { + if (MapQueryLazyWorker.currentWorker() == null) + submit(new Runnable() { + @Override public void run() { + stop(); + } + }); + else { + isCancelled = true; + + stopLatch.countDown(); + } + } + + /** + * Await worker stop. + */ + public void awaitStop() { + try { + U.await(stopLatch); + } + catch (IgniteInterruptedCheckedException e) { + throw new IgniteException("Failed to wait for lazy worker stop (interrupted): " + name(), e); + } + } + + /** + * @param res Map query result. + */ + public void result(MapQueryResult res) { + this.res = res; + } + + /** + * @return Current worker or {@code null} if call is performed not from lazy worker thread. + */ + @Nullable public static MapQueryLazyWorker currentWorker() { + return LAZY_WORKER.get(); + } + + /** + * @return Active workers count. + */ + public static int activeCount() { + return ACTIVE_CNT.intValue(); + } + + /** + * Construct worker name. + * + * @param instanceName Instance name. + * @param key Key. + * @return Name. + */ + private static String workerName(String instanceName, MapQueryLazyWorkerKey key) { + return "query-lazy-worker_" + instanceName + "_" + key.nodeId() + "_" + key.queryRequestId() + "_" + + key.segment(); + } +} diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorkerKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorkerKey.java new file mode 100644 index 0000000000000..a0f5ebb2de4f3 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryLazyWorkerKey.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.query.h2.twostep; + +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +import java.util.UUID; + +/** + * Key to identify lazy worker. + */ +public class MapQueryLazyWorkerKey { + /** Client node ID. */ + private final UUID nodeId; + + /** Query request ID. */ + private final long qryReqId; + + /** Segment. */ + private final int segment; + + /** + * Constructor. + * + * @param nodeId Node ID. + * @param qryReqId Query request ID. + * @param segment Segment. + */ + public MapQueryLazyWorkerKey(UUID nodeId, long qryReqId, int segment) { + this.nodeId = nodeId; + this.qryReqId = qryReqId; + this.segment = segment; + } + + /** + * @return Node id. + */ + public UUID nodeId() { + return nodeId; + } + + /** + * @return Query request ID. + */ + public long queryRequestId() { + return qryReqId; + } + + /** + * @return Segment. + */ + public int segment() { + return segment; + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int res = nodeId.hashCode(); + + res = 31 * res + (int)(qryReqId ^ (qryReqId >>> 32)); + res = 31 * res + segment; + + return res; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object obj) { + if (obj != null && obj instanceof MapQueryLazyWorkerKey) { + MapQueryLazyWorkerKey other = (MapQueryLazyWorkerKey)obj; + + return F.eq(qryReqId, other.qryReqId) && F.eq(nodeId, other.nodeId) && F.eq(segment, other.segment); + } + + return false; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(MapQueryLazyWorkerKey.class, this); + } +} diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java index 4799e03c05b94..e54c784d75746 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapQueryResult.java @@ -26,6 +26,7 @@ import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.h2.jdbc.JdbcResultSet; +import org.h2.result.LazyResult; import org.h2.result.ResultInterface; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; @@ -41,7 +42,7 @@ /** * Mapper result for a single part of the query. */ -class MapQueryResult implements AutoCloseable { +class MapQueryResult { /** */ private static final Field RESULT_FIELD; @@ -95,24 +96,30 @@ class MapQueryResult implements AutoCloseable { /** */ private final Object[] params; + /** Lazy worker. */ + private final MapQueryLazyWorker lazyWorker; + /** * @param rs Result set. * @param cacheName Cache name. * @param qrySrcNodeId Query source node. * @param qry Query. * @param params Query params. + * @param lazyWorker Lazy worker. */ MapQueryResult(IgniteH2Indexing h2, ResultSet rs, @Nullable String cacheName, - UUID qrySrcNodeId, GridCacheSqlQuery qry, Object[] params) { + UUID qrySrcNodeId, GridCacheSqlQuery qry, Object[] params, @Nullable MapQueryLazyWorker lazyWorker) { this.h2 = h2; this.cacheName = cacheName; this.qry = qry; this.params = params; this.qrySrcNodeId = qrySrcNodeId; this.cpNeeded = F.eq(h2.kernalContext().localNodeId(), qrySrcNodeId); + this.lazyWorker = lazyWorker; if (rs != null) { this.rs = rs; + try { res = (ResultInterface)RESULT_FIELD.get(rs); } @@ -120,7 +127,7 @@ class MapQueryResult implements AutoCloseable { throw new IllegalStateException(e); // Must not happen. } - rowCnt = res.getRowCount(); + rowCnt = (res instanceof LazyResult) ? -1 : res.getRowCount(); cols = res.getVisibleColumnCount(); } else { @@ -167,6 +174,8 @@ boolean closed() { * @return {@code true} If there are no more rows available. */ synchronized boolean fetchNextPage(List rows, int pageSize) { + assert lazyWorker == null || lazyWorker == MapQueryLazyWorker.currentWorker(); + if (closed) return true; @@ -246,13 +255,34 @@ private List row(Value[] row) { return res; } - /** {@inheritDoc} */ - @Override public synchronized void close() { - if (closed) + /** + * Close the result. + */ + public void close() { + if (lazyWorker != null && MapQueryLazyWorker.currentWorker() == null) { + lazyWorker.submit(new Runnable() { + @Override public void run() { + close(); + } + }); + + lazyWorker.awaitStop(); + return; + } - closed = true; + synchronized (this) { + assert lazyWorker == null || lazyWorker == MapQueryLazyWorker.currentWorker(); - U.closeQuiet(rs); + if (closed) + return; + + closed = true; + + U.closeQuiet(rs); + + if (lazyWorker != null) + lazyWorker.stop(); + } } } 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 7ad1d14fa9fc7..99f196651f587 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 @@ -45,20 +45,27 @@ class MapQueryResults { /** */ private final String cacheName; + /** Lazy worker. */ + private final MapQueryLazyWorker lazyWorker; + /** */ private volatile boolean cancelled; /** + * Constructor. + * * @param qryReqId Query request ID. * @param qrys Number of queries. * @param cacheName Cache name. + * @param lazyWorker Lazy worker (if any). */ @SuppressWarnings("unchecked") - MapQueryResults(IgniteH2Indexing h2, long qryReqId, int qrys, - @Nullable String cacheName) { + MapQueryResults(IgniteH2Indexing h2, long qryReqId, int qrys, @Nullable String cacheName, + @Nullable MapQueryLazyWorker lazyWorker) { this.h2 = h2; this.qryReqId = qryReqId; this.cacheName = cacheName; + this.lazyWorker = lazyWorker; results = new AtomicReferenceArray<>(qrys); cancels = new GridQueryCancel[qrys]; @@ -86,13 +93,25 @@ GridQueryCancel queryCancel(int qryIdx) { } /** + * @return Lazy worker. + */ + MapQueryLazyWorker lazyWorker() { + return lazyWorker; + } + + /** + * Add result. + * * @param qry Query result index. * @param q Query object. * @param qrySrcNodeId Query source node. * @param rs Result set. */ void addResult(int qry, GridCacheSqlQuery q, UUID qrySrcNodeId, ResultSet rs, Object[] params) { - MapQueryResult res = new MapQueryResult(h2, rs, cacheName, qrySrcNodeId, q, params); + MapQueryResult res = new MapQueryResult(h2, rs, cacheName, qrySrcNodeId, q, params, lazyWorker); + + if (lazyWorker != null) + lazyWorker.result(res); if (!results.compareAndSet(qry, null, res)) throw new IllegalStateException(); @@ -130,6 +149,7 @@ void cancel(boolean forceQryCancel) { continue; } + // NB: Cancel is already safe even for lazy queries (see implementation of passed Runnable). if (forceQryCancel) { GridQueryCancel cancel = cancels[i]; diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java index 6feb8ea68da8c..9d987db693048 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/MapRequestKey.java @@ -17,18 +17,32 @@ package org.apache.ignite.internal.processors.query.h2.twostep; +import org.apache.ignite.internal.util.typedef.F; + +import java.util.UUID; + /** * Mapper request key. */ class MapRequestKey { + /** Node ID. */ + private UUID nodeId; + /** */ private long reqId; /** */ private int segmentId; - /** Constructor */ - MapRequestKey(long reqId, int segmentId) { + /** + * Constructor. + * + * @param nodeId Node ID. + * @param reqId Request ID. + * @param segmentId Segment ID. + */ + MapRequestKey(UUID nodeId, long reqId, int segmentId) { + this.nodeId = nodeId; this.reqId = reqId; this.segmentId = segmentId; } @@ -50,14 +64,15 @@ public long requestId() { MapRequestKey other = (MapRequestKey)o; - return reqId == other.reqId && segmentId == other.segmentId; + return F.eq(nodeId, other.nodeId) && reqId == other.reqId && segmentId == other.segmentId; } /** {@inheritDoc} */ @Override public int hashCode() { - int res = (int)(reqId ^ (reqId >>> 32)); + int res = nodeId != null ? nodeId.hashCode() : 0; + res = 31 * res + (int)(reqId ^ (reqId >>> 32)); res = 31 * res + segmentId; return res; 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 93a383cd12013..4e1fadbfe3825 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 @@ -78,6 +78,11 @@ public class GridH2QueryRequest implements Message, GridCacheQueryMarshallable { */ public static final int FLAG_REPLICATED = 1 << 4; + /** + * If lazy execution is enabled. + */ + public static final int FLAG_LAZY = 1 << 5; + /** */ private long reqId; @@ -185,6 +190,10 @@ public GridH2QueryRequest tables(Collection tbls) { } /** + * Get tables. + *

    + * N.B.: Was used in AI 1.9 for snapshots. Unused at the moment, but should be kept for compatibility reasons. + * * @return Tables. */ public Collection tables() { diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/LazyQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/LazyQuerySelfTest.java new file mode 100644 index 0000000000000..d5cc0ebc0ee91 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/LazyQuerySelfTest.java @@ -0,0 +1,389 @@ +/* + * 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.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +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.configuration.CacheConfiguration; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; +import org.apache.ignite.internal.processors.query.h2.twostep.MapQueryLazyWorker; +import org.apache.ignite.internal.util.lang.GridAbsPredicate; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Tests for lazy query execution. + */ +public class LazyQuerySelfTest extends GridCommonAbstractTest { + /** Keys ocunt. */ + private static final int KEY_CNT = 200; + + /** Base query argument. */ + private static final int BASE_QRY_ARG = 50; + + /** Size for small pages. */ + private static final int PAGE_SIZE_SMALL = 12; + + /** Cache name. */ + private static final String CACHE_NAME = "cache"; + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** + * Test local query execution. + * + * @throws Exception If failed. + */ + public void testSingleNode() throws Exception { + checkSingleNode(1); + } + + /** + * Test local query execution. + * + * @throws Exception If failed. + */ + public void testSingleNodeWithParallelism() throws Exception { + checkSingleNode(4); + } + + /** + * Test query execution with multiple topology nodes. + * + * @throws Exception If failed. + */ + public void testMultipleNodes() throws Exception { + checkMultipleNodes(1); + } + + /** + * Test query execution with multiple topology nodes with query parallelism. + * + * @throws Exception If failed. + */ + public void testMultipleNodesWithParallelism() throws Exception { + checkMultipleNodes(4); + } + + /** + * Check local query execution. + * + * @param parallelism Query parallelism. + * @throws Exception If failed. + */ + public void checkSingleNode(int parallelism) throws Exception { + Ignite srv = startGrid(); + + srv.createCache(cacheConfiguration(parallelism)); + + populateBaseQueryData(srv); + + checkBaseOperations(srv); + } + + /** + * Check query execution with multiple topology nodes. + * + * @param parallelism Query parallelism. + * @throws Exception If failed. + */ + public void checkMultipleNodes(int parallelism) throws Exception { + Ignite srv1 = startGrid(1); + Ignite srv2 = startGrid(2); + + Ignite cli; + + try { + Ignition.setClientMode(true); + + cli = startGrid(3); + } + finally { + Ignition.setClientMode(false); + } + + cli.createCache(cacheConfiguration(parallelism)); + + populateBaseQueryData(cli); + + checkBaseOperations(srv1); + checkBaseOperations(srv2); + checkBaseOperations(cli); + + // Test originating node leave. + FieldsQueryCursor> cursor = execute(cli, baseQuery().setPageSize(PAGE_SIZE_SMALL)); + + Iterator> iter = cursor.iterator(); + + for (int i = 0; i < 30; i++) + iter.next(); + + stopGrid(3); + + assertNoWorkers(); + + // Test server node leave with active worker. + cursor = execute(srv1, baseQuery().setPageSize(PAGE_SIZE_SMALL)); + + try { + iter = cursor.iterator(); + + for (int i = 0; i < 30; i++) + iter.next(); + + stopGrid(2); + } + finally { + cursor.close(); + } + + assertNoWorkers(); + } + + /** + * Check base operations. + * + * @param node Node. + * @throws Exception If failed. + */ + private void checkBaseOperations(Ignite node) throws Exception { + // Get full data. + List> rows = execute(node, baseQuery()).getAll(); + + assertBaseQueryResults(rows); + assertNoWorkers(); + + // Get data in several pages. + rows = execute(node, baseQuery().setPageSize(PAGE_SIZE_SMALL)).getAll(); + + assertBaseQueryResults(rows); + assertNoWorkers(); + + // Test full iteration. + rows = new ArrayList<>(); + + FieldsQueryCursor> cursor = execute(node, baseQuery().setPageSize(PAGE_SIZE_SMALL)); + + for (List row : cursor) + rows.add(row); + + assertBaseQueryResults(rows); + assertNoWorkers(); + + // Test partial iteration with cursor close. + try (FieldsQueryCursor> partialCursor = execute(node, baseQuery().setPageSize(PAGE_SIZE_SMALL))) { + Iterator> iter = partialCursor.iterator(); + + for (int i = 0; i < 30; i++) + iter.next(); + } + + assertNoWorkers(); + + // Test execution of multiple queries at a time. + List>> iters = new ArrayList<>(); + + for (int i = 0; i < 200; i++) + iters.add(execute(node, randomizedQuery().setPageSize(PAGE_SIZE_SMALL)).iterator()); + + while (!iters.isEmpty()) { + Iterator>> iterIter = iters.iterator(); + + while (iterIter.hasNext()) { + Iterator> iter = iterIter.next(); + + int i = 0; + + while (iter.hasNext() && i < 20) { + iter.next(); + + i++; + } + + if (!iter.hasNext()) + iterIter.remove(); + } + } + + assertNoWorkers(); + } + + /** + * Populate base query data. + * + * @param node Node. + */ + private static void populateBaseQueryData(Ignite node) { + IgniteCache cache = cache(node); + + for (long i = 0; i < KEY_CNT; i++) + cache.put(i, new Person(i)); + } + + /** + * @return Query with randomized argument. + */ + private static SqlFieldsQuery randomizedQuery() { + return query(ThreadLocalRandom.current().nextInt(KEY_CNT / 2)); + } + + /** + * @return Base query. + */ + private static SqlFieldsQuery baseQuery() { + return query(BASE_QRY_ARG); + } + + /** + * @param parallelism Query parallelism. + * @return Default cache configuration. + */ + private static CacheConfiguration cacheConfiguration(int parallelism) { + return new CacheConfiguration().setName(CACHE_NAME).setIndexedTypes(Long.class, Person.class) + .setQueryParallelism(parallelism); + } + + /** + * Default query. + * + * @param arg Argument. + * @return Query. + */ + private static SqlFieldsQuery query(long arg) { + return new SqlFieldsQuery("SELECT id, name FROM Person WHERE id >= ?").setArgs(arg); + } + + /** + * Assert base query results. + * + * @param rows Result rows. + */ + private static void assertBaseQueryResults(List> rows) { + assertEquals(KEY_CNT - BASE_QRY_ARG, rows.size()); + + for (List row : rows) { + Long id = (Long)row.get(0); + String name = (String)row.get(1); + + assertTrue(id >= BASE_QRY_ARG); + assertEquals(nameForId(id), name); + } + } + + /** + * Get cache for node. + * + * @param node Node. + * @return Cache. + */ + private static IgniteCache cache(Ignite node) { + return node.cache(CACHE_NAME); + } + + /** + * Execute query on the given cache. + * + * @param node Node. + * @param qry Query. + * @return Cursor. + */ + @SuppressWarnings("unchecked") + private static FieldsQueryCursor> execute(Ignite node, SqlFieldsQuery qry) { + return cache(node).query(qry.setLazy(true)); + } + + /** + * Make sure that are no active lazy workers. + * + * @throws Exception If failed. + */ + private static void assertNoWorkers() throws Exception { + assert GridTestUtils.waitForCondition(new GridAbsPredicate() { + @Override public boolean apply() { + for (Ignite node : Ignition.allGrids()) { + IgniteH2Indexing idx = (IgniteH2Indexing) ((IgniteKernal)node).context().query().getIndexing(); + + if (idx.mapQueryExecutor().registeredLazyWorkers() != 0) + return false; + } + + return MapQueryLazyWorker.activeCount() == 0; + } + }, 1000L); + } + + /** + * Get name for ID. + * + * @param id ID. + * @return Name. + */ + private static String nameForId(long id) { + return "name-" + id; + } + + /** + * Person class. + */ + private static class Person { + /** ID. */ + @QuerySqlField(index = true) + private long id; + + /** Name. */ + @QuerySqlField + private String name; + + /** + * Constructor. + * + * @param id ID. + */ + public Person(long id) { + this.id = id; + this.name = nameForId(id); + } + + /** + * @return ID. + */ + public long id() { + return id; + } + + /** + * @return Name. + */ + public String name() { + return name; + } + } +} 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 99b03707c2e2d..5ac0655f953e8 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 @@ -127,6 +127,7 @@ import org.apache.ignite.internal.processors.query.IgniteSqlSegmentedIndexMultiNodeSelfTest; import org.apache.ignite.internal.processors.query.IgniteSqlSegmentedIndexSelfTest; import org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest; +import org.apache.ignite.internal.processors.query.LazyQuerySelfTest; 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; @@ -184,6 +185,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IncorrectQueryEntityTest.class); // Queries tests. + suite.addTestSuite(LazyQuerySelfTest.class); suite.addTestSuite(IgniteSqlSplitterSelfTest.class); suite.addTestSuite(IgniteSqlSegmentedIndexSelfTest.class); suite.addTestSuite(IgniteSqlSegmentedIndexMultiNodeSelfTest.class); From df916d9f6266339167d3d23df0497480c8a80562 Mon Sep 17 00:00:00 2001 From: devozerov Date: Thu, 10 Aug 2017 23:13:37 +0300 Subject: [PATCH 044/145] IGNITE-6027: Added last page marker to GridQueryNexPageResponse, so that responses with unknown row count could be processed correctly. This closes #2426. --- .../messages/GridQueryNextPageResponse.java | 36 +++++++++++++++++-- .../h2/twostep/GridMapQueryExecutor.java | 6 ++-- .../query/h2/twostep/GridMergeIndex.java | 7 ++-- 3 files changed, 41 insertions(+), 8 deletions(-) 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 fe8b34a5471dc..4d918a04eacb8 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 @@ -67,6 +67,9 @@ public class GridQueryNextPageResponse implements Message { /** */ private AffinityTopologyVersion retry; + /** Last page flag. */ + private boolean last; + /** * For {@link Externalizable}. */ @@ -83,9 +86,10 @@ public GridQueryNextPageResponse() { * @param cols Number of columns in row. * @param vals Values for rows in this page added sequentially. * @param plainRows Not marshalled rows for local node. + * @param last Last page flag. */ public GridQueryNextPageResponse(long qryReqId, int segmentId, int qry, int page, int allRows, int cols, - Collection vals, Collection plainRows) { + Collection vals, Collection plainRows, boolean last) { assert vals != null ^ plainRows != null; assert cols > 0 : cols; @@ -97,6 +101,7 @@ public GridQueryNextPageResponse(long qryReqId, int segmentId, int qry, int page this.cols = cols; this.vals = vals; this.plainRows = plainRows; + this.last = last; } /** @@ -220,6 +225,11 @@ public Collection plainRows() { writer.incrementState(); + case 8: + if (!writer.writeBoolean("last", last)) + return false; + + writer.incrementState(); } return true; @@ -292,6 +302,14 @@ public Collection plainRows() { case 7: segmentId = reader.readInt("segmentId"); + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 8: + last = reader.readBoolean("last"); + if (!reader.isLastRead()) return false; @@ -308,7 +326,7 @@ public Collection plainRows() { /** {@inheritDoc} */ @Override public byte fieldsCount() { - return 8; + return 9; } /** @@ -325,6 +343,20 @@ public void retry(AffinityTopologyVersion retry) { this.retry = retry; } + /** + * @return Last page flag. + */ + public boolean last() { + return last; + } + + /** + * @param last Last page flag. + */ + public void last(boolean last) { + this.last = last; + } + /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridQueryNextPageResponse.class, this, 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 37e144efd0b38..0cc417281ddcd 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 @@ -833,7 +833,8 @@ private void sendNextPage(MapNodeResults nodeRess, ClusterNode node, MapQueryRes page == 0 ? res.rowCount() : -1, res.columnCount(), loc ? null : toMessages(rows, new ArrayList(res.columnCount())), - loc ? rows : null); + loc ? rows : null, + last); if (loc) h2.reduceQueryExecutor().onMessage(ctx.localNodeId(), msg); @@ -859,7 +860,8 @@ private void sendRetry(ClusterNode node, long reqId, int segmentId) { GridQueryNextPageResponse msg = new GridQueryNextPageResponse(reqId, segmentId, /*qry*/0, /*page*/0, /*allRows*/0, /*cols*/1, loc ? null : Collections.emptyList(), - loc ? Collections.emptyList() : null); + loc ? Collections.emptyList() : null, + false); msg.retry(h2.readyTopologyVersion()); diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java index 86601cda34d4e..1c1cfaf03e27a 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java @@ -46,7 +46,6 @@ import org.h2.message.DbException; import org.h2.result.Row; import org.h2.result.SearchRow; -import org.h2.table.Column; import org.h2.table.IndexColumn; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; @@ -329,7 +328,7 @@ private void initLastPages(UUID nodeId, GridQueryNextPageResponse res) { private void markLastPage(GridResultPage page) { GridQueryNextPageResponse res = page.response(); - if (res.allRows() != -2) { // -2 means the last page. + if (!res.last()) { UUID nodeId = page.source(); initLastPages(nodeId, res); @@ -337,12 +336,12 @@ private void markLastPage(GridResultPage page) { ConcurrentMap lp = lastPages; if (lp == null) - return; // It was not initialized --> wait for -2. + return; // It was not initialized --> wait for last page flag. Integer lastPage = lp.get(new SourceKey(nodeId, res.segmentId())); if (lastPage == null) - return; // This node may use the new protocol --> wait for -2. + return; // This node may use the new protocol --> wait for last page flag. if (lastPage != res.page()) { assert lastPage > res.page(); From 85299bd887cd16efde4a50bc2dfd8320e32f332d Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Thu, 17 Aug 2017 19:10:33 +0300 Subject: [PATCH 045/145] IGNITE-6102 - Implemented persistence configuration consistency check. Fixes #2470 --- .../apache/ignite/internal/IgniteKernal.java | 8 ++ .../ignite/internal/IgniteNodeAttributes.java | 3 + .../processors/cache/GridCacheProcessor.java | 21 +++++ ...emoryConfigurationConsistencySelfTest.java | 79 +++++++++++++++++++ .../testsuites/IgniteCacheTestSuite.java | 2 + 5 files changed, 113 insertions(+) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridMemoryConfigurationConsistencySelfTest.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 ae6722c198750..ca90a404e43d5 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 @@ -227,6 +227,7 @@ import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_MARSHALLER_COMPACT_FOOTER; 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_MEMORY_CONFIG; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_NODE_CONSISTENT_ID; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_PEER_CLASSLOADING; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_PHY_RAM; @@ -234,6 +235,7 @@ import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_RESTART_ENABLED; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_REST_PORT_RANGE; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_SPI_CLASS; +import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_TX_CONFIG; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_USER_NAME; import static org.apache.ignite.internal.IgniteVersionUtils.ACK_VER_STR; import static org.apache.ignite.internal.IgniteVersionUtils.BUILD_TSTAMP_STR; @@ -1568,6 +1570,12 @@ private void fillNodeAttributes(boolean notifyEnabled) throws IgniteCheckedExcep if (cfg.getConnectorConfiguration() != null) add(ATTR_REST_PORT_RANGE, cfg.getConnectorConfiguration().getPortRange()); + // Save database configuration. + add(ATTR_MEMORY_CONFIG, cfg.getMemoryConfiguration()); + + // Save transactions configuration. + add(ATTR_TX_CONFIG, cfg.getTransactionConfiguration()); + // Stick in SPI versions and classes attributes. addSpiAttributes(cfg.getCollisionSpi()); addSpiAttributes(cfg.getDiscoverySpi()); 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 a45f9915d8ad0..467956bdc2d1f 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 @@ -177,6 +177,9 @@ public final class IgniteNodeAttributes { /** Ignite security compatibility mode. */ public static final String ATTR_SECURITY_COMPATIBILITY_MODE = ATTR_PREFIX + ".security.compatibility.enabled"; + /** Memory configuration. */ + public static final String ATTR_MEMORY_CONFIG = ATTR_PREFIX + ".memory"; + /** * Enforces singleton. */ 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 ef27a14db4e47..306c595164c7c 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 @@ -52,6 +52,7 @@ import org.apache.ignite.configuration.DeploymentMode; import org.apache.ignite.configuration.FileSystemConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.MemoryConfiguration; import org.apache.ignite.configuration.NearCacheConfiguration; import org.apache.ignite.configuration.TransactionConfiguration; import org.apache.ignite.events.EventType; @@ -811,6 +812,8 @@ private void checkConsistency() throws IgniteCheckedException { checkTransactionConfiguration(n); + checkMemoryConfiguration(n); + DeploymentMode locDepMode = ctx.config().getDeploymentMode(); DeploymentMode rmtDepMode = n.attribute(IgniteNodeAttributes.ATTR_DEPLOYMENT_MODE); @@ -3033,6 +3036,24 @@ private void checkTransactionConfiguration(ClusterNode rmt) throws IgniteChecked } } + /** + * @param rmt Remote node to check. + * @throws IgniteCheckedException If check failed. + */ + private void checkMemoryConfiguration(ClusterNode rmt) throws IgniteCheckedException { + MemoryConfiguration memCfg = rmt.attribute(IgniteNodeAttributes.ATTR_MEMORY_CONFIG); + + if (memCfg != null) { + MemoryConfiguration locMemCfg = ctx.config().getMemoryConfiguration(); + + if (memCfg.getPageSize() != locMemCfg.getPageSize()) { + throw new IgniteCheckedException("Memory configuration mismatch (fix configuration or set -D" + + IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK + "=true system property) [rmtNodeId=" + rmt.id() + + ", locPageSize = " + locMemCfg.getPageSize() + ", rmtPageSize = " + memCfg.getPageSize() + "]"); + } + } + } + /** * @param cfg Cache configuration. * @return Query manager. diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridMemoryConfigurationConsistencySelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridMemoryConfigurationConsistencySelfTest.java new file mode 100644 index 0000000000000..bc71e336ae594 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridMemoryConfigurationConsistencySelfTest.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.processors.cache; + +import java.util.concurrent.Callable; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.MemoryConfiguration; +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; + +/** + * Tests a check of memory configuration consistency. + */ +public class GridMemoryConfigurationConsistencySelfTest extends GridCommonAbstractTest { + /** IP finder. */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + TcpDiscoverySpi discoSpi = new TcpDiscoverySpi(); + discoSpi.setIpFinder(IP_FINDER); + + cfg.setDiscoverySpi(discoSpi); + + MemoryConfiguration memCfg = new MemoryConfiguration(); + + // Nodes will have different page size. + memCfg.setPageSize(MemoryConfiguration.DFLT_PAGE_SIZE * (1 + getTestIgniteInstanceIndex(gridName))); + + cfg.setMemoryConfiguration(memCfg); + + return cfg; + } + + /** + * @throws Exception If failed. + */ + public void testMemoryConfigurationConsistency() throws Exception { + GridTestUtils.assertThrows(log, new Callable() { + /** {@inheritDoc} */ + @Override public Void call() throws Exception { + startGrids(2); + + return null; + } + }, IgniteCheckedException.class, null); + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + stopAllGrids(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java index ce1e3860c9989..a1e3d6a61c38b 100755 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java @@ -78,6 +78,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheSwapPreloadSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheTtlManagerSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheTxPartitionedLocalStoreSelfTest; +import org.apache.ignite.internal.processors.cache.GridMemoryConfigurationConsistencySelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheAtomicInvokeTest; import org.apache.ignite.internal.processors.cache.IgniteCacheAtomicLocalInvokeTest; import org.apache.ignite.internal.processors.cache.IgniteCacheAtomicLocalWithStoreInvokeTest; @@ -224,6 +225,7 @@ public static TestSuite suite(Set ignoredTests) throws Exception { // suite.addTestSuite(GridCacheP2PUndeploySelfTest.class); suite.addTestSuite(GridCacheConfigurationValidationSelfTest.class); suite.addTestSuite(GridCacheConfigurationConsistencySelfTest.class); + suite.addTestSuite(GridMemoryConfigurationConsistencySelfTest.class); suite.addTestSuite(GridCacheJdbcBlobStoreSelfTest.class); suite.addTestSuite(GridCacheJdbcBlobStoreMultithreadedSelfTest.class); suite.addTestSuite(JdbcTypesDefaultTransformerTest.class); From 9b071aa193f3b7c57d7c1014dd3eb0dd0047e9b5 Mon Sep 17 00:00:00 2001 From: Dmitriy Shabalin Date: Thu, 17 Aug 2017 23:57:07 +0700 Subject: [PATCH 046/145] IGNITE-6104 Web Console: moved "Download Web Agent" link to footer. (cherry picked from commit fb890a2) --- .../web-console-footer-links/component.js | 23 ++++++++++++++ .../web-console-footer-links/style.scss | 31 +++++++++++++++++++ .../web-console-footer-links/template.pug | 17 ++++++++++ .../components/web-console-footer/index.js | 4 ++- .../components/web-console-footer/style.scss | 8 ++++- .../web-console-footer/template.pug | 1 + .../components/web-console-header/style.scss | 17 ---------- .../web-console-header/template.pug | 9 ------ 8 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/component.js create mode 100644 modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/style.scss create mode 100644 modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug diff --git a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/component.js b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/component.js new file mode 100644 index 0000000000000..40a1051a75820 --- /dev/null +++ b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/component.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +import template from './template.pug'; +import './style.scss'; + +export default { + template +}; diff --git a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/style.scss b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/style.scss new file mode 100644 index 0000000000000..47b88af82c7c3 --- /dev/null +++ b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/style.scss @@ -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. + */ + +web-console-footer-links { + margin-left: auto; + + a + a { + margin-left: 5px; + + &:before { + content: '|'; + display: inline-block; + margin-right: 5px; + color: #fafafa; + } + } +} diff --git a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug new file mode 100644 index 0000000000000..105aa5131b414 --- /dev/null +++ b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug @@ -0,0 +1,17 @@ +//- + 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. + +a(href="/downloads/agent" target="_blank") Download Agent diff --git a/modules/web-console/frontend/app/components/web-console-footer/index.js b/modules/web-console/frontend/app/components/web-console-footer/index.js index 8796bb7b787be..e26b60238a6bb 100644 --- a/modules/web-console/frontend/app/components/web-console-footer/index.js +++ b/modules/web-console/frontend/app/components/web-console-footer/index.js @@ -17,7 +17,9 @@ import angular from 'angular'; import component from './component'; +import componentLinks from './components/web-console-footer-links/component'; export default angular .module('ignite-console.web-console-footer', []) - .component('webConsoleFooter', component); + .component('webConsoleFooter', component) + .component('webConsoleFooterLinks', componentLinks); diff --git a/modules/web-console/frontend/app/components/web-console-footer/style.scss b/modules/web-console/frontend/app/components/web-console-footer/style.scss index 4865ff2d439eb..f3de59fccedb5 100644 --- a/modules/web-console/frontend/app/components/web-console-footer/style.scss +++ b/modules/web-console/frontend/app/components/web-console-footer/style.scss @@ -37,10 +37,16 @@ web-console-footer { display: flex; flex-direction: row; align-items: center; + + & > :nth-child(2) { + margin-left: auto; + } } ignite-powered-by-apache { - margin-left: auto; + & > a { + margin-left: 40px; + } } a { diff --git a/modules/web-console/frontend/app/components/web-console-footer/template.pug b/modules/web-console/frontend/app/components/web-console-footer/template.pug index be9cc6a54acaa..e7eecf51be784 100644 --- a/modules/web-console/frontend/app/components/web-console-footer/template.pug +++ b/modules/web-console/frontend/app/components/web-console-footer/template.pug @@ -16,4 +16,5 @@ .container.wcf-content ignite-footer + web-console-footer-links(ng-if='$parent.user') ignite-powered-by-apache \ No newline at end of file diff --git a/modules/web-console/frontend/app/components/web-console-header/style.scss b/modules/web-console/frontend/app/components/web-console-header/style.scss index 5cc23551d02e9..aef4424140ac2 100644 --- a/modules/web-console/frontend/app/components/web-console-header/style.scss +++ b/modules/web-console/frontend/app/components/web-console-header/style.scss @@ -145,21 +145,4 @@ web-console-header { border-top: 1px solid darken($brand-warning, 15%); } } - - .wch-web-agent-download { - border-bottom-right-radius: $ignite-button-border-radius; - border-bottom-left-radius: $ignite-button-border-radius; - position: absolute; - top: 100%; - right: 0; - margin-top: $bottom-border-width; - // If defined on button, shadow would conflict with already present - // shadows of .btn-ignite, so we shadow wrapper instead. - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.3); - - .btn-ignite--primary { - border-top-right-radius: 0; - border-top-left-radius: 0; - } - } } \ No newline at end of file diff --git a/modules/web-console/frontend/app/components/web-console-header/template.pug b/modules/web-console/frontend/app/components/web-console-header/template.pug index 3971cec8ddd85..6930d0773da4c 100644 --- a/modules/web-console/frontend/app/components/web-console-header/template.pug +++ b/modules/web-console/frontend/app/components/web-console-header/template.pug @@ -25,12 +25,3 @@ .wch-slot.wch-slot-left(ng-transclude='slotLeft') .wch-slot.wch-slot-right(ng-transclude='slotRight') - - .wch-web-agent-download - a.btn-ignite.btn-ignite--primary( - ng-if='$ctrl.isWebAgentDownloadVisible' - href='/api/v1/downloads/agent' - target='_self' - ) - svg.icon-left(ignite-icon='download') - | Download Web Agent \ No newline at end of file From d81c32ee4eeb5676aa377927d287436cb70d99da Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Thu, 17 Aug 2017 20:35:06 +0300 Subject: [PATCH 047/145] gg-12644 : Fixed backward compatibility. --- .../processors/cache/persistence/MetadataStorage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java index 498ecdd3b14e3..ca3350c3a16a2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java @@ -344,7 +344,7 @@ public static final class MetaStoreInnerIO extends BPlusInnerIO imple */ private MetaStoreInnerIO(final int ver) { // name bytes and 1 byte for length, 8 bytes pageId - super(T_METASTORE_INNER, ver, false, MAX_IDX_NAME_LEN + 1 + 8); + super(T_METASTORE_INNER, ver, false, 768 + 1 + 8); } /** {@inheritDoc} */ @@ -385,7 +385,7 @@ public static final class MetaStoreLeafIO extends BPlusLeafIO impleme */ private MetaStoreLeafIO(final int ver) { // 4 byte cache ID, UTF-16 symbols and 1 byte for length, 8 bytes pageId - super(T_METASTORE_LEAF, ver, MAX_IDX_NAME_LEN + 1 + 8); + super(T_METASTORE_LEAF, ver, 768 + 1 + 8); } /** {@inheritDoc} */ From b6389d01dfdebd29b9108691f8ce2ef5991a4ef3 Mon Sep 17 00:00:00 2001 From: Vasiliy Sisko Date: Fri, 18 Aug 2017 11:08:13 +0700 Subject: [PATCH 048/145] IGNITE-5985 Web Console: added generation for QueryEntity key fields. (cherry picked from commit ef055fe) --- modules/web-console/backend/app/mongo.js | 1 + .../generator/ConfigurationGenerator.js | 3 ++- .../states/configuration/domains/query.pug | 7 ++++++ .../app/services/LegacyTable.service.js | 6 ++--- .../controllers/domains-controller.js | 24 ++++++++++++++----- .../configuration/domains-import.tpl.pug | 6 +++++ 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/modules/web-console/backend/app/mongo.js b/modules/web-console/backend/app/mongo.js index 0bc0e5c1dbe7c..28eaa5d2305d6 100644 --- a/modules/web-console/backend/app/mongo.js +++ b/modules/web-console/backend/app/mongo.js @@ -124,6 +124,7 @@ module.exports.factory = function(passportMongo, settings, pluginMongo, mongoose javaFieldName: String, javaFieldType: String }], + queryKeyFields: [String], fields: [{name: String, className: String}], aliases: [{field: String, alias: String}], indexes: [{ diff --git a/modules/web-console/frontend/app/modules/configuration/generator/ConfigurationGenerator.js b/modules/web-console/frontend/app/modules/configuration/generator/ConfigurationGenerator.js index a14bfd339c0b4..24dc0d3bac0e2 100644 --- a/modules/web-console/frontend/app/modules/configuration/generator/ConfigurationGenerator.js +++ b/modules/web-console/frontend/app/modules/configuration/generator/ConfigurationGenerator.js @@ -1695,7 +1695,8 @@ export default class IgniteConfigurationGenerator { fields.push({name: valFieldName, className: javaTypes.fullClassName(domain.valueType)}); } - cfg.mapProperty('fields', fields, 'fields', true) + cfg.collectionProperty('keyFields', 'keyFields', domain.queryKeyFields, 'java.lang.String', 'java.util.HashSet') + .mapProperty('fields', fields, 'fields', true) .mapProperty('aliases', 'aliases'); const indexes = _.map(domain.indexes, (index) => diff --git a/modules/web-console/frontend/app/modules/states/configuration/domains/query.pug b/modules/web-console/frontend/app/modules/states/configuration/domains/query.pug index 19cb83db3a19e..e8eceacc507dc 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/domains/query.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/domains/query.pug @@ -18,6 +18,7 @@ include /app/helpers/jade/mixins -var form = 'query' -var model = 'backupItem' +-var queryKeyFields = `${model}.queryKeyFields` -var queryFields = `${model}.fields` -var queryAliases = `${model}.aliases` -var queryIndexes = `${model}.indexes` @@ -99,6 +100,12 @@ mixin table-index-item-edit(prefix, index, sortAvailable, idAddition) tr td.col-sm-12 +table-pair-edit('queryFieldsTbl', 'new', 'Field name', 'Field full class name', false, true, '{{::queryFieldsTbl.focusId + $index}}', '-1', '/') + .settings-row + +ignite-form-field-dropdown('Key fields:', queryKeyFields, '"queryKeyFields"', false, false, true, + 'Select key fields', 'Configure available fields', 'fields(\'cur\', ' + queryKeyFields + ')', + 'Query fields that belongs to the key.
    \ + Used to build / modify keys and values during SQL DML operations when no key - value classes are present on cluster nodes.' + ) .settings-row +ignite-form-group(ng-model=queryAliases ng-form=queryAliasesForm) ignite-form-field-label diff --git a/modules/web-console/frontend/app/services/LegacyTable.service.js b/modules/web-console/frontend/app/services/LegacyTable.service.js index ab951647d6a52..38b041a0ada38 100644 --- a/modules/web-console/frontend/app/services/LegacyTable.service.js +++ b/modules/web-console/frontend/app/services/LegacyTable.service.js @@ -72,10 +72,10 @@ export default ['IgniteLegacyTable', table.editIndex = editIndex; } - function _tableUI(field) { - const ui = field.ui; + function _tableUI(tbl) { + const ui = tbl.ui; - return ui ? ui : field.type; + return ui ? ui : tbl.type; } function _tableFocus(focusId, index) { diff --git a/modules/web-console/frontend/controllers/domains-controller.js b/modules/web-console/frontend/controllers/domains-controller.js index 7c87ce00ef017..840086ea86f40 100644 --- a/modules/web-console/frontend/controllers/domains-controller.js +++ b/modules/web-console/frontend/controllers/domains-controller.js @@ -28,7 +28,7 @@ export default ['$rootScope', '$scope', '$http', '$state', '$filter', '$timeout' let __original_value; - const blank = {}; + const blank = {queryKeyFields: []}; // We need to initialize backupItem with empty object in order to properly used from angular directives. $scope.backupItem = emptyDomain; @@ -63,6 +63,7 @@ export default ['$rootScope', '$scope', '$http', '$state', '$filter', '$timeout' $scope.ui.generatePojo = true; $scope.ui.builtinKeys = true; + $scope.ui.generateKeyFields = true; $scope.ui.usePrimitives = true; $scope.ui.generateTypeAliases = true; $scope.ui.generateFieldAliases = true; @@ -145,13 +146,17 @@ export default ['$rootScope', '$scope', '$http', '$state', '$filter', '$timeout' $scope.tableRemove = function(item, field, index) { if ($scope.tableReset(true)) { - // Remove field from indexes. if (field.type === 'fields') { + // Remove field from indexes. _.forEach($scope.backupItem.indexes, (modelIndex) => { modelIndex.fields = _.filter(modelIndex.fields, (indexField) => { return indexField.name !== $scope.backupItem.fields[index].name; }); }); + + // Remove field from query key fields. + $scope.backupItem.queryKeyFields = _.filter($scope.backupItem.queryKeyFields, + (keyField) => keyField !== $scope.backupItem.fields[index].name); } LegacyTable.tableRemove(item, field, index); @@ -212,8 +217,10 @@ export default ['$rootScope', '$scope', '$http', '$state', '$filter', '$timeout' if (prefix === 'new') return fields; - if (cur && !_.find(fields, {value: cur})) - fields.push({value: cur, label: cur + ' (Unknown field)'}); + _.forEach(_.isArray(cur) ? cur : [cur], (value) => { + if (!_.find(fields, {value})) + fields.push({value, label: value + ' (Unknown field)'}); + }); return fields; }; @@ -929,6 +936,7 @@ export default ['$rootScope', '$scope', '$http', '$state', '$filter', '$timeout' newDomain.databaseSchema = table.schema; newDomain.databaseTable = tableName; newDomain.fields = qryFields; + newDomain.queryKeyFields = _.map(keyFields, (field) => field.javaFieldName); newDomain.indexes = indexes; newDomain.keyFields = keyFields; newDomain.aliases = aliases; @@ -945,8 +953,12 @@ export default ['$rootScope', '$scope', '$http', '$state', '$filter', '$timeout' newDomain.keyType = keyField.javaType; newDomain.keyFieldName = keyField.javaFieldName; - // Exclude key column from query fields. - newDomain.fields = _.filter(newDomain.fields, (field) => field.name !== keyField.javaFieldName); + if (!$scope.ui.generateKeyFields) { + // Exclude key column from query fields. + newDomain.fields = _.filter(newDomain.fields, (field) => field.name !== keyField.javaFieldName); + + newDomain.queryKeyFields = []; + } // Exclude key column from indexes. _.forEach(newDomain.indexes, (index) => { diff --git a/modules/web-console/frontend/views/configuration/domains-import.tpl.pug b/modules/web-console/frontend/views/configuration/domains-import.tpl.pug index e9ed6f3c0b687..369c7de236ac2 100644 --- a/modules/web-console/frontend/views/configuration/domains-import.tpl.pug +++ b/modules/web-console/frontend/views/configuration/domains-import.tpl.pug @@ -157,6 +157,12 @@ mixin td-ellipses-lbl(w, lbl) +checkbox('Use Java built-in types for keys', 'ui.builtinKeys', '"domainBuiltinKeys"', 'Use Java built-in types like "Integer", "Long", "String" instead of POJO generation in case when table primary key contains only one field') .settings-row +checkbox('Use primitive types for NOT NULL table columns', 'ui.usePrimitives', '"domainUsePrimitives"', 'Use primitive types like "int", "long", "double" for POJOs fields generation in case of NOT NULL columns') + .settings-row + +checkbox('Generate query entity key fields', 'ui.generateKeyFields', '"generateKeyFields"', + 'Generate key fields for query entity.\ + We need this for the cases when no key-value classes\ + are present on cluster nodes, and we need to build/modify keys and values during SQL DML operations.\ + Thus, setting this parameter is not mandatory and should be based on particular use case.') .settings-row +checkbox('Generate POJO classes', generatePojo, '"domainGeneratePojo"', 'If selected then POJO classes will be generated from database tables') .settings-row(ng-show=generatePojo) From 9cf8950af3945332d87d653ca841cf7b08be2e17 Mon Sep 17 00:00:00 2001 From: Vasiliy Sisko Date: Fri, 18 Aug 2017 11:12:27 +0700 Subject: [PATCH 049/145] IGNITE-6105 Web console: Fixed missed cache name in preview for cache checkpoint SPI. (cherry picked from commit bb326d1) --- .../ui-ace-java/ui-ace-java.controller.js | 13 +++++++++++++ .../ui-ace-spring/ui-ace-spring.controller.js | 13 +++++++++++++ .../configuration/generator/AbstractTransformer.js | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/web-console/frontend/app/directives/ui-ace-java/ui-ace-java.controller.js b/modules/web-console/frontend/app/directives/ui-ace-java/ui-ace-java.controller.js index 4275401c6c9fd..e50ac6cee3d45 100644 --- a/modules/web-console/frontend/app/directives/ui-ace-java/ui-ace-java.controller.js +++ b/modules/web-console/frontend/app/directives/ui-ace-java/ui-ace-java.controller.js @@ -87,6 +87,19 @@ export default ['IgniteVersion', 'JavaTransformer', function(Version, java) { return java.clusterServiceConfiguration(cluster.serviceConfigurations, clusterCaches); }; + break; + case 'clusterCheckpoint': + ctrl.generate = (cluster, caches) => { + const clusterCaches = _.reduce(caches, (acc, cache) => { + if (_.includes(cluster.caches, cache.value)) + acc.push(cache.cache); + + return acc; + }, []); + + return java.clusterCheckpoint(cluster, clusterCaches); + }; + break; case 'igfss': ctrl.generate = (cluster, igfss) => { diff --git a/modules/web-console/frontend/app/directives/ui-ace-spring/ui-ace-spring.controller.js b/modules/web-console/frontend/app/directives/ui-ace-spring/ui-ace-spring.controller.js index f14c7819b4606..17da1fd18d14c 100644 --- a/modules/web-console/frontend/app/directives/ui-ace-spring/ui-ace-spring.controller.js +++ b/modules/web-console/frontend/app/directives/ui-ace-spring/ui-ace-spring.controller.js @@ -83,6 +83,19 @@ export default ['IgniteVersion', 'SpringTransformer', function(Version, spring) return spring.clusterServiceConfiguration(cluster.serviceConfigurations, clusterCaches); }; + break; + case 'clusterCheckpoint': + ctrl.generate = (cluster, caches) => { + const clusterCaches = _.reduce(caches, (acc, cache) => { + if (_.includes(cluster.caches, cache.value)) + acc.push(cache.cache); + + return acc; + }, []); + + return spring.clusterCheckpoint(cluster, clusterCaches); + }; + break; case 'igfss': ctrl.generate = (cluster, igfss) => { diff --git a/modules/web-console/frontend/app/modules/configuration/generator/AbstractTransformer.js b/modules/web-console/frontend/app/modules/configuration/generator/AbstractTransformer.js index 4d66a6467631c..7e89fbddfb81c 100644 --- a/modules/web-console/frontend/app/modules/configuration/generator/AbstractTransformer.js +++ b/modules/web-console/frontend/app/modules/configuration/generator/AbstractTransformer.js @@ -294,7 +294,7 @@ export default class AbstractTransformer { } // Generate caches configs. - static clusterCheckpoint(cluster, available, caches) { + static clusterCheckpoint(cluster, caches) { return this.toSection(this.generator.clusterCheckpoint(cluster, caches)); } From 1193f5e418153234f3f1cec19a3ab6d55a8db526 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Fri, 18 Aug 2017 10:21:24 +0300 Subject: [PATCH 050/145] IGNITE-6102 - Do not run consistency check for client and daemon nodes --- .../ignite/internal/processors/cache/GridCacheProcessor.java | 5 +++++ 1 file changed, 5 insertions(+) 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 306c595164c7c..c4e8559a37785 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 @@ -3041,6 +3041,11 @@ private void checkTransactionConfiguration(ClusterNode rmt) throws IgniteChecked * @throws IgniteCheckedException If check failed. */ private void checkMemoryConfiguration(ClusterNode rmt) throws IgniteCheckedException { + ClusterNode locNode = ctx.discovery().localNode(); + + if (locNode.isClient() || locNode.isDaemon() || rmt.isClient() || rmt.isDaemon()) + return; + MemoryConfiguration memCfg = rmt.attribute(IgniteNodeAttributes.ATTR_MEMORY_CONFIG); if (memCfg != null) { From bcf908cb3fd9521be5f4b753029c718397e66657 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Fri, 18 Aug 2017 11:56:50 +0300 Subject: [PATCH 051/145] IGNITE-6102 - Do not run consistency check for client and daemon nodes --- .../ignite/internal/processors/cache/GridCacheProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c4e8559a37785..2e543c735015f 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 @@ -3043,7 +3043,7 @@ private void checkTransactionConfiguration(ClusterNode rmt) throws IgniteChecked private void checkMemoryConfiguration(ClusterNode rmt) throws IgniteCheckedException { ClusterNode locNode = ctx.discovery().localNode(); - if (locNode.isClient() || locNode.isDaemon() || rmt.isClient() || rmt.isDaemon()) + if (ctx.config().isClientMode() || locNode.isDaemon() || rmt.isClient() || rmt.isDaemon()) return; MemoryConfiguration memCfg = rmt.attribute(IgniteNodeAttributes.ATTR_MEMORY_CONFIG); From 49d73545ed703483bf9831abe4bc6ae6388b0901 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Fri, 18 Aug 2017 12:47:08 +0300 Subject: [PATCH 052/145] IGNITE-5901 Fixed AsynchronousCloseException in WAL --- .../wal/FileWriteAheadLogManager.java | 68 +++++++++++-------- .../db/wal/IgniteWalRecoveryTest.java | 2 + 2 files changed, 41 insertions(+), 29 deletions(-) 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 bb1f910189736..a9327a0d99936 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 @@ -2019,6 +2019,9 @@ private void fsync(FileWALPointer ptr, boolean stop) throws StorageException, Ig flushOrWait(ptr, stop); + if (stopped()) + return; + if (lastFsyncPos != written) { assert lastFsyncPos < written; // Fsync position must be behind. @@ -2056,51 +2059,58 @@ private void fsync(FileWALPointer ptr, boolean stop) throws StorageException, Ig */ private boolean close(boolean rollOver) throws IgniteCheckedException, StorageException { if (stop.compareAndSet(false, true)) { - flushOrWait(null, true); - - assert stopped() : "Segment is not closed after close flush: " + head.get(); + lock.lock(); try { - int switchSegmentRecSize = RecordV1Serializer.REC_TYPE_SIZE + RecordV1Serializer.FILE_WAL_POINTER_SIZE; + flushOrWait(null, true); + + assert stopped() : "Segment is not closed after close flush: " + head.get(); + + try { + int switchSegmentRecSize = RecordV1Serializer.REC_TYPE_SIZE + RecordV1Serializer.FILE_WAL_POINTER_SIZE; - if (rollOver && written < (maxSegmentSize - switchSegmentRecSize)) { - //it is expected there is sufficient space for this record because rollover should run early - final ByteBuffer buf = ByteBuffer.allocate(switchSegmentRecSize); - buf.put((byte)(WALRecord.RecordType.SWITCH_SEGMENT_RECORD.ordinal() + 1)); + if (rollOver && written < (maxSegmentSize - switchSegmentRecSize)) { + //it is expected there is sufficient space for this record because rollover should run early + final ByteBuffer buf = ByteBuffer.allocate(switchSegmentRecSize); + buf.put((byte)(WALRecord.RecordType.SWITCH_SEGMENT_RECORD.ordinal() + 1)); - final FileWALPointer pointer = new FileWALPointer(idx, (int)fileIO.position(), -1); - RecordV1Serializer.putPosition(buf, pointer); + final FileWALPointer pointer = new FileWALPointer(idx, (int)fileIO.position(), -1); + RecordV1Serializer.putPosition(buf, pointer); - buf.rewind(); + buf.rewind(); - int rem = buf.remaining(); + int rem = buf.remaining(); - while (rem > 0) { - int written0 = fileIO.write(buf, written); + while (rem > 0) { + int written0 = fileIO.write(buf, written); - written += written0; + written += written0; - rem -= written0; + rem -= written0; + } } - } - // Do the final fsync. - if (mode == WALMode.DEFAULT) { - fileIO.force(); + // Do the final fsync. + if (mode == WALMode.DEFAULT) { + fileIO.force(); - lastFsyncPos = written; + lastFsyncPos = written; + } + + fileIO.close(); } + catch (IOException e) { + throw new IgniteCheckedException(e); + } + + if (log.isDebugEnabled()) + log.debug("Closed WAL write handle [idx=" + idx + "]"); - fileIO.close(); + return true; } - catch (IOException e) { - throw new IgniteCheckedException(e); + finally { + lock.unlock(); } - - if (log.isDebugEnabled()) - log.debug("Closed WAL write handle [idx=" + idx + "]"); - - return true; } else return false; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRecoveryTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRecoveryTest.java index c5d6a8b16d7c3..399e36d89e380 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRecoveryTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalRecoveryTest.java @@ -138,6 +138,8 @@ public class IgniteWalRecoveryTest extends GridCommonAbstractTest { pCfg.setWalRecordIteratorBufferSize(1024 * 1024); + pCfg.setWalHistorySize(2); + if (logOnly) pCfg.setWalMode(WALMode.LOG_ONLY); From 55dac67098f2d6a7acde7b12337475b2e1a6f7d8 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Fri, 18 Aug 2017 13:07:39 +0300 Subject: [PATCH 053/145] gg-12644 : Cosmetic changes. --- .../processors/cache/persistence/MetadataStorage.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java index ca3350c3a16a2..359e54ebe7cc9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java @@ -41,6 +41,9 @@ public class MetadataStorage implements MetaStore { /** Max index name length (bytes num) */ public static final int MAX_IDX_NAME_LEN = 255; + /** Reserved size for index name. Needed for backward compatibility. */ + public static final int RESERVED_IDX_NAME_LEN = 768; + /** Bytes in byte. */ private static final int BYTE_LEN = 1; @@ -344,7 +347,7 @@ public static final class MetaStoreInnerIO extends BPlusInnerIO imple */ private MetaStoreInnerIO(final int ver) { // name bytes and 1 byte for length, 8 bytes pageId - super(T_METASTORE_INNER, ver, false, 768 + 1 + 8); + super(T_METASTORE_INNER, ver, false, RESERVED_IDX_NAME_LEN + 1 + 8); } /** {@inheritDoc} */ @@ -385,7 +388,7 @@ public static final class MetaStoreLeafIO extends BPlusLeafIO impleme */ private MetaStoreLeafIO(final int ver) { // 4 byte cache ID, UTF-16 symbols and 1 byte for length, 8 bytes pageId - super(T_METASTORE_LEAF, ver, 768 + 1 + 8); + super(T_METASTORE_LEAF, ver, RESERVED_IDX_NAME_LEN + 1 + 8); } /** {@inheritDoc} */ From 1406c19ec3e84c3bfb13d94b6e4f9d21eb03506b Mon Sep 17 00:00:00 2001 From: Vasiliy Sisko Date: Fri, 18 Aug 2017 17:04:41 +0700 Subject: [PATCH 054/145] IGNITE-6106 New screenshots. (cherry picked from commit 9ca9bf6) --- .../frontend/app/data/getting-started.json | 13 +++++++++++++ .../frontend/public/images/multicluster.png | Bin 0 -> 21921 bytes .../web-console/frontend/views/signin.tpl.pug | 6 ++++++ 3 files changed, 19 insertions(+) create mode 100644 modules/web-console/frontend/public/images/multicluster.png diff --git a/modules/web-console/frontend/app/data/getting-started.json b/modules/web-console/frontend/app/data/getting-started.json index 1b435ab68cd8a..fd1869cb9fedf 100644 --- a/modules/web-console/frontend/app/data/getting-started.json +++ b/modules/web-console/frontend/app/data/getting-started.json @@ -105,5 +105,18 @@ " ", "" ] + }, + { + "title": "Multicluster support", + "message": [ + "

    ", + " ", + "
    ", + "
    ", + "
      ", + "
    • Execute queries on different clusters
    • ", + "
    ", + "
    " + ] } ] diff --git a/modules/web-console/frontend/public/images/multicluster.png b/modules/web-console/frontend/public/images/multicluster.png new file mode 100644 index 0000000000000000000000000000000000000000..dd82495c4d8888d11af2dcb294212378848d9536 GIT binary patch literal 21921 zcmbq)Ra6{L&@Tkn;IJXMB*0>eyIXK)aa%k+9p=j1C1_X3SvTFeM3GzWk}`D%hT{!r1BS0;l9Ub5r?{* zoSdC1E93m7$>$e-;V);6&U2}{Pgh;SW~IUkVeJI?ZI_ScqKeCp&o3|k6s;l!1dX1K z8)3&!LK^YGrWz}Srq2s$&u8N%re==yr^$2A!jj6*f6HoWYFgV`o#ln6rlxFdY{tjO zONxtJTwHv*9#VcTN{ia*%8BYYW}6ENM2tPl1r8J%35m)XR$EKXTs#R#`$?>p|I`a@SwB9xxj7P)|55|t+`7CI_v${IfwrvQpLGYRMUTQu-P81hU!K|}-CM*3 zBqmli?yuH_4bprn;dTDt=$>739c_Cp*#If-fy1Z$U*&@ogkJ3@Cx=U6$|4ILS@Vq% zX%0G<3al2PP#sOLQXk`x{#)qGSw;WW<8ppNOte!%*Qs-Ta!S&|NMk!GSBbU6%OSLx zJJ4TBGG_d`Yi9ZK7*6xPye+}r$x!gw>y1x&Henp!} zHXI#2)|=~dLYuz%{OP+{+`VwAT8nNSXrt3TaQb+&@m&39_D0uwb7?Xo!X-wLr?ceS zUzo?5CdD}d!p+#!dE!)$-}kP`L&uObSZ3N37s=z=+FeH0KwH@`H2Ef9@3Qt_xL~~^ zc_}yp+Nqq><7v5I~JQ zfu0lMKtK&0qv4z{W6PpJB~ZW~-o&QhRnH7=B|-f{*vorw(T8^v`7h6P(e>e{BZqV9 zCG~Bd@7OO=-=E{FuB)yU;?9Z~N<7wC6IRwSQc5=;Tjs|b#Z!S!woTdjCj~sRN8R4DXO^O1ax>;_6f(Q z(tTVWCIzykI8$Vf&E({U5tm9-2N)35_KgH>yA~@uNsHAD2b@fYuCHf&1R2mQi-(T< zWnDx@j3S>i_vRyfh5*jTET8g@h_n&{LazIiLX7+dQEvYObGA1fFcma~?r0$2KnhB? zsE#Xu5MhKj{IsR>pmP#A^iW;24E)O}632VG{SCF7!`s8z`K*HGydL@!+afGcEa&1E zVuaM}7M~e60DXG2n*JRzJkB=PO&sbN;D9tyLPrCKn#DW*{rE(2QmJHA&-z_{A)H0^ zxy;`)$83x5bJcI56Agxdq2NnuGDcR$)XGBQqJ;_F=?P!=k_?e0T?<-W2^B(;J)^me z_}1(8XyRI?rU+BVODzfE(YCyW++@!5PQ}Ug+o4beR$qP|p2nj#|5kE9=9k~pU6EGH zAS_3^2FXNz+*J$9nu9Z0TP^yW_UElMs#(V=Dd2STkCyJ7S<2hC> ztXQ`ub-`qKNU3obj&bCwm&ovDu;!$?j4!O@Snr#LwJ^FodJ$oNMsx&RGjgpAJl07vEey+It+W*nK%gKI zBdhr0uH_UoXM+ZB@Ah5TlSB*tsPdrd2Ah~FGSt8dtn(m&6hwjqj{@5FrGA<2#q9qk zQt(-KZW;9I zJ#+({!%?ki;0~76sM5+$Q%M$Bczg1!Js>(Uz_QkGj#cmlQkmaED$_1=-JzbE2mSVP zO1ZVu-BGeKVO0f$i*IhFvmDd|gRN=mKt3CKUo3;JxKjp9@$Bz~MqwH4KDzanR5oLm ztt$BN2IaB)gT+qml5OeKC|{`w62clAFEeR9k^}Vn7$p%3LF~}Es)w^tUd>bYDp?N$ zr^5`!nXt+!kBKCE!O*`jF2U74)`S`}Hnu2IVQW#lAe_L1#_-BmGn$OQ24(&&mN^Y= zTX^da^;gfmn!0;>W~_Flda}kpq49^BV<(1B{KlZ;u7z)x9&zGad6QJ@0zM?)yf{+R zs2SwI1GHZaiY%wHbIQIf08MSM&Qd47TXf$Ko>!&|i~GhM(W%N#H=y8WY^3(piJ|6awa5z&^v?x1v z2LD+>U1Ze)k${iqpV~2FAa{)8lNPSZncjp9Xb;Kfb_H%vM@JlNT3015Zh*yhy0De_e`FPLT>7ME2V*hX+p*Xc9*P6Iq z;-s?Hn@>I(aL^R((0gS#EWo&suI@7lQBq>A*yo?-0MYl1q|I?R)xS|(DRnDd<~qJE zgErGX$=oOH5!FNc?-D=tV_u7*Z;ls7{gA_$f@tvD$UA0{SS(bkZFT>Kq>m)5NH$;u z^@GOK3H|e)o#AS9T*f0gdqdgFqD`nU!8Z5DaY}A=R9itG&V`PY)*Ymvi?9sh$^0SL z-K5({(s^=X2)KK?{yZRG%8z&#FmM%26(*vIGMpSPzTK1C$s{SR#&2Z7EOSwREK0^4ENX`Zio!XQ2M*T zP2q+hf@x0pg_MsAe9sw(^$Y8evxy*w{N9^Tq_3K$3DrfJ^rZ20P&le+fjYf*T|#iA zoMd|}=UW#n+%=E(7hFIU#}?4xa*0Ki^R}S>cvK4$?mLEPjs z=u_9^kCf*X9c`HAC*B(8jz#d!Kjn%43Z6ut83mUG4?3%xWbLbog(lX<# zZWmdwh_aE7fVhUY#{14G@>C*R@TEY-oEw4zh*wJcysgxxyBX=sf2&6=GwMW3Q9n-Y zL9KweW1q-P#dgRz%_`$mNgmVTwcvdc&D(TF=!P*3GaGk>o; zCcWlM3aP6W%i;dCdPSX6gwGPgO-#NVb;(080kIXjlraP>@(F~&fRt6LEDArUY8TWsmo*I2Vuh<#wa1ldZ-Tz7ayzu-!tu6X169U zWz=`RXNNj;KsOed`aybfi|l(nx_ZA>qHSdf2^4IpH z5Hh^WrWp&FuXGs0GAYr@WmmKT;I_?U_3ywXGobM$KS6P(#*W~v#Y7FP@7MC? zSxdB)iVKqXvA>m>XPkv5Nkm0_!w#X&K1$Anz$%H(`voXB0ROs6Z%1*4rqN;F_#gXO zY?i|(U>oEodbyC0rB0AP?0W%zacau zc4p@_J)Zn_zx@dZ2ybl%HsAWOfxS98HE{|u6cqfB{(g(U7riFK#+54QdHGvMHeO_E z@;6yM##b6CL@2B6H2i$vmuF(=R(TqUbQlOxDd>C<`49hBOAeR8<;@)BQ#WSQM|DnI zdZdZk)gp)Yv47>eC(z*qo8QmzMxP<4jxMDzTEHY5XT__9V&equGRBsTC?N zwemj|-uA?M9TQ-tC5l(Z^j=z6j!z78S;osNm^l zpn$<7qvohD0voh&3ry8qYrRow>_Xj$(RGs#dTF~j;pM}wPzWAsa~eem^r$Nqz#JNw zk5_@cJ{(XCTRdQ5IKcUw!a9%;QUPpGj=uKuS0Peut%&;g?P_ahEc;HJURJEWI8GgK z6jks$-Ca=o*GY#im~Gt6x~_cQO?rAXlPN*#mU(;cs)8g{vsg2a>yx{tGK#u=B;B|} z8WwY?$axOcFkgE5>1xNfPF986fQ<#itV)5e(JG)1*#-oLxtf>T$qsu{#=G8(F%a2o ztoL4wuo@BrX2sekL8P1yeXXPOR1QsootqSP^^&=FHPUR@pXi;gOXY`7L~QCeNW z4ELbe9wEcDw&pGOBXA2%$|fu&-ma!*NCrOLHKNhUL--NU6fd=Ze|?T9Y1bix_X z3Fi|2m>zrMcoBw;|&FB3hJn6>IRSW)-(E#r_ z_nl8tkXrK14p_UH&sO36*k9O9^+?4LW3j0AY`PLOodU8y+w^$T68B@5d^bEQCL?l| z-K^}kjapcVt4>CcR15Xti^+mznXS)mL$ z)AgN&q{v!cLOu)Ojf=4I^z|JG{Im4XuZeCjJ)<%1?%{x5!6o|`%(^4~P4s*YT68m z{gb^!oZEmmqcxKn;HZB^e~0hf3F)a#4G{Jw9Z@*z-ye~IKvL#VHA5Dxi@=To5PAq+ z*a*UjDXSC&k0|(+GW%Z&3PD&4Zp2QPP^-;A^h28kX`xDV>)%B2A>{ZuuWEX&20E{{ z<1vRSt1VEy&N@ExgGB{4uO#Gc0nvJH`K|xer8;sGefvdjV}EnQPgHyCoG0)j;(P&6vA^z*DnFPhEB^R9=}4{| zP3cU3bh~=hczx2UJW62i%tL4TR1^M_)^B&F=cm-nLhSX-3*h+M-0V;J&af#r^}3TM z>nUZ)o9K7$O3)dNn)=RQh!#*`;V_vcC&jWB>q)DMzg8;GnqBJ6dS=nb%`yJ^tJ8dF ziKRk=;j(J#=M5HnFNd1-a}8&sL%&~U{(~E`8HRu-pWjLXAV**f?K$nMv(dE8+3EX) zDmpGackQi3X_1YfeqD776~oN@Q)aqpBNoMNc9y^CNAB^3pn02`Ba9C$b$vI&Dm;PE z^q=bU4O^{`e!76fSGelEE>(~I!mFwxS$NM6V~lu|8qVPv|Lp^PYZ{(<6CK9J3|F6rwWOwBcpNzO7nz(58WRgL(ssY8$D;o$>|cOwIAYTq#t zuD|ax^H=a?{Y%ya6LTm<)a*!}Gb|S>7Scbfv-N)4x*&O^vC^q=MJR(V?47`=R*bdjcLsh-~-FgrLc8$iX|hH zhc9KPT04yQFyJOL_6u{o}pp=PKlM2{L3O78b?p{esd!qyB`G+ShbYh$kui zERMMi!a|ZOdsHV%HfSY*K;WLE%G@@V&PIl z=4{K%60DYR@~HuN0GQ84gt^>#KPievOw$#Eh|D~#4jj^UK3tOBASbH>G6Fzjqtc}R zMjc5Lq2z7YJfovCSx2z=RBs162iaK?H9(Kv<><^pEbFPW+&{)*5VW?Ch5M(|m&Gsg zr|0pn3g)qu9r8+~66+;Sf3rzZpopd0Z3MQLfPfrj@CNjBW}zH88Z!0+s!9uloDInn>dkZ#!%XN0}=!JWIXW$jOX!HyoH0D`?OE zhW2>gwikuSOef}W{yp}94ck%yw=opkBiVbtaK2a1^MndC|L}{9v2^HDPSMx#!pDHS zUztwmoo^LfkN4JK6*?VgQgq>p9LQGjhUjb%KU(sCfs6y)7To_zl)7XH_|JR>1nC3P&0c6+O z)$V#MUxSTS)7%(QZ^yJqy!1Hc&GN6yKN`A4@5~eEMDxC?=-*os_s`cKefkTYg)B&M z)qPCGHLqV=En$7nU1vy?Vq5V&3la-de3|8_Xg<}~Q^f@9qsGAe;s#VJKQ$}|A0y!` zp%JV9&KPd!vwqkN6WtJ98Il>|Yl51h^|I9@)s$m;@?rl<#?A*fK~R3H6mw?jzSWmh z8;4cLS%Whnn}s%-)O9nuSF{PD(5W8m!EZ-C|}m!hYVAa@U3F+u2s97gRN} zYlmn&yo+bhkH(MoPm~Qq>r2r}%HTa0zu){_jP`Qc=4!QQ&|4w#=SgvSJJ)yY_}jzb z?Y9>Jiopn}wITaaF>U+Rn^8e zP?2kmP)_p*BR2zc@x$e@bg?AzM{=^ypQ`>iJ>uE@@<2}T6`erp+P95l)&RX=5<a(83F(Rrv-;yH{z2q@5ABa!px@Syv?L*HobbU#1*Q8JSY^oe*FztQo{Y)l8qE zcn4jyyQy|x#_zrq)a$!%th-W-Bni+4rlFJokM8Y+W!8yw&5qQzfg;OF_8W18)_yA7 ztEM9aVoECabZI}W9JB3h>C=)QXqb9i4tusKMOL>XRRN`0XADaeG50i(n^QHCr5iz( zZG*IxiIG*`VJAP_)U6)T6$=DQ0BP~Gkn%XbJCm!EVNjsrv)IUJ$5e0PKO+_qO?B;i zbc?#)?caM{Xgzl75v+JAC~-#0GD_<4;l*aeAFErzc`}tBnmNYqz+N%Nyuuma{C@Y{ zG|uYLE3*$D>m8%Uys9L+9^_i%KdGV0f%BQZSaxTDcw@E%PHt0mDWtgAWQK85L5&Yp zr2Aftp%lw`M|nmOaPNspxZB42e)Q=QDPxDCm3qEdFUZQ%s6u2>Kw>I86M1H!S$*Jo z*yYa*C-pKo8JPLa;;b5=I1PxaVU?Qpr&3?cLW#h=fj7~dwGu)xL{#dO#_{nIKFXn#Tpt@WxEt-fDjs~9_kErgE32~GY(p6&ZeA+HU+5kmoJ3j z#2?QOm{Ki33(35^y^RWVzJFJ6+&EyjMG4+n3-1zRsZ2y>=OQ%iF?aj2eDjQ4YwVkp zmEC?g{qoybjnW*bpGpB`-t4fWL8x&7?<7nTL9U-~Ehc1`O&O(HsL;wNVdy$VR+-VfY@lhcXmEevky8=3#M5{i0&C+t99yM8o zU>XCLQcJde!QL!(xTLwx-IJb`NfFh%!M6!L5A1y{;gz|ZKA86?1cIw*p<2-)dGbMl z8+(gygGLJ!<$Og@zR-XMq-A3M0-7yMLee-VBqV*PmQ|Zj#R6!oOXv5}eD(|Ho6=7D z;Q%P{(=Z!td(Wo<5dYmy4?zsE4%wdi(bgeTs>(BE7wRW9QXm0<862KMm=Gsms7#({E0bqK?zhST-az5Bo|&Yc4KfDa8}m|xlXY{K zo{v zvP1JMkm%REz~Yh>oCR9_(^3C(AN)a~gJWWec-a0O0ycsRPj&^CD5|@|0An}}*GH_6 zsQA&QW*P7EDe>txAaU~8S+I1K&xX81fUUG)e@SA!^mH*1Hdjt;B-vn?s1n$PbtL6b zy~$BI5|<*wE^YGREwIN`%o&4_FO>X`W<=AfvEjU*x2@9G1epBOHrB+{lq7nv z#iK10viR1;#+)T~Lg;6(uAW5u4n|QyBe13CV20e-EWPYER%Ty^O6`6J zuVrR7M3BCJhpqbgwwI0R(jR+~HfiuQpbLe`#5l~gBWIf!ZUPE;f0)aYfhu%ocRTrE zsab4MNTDxoZ#1puh=R1hfnkgb5{PxSr zaF4*gz|Ki56&0(iQ^SVv&8-QGRDo-aap33B6mlLIpF?zL@p=HB(QBtCJc8HY(5HVt zA&!OyB2^xSzkft1$EQ~{7UG>(JQ++dGpHFKYiiHW>Hss~)fl<524;h>^9A{QMm`$N zkPcmIL*1WVghm3C9h}2*Cmqig-yRBwk6t5XjTqKBq%!@j1Qm?``!;#`!`kk>x!7M? zd076*_UfNM+@Zi5g|?}bbIyVD4=ZydJBx{4{UYjJ<$X3R6Ws~mvJHrXv}Rk`hCqlD zZNDVt%YrZi8|BZV%r{CTj}jv%tKD(*P)sMf@=-n3_g}hsZAprdHLW|{Dd?=@Xt?*h zraBuFIH87|BzaOLQdxi9Fo1DHp)gtCwJ_^BI_IkI#onz3FUMB)KsEW)12@mesN8~E z3mq*BurAVt6&0ea*LjvpNW_3_b>n@h`eec))ijWKgke zvMiYBUJ@#0C&Y*d$V6}f7=(`j)tiZ>p?dR4G^G1@jHXHPcms@Ix*f!mzsB@xk&2=@8*e;gTvBo=~vnKO0@7DY6!o$+3z;0SOwk(kz1Y340 zSbM-!`o9}J#ee+&)1aA<*3G;FW@vX*pTl)@r^mhKT3PR^82uy)m%AfNXy~jZMP*Ow`U1d6OzjT$_gC3u69@oL}ujb9yT7Z zgk^GO4XfOM7*l`64J&y#{>(&7*3J1G7S@Oh!{>TIV}8J5<8EE!Q1uSf8xdXFi8(S= z8wv43dn*st9v8G=3>}$`$$S2u22&tLhPtkQw-k>#SyXBj2E ze}5iqUpw-3xDckjq0)YDGm?0N=2T<-7XBb7W2H75J;tygoWzW7`wOIqZa*gL0~jcL z-Wo}niCVq!Q)P~I153CFYJj!k0;N@WvtB~Tt6|SnBrL%Z(;h1^Iilk#ydG0l9gndi zK`r~GjbH85}-arP+u4-BAvI&+3XfS*;WmME#*ZF+E?C>tyrgZ$a@$2 z0P*fLTGv`8K5{aQ1dUwyJa$Bjw;aaPSZ}k(hp!dhDiG`}v7=84p16$Vf2bQ`2_4)1 z#s3BHqf`!^r_Mg-QixpkY^PX1)9K5n5)qbQL9=53CXcG_L^R>w?-__f;TJ-?iHpeC z9z$o!aUa>+vgt}IudT6}umWvMerBJ1BCV(|lFM_k4r@8dWSyqy5sE~|IgM7zOhZa# zTXU$*0G?qK>)P$LT>$+y9(nRz&az`3Yau4>Sl| zm98c5o62(e3hy-dUFq9XO!(M+A#6-v1AJ&^SKWH>@47b4D-UZV!pS!Tb|I3x(XyX8 zmcfqgJ;l}LkcmHBlVLxwG zzLR~pz z6w(0SV@b&i?d}1&#&f1{2H?q$CqI~!Ww&+FbL){-wRI{Jh4TURVQ}`%VMapn)l<+3+mF zUG_!76Ojul(!S9WRbu~Gj%{22DraLXTZb7~epb@qT9r8@+Z@_GaT)R8Vp+lvc+~@* zrgSqZ1i68h48%m35m=%Ln0rItkypq4Bxbp@ZA%;30}O#)JUl3>H z<=ns+B_;wSBK~q=+Mh8uW&W{Yuda%Qa&CT}hl_?vY)_+T^Nox5?=z)?Xx`dp`tsrTe!diXw*Ba z0(TsK5IZ=;q|Lt_L>czSNL$Z0@W%yc<+*fJDvWNkhdKS6md;i225x-cNYG-Xs%a-w zzC?p^E0Ddju2OCYPfFKpF@aoaL&=-o_Zv6*(&Eh~j5 zxGFBa38u}(%u|V%6SBuQErH8?&^6L=xmnRUj8c79idZo!4o#AKF9_`+HO22u`mJXH zJbZj(k@YPZ<|5r+e4Xg=F#w`lDZ80drkHcw(thi^|1gon?>)tEs%nN0^UnVLy4(+t zB6oH0S5PAhVqwnP3Io0x(i<5r`o?eCB98wue9uAbCI_m7=Hx8gVhYT3%D>mJQ|=m7 z%p>j0krz|8)Wz{d4zb^Ml)bT>2*`5%67yYj&xPcxuX(K8v2TYbdm{O7yUwFltg3<7 zE6PR_S)a6%ZL`)60gaL>vSNtL+_OaH4>J|JA3c7+uoWQ(F6yl~s84BfK=%9VNzS_C zm$cEaZPf2usaZxs0gwNTO5!qQt|F61%lRinwC_nTTLOA=e8t>E1 z*?M@uqW^E2YV`BNk=*@TpXQ^hz_vr_-4?eOlBh4zV%x2?fOdgqx5c<_mWxs8TTsiw zTt1tm1rpuMwK40|oyB{7SB|%f^fW|?8}<4?kX49pmJC9oCzQ?~vlkZJ={-zUZ|x}b z^8_t5%kZ^$ffnFjm0oMt<%`_^sbK#Vv?GX;73{x)X31^=O8xfA?z~LH(KD1k)Sj?W z2X-W9#Tn0n4~vQlbNn=QO;Yk2Eig`7s`s5@+oP=${?tx>JrdS??j}I`o=f5W zrR>t)-z5D21IIsi3AD-#EN;*WhS?ODvQqAcrPPae(aVO|H~_IAs7>^zQ1@B;+I_7UQrV0kdW0YA$+q zQ4MYzytyh*Cnx=#8GDn$O}+memUiPyh;h-yU|&h~9rpo!QHwU)wYocr3Aqa|va~33 zRndAHT3{vc`HoWTlfO8Guu+BR0as2FO`FEz6W9RNL90{DO$DLibn;^VB-t0XEqeM5&XKU|eCQP4?{Zb5LbdVk3)heFUl~RvOFuUYM%JtK z;aoelTTo7}`34?b0<4$LeNShK6AXMu`b$$VDQaGR?`%7LuJW*3s;obaiy>TKpzw04 z+iA)-(PM@x9R4PVGGPa;0a}Dw#7uG3j)ZiI4V-tz>bD2a|@ zmaRyU=hWP^F4`LohlCzPv^E-n{e>>fJM)(CzAv$AP4Z7_;p*Bf z*Xz`O0;H9zMUrwy$R@SrTu0ro5CjJqg2m>u(Fe==;yOG8>2{A=&Z({M=uT$4MH z@xiZ|U>jo+odvXVkCWwv4X=87YFwLiinh=u14)SIoS?j!KIE!4ZZyHXrV<9MfeBLl z)&jKXLueyL7M*)EDOg@n+Be4+d2-n!%2n46+<8}w1J?iS&u>vO&^ws^6Ak(Z(l01b2eD>e*by9DAv}pMcGKRtVtSJ&4E@y*~=O^f>wL~B}{uj ziGw}%a^bE1$MVfsugH)^L}>B@qYrlC*;rU>xTl7{Aad*d!k_guLIuX?D4B|k3!ytyQWO`-}ND--+e<3|V z_`F+~>Iu!ja|vB`6~$tRlCh zu#=A5UEX8%-PI-6fK2?@C{dHwwuQWCB9oI$9rXr+J%D=!(Q1P5LB?LNXS)3~#wAhW zY;GV$^2z<*uHGlmNvm3M2z?E)YS4^IM{G}MCHYaDA(m$Htf2C}z$MiB+coVG6%`#b z9H0cBlYv5{I3a;o$pa|P(!l{S8PQWC&nQc*j5~z8JIWG(6DB@jyrC75A>4Sz zof~~=R9nItZKEKA%nK8OR@307fA;f8>^Kx}%9}UtMLbhv z7kJUDothYg3|S-LlzM~|tHa|bUMvi7oAvcN<3bV$PG_x`i{#jr^9tsUWlWEbn<=Pn z;0(f_Z*qb?6Dw&7DCwAgOviEL{)1H&toPie`Ln4uFgtFjkC%zy-vbER-Kbw=vA5ht zDj!&YRR4S%U@Y}Bt;L)h8LPnUs4hmetXkI{T}?jd1_Ih=sc9rKHtd+oM}Y42G@9}@ zO^bWq2WJ(5QuPUgAKqUga`@-CEMuEQ2tY34=y3Eg`XvjZw!A`38)#P^IYsmEw=ogBA6%|~5q01ETC-=L7*A3z6DA~qR<}ZMa zNdzGGBq!tw?dE&`t8TnEy3RPWPt*2w*r_5^1(rcLK8?*f2S7#P$+%IAy1QR0%d{KmN{;dOyA=WvKJMU^C|J{;(g$`)h^ zz#vKbr!AAv4I15{H_`DEHTiIIRat5H)4U<1dUVj^-D|K+FliUZ1`K$;K^+P?HJ4WvAIYjtL95M9vVbSP$1a(t( zT1J9rtT3xhlZ`|QBCUTUrxQ%%D9Xg<9AqruV)v7atioCq zE~3X{2h-t4UdAz>wD9CDi&{RU!Vs-L=eT)WijyJl{MXk$Dj>Z5{PaMuM?56qU+m!p zqkPx^|NNACIHgSaLMbCUAhI8?o2DfOL0U$)I^^LOe4@~?wUkynk_#e`CNP0=%`BH4 z!z-X-=qA*$E3P|MhNLhxM7je#5)J>$iCZC$R1>oEP__bXHAJW=P!7L@kViEKbjm6GVm|M$u|+$w7;5b_RJF z9OjEf=b$Bfr6Xv&J*b4Ww$4Nd;^s}W3gz+o>da7>2#}p+wL)5ennHal?`lT*WUp!+ zrp>z|XoCgz=xS+$hdZ-SQTFhaq`G>nui_{g*i&PWJ9rA22=ulX6hAH1DzC=6)fnbJ zqne|$lMx`16XH)CCr63Tiy_Ejo3;^Q_}NKn(2FB6eIWPv-9*_X^gM6?(;lp>_dIo9 z&|yI3rKc6Yal=Pcak7#|f%BHguNx4;XeA1piGJkDq*QrQMvV)0Q{bkj_93J=_~RqO z%3)CmbTN8OoHB3nvnPl!)Rz7lFa1?uG5U#{O_AjxA9$`Duo@JFuwa3YIH0ol8bPKe zRtr$tV6`7&7t3sN8N-g>Nz@^=*vB>Oucq~+gX%*476Q{-q_pX4{pL;keGD(7&rykT zHNJIpjKUCKP0G=}83oC5*E+nq<7~nkee8A5Y3%XEY2cVNQ<#C&j~=xTRT|mN7f9O` z#hCH=CYgqr03B<_mxw}aC z!zAh!wqOP6pujXMX@ny7O9rn?AKEwXjrvV9K2vB&QyBzt*0x(W__8LBF~)M->{1k}hltAhw~?EJLHuBg@_=I-Kg}RzYvaVvR~H z83Y*ZY15#o4AA$u1OiZxC|lD$J9p*dbB~O|5J)xEaA2e6wWA!$WuuDPS?MGdUvn!= zXfkRzyB_&FH-?tCMRVIDue{wheJe=~LTNmO=wm+2#Ufl=`l`aL#V zp;9n$cDfZ{xW!ppejMdI(e@_ft?V?pLD)pv`HI59zd4O%>hXsmSHJnDmpN$hSy~Fx zKmK<(c4*_WZBVcHh9aW0BHW&y+zDx{`=iw#!j0u8lqFi0Kd~r~+9Or-k(ox8)YAC(;ddW4q2Z7Krgsp#=q&TYUD&jF6$8%Y@SB6*;N{@FP^9c%F z=m$1woYn@uM)MY&2pC}4cfGvIwIDgf&5`nc&z4mgO!jR)P@xs}w0p@DI9E5wESmLw zRwpj>)|Qh|Rjgr7WqxO}L&=}``Tg&I$keW#$EYx<+Uu(hIKLjD8v$~ACC4z?+{fKm zaT_q*#$j(#x#-wY67~8OBuQim3Gzm-D6k?@g(|Gj=TINpaZUQxv&Ygnq|yclYRfFB zx3e1slq=98FgpqQo?ZpqwF-NbXR74(wMu1SBqXMid;sZ;jrI>gi4E*rWtYA?Z`YbkaVi%OT|N<~MQb3GoF9&hu9b(@06hfxm$E+8tWe|Y?|;AaqG_sefp zbO>-8?mG~?6;`*l zwkha%+Mi9*NK^yAF}D%zTEb76HBfA5z!BtvG(HK)=MdW;bP7ojw2}gCPP~{K2zRz$ zmTiAa6FXgKA81B6Xjs8S7L_pYFv8GjZt}XyJwMn zpBr}!z307Kjux+3vYa&pg377>oGmTm9n3rfoY1L0?&42B;+&_Kh}5b3C@F49g+4cDJ0X`=O~x*kRb7wdC;k zHoGq%AAgIH(nR`IwJLN(B|;bD2aXajy-GwUd&DpKnu8RN?nv7wS~xDzbivwMhn6sG z&EZkOZoy%Wegr4Oer+ve8$$g7q1_#E-BBL3uY&p4o?{aCKSAFEjNiiwTUM>_b2@~p zO=f-#ITU|#UZh?oqyKy$t{Xk!UkfX2^g`p&(U==VhDg2XG1zIjzzhd|;W{^LamLWH z+60oD0!Y~mn>4;c44)zSxEgp7!3YQ`CDf(ClJ~qXFT~?)+!X>tJ)=|+>|oS=J=9=G z((8|5djH?#qZ!!N0_h}9M<+AAo{G4|^{O7oX8-FA*DqE!a^kP#vG69$(a#y(Lht~W zqq)oTpY}h_%y<3FEuwl5#ym~+(7@hoGqq7r>=jeZ)+y6T%D((hF?4` zKc|jcK?ksh5))ia`P<`ViVDDfpcVoKSu7&c)Pn7945(?134GJoX=85NvY`_$F3AIys@Z^v%D^AUzsiIyo5$d?KF;6#lu7pA#7N ziUX$zx62IBUw&Ov(x{yLVJ+UU1aHZUV>aD>h{Vm!OvlXiO;%%**rIN{`ZfP}23qOV zPH*)Lp&T(?$gNS=j#>mW!gt!f7Y2Bq`kBMY5XcR5Xx-Yy9Tpma$bCNJ!$@-m6YQ)D zJqF%h-qlA~wsS)LA(2witKpyg|Cle}WWwD)-xy^cPAWFVAwKAn z2r#d_XfR9v?qViFPr8GV%BJW`sBcj}E!fZK@O)M(z_R=l@M(zn%D&WZ{>YwZ45V9w zlgwvb_v7k4LiqwhE#_d^VozDT?-yZI#S#VtZAugQw^_hx27_B^HyMi^VmgeSR`IGY zY!(Pd_Ta+7z_(SO?0cLcq!{6gQ4C4aQm+Tw8-(|cX)paaOpf-qRGO$g!0Hv#nJ!W1);HgY0;lywwv|o6Fd4csluh{xtj_y%wV%;Vvk)t03X2OV zHbDJN<)c;kZZ4W9)n@7kjfiDk96dQ@a-1vR66?qwFd~(mY}u2eV&*MAv6-Y3zfW9z zlIjRj61NTe49ApsJk34(}3kswHi&>?grR6*%YkfNZ3 zE+T^T66w7Q2^~Q?getwaQ2gWj9sMVBu-ESFv-dU6%ssn1>)+`!ZE1+!eu#D`B@7ij zTh?9@?uWDU6(WDk%#fNw1H`PML~Be)^i!1*Ijb-qV<7R$sW6WirGMtBd$~Jkhq{Fx z>a;TNb_OcuWpv*4-WX+=!F>82-@oKxR4gE6k@$F9`hz@~oqgXbmFR9)po!{$_U zff}1(k2x&SFQ-v_)M{Oas#IExkqv)JypN1KK5t`m%_?lMlz60%Q=rro_3H6yj_C=@ z0EezD(G(H_M&w{jbiyZhHB>orEE*B>ZCcUN*qY!?;lnQh!6P1y_h$<=f4N~ z(D3X?<9(heTIOdQbve!YYt;2Re)ytsxgA}O6j6Oxe9!6TwU0~)1UlH~yKG93c?h#* zPDjwdlB8TmG6H7*WSuLU+jN>@t`!ZD3wvX_bOn5ymPS$crm60n#!|qy zEf{bCVRI$zOZ0!kk#TpCBc~c}y*usbOPY%@ojV-k&i;)w+5y`ovUh)@H7t@{H!}j^pOBm_Jbo!nPsU>xU%#EDfp&&zpYg4esy`q3z;qYR zT(m1Rp-1-YZZHh0iOMypE7vAP3|cvIkHaGAVOMyqN#pL+ieBf7@AUe~x*O;IbMgq;vbM5hT$LqTqNHIqzw>n`AQi;GJy{Poco&g+4; zkpro4+Wr&5__SweChl$tL+Q@wSY|UF*c9}P;@asciem5SzbUf9KpY-uDLsSV-l+HL zBYW$UR4>EMHhXhgx#r9z+!&O-&c&Y4UE_96sXARhw^a7qpV$D*bQ|XOiP9Pacbt3~ry8mQaZfuV5skN`R;dc6T95k7QSU=GhhP1?MfK`1-FpDK{5`?m-UL4W=lc91F8PF(RL?-kd?r zwk|}*W7%)?gAg7(+K)Z6uk^jVTt$Tt`hJ|xlRK!=At`vaG8DbLSj2-wFZfli!2&hJ z8dP!=EF&|yT2$1iH}j^@SX%3Ud6<5z~9C)E|`$WF%nPeNtw`lI$O5enV2 zqJn3=>|9)4dU^@O^EcOrM=KeFK|Oj^^d;YG*I!oa;8xtG+foJ6u_!x7bH1N5Z+5X8 zQvT+sg6hEbALnu%{+1psqP`gaf_n;xg`qF83kJ>+h;U_^0OOro`nX;A9NbEYrjLw^J@sXfm7l) zp7ZPNg{EfQdEO;WHa9$zwe27^xBlGa{;X3$QPY{s#T59x-b`HAj=b@aMnJQ(h%k$@ zqvP`hU73*so%)^B7ED1s*ZLKeoae-Er1snCKl$(a|28>_8+zyVPTw%~vKv0wcvwwX zv5@6620ZA19!&WswN%LDhK+4r!x69|rI)tuhWcl`RbLxCf{-Vl)6*xt?1W_EUl`x< zFC{xCKLo4RSBD69X)CN7J~F`(maOl0F`tCFgjvsjKKe0$E>8i9a-!F!a9{N1T7%-N z;JAq~vxeH4fI9*$MxxRHspZXk3WQ0xpgqjA@T;WO3}#kR?1E zkp80_Qb*hvR?~tN?V$35GGEH$dEFh?e!41w z!a;fQ4OMj6xmJML7*j5Tss4+Egbq4a)BHKse03A9(lR=Q%@`~jc#7&Qo3g>_J?5qD z)YqbAhXoQBU*lg(`g;~zB=0vm1q`XU0HiCmOo>!a{C#ylRSxNO`798YF1AAU`l>h# zlsIQ%$}fGkwle3{J(_RjyHp@-)fwwGFbJ&Sic84)lQf#NcTD!8+#PELU<;#058Qg% z331W5j=a$zBktS?j5xDy-;?fo}d5=AXuu}hINE<5v5b9v-R>#{^s>1F8 z%w`Z$+mpF?D+`6W#Y%tK+ok>5hEWDuH%T@jIlB?gMF zmVprc>@H|G`w8YUx*(Q`baGCICKy`IG^r0eDcY-q@4&=}-ipm-JE$XW>W8N=h~b$TBw*~Zv5f3MbE+W3U_x?1|}-8?0z zuXM2<0I|$3`;7pzJ<#6xw*EHL@KS4LXDkR+^1|4*G@zz}j-qw_XJ7E>`+B02QZr$p z_*%81j8^7TiG9)ot+hrSSvR*s^D)EqH_2?7(>ZM>;^c42BDzl9G??|3w6h3&h^jB8 zy$z)Izl`HEUp=`>Bhf_Byy}L2mx7VA9%M3Tw#6_B#IEV~^9Za$u(J-IUh zEf=V#ySipk^3o2CzqK*(==7eU%aLlD4Q{=3^stexyh!o!i<;7DEghBs|&$goi%M4tLu4EeDc(hKvnLuZXKU z&V!N`@+^<_!7855A(<*n$Mp4l1$33*5^%fzpBt_)RKHi(Q#6xx3clfeIm^beFz_C> zZubtOwcD>H5&_-)k^3E(E5(((CD)%Jjg?<#Zt?2xBa9%ww*ET)eAW0$K*Vw1R)0c_ z`=MgDD5Sf#sr?;DpbPr>O*JYCY|S}aAO2u@ub=1jd$mpLFuVCE^bH#_W0()84bTWi z7ffU-)C|_L-oJfAVwp9+B_PADLFv%>qt2hS3jd=YXj9G6kK@7;YFj(1LUkUH+3$9K)l!uV>@`QYO4 z`v8{yXq!ondWuCdpL-EeC)-cT!WEh?XwzxEH~&hzFOlty zECQAjANh%tiUw68xcJzn52el}Ex94}!HN4~Z|V_ZapRG+=wdj$wcK^FsB=K=`=Lj* zq>8hGVons1zdi1(Mb0WZawyJXki&`w_KXYm>^CU zGeyN(bO9*9flL*&`0If3j`oeS;^zk? zI9irWmb9YZRilIKKHL{QPZq7n$?Wf%dI2g^ zYLOTj@pK`x8J-S)ZCX_F3>_DqV?kU!;rPCL*`p`J;?@QY}%KJ6|zUc!8VI>Pv# ze5B^1TIVzdzm@}e%6vlBUVo_9L{+Gz@v@PMOKliotS5lbE|E@zeE65+r%v|k5r z=1T_CECi4H5dV7Dnl#8)WX*bNVx<8|JC!lOU(7n9m_Vw0_dZHx(>DLxi#hGX4G(ef zB`kX=*)z^7da(l}m&)f}MZ0lsInQJ+~W02$2(?kbd20ESD_3E`Z ztQ8&-*@lw<=nEi|c8ro;E&V3-$Xyzt{H~`l>eCbP4@&}U*3qD5ldDIA3Vc-X(A^(o zVJSj>3GT*1X5x~o$c%Kr{@Ew=k=wbVk#XK#)4DjIbgok!E#?2B#W9e0RYb%(e2BLM z=S@q{pk#9a3-v&)rt!0J6v>cWw!SMrNnNJ)f1bboX8*y^X*#5(BDi!Umv*KFgmFklq)-<}u405)S|3!+FQw~62#I9e}2OstllBEP>M@^`= zATN$OX)un0=$7~fADsP{7H(!9Z|B@DkceA6)v1uJ=M_xt>dn$kcu9Oz$6m*XRh1vy zU~zv2eoM8%CUl;E7r>Sf&ugeI!z|_*zf=Nv^b4_egW~e9^=mknJo(;u`s(?F&1A*- z!ror`oEFfXF7X9W`U_McecD)+p@VNwy5Ka@s- z)MDD6y~z#nkVcoiTnOdd=M*p-KnK_+{)br z!NQNszR6|Tz^sSX>t?l9zeX4ut>BpfAf;seA+3~G-zJiKL`?B=F0`+4%e9gv?D&W; ziHWD7oM~WId*d_%eZDDPLbWF{DF?%>*(oS163Rhl0gym-kEYr48hpZ0A5HXL4Bsr7l^d4?{IoATlUyZ_$HNaZN)7gZC} z0GZv2)CRVR&GQF;Ug8<6B`vR$YOu|EC__nDDm0Dl$vJ%M`y*%d$l`AoRxO}(HG?oN zk~ug>(>-1mpa_}sJfu(L+r*d%$|`Q=-w%{gLQMapUbU%x%E&XBeVigT!O~Bg*lJ{K zbbL4)2P!?vqw8PjxoOq*Kf56s&v+0vk4`RvD1hO%@w}J))#SPnyOUR{A&>lbk~)ZX zWbbm$uiWrWykN%zhNLbg;)3nsZ~abL{1MA&MmoDU*2b@lZ%@;W7$o58tEY?vYs`Le z Date: Fri, 18 Aug 2017 18:40:01 +0700 Subject: [PATCH 055/145] IGNITE-6107 first(list) should behave the same way as first(iterable) in case of null argument. (cherry picked from commit d779f06) --- .../java/org/apache/ignite/internal/util/lang/GridFunc.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java b/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java index ed2351daae701..fbb47c42bf030 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java @@ -1549,7 +1549,7 @@ public static T first(@Nullable Iterable c) { * @return List' first element or {@code null} in case if list is empty. */ public static T first(List list) { - if (list.isEmpty()) + if (list == null || list.isEmpty()) return null; return list.get(0); From 13c1d367161aef9855279c5ab9e23a9ac9754ffe Mon Sep 17 00:00:00 2001 From: EdShangGG Date: Fri, 18 Aug 2017 18:52:59 +0300 Subject: [PATCH 056/145] IGNITE-5943 Communication. Server node may reject client connection during massive clients join. This closes #2423 --- .../dht/atomic/GridDhtAtomicCache.java | 32 ++-- .../tcp/TcpCommunicationSpi.java | 60 ++++++- .../ignite/spi/discovery/tcp/ServerImpl.java | 16 ++ .../spi/discovery/tcp/TcpDiscoverySpi.java | 10 ++ .../tcp/IgniteClientConnectTest.java | 163 ++++++++++++++++++ .../IgniteSpiDiscoverySelfTestSuite.java | 6 + 6 files changed, 268 insertions(+), 19 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteClientConnectTest.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 e67dabf79d668..f1656938a1317 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 @@ -609,21 +609,23 @@ private IgniteInternalFuture> getAllAsyncInternal( final boolean skipStore = opCtx != null && opCtx.skipStore(); - if (asyncOp) {return asyncOp(new CO>>() { - @Override public IgniteInternalFuture> apply() { - return getAllAsync0(ctx.cacheKeysView(keys), - forcePrimary, - subjId0, - taskName, - deserializeBinary, - recovery, - expiryPlc, - skipVals, - skipStore, - canRemap, - needVer); - } - });} + if (asyncOp) { + return asyncOp(new CO>>() { + @Override public IgniteInternalFuture> apply() { + return getAllAsync0(ctx.cacheKeysView(keys), + forcePrimary, + subjId0, + taskName, + deserializeBinary, + recovery, + expiryPlc, + skipVals, + skipStore, + canRemap, + needVer); + } + }); + } else { return getAllAsync0(ctx.cacheKeysView(keys), forcePrimary, diff --git a/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java b/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java index 35d3032cdb250..1dd48d118c06e 100755 --- a/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java @@ -109,6 +109,7 @@ import org.apache.ignite.lang.IgniteFuture; import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgnitePredicate; +import org.apache.ignite.lang.IgniteProductVersion; import org.apache.ignite.lang.IgniteRunnable; import org.apache.ignite.lang.IgniteUuid; import org.apache.ignite.plugin.extensions.communication.Message; @@ -132,6 +133,8 @@ import org.apache.ignite.spi.IgniteSpiTimeoutObject; import org.apache.ignite.spi.communication.CommunicationListener; import org.apache.ignite.spi.communication.CommunicationSpi; +import org.apache.ignite.spi.discovery.DiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; import org.apache.ignite.thread.IgniteThread; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentLinkedDeque8; @@ -141,6 +144,7 @@ import static org.apache.ignite.events.EventType.EVT_NODE_LEFT; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.SSL_META; import static org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi.RecoveryLastReceivedMessage.ALREADY_CONNECTED; +import static org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi.RecoveryLastReceivedMessage.NEED_WAIT; import static org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi.RecoveryLastReceivedMessage.NODE_STOPPING; /** @@ -296,6 +300,12 @@ public class TcpCommunicationSpi extends IgniteSpiAdapter implements Communicati */ public static final int DFLT_SELECTORS_CNT = Math.max(4, Runtime.getRuntime().availableProcessors() / 2); + /** + * Version when client is ready to wait to connect to server (could be needed when client tries to open connection + * before it starts being visible for server) + */ + private static final IgniteProductVersion VERSION_SINCE_CLIENT_COULD_WAIT_TO_CONNECT = IgniteProductVersion.fromString("2.1.4"); + /** Connection index meta for session. */ private static final int CONN_IDX_META = GridNioSessionMetaKey.nextUniqueKey(); @@ -439,7 +449,7 @@ public class TcpCommunicationSpi extends IgniteSpiAdapter implements Communicati * @param ses Session. * @param msg Message. */ - private void onFirstMessage(GridNioSession ses, Message msg) { + private void onFirstMessage(final GridNioSession ses, Message msg) { UUID sndId; ConnectionKey connKey; @@ -463,10 +473,35 @@ private void onFirstMessage(GridNioSession ses, Message msg) { final ClusterNode rmtNode = getSpiContext().node(sndId); if (rmtNode == null) { - U.warn(log, "Close incoming connection, unknown node [nodeId=" + sndId + - ", ses=" + ses + ']'); + DiscoverySpi discoverySpi = ignite().configuration().getDiscoverySpi(); + + assert discoverySpi instanceof TcpDiscoverySpi; + + TcpDiscoverySpi tcpDiscoverySpi = (TcpDiscoverySpi) discoverySpi; + + ClusterNode node0 = tcpDiscoverySpi.getNode0(sndId); + + boolean unknownNode = true; - ses.close(); + if (node0 != null) { + assert node0.isClient() : node0; + + if (node0.version().compareTo(VERSION_SINCE_CLIENT_COULD_WAIT_TO_CONNECT) >= 0) + unknownNode = false; + } + + if (unknownNode) { + U.warn(log, "Close incoming connection, unknown node [nodeId=" + sndId + ", ses=" + ses + ']'); + + ses.close(); + } + else { + ses.send(new RecoveryLastReceivedMessage(NEED_WAIT)).listen(new CI1>() { + @Override public void apply(IgniteInternalFuture fut) { + ses.close(); + } + }); + } return; } @@ -2996,6 +3031,8 @@ protected GridCommunicationClient createTcpClient(ClusterNode node, int connIdx) IgniteSpiOperationTimeoutHelper timeoutHelper = new IgniteSpiOperationTimeoutHelper(this, !node.isClient()); + int lastWaitingTimeout = 1; + while (!conn) { // Reconnection on handshake timeout. try { SocketChannel ch = SocketChannel.open(); @@ -3066,6 +3103,18 @@ else if (rcvCnt == NODE_STOPPING) { throw new ClusterTopologyCheckedException("Remote node started stop procedure: " + node.id()); } + else if (rcvCnt == NEED_WAIT) { + recoveryDesc.release(); + + U.closeQuiet(ch); + + if (lastWaitingTimeout < 60000) + lastWaitingTimeout *= 2; + + U.sleep(lastWaitingTimeout); + + continue; + } } finally { if (recoveryDesc != null && rcvCnt == null) @@ -4509,6 +4558,9 @@ public static class RecoveryLastReceivedMessage implements Message { /** */ static final long NODE_STOPPING = -2; + /** Need wait. */ + static final long NEED_WAIT = -3; + /** Message body size in bytes. */ private static final int MESSAGE_SIZE = 8; diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java index f673f3fd220a1..e0be62f5d7e3d 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java @@ -1803,6 +1803,22 @@ private static void removeMetrics(TcpDiscoveryMetricsUpdateMessage msg, UUID nod return S.toString(ServerImpl.class, this); } + /** + * Trying get node in any state (visible or not) + * @param nodeId Node id. + */ + ClusterNode getNode0(UUID nodeId) { + assert nodeId != null; + + UUID locNodeId0 = getLocalNodeId(); + + if (locNodeId0 != null && locNodeId0.equals(nodeId)) + // Return local node directly. + return locNode; + + return ring.node(nodeId); + } + /** * Thread that cleans IP finder and keeps it in the correct state, unregistering * addresses of the nodes that has left the topology. diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java index c988d7e91e667..e6eaa8ef643e1 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java @@ -433,6 +433,16 @@ public UUID getCoordinator() { return impl.getNode(nodeId); } + /** + * @param id Id. + */ + public ClusterNode getNode0(UUID id) { + if (impl instanceof ServerImpl) + return ((ServerImpl)impl).getNode0(id); + + return getNode(id); + } + /** {@inheritDoc} */ @Override public boolean pingNode(UUID nodeId) { return impl.pingNode(nodeId); diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteClientConnectTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteClientConnectTest.java new file mode 100644 index 0000000000000..1a899876ecb3c --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteClientConnectTest.java @@ -0,0 +1,163 @@ +/* + * 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.spi.discovery.tcp; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; +import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryNodeAddFinishedMessage; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + + +/** + * We emulate that client receive message about joining to topology earlier than some server nodes in topology. + * And make this client connect to such servers. + * To emulate this we connect client to second node in topology and pause sending message about joining finishing to + * third node. + */ +public class IgniteClientConnectTest extends GridCommonAbstractTest { + /** */ + private static TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** Latch to stop message sending. */ + private final CountDownLatch latch = new CountDownLatch(1); + + /** Start client flag. */ + private final AtomicBoolean clientJustStarted = new AtomicBoolean(false); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + TestTcpDiscoverySpi disco = new TestTcpDiscoverySpi(); + + if (igniteInstanceName.equals("client")) { + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + ipFinder.registerAddresses(Collections.singleton(new InetSocketAddress(InetAddress.getLoopbackAddress(), 47501))); + + disco.setIpFinder(ipFinder); + } + else + disco.setIpFinder(ipFinder); + + disco.setJoinTimeout(2 * 60_000); + disco.setSocketTimeout(1000); + disco.setNetworkTimeout(2000); + + cfg.setDiscoverySpi(disco); + + CacheConfiguration cacheConfiguration = new CacheConfiguration() + .setName(DEFAULT_CACHE_NAME) + .setCacheMode(CacheMode.PARTITIONED) + .setAffinity(new RendezvousAffinityFunction(false, 8)) + .setBackups(0); + + cfg.setCacheConfiguration(cacheConfiguration); + + return cfg; + } + + /** + * + * @throws Exception If failed. + */ + public void testClientConnectToBigTopology() throws Exception { + Ignite ignite = startGrids(3); + + IgniteCache cache = ignite.cache(DEFAULT_CACHE_NAME); + + Set keys = new HashSet<>(); + + for (int i = 0; i < 80; i++) { + cache.put(i, i); + + keys.add(i); + } + + TcpDiscoveryImpl discovery = ((TestTcpDiscoverySpi) ignite.configuration().getDiscoverySpi()).discovery(); + + assertTrue(discovery instanceof ServerImpl); + + IgniteConfiguration clientCfg = getConfiguration("client"); + + clientCfg.setClientMode(true); + + clientJustStarted.set(true); + + IgniteEx client = startGrid(clientCfg); + + latch.countDown(); + + System.err.println("GET ALL"); + client.cache(DEFAULT_CACHE_NAME).getAll(keys); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** + * + */ + class TestTcpDiscoverySpi extends TcpDiscoverySpi { + /** {@inheritDoc} */ + protected void writeToSocket(Socket sock, OutputStream out, TcpDiscoveryAbstractMessage msg, long timeout) throws IOException, + IgniteCheckedException { + if (msg instanceof TcpDiscoveryNodeAddFinishedMessage) { + if (msg.senderNodeId() != null && clientJustStarted.get()) + try { + latch.await(); + + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + super.writeToSocket(sock, out, msg, timeout); + } + else + super.writeToSocket(sock, out, msg, timeout); + } + + /** + * + */ + TcpDiscoveryImpl discovery() { + return impl; + } + } +} \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java index 12871492d0979..c506ca7e95bf2 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java @@ -20,6 +20,8 @@ import junit.framework.TestSuite; import org.apache.ignite.spi.GridTcpSpiForwardingSelfTest; import org.apache.ignite.spi.discovery.AuthenticationRestartTest; +import org.apache.ignite.spi.discovery.tcp.IgniteClientConnectTest; +import org.apache.ignite.spi.discovery.tcp.IgniteClientReconnectMassiveShutdownTest; import org.apache.ignite.spi.discovery.tcp.TcpClientDiscoveryMarshallerCheckSelfTest; import org.apache.ignite.spi.discovery.tcp.TcpClientDiscoverySpiFailureTimeoutSelfTest; import org.apache.ignite.spi.discovery.tcp.TcpClientDiscoverySpiMulticastTest; @@ -90,6 +92,10 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(TcpDiscoveryNodeAttributesUpdateOnReconnectTest.class)); suite.addTest(new TestSuite(AuthenticationRestartTest.class)); + //Client connect + suite.addTest(new TestSuite(IgniteClientConnectTest.class)); + suite.addTest(new TestSuite(IgniteClientReconnectMassiveShutdownTest.class)); + // SSL. suite.addTest(new TestSuite(TcpDiscoverySslSelfTest.class)); suite.addTest(new TestSuite(TcpDiscoverySslSecuredUnsecuredTest.class)); From 2971a3df264493f594feb851ba11a65219397a4c Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Sat, 19 Aug 2017 12:32:08 +0700 Subject: [PATCH 057/145] IGNITE-6065 Fixed broken links. (cherry picked from commit b67fdce) --- .../frontend/app/modules/states/configuration/caches/memory.pug | 2 +- .../frontend/app/modules/states/configuration/caches/store.pug | 2 +- .../app/modules/states/configuration/clusters/connector.pug | 2 +- .../app/modules/states/configuration/clusters/memory.pug | 2 +- .../frontend/app/modules/states/configuration/clusters/swap.pug | 2 +- .../app/modules/states/configuration/domains/general.pug | 2 +- .../frontend/app/modules/states/configuration/domains/store.pug | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/memory.pug b/modules/web-console/frontend/app/modules/states/configuration/caches/memory.pug index 826473172c527..17ceedf46531b 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/caches/memory.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/caches/memory.pug @@ -25,7 +25,7 @@ include /app/helpers/jade/mixins label Memory ignite-form-field-tooltip.tipLabel(ng-show='$ctrl.available(["1.0.0", "2.0.0"])') | Cache memory settings#[br] - | #[a(href="https://apacheignite.readme.io/v1.9/off-heap-memory" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/v1.9/docs/off-heap-memory" target="_blank") More info] ignite-form-field-tooltip.tipLabel(ng-show='$ctrl.available("2.0.0")') | Cache memory settings#[br] | #[a(href="https://apacheignite.readme.io/docs/evictions" target="_blank") More info] diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/store.pug b/modules/web-console/frontend/app/modules/states/configuration/caches/store.pug index d68b57ea4b4a6..d6dcbbe92a2b0 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/caches/store.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/caches/store.pug @@ -48,7 +48,7 @@ mixin hibernateField(name, model, items, valid, save, newItem) label Store ignite-form-field-tooltip.tipLabel | Cache store settings#[br] - | #[a(href="https://apacheignite.readme.io/docs/persistent-store" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/docs/3rd-party-store" target="_blank") More info] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body(ng-if=`ui.isPanelLoaded('${form}')`) diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/connector.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/connector.pug index b145ce28cfe26..6b148165d3b77 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/clusters/connector.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/connector.pug @@ -27,7 +27,7 @@ include /app/helpers/jade/mixins label Connector configuration ignite-form-field-tooltip.tipLabel | Configure HTTP REST configuration to enable HTTP server features#[br] - | #[a(href="https://apacheignite.readme.io/docs/configuration" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/docs/rest-api#general-configuration" target="_blank") More info] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body(ng-if=`ui.isPanelLoaded('${form}')`) diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug index a09feddfa2207..ad8613bd37d2a 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug @@ -26,7 +26,7 @@ include /app/helpers/jade/mixins label Memory configuration ignite-form-field-tooltip.tipLabel | Page memory is a manageable off-heap based memory architecture that is split into pages of fixed size#[br] - | #[a(href="https://apacheignite.readme.io/docs/page-memory" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/durable-memory" target="_blank") More info] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body(ng-if=`$ctrl.available("2.0.0") && ui.isPanelLoaded('${form}')`) diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug index 019b0cc14e3c1..60226cd01890d 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/swap.pug @@ -27,7 +27,7 @@ include /app/helpers/jade/mixins label Swap ignite-form-field-tooltip.tipLabel | Settings for overflow data to disk if it cannot fit in memory#[br] - | #[a(href="https://apacheignite.readme.io/docs/off-heap-memory#swap-space" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/v1.9/docs/off-heap-memory#swap-space" target="_blank") More info] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body(ng-if=`$ctrl.available(["1.0.0", "2.0.0"]) && ui.isPanelLoaded('${form}')`) diff --git a/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug b/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug index a6d3fc231ed36..f6f4e7293137f 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/domains/general.pug @@ -27,7 +27,7 @@ include /app/helpers/jade/mixins ignite-form-field-tooltip.tipLabel | Domain model properties common for Query and Store#[br] | #[a(href="https://apacheignite.readme.io/docs/cache-queries" target="_blank") More info about query configuration]#[br] - | #[a(href="https://apacheignite.readme.io/docs/persistent-store" target="_blank") More info about store] + | #[a(href="https://apacheignite.readme.io/docs/3rd-party-store" target="_blank") More info about store] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body diff --git a/modules/web-console/frontend/app/modules/states/configuration/domains/store.pug b/modules/web-console/frontend/app/modules/states/configuration/domains/store.pug index 4dee98677950a..7afb8e5d5a968 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/domains/store.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/domains/store.pug @@ -67,7 +67,7 @@ mixin table-db-field-edit(tbl, prefix, focusId, index) label(id='store-title') Domain model for cache store ignite-form-field-tooltip.tipLabel | Domain model properties for binding database with cache via POJO cache store#[br] - | #[a(href="https://apacheignite.readme.io/docs/persistent-store" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/docs/3rd-party-store" target="_blank") More info] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body(ng-if=`ui.isPanelLoaded('${form}')`) From 00a7d1b6b94567ed3fbeb922ac21d93a634d5d94 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Sat, 19 Aug 2017 12:48:22 +0700 Subject: [PATCH 058/145] IGNITE-6065 Fixed broken links. (cherry picked from commit b8b8064) --- .../app/modules/states/configuration/clusters/memory.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug b/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug index ad8613bd37d2a..e22afe284182d 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug +++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/memory.pug @@ -26,7 +26,7 @@ include /app/helpers/jade/mixins label Memory configuration ignite-form-field-tooltip.tipLabel | Page memory is a manageable off-heap based memory architecture that is split into pages of fixed size#[br] - | #[a(href="https://apacheignite.readme.io/durable-memory" target="_blank") More info] + | #[a(href="https://apacheignite.readme.io/docs/durable-memory" target="_blank") More info] ignite-form-revert .panel-collapse(role='tabpanel' bs-collapse-target id=`${form}`) .panel-body(ng-if=`$ctrl.available("2.0.0") && ui.isPanelLoaded('${form}')`) From 9d82c582dab1578d2126b12b8e30ae3c4dee230b Mon Sep 17 00:00:00 2001 From: Dmitriy Shabalin Date: Mon, 21 Aug 2017 16:53:15 +0700 Subject: [PATCH 059/145] IGNITE-4784 Web Console: Changed demo mode UI. (cherry picked from commit 90cf702) --- .../components/web-console-header/style.scss | 44 +++++++++++++++++-- .../web-console-header/template.pug | 4 ++ .../frontend/public/stylesheets/style.scss | 4 ++ .../frontend/views/includes/header-right.pug | 4 -- modules/web-console/frontend/views/index.pug | 8 ++-- 5 files changed, 52 insertions(+), 12 deletions(-) diff --git a/modules/web-console/frontend/app/components/web-console-header/style.scss b/modules/web-console/frontend/app/components/web-console-header/style.scss index aef4424140ac2..f221a00fedd90 100644 --- a/modules/web-console/frontend/app/components/web-console-header/style.scss +++ b/modules/web-console/frontend/app/components/web-console-header/style.scss @@ -131,12 +131,12 @@ web-console-header { } .wch-notification { - font-size: $font-size-base; line-height: 16px; - padding: 4px; + padding: 7px; background: $brand-warning; + font-size: 16px; text-align: center; - + a { color: $brand-info; } @@ -145,4 +145,40 @@ web-console-header { border-top: 1px solid darken($brand-warning, 15%); } } -} \ No newline at end of file + + .wch-notification.wch-notification--demo { + z-index: 10000000; + position: fixed; + top: 0; + width: 100%; + + color: white; + background: $ignite-brand-success; + border: none; + + box-shadow: 0 0 15px #008eff; + animation: pulse 2s infinite; + + a { + color: white; + text-decoration: underline; + + &:hover { + color: #ffab40; + text-decoration: none; + } + } + } +} + +@keyframes pulse { + 0% { + box-shadow: 0 1px 2px #008eff; + } + 50% { + box-shadow: 0 1px 15px #008eff; + } + 100% { + box-shadow: 0 1px 2px #008eff; + } +} diff --git a/modules/web-console/frontend/app/components/web-console-header/template.pug b/modules/web-console/frontend/app/components/web-console-header/template.pug index 6930d0773da4c..23fc81e52415c 100644 --- a/modules/web-console/frontend/app/components/web-console-header/template.pug +++ b/modules/web-console/frontend/app/components/web-console-header/template.pug @@ -19,6 +19,10 @@ .wch-notification(ng-show='$ctrl.$rootScope.user.becomeUsed') | You are currently viewing user #[strong {{$ctrl.$rootScope.user.firstName}} {{$ctrl.$rootScope.user.lastName}}] as administrator. #[a(ng-click='$ctrl.$rootScope.revertIdentity()') Revert to your identity?] +.wch-notification.wch-notification--demo(ng-if='$ctrl.$rootScope.IgniteDemoMode') + .container(ng-controller='demoController') + | You are now in #[b Demo Mode]. #[a(ng-click='closeDemo();') Close Demo?] + .wch-content.container a(ui-sref='signin') img.wch-logo(ng-src='{{::$ctrl.branding.headerLogo}}') diff --git a/modules/web-console/frontend/public/stylesheets/style.scss b/modules/web-console/frontend/public/stylesheets/style.scss index 183dc2979c7fc..eeb3a55eea26c 100644 --- a/modules/web-console/frontend/public/stylesheets/style.scss +++ b/modules/web-console/frontend/public/stylesheets/style.scss @@ -2189,3 +2189,7 @@ html,body,.splash-screen { .center-container { position: absolute !important; } + +.demo-mode { + padding-top: 30px; +} diff --git a/modules/web-console/frontend/views/includes/header-right.pug b/modules/web-console/frontend/views/includes/header-right.pug index de67445001e62..8eeb281d970fa 100644 --- a/modules/web-console/frontend/views/includes/header-right.pug +++ b/modules/web-console/frontend/views/includes/header-right.pug @@ -15,10 +15,6 @@ limitations under the License. .wch-demo-toggle(ng-controller='demoController') - button.btn-ignite.btn-ignite--success( - ng-if='IgniteDemoMode' - ng-click='closeDemo()' - ) Close Demo button.btn-ignite.btn-ignite--success( ng-if='!IgniteDemoMode' ng-click='startDemo()' diff --git a/modules/web-console/frontend/views/index.pug b/modules/web-console/frontend/views/index.pug index 2e47e873db3de..9565949120d86 100644 --- a/modules/web-console/frontend/views/index.pug +++ b/modules/web-console/frontend/views/index.pug @@ -28,7 +28,7 @@ html(ng-app='ignite-console' id='app' ng-strict-di) meta(name='fragment' content='!') - body.theme-line.body-overlap + body.theme-line.body-overlap(ng-class='{ "demo-mode": IgniteDemoMode }') .splash.splash-max-foreground(hide-on-state-change) .splash-wrapper @@ -39,8 +39,8 @@ html(ng-app='ignite-console' id='app' ng-strict-di) .splash-wellcome Loading... - .ribbon-wrapper.right(ng-cloak) - .ribbon(ng-style='IgniteDemoMode && {"background": "#1b6d88"}') - label {{IgniteDemoMode ? "Demo" : "Beta" }} + .ribbon-wrapper.right(ng-if='!IgniteDemoMode') + .ribbon + label Beta .wrapper(ui-view='') From 2d39bab7e8c40a179fdd0c30b492a53fe2142ba0 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Mon, 21 Aug 2017 18:42:08 +0700 Subject: [PATCH 060/145] IGNITE-6127 Implemented GZip data between web-agent and web server. (cherry picked from commit 90f4d4d) --- .../VisorNodeDataCollectorTaskResult.java | 29 +++++++++++++++++++ .../web-console/backend/app/agentSocket.js | 29 ++++++++++++++----- .../agent/handlers/AbstractListener.java | 21 ++++++++++++++ .../ignite/console/agent/rest/RestResult.java | 25 ++++++++++++++-- 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeDataCollectorTaskResult.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeDataCollectorTaskResult.java index 093e86769e4f2..c1e2f1f126a69 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeDataCollectorTaskResult.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorNodeDataCollectorTaskResult.java @@ -271,6 +271,35 @@ public Map getPersistenceMetricsEx() { return persistenceMetricsEx; } + /** + * Add specified results. + * + * @param res Results to add. + */ + public void add(VisorNodeDataCollectorTaskResult res) { + assert res != null; + + active = active || res.isActive(); + unhandledEx.putAll(res.getUnhandledEx()); + gridNames.putAll(res.getGridNames()); + topVersions.putAll(res.getTopologyVersions()); + taskMonitoringEnabled.putAll(res.isTaskMonitoringEnabled()); + errCnts.putAll(res.getErrorCounts()); + evts.addAll(res.getEvents()); + evtsEx.putAll(res.getEventsEx()); + memoryMetrics.putAll(res.getMemoryMetrics()); + memoryMetricsEx.putAll(res.getMemoryMetricsEx()); + caches.putAll(res.getCaches()); + cachesEx.putAll(res.getCachesEx()); + igfss.putAll(res.getIgfss()); + igfsEndpoints.putAll(res.getIgfsEndpoints()); + igfssEx.putAll(res.getIgfssEx()); + readyTopVers.putAll(res.getReadyAffinityVersions()); + pendingExchanges.putAll(res.getPendingExchanges()); + persistenceMetrics.putAll(res.getPersistenceMetrics()); + persistenceMetricsEx.putAll(res.getPersistenceMetricsEx()); + } + /** {@inheritDoc} */ @Override protected void writeExternalData(ObjectOutput out) throws IOException { out.writeBoolean(active); diff --git a/modules/web-console/backend/app/agentSocket.js b/modules/web-console/backend/app/agentSocket.js index 08533392968d7..489d1454c5179 100644 --- a/modules/web-console/backend/app/agentSocket.js +++ b/modules/web-console/backend/app/agentSocket.js @@ -24,7 +24,7 @@ */ module.exports = { implements: 'agent-socket', - inject: ['require(lodash)'] + inject: ['require(lodash)', 'require(zlib)'] }; /** @@ -79,9 +79,10 @@ class Command { /** * @param _ + * @param zlib * @returns {AgentSocket} */ -module.exports.factory = function(_) { +module.exports.factory = function(_, zlib) { /** * Connected agent descriptor. */ @@ -131,11 +132,25 @@ module.exports.factory = function(_) { */ emitEvent(event, ...args) { return new Promise((resolve, reject) => - this._emit(event, args, (error, res) => { - if (error) - return reject(error); - - resolve(res); + this._emit(event, args, (resErr, res) => { + if (resErr) + return reject(resErr); + + if (res.zipped) { + // TODO IGNITE-6127 Temporary solution until GZip support for socket.io-client-java. + // See: https://github.com/socketio/socket.io-client-java/issues/312 + // We can GZip manually for now. + zlib.gunzip(new Buffer(res.data, 'base64'), (unzipErr, unzipped) => { + if (unzipErr) + return reject(unzipErr); + + res.data = unzipped.toString(); + + resolve(res); + }); + } + else + resolve(res); }) ); } diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java index 987dac90bb77a..faf864b7b1b33 100644 --- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java +++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractListener.java @@ -19,11 +19,15 @@ import io.socket.client.Ack; import io.socket.emitter.Emitter; +import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.zip.GZIPOutputStream; +import org.apache.commons.codec.binary.Base64OutputStream; +import org.apache.ignite.console.agent.rest.RestResult; import org.apache.log4j.Logger; import static org.apache.ignite.console.agent.AgentUtils.removeCallback; @@ -66,6 +70,23 @@ else if (args.length == 1) try { Object res = execute(params); + // TODO IGNITE-6127 Temporary solution until GZip support for socket.io-client-java. + // See: https://github.com/socketio/socket.io-client-java/issues/312 + // We can GZip manually for now. + if (res instanceof RestResult) { + RestResult restRes = (RestResult) res; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); + Base64OutputStream b64os = new Base64OutputStream(baos); + GZIPOutputStream gzip = new GZIPOutputStream(b64os); + + gzip.write(restRes.getData().getBytes()); + + gzip.close(); + + restRes.zipData(baos.toString()); + } + cb.call(null, toJSON(res)); } catch (Exception e) { cb.call(e, null); diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java index 5beeee721346c..962ffb6709764 100644 --- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java +++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestResult.java @@ -22,13 +22,16 @@ */ public class RestResult { /** REST http code. */ - private final int status; + private int status; /** The field contains description of error if server could not handle the request. */ - private final String error; + private String error; /** The field contains result of command. */ - private final String data; + private String data; + + /** Flag of zipped data. */ + private boolean zipped; /** * @param status REST http code. @@ -78,4 +81,20 @@ public String getError() { public String getData() { return data; } + + /** + * @param data Set zipped data. + */ + public void zipData(String data) { + zipped = true; + + this.data = data; + } + + /** + * @return {@code true if data is zipped and Base64 encoded.} + */ + public boolean isZipped() { + return zipped; + } } From 324915f37285e81b872e14a5c13a066258302bd5 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Mon, 21 Aug 2017 14:46:48 +0300 Subject: [PATCH 061/145] ignite-6096: Fixed races on partition evict (backport). --- .../dht/GridDhtPartitionTopologyImpl.java | 16 ++++++++++++++++ .../GridCacheDatabaseSharedManager.java | 13 ++++++++----- .../persistence/pagemem/PageMemoryImpl.java | 9 ++++++++- 3 files changed, 32 insertions(+), 6 deletions(-) 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 0ca291dd95dc7..29a6662e011a2 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 @@ -671,6 +671,15 @@ private GridDhtLocalPartition createPartition(int p) { GridDhtLocalPartition loc = locParts.get(p); if (loc == null || loc.state() == EVICTED) { + if (loc != null) { + try { + loc.rent(false).get(); + } + catch (IgniteCheckedException e) { + throw new IgniteException(e); + } + } + locParts.set(p, loc = new GridDhtLocalPartition(ctx, grp, p)); T2 cntr = cntrMap.get(p); @@ -729,6 +738,13 @@ private GridDhtLocalPartition localPartition0(int p, boolean belongs = partitionLocalNode(p, topVer); if (loc != null && state == EVICTED) { + try { + loc.rent(false).get(); + } + catch (IgniteCheckedException ex) { + throw new IgniteException(ex); + } + locParts.set(p, loc = null); if (!treatAllPartAsLoc && !belongs) 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 cc1599b10ad31..4f3dec2139ac0 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 @@ -1550,6 +1550,14 @@ private void restorePartitionState( if (storeMgr.pages(grpId, i) <= 1) continue; + GridDhtLocalPartition part = grp.topology() + .localPartition(i, AffinityTopologyVersion.NONE, true); + + assert part != null; + + // TODO: https://issues.apache.org/jira/browse/IGNITE-6097 + grp.offheap().onPartitionInitialCounterUpdated(i, 0); + long partMetaId = pageMem.partitionMetaPageId(grpId, i); long partMetaPage = pageMem.acquirePage(grpId, partMetaId); try { @@ -1562,11 +1570,6 @@ private void restorePartitionState( T2 fromWal = partStates.get(new T2<>(grpId, i)); - GridDhtLocalPartition part = grp.topology() - .localPartition(i, AffinityTopologyVersion.NONE, true); - - assert part != null; - if (fromWal != null) { int stateId = fromWal.get1(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java index b6e5f4653e944..dbb64f8c22a50 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java @@ -640,13 +640,14 @@ private long refreshOutdatedPage(Segment seg, int cacheId, long pageId, boolean GridUnsafe.setMemory(absPtr + PAGE_OVERHEAD, pageSize(), (byte)0); + PageHeader.dirty(absPtr, false); + long tmpBufPtr = PageHeader.tempBufferPointer(absPtr); if (tmpBufPtr != INVALID_REL_PTR) { GridUnsafe.setMemory(checkpointPool.absolute(tmpBufPtr) + PAGE_OVERHEAD, pageSize(), (byte)0); PageHeader.tempBufferPointer(absPtr, INVALID_REL_PTR); - PageHeader.dirty(absPtr, false); // We pinned the page when allocated the temp buffer, release it now. PageHeader.releasePage(absPtr); @@ -657,6 +658,12 @@ private long refreshOutdatedPage(Segment seg, int cacheId, long pageId, boolean if (rmv) seg.loadedPages.remove(cacheId, PageIdUtils.effectivePageId(pageId), tag); + if (seg.segCheckpointPages != null) + seg.segCheckpointPages.remove(new FullPageId(pageId, cacheId)); + + if (seg.dirtyPages != null) + seg.dirtyPages.remove(new FullPageId(pageId, cacheId)); + return relPtr; } From c3aa151698f82e46b97052a2189ecfdf11a58f37 Mon Sep 17 00:00:00 2001 From: sboikov Date: Mon, 24 Jul 2017 11:47:16 +0300 Subject: [PATCH 062/145] Test for cache partitions state, fix for client cache start. (cherry picked from commit aeb9336b3b161ddfff73f17e41cd453409b84a16) # Conflicts: # modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java # modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java # modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java --- .../cache/CacheAffinitySharedManager.java | 51 ++- .../dht/GridClientPartitionTopology.java | 7 +- .../dht/GridDhtPartitionTopology.java | 12 +- .../dht/GridDhtPartitionTopologyImpl.java | 45 +- .../GridDhtPartitionsExchangeFuture.java | 120 +++-- .../GridCacheDatabaseSharedManager.java | 5 +- .../CacheLateAffinityAssignmentTest.java | 36 +- .../distributed/CachePartitionStateTest.java | 410 ++++++++++++++++++ .../TestCacheNodeExcludingFilter.java | 53 +++ .../db/IgnitePdsCacheRestoreTest.java | 208 +++++++++ .../testsuites/IgniteCacheTestSuite6.java | 3 + .../ignite/testsuites/IgnitePdsTestSuite.java | 8 +- 12 files changed, 832 insertions(+), 126 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/TestCacheNodeExcludingFilter.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsCacheRestoreTest.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 9fc791e561870..4db8b76daa7c2 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 @@ -517,6 +517,16 @@ else if (!fetchFuts.containsKey(grp.groupId())) { } } + for (DynamicCacheDescriptor desc : startDescs) { + if (desc.cacheConfiguration().getCacheMode() != LOCAL) { + CacheGroupContext grp = cctx.cache().cacheGroup(desc.groupId()); + + assert grp != null; + + grp.topology().onExchangeDone(grp.affinity().cachedAffinity(topVer), true); + } + } + cctx.cache().initCacheProxies(topVer, null); cctx.cache().completeClientCacheChangeFuture(msg.requestId(), null); @@ -1272,6 +1282,19 @@ private String groupNames(Collection grpIds) { return names.toString(); } + /** + * @param grpId Group ID. + * @return Group name for debug purpose. + */ + private String debugGroupName(int grpId) { + CacheGroupDescriptor desc = caches.group(grpId); + + if (desc != null) + return desc.cacheOrGroupName(); + else + return "Unknown group: " + grpId; + } + /** * @param fut Exchange future. * @throws IgniteCheckedException If failed. @@ -1370,19 +1393,31 @@ private GridDhtAffinityAssignmentResponse fetchAffinity(AffinityTopologyVersion * Called on exchange initiated by server node leave. * * @param fut Exchange future. + * @param crd Coordinator flag. * @throws IgniteCheckedException If failed. * @return {@code True} if affinity should be assigned by coordinator. */ - public boolean onServerLeft(final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException { + public boolean onServerLeft(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException { ClusterNode leftNode = fut.discoveryEvent().eventNode(); assert !leftNode.isClient() : leftNode; - for (CacheGroupContext grp : cctx.cache().cacheGroups()) { - if (grp.isLocal()) - continue; + if (crd) { + // Need initialize CacheGroupHolders if this node become coordinator on this exchange. + forAllRegisteredCacheGroups(new IgniteInClosureX() { + @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { + CacheGroupHolder cache = groupHolder(fut.topologyVersion(), desc); - grp.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + cache.aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + } + }); + } + else { + forAllCacheGroups(false, new IgniteInClosureX() { + @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException { + aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + } + }); } synchronized (mux) { @@ -1407,12 +1442,8 @@ private IgniteInternalFuture initCoordinatorCaches(final GridDhtPartitionsExc @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { CacheGroupHolder grpHolder = grpHolders.get(desc.groupId()); - if (grpHolder != null) { - if (grpHolder.client()) // Affinity for non-client holders calculated in {@link #onServerLeft}. - grpHolder.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); - + if (grpHolder != null) return; - } // Need initialize holders and affinity if this node became coordinator during this exchange. final Integer grpId = desc.groupId(); 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 6ff572b189f2c..1fb955703b203 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 @@ -366,6 +366,11 @@ else if (!node2part.nodeId().equals(loc.id())) { return localPartition(p, topVer, create); } + /** {@inheritDoc} */ + @Override public GridDhtLocalPartition forceCreatePartition(int p) throws IgniteCheckedException { + throw new UnsupportedOperationException(); + } + /** {@inheritDoc} */ @Override public GridDhtLocalPartition localPartition(int p) { return localPartition(p, AffinityTopologyVersion.NONE, false); @@ -835,7 +840,7 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa } /** {@inheritDoc} */ - @Override public void onExchangeDone(AffinityAssignment assignment) { + @Override public void onExchangeDone(AffinityAssignment assignment, boolean updateRebalanceVer) { // No-op. } 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 5f76d12a16fae..d9e04a6da4a46 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 @@ -129,6 +129,15 @@ public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affR @Nullable public GridDhtLocalPartition localPartition(int p, AffinityTopologyVersion topVer, boolean create) throws GridDhtInvalidPartitionException; + /** + * Unconditionally creates partition during restore of persisted partition state. + * + * @param p Partition ID. + * @return Partition. + * @throws IgniteCheckedException If failed. + */ + public GridDhtLocalPartition forceCreatePartition(int p) throws IgniteCheckedException; + /** * @param topVer Topology version at the time of creation. * @param p Partition ID. @@ -331,6 +340,7 @@ public boolean update(@Nullable GridDhtPartitionExchangeId exchId, * Callback on exchange done. * * @param assignment New affinity assignment. + * @param updateRebalanceVer {@code True} if need check rebalance state. */ - public void onExchangeDone(AffinityAssignment assignment); + public void onExchangeDone(AffinityAssignment assignment, boolean updateRebalanceVer); } 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 29a6662e011a2..197c753924fec 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 @@ -135,9 +135,6 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology { /** */ private volatile AffinityTopologyVersion rebalancedTopVer = AffinityTopologyVersion.NONE; - /** */ - private volatile boolean treatAllPartAsLoc; - /** * @param ctx Cache shared context. * @param grp Cache group. @@ -421,14 +418,6 @@ else if (localNode(p, aff)) /** {@inheritDoc} */ @Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affReady) throws IgniteCheckedException { - DiscoveryEvent discoEvt = exchFut.discoveryEvent(); - - treatAllPartAsLoc = exchFut.activateCluster() - || (discoEvt.type() == EventType.EVT_NODE_JOINED - && discoEvt.eventNode().isLocal() - && !ctx.kernalContext().clientNode() - ); - ClusterNode loc = ctx.localNode(); ctx.database().checkpointReadLock(); @@ -540,8 +529,6 @@ private boolean partitionLocalNode(int p, AffinityTopologyVersion topVer) { /** {@inheritDoc} */ @Override public boolean afterExchange(GridDhtPartitionsExchangeFuture exchFut) throws IgniteCheckedException { - treatAllPartAsLoc = false; - boolean changed = false; int num = grp.affinity().partitions(); @@ -701,6 +688,29 @@ private GridDhtLocalPartition createPartition(int p) { return loc; } + /** {@inheritDoc} */ + @Override public GridDhtLocalPartition forceCreatePartition(int p) throws IgniteCheckedException { + lock.writeLock().lock(); + + try { + GridDhtLocalPartition part = locParts.get(p); + + if (part != null) + return part; + + part = new GridDhtLocalPartition(ctx, grp, p); + + locParts.set(p, part); + + ctx.pageStore().onPartitionCreated(grp.groupId(), p); + + return part; + } + finally { + lock.writeLock().unlock(); + } + } + /** * @param p Partition number. * @param topVer Topology version. @@ -747,7 +757,7 @@ private GridDhtLocalPartition localPartition0(int p, locParts.set(p, loc = null); - if (!treatAllPartAsLoc && !belongs) + if (!belongs) throw new GridDhtInvalidPartitionException(p, "Adding entry to evicted partition " + "(often may be caused by inconsistent 'key.hashCode()' implementation) " + "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']'); @@ -757,7 +767,7 @@ else if (loc != null && state == RENTING && !showRenting) "[part=" + p + ", shouldBeMoving=" + loc.reload() + "]"); if (loc == null) { - if (!treatAllPartAsLoc && !belongs) + if (!belongs) throw new GridDhtInvalidPartitionException(p, "Creating partition which does not belong to " + "local node (often may be caused by inconsistent 'key.hashCode()' implementation) " + "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']'); @@ -1520,12 +1530,15 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa } /** {@inheritDoc} */ - @Override public void onExchangeDone(AffinityAssignment assignment) { + @Override public void onExchangeDone(AffinityAssignment assignment, boolean updateRebalanceVer) { lock.writeLock().lock(); try { if (assignment.topologyVersion().compareTo(diffFromAffinityVer) >= 0) rebuildDiff(assignment); + + if (updateRebalanceVer) + updateRebalanceVersion(assignment.assignment()); } finally { lock.writeLock().unlock(); 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 ad17666b5d5df..403923ca84a0a 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 @@ -193,9 +193,6 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte /** */ private CacheAffinityChangeMessage affChangeMsg; - /** */ - private boolean clientOnlyExchange; - /** Init timestamp. Used to track the amount of time spent to complete the future. */ private long initTs; @@ -486,26 +483,8 @@ else if (msg instanceof SnapshotDiscoveryMessage) { cctx.affinity().initStartedCaches(crdNode, this, receivedCaches); } - else { - cctx.activate(); - - List> caches = - cctx.cache().cachesToStartOnLocalJoin(); - - if (cctx.database().persistenceEnabled() && - !cctx.kernalContext().clientNode()) { - List startDescs = new ArrayList<>(); - - if (caches != null) { - for (T2 c : caches) - startDescs.add(c.get1()); - } - - cctx.database().readCheckpointAndRestoreMemory(startDescs); - } - - cctx.cache().startCachesOnLocalJoin(caches, topVer); - } + else + initCachesOnLocalJoin(); } exchange = CU.clientNode(discoEvt.eventNode()) ? @@ -569,6 +548,29 @@ else if (msg instanceof SnapshotDiscoveryMessage) { } } + /** + * @throws IgniteCheckedException If failed. + */ + private void initCachesOnLocalJoin() throws IgniteCheckedException { + cctx.activate(); + + List> caches = + cctx.cache().cachesToStartOnLocalJoin(); + + if (cctx.database().persistenceEnabled() && !cctx.kernalContext().clientNode()) { + List startDescs = new ArrayList<>(); + + if (caches != null) { + for (T2 c : caches) + startDescs.add(c.get1()); + } + + cctx.database().readCheckpointAndRestoreMemory(startDescs); + } + + cctx.cache().startCachesOnLocalJoin(caches, topologyVersion()); + } + /** * @throws IgniteCheckedException If failed. */ @@ -777,7 +779,7 @@ private ExchangeType onServerNodeEvent(boolean crd) throws IgniteCheckedExceptio warnNoAffinityNodes(); - centralizedAff = cctx.affinity().onServerLeft(this); + centralizedAff = cctx.affinity().onServerLeft(this, crd); } else cctx.affinity().onServerJoin(this, crd); @@ -789,40 +791,15 @@ private ExchangeType onServerNodeEvent(boolean crd) throws IgniteCheckedExceptio * @throws IgniteCheckedException If failed. */ private void clientOnlyExchange() throws IgniteCheckedException { - clientOnlyExchange = true; - if (crd != null) { - if (crd.isLocal()) { - for (CacheGroupContext grp : cctx.cache().cacheGroups()) { - boolean updateTop = !grp.isLocal() && - exchId.topologyVersion().equals(grp.localStartVersion()); - - if (updateTop) { - for (GridClientPartitionTopology top : cctx.exchange().clientTopologies()) { - if (top.groupId() == grp.groupId()) { - GridDhtPartitionFullMap fullMap = top.partitionMap(true); - - assert fullMap != null; - - grp.topology().update(topologyVersion(), - fullMap, - top.updateCounters(false), - Collections.emptySet()); + assert !crd.isLocal() : crd; - break; - } - } - } - } - } - else { - if (!centralizedAff) - sendLocalPartitions(crd); + if (!centralizedAff) + sendLocalPartitions(crd); - initDone(); + initDone(); - return; - } + return; } else { if (centralizedAff) { // Last server node failed. @@ -897,8 +874,7 @@ private void tryToPerformLocalSnapshotOperation() { try { long start = U.currentTimeMillis(); - IgniteInternalFuture fut = cctx.snapshot() - .tryStartLocalSnapshotOperation(discoEvt); + IgniteInternalFuture fut = cctx.snapshot().tryStartLocalSnapshotOperation(discoEvt); if (fut != null) { fut.get(); @@ -1124,6 +1100,8 @@ private boolean cacheStopping(int cacheId) { private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException { assert node != null; + GridDhtPartitionsSingleMessage msg; + // Reset lost partition before send local partition to coordinator. if (exchActions != null) { Set caches = exchActions.cachesToResetLostPartitions(); @@ -1132,22 +1110,32 @@ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException resetLostPartitions(caches); } - GridDhtPartitionsSingleMessage m = cctx.exchange().createPartitionsSingleMessage( - node, exchangeId(), clientOnlyExchange, true); + if (cctx.kernalContext().clientNode()) { + msg = new GridDhtPartitionsSingleMessage(exchangeId(), + true, + null, + true); + } + else { + msg = cctx.exchange().createPartitionsSingleMessage(node, + exchangeId(), + false, + true); + } Map> partHistReserved0 = partHistReserved; if (partHistReserved0 != null) - m.partitionHistoryCounters(partHistReserved0); + msg.partitionHistoryCounters(partHistReserved0); if (stateChangeExchange() && changeGlobalStateE != null) - m.setError(changeGlobalStateE); + msg.setError(changeGlobalStateE); if (log.isDebugEnabled()) - log.debug("Sending local partitions [nodeId=" + node.id() + ", exchId=" + exchId + ", msg=" + m + ']'); + log.debug("Sending local partitions [nodeId=" + node.id() + ", exchId=" + exchId + ", msg=" + msg + ']'); try { - cctx.io().send(node, m, SYSTEM_POOL); + cctx.io().send(node, msg, SYSTEM_POOL); } catch (ClusterTopologyCheckedException ignored) { if (log.isDebugEnabled()) @@ -1320,7 +1308,7 @@ public boolean serverNodeDiscoveryEvent() { if (err == null) { for (CacheGroupContext grp : cctx.cache().cacheGroups()) { if (!grp.isLocal()) - grp.topology().onExchangeDone(grp.affinity().cachedAffinity(topologyVersion())); + grp.topology().onExchangeDone(grp.affinity().cachedAffinity(topologyVersion()), false); } } @@ -1388,10 +1376,12 @@ private void updateLastVersion(GridCacheVersion ver) { public void onReceive(final ClusterNode node, final GridDhtPartitionsSingleMessage msg) { assert msg != null; assert msg.exchangeId().equals(exchId) : msg; - assert msg.lastVersion() != null : msg; - if (!msg.client()) + if (!msg.client()) { + assert msg.lastVersion() != null : msg; + updateLastVersion(msg.lastVersion()); + } if (isDone()) { if (log.isDebugEnabled()) 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 4f3dec2139ac0..4e4048fa5ca52 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 @@ -1550,8 +1550,7 @@ private void restorePartitionState( if (storeMgr.pages(grpId, i) <= 1) continue; - GridDhtLocalPartition part = grp.topology() - .localPartition(i, AffinityTopologyVersion.NONE, true); + GridDhtLocalPartition part = grp.topology().forceCreatePartition(i); assert part != null; @@ -1631,7 +1630,7 @@ private void applyUpdate(GridCacheContext cacheCtx, DataEntry dataEntry) throws if (partId == -1) partId = cacheCtx.affinity().partition(dataEntry.key()); - GridDhtLocalPartition locPart = cacheCtx.topology().localPartition(partId); + GridDhtLocalPartition locPart = cacheCtx.topology().forceCreatePartition(dataEntry.partitionId()); switch (dataEntry.op()) { case CREATE: diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java index 23043d10f773f..7d8620a210502 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java @@ -331,7 +331,7 @@ public void testAffinitySimpleSequentialStartNoCacheOnCoordinator() throws Excep } }; - cacheNodeFilter = new CacheNodeFilter(F.asList(getTestIgniteInstanceName(0))); + cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(0))); testAffinitySimpleSequentialStart(); @@ -351,7 +351,7 @@ public void testAffinitySimpleNoCacheOnCoordinator1() throws Exception { } }; - cacheNodeFilter = new CacheNodeFilter(F.asList(getTestIgniteInstanceName(1))); + cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(1))); startServer(0, 1); @@ -391,7 +391,7 @@ public void testAffinitySimpleNoCacheOnCoordinator2() throws Exception { } }; - cacheNodeFilter = new CacheNodeFilter(F.asList(getTestIgniteInstanceName(1), getTestIgniteInstanceName(2))); + cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(1), getTestIgniteInstanceName(2))); startServer(0, 1); startServer(1, 2); @@ -439,7 +439,7 @@ public void testCreateCloseClientCacheOnCoordinator1() throws Exception { } }; - cacheNodeFilter = new CacheNodeFilter(F.asList(getTestIgniteInstanceName(0))); + cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(0))); Ignite ignite0 = startServer(0, 1); @@ -467,7 +467,7 @@ public void testCreateCloseClientCacheOnCoordinator2() throws Exception { } }; - cacheNodeFilter = new CacheNodeFilter(F.asList(getTestIgniteInstanceName(0))); + cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(0))); Ignite ignite0 = startServer(0, 1); @@ -520,7 +520,7 @@ public void testCacheDestroyAndCreate2() throws Exception { */ private void cacheDestroyAndCreate(boolean cacheOnCrd) throws Exception { if (!cacheOnCrd) - cacheNodeFilter = new CacheNodeFilter(Collections.singletonList(getTestIgniteInstanceName(0))); + cacheNodeFilter = new TestCacheNodeExcludingFilter(Collections.singletonList(getTestIgniteInstanceName(0))); startServer(0, 1); @@ -2069,7 +2069,7 @@ private CacheConfiguration randomCacheConfiguration(Random rnd, String name, Lis exclude.add("server-" + (srvIdx + rnd.nextInt(10))); } - ccfg.setNodeFilter(new CacheNodeFilter(exclude)); + ccfg.setNodeFilter(new TestCacheNodeExcludingFilter(exclude)); } ccfg.setName(name); @@ -2642,28 +2642,6 @@ public TestServiceImpl(int key) { } } - /** - * - */ - static class CacheNodeFilter implements IgnitePredicate { - /** */ - private Collection excludeNodes; - - /** - * @param excludeNodes Nodes names. - */ - public CacheNodeFilter(Collection excludeNodes) { - this.excludeNodes = excludeNodes; - } - - /** {@inheritDoc} */ - @Override public boolean apply(ClusterNode clusterNode) { - String name = clusterNode.attribute(ATTR_IGNITE_INSTANCE_NAME).toString(); - - return !excludeNodes.contains(name); - } - } - /** * */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java new file mode 100644 index 0000000000000..c64ed0b6b533a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java @@ -0,0 +1,410 @@ +/* + * 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; + +import java.util.HashSet; +import java.util.Set; +import org.apache.ignite.Ignite; +import org.apache.ignite.cache.affinity.Affinity; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.TestRecordingCommunicationSpi; +import org.apache.ignite.internal.processors.affinity.AffinityAssignment; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.GridCacheAdapter; +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.distributed.dht.preloader.GridDhtPartitionMap; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage; +import org.apache.ignite.internal.util.typedef.G; +import org.apache.ignite.internal.util.typedef.internal.CU; +import org.apache.ignite.lang.IgniteBiPredicate; +import org.apache.ignite.plugin.extensions.communication.Message; +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 static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; +import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.EVICTED; +import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.MOVING; +import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING; + +/** + * + */ +public class CachePartitionStateTest extends GridCommonAbstractTest { + /** */ + private static TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** */ + private boolean client; + + /** */ + private CacheConfiguration ccfg; + + /** {@inheritDoc} */ + protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder); + + cfg.setCommunicationSpi(new TestRecordingCommunicationSpi()); + + cfg.setClientMode(client); + + if (ccfg != null) { + cfg.setCacheConfiguration(ccfg); + + ccfg = null; + } + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + + super.afterTest(); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState1_1() throws Exception { + partitionState1(0, true); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState1_2() throws Exception { + partitionState1(1, true); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState1_2_NoCacheOnCoordinator() throws Exception { + partitionState1(1, false); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState1_3() throws Exception { + partitionState1(100, true); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState2_1() throws Exception { + partitionState2(0, true); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState2_2() throws Exception { + partitionState2(1, true); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState2_2_NoCacheOnCoordinator() throws Exception { + partitionState2(1, false); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionState2_3() throws Exception { + partitionState2(100, true); + } + + /** + * @param backups Number of backups. + * @param crdAffNode If {@code false} cache is not created on coordinator. + * @throws Exception If failed. + */ + private void partitionState1(int backups, boolean crdAffNode) throws Exception { + startGrids(3); + + blockSupplySend(DEFAULT_CACHE_NAME); + + CacheConfiguration ccfg = cacheConfiguration(DEFAULT_CACHE_NAME, backups); + + if (!crdAffNode) + ccfg.setNodeFilter(new TestCacheNodeExcludingFilter(getTestIgniteInstanceName(0))); + + ignite(1).createCache(ccfg); + + AffinityAssignment assign0 = + grid(1).context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity().assignment( + new AffinityTopologyVersion(3, 1)); + + awaitPartitionMapExchange(); + + checkPartitionsState(assign0, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + + client = true; + + Ignite clientNode = startGrid(4); + + checkPartitionsState(assign0, DEFAULT_CACHE_NAME, OWNING); + + clientNode.cache(DEFAULT_CACHE_NAME); + + checkPartitionsState(assign0, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + + client = false; + + startGrid(5); + + checkRebalance(DEFAULT_CACHE_NAME, false); + + for (int i = 0; i < 3; i++) + checkNodePartitions(assign0, ignite(i).cluster().localNode(), DEFAULT_CACHE_NAME, OWNING); + + AffinityAssignment assign1 = + grid(1).context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity().assignment( + new AffinityTopologyVersion(5, 0)); + + checkNodePartitions(assign1, ignite(5).cluster().localNode(), DEFAULT_CACHE_NAME, MOVING); + + stopBlock(); + + awaitPartitionMapExchange(); + + AffinityAssignment assign2 = + grid(1).context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity().assignment( + new AffinityTopologyVersion(5, 1)); + + checkPartitionsState(assign2, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + + if (!crdAffNode) + ignite(0).cache(DEFAULT_CACHE_NAME); + + checkPartitionsState(assign2, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + + startGrid(6); + + awaitPartitionMapExchange(); + + AffinityAssignment assign3 = + grid(1).context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity().assignment( + new AffinityTopologyVersion(6, 1)); + + checkPartitionsState(assign3, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + } + + /** + * @param backups Number of backups. + * @param crdAffNode If {@code false} cache is not created on coordinator. + * @throws Exception If failed. + */ + private void partitionState2(int backups, boolean crdAffNode) throws Exception { + startGrids(3); + + blockSupplySend(DEFAULT_CACHE_NAME); + + ccfg = cacheConfiguration(DEFAULT_CACHE_NAME, backups); + + if (!crdAffNode) + ccfg.setNodeFilter(new TestCacheNodeExcludingFilter(getTestIgniteInstanceName(0))); + + startGrid(4); + + AffinityAssignment assign0 = + grid(1).context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity().assignment( + new AffinityTopologyVersion(4, 0)); + + checkPartitionsState(assign0, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + + if (!crdAffNode) + ignite(0).cache(DEFAULT_CACHE_NAME); + + checkPartitionsState(assign0, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + + stopBlock(); + + startGrid(5); + + AffinityAssignment assign1 = + grid(1).context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity().assignment( + new AffinityTopologyVersion(5, 1)); + + awaitPartitionMapExchange(); + + checkPartitionsState(assign1, DEFAULT_CACHE_NAME, OWNING); + + checkRebalance(DEFAULT_CACHE_NAME, true); + } + + /** + * @param assign Assignments. + * @param cacheName Cache name. + * @param expState Expected state. + */ + private void checkPartitionsState(AffinityAssignment assign, String cacheName, GridDhtPartitionState expState) { + for (Ignite node : G.allGrids()) + checkNodePartitions(assign, node.cluster().localNode(), cacheName, expState); + } + + /** + * @param assign Assignments. + * @param clusterNode Node. + * @param cacheName Cache name. + * @param expState Expected partitions state. + */ + private void checkNodePartitions(AffinityAssignment assign, + ClusterNode clusterNode, + String cacheName, + GridDhtPartitionState expState) + { + Affinity aff = ignite(0).affinity(cacheName); + + Set nodeParts = new HashSet<>(); + + nodeParts.addAll(assign.primaryPartitions(clusterNode.id())); + nodeParts.addAll(assign.backupPartitions(clusterNode.id())); + + log.info("Test state [node=" + clusterNode.id() + ", parts=" + nodeParts.size() + ", state=" + expState + ']'); + + if (grid(0).context().discovery().cacheAffinityNode(clusterNode, cacheName)) + assertFalse(nodeParts.isEmpty()); + + boolean check = false; + + for (Ignite node : G.allGrids()) { + GridCacheAdapter cache = + ((IgniteKernal)node).context().cache().internalCache(cacheName); + + if (cache != null) { + check = true; + + GridDhtPartitionTopology top = cache.context().topology(); + + GridDhtPartitionMap partsMap = top.partitions(clusterNode.id()); + + for (int p = 0; p < aff.partitions(); p++) { + if (nodeParts.contains(p)) { + assertNotNull(partsMap); + assertEquals(expState, partsMap.get(p)); + } + else { + if (partsMap != null) { + GridDhtPartitionState state = partsMap.get(p); + + assertTrue("Unexpected state: " + state, state == null || state == EVICTED); + } + } + } + } + else { + assertEquals(0, aff.primaryPartitions(((IgniteKernal)node).localNode()).length); + assertEquals(0, aff.backupPartitions(((IgniteKernal)node).localNode()).length); + } + } + + assertTrue(check); + } + + /** + * @param cacheName Cache name. + * @param expDone Expected rebalance finish flag. + */ + private void checkRebalance(String cacheName, boolean expDone) { + for (Ignite node : G.allGrids()) { + IgniteKernal node0 = (IgniteKernal)node; + + GridCacheAdapter cache = node0.context().cache().internalCache(cacheName); + + AffinityTopologyVersion topVer = node0.context().cache().context().exchange().readyAffinityVersion(); + + if (cache != null) + assertEquals(expDone, cache.context().topology().rebalanceFinished(topVer)); + else + node0.context().discovery().cacheAffinityNode(node0.localNode(), cacheName); + } + } + + /** + * @param cacheName Cache name. + */ + private void blockSupplySend(String cacheName) { + for (Ignite node : G.allGrids()) + blockSupplySend(TestRecordingCommunicationSpi.spi(node), cacheName); + } + + /** + * @param spi SPI. + * @param cacheName Cache name. + */ + private void blockSupplySend(TestRecordingCommunicationSpi spi, final String cacheName) { + final int grpId = CU.cacheId(cacheName); + + spi.blockMessages(new IgniteBiPredicate() { + @Override public boolean apply(ClusterNode node, Message msg) { + return msg.getClass().equals(GridDhtPartitionSupplyMessage.class) && + ((GridDhtPartitionSupplyMessage)msg).groupId() == grpId; + } + }); + } + + /** + * + */ + private void stopBlock() { + for (Ignite node : G.allGrids()) + TestRecordingCommunicationSpi.spi(node).stopBlock(); + } + + /** + * @param name Cache name. + * @param backups Backups number. + * @return Cache configuration. + */ + private CacheConfiguration cacheConfiguration(String name, int backups) { + CacheConfiguration ccfg = new CacheConfiguration(name); + + ccfg.setWriteSynchronizationMode(FULL_SYNC); + ccfg.setBackups(backups); + + return ccfg; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/TestCacheNodeExcludingFilter.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/TestCacheNodeExcludingFilter.java new file mode 100644 index 0000000000000..a3f7d270ffe98 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/TestCacheNodeExcludingFilter.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.distributed; + +import java.util.Arrays; +import java.util.Collection; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.lang.IgnitePredicate; + +import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_IGNITE_INSTANCE_NAME; + +/** + * + */ +public class TestCacheNodeExcludingFilter implements IgnitePredicate { + /** */ + private Collection excludeNodes; + + /** + * @param excludeNodes Nodes names. + */ + public TestCacheNodeExcludingFilter(Collection excludeNodes) { + this.excludeNodes = excludeNodes; + } + /** + * @param excludeNodes Nodes names. + */ + public TestCacheNodeExcludingFilter(String... excludeNodes) { + this.excludeNodes = Arrays.asList(excludeNodes); + } + + /** {@inheritDoc} */ + @Override public boolean apply(ClusterNode clusterNode) { + String name = clusterNode.attribute(ATTR_IGNITE_INSTANCE_NAME).toString(); + + return !excludeNodes.contains(name); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsCacheRestoreTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsCacheRestoreTest.java new file mode 100644 index 0000000000000..25626f4bb33c3 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/IgnitePdsCacheRestoreTest.java @@ -0,0 +1,208 @@ +/* + * 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 java.util.Arrays; +import java.util.List; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.MemoryConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +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.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; + +/** + * + */ +public class IgnitePdsCacheRestoreTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private CacheConfiguration[] ccfgs; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER); + + if (ccfgs != null) { + cfg.setCacheConfiguration(ccfgs); + + ccfgs = null; + } + + MemoryConfiguration memCfg = new MemoryConfiguration(); + memCfg.setPageSize(1024); + memCfg.setDefaultMemoryPolicySize(10 * 1024 * 1024); + + cfg.setMemoryConfiguration(memCfg); + + PersistentStoreConfiguration pCfg = new PersistentStoreConfiguration(); + + pCfg.setWalMode(WALMode.LOG_ONLY); + + cfg.setPersistentStoreConfiguration(pCfg); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + GridTestUtils.deleteDbFiles(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + + GridTestUtils.deleteDbFiles(); + + super.afterTest(); + } + + /** + * @throws Exception If failed. + */ + public void testRestoreAndNewCache1() throws Exception { + restoreAndNewCache(false); + } + + /** + * @throws Exception If failed. + */ + public void testRestoreAndNewCache2() throws Exception { + restoreAndNewCache(true); + } + + /** + * @param createNew If {@code true} need cache is added while node is stopped. + * @throws Exception If failed. + */ + private void restoreAndNewCache(boolean createNew) throws Exception { + for (int i = 0; i < 3; i++) { + ccfgs = configurations1(); + + startGrid(i); + } + + ignite(0).active(true); + + IgniteCache cache1 = ignite(2).cache("c1"); + + List keys = primaryKeys(cache1, 10); + + for (Integer key : keys) + cache1.put(key, key); + + stopGrid(2); + + if (createNew) { + // New cache is added when node is stopped. + ignite(0).getOrCreateCaches(Arrays.asList(configurations2())); + } + else { + // New cache is added on node restart. + ccfgs = configurations2(); + } + + startGrid(2); + + cache1 = ignite(2).cache("c1"); + + IgniteCache cache2 = ignite(2).cache("c2"); + + for (Integer key : keys) { + assertEquals(key, cache1.get(key)); + + assertNull(cache2.get(key)); + + cache2.put(key, key); + + assertEquals(key, cache2.get(key)); + } + + List nearKeys = nearKeys(cache1, 10, 0); + + for (Integer key : nearKeys) { + assertNull(cache1.get(key)); + assertNull(cache2.get(key)); + + cache2.put(key, key); + assertEquals(key, cache2.get(key)); + + cache1.put(key, key); + assertEquals(key, cache1.get(key)); + } + + startGrid(3); + + awaitPartitionMapExchange(); + + for (Integer key : nearKeys) { + assertEquals(key, cache2.get(key)); + + assertEquals(key, cache1.get(key)); + } + } + + /** + * @return Configurations set 1. + */ + private CacheConfiguration[] configurations1() { + CacheConfiguration[] ccfgs = new CacheConfiguration[1]; + + ccfgs[0] = cacheConfiguration("c1"); + + return ccfgs; + } + + /** + * @return Configurations set 1. + */ + private CacheConfiguration[] configurations2() { + CacheConfiguration[] ccfgs = new CacheConfiguration[2]; + + ccfgs[0] = cacheConfiguration("c1"); + ccfgs[1] = cacheConfiguration("c2"); + + return ccfgs; + } + + /** + * @param name Cache name. + * @return Cache configuration. + */ + private CacheConfiguration cacheConfiguration(String name) { + CacheConfiguration ccfg = new CacheConfiguration(name); + + ccfg.setWriteSynchronizationMode(FULL_SYNC); + + return ccfg; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java index 90190d064844b..22890dfb766ad 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java @@ -21,6 +21,7 @@ import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeMultiServerTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeTest; import org.apache.ignite.internal.processors.cache.distributed.IgnitePessimisticTxSuspendResumeTest; +import org.apache.ignite.internal.processors.cache.distributed.CachePartitionStateTest; /** * Test suite. @@ -37,6 +38,8 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteOptimisticTxSuspendResumeMultiServerTest.class); suite.addTestSuite(IgnitePessimisticTxSuspendResumeTest.class); + suite.addTestSuite(CachePartitionStateTest.class); + return suite; } } diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java index f09973b6d88f9..5762c0299376f 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java @@ -18,11 +18,13 @@ package org.apache.ignite.testsuites; import junit.framework.TestSuite; +import org.apache.ignite.internal.processors.cache.IgniteClusterActivateDeactivateTestWithPersistence; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsClientNearCachePutGetTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsDynamicCacheTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsSingleNodePutGetPersistenceTest; -import org.apache.ignite.internal.processors.cache.persistence.db.file.IgnitePdsEvictionTest; +import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsCacheRestoreTest; import org.apache.ignite.internal.processors.cache.persistence.db.file.IgnitePdsCheckpointSimulationWithRealCpDisabledTest; +import org.apache.ignite.internal.processors.cache.persistence.db.file.IgnitePdsEvictionTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.BPlusTreePageMemoryImplTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.BPlusTreeReuseListPageMemoryImplTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.MetadataStoragePageMemoryImplTest; @@ -71,6 +73,10 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgnitePdsDynamicCacheTest.class); suite.addTestSuite(IgnitePdsClientNearCachePutGetTest.class); + suite.addTestSuite(IgniteClusterActivateDeactivateTestWithPersistence.class); + + suite.addTestSuite(IgnitePdsCacheRestoreTest.class); + return suite; } } From 270e2b260d3f0519d6f8ab5c2465a84b4c0a6327 Mon Sep 17 00:00:00 2001 From: sboikov Date: Thu, 27 Jul 2017 12:50:37 +0300 Subject: [PATCH 063/145] Tried to simplify GridDhtAtomicCache.updateAllAsyncInternal0. (cherry picked from commit 5704e39) --- .../dht/atomic/DhtAtomicUpdateResult.java | 131 ++++++ .../dht/atomic/GridDhtAtomicCache.java | 380 +++++++----------- .../GridNearAtomicAbstractUpdateFuture.java | 2 +- .../atomic/GridNearAtomicUpdateFuture.java | 4 +- 4 files changed, 282 insertions(+), 235 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/DhtAtomicUpdateResult.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/DhtAtomicUpdateResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/DhtAtomicUpdateResult.java new file mode 100644 index 0000000000000..e7d2b1996a105 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/DhtAtomicUpdateResult.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.cache.distributed.dht.atomic; + +import java.util.ArrayList; +import java.util.Collection; +import org.apache.ignite.internal.processors.cache.GridCacheReturn; +import org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult; +import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy; +import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; +import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.lang.IgniteBiTuple; +import org.jetbrains.annotations.Nullable; + +/** + * + */ +class DhtAtomicUpdateResult { + /** */ + private GridCacheReturn retVal; + + /** */ + private Collection> deleted; + + /** */ + private GridDhtAtomicAbstractUpdateFuture dhtFut; + + /** */ + private IgniteCacheExpiryPolicy expiry; + + /** + * + */ + DhtAtomicUpdateResult() { + // No-op. + } + + /** + * @param retVal Return value. + * @param deleted Deleted entries. + * @param dhtFut DHT update future. + */ + DhtAtomicUpdateResult(GridCacheReturn retVal, + Collection> deleted, + GridDhtAtomicAbstractUpdateFuture dhtFut) { + this.retVal = retVal; + this.deleted = deleted; + this.dhtFut = dhtFut; + } + + /** + * @param expiry Expiry policy. + */ + void expiryPolicy(@Nullable IgniteCacheExpiryPolicy expiry) { + this.expiry = expiry; + } + + /** + * @return Expiry policy. + */ + @Nullable IgniteCacheExpiryPolicy expiryPolicy() { + return expiry; + } + + /** + * @param entry Entry. + * @param updRes Entry update result. + * @param entries All entries. + */ + void addDeleted(GridDhtCacheEntry entry, + GridCacheUpdateAtomicResult updRes, + Collection entries) { + if (updRes.removeVersion() != null) { + if (deleted == null) + deleted = new ArrayList<>(entries.size()); + + deleted.add(F.t(entry, updRes.removeVersion())); + } + } + + /** + * @return Deleted entries. + */ + Collection> deleted() { + return deleted; + } + + /** + * @return DHT future. + */ + GridDhtAtomicAbstractUpdateFuture dhtFuture() { + return dhtFut; + } + + /** + * @param retVal Result for operation. + */ + void returnValue(GridCacheReturn retVal) { + this.retVal = retVal; + } + + /** + * @return Result for invoke operation. + */ + GridCacheReturn returnValue() { + return retVal; + } + + /** + * @param dhtFut DHT future. + */ + void dhtFuture(@Nullable GridDhtAtomicAbstractUpdateFuture dhtFut) { + this.dhtFut = dhtFut; + } +} 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 f1656938a1317..c87184c344d74 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 @@ -1660,12 +1660,12 @@ else if (!skipVals && ctx.config().isStatisticsEnabled()) /** * Executes local update. * - * @param nodeId Node ID. + * @param node Node. * @param req Update request. * @param completionCb Completion callback. */ void updateAllAsyncInternal( - final UUID nodeId, + final ClusterNode node, final GridNearAtomicAbstractUpdateRequest req, final UpdateReplyClosure completionCb ) { @@ -1680,12 +1680,12 @@ void updateAllAsyncInternal( return; } catch (IgniteCheckedException e) { - onForceKeysError(nodeId, req, completionCb, e); + onForceKeysError(node.id(), req, completionCb, e); return; } - updateAllAsyncInternal0(nodeId, req, completionCb); + updateAllAsyncInternal0(node, req, completionCb); } else { forceFut.listen(new CI1>() { @@ -1697,12 +1697,12 @@ void updateAllAsyncInternal( return; } catch (IgniteCheckedException e) { - onForceKeysError(nodeId, req, completionCb, e); + onForceKeysError(node.id(), req, completionCb, e); return; } - updateAllAsyncInternal0(nodeId, req, completionCb); + updateAllAsyncInternal0(node, req, completionCb); } }); } @@ -1734,26 +1734,17 @@ private void onForceKeysError(final UUID nodeId, /** * Executes local update after preloader fetched values. * - * @param nodeId Node ID. + * @param node Node. * @param req Update request. * @param completionCb Completion callback. */ private void updateAllAsyncInternal0( - UUID nodeId, + ClusterNode node, GridNearAtomicAbstractUpdateRequest req, UpdateReplyClosure completionCb ) { - ClusterNode node = ctx.discovery().node(nodeId); - - if (node == null) { - U.warn(msgLog, "Skip near update request, node originated update request left [" + - "futId=" + req.futureId() + ", node=" + nodeId + ']'); - - return; - } - GridNearAtomicUpdateResponse res = new GridNearAtomicUpdateResponse(ctx.cacheId(), - nodeId, + node.id(), req.futureId(), req.partition(), false, @@ -1765,8 +1756,6 @@ private void updateAllAsyncInternal0( boolean remap = false; - String taskName = ctx.kernalContext().task().resolveTaskName(req.taskNameHash()); - IgniteCacheExpiryPolicy expiry = null; ctx.shared().database().checkpointReadLock(); @@ -1797,97 +1786,11 @@ private void updateAllAsyncInternal0( // Do not check topology version if topology was locked on near node by // external transaction or explicit lock. if (req.topologyLocked() || !needRemap(req.topologyVersion(), top.topologyVersion())) { - boolean hasNear = req.nearCache(); - - // Assign next version for update inside entries lock. - GridCacheVersion ver = ctx.versions().next(top.topologyVersion()); - - if (hasNear) - res.nearVersion(ver); - - if (msgLog.isDebugEnabled()) { - msgLog.debug("Assigned update version [futId=" + req.futureId() + - ", writeVer=" + ver + ']'); - } - - assert ver != null : "Got null version for update request: " + req; - - boolean sndPrevVal = !top.rebalanceFinished(req.topologyVersion()); - - dhtFut = createDhtFuture(ver, req); - - expiry = expiryPolicy(req.expiry()); - - GridCacheReturn retVal = null; - - if (req.size() > 1 && // Several keys ... - writeThrough() && !req.skipStore() && // and store is enabled ... - !ctx.store().isLocal() && // and this is not local store ... - // (conflict resolver should be used for local store) - !ctx.dr().receiveEnabled() // and no DR. - ) { - // This method can only be used when there are no replicated entries in the batch. - UpdateBatchResult updRes = updateWithBatch(node, - hasNear, - req, - res, - locked, - ver, - dhtFut, - ctx.isDrEnabled(), - taskName, - expiry, - sndPrevVal); - - deleted = updRes.deleted(); - dhtFut = updRes.dhtFuture(); - - if (req.operation() == TRANSFORM) - retVal = updRes.invokeResults(); - } - else { - UpdateSingleResult updRes = updateSingle(node, - hasNear, - req, - res, - locked, - ver, - dhtFut, - ctx.isDrEnabled(), - taskName, - expiry, - sndPrevVal); - - retVal = updRes.returnValue(); - deleted = updRes.deleted(); - dhtFut = updRes.dhtFuture(); - } + DhtAtomicUpdateResult updRes = update(node, locked, req, res); - if (retVal == null) - retVal = new GridCacheReturn(ctx, node.isLocal(), true, null, true); - - res.returnValue(retVal); - - if (dhtFut != null) { - if (req.writeSynchronizationMode() == PRIMARY_SYNC - // To avoid deadlock disable back-pressure for sender data node. - && !ctx.discovery().cacheAffinityNode(node, ctx.name()) - && !dhtFut.isDone()) { - final IgniteRunnable tracker = GridNioBackPressureControl.threadTracker(); - - if (tracker != null && tracker instanceof GridNioMessageTracker) { - ((GridNioMessageTracker)tracker).onMessageReceived(); - - dhtFut.listen(new IgniteInClosure>() { - @Override public void apply(IgniteInternalFuture fut) { - ((GridNioMessageTracker)tracker).onMessageProcessed(); - } - }); - } - } - - ctx.mvcc().addAtomicFuture(dhtFut.id(), dhtFut); - } + dhtFut = updRes.dhtFuture(); + deleted = updRes.deleted(); + expiry = updRes.expiryPolicy(); } else { // Should remap all keys. @@ -1955,9 +1858,10 @@ private void updateAllAsyncInternal0( completionCb.apply(req, res); } - else + else { if (dhtFut != null) dhtFut.map(node, res.returnValue(), res, completionCb); + } if (req.writeSynchronizationMode() != FULL_ASYNC) req.cleanup(!node.isLocal()); @@ -1965,6 +1869,122 @@ private void updateAllAsyncInternal0( sendTtlUpdateRequest(expiry); } + /** + * @param node Node. + * @param locked Entries. + * @param req Request. + * @param res Response. + * @return Operation result. + * @throws GridCacheEntryRemovedException If got obsolete entry. + */ + private DhtAtomicUpdateResult update( + ClusterNode node, + List locked, + GridNearAtomicAbstractUpdateRequest req, + GridNearAtomicUpdateResponse res) + throws GridCacheEntryRemovedException + { + GridDhtPartitionTopology top = topology(); + + String taskName = ctx.kernalContext().task().resolveTaskName(req.taskNameHash()); + + boolean hasNear = req.nearCache(); + + // Assign next version for update inside entries lock. + GridCacheVersion ver = ctx.versions().next(top.topologyVersion()); + + if (hasNear) + res.nearVersion(ver); + + if (msgLog.isDebugEnabled()) { + msgLog.debug("Assigned update version [futId=" + req.futureId() + + ", writeVer=" + ver + ']'); + } + + assert ver != null : "Got null version for update request: " + req; + + boolean sndPrevVal = !top.rebalanceFinished(req.topologyVersion()); + + GridDhtAtomicAbstractUpdateFuture dhtFut = createDhtFuture(ver, req); + + IgniteCacheExpiryPolicy expiry = expiryPolicy(req.expiry()); + + GridCacheReturn retVal = null; + + DhtAtomicUpdateResult updRes; + + if (req.size() > 1 && // Several keys ... + writeThrough() && !req.skipStore() && // and store is enabled ... + !ctx.store().isLocal() && // and this is not local store ... + // (conflict resolver should be used for local store) + !ctx.dr().receiveEnabled() // and no DR. + ) { + // This method can only be used when there are no replicated entries in the batch. + updRes = updateWithBatch(node, + hasNear, + req, + res, + locked, + ver, + dhtFut, + ctx.isDrEnabled(), + taskName, + expiry, + sndPrevVal); + + dhtFut = updRes.dhtFuture(); + + if (req.operation() == TRANSFORM) + retVal = updRes.returnValue(); + } + else { + updRes = updateSingle(node, + hasNear, + req, + res, + locked, + ver, + dhtFut, + ctx.isDrEnabled(), + taskName, + expiry, + sndPrevVal); + + retVal = updRes.returnValue(); + dhtFut = updRes.dhtFuture(); + } + + if (retVal == null) + retVal = new GridCacheReturn(ctx, node.isLocal(), true, null, true); + + res.returnValue(retVal); + + if (dhtFut != null) { + if (req.writeSynchronizationMode() == PRIMARY_SYNC + // To avoid deadlock disable back-pressure for sender data node. + && !ctx.discovery().cacheAffinityNode(node, ctx.name()) + && !dhtFut.isDone()) { + final IgniteRunnable tracker = GridNioBackPressureControl.threadTracker(); + + if (tracker != null && tracker instanceof GridNioMessageTracker) { + ((GridNioMessageTracker)tracker).onMessageReceived(); + + dhtFut.listen(new IgniteInClosure>() { + @Override public void apply(IgniteInternalFuture fut) { + ((GridNioMessageTracker)tracker).onMessageProcessed(); + } + }); + } + } + + ctx.mvcc().addAtomicFuture(dhtFut.id(), dhtFut); + } + + updRes.expiryPolicy(expiry); + + return updRes; + } + /** * Updates locked entries using batched write-through. * @@ -1983,7 +2003,7 @@ private void updateAllAsyncInternal0( * @throws GridCacheEntryRemovedException Should not be thrown. */ @SuppressWarnings("unchecked") - private UpdateBatchResult updateWithBatch( + private DhtAtomicUpdateResult updateWithBatch( final ClusterNode node, final boolean hasNear, final GridNearAtomicAbstractUpdateRequest req, @@ -2006,7 +2026,7 @@ private UpdateBatchResult updateWithBatch( catch (IgniteCheckedException e) { res.addFailedKeys(req.keys(), e); - return new UpdateBatchResult(); + return new DhtAtomicUpdateResult(); } } @@ -2020,7 +2040,7 @@ private UpdateBatchResult updateWithBatch( List writeVals = null; - UpdateBatchResult updRes = new UpdateBatchResult(); + DhtAtomicUpdateResult updRes = new DhtAtomicUpdateResult(); List filtered = new ArrayList<>(size); @@ -2319,7 +2339,7 @@ else if (op == UPDATE) { updRes.dhtFuture(dhtFut); - updRes.invokeResult(invokeRes); + updRes.returnValue(invokeRes); return updRes; } @@ -2392,7 +2412,7 @@ private void reloadIfNeeded(final List entries) throws Ignite * @return Return value. * @throws GridCacheEntryRemovedException Should be never thrown. */ - private UpdateSingleResult updateSingle( + private DhtAtomicUpdateResult updateSingle( ClusterNode nearNode, boolean hasNear, GridNearAtomicAbstractUpdateRequest req, @@ -2579,7 +2599,7 @@ else if (GridDhtCacheEntry.ReaderId.contains(readers, nearNode.id())) { } } - return new UpdateSingleResult(retVal, deleted, dhtFut); + return new DhtAtomicUpdateResult(retVal, deleted, dhtFut); } /** @@ -2617,7 +2637,7 @@ else if (GridDhtCacheEntry.ReaderId.contains(readers, nearNode.id())) { final GridNearAtomicAbstractUpdateRequest req, final GridNearAtomicUpdateResponse res, final boolean replicate, - final UpdateBatchResult batchRes, + final DhtAtomicUpdateResult batchRes, final String taskName, @Nullable final IgniteCacheExpiryPolicy expiry, final boolean sndPrevVal @@ -3062,7 +3082,16 @@ private void processNearAtomicUpdateRequest(UUID nodeId, GridNearAtomicAbstractU ", node=" + nodeId + ']'); } - updateAllAsyncInternal(nodeId, req, updateReplyClos); + ClusterNode node = ctx.discovery().node(nodeId); + + if (node == null) { + U.warn(msgLog, "Skip near update request, node originated update request left [" + + "futId=" + req.futureId() + ", node=" + nodeId + ']'); + + return; + } + + updateAllAsyncInternal(node, req, updateReplyClos); } /** @@ -3542,119 +3571,6 @@ private void sendNearUpdateReply(UUID nodeId, GridNearAtomicUpdateResponse res) return S.toString(GridDhtAtomicCache.class, this, super.toString()); } - /** - * Result of {@link GridDhtAtomicCache#updateSingle} execution. - */ - private static class UpdateSingleResult { - /** */ - private final GridCacheReturn retVal; - - /** */ - private final Collection> deleted; - - /** */ - private final GridDhtAtomicAbstractUpdateFuture dhtFut; - - /** - * @param retVal Return value. - * @param deleted Deleted entries. - * @param dhtFut DHT future. - */ - private UpdateSingleResult(GridCacheReturn retVal, - Collection> deleted, - GridDhtAtomicAbstractUpdateFuture dhtFut) { - this.retVal = retVal; - this.deleted = deleted; - this.dhtFut = dhtFut; - } - - /** - * @return Return value. - */ - private GridCacheReturn returnValue() { - return retVal; - } - - /** - * @return Deleted entries. - */ - private Collection> deleted() { - return deleted; - } - - /** - * @return DHT future. - */ - public GridDhtAtomicAbstractUpdateFuture dhtFuture() { - return dhtFut; - } - } - - /** - * Result of {@link GridDhtAtomicCache#updateWithBatch} execution. - */ - private static class UpdateBatchResult { - /** */ - private Collection> deleted; - - /** */ - private GridDhtAtomicAbstractUpdateFuture dhtFut; - - /** */ - private GridCacheReturn invokeRes; - - /** - * @param entry Entry. - * @param updRes Entry update result. - * @param entries All entries. - */ - private void addDeleted(GridDhtCacheEntry entry, - GridCacheUpdateAtomicResult updRes, - Collection entries) { - if (updRes.removeVersion() != null) { - if (deleted == null) - deleted = new ArrayList<>(entries.size()); - - deleted.add(F.t(entry, updRes.removeVersion())); - } - } - - /** - * @return Deleted entries. - */ - private Collection> deleted() { - return deleted; - } - - /** - * @return DHT future. - */ - public GridDhtAtomicAbstractUpdateFuture dhtFuture() { - return dhtFut; - } - - /** - * @param invokeRes Result for invoke operation. - */ - private void invokeResult(GridCacheReturn invokeRes) { - this.invokeRes = invokeRes; - } - - /** - * @return Result for invoke operation. - */ - GridCacheReturn invokeResults() { - return invokeRes; - } - - /** - * @param dhtFut DHT future. - */ - private void dhtFuture(@Nullable GridDhtAtomicAbstractUpdateFuture dhtFut) { - this.dhtFut = dhtFut; - } - } - /** * */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateFuture.java index 6fe96a437a56e..983b18ac38ba0 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateFuture.java @@ -296,7 +296,7 @@ final boolean storeFuture() { */ final void sendSingleRequest(UUID nodeId, GridNearAtomicAbstractUpdateRequest req) { if (cctx.localNodeId().equals(nodeId)) { - cache.updateAllAsyncInternal(nodeId, req, + cache.updateAllAsyncInternal(cctx.localNode(), req, new GridDhtAtomicCache.UpdateReplyClosure() { @Override public void apply(GridNearAtomicAbstractUpdateRequest req, GridNearAtomicUpdateResponse res) { if (syncMode != FULL_ASYNC) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java index 138645d86bcf6..930012ab1cb85 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java @@ -706,7 +706,7 @@ private void sendUpdateRequests(Map mappings) { } if (locUpdate != null) { - cache.updateAllAsyncInternal(cctx.localNodeId(), locUpdate, + cache.updateAllAsyncInternal(cctx.localNode(), locUpdate, new GridDhtAtomicCache.UpdateReplyClosure() { @Override public void apply(GridNearAtomicAbstractUpdateRequest req, GridNearAtomicUpdateResponse res) { if (syncMode != FULL_ASYNC) @@ -730,7 +730,7 @@ else if (res.remapTopologyVersion() != null) * @param topVer Topology version. * @param remapKeys Keys to remap. */ - void map(AffinityTopologyVersion topVer, @Nullable Collection remapKeys) { + private void map(AffinityTopologyVersion topVer, @Nullable Collection remapKeys) { Collection topNodes = CU.affinityNodes(cctx, topVer); if (F.isEmpty(topNodes)) { From f34ef163359bde62865246856a705e09b92c23e2 Mon Sep 17 00:00:00 2001 From: sboikov Date: Thu, 27 Jul 2017 13:21:14 +0300 Subject: [PATCH 064/145] Removed unused methods from IgniteThreadPoolExecutor. (cherry picked from commit 2574beb) --- .../apache/ignite/internal/IgnitionEx.java | 3 +- .../internal/processors/igfs/IgfsImpl.java | 2 +- .../thread/IgniteThreadPoolExecutor.java | 144 +----------------- .../loadtests/colocation/GridTestMain.java | 45 ------ .../GridMarshallerResourceBean.java | 5 +- ...GridThreadPoolExecutorServiceSelfTest.java | 7 +- 6 files changed, 10 insertions(+), 196 deletions(-) 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 d219333ea958c..1139ec60860d0 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 @@ -1776,8 +1776,7 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getIgfsThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, new LinkedBlockingQueue(), - new IgfsThreadFactory(cfg.getIgniteInstanceName(), "igfs"), - null /* Abort policy will be used. */); + new IgfsThreadFactory(cfg.getIgniteInstanceName(), "igfs")); igfsExecSvc.allowCoreThreadTimeOut(true); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java index 7eb61d10366b3..5808e7cc4a620 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java @@ -247,7 +247,7 @@ public final class IgfsImpl implements IgfsEx { } dualPool = secondaryFs != null ? new IgniteThreadPoolExecutor(4, Integer.MAX_VALUE, 5000L, - new SynchronousQueue(), new IgfsThreadFactory(cfg.getName()), null) : null; + new SynchronousQueue(), new IgfsThreadFactory(cfg.getName())) : null; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java index 8002aaa5d1069..639ef9438a669 100644 --- a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java +++ b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java @@ -19,150 +19,14 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import org.jetbrains.annotations.Nullable; /** * An {@link ExecutorService} that executes submitted tasks using pooled grid threads. */ public class IgniteThreadPoolExecutor extends ThreadPoolExecutor { - /** Default core pool size (value is {@code 100}). */ - public static final int DFLT_CORE_POOL_SIZE = 100; - - /** - * Creates a new service with default initial parameters. - * Default values are: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    NameDefault Value
    Core Pool Size{@code 100} (see {@link #DFLT_CORE_POOL_SIZE}).
    Maximum Pool SizeNone, is it is not used for unbounded queues.
    Keep alive timeNo limit (see {@link Long#MAX_VALUE}).
    Blocking Queue (see {@link BlockingQueue}).Unbounded linked blocking queue (see {@link LinkedBlockingDeque}).
    - */ - public IgniteThreadPoolExecutor() { - this( - DFLT_CORE_POOL_SIZE, - DFLT_CORE_POOL_SIZE, - 0, - new LinkedBlockingDeque(), - new IgniteThreadFactory(null), - null - ); - } - - /** - * Creates a new service with the given initial parameters. - * - * @param corePoolSize The number of threads to keep in the pool, even if they are idle. - * @param maxPoolSize The maximum number of threads to allow in the pool. - * @param keepAliveTime When the number of threads is greater than the core, this is the maximum time - * that excess idle threads will wait for new tasks before terminating. - * @param workQueue The queue to use for holding tasks before they are executed. This queue will hold only - * runnable tasks submitted by the {@link #execute(Runnable)} method. - */ - public IgniteThreadPoolExecutor( - int corePoolSize, - int maxPoolSize, - long keepAliveTime, - BlockingQueue workQueue) { - this( - corePoolSize, - maxPoolSize, - keepAliveTime, - workQueue, - new IgniteThreadFactory(null), - null - ); - } - - /** - * Creates a new service with the given initial parameters. - * - * @param corePoolSize The number of threads to keep in the pool, even if they are idle. - * @param maxPoolSize The maximum number of threads to allow in the pool. - * @param keepAliveTime When the number of threads is greater than the core, this is the maximum time - * that excess idle threads will wait for new tasks before terminating. - * @param workQ The queue to use for holding tasks before they are executed. This queue will hold only the - * runnable tasks submitted by the {@link #execute(Runnable)} method. - * @param hnd Optional handler to use when execution is blocked because the thread bounds and queue - * capacities are reached. If {@code null} then {@code AbortPolicy} - * handler is used by default. - */ - public IgniteThreadPoolExecutor( - int corePoolSize, - int maxPoolSize, - long keepAliveTime, - BlockingQueue workQ, - RejectedExecutionHandler hnd) { - this( - corePoolSize, - maxPoolSize, - keepAliveTime, - workQ, - new IgniteThreadFactory(null), - hnd - ); - } - - /** - * Creates a new service with default initial parameters. - * Default values are: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    NameDefault Value
    Core Pool Size{@code 100} (see {@link #DFLT_CORE_POOL_SIZE}).
    Maximum Pool SizeNone, is it is not used for unbounded queues.
    Keep alive timeNo limit (see {@link Long#MAX_VALUE}).
    Blocking Queue (see {@link BlockingQueue}).Unbounded linked blocking queue (see {@link LinkedBlockingDeque}).
    - * - * @param igniteInstanceName Name of the grid. - */ - public IgniteThreadPoolExecutor(String igniteInstanceName) { - this( - DFLT_CORE_POOL_SIZE, - DFLT_CORE_POOL_SIZE, - 0, - new LinkedBlockingDeque(), - new IgniteThreadFactory(igniteInstanceName), - null - ); - } - /** * Creates a new service with the given initial parameters. * @@ -202,17 +66,13 @@ public IgniteThreadPoolExecutor( * @param workQ The queue to use for holding tasks before they are executed. This queue will hold only the * runnable tasks submitted by the {@link #execute(Runnable)} method. * @param threadFactory Thread factory. - * @param hnd Optional handler to use when execution is blocked because the thread bounds and queue - * capacities are reached. If {@code null} then {@code AbortPolicy} - * handler is used by default. */ public IgniteThreadPoolExecutor( int corePoolSize, int maxPoolSize, long keepAliveTime, BlockingQueue workQ, - ThreadFactory threadFactory, - @Nullable RejectedExecutionHandler hnd) { + ThreadFactory threadFactory) { super( corePoolSize, maxPoolSize, @@ -220,7 +80,7 @@ public IgniteThreadPoolExecutor( TimeUnit.MILLISECONDS, workQ, threadFactory, - hnd == null ? new AbortPolicy() : hnd + new AbortPolicy() ); } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/loadtests/colocation/GridTestMain.java b/modules/core/src/test/java/org/apache/ignite/loadtests/colocation/GridTestMain.java index bf345452e3ecb..aa0764e2a35fd 100644 --- a/modules/core/src/test/java/org/apache/ignite/loadtests/colocation/GridTestMain.java +++ b/modules/core/src/test/java/org/apache/ignite/loadtests/colocation/GridTestMain.java @@ -118,51 +118,6 @@ private static void colocateJobs() throws Exception { X.println("Executed " + GridTestConstants.ENTRY_COUNT + " computations in " + (end - start) + "ms."); } - /** - * - */ - private static void localPoolRun() { - X.println("Local thread pool run..."); - - ExecutorService exe = new IgniteThreadPoolExecutor(400, 400, 0, new ArrayBlockingQueue(400) { - @Override public boolean offer(Runnable runnable) { - try { - put(runnable); - } - catch (InterruptedException e) { - e.printStackTrace(); - } - - return true; - } - }); - - long start = System.currentTimeMillis(); - - final IgniteCache cache = G.ignite().cache("partitioned"); - - // Collocate computations and data. - for (long i = 0; i < GridTestConstants.ENTRY_COUNT; i++) { - final long key = i; - - exe.submit(new Runnable() { - @Override public void run() { - Long val = cache.localPeek(new GridTestKey(key), CachePeekMode.ONHEAP); - - if (val == null || val != key) - throw new RuntimeException("Invalid value found [key=" + key + ", val=" + val + ']'); - } - }); - - if (i % 10000 == 0) - X.println("Executed jobs: " + i); - } - - long end = System.currentTimeMillis(); - - X.println("Executed " + GridTestConstants.ENTRY_COUNT + " computations in " + (end - start) + "ms."); - } - /** * Load cache from data store. Also take a look at * {@link GridTestCacheStore#loadAll} method. diff --git a/modules/core/src/test/java/org/apache/ignite/marshaller/GridMarshallerResourceBean.java b/modules/core/src/test/java/org/apache/ignite/marshaller/GridMarshallerResourceBean.java index a0bdf7e92ae73..0887879b52f04 100644 --- a/modules/core/src/test/java/org/apache/ignite/marshaller/GridMarshallerResourceBean.java +++ b/modules/core/src/test/java/org/apache/ignite/marshaller/GridMarshallerResourceBean.java @@ -21,7 +21,7 @@ import java.lang.management.ManagementFactory; import java.util.Collection; import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Executors; import javax.management.MBeanServer; import org.apache.ignite.GridTestJobContext; import org.apache.ignite.GridTestTaskSession; @@ -74,7 +74,7 @@ class GridMarshallerResourceBean implements Serializable { marshaller = new JdkMarshaller(); mbeanSrv = ManagementFactory.getPlatformMBeanServer(); ses = new GridTestTaskSession(); - execSvc = new IgniteThreadPoolExecutor(1, 1, 0, new LinkedBlockingQueue()); + execSvc = Executors.newSingleThreadExecutor(); appCtx = new GenericApplicationContext(); jobCtx = new GridTestJobContext(); balancer = new LoadBalancer(); @@ -98,6 +98,7 @@ public void checkNullResources() { private static class LoadBalancer extends GridLoadBalancerAdapter { /** */ public LoadBalancer() { + // No-op. } /** {@inheritDoc} */ diff --git a/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java b/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java index bad42b0a9be41..3948f6a620bc6 100644 --- a/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java @@ -85,7 +85,7 @@ public void testSingleGridThreadExecutor() throws Exception { * @throws ExecutionException If failed. */ public void testGridThreadPoolExecutor() throws Exception { - IgniteThreadPoolExecutor exec = new IgniteThreadPoolExecutor(1, 1, 0, new LinkedBlockingQueue()); + IgniteThreadPoolExecutor exec = new IgniteThreadPoolExecutor("", "", 1, 1, 0, new LinkedBlockingQueue()); exec.submit(new InterruptingRunnable()).get(); @@ -101,7 +101,7 @@ public void testGridThreadPoolExecutor() throws Exception { * @throws ExecutionException If failed. */ public void testGridThreadPoolExecutorRejection() throws Exception { - IgniteThreadPoolExecutor exec = new IgniteThreadPoolExecutor(1, 1, 0, new LinkedBlockingQueue()); + IgniteThreadPoolExecutor exec = new IgniteThreadPoolExecutor("", "", 1, 1, 0, new LinkedBlockingQueue()); for (int i = 0; i < 10; i++) exec.submit(new TestRunnable()); @@ -141,8 +141,7 @@ public void testGridThreadPoolExecutorPrestartCoreThreads() throws Exception { } }); } - }, - null + } ); assert exec.prestartAllCoreThreads() == THREAD_CNT; From 4bfe699e79fcf34afd62a9bb3a356af2d89d9cc9 Mon Sep 17 00:00:00 2001 From: sboikov Date: Thu, 27 Jul 2017 13:57:11 +0300 Subject: [PATCH 065/145] Added related GridIoPolicy in IgniteThread. (cherry picked from commit 3a3650f) --- .../apache/ignite/internal/IgnitionEx.java | 34 ++++++++++++------ .../managers/communication/GridIoPolicy.java | 3 ++ .../service/GridServiceProcessor.java | 2 +- .../util/StripedCompositeReadWriteLock.java | 6 ++-- .../ignite/internal/util/StripedExecutor.java | 4 ++- .../apache/ignite/thread/IgniteThread.java | 35 +++++++++---------- .../ignite/thread/IgniteThreadFactory.java | 15 +++++--- .../thread/IgniteThreadPoolExecutor.java | 33 ++++++++++++++++- ...GridThreadPoolExecutorServiceSelfTest.java | 2 +- 9 files changed, 95 insertions(+), 39 deletions(-) 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 1139ec60860d0..23baeb3781e62 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 @@ -64,6 +64,7 @@ import org.apache.ignite.configuration.MemoryConfiguration; import org.apache.ignite.configuration.TransactionConfiguration; import org.apache.ignite.internal.binary.BinaryMarshaller; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; import org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor; import org.apache.ignite.internal.processors.igfs.IgfsThreadFactory; import org.apache.ignite.internal.processors.igfs.IgfsUtils; @@ -1694,7 +1695,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getPublicThreadPoolSize(), cfg.getPublicThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.PUBLIC_POOL); execSvc.allowCoreThreadTimeOut(true); @@ -1706,7 +1708,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getServiceThreadPoolSize(), cfg.getServiceThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.SERVICE_POOL); svcExecSvc.allowCoreThreadTimeOut(true); @@ -1718,7 +1721,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getSystemThreadPoolSize(), cfg.getSystemThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.SYSTEM_POOL); sysExecSvc.allowCoreThreadTimeOut(true); @@ -1738,7 +1742,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getManagementThreadPoolSize(), cfg.getManagementThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.MANAGEMENT_POOL); mgmtExecSvc.allowCoreThreadTimeOut(true); @@ -1753,7 +1758,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getPeerClassLoadingThreadPoolSize(), cfg.getPeerClassLoadingThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.P2P_POOL); p2pExecSvc.allowCoreThreadTimeOut(true); @@ -1764,7 +1770,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getDataStreamerThreadPoolSize(), cfg.getDataStreamerThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.DATA_STREAMER_POOL); dataStreamerExecSvc.allowCoreThreadTimeOut(true); @@ -1811,7 +1818,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { myCfg.getUtilityCacheThreadPoolSize(), myCfg.getUtilityCacheThreadPoolSize(), myCfg.getUtilityCacheKeepAliveTime(), - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.UTILITY_CACHE_POOL); utilityCacheExecSvc.allowCoreThreadTimeOut(true); @@ -1821,7 +1829,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { 1, 1, DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.AFFINITY_POOL); affExecSvc.allowCoreThreadTimeOut(true); @@ -1834,7 +1843,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cpus, cpus * 2, 3000L, - new LinkedBlockingQueue(1000) + new LinkedBlockingQueue(1000), + GridIoPolicy.IDX_POOL ); } @@ -1846,7 +1856,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { cfg.getQueryThreadPoolSize(), cfg.getQueryThreadPoolSize(), DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.QUERY_POOL); qryExecSvc.allowCoreThreadTimeOut(true); @@ -1856,7 +1867,8 @@ private void start0(GridStartContext startCtx) throws IgniteCheckedException { 2, 2, DFLT_THREAD_KEEP_ALIVE_TIME, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + GridIoPolicy.SCHEMA_POOL); schemaExecSvc.allowCoreThreadTimeOut(true); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java index 13bc4c470603b..3f31f92088315 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoPolicy.java @@ -22,6 +22,9 @@ * message processing by the communication manager. */ public class GridIoPolicy { + /** */ + public static final byte UNDEFINED = -1; + /** Public execution pool. */ public static final byte PUBLIC_POOL = 0; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java index db632ec7b1cf8..46fcfea8ef509 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProcessor.java @@ -153,7 +153,7 @@ public class GridServiceProcessor extends GridProcessorAdapter implements Ignite private volatile GridSpinBusyLock busyLock = new GridSpinBusyLock(); /** Thread factory. */ - private ThreadFactory threadFactory = new IgniteThreadFactory(ctx.igniteInstanceName()); + private ThreadFactory threadFactory = new IgniteThreadFactory(ctx.igniteInstanceName(), "service"); /** Thread local for service name. */ private ThreadLocal svcName = new ThreadLocal<>(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/StripedCompositeReadWriteLock.java b/modules/core/src/main/java/org/apache/ignite/internal/util/StripedCompositeReadWriteLock.java index e215663dbcc71..18ef06cdd5401 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/StripedCompositeReadWriteLock.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/StripedCompositeReadWriteLock.java @@ -67,8 +67,10 @@ public StripedCompositeReadWriteLock(int concurrencyLvl) { @NotNull @Override public Lock readLock() { int idx; - if (Thread.currentThread() instanceof IgniteThread) { - IgniteThread igniteThread = (IgniteThread)Thread.currentThread(); + Thread curThread = Thread.currentThread(); + + if (curThread instanceof IgniteThread) { + IgniteThread igniteThread = (IgniteThread)curThread; idx = igniteThread.compositeRwLockIndex(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/StripedExecutor.java b/modules/core/src/main/java/org/apache/ignite/internal/util/StripedExecutor.java index 6c85b32c89734..6d5dc71335d62 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/StripedExecutor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/StripedExecutor.java @@ -35,6 +35,7 @@ import java.util.concurrent.locks.LockSupport; import org.apache.ignite.IgniteInterruptedException; import org.apache.ignite.IgniteLogger; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; @@ -439,7 +440,8 @@ void start() { poolName + "-stripe-" + idx, this, IgniteThread.GRP_IDX_UNASSIGNED, - idx); + idx, + GridIoPolicy.UNDEFINED); thread.start(); } diff --git a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java index 6005ac9a17f8c..c814625d23fb3 100644 --- a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java +++ b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java @@ -18,6 +18,7 @@ package org.apache.ignite.thread; import java.util.concurrent.atomic.AtomicLong; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.worker.GridWorker; @@ -52,13 +53,16 @@ public class IgniteThread extends Thread { /** */ private final int stripe; + /** */ + private final byte plc; + /** * Creates thread with given worker. * * @param worker Runnable to create thread with. */ public IgniteThread(GridWorker worker) { - this(DFLT_GRP, worker.igniteInstanceName(), worker.name(), worker, GRP_IDX_UNASSIGNED, -1); + this(worker.igniteInstanceName(), worker.name(), worker, GRP_IDX_UNASSIGNED, -1, GridIoPolicy.UNDEFINED); } /** @@ -69,41 +73,28 @@ public IgniteThread(GridWorker worker) { * @param r Runnable to execute. */ public IgniteThread(String igniteInstanceName, String threadName, Runnable r) { - this(igniteInstanceName, threadName, r, GRP_IDX_UNASSIGNED, -1); - } - - /** - * Creates grid thread with given name for a given Ignite instance. - * - * @param igniteInstanceName Name of the Ignite instance this thread is created for. - * @param threadName Name of thread. - * @param r Runnable to execute. - * @param grpIdx Index within a group. - * @param stripe Non-negative stripe number if this thread is striped pool thread. - */ - public IgniteThread(String igniteInstanceName, String threadName, Runnable r, int grpIdx, int stripe) { - this(DFLT_GRP, igniteInstanceName, threadName, r, grpIdx, stripe); + this(igniteInstanceName, threadName, r, GRP_IDX_UNASSIGNED, -1, GridIoPolicy.UNDEFINED); } /** * Creates grid thread with given name for a given Ignite instance with specified * thread group. * - * @param grp Thread group. * @param igniteInstanceName Name of the Ignite instance this thread is created for. * @param threadName Name of thread. * @param r Runnable to execute. * @param grpIdx Thread index within a group. * @param stripe Non-negative stripe number if this thread is striped pool thread. */ - public IgniteThread(ThreadGroup grp, String igniteInstanceName, String threadName, Runnable r, int grpIdx, int stripe) { - super(grp, r, createName(cntr.incrementAndGet(), threadName, igniteInstanceName)); + public IgniteThread(String igniteInstanceName, String threadName, Runnable r, int grpIdx, int stripe, byte plc) { + super(DFLT_GRP, r, createName(cntr.incrementAndGet(), threadName, igniteInstanceName)); A.ensure(grpIdx >= -1, "grpIdx >= -1"); this.igniteInstanceName = igniteInstanceName; this.compositeRwLockIdx = grpIdx; this.stripe = stripe; + this.plc = plc; } /** @@ -117,6 +108,14 @@ protected IgniteThread(String igniteInstanceName, ThreadGroup threadGrp, String this.igniteInstanceName = igniteInstanceName; this.compositeRwLockIdx = GRP_IDX_UNASSIGNED; this.stripe = -1; + this.plc = GridIoPolicy.UNDEFINED; + } + + /** + * @return Related {@link GridIoPolicy} for internal Ignite pools. + */ + public byte policy() { + return plc; } /** diff --git a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadFactory.java b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadFactory.java index d2f0b159aaf24..062c973ba1220 100644 --- a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadFactory.java +++ b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadFactory.java @@ -20,6 +20,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; import org.apache.ignite.internal.util.typedef.internal.S; import org.jetbrains.annotations.NotNull; @@ -37,14 +38,18 @@ public class IgniteThreadFactory implements ThreadFactory { /** Index generator for threads. */ private final AtomicInteger idxGen = new AtomicInteger(); + /** */ + private final byte plc; + /** * Constructs new thread factory for given grid. All threads will belong * to the same default thread group. * * @param igniteInstanceName Ignite instance name. + * @param threadName Thread name. */ - public IgniteThreadFactory(String igniteInstanceName) { - this(igniteInstanceName, "ignite"); + public IgniteThreadFactory(String igniteInstanceName, String threadName) { + this(igniteInstanceName, threadName, GridIoPolicy.UNDEFINED); } /** @@ -53,15 +58,17 @@ public IgniteThreadFactory(String igniteInstanceName) { * * @param igniteInstanceName Ignite instance name. * @param threadName Thread name. + * @param plc {@link GridIoPolicy} for thread pool. */ - public IgniteThreadFactory(String igniteInstanceName, String threadName) { + public IgniteThreadFactory(String igniteInstanceName, String threadName, byte plc) { this.igniteInstanceName = igniteInstanceName; this.threadName = threadName; + this.plc = plc; } /** {@inheritDoc} */ @Override public Thread newThread(@NotNull Runnable r) { - return new IgniteThread(igniteInstanceName, threadName, r, idxGen.incrementAndGet(), -1); + return new IgniteThread(igniteInstanceName, threadName, r, idxGen.incrementAndGet(), -1, plc); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java index 639ef9438a669..83c64c3363000 100644 --- a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java +++ b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThreadPoolExecutor.java @@ -22,6 +22,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; /** * An {@link ExecutorService} that executes submitted tasks using pooled grid threads. @@ -46,13 +47,43 @@ public IgniteThreadPoolExecutor( int maxPoolSize, long keepAliveTime, BlockingQueue workQ) { + this(threadNamePrefix, + igniteInstanceName, + corePoolSize, + maxPoolSize, + keepAliveTime, + workQ, + GridIoPolicy.UNDEFINED); + } + + /** + * Creates a new service with the given initial parameters. + * + * @param threadNamePrefix Will be added at the beginning of all created threads. + * @param igniteInstanceName Must be the name of the grid. + * @param corePoolSize The number of threads to keep in the pool, even if they are idle. + * @param maxPoolSize The maximum number of threads to allow in the pool. + * @param keepAliveTime When the number of threads is greater than the core, this is the maximum time + * that excess idle threads will wait for new tasks before terminating. + * @param workQ The queue to use for holding tasks before they are executed. This queue will hold only + * runnable tasks submitted by the {@link #execute(Runnable)} method. + * @param plc {@link GridIoPolicy} for thread pool. + */ + public IgniteThreadPoolExecutor( + String threadNamePrefix, + String igniteInstanceName, + int corePoolSize, + int maxPoolSize, + long keepAliveTime, + BlockingQueue workQ, + byte plc) { super( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, workQ, - new IgniteThreadFactory(igniteInstanceName, threadNamePrefix) + new IgniteThreadFactory(igniteInstanceName, threadNamePrefix, plc) ); } diff --git a/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java b/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java index 3948f6a620bc6..dce6328775ef8 100644 --- a/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/thread/GridThreadPoolExecutorServiceSelfTest.java @@ -64,7 +64,7 @@ public void testSingleThreadExecutor() throws Exception { * @throws Exception If failed. */ public void testSingleGridThreadExecutor() throws Exception { - ExecutorService exec = Executors.newSingleThreadExecutor(new IgniteThreadFactory("gridName")); + ExecutorService exec = Executors.newSingleThreadExecutor(new IgniteThreadFactory("gridName", "testThread")); exec.submit(new InterruptingRunnable()).get(); From 93eb78f62322ce32900efd430a489afee078dd89 Mon Sep 17 00:00:00 2001 From: sboikov Date: Thu, 27 Jul 2017 15:25:06 +0300 Subject: [PATCH 066/145] Minor (added more clear method IgniteTxHandler.prepareNearTxLocal). (cherry picked from commit e9a0d69) --- ...OptimisticSerializableTxPrepareFuture.java | 2 +- .../GridNearOptimisticTxPrepareFuture.java | 2 +- .../GridNearPessimisticTxPrepareFuture.java | 2 +- .../cache/transactions/IgniteTxHandler.java | 59 ++++++++++--------- 4 files changed, 35 insertions(+), 30 deletions(-) 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 561c4f7226d3b..1995e2e148bad 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 @@ -575,7 +575,7 @@ private void prepareLocal(GridNearTxPrepareRequest req, final MiniFuture fut, final boolean nearEntries) { IgniteInternalFuture prepFut = nearEntries ? - cctx.tm().txHandler().prepareNearTx(cctx.localNodeId(), req, true) : + cctx.tm().txHandler().prepareNearTxLocal(req) : cctx.tm().txHandler().prepareColocatedTx(tx, req); prepFut.listen(new CI1>() { 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 1e7a56770c124..b763f02f2b047 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 @@ -569,7 +569,7 @@ private void proceedPrepare(GridDistributedTxMapping m, @Nullable final Queue prepFut = - m.hasNearCacheEntries() ? cctx.tm().txHandler().prepareNearTx(n.id(), req, true) + m.hasNearCacheEntries() ? cctx.tm().txHandler().prepareNearTxLocal(req) : cctx.tm().txHandler().prepareColocatedTx(tx, req); prepFut.listen(new CI1>() { 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 11cd9f93ab974..db065a7e6fa42 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 @@ -238,7 +238,7 @@ private void prepareLocal(GridNearTxPrepareRequest req, add(fut); IgniteInternalFuture prepFut = nearEntries ? - cctx.tm().txHandler().prepareNearTx(cctx.localNodeId(), req, true) : + cctx.tm().txHandler().prepareNearTxLocal(req) : cctx.tm().txHandler().prepareColocatedTx(tx, req); prepFut.listen(new CI1>() { 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 b7ff319ea1f20..b53849d5192ac 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 @@ -118,7 +118,19 @@ private void processNearTxPrepareRequest(final UUID nearNodeId, GridNearTxPrepar ", node=" + nearNodeId + ']'); } - IgniteInternalFuture fut = prepareNearTx(nearNodeId, req, false); + ClusterNode nearNode = ctx.node(nearNodeId); + + if (nearNode == null) { + if (txPrepareMsgLog.isDebugEnabled()) { + txPrepareMsgLog.debug("Received near prepare from node that left grid (will ignore) [" + + "txId=" + req.version() + + ", node=" + nearNodeId + ']'); + } + + return; + } + + IgniteInternalFuture fut = prepareNearTx(nearNode, req); assert req.txState() != null || fut == null || fut.error() != null || (ctx.tm().tx(req.version()) == null && ctx.tm().nearTx(req.version()) == null); @@ -278,32 +290,25 @@ private IgniteTxEntry unmarshal(@Nullable Collection entries) thr } /** - * @param nearNodeId Near node ID that initiated transaction. - * @param req Near prepare request. - * @param locReq Local request flag. + * @param req Request. * @return Prepare future. */ - public IgniteInternalFuture prepareNearTx( - final UUID nearNodeId, - final GridNearTxPrepareRequest req, - boolean locReq - ) { + public IgniteInternalFuture prepareNearTxLocal(final GridNearTxPrepareRequest req) { // Make sure not to provide Near entries to DHT cache. - if (locReq) - req.cloneEntries(); - - ClusterNode nearNode = ctx.node(nearNodeId); + req.cloneEntries(); - if (nearNode == null) { - if (txPrepareMsgLog.isDebugEnabled()) { - txPrepareMsgLog.debug("Received near prepare from node that left grid (will ignore) [" + - "txId=" + req.version() + - ", node=" + nearNodeId + ']'); - } - - return null; - } + return prepareNearTx(ctx.localNode(), req); + } + /** + * @param nearNode Node that initiated transaction. + * @param req Near prepare request. + * @return Prepare future. + */ + private IgniteInternalFuture prepareNearTx( + final ClusterNode nearNode, + final GridNearTxPrepareRequest req + ) { IgniteTxEntry firstEntry; try { @@ -350,7 +355,7 @@ public IgniteInternalFuture prepareNearTx( if (txPrepareMsgLog.isDebugEnabled()) { txPrepareMsgLog.debug("Topology version mismatch for near prepare, need remap transaction [" + "txId=" + req.version() + - ", node=" + nearNodeId + + ", node=" + nearNode.id() + ", reqTopVer=" + req.topologyVersion() + ", locTopVer=" + top.topologyVersion() + ", req=" + req + ']'); @@ -370,24 +375,24 @@ public IgniteInternalFuture prepareNearTx( req.deployInfo() != null); try { - ctx.io().send(nearNodeId, res, req.policy()); + ctx.io().send(nearNode, res, req.policy()); if (txPrepareMsgLog.isDebugEnabled()) { txPrepareMsgLog.debug("Sent remap response for near prepare [txId=" + req.version() + - ", node=" + nearNodeId + ']'); + ", node=" + nearNode.id() + ']'); } } catch (ClusterTopologyCheckedException ignored) { if (txPrepareMsgLog.isDebugEnabled()) { txPrepareMsgLog.debug("Failed to send remap response for near prepare, node failed [" + "txId=" + req.version() + - ", node=" + nearNodeId + ']'); + ", node=" + nearNode.id() + ']'); } } catch (IgniteCheckedException e) { U.error(txPrepareMsgLog, "Failed to send remap response for near prepare " + "[txId=" + req.version() + - ", node=" + nearNodeId + + ", node=" + nearNode.id() + ", req=" + req + ']', e); } From e0fe1c3ca0df4ae51dee84ce5d23eb6292cdac22 Mon Sep 17 00:00:00 2001 From: sboikov Date: Fri, 28 Jul 2017 10:25:16 +0300 Subject: [PATCH 067/145] ignite-5858 Fixed affinity initialization on new coordinator (broken in aeb9336b3b161ddfff73f17e41cd453409b84a16). (cherry picked from commit b698bbf) --- .../cache/CacheAffinitySharedManager.java | 2 +- .../cache/GridCachePartitionExchangeManager.java | 13 ++++++++++++- .../preloader/GridDhtPartitionsExchangeFuture.java | 10 +++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) 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 4db8b76daa7c2..6ed78414dc76d 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 @@ -1434,7 +1434,7 @@ public boolean onServerLeft(final GridDhtPartitionsExchangeFuture fut, boolean c * @throws IgniteCheckedException If failed. * @return Future completed when caches initialization is done. */ - private IgniteInternalFuture initCoordinatorCaches(final GridDhtPartitionsExchangeFuture fut) + public IgniteInternalFuture initCoordinatorCaches(final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException { final List> futs = new ArrayList<>(); 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 71572f1703244..a0b24f879b81a 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 @@ -1732,6 +1732,9 @@ private class ExchangeWorker extends GridWorker { /** Busy flag used as performance optimization to stop current preloading. */ private volatile boolean busy; + /** */ + private boolean crd; + /** * Constructor. */ @@ -1927,7 +1930,15 @@ else if (task instanceof ForceRebalanceExchangeTask) { lastInitializedFut = exchFut; - exchFut.init(); + boolean newCrd = false; + + if (!crd) { + List srvNodes = exchFut.discoCache().serverNodes(); + + crd = newCrd = !srvNodes.isEmpty() && srvNodes.get(0).isLocal(); + } + + exchFut.init(newCrd); int dumpCnt = 0; 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 403923ca84a0a..1b5deb558d4d0 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 @@ -411,9 +411,10 @@ private void leaveBusy() { /** * Starts activity. * + * @param newCrd {@code True} if node become coordinator on this exchange. * @throws IgniteInterruptedCheckedException If interrupted. */ - public void init() throws IgniteInterruptedCheckedException { + public void init(boolean newCrd) throws IgniteInterruptedCheckedException { if (isDone()) return; @@ -487,6 +488,13 @@ else if (msg instanceof SnapshotDiscoveryMessage) { initCachesOnLocalJoin(); } + if (newCrd) { + IgniteInternalFuture fut = cctx.affinity().initCoordinatorCaches(this); + + if (fut != null) + fut.get(); + } + exchange = CU.clientNode(discoEvt.eventNode()) ? onClientNodeEvent(crdNode) : onServerNodeEvent(crdNode); From 8d51fb95329aede1ae733b368c68ffe5528e21e6 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Mon, 21 Aug 2017 19:09:08 +0700 Subject: [PATCH 068/145] IGNITE-6104 Fixed link. (cherry picked from commit e678c81) --- .../components/web-console-footer-links/template.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug index 105aa5131b414..c7c45722f0b07 100644 --- a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug +++ b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug @@ -14,4 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. -a(href="/downloads/agent" target="_blank") Download Agent +a(href="/api/v1/downloads/agent" target="_blank") Download Agent From 3b613407c056c01a4eda0c77612a08b8468886d5 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Wed, 2 Aug 2017 11:25:08 +0300 Subject: [PATCH 069/145] IGNITE-5757 - Rent partitions on exchange completion (cherry picked from commit c6fbe2d) --- .../processors/cache/GridCacheMapEntry.java | 3 +++ .../dht/GridDhtPartitionTopology.java | 4 ++- .../dht/GridDhtPartitionTopologyImpl.java | 21 ++++++++++------ .../dht/GridDhtTxPrepareFuture.java | 25 +++++++++++++++++-- .../distributed/near/GridNearCacheEntry.java | 2 +- .../near/GridNearTxPrepareRequest.java | 8 +++--- .../cache/transactions/IgniteTxManager.java | 3 ++- .../IgniteRejectConnectOnNodeStopTest.java | 7 +++++- .../junits/GridAbstractTest.java | 3 ++- 9 files changed, 57 insertions(+), 19 deletions(-) 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 edfa95087a7fc..2ee80a6f0e168 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 @@ -2088,6 +2088,9 @@ protected void clearReader(UUID nodeId) throws GridCacheEntryRemovedException { long expireTime = expireTimeExtras(); if (expireTime > 0 && (expireTime - U.currentTimeMillis() <= 0)) { + if (obsoleteVer == null) + obsoleteVer = nextVersion(); + if (onExpired(this.val, obsoleteVer)) { if (cctx.deferredDelete()) { deferred = true; 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 d9e04a6da4a46..98a5e135d217a 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 @@ -203,7 +203,9 @@ public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affR * @param p Partition ID. * @param affAssignment Assignments. * @param affNodes Node assigned for given partition by affinity. - * @return Collection of all nodes responsible for this partition with primary node being first. + * @return Collection of all nodes responsible for this partition with primary node being first. The first N + * elements of this collection (with N being 1 + backups) are actual DHT affinity nodes, other nodes + * are current additional owners of the partition after topology change. */ @Nullable public List nodes(int p, AffinityAssignment affAssignment, List affNodes); 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 197c753924fec..07473c47de7ea 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 @@ -380,9 +380,6 @@ else if (belongs) { } } } - - if (node2part != null && node2part.valid()) - checkEvictions(updateSeq, aff); } updateRebalanceVersion(aff); @@ -624,7 +621,12 @@ else if (log.isDebugEnabled()) } } - updateRebalanceVersion(grp.affinity().assignments(topVer)); + List> aff = grp.affinity().assignments(topVer); + + updateRebalanceVersion(aff); + + if (node2part != null && node2part.valid()) + changed |= checkEvictions(updateSeq, aff); consistencyCheck(); } @@ -763,8 +765,9 @@ private GridDhtLocalPartition localPartition0(int p, "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']'); } else if (loc != null && state == RENTING && !showRenting) - throw new GridDhtInvalidPartitionException(p, "Adding entry to partition that is concurrently evicted " + - "[part=" + p + ", shouldBeMoving=" + loc.reload() + "]"); + throw new GridDhtInvalidPartitionException(p, "Adding entry to partition that is concurrently " + + "evicted [part=" + p + ", shouldBeMoving=" + loc.reload() + ", belongs=" + belongs + + ", topVer=" + topVer + ", curTopVer=" + this.topVer + "]"); if (loc == null) { if (!belongs) @@ -1320,7 +1323,8 @@ else if (locPart.state() == OWNING || locPart.state() == MOVING) { if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) { List> aff = grp.affinity().assignments(topVer); - changed |= checkEvictions(updateSeq, aff); + if (exchangeVer == null) + changed |= checkEvictions(updateSeq, aff); updateRebalanceVersion(aff); } @@ -1509,7 +1513,8 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) { List> aff = grp.affinity().assignments(topVer); - changed |= checkEvictions(updateSeq, aff); + if (exchId == null) + changed |= checkEvictions(updateSeq, aff); updateRebalanceVersion(aff); } 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 a31c540756b46..03d99fc74852c 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 @@ -1510,7 +1510,7 @@ private void map(IgniteTxEntry entry) { try { List dhtNodes = dht.topology().nodes(cached.partition(), tx.topologyVersion()); - assert dhtNodes.size() > 0 && dhtNodes.get(0).id().equals(cctx.localNodeId()) : dhtNodes; + assert !dhtNodes.isEmpty() && dhtNodes.get(0).id().equals(cctx.localNodeId()) : dhtNodes; if (log.isDebugEnabled()) log.debug("Mapping entry to DHT nodes [nodes=" + U.toShortString(dhtNodes) + @@ -1531,7 +1531,7 @@ private void map(IgniteTxEntry entry) { ClusterNode readerNode = cctx.discovery().node(readerId); - if (readerNode == null || dhtNodes.contains(readerNode)) + if (readerNode == null || canSkipNearReader(dht, readerNode, dhtNodes)) continue; if (log.isDebugEnabled()) @@ -1553,6 +1553,27 @@ else if (log.isDebugEnabled()) } } + /** + * This method checks if we should skip mapping of an entry update to the near reader. We can skip the update + * if the reader is a primary or a backup. If the reader is a partition owner, but not a primary or a backup, + * we cannot skip the reader update and must attempt to update a near entry anyway. + * + * @param dhtCache DHT cache to check mapping. + * @param readerNode Reader node. + * @param dhtNodes Current DHT nodes (primary + backups first and other DHT nodes afterwards). + * @return {@code true} if reader is either a primary or a backup. + */ + private boolean canSkipNearReader(GridDhtCacheAdapter dhtCache, ClusterNode readerNode, List dhtNodes) { + int limit = Math.min(dhtCache.configuration().getBackups() + 1, dhtNodes.size()); + + for (int i = 0; i < limit; i++) { + if (dhtNodes.get(i).id().equals(readerNode.id())) + return true; + } + + return false; + } + /** * @param entry Entry. * @param n Node. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheEntry.java index 646281bcf41f2..6e606bfc7f345 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheEntry.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheEntry.java @@ -249,7 +249,7 @@ public void updateOrEvict(GridCacheVersion dhtVer, // If we are here, then we already tried to evict this entry. // If cannot evict, then update. if (this.dhtVer == null) { - if (!markObsolete(dhtVer)) { + if (!markObsolete(cctx.versions().next())) { value(val); ttlAndExpireTimeExtras((int) ttl, expireTime); 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 29c7aad873bb1..875f3975914c6 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 @@ -406,13 +406,13 @@ private boolean isFlag(int mask) { StringBuilder flags = new StringBuilder(); if (near()) - flags.append("near"); + flags.append("[near]"); if (firstClientRequest()) - flags.append("clientReq"); + flags.append("[firstClientReq]"); if (implicitSingle()) - flags.append("single"); + flags.append("[implicitSingle]"); if (explicitLock()) - flags.append("explicitLock"); + flags.append("[explicitLock]"); return S.toString(GridNearTxPrepareRequest.class, this, "flags", flags.toString(), 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 a427da34f80d4..6c5970676ccbc 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 @@ -864,7 +864,7 @@ private void removeObsolete(IgniteInternalTx tx) { GridNearCacheEntry e = near.peekExx(entry.key()); - if (e != null && e.markObsoleteIfEmpty(tx.xidVersion())) + if (e != null && e.markObsoleteIfEmpty(null)) near.removeEntry(e); } } @@ -1192,6 +1192,7 @@ public void commitTx(IgniteInternalTx tx) throws IgniteCheckedException { throw new IgniteCheckedException("Missing commit version (consider increasing " + IGNITE_MAX_COMPLETED_TX_COUNT + " system property) [ver=" + tx.xidVersion() + + ", committed0=" + committed0 + ", tx=" + tx.getClass().getSimpleName() + ']'); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteRejectConnectOnNodeStopTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteRejectConnectOnNodeStopTest.java index d34de1236a27f..97d685f225357 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteRejectConnectOnNodeStopTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteRejectConnectOnNodeStopTest.java @@ -81,6 +81,11 @@ public class IgniteRejectConnectOnNodeStopTest extends GridCommonAbstractTest { return cfg; } + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(true); + } + /** * @throws Exception If failed. */ @@ -126,7 +131,7 @@ public void testNodeStop() throws Exception { boolean err = false; - try{ + try { stopStartLatch.await(); IgniteCacheMessageRecoveryAbstractTest.closeSessions(srv); 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 9b99e01b4585d..4965d166c06fd 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 @@ -766,7 +766,8 @@ protected void checkTopology(int cnt) throws Exception { Thread.sleep(1000); } - throw new Exception("Failed to wait for proper topology: " + cnt); + throw new Exception("Failed to wait for proper topology [expCnt=" + cnt + + ", actualTopology=" + grid(0).cluster().nodes() + ']'); } /** */ From e9139d4916a22acab22008663e89f963f3d3a6b5 Mon Sep 17 00:00:00 2001 From: sboikov Date: Wed, 2 Aug 2017 17:25:31 +0300 Subject: [PATCH 070/145] Call updateRebalanceVersion after evictions (was broken in c6fbe2d82a9f56f96c94551b09e85a12d192f32e); (cherry picked from commit b277682) --- .../cache/distributed/dht/GridDhtPartitionTopologyImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 07473c47de7ea..05d49b7f7809d 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 @@ -623,11 +623,11 @@ else if (log.isDebugEnabled()) List> aff = grp.affinity().assignments(topVer); - updateRebalanceVersion(aff); - if (node2part != null && node2part.valid()) changed |= checkEvictions(updateSeq, aff); + updateRebalanceVersion(aff); + consistencyCheck(); } finally { From 615a582b83d9acdd5703995f52f97e70027e0639 Mon Sep 17 00:00:00 2001 From: Dmitriy Shabalin Date: Mon, 21 Aug 2017 19:34:59 +0700 Subject: [PATCH 071/145] IGNITE-4728 Fixed get params for saved state. (cherry picked from commit 97b3cef) --- modules/web-console/frontend/app/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/web-console/frontend/app/app.js b/modules/web-console/frontend/app/app.js index 8599eb799b664..f072fc5b981cc 100644 --- a/modules/web-console/frontend/app/app.js +++ b/modules/web-console/frontend/app/app.js @@ -291,7 +291,8 @@ angular.module('ignite-console', [ .run(['$transitions', ($transitions) => { $transitions.onSuccess({ }, (trans) => { try { - const {name, params, unsaved} = trans.$to(); + const {name, unsaved} = trans.$to(); + const params = trans.params(); if (unsaved) localStorage.removeItem('lastStateChangeSuccess'); From 891a9e0001bd0d77e0bafc3ad109d04d02a9b7ff Mon Sep 17 00:00:00 2001 From: sboikov Date: Mon, 21 Aug 2017 13:21:44 +0300 Subject: [PATCH 072/145] ignite-6124 Merge exchanges for multiple discovery events (cherry picked from commit bebf299799712b464ee0e3800752ecc07770d9f0) --- .../apache/ignite/IgniteSystemProperties.java | 3 + .../internal/IgniteDiagnosticMessage.java | 2 +- .../communication/GridIoMessageFactory.java | 9 +- .../managers/discovery/DiscoCache.java | 79 +- .../discovery/GridDiscoveryManager.java | 28 +- .../affinity/AffinityTopologyVersion.java | 7 + .../affinity/GridAffinityAssignmentCache.java | 42 + .../affinity/GridAffinityProcessor.java | 8 +- .../cache/CacheAffinitySharedManager.java | 695 ++++-- .../processors/cache/CacheGroupContext.java | 18 +- .../CachePartitionExchangeWorkerTask.java | 5 +- ...lientCacheChangeDummyDiscoveryMessage.java | 5 + .../cache/ClientCacheUpdateTimeout.java | 5 + .../processors/cache/ClusterCachesInfo.java | 22 +- .../processors/cache/ExchangeContext.java | 131 ++ .../cache/ExchangeDiscoveryEvents.java | 262 +++ .../processors/cache/GridCacheAdapter.java | 8 +- .../processors/cache/GridCacheContext.java | 2 +- .../processors/cache/GridCacheIoManager.java | 57 +- .../processors/cache/GridCacheMapEntry.java | 6 +- .../GridCachePartitionExchangeManager.java | 413 +++- .../processors/cache/GridCacheProcessor.java | 14 +- .../dht/ClientCacheDhtTopologyFuture.java | 12 +- .../dht/GridClientPartitionTopology.java | 130 +- .../distributed/dht/GridDhtCacheAdapter.java | 18 +- .../dht/GridDhtLocalPartition.java | 4 +- .../dht/GridDhtPartitionTopology.java | 38 +- .../dht/GridDhtPartitionTopologyImpl.java | 485 ++-- .../dht/GridDhtPartitionsReservation.java | 2 +- .../dht/GridDhtTopologyFuture.java | 36 +- .../dht/GridDhtTransactionalCacheAdapter.java | 95 +- .../dht/GridPartitionedGetFuture.java | 4 +- .../dht/GridPartitionedSingleGetFuture.java | 4 +- .../GridDhtAtomicAbstractUpdateFuture.java | 2 +- .../dht/atomic/GridDhtAtomicCache.java | 31 +- .../GridNearAtomicSingleUpdateFuture.java | 1 - .../colocated/GridDhtColocatedLockFuture.java | 2 +- .../preloader/CacheGroupAffinityMessage.java | 339 +++ .../preloader/ForceRebalanceExchangeTask.java | 5 + .../preloader/GridDhtPartitionExchangeId.java | 11 + .../dht/preloader/GridDhtPartitionMap.java | 2 +- .../preloader/GridDhtPartitionSupplier.java | 2 +- .../GridDhtPartitionsAbstractMessage.java | 37 +- .../GridDhtPartitionsExchangeFuture.java | 1972 +++++++++++++---- .../GridDhtPartitionsFullMessage.java | 161 +- .../GridDhtPartitionsSingleMessage.java | 78 +- .../GridDhtPartitionsSingleRequest.java | 47 +- .../dht/preloader/GridDhtPreloader.java | 44 +- .../IgniteDhtPartitionCountersMap.java | 7 + .../preloader/InitNewCoordinatorFuture.java | 307 +++ .../RebalanceReassignExchangeTask.java | 5 + .../near/GridNearCacheAdapter.java | 2 +- .../distributed/near/GridNearGetFuture.java | 4 +- .../distributed/near/GridNearLockFuture.java | 2 +- ...OptimisticSerializableTxPrepareFuture.java | 1 + .../GridNearOptimisticTxPrepareFuture.java | 1 + .../GridNearPessimisticTxPrepareFuture.java | 1 + .../near/GridNearTxPrepareRequest.java | 14 + .../GridCacheDatabaseSharedManager.java | 6 +- .../cache/query/GridCacheQueryAdapter.java | 4 +- .../cache/transactions/IgniteTxAdapter.java | 2 +- .../cache/transactions/IgniteTxHandler.java | 184 +- .../closure/GridClosureProcessor.java | 36 +- .../cluster/GridClusterStateProcessor.java | 2 +- .../datastreamer/DataStreamProcessor.java | 57 +- .../datastreamer/DataStreamerImpl.java | 65 +- .../datastreamer/PlatformDataStreamer.java | 3 +- .../schema/SchemaExchangeWorkerTask.java | 5 + .../SchemaNodeLeaveExchangeWorkerTask.java | 5 + .../processors/task/GridTaskWorker.java | 8 +- .../apache/ignite/thread/IgniteThread.java | 9 + .../TestDelayingCommunicationSpi.java | 63 + ...cheExchangeMessageDuplicatedStateTest.java | 9 +- .../IgniteClientCacheStartFailoverTest.java | 4 +- .../IgniteClusterActivateDeactivateTest.java | 4 +- .../IgniteDynamicCacheStartSelfTest.java | 26 +- ...teTopologyValidatorGridSplitCacheTest.java | 6 +- ...finityCoordinatorDynamicStartStopTest.java | 2 +- ...bstractDataStructuresFailoverSelfTest.java | 7 +- .../distributed/CacheExchangeMergeTest.java | 1528 +++++++++++++ .../CacheLateAffinityAssignmentTest.java | 598 +++-- ...cheLoadingConcurrentGridStartSelfTest.java | 11 + .../CacheLockReleaseNodeLeaveTest.java | 13 +- .../distributed/CachePartitionStateTest.java | 18 +- ...urrentGridStartSelfTestAllowOverwrite.java | 33 + ...teCacheClientNodeChangingTopologyTest.java | 5 +- ...CacheClientNodePartitionsExchangeTest.java | 52 +- ...rimaryNodeFailureRecoveryAbstractTest.java | 4 +- ...tomicInvalidPartitionHandlingSelfTest.java | 36 +- .../IgniteChangeGlobalStateTest.java | 11 +- .../join/JoinInActiveNodeToActiveCluster.java | 4 +- .../junits/common/GridCommonAbstractTest.java | 22 +- .../testsuites/IgniteCacheTestSuite2.java | 7 +- .../testsuites/IgniteCacheTestSuite6.java | 4 +- .../cache/WaitMapExchangeFinishCallable.java | 4 +- 95 files changed, 7114 insertions(+), 1470 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeDiscoveryEvents.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CacheGroupAffinityMessage.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/TestDelayingCommunicationSpi.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheExchangeMergeTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite.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 2fa52b6f2f3df..d3cba2bd3da2a 100644 --- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java +++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java @@ -168,6 +168,9 @@ public final class IgniteSystemProperties { /** Maximum size for exchange history. Default value is {@code 1000}.*/ public static final String IGNITE_EXCHANGE_HISTORY_SIZE = "IGNITE_EXCHANGE_HISTORY_SIZE"; + /** */ + public static final String IGNITE_EXCHANGE_MERGE_DELAY = "IGNITE_EXCHANGE_MERGE_DELAY"; + /** * Name of the system property defining name of command line program. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticMessage.java index bd4ec3a315068..8739c0e13ec61 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticMessage.java @@ -364,7 +364,7 @@ public final static class ExchangeInfoClosure extends DiagnosticBaseClosure { List futs = ctx.cache().context().exchange().exchangeFutures(); for (GridDhtPartitionsExchangeFuture fut : futs) { - if (topVer.equals(fut.topologyVersion())) { + if (topVer.equals(fut.initialVersion())) { sb.append("Exchange future: ").append(fut); return; 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 29c89a5ce90a4..97e06bfe72c51 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 @@ -81,6 +81,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicUpdateResponse; import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.NearCacheUpdates; import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.UpdateErrors; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CacheGroupAffinityMessage; 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.GridDhtPartitionDemandMessage; @@ -869,7 +870,13 @@ public GridIoMessageFactory(MessageFactory[] ext) { break; - // [-3..119] [124..127] [-23..-27] [-36..-55]- this + case 128: + msg = new CacheGroupAffinityMessage(); + + break; + + + // [-3..119] [124..128] [-23..-27] [-36..-55]- this // [120..123] - DR // [-4..-22, -30..-35] - SQL // [2048..2053] - Snapshots 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 4c1077b039544..5ac99f12d840c 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 @@ -25,6 +25,7 @@ import java.util.Set; import java.util.UUID; import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState; import org.apache.ignite.internal.util.GridConcurrentHashSet; import org.apache.ignite.internal.util.tostring.GridToStringInclude; @@ -32,6 +33,7 @@ import org.apache.ignite.internal.util.typedef.P1; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.lang.IgniteProductVersion; import org.jetbrains.annotations.Nullable; /** @@ -81,7 +83,14 @@ public class DiscoCache { /** Alive nodes. */ private final Set alives = new GridConcurrentHashSet<>(); + /** */ + private final IgniteProductVersion minNodeVer; + + /** */ + private final AffinityTopologyVersion topVer; + /** + * @param topVer Topology version. * @param state Current cluster state. * @param loc Local node. * @param rmtNodes Remote nodes. @@ -97,6 +106,7 @@ public class DiscoCache { * @param alives Alive nodes. */ DiscoCache( + AffinityTopologyVersion topVer, DiscoveryDataClusterState state, ClusterNode loc, List rmtNodes, @@ -110,6 +120,7 @@ public class DiscoCache { Map> cacheGrpAffNodes, Map nodeMap, Set alives) { + this.topVer = topVer; this.state = state; this.loc = loc; this.rmtNodes = rmtNodes; @@ -123,6 +134,33 @@ public class DiscoCache { this.cacheGrpAffNodes = cacheGrpAffNodes; this.nodeMap = nodeMap; this.alives.addAll(alives); + + IgniteProductVersion minVer = null; + + for (int i = 0; i < allNodes.size(); i++) { + ClusterNode node = allNodes.get(i); + + if (minVer == null) + minVer = node.version(); + else if (node.version().compareTo(minVer) < 0) + minVer = node.version(); + } + + minNodeVer = minVer; + } + + /** + * @return Topology version. + */ + public AffinityTopologyVersion version() { + return topVer; + } + + /** + * @return Minimum node version. + */ + public IgniteProductVersion minimumNodeVersion() { + return minNodeVer; } /** @@ -254,7 +292,7 @@ public List cacheGroupAffinityNodes(int grpId) { * @param id Node ID. * @return Node. */ - public @Nullable ClusterNode node(UUID id) { + @Nullable public ClusterNode node(UUID id) { return nodeMap.get(id); } @@ -279,6 +317,45 @@ public void updateAlives(GridDiscoveryManager discovery) { } } + /** + * @param order Order. + * @return Server node instance. + */ + @Nullable public ClusterNode serverNodeByOrder(long order) { + int idx = serverNodeBinarySearch(order); + + if (idx >= 0) + return srvNodes.get(idx); + + return null; + } + + /** + * @param order Node order. + * @return Node index. + */ + private int serverNodeBinarySearch(long order) { + int low = 0; + int high = srvNodes.size() - 1; + + while (low <= high) { + int mid = (low + high) >>> 1; + + ClusterNode midVal = srvNodes.get(mid); + + int cmp = Long.compare(midVal.order(), order); + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; + } + + return -(low + 1); + } + /** * @param nodes Cluster nodes. * @return Empty collection if nodes list is {@code null} 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 7ad058db71213..49278e6d8d10e 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 @@ -597,7 +597,10 @@ private void updateClientNodes(UUID leftNodeId) { ChangeGlobalStateFinishMessage stateFinishMsg = null; if (locJoinEvt) { - discoCache = createDiscoCache(ctx.state().clusterState(), locNode, topSnapshot); + discoCache = createDiscoCache(new AffinityTopologyVersion(topVer, minorTopVer), + ctx.state().clusterState(), + locNode, + topSnapshot); transitionWaitFut = ctx.state().onLocalJoin(discoCache); } @@ -620,7 +623,10 @@ else if (type == EVT_NODE_FAILED || type == EVT_NODE_LEFT) else if (customMsg instanceof ChangeGlobalStateFinishMessage) { ctx.state().onStateFinishMessage((ChangeGlobalStateFinishMessage)customMsg); - discoCache = createDiscoCache(ctx.state().clusterState(), locNode, topSnapshot); + discoCache = createDiscoCache(topSnap.get().topVer, + ctx.state().clusterState(), + locNode, + topSnapshot); topSnap.set(new Snapshot(topSnap.get().topVer, discoCache)); @@ -667,8 +673,12 @@ else if (customMsg instanceof ChangeGlobalStateFinishMessage) { // There is no race possible between history maintenance and concurrent discovery // event notifications, since SPI notifies manager about all events from this listener. if (verChanged) { - if (discoCache == null) - discoCache = createDiscoCache(ctx.state().clusterState(), locNode, topSnapshot); + if (discoCache == null) { + discoCache = createDiscoCache(nextTopVer, + ctx.state().clusterState(), + locNode, + topSnapshot); + } discoCacheHist.put(nextTopVer, discoCache); @@ -739,7 +749,7 @@ else if (type == EVT_CLIENT_NODE_DISCONNECTED) { topHist.clear(); topSnap.set(new Snapshot(AffinityTopologyVersion.ZERO, - createDiscoCache(ctx.state().clusterState(), locNode, Collections.emptySet()))); + createDiscoCache(AffinityTopologyVersion.ZERO, ctx.state().clusterState(), locNode, Collections.emptySet()))); } else if (type == EVT_CLIENT_NODE_RECONNECTED) { assert locNode.isClient() : locNode; @@ -2154,12 +2164,15 @@ public void reconnect() { /** * Called from discovery thread. * + * @param topVer Topology version. * @param state Current state. * @param loc Local node. * @param topSnapshot Topology snapshot. * @return Newly created discovery cache. */ - @NotNull private DiscoCache createDiscoCache(DiscoveryDataClusterState state, + @NotNull private DiscoCache createDiscoCache( + AffinityTopologyVersion topVer, + DiscoveryDataClusterState state, ClusterNode loc, Collection topSnapshot) { HashSet alives = U.newHashSet(topSnapshot.size()); @@ -2236,6 +2249,7 @@ public void reconnect() { } return new DiscoCache( + topVer, state, loc, Collections.unmodifiableList(rmtNodes), @@ -2378,7 +2392,7 @@ public void scheduleSegmentCheck() { discoWrk.addEvent(EVT_NODE_SEGMENTED, AffinityTopologyVersion.NONE, node, - createDiscoCache(null, node, empty), + createDiscoCache(AffinityTopologyVersion.NONE, null, node, empty), empty, null); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityTopologyVersion.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityTopologyVersion.java index 866953005985e..44b27534dee62 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityTopologyVersion.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityTopologyVersion.java @@ -72,6 +72,13 @@ public AffinityTopologyVersion( this.minorTopVer = minorTopVer; } + /** + * @return {@code True} if this is real topology version (neither {@link #NONE} nor {@link #ZERO}. + */ + public boolean initialized() { + return topVer > 0; + } + /** * @return Topology version with incremented minor version. */ 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 a8c6c59747bba..f921251ccc421 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 @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.affinity; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -352,6 +353,17 @@ public List> assignments(AffinityTopologyVersion topVer) { return aff.assignment(); } + /** + * @param topVer Topology version. + * @return Affinity assignment. + */ + public List> readyAssignments(AffinityTopologyVersion topVer) { + AffinityAssignment aff = readyAffinity(topVer); + + assert aff != null : "No ready affinity [grp=" + cacheOrGrpName + ", ver=" + topVer + ']'; + + return aff.assignment(); + } /** * Gets future that will be completed after topology with version {@code topVer} is calculated. @@ -455,6 +467,30 @@ public boolean dumpDebugInfo() { return false; } + /** + * @param topVer Topology version. + * @return Assignment. + */ + public AffinityAssignment readyAffinity(AffinityTopologyVersion topVer) { + AffinityAssignment cache = head.get(); + + if (!cache.topologyVersion().equals(topVer)) { + cache = affCache.get(topVer); + + if (cache == null) { + throw new IllegalStateException("Affinity for topology version is " + + "not initialized [locNode=" + ctx.discovery().localNode().id() + + ", grp=" + cacheOrGrpName + + ", topVer=" + topVer + + ", head=" + head.get().topologyVersion() + + ", history=" + affCache.keySet() + + ']'); + } + } + + return cache; + } + /** * Get cached affinity for specified topology version. * @@ -600,6 +636,12 @@ private void onHistoryAdded(GridAffinityAssignment aff) { } } + /** + * @return All initialized versions. + */ + public Collection cachedVersions() { + return affCache.keySet(); + } /** * Affinity ready future. Will remove itself from ready futures map. 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 b2989d3db5850..5b93be89de75a 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 @@ -184,7 +184,7 @@ public int partition0(String cacheName, Object key, @Nullable AffinityInfo aff) assert cacheName != null; if (aff == null) { - aff = affinityCache(cacheName, ctx.discovery().topologyVersionEx()); + aff = affinityCache(cacheName, ctx.cache().context().exchange().readyAffinityVersion()); if (aff == null) throw new IgniteCheckedException("Failed to get cache affinity (cache was not started " + @@ -303,7 +303,7 @@ public List mapKeyToPrimaryAndBackups(String cacheName, if (key == null) return null; - AffinityInfo affInfo = affinityCache(cacheName, ctx.discovery().topologyVersionEx()); + AffinityInfo affInfo = affinityCache(cacheName, ctx.cache().context().exchange().readyAffinityVersion()); if (affInfo == null) return null; @@ -329,7 +329,7 @@ public CacheAffinityProxy affinityProxy(String cacheName) { */ private Map> keysToNodes(@Nullable final String cacheName, Collection keys) throws IgniteCheckedException { - return keysToNodes(cacheName, keys, ctx.discovery().topologyVersionEx()); + return keysToNodes(cacheName, keys, ctx.cache().context().exchange().readyAffinityVersion()); } /** @@ -974,7 +974,7 @@ public CacheAffinityProxy(String cacheName) { * @throws IgniteCheckedException If failed. */ private AffinityInfo cache() throws IgniteCheckedException { - AffinityInfo aff = affinityCache(cacheName, ctx.discovery().topologyVersionEx()); + AffinityInfo aff = affinityCache(cacheName, ctx.cache().context().exchange().readyAffinityVersion()); if (aff == null) throw new IgniteException("Failed to find cache (cache was not started " + 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 6ed78414dc76d..138cb648da9a4 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 @@ -43,6 +43,7 @@ import org.apache.ignite.internal.managers.discovery.DiscoCache; import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener; +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.distributed.dht.ClientCacheDhtTopologyFuture; @@ -51,10 +52,15 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAssignmentFetchFuture; 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.distributed.dht.preloader.CacheGroupAffinityMessage; 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.distributed.dht.preloader.GridDhtPartitionsFullMessage; 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; import org.apache.ignite.internal.util.future.GridCompoundFuture; import org.apache.ignite.internal.util.future.GridFinishedFuture; import org.apache.ignite.internal.util.future.GridFutureAdapter; @@ -63,6 +69,7 @@ import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiInClosure; +import org.apache.ignite.lang.IgniteClosure; import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgniteUuid; import org.jetbrains.annotations.Nullable; @@ -83,12 +90,23 @@ public class CacheAffinitySharedManager extends GridCacheSharedManagerAdap private final long clientCacheMsgTimeout = IgniteSystemProperties.getLong(IgniteSystemProperties.IGNITE_CLIENT_CACHE_CHANGE_MESSAGE_TIMEOUT, 10_000); + /** */ + private static final IgniteClosure NODE_TO_ID = new IgniteClosure() { + @Override public UUID apply(ClusterNode node) { + return node.id(); + } + }; + + /** */ + private static final IgniteClosure NODE_TO_ORDER = new IgniteClosure() { + @Override public Long apply(ClusterNode node) { + return node.order(); + } + }; + /** Affinity information for all started caches (initialized on coordinator). */ private ConcurrentMap grpHolders = new ConcurrentHashMap<>(); - /** Last topology version when affinity was calculated (updated from exchange thread). */ - private AffinityTopologyVersion affCalcVer; - /** Topology version which requires affinity re-calculation (set from discovery thread). */ private AffinityTopologyVersion lastAffVer; @@ -153,8 +171,6 @@ void onDiscoveryEvent(int type, // Clean-up in case of client reconnect. caches.clear(); - affCalcVer = null; - lastAffVer = null; caches.init(cctx.cache().cacheGroupDescriptors(), cctx.cache().cacheDescriptors()); @@ -170,9 +186,11 @@ else if (customMsg instanceof ChangeGlobalStateFinishMessage) { } if (!CU.clientNode(node) && (type == EVT_NODE_FAILED || type == EVT_NODE_JOINED || type == EVT_NODE_LEFT)) { - assert lastAffVer == null || topVer.compareTo(lastAffVer) > 0; + synchronized (mux) { + assert lastAffVer == null || topVer.compareTo(lastAffVer) > 0; - lastAffVer = topVer; + lastAffVer = topVer; + } } } @@ -250,13 +268,9 @@ void checkRebalanceState(GridDhtPartitionTopology top, Integer checkGrpId) { CacheAffinityChangeMessage msg = null; synchronized (mux) { - if (waitInfo == null) + if (waitInfo == null || !waitInfo.topVer.equals(lastAffVer) ) return; - assert affCalcVer != null; - assert affCalcVer.equals(waitInfo.topVer) : "Invalid affinity version [calcVer=" + affCalcVer + - ", waitVer=" + waitInfo.topVer + ']'; - Map partWait = waitInfo.waitGrps.get(checkGrpId); boolean rebalanced = true; @@ -293,14 +307,14 @@ void checkRebalanceState(GridDhtPartitionTopology top, Integer checkGrpId) { } } } - } - try { - if (msg != null) - cctx.discovery().sendCustomEvent(msg); - } - catch (IgniteCheckedException e) { - U.error(log, "Failed to send affinity change message.", e); + try { + if (msg != null) + cctx.discovery().sendCustomEvent(msg); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to send affinity change message.", e); + } } } @@ -436,19 +450,19 @@ void onCacheGroupCreated(CacheGroupContext grp) { grp.topology().updateTopologyVersion(topFut, discoCache, -1, false); + grpHolder = new CacheGroupHolder1(grp, grpHolder.affinity()); + + grpHolders.put(grp.groupId(), grpHolder); + GridClientPartitionTopology clientTop = cctx.exchange().clearClientTopology(grp.groupId()); if (clientTop != null) { - grp.topology().update(topVer, + grp.topology().update(grpHolder.affinity().lastVersion(), clientTop.partitionMap(true), clientTop.updateCounters(false), Collections.emptySet()); } - grpHolder = new CacheGroupHolder1(grp, grpHolder.affinity()); - - grpHolders.put(grp.groupId(), grpHolder); - assert grpHolder.affinity().lastVersion().equals(grp.affinity().lastVersion()); } } @@ -523,7 +537,7 @@ else if (!fetchFuts.containsKey(grp.groupId())) { assert grp != null; - grp.topology().onExchangeDone(grp.affinity().cachedAffinity(topVer), true); + grp.topology().onExchangeDone(null, grp.affinity().cachedAffinity(topVer), true); } } @@ -676,12 +690,14 @@ private void scheduleClientChangeMessage(Map startedCaches, Se * @throws IgniteCheckedException If failed. */ public void onCacheChangeRequest( - final GridDhtPartitionsExchangeFuture fut, + GridDhtPartitionsExchangeFuture fut, boolean crd, final ExchangeActions exchActions ) throws IgniteCheckedException { assert exchActions != null && !exchActions.empty() : exchActions; + final ExchangeDiscoveryEvents evts = fut.context().events(); + caches.updateCachesInfo(exchActions); // Affinity did not change for existing caches. @@ -690,7 +706,7 @@ public void onCacheChangeRequest( if (exchActions.cacheGroupStopping(aff.groupId())) return; - aff.clientEventTopologyChange(fut.discoveryEvent(), fut.topologyVersion()); + aff.clientEventTopologyChange(evts.lastEvent(), evts.topologyVersion()); } }); @@ -734,10 +750,10 @@ public void onCacheChangeRequest( cctx.cache().prepareCacheStart(req.startCacheConfiguration(), cacheDesc, nearCfg, - fut.topologyVersion()); + evts.topologyVersion()); if (fut.cacheAddedOnExchange(cacheDesc.cacheId(), cacheDesc.receivedFrom())) { - if (fut.discoCache().cacheGroupAffinityNodes(cacheDesc.groupId()).isEmpty()) + if (fut.events().discoveryCache().cacheGroupAffinityNodes(cacheDesc.groupId()).isEmpty()) U.quietAndWarn(log, "No server nodes found for cache client: " + req.cacheName()); } } @@ -763,7 +779,7 @@ public void onCacheChangeRequest( else { CacheGroupContext grp = cctx.cache().cacheGroup(grpId); - if (grp != null && !grp.isLocal() && grp.localStartVersion().equals(fut.topologyVersion())) { + if (grp != null && !grp.isLocal() && grp.localStartVersion().equals(fut.initialVersion())) { assert grp.affinity().lastVersion().equals(AffinityTopologyVersion.NONE) : grp.affinity().lastVersion(); initAffinity(caches.group(grp.groupId()), grp.affinity(), fut); @@ -798,7 +814,7 @@ public void onCacheChangeRequest( } if (stoppedGrps != null) { - boolean notify = false; + AffinityTopologyVersion notifyTopVer = null; synchronized (mux) { if (waitInfo != null) { @@ -806,7 +822,7 @@ public void onCacheChangeRequest( boolean rmv = waitInfo.waitGrps.remove(grpId) != null; if (rmv) { - notify = true; + notifyTopVer = waitInfo.topVer; waitInfo.assignments.remove(grpId); } @@ -814,8 +830,8 @@ public void onCacheChangeRequest( } } - if (notify) { - final AffinityTopologyVersion topVer = affCalcVer; + if (notifyTopVer != null) { + final AffinityTopologyVersion topVer = notifyTopVer; cctx.kernalContext().closure().runLocalSafe(new Runnable() { @Override public void run() { @@ -855,13 +871,13 @@ public void onExchangeChangeAffinityMessage(GridDhtPartitionsExchangeFuture exch boolean crd, CacheAffinityChangeMessage msg) { if (log.isDebugEnabled()) { - log.debug("Process exchange affinity change message [exchVer=" + exchFut.topologyVersion() + + log.debug("Process exchange affinity change message [exchVer=" + exchFut.initialVersion() + ", msg=" + msg + ']'); } assert exchFut.exchangeId().equals(msg.exchangeId()) : msg; - final AffinityTopologyVersion topVer = exchFut.topologyVersion(); + final AffinityTopologyVersion topVer = exchFut.initialVersion(); final Map>> assignment = msg.assignmentChange(); @@ -905,16 +921,12 @@ public void onChangeAffinityMessage(final GridDhtPartitionsExchangeFuture exchFu boolean crd, final CacheAffinityChangeMessage msg) throws IgniteCheckedException { - assert affCalcVer != null || cctx.kernalContext().clientNode(); assert msg.topologyVersion() != null && msg.exchangeId() == null : msg; - assert affCalcVer == null || affCalcVer.equals(msg.topologyVersion()) : - "Invalid version [affCalcVer=" + affCalcVer + ", msg=" + msg + ']'; - final AffinityTopologyVersion topVer = exchFut.topologyVersion(); + final AffinityTopologyVersion topVer = exchFut.initialVersion(); if (log.isDebugEnabled()) { - log.debug("Process affinity change message [exchVer=" + exchFut.topologyVersion() + - ", affCalcVer=" + affCalcVer + + log.debug("Process affinity change message [exchVer=" + topVer + ", msgVer=" + msg.topologyVersion() + ']'); } @@ -939,7 +951,7 @@ public void onChangeAffinityMessage(final GridDhtPartitionsExchangeFuture exchFu IgniteUuid deploymentId = desc.deploymentId(); if (!deploymentId.equals(deploymentIds.get(aff.groupId()))) { - aff.clientEventTopologyChange(exchFut.discoveryEvent(), topVer); + aff.clientEventTopologyChange(exchFut.firstEvent(), topVer); return; } @@ -963,7 +975,7 @@ public void onChangeAffinityMessage(final GridDhtPartitionsExchangeFuture exchFu ", part=" + part + ", cur=" + F.nodeIds(assignment.get(part)) + ", new=" + F.nodeIds(nodes) + - ", exchVer=" + exchFut.topologyVersion() + + ", exchVer=" + exchFut.initialVersion() + ", msgVer=" + msg.topologyVersion() + ']'; @@ -973,14 +985,9 @@ public void onChangeAffinityMessage(final GridDhtPartitionsExchangeFuture exchFu aff.initialize(topVer, cachedAssignment(aff, assignment, affCache)); } else - aff.clientEventTopologyChange(exchFut.discoveryEvent(), topVer); + aff.clientEventTopologyChange(exchFut.firstEvent(), topVer); } }); - - synchronized (mux) { - if (affCalcVer == null) - affCalcVer = msg.topologyVersion(); - } } /** @@ -991,14 +998,14 @@ public void onChangeAffinityMessage(final GridDhtPartitionsExchangeFuture exchFu * @throws IgniteCheckedException If failed. */ public void onClientEvent(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException { - boolean locJoin = fut.discoveryEvent().eventNode().isLocal(); + boolean locJoin = fut.firstEvent().eventNode().isLocal(); if (!locJoin) { forAllCacheGroups(crd, new IgniteInClosureX() { @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException { - AffinityTopologyVersion topVer = fut.topologyVersion(); + AffinityTopologyVersion topVer = fut.initialVersion(); - aff.clientEventTopologyChange(fut.discoveryEvent(), topVer); + aff.clientEventTopologyChange(fut.firstEvent(), topVer); } }); } @@ -1092,17 +1099,13 @@ private void initStartedGroupOnCoordinator(GridDhtPartitionsExchangeFuture fut, if (grpHolder == null) { grpHolder = grp != null ? new CacheGroupHolder1(grp, null) : - CacheGroupHolder2.create(cctx, grpDesc, fut.topologyVersion(), null); + CacheGroupHolder2.create(cctx, grpDesc, fut.initialVersion(), null); CacheGroupHolder old = grpHolders.put(grpId, grpHolder); assert old == null : old; - List> newAff = grpHolder.affinity().calculate(fut.topologyVersion(), - fut.discoveryEvent(), - fut.discoCache()); - - grpHolder.affinity().initialize(fut.topologyVersion(), newAff); + calculateAndInit(fut.events(), grpHolder.affinity(), fut.initialVersion()); } else if (grpHolder.client() && grp != null) { assert grpHolder.affinity().idealAssignment() != null; @@ -1128,17 +1131,16 @@ public void initStartedCaches( ) throws IgniteCheckedException { caches.initStartedCaches(descs); + if (fut.context().mergeExchanges()) + return; + if (crd) { forAllRegisteredCacheGroups(new IgniteInClosureX() { @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { - CacheGroupHolder cache = groupHolder(fut.topologyVersion(), desc); + CacheGroupHolder cache = groupHolder(fut.initialVersion(), desc); - if (cache.affinity().lastVersion().equals(AffinityTopologyVersion.NONE)) { - List> assignment = - cache.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); - - cache.affinity().initialize(fut.topologyVersion(), assignment); - } + if (cache.affinity().lastVersion().equals(AffinityTopologyVersion.NONE)) + calculateAndInit(fut.events(), cache.affinity(), fut.initialVersion()); } }); } @@ -1164,22 +1166,21 @@ private void initAffinity(CacheGroupDescriptor desc, throws IgniteCheckedException { assert desc != null : aff.cacheOrGroupName(); - if (canCalculateAffinity(desc, aff, fut)) { - List> assignment = aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + ExchangeDiscoveryEvents evts = fut.context().events(); - aff.initialize(fut.topologyVersion(), assignment); - } + if (canCalculateAffinity(desc, aff, fut)) + calculateAndInit(evts, aff, evts.topologyVersion()); else { GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(cctx, desc.groupId(), - fut.topologyVersion(), - fut.discoCache()); + evts.topologyVersion(), + evts.discoveryCache()); fetchFut.init(false); - fetchAffinity(fut.topologyVersion(), - fut.discoveryEvent(), - fut.discoCache(), + fetchAffinity(evts.topologyVersion(), + evts.lastEvent(), + evts.discoveryCache(), aff, fetchFut); } } @@ -1200,13 +1201,192 @@ private boolean canCalculateAffinity(CacheGroupDescriptor desc, return true; // If local node did not initiate exchange or local node is the only cache node in grid. - Collection affNodes = fut.discoCache().cacheGroupAffinityNodes(aff.groupId()); + Collection affNodes = fut.events().discoveryCache().cacheGroupAffinityNodes(aff.groupId()); return fut.cacheGroupAddedOnExchange(aff.groupId(), desc.receivedFrom()) || !fut.exchangeId().nodeId().equals(cctx.localNodeId()) || (affNodes.isEmpty() || (affNodes.size() == 1 && affNodes.contains(cctx.localNode()))); } + /** + * @param grpId Cache group ID. + * @return Affinity assignments. + */ + public GridAffinityAssignmentCache affinity(Integer grpId) { + CacheGroupHolder grpHolder = grpHolders.get(grpId); + + assert grpHolder != null : debugGroupName(grpId); + + return grpHolder.affinity(); + } + + /** + * @param fut Current exchange future. + * @param msg Finish exchange message. + */ + public void mergeExchangesOnServerLeft(final GridDhtPartitionsExchangeFuture fut, + final GridDhtPartitionsFullMessage msg) { + final Map nodesByOrder = new HashMap<>(); + + final Map>> affCache = new HashMap<>(); + + forAllCacheGroups(false, new IgniteInClosureX() { + @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException { + ExchangeDiscoveryEvents evts = fut.context().events(); + + Map idealAffDiff = msg.idealAffinityDiff(); + + List> idealAssignment = + aff.calculate(evts.topologyVersion(), evts.lastEvent(), evts.discoveryCache()); + + CacheGroupAffinityMessage affMsg = idealAffDiff != null ? idealAffDiff.get(aff.groupId()) : null; + + List> newAssignment; + + if (affMsg != null) { + Map diff = affMsg.assignmentsDiff(); + + assert !F.isEmpty(diff); + + newAssignment = new ArrayList<>(idealAssignment); + + for (Map.Entry e : diff.entrySet()) { + GridLongList assign = e.getValue(); + + newAssignment.set(e.getKey(), CacheGroupAffinityMessage.toNodes(assign, + nodesByOrder, + evts.discoveryCache())); + } + } + else + newAssignment = idealAssignment; + + aff.initialize(evts.topologyVersion(), cachedAssignment(aff, newAssignment, affCache)); + } + }); + } + + /** + * @param fut Current exchange future. + * @param msg Message finish message. + * @param resTopVer Result topology version. + * @throws IgniteCheckedException If failed. + */ + public void onLocalJoin(final GridDhtPartitionsExchangeFuture fut, + GridDhtPartitionsFullMessage msg, + final AffinityTopologyVersion resTopVer) + throws IgniteCheckedException { + final Set affReq = fut.context().groupsAffinityRequestOnJoin(); + + final Map nodesByOrder = new HashMap<>(); + + final Map joinedNodeAff = msg.joinedNodeAffinity(); + + assert !F.isEmpty(joinedNodeAff) : msg; + assert joinedNodeAff.size() >= affReq.size(); + + forAllCacheGroups(false, new IgniteInClosureX() { + @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException { + ExchangeDiscoveryEvents evts = fut.context().events(); + + CacheGroupContext grp = cctx.cache().cacheGroup(aff.groupId()); + + assert grp != null; + + if (affReq.contains(aff.groupId())) { + assert AffinityTopologyVersion.NONE.equals(aff.lastVersion()); + + CacheGroupAffinityMessage affMsg = joinedNodeAff.get(aff.groupId()); + + assert affMsg != null; + + List> assignments = affMsg.createAssignments(nodesByOrder, evts.discoveryCache()); + + assert resTopVer.equals(evts.topologyVersion()); + + List> idealAssign = + affMsg.createIdealAssignments(nodesByOrder, evts.discoveryCache()); + + if (idealAssign != null) + aff.idealAssignment(idealAssign); + else { + assert !aff.centralizedAffinityFunction(); + + // Calculate ideal assignments. + aff.calculate(evts.topologyVersion(), evts.lastEvent(), evts.discoveryCache()); + } + + aff.initialize(evts.topologyVersion(), assignments); + } + else if (fut.cacheGroupAddedOnExchange(aff.groupId(), grp.receivedFrom())) + calculateAndInit(evts, aff, evts.topologyVersion()); + + grp.topology().initPartitionsWhenAffinityReady(resTopVer, fut); + } + }); + } + + /** + * @param fut Current exchange future. + * @param crd Coordinator flag. + * @throws IgniteCheckedException If failed. + */ + public void onServerJoinWithExchangeMergeProtocol(GridDhtPartitionsExchangeFuture fut, boolean crd) + throws IgniteCheckedException { + final ExchangeDiscoveryEvents evts = fut.context().events(); + + assert fut.context().mergeExchanges(); + assert evts.hasServerJoin() && !evts.hasServerLeft(); + + WaitRebalanceInfo waitRebalanceInfo = initAffinityOnNodeJoin(fut, crd); + + this.waitInfo = waitRebalanceInfo != null && !waitRebalanceInfo.empty() ? waitRebalanceInfo : null; + + WaitRebalanceInfo info = this.waitInfo; + + if (crd) { + if (log.isDebugEnabled()) { + log.debug("Computed new affinity after node join [topVer=" + evts.topologyVersion() + + ", waitGrps=" + (info != null ? groupNames(info.waitGrps.keySet()) : null) + ']'); + } + } + } + + /** + * @param fut Current exchange future. + * @return Computed difference with ideal affinity. + * @throws IgniteCheckedException If failed. + */ + public Map onServerLeftWithExchangeMergeProtocol( + final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException + { + final ExchangeDiscoveryEvents evts = fut.context().events(); + + assert fut.context().mergeExchanges(); + assert evts.hasServerLeft(); + + forAllRegisteredCacheGroups(new IgniteInClosureX() { + @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { + AffinityTopologyVersion topVer = evts.topologyVersion(); + + CacheGroupHolder cache = groupHolder(topVer, desc); + + List> assign = + cache.affinity().calculate(topVer, evts.lastEvent(), evts.discoveryCache()); + + if (!cache.rebalanceEnabled || fut.cacheGroupAddedOnExchange(desc.groupId(), desc.receivedFrom())) + cache.affinity().initialize(topVer, assign); + } + }); + + Map>> diff = initAffinityOnNodeLeft0(evts.topologyVersion(), + fut, + NODE_TO_ORDER, + true); + + return CacheGroupAffinityMessage.createAffinityDiffMessages(diff); + } + /** * Called on exchange initiated by server node join. * @@ -1215,9 +1395,9 @@ private boolean canCalculateAffinity(CacheGroupDescriptor desc, * @throws IgniteCheckedException If failed. */ public void onServerJoin(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException { - assert !fut.discoveryEvent().eventNode().isClient(); + assert !fut.firstEvent().eventNode().isClient(); - boolean locJoin = fut.discoveryEvent().eventNode().isLocal(); + boolean locJoin = fut.firstEvent().eventNode().isLocal(); WaitRebalanceInfo waitRebalanceInfo = null; @@ -1225,40 +1405,28 @@ public void onServerJoin(final GridDhtPartitionsExchangeFuture fut, boolean crd) if (crd) { forAllRegisteredCacheGroups(new IgniteInClosureX() { @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { - AffinityTopologyVersion topVer = fut.topologyVersion(); - - CacheGroupHolder cache = groupHolder(topVer, desc); + AffinityTopologyVersion topVer = fut.initialVersion(); - List> newAff = cache.affinity().calculate(topVer, - fut.discoveryEvent(), - fut.discoCache()); + CacheGroupHolder grpHolder = groupHolder(topVer, desc); - cache.affinity().initialize(topVer, newAff); + calculateAndInit(fut.events(), grpHolder.affinity(), topVer); } }); } else fetchAffinityOnJoin(fut); } - else { - waitRebalanceInfo = initAffinityOnNodeJoin(fut.topologyVersion(), - fut.discoveryEvent(), - fut.discoCache(), - crd); - } - - synchronized (mux) { - affCalcVer = fut.topologyVersion(); + else + waitRebalanceInfo = initAffinityOnNodeJoin(fut, crd); - this.waitInfo = waitRebalanceInfo != null && !waitRebalanceInfo.empty() ? waitRebalanceInfo : null; + this.waitInfo = waitRebalanceInfo != null && !waitRebalanceInfo.empty() ? waitRebalanceInfo : null; - WaitRebalanceInfo info = this.waitInfo; + WaitRebalanceInfo info = this.waitInfo; - if (crd) { - if (log.isDebugEnabled()) { - log.debug("Computed new affinity after node join [topVer=" + fut.topologyVersion() + - ", waitGrps=" + (info != null ? groupNames(info.waitGrps.keySet()) : null) + ']'); - } + if (crd) { + if (log.isDebugEnabled()) { + log.debug("Computed new affinity after node join [topVer=" + fut.initialVersion() + + ", waitGrps=" + (info != null ? groupNames(info.waitGrps.keySet()) : null) + ']'); } } } @@ -1295,12 +1463,28 @@ private String debugGroupName(int grpId) { return "Unknown group: " + grpId; } + /** + * @param evts Discovery events. + * @param aff Affinity. + * @param topVer Topology version. + */ + private void calculateAndInit(ExchangeDiscoveryEvents evts, + GridAffinityAssignmentCache aff, + AffinityTopologyVersion topVer) + { + List> assignment = aff.calculate(topVer, + evts.lastEvent(), + evts.discoveryCache()); + + aff.initialize(topVer, assignment); + } + /** * @param fut Exchange future. * @throws IgniteCheckedException If failed. */ private void fetchAffinityOnJoin(GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException { - AffinityTopologyVersion topVer = fut.topologyVersion(); + AffinityTopologyVersion topVer = fut.initialVersion(); List fetchFuts = new ArrayList<>(); @@ -1309,25 +1493,31 @@ private void fetchAffinityOnJoin(GridDhtPartitionsExchangeFuture fut) throws Ign continue; if (fut.cacheGroupAddedOnExchange(grp.groupId(), grp.receivedFrom())) { - List> assignment = grp.affinity().calculate(fut.topologyVersion(), - fut.discoveryEvent(), - fut.discoCache()); - - grp.affinity().initialize(fut.topologyVersion(), assignment); + // In case if merge is allowed do not calculate affinity since it can change on exchange end. + if (!fut.context().mergeExchanges()) + calculateAndInit(fut.events(), grp.affinity(), topVer); } else { - CacheGroupDescriptor grpDesc = caches.group(grp.groupId()); + if (fut.context().fetchAffinityOnJoin()) { + CacheGroupDescriptor grpDesc = caches.group(grp.groupId()); - assert grpDesc != null : grp.cacheOrGroupName(); + assert grpDesc != null : grp.cacheOrGroupName(); - GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(cctx, - grpDesc.groupId(), - topVer, - fut.discoCache()); + GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(cctx, + grpDesc.groupId(), + topVer, + fut.events().discoveryCache()); - fetchFut.init(false); + fetchFut.init(false); - fetchFuts.add(fetchFut); + fetchFuts.add(fetchFut); + } + else { + if (fut.events().discoveryCache().serverNodes().size() > 0) + fut.context().addGroupAffinityRequestOnJoin(grp.groupId()); + else + calculateAndInit(fut.events(), grp.affinity(), topVer); + } } } @@ -1336,9 +1526,9 @@ private void fetchAffinityOnJoin(GridDhtPartitionsExchangeFuture fut) throws Ign Integer grpId = fetchFut.groupId(); - fetchAffinity(fut.topologyVersion(), - fut.discoveryEvent(), - fut.discoCache(), + fetchAffinity(topVer, + fut.events().lastEvent(), + fut.events().discoveryCache(), cctx.cache().cacheGroup(grpId).affinity(), fetchFut); } @@ -1398,7 +1588,7 @@ private GridDhtAffinityAssignmentResponse fetchAffinity(AffinityTopologyVersion * @return {@code True} if affinity should be assigned by coordinator. */ public boolean onServerLeft(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException { - ClusterNode leftNode = fut.discoveryEvent().eventNode(); + ClusterNode leftNode = fut.firstEvent().eventNode(); assert !leftNode.isClient() : leftNode; @@ -1406,23 +1596,21 @@ public boolean onServerLeft(final GridDhtPartitionsExchangeFuture fut, boolean c // Need initialize CacheGroupHolders if this node become coordinator on this exchange. forAllRegisteredCacheGroups(new IgniteInClosureX() { @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { - CacheGroupHolder cache = groupHolder(fut.topologyVersion(), desc); + CacheGroupHolder cache = groupHolder(fut.initialVersion(), desc); - cache.aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + cache.aff.calculate(fut.initialVersion(), fut.firstEvent(), fut.firstEventCache()); } }); } else { forAllCacheGroups(false, new IgniteInClosureX() { @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException { - aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + aff.calculate(fut.initialVersion(), fut.firstEvent(), fut.firstEventCache()); } }); } synchronized (mux) { - affCalcVer = fut.topologyVersion(); - this.waitInfo = null; } @@ -1431,13 +1619,16 @@ public boolean onServerLeft(final GridDhtPartitionsExchangeFuture fut, boolean c /** * @param fut Exchange future. + * @param newAff {@code True} if there are no older nodes with affinity info available. * @throws IgniteCheckedException If failed. * @return Future completed when caches initialization is done. */ - public IgniteInternalFuture initCoordinatorCaches(final GridDhtPartitionsExchangeFuture fut) - throws IgniteCheckedException { + public IgniteInternalFuture initCoordinatorCaches(final GridDhtPartitionsExchangeFuture fut, + final boolean newAff) throws IgniteCheckedException { final List> futs = new ArrayList<>(); + final AffinityTopologyVersion topVer = fut.initialVersion(); + forAllRegisteredCacheGroups(new IgniteInClosureX() { @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { CacheGroupHolder grpHolder = grpHolders.get(desc.groupId()); @@ -1459,55 +1650,74 @@ public IgniteInternalFuture initCoordinatorCaches(final GridDhtPartitionsExch } ); - grpHolder = CacheGroupHolder2.create(cctx, desc, fut.topologyVersion(), null); + grpHolder = CacheGroupHolder2.create(cctx, desc, topVer, null); final GridAffinityAssignmentCache aff = grpHolder.affinity(); - List exchFuts = cctx.exchange().exchangeFutures(); + if (newAff) { + if (!aff.lastVersion().equals(topVer)) + calculateAndInit(fut.events(), aff, topVer); - int idx = exchFuts.indexOf(fut); + grpHolder.topology().beforeExchange(fut, true, false); + } + else { + List exchFuts = cctx.exchange().exchangeFutures(); - assert idx >= 0 && idx < exchFuts.size() - 1 : "Invalid exchange futures state [cur=" + idx + - ", total=" + exchFuts.size() + ']'; + int idx = exchFuts.indexOf(fut); - final GridDhtPartitionsExchangeFuture prev = exchFuts.get(idx + 1); + assert idx >= 0 && idx < exchFuts.size() - 1 : "Invalid exchange futures state [cur=" + idx + + ", total=" + exchFuts.size() + ']'; - if (log.isDebugEnabled()) { - log.debug("Need initialize affinity on coordinator [" + - "cacheGrp=" + desc.cacheOrGroupName() + - "prevAff=" + prev.topologyVersion() + ']'); - } + final GridDhtPartitionsExchangeFuture prev = exchFuts.get(idx + 1); - assert prev.topologyVersion().compareTo(fut.topologyVersion()) < 0 : prev; + assert prev.isDone() && prev.topologyVersion().compareTo(topVer) < 0 : prev; - GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(cctx, - desc.groupId(), - prev.topologyVersion(), - prev.discoCache()); + if (log.isDebugEnabled()) { + log.debug("Need initialize affinity on coordinator [" + + "cacheGrp=" + desc.cacheOrGroupName() + + "prevAff=" + prev.topologyVersion() + ']'); + } - fetchFut.init(false); + GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(cctx, + desc.groupId(), + prev.topologyVersion(), + prev.events().discoveryCache()); - final GridFutureAdapter affFut = new GridFutureAdapter<>(); + fetchFut.init(false); - fetchFut.listen(new IgniteInClosureX>() { - @Override public void applyx(IgniteInternalFuture fetchFut) - throws IgniteCheckedException { - fetchAffinity(prev.topologyVersion(), - prev.discoveryEvent(), - prev.discoCache(), - aff, (GridDhtAssignmentFetchFuture)fetchFut); + final GridFutureAdapter affFut = new GridFutureAdapter<>(); - aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache()); + fetchFut.listen(new IgniteInClosureX>() { + @Override public void applyx(IgniteInternalFuture fetchFut) + throws IgniteCheckedException { + fetchAffinity(prev.topologyVersion(), + prev.events().lastEvent(), + prev.events().discoveryCache(), + aff, + (GridDhtAssignmentFetchFuture)fetchFut); - affFut.onDone(fut.topologyVersion()); - } - }); + aff.calculate(topVer, fut.events().lastEvent(), fut.events().discoveryCache()); + + affFut.onDone(topVer); + } + }); - futs.add(affFut); + futs.add(affFut); + } } - else + else { grpHolder = new CacheGroupHolder1(grp, null); + if (newAff) { + GridAffinityAssignmentCache aff = grpHolder.affinity(); + + if (!aff.lastVersion().equals(topVer)) + calculateAndInit(fut.events(), aff, topVer); + + grpHolder.topology().beforeExchange(fut, true, false); + } + } + CacheGroupHolder old = grpHolders.put(grpHolder.groupId(), grpHolder); assert old == null : old; @@ -1565,18 +1775,15 @@ private CacheGroupHolder groupHolder(AffinityTopologyVersion topVer, final Cache } /** - * @param topVer Topology version. - * @param evt Discovery event. - * @param discoCache Discovery data cache. + * @param fut Current exchange future. * @param crd Coordinator flag. * @throws IgniteCheckedException If failed. * @return Rabalance info. */ - @Nullable private WaitRebalanceInfo initAffinityOnNodeJoin(final AffinityTopologyVersion topVer, - final DiscoveryEvent evt, - final DiscoCache discoCache, - boolean crd) + @Nullable private WaitRebalanceInfo initAffinityOnNodeJoin(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException { + final ExchangeDiscoveryEvents evts = fut.context().events(); + final Map>> affCache = new HashMap<>(); if (!crd) { @@ -1586,27 +1793,47 @@ private CacheGroupHolder groupHolder(AffinityTopologyVersion topVer, final Cache boolean latePrimary = grp.rebalanceEnabled(); - initAffinityOnNodeJoin(topVer, evt, discoCache, grp.affinity(), null, latePrimary, affCache); + initAffinityOnNodeJoin(evts, + evts.nodeJoined(grp.receivedFrom()), + grp.affinity(), + null, + latePrimary, + affCache); } return null; } else { - final WaitRebalanceInfo waitRebalanceInfo = new WaitRebalanceInfo(topVer); + final WaitRebalanceInfo waitRebalanceInfo = new WaitRebalanceInfo(evts.lastServerEventVersion()); forAllRegisteredCacheGroups(new IgniteInClosureX() { @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { - CacheGroupHolder cache = groupHolder(topVer, desc); + CacheGroupHolder cache = groupHolder(evts.topologyVersion(), desc); boolean latePrimary = cache.rebalanceEnabled; - initAffinityOnNodeJoin(topVer, - evt, - discoCache, + boolean grpAdded = evts.nodeJoined(desc.receivedFrom()); + + initAffinityOnNodeJoin(evts, + grpAdded, cache.affinity(), waitRebalanceInfo, latePrimary, affCache); + + if (grpAdded) { + AffinityAssignment aff = cache.aff.cachedAffinity(cache.aff.lastVersion()); + + assert evts.topologyVersion().equals(aff.topologyVersion()) : "Unexpected version [" + + "grp=" + cache.aff.cacheOrGroupName() + + ", evts=" + evts.topologyVersion() + + ", aff=" + cache.aff.lastVersion() + ']'; + + Map map = affinityFullMap(aff); + + for (GridDhtPartitionMap map0 : map.values()) + cache.topology().update(fut.exchangeId(), map0, true); + } } }); @@ -1614,25 +1841,57 @@ private CacheGroupHolder groupHolder(AffinityTopologyVersion topVer, final Cache } } + private Map affinityFullMap(AffinityAssignment aff) { + Map map = new HashMap<>(); + + for (int p = 0; p < aff.assignment().size(); p++) { + HashSet ids = aff.getIds(p); + + for (UUID nodeId : ids) { + GridDhtPartitionMap partMap = map.get(nodeId); + + if (partMap == null) { + partMap = new GridDhtPartitionMap(nodeId, + 1L, + aff.topologyVersion(), + new GridPartitionStateMap(), + false); + + map.put(nodeId, partMap); + } + + partMap.put(p, GridDhtPartitionState.OWNING); + } + } + + return map; + } + /** - * @param topVer Topology version. - * @param evt Discovery event. - * @param discoCache Discovery data cache. + * @param evts Discovery events processed during exchange. + * @param addedOnExchnage {@code True} if cache group was added during this exchange. * @param aff Affinity. * @param rebalanceInfo Rebalance information. * @param latePrimary If {@code true} delays primary assignment if it is not owner. * @param affCache Already calculated assignments (to reduce data stored in history). * @throws IgniteCheckedException If failed. */ - private void initAffinityOnNodeJoin(AffinityTopologyVersion topVer, - DiscoveryEvent evt, - DiscoCache discoCache, + private void initAffinityOnNodeJoin( + ExchangeDiscoveryEvents evts, + boolean addedOnExchnage, GridAffinityAssignmentCache aff, WaitRebalanceInfo rebalanceInfo, boolean latePrimary, Map>> affCache) throws IgniteCheckedException { + if (addedOnExchnage) { + if (!aff.lastVersion().equals(evts.topologyVersion())) + calculateAndInit(evts, aff, evts.topologyVersion()); + + return; + } + AffinityTopologyVersion affTopVer = aff.lastVersion(); assert affTopVer.topologyVersion() > 0 : "Affinity is not initialized [grp=" + aff.cacheOrGroupName() + @@ -1642,7 +1901,7 @@ private void initAffinityOnNodeJoin(AffinityTopologyVersion topVer, assert aff.idealAssignment() != null : "Previous assignment is not available."; - List> idealAssignment = aff.calculate(topVer, evt, discoCache); + List> idealAssignment = aff.calculate(evts.topologyVersion(), evts.lastEvent(), evts.discoveryCache()); List> newAssignment = null; if (latePrimary) { @@ -1654,7 +1913,7 @@ private void initAffinityOnNodeJoin(AffinityTopologyVersion topVer, ClusterNode newPrimary = newNodes.size() > 0 ? newNodes.get(0) : null; if (curPrimary != null && newPrimary != null && !curPrimary.equals(newPrimary)) { - assert cctx.discovery().node(topVer, curPrimary.id()) != null : curPrimary; + assert cctx.discovery().node(evts.topologyVersion(), curPrimary.id()) != null : curPrimary; List nodes0 = latePrimaryAssignment(aff, p, @@ -1673,7 +1932,7 @@ private void initAffinityOnNodeJoin(AffinityTopologyVersion topVer, if (newAssignment == null) newAssignment = idealAssignment; - aff.initialize(topVer, cachedAssignment(aff, newAssignment, affCache)); + aff.initialize(evts.topologyVersion(), cachedAssignment(aff, newAssignment, affCache)); } /** @@ -1737,7 +1996,9 @@ private List latePrimaryAssignment( */ public IgniteInternalFuture>>> initAffinityOnNodeLeft( final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException { - IgniteInternalFuture initFut = initCoordinatorCaches(fut); + assert !fut.context().mergeExchanges(); + + IgniteInternalFuture initFut = initCoordinatorCaches(fut, false); if (initFut != null && !initFut.isDone()) { final GridFutureAdapter>>> resFut = new GridFutureAdapter<>(); @@ -1745,7 +2006,7 @@ public IgniteInternalFuture>>> initAffinity initFut.listen(new IgniteInClosure>() { @Override public void apply(IgniteInternalFuture initFut) { try { - resFut.onDone(initAffinityOnNodeLeft0(fut)); + resFut.onDone(initAffinityOnNodeLeft0(fut.initialVersion(), fut, NODE_TO_ID, false)); } catch (IgniteCheckedException e) { resFut.onDone(e); @@ -1756,29 +2017,33 @@ public IgniteInternalFuture>>> initAffinity return resFut; } else - return new GridFinishedFuture<>(initAffinityOnNodeLeft0(fut)); + return new GridFinishedFuture<>(initAffinityOnNodeLeft0(fut.initialVersion(), fut, NODE_TO_ID, false)); } /** + * @param topVer Topology version. * @param fut Exchange future. + * @param c Closure converting affinity diff. + * @param initAff {@code True} if need initialize affinity. * @return Affinity assignment. * @throws IgniteCheckedException If failed. */ - private Map>> initAffinityOnNodeLeft0(final GridDhtPartitionsExchangeFuture fut) + private Map>> initAffinityOnNodeLeft0(final AffinityTopologyVersion topVer, + final GridDhtPartitionsExchangeFuture fut, + final IgniteClosure c, + final boolean initAff) throws IgniteCheckedException { - final AffinityTopologyVersion topVer = fut.topologyVersion(); - - final WaitRebalanceInfo waitRebalanceInfo = new WaitRebalanceInfo(topVer); + final WaitRebalanceInfo waitRebalanceInfo = new WaitRebalanceInfo(fut.context().events().lastServerEventVersion()); - final Collection aliveNodes = cctx.discovery().nodes(topVer); + final Collection aliveNodes = fut.context().events().discoveryCache().serverNodes(); - final Map>> assignment = new HashMap<>(); + final Map>> assignment = new HashMap<>(); forAllRegisteredCacheGroups(new IgniteInClosureX() { @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException { - CacheGroupHolder grpHolder = groupHolder(fut.topologyVersion(), desc); + CacheGroupHolder grpHolder = groupHolder(topVer, desc); - if (!grpHolder.rebalanceEnabled) + if (!grpHolder.rebalanceEnabled || fut.cacheGroupAddedOnExchange(desc.groupId(), desc.receivedFrom())) return; AffinityTopologyVersion affTopVer = grpHolder.affinity().lastVersion(); @@ -1791,14 +2056,21 @@ private Map>> initAffinityOnNodeLeft0(final Gri assert newAssignment != null; - GridDhtPartitionTopology top = grpHolder.topology(fut); + List> newAssignment0 = initAff ? new ArrayList<>(newAssignment) : null; - Map> cacheAssignment = null; + GridDhtPartitionTopology top = grpHolder.topology(); + + Map> cacheAssignment = null; for (int p = 0; p < newAssignment.size(); p++) { List newNodes = newAssignment.get(p); List curNodes = curAssignment.get(p); + assert aliveNodes.containsAll(newNodes) : "Invalid new assignment [grp=" + grpHolder.aff.cacheOrGroupName() + + ", nodes=" + newNodes + + ", topVer=" + fut.context().events().discoveryCache().version() + + ", evts=" + fut.context().events().events() + "]"; + ClusterNode curPrimary = curNodes.size() > 0 ? curNodes.get(0) : null; ClusterNode newPrimary = newNodes.size() > 0 ? newNodes.get(0) : null; @@ -1828,7 +2100,8 @@ private Map>> initAffinityOnNodeLeft0(final Gri for (int i = 1; i < curNodes.size(); i++) { ClusterNode curNode = curNodes.get(i); - if (top.partitionState(curNode.id(), p) == GridDhtPartitionState.OWNING) { + if (top.partitionState(curNode.id(), p) == GridDhtPartitionState.OWNING && + aliveNodes.contains(curNode)) { newNodes0 = latePrimaryAssignment(grpHolder.affinity(), p, curNode, @@ -1859,21 +2132,35 @@ private Map>> initAffinityOnNodeLeft0(final Gri } if (newNodes0 != null) { + assert aliveNodes.containsAll(newNodes0) : "Invalid late assignment [grp=" + grpHolder.aff.cacheOrGroupName() + + ", nodes=" + newNodes + + ", topVer=" + fut.context().events().discoveryCache().version() + + ", evts=" + fut.context().events().events() + "]"; + + if (newAssignment0 != null) + newAssignment0.set(p, newNodes0); + if (cacheAssignment == null) cacheAssignment = new HashMap<>(); - cacheAssignment.put(p, toIds0(newNodes0)); + List n = new ArrayList<>(newNodes0.size()); + + for (int i = 0; i < newNodes0.size(); i++) + n.add(c.apply(newNodes0.get(i))); + + cacheAssignment.put(p, n); } } if (cacheAssignment != null) assignment.put(grpHolder.groupId(), cacheAssignment); + + if (initAff) + grpHolder.affinity().initialize(topVer, newAssignment0); } }); synchronized (mux) { - assert affCalcVer.equals(topVer); - this.waitInfo = !waitRebalanceInfo.empty() ? waitRebalanceInfo : null; WaitRebalanceInfo info = this.waitInfo; @@ -1887,6 +2174,13 @@ private Map>> initAffinityOnNodeLeft0(final Gri return assignment; } + /** + * @return All registered cache groups. + */ + public Map cacheGroups() { + return caches.registeredGrps; + } + /** * */ @@ -1982,10 +2276,9 @@ int partitions() { } /** - * @param fut Exchange future. * @return Cache topology. */ - abstract GridDhtPartitionTopology topology(GridDhtPartitionsExchangeFuture fut); + abstract GridDhtPartitionTopology topology(); /** * @return Affinity. @@ -2020,7 +2313,7 @@ private class CacheGroupHolder1 extends CacheGroupHolder { } /** {@inheritDoc} */ - @Override public GridDhtPartitionTopology topology(GridDhtPartitionsExchangeFuture fut) { + @Override public GridDhtPartitionTopology topology() { return grp.topology(); } } @@ -2096,8 +2389,8 @@ static CacheGroupHolder2 create( } /** {@inheritDoc} */ - @Override public GridDhtPartitionTopology topology(GridDhtPartitionsExchangeFuture fut) { - return cctx.exchange().clientTopology(groupId(), fut); + @Override public GridDhtPartitionTopology topology() { + return cctx.exchange().clientTopology(groupId()); } } 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 14eb3628e5036..5e5e02e74c4e0 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 @@ -37,15 +37,15 @@ 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.persistence.GridCacheOffheapManager; -import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicy; -import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; -import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffinityAssignmentRequest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffinityAssignmentResponse; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopologyImpl; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader; +import org.apache.ignite.internal.processors.cache.persistence.GridCacheOffheapManager; +import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicy; +import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; +import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList; import org.apache.ignite.internal.processors.cache.query.continuous.CounterSkipContext; import org.apache.ignite.internal.processors.query.QueryUtils; import org.apache.ignite.internal.util.typedef.CI1; @@ -59,6 +59,7 @@ import org.jetbrains.annotations.Nullable; import static org.apache.ignite.cache.CacheMode.LOCAL; +import static org.apache.ignite.cache.CacheMode.REPLICATED; import static org.apache.ignite.cache.CacheRebalanceMode.NONE; import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_PART_UNLOADED; import static org.apache.ignite.internal.managers.communication.GridIoPolicy.AFFINITY_POOL; @@ -341,7 +342,7 @@ private void removeCacheContext(GridCacheContext cctx) { public GridCacheContext singleCacheContext() { List caches = this.caches; - assert !sharedGroup() && caches.size() == 1; + assert !sharedGroup() && caches.size() == 1 : ctx.kernalContext().isStopping(); return caches.get(0); } @@ -580,6 +581,13 @@ public boolean isLocal() { return ccfg.getCacheMode() == LOCAL; } + /** + * @return {@code True} if cache is local. + */ + public boolean isReplicated() { + return ccfg.getCacheMode() == REPLICATED; + } + /** * @return Cache configuration. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java index ad0dcc9536a8c..f4c1392ac2f30 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CachePartitionExchangeWorkerTask.java @@ -21,5 +21,8 @@ * Cache partition exchange worker task marker interface. */ public interface CachePartitionExchangeWorkerTask { - // No-op. + /** + * @return {@code False} if exchange merge should stop if this task is found in exchange worker queue. + */ + boolean skipForExchangeMerge(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheChangeDummyDiscoveryMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheChangeDummyDiscoveryMessage.java index 68bca274e6837..44f6002c38291 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheChangeDummyDiscoveryMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheChangeDummyDiscoveryMessage.java @@ -60,6 +60,11 @@ public ClientCacheChangeDummyDiscoveryMessage(UUID reqId, this.cachesToClose = cachesToClose; } + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return true; + } + /** * @return Start request ID. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheUpdateTimeout.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheUpdateTimeout.java index aab3a3ea5e12a..73cc69a8dcaac 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheUpdateTimeout.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClientCacheUpdateTimeout.java @@ -36,6 +36,11 @@ class ClientCacheUpdateTimeout extends GridTimeoutObjectAdapter implements Cache this.cctx = cctx; } + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return true; + } + /** {@inheritDoc} */ @Override public void onTimeout() { if (!cctx.kernalContext().isStopping()) 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 1a05b96917181..23454e83fd947 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 @@ -793,11 +793,28 @@ private Serializable joinDiscoveryData() { return result; } + /** + * @param joinedNodeId Joined node ID. + * @return {@code True} if there are new caches received from joined node. + */ + boolean hasCachesReceivedFromJoin(UUID joinedNodeId) { + for (DynamicCacheDescriptor desc : registeredCaches.values()) { + if (desc.staticallyConfigured()) { + assert desc.receivedFrom() != null : desc; + + if (joinedNodeId.equals(desc.receivedFrom())) + return true; + } + } + + return false; + } + /** * @param joinedNodeId Joined node ID. * @return New caches received from joined node. */ - @NotNull public List cachesReceivedFromJoin(UUID joinedNodeId) { + List cachesReceivedFromJoin(UUID joinedNodeId) { assert joinedNodeId != null; List started = null; @@ -1707,8 +1724,7 @@ private static class CacheComparators { * DIRECT comparator for cache descriptors (first system caches). */ static Comparator DIRECT = new Comparator() { - @Override - public int compare(DynamicCacheDescriptor o1, DynamicCacheDescriptor o2) { + @Override public int compare(DynamicCacheDescriptor o1, DynamicCacheDescriptor o2) { if (!o1.cacheType().userCache()) return -1; if (!o2.cacheType().userCache()) 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 new file mode 100644 index 0000000000000..4046c98b29414 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeContext.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.cache; + +import java.util.HashSet; +import java.util.Set; +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.typedef.internal.S; +import org.jetbrains.annotations.Nullable; + +import static org.apache.ignite.IgniteSystemProperties.getBoolean; +import static org.apache.ignite.internal.events.DiscoveryCustomEvent.EVT_DISCOVERY_CUSTOM_EVT; +import static org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager.exchangeProtocolVersion; + +/** + * + */ +public class ExchangeContext { + /** */ + public static final String IGNITE_EXCHANGE_COMPATIBILITY_VER_1 = "IGNITE_EXCHANGE_COMPATIBILITY_VER_1"; + + /** Cache groups to request affinity for during local join exchange. */ + private Set requestGrpsAffOnJoin; + + /** Per-group affinity fetch on join (old protocol). */ + private boolean fetchAffOnJoin; + + /** Merges allowed flag. */ + private final boolean merge; + + /** */ + private final ExchangeDiscoveryEvents evts; + + /** */ + private final boolean compatibilityNode = getBoolean(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, false); + + /** + * @param crd Coordinator flag. + * @param fut Exchange future. + */ + public ExchangeContext(boolean crd, GridDhtPartitionsExchangeFuture fut) { + int protocolVer = exchangeProtocolVersion(fut.firstEventCache().minimumNodeVersion()); + + if (compatibilityNode || (crd && fut.localJoinExchange())) { + fetchAffOnJoin = true; + + merge = false; + } + else { + boolean startCaches = fut.exchangeId().isJoined() && + fut.sharedContext().cache().hasCachesReceivedFromJoin(fut.exchangeId().eventNode()); + + fetchAffOnJoin = protocolVer == 1; + + merge = !startCaches && + protocolVer > 1 && + fut.firstEvent().type() != EVT_DISCOVERY_CUSTOM_EVT; + } + + evts = new ExchangeDiscoveryEvents(fut); + } + + /** + * @param node Node. + * @return {@code True} if node supports exchange merge protocol. + */ + boolean supportsMergeExchanges(ClusterNode node) { + return !compatibilityNode && exchangeProtocolVersion(node.version()) > 1; + } + + /** + * @return Discovery events. + */ + public ExchangeDiscoveryEvents events() { + return evts; + } + + /** + * @return {@code True} if on local join need fetch affinity per-group (old protocol), + * otherwise affinity is sent in {@link GridDhtPartitionsFullMessage}. + */ + public boolean fetchAffinityOnJoin() { + return fetchAffOnJoin; + } + + /** + * @param grpId Cache group ID. + */ + void addGroupAffinityRequestOnJoin(Integer grpId) { + if (requestGrpsAffOnJoin == null) + requestGrpsAffOnJoin = new HashSet<>(); + + requestGrpsAffOnJoin.add(grpId); + } + + /** + * @return Groups to request affinity for. + */ + @Nullable public Set groupsAffinityRequestOnJoin() { + return requestGrpsAffOnJoin; + } + + /** + * @return {@code True} if exchanges merge is allowed during current exchange. + */ + public boolean mergeExchanges() { + return merge; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(ExchangeContext.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeDiscoveryEvents.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeDiscoveryEvents.java new file mode 100644 index 0000000000000..d4fbe605f02f5 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeDiscoveryEvents.java @@ -0,0 +1,262 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.UUID; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.events.CacheEvent; +import org.apache.ignite.events.DiscoveryEvent; +import org.apache.ignite.events.Event; +import org.apache.ignite.events.EventType; +import org.apache.ignite.internal.managers.discovery.DiscoCache; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture; +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.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.events.DiscoveryCustomEvent.EVT_DISCOVERY_CUSTOM_EVT; + +/** + * Discovery events processed in single exchange (contain multiple events if exchanges for multiple + * discovery events are merged into single exchange). + */ +public class ExchangeDiscoveryEvents { + /** Last event version. */ + private AffinityTopologyVersion topVer; + + /** Last server join/fail event version. */ + private AffinityTopologyVersion srvEvtTopVer; + + /** Discovery data cache for last event. */ + private DiscoCache discoCache; + + /** Last event. */ + private DiscoveryEvent lastEvt; + + /** Last server join/fail event. */ + private DiscoveryEvent lastSrvEvt; + + /** All events. */ + private List evts = new ArrayList<>(); + + /** Server join flag. */ + private boolean srvJoin; + + /** Sever left flag. */ + private boolean srvLeft; + + /** + * @param fut Current exchange future. + */ + ExchangeDiscoveryEvents(GridDhtPartitionsExchangeFuture fut) { + addEvent(fut.initialVersion(), fut.firstEvent(), fut.firstEventCache()); + } + + /** + * @param fut Current exchange future. + */ + public void processEvents(GridDhtPartitionsExchangeFuture fut) { + for (DiscoveryEvent evt : evts) { + if (evt.type() == EVT_NODE_LEFT || evt.type() == EVT_NODE_FAILED) + fut.sharedContext().mvcc().removeExplicitNodeLocks(evt.eventNode().id(), fut.initialVersion()); + } + + if (hasServerLeft()) + warnNoAffinityNodes(fut.sharedContext()); + } + + /** + * @param nodeId Node ID. + * @return {@code True} if has join event for give node. + */ + public boolean nodeJoined(UUID nodeId) { + for (int i = 0; i < evts.size(); i++) { + DiscoveryEvent evt = evts.get(i); + + if (evt.type() == EVT_NODE_JOINED && nodeId.equals(evt.eventNode().id())) + return true; + } + + return false; + } + + /** + * @return Last server join/fail event version. + */ + AffinityTopologyVersion lastServerEventVersion() { + assert srvEvtTopVer != null : this; + + return srvEvtTopVer; + } + + /** + * @param topVer Event version. + * @param evt Event. + * @param cache Discovery data cache for given topology version. + */ + void addEvent(AffinityTopologyVersion topVer, DiscoveryEvent evt, DiscoCache cache) { + assert evts.isEmpty() || topVer.compareTo(this.topVer) > 0 : topVer; + + evts.add(evt); + + this.topVer = topVer; + this.lastEvt = evt; + this.discoCache = cache; + + if (evt.type() != EVT_DISCOVERY_CUSTOM_EVT) { + ClusterNode node = evt.eventNode(); + + if (!CU.clientNode(node)) { + lastSrvEvt = evt; + + srvEvtTopVer = new AffinityTopologyVersion(evt.topologyVersion(), 0); + + if (evt.type()== EVT_NODE_JOINED) + srvJoin = true; + else { + assert evt.type() == EVT_NODE_LEFT || evt.type() == EVT_NODE_FAILED : evt; + + srvLeft = !CU.clientNode(node); + } + } + } + } + + /** + * @return All events. + */ + public List events() { + return evts; + } + + /** + * @param evt Event. + * @return {@code True} if given event is {@link EventType#EVT_NODE_FAILED} or {@link EventType#EVT_NODE_LEFT}. + */ + public static boolean serverLeftEvent(DiscoveryEvent evt) { + return ((evt.type() == EVT_NODE_FAILED || evt.type() == EVT_NODE_LEFT) && !CU.clientNode(evt.eventNode())); + } + + /** + * @return Discovery data cache for last event. + */ + public DiscoCache discoveryCache() { + return discoCache; + } + + /** + * @return Last event. + */ + public DiscoveryEvent lastEvent() { + return lastSrvEvt != null ? lastSrvEvt : lastEvt; + } + + /** + * @return Last event version. + */ + public AffinityTopologyVersion topologyVersion() { + return topVer; + } + + /** + * @return {@code True} if has event for server join. + */ + public boolean hasServerJoin() { + return srvJoin; + } + + /** + * @return {@code True} if has event for server leave. + */ + public boolean hasServerLeft() { + return srvLeft; + } + + /** + * @param cctx Context. + */ + public void warnNoAffinityNodes(GridCacheSharedContext cctx) { + List cachesWithoutNodes = null; + + for (DynamicCacheDescriptor cacheDesc : cctx.cache().cacheDescriptors().values()) { + if (discoCache.cacheGroupAffinityNodes(cacheDesc.groupId()).isEmpty()) { + if (cachesWithoutNodes == null) + cachesWithoutNodes = new ArrayList<>(); + + cachesWithoutNodes.add(cacheDesc.cacheName()); + + // Fire event even if there is no client cache started. + if (cctx.gridEvents().isRecordable(EventType.EVT_CACHE_NODES_LEFT)) { + Event evt = new CacheEvent( + cacheDesc.cacheName(), + cctx.localNode(), + cctx.localNode(), + "All server nodes have left the cluster.", + EventType.EVT_CACHE_NODES_LEFT, + 0, + false, + null, + null, + null, + null, + false, + null, + false, + null, + null, + null + ); + + cctx.gridEvents().record(evt); + } + } + } + + if (cachesWithoutNodes != null) { + StringBuilder sb = + new StringBuilder("All server nodes for the following caches have left the cluster: "); + + for (int i = 0; i < cachesWithoutNodes.size(); i++) { + String cache = cachesWithoutNodes.get(i); + + sb.append('\'').append(cache).append('\''); + + if (i != cachesWithoutNodes.size() - 1) + sb.append(", "); + } + + IgniteLogger log = cctx.logger(getClass()); + + U.quietAndWarn(log, sb.toString()); + + U.quietAndWarn(log, "Must have server nodes for caches to operate."); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(ExchangeDiscoveryEvents.class, this); + } +} 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 4ba4e48265ebd..fed716c84437b 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 @@ -3328,8 +3328,10 @@ public CacheMetricsImpl metrics0() { try { KeyCacheObject cacheKey = ctx.toCacheKeyObject(key); - GridCacheEntryEx e = entry0(cacheKey, ctx.discovery().topologyVersionEx(), - false, false); + GridCacheEntryEx e = entry0(cacheKey, + ctx.shared().exchange().readyAffinityVersion(), + false, + false); if (e == null) return false; @@ -5078,7 +5080,7 @@ public IgniteInternalFuture op(final GridNearTxLocal tx, CacheOperationContex return op(tx, (AffinityTopologyVersion)null); // Tx needs affinity for entry creation, wait when affinity is ready to avoid blocking inside async operation. - final AffinityTopologyVersion topVer = ctx.shared().exchange().topologyVersion(); + final AffinityTopologyVersion topVer = ctx.shared().exchange().readyAffinityVersion(); IgniteInternalFuture topFut = ctx.shared().exchange().affinityReadyFuture(topVer); 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 33db2ff73139b..b6faf47785953 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 @@ -59,7 +59,6 @@ import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager; import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; -import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicy; import org.apache.ignite.internal.processors.cache.datastructures.CacheDataStructuresManager; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; @@ -72,6 +71,7 @@ import org.apache.ignite.internal.processors.cache.dr.GridCacheDrManager; import org.apache.ignite.internal.processors.cache.jta.CacheJtaManagerAdapter; import org.apache.ignite.internal.processors.cache.local.GridLocalCache; +import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicy; import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager; import org.apache.ignite.internal.processors.cache.query.continuous.CacheContinuousQueryManager; import org.apache.ignite.internal.processors.cache.store.CacheStoreManager; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java index 9f1873e01671a..45edc531faa6b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java @@ -479,6 +479,24 @@ private void sendMessageForMissedHandler( cctx.gridIO().addMessageListener(TOPIC_CACHE, lsnr); } + /** + * @return Lock or {@code null} if node is stopping. + */ + @Nullable public Lock readLock() { + Lock lock = rw.readLock(); + + if (!lock.tryLock()) + return null; + + if (stopping) { + lock.unlock(); + + return null; + } + + return lock; + } + /** * */ @@ -1051,27 +1069,34 @@ private void processMessage(UUID nodeId, GridCacheMessage msg, IgniteBiInClosure throw e; } finally { - // Reset thread local context. - cctx.tm().resetContext(); + onMessageProcessed(msg); + } + } + + /** + * @param msg Message. + */ + public void onMessageProcessed(GridCacheMessage msg) { + // Reset thread local context. + cctx.tm().resetContext(); - GridCacheMvccManager mvcc = cctx.mvcc(); + GridCacheMvccManager mvcc = cctx.mvcc(); - if (mvcc != null) - mvcc.contextReset(); + if (mvcc != null) + mvcc.contextReset(); - // Unwind eviction notifications. - if (msg instanceof IgniteTxStateAware) { - IgniteTxState txState = ((IgniteTxStateAware)msg).txState(); + // Unwind eviction notifications. + if (msg instanceof IgniteTxStateAware) { + IgniteTxState txState = ((IgniteTxStateAware)msg).txState(); - if (txState != null) - txState.unwindEvicts(cctx); - } - else if (msg instanceof GridCacheIdMessage) { - GridCacheContext ctx = cctx.cacheContext(((GridCacheIdMessage)msg).cacheId()); + if (txState != null) + txState.unwindEvicts(cctx); + } + else if (msg instanceof GridCacheIdMessage) { + GridCacheContext ctx = cctx.cacheContext(((GridCacheIdMessage)msg).cacheId()); - if (ctx != null) - CU.unwindEvicts(ctx); - } + if (ctx != null) + CU.unwindEvicts(ctx); } } 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 2ee80a6f0e168..d991c86f9bd6b 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 @@ -38,9 +38,6 @@ import org.apache.ignite.internal.pagemem.wal.record.DataRecord; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult.UpdateOutcome; -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.MemoryPolicy; 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.atomic.GridDhtAtomicAbstractUpdateFuture; @@ -49,6 +46,9 @@ 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.persistence.CacheDataRow; +import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter; +import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicy; import org.apache.ignite.internal.processors.cache.query.continuous.CacheContinuousQueryListener; import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry; 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 a0b24f879b81a..1d7fed57bc598 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 @@ -89,6 +89,7 @@ import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.util.GridListSet; import org.apache.ignite.internal.util.GridPartitionStateMap; +import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.internal.util.future.GridCompoundFuture; import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.tostring.GridToStringExclude; @@ -102,6 +103,7 @@ import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.internal.util.worker.GridWorker; import org.apache.ignite.lang.IgniteBiInClosure; +import org.apache.ignite.lang.IgniteProductVersion; import org.apache.ignite.lang.IgniteUuid; import org.apache.ignite.thread.IgniteThread; import org.jetbrains.annotations.Nullable; @@ -126,9 +128,16 @@ */ public class GridCachePartitionExchangeManager extends GridCacheSharedManagerAdapter { /** Exchange history size. */ - private static final int EXCHANGE_HISTORY_SIZE = + private final int EXCHANGE_HISTORY_SIZE = IgniteSystemProperties.getInteger(IgniteSystemProperties.IGNITE_EXCHANGE_HISTORY_SIZE, 1_000); + /** */ + private final long IGNITE_EXCHANGE_MERGE_DELAY = + IgniteSystemProperties.getLong(IgniteSystemProperties.IGNITE_EXCHANGE_MERGE_DELAY, 0); + + /** */ + private static final IgniteProductVersion EXCHANGE_PROTOCOL_2_SINCE = IgniteProductVersion.fromString("2.1.4"); + /** Atomic reference for pending timeout object. */ private AtomicReference pendingResend = new AtomicReference<>(); @@ -176,7 +185,7 @@ public class GridCachePartitionExchangeManager extends GridCacheSharedMana * discovery event. In case if remote node will retry partition exchange, completed future will indicate * that full partition map should be sent to requesting node right away. */ - private ExchangeFutureSet exchFuts = new ExchangeFutureSet(); + private ExchangeFutureSet exchFuts = new ExchangeFutureSet(EXCHANGE_HISTORY_SIZE); /** */ private volatile IgniteCheckedException stopErr; @@ -193,6 +202,12 @@ public class GridCachePartitionExchangeManager extends GridCacheSharedMana /** Events received while cluster state transition was in progress. */ private final List pendingEvts = new ArrayList<>(); + /** */ + private final GridFutureAdapter crdInitFut = new GridFutureAdapter(); + + /** For tests only. */ + private volatile AffinityTopologyVersion exchMergeTestWaitVer; + /** Discovery listener. */ private final DiscoveryEventListener discoLsnr = new DiscoveryEventListener() { @Override public void onEvent(DiscoveryEvent evt, DiscoCache cache) { @@ -288,7 +303,23 @@ private void processEventInactive(DiscoveryEvent evt, DiscoCache cache) { cctx.io().addCacheHandler(0, GridDhtPartitionsSingleMessage.class, new MessageHandler() { - @Override public void onMessage(ClusterNode node, GridDhtPartitionsSingleMessage msg) { + @Override public void onMessage(final ClusterNode node, final GridDhtPartitionsSingleMessage msg) { + if (!crdInitFut.isDone() && !msg.restoreState()) { + GridDhtPartitionExchangeId exchId = msg.exchangeId(); + + log.info("Waiting for coordinator initialization [node=" + node.id() + + ", nodeOrder=" + node.order() + + ", ver=" + (exchId != null ? exchId.topologyVersion() : null) + ']'); + + crdInitFut.listen(new CI1() { + @Override public void apply(IgniteInternalFuture fut) { + processSinglePartitionUpdate(node, msg); + } + }); + + return; + } + processSinglePartitionUpdate(node, msg); } }); @@ -343,6 +374,13 @@ else if (m instanceof GridDhtPartitionDemandMessage) { } } + /** + * + */ + public void onCoordinatorInitialized() { + crdInitFut.onDone(); + } + /** * Callback for local join event (needed since regular event for local join is not generated). * @@ -375,7 +413,7 @@ private void onDiscoveryEvent(DiscoveryEvent evt, DiscoCache cache) { exchId = exchangeId(n.id(), affinityTopologyVersion(evt), evt); - exchFut = exchangeFuture(exchId, evt, cache,null, null); + exchFut = exchangeFuture(exchId, evt, cache, null, null); } else { DiscoveryCustomMessage customMsg = ((DiscoveryCustomEvent)evt).customMessage(); @@ -588,6 +626,17 @@ else if (fut != null) { } } + /** + * @param ver Node version. + * @return Supported exchange protocol version. + */ + public static int exchangeProtocolVersion(IgniteProductVersion ver) { + if (ver.compareToIgnoreTimestamp(EXCHANGE_PROTOCOL_2_SINCE) >= 0) + return 2; + + return 1; + } + /** * @param idx Index. * @return Topic for index. @@ -598,6 +647,8 @@ public static Object rebalanceTopic(int idx) { /** {@inheritDoc} */ @Override protected void onKernalStop0(boolean cancel) { + exchWorker.onKernalStop(); + cctx.gridEvents().removeDiscoveryEventListener(discoLsnr); cctx.io().removeHandler(false, 0, GridDhtPartitionsSingleMessage.class); @@ -620,6 +671,11 @@ public static Object rebalanceTopic(int idx) { // Finish all exchange futures. ExchangeFutureSet exchFuts0 = exchFuts; + for (CachePartitionExchangeWorkerTask task : exchWorker.futQ) { + if (task instanceof GridDhtPartitionsExchangeFuture) + ((GridDhtPartitionsExchangeFuture)task).onDone(stopErr); + } + if (exchFuts0 != null) { for (GridDhtPartitionsExchangeFuture f : exchFuts.values()) f.onDone(stopErr); @@ -659,10 +715,9 @@ public Object interruptLock() { /** * @param grpId Cache group ID. - * @param exchFut Exchange future. * @return Topology. */ - public GridDhtPartitionTopology clientTopology(int grpId, GridDhtPartitionsExchangeFuture exchFut) { + public GridDhtPartitionTopology clientTopology(int grpId) { GridClientPartitionTopology top = clientTops.get(grpId); if (top != null) @@ -670,7 +725,7 @@ public GridDhtPartitionTopology clientTopology(int grpId, GridDhtPartitionsExcha Object affKey = null; - CacheGroupDescriptor grpDesc = cctx.cache().cacheGroupDescriptors().get(grpId); + CacheGroupDescriptor grpDesc = cctx.affinity().cacheGroups().get(grpId); if (grpDesc != null) { CacheConfiguration ccfg = grpDesc.config(); @@ -684,7 +739,7 @@ public GridDhtPartitionTopology clientTopology(int grpId, GridDhtPartitionsExcha } GridClientPartitionTopology old = clientTops.putIfAbsent(grpId, - top = new GridClientPartitionTopology(cctx, grpId, exchFut, affKey)); + top = new GridClientPartitionTopology(cctx, grpId, affKey)); return old != null ? old : top; } @@ -704,19 +759,6 @@ public GridClientPartitionTopology clearClientTopology(int grpId) { return clientTops.remove(grpId); } - /** - * Gets topology version of last partition exchange, it is possible that last partition exchange - * is not completed yet. - * - * @return Topology version. - */ - public AffinityTopologyVersion topologyVersion() { - GridDhtPartitionsExchangeFuture lastInitializedFut0 = lastInitializedFut; - - return lastInitializedFut0 != null - ? lastInitializedFut0.exchangeId().topologyVersion() : AffinityTopologyVersion.NONE; - } - /** * @return Topology version of latest completed partition exchange. */ @@ -727,7 +769,7 @@ public AffinityTopologyVersion readyAffinityVersion() { /** * @return Last initialized topology future. */ - public GridDhtTopologyFuture lastTopologyFuture() { + public GridDhtPartitionsExchangeFuture lastTopologyFuture() { return lastInitializedFut; } @@ -763,7 +805,7 @@ public void lastFinishedFuture(GridDhtTopologyFuture fut) { @Nullable public IgniteInternalFuture affinityReadyFuture(AffinityTopologyVersion ver) { GridDhtPartitionsExchangeFuture lastInitializedFut0 = lastInitializedFut; - if (lastInitializedFut0 != null && lastInitializedFut0.topologyVersion().compareTo(ver) == 0) { + if (lastInitializedFut0 != null && lastInitializedFut0.initialVersion().compareTo(ver) == 0) { if (log.isDebugEnabled()) log.debug("Return lastInitializedFut for topology ready future " + "[ver=" + ver + ", fut=" + lastInitializedFut0 + ']'); @@ -907,7 +949,7 @@ private void refreshPartitions() { // No need to send to nodes which did not finish their first exchange. AffinityTopologyVersion rmtTopVer = - lastFut != null ? lastFut.topologyVersion() : AffinityTopologyVersion.NONE; + lastFut != null ? (lastFut.isDone() ? lastFut.topologyVersion() : lastFut.initialVersion()) : AffinityTopologyVersion.NONE; Collection rmts = CU.remoteNodes(cctx, rmtTopVer); @@ -1068,8 +1110,7 @@ private void addFullPartitionsMap(GridDhtPartitionsFullMessage m, * @param id ID. */ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExchangeId id) { - GridDhtPartitionsSingleMessage m = createPartitionsSingleMessage(node, - id, + GridDhtPartitionsSingleMessage m = createPartitionsSingleMessage(id, cctx.kernalContext().clientNode(), false); @@ -1090,14 +1131,12 @@ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExc } /** - * @param targetNode Target node. * @param exchangeId ID. * @param clientOnlyExchange Client exchange flag. * @param sndCounters {@code True} if need send partition update counters. * @return Message. */ - public GridDhtPartitionsSingleMessage createPartitionsSingleMessage(ClusterNode targetNode, - @Nullable GridDhtPartitionExchangeId exchangeId, + public GridDhtPartitionsSingleMessage createPartitionsSingleMessage(@Nullable GridDhtPartitionExchangeId exchangeId, boolean clientOnlyExchange, boolean sndCounters) { @@ -1223,14 +1262,16 @@ private GridDhtPartitionsExchangeFuture exchangeFuture(GridDhtPartitionExchangeI } /** - * @param exchFut Exchange. + * @param topVer Exchange result topology version. + * @param initTopVer Exchange initial version. * @param err Error. */ - public void onExchangeDone(GridDhtPartitionsExchangeFuture exchFut, @Nullable Throwable err) { - AffinityTopologyVersion topVer = exchFut.topologyVersion(); + public void onExchangeDone(AffinityTopologyVersion topVer, AffinityTopologyVersion initTopVer, @Nullable Throwable err) { + assert topVer != null || err != null; + assert initTopVer != null; if (log.isDebugEnabled()) - log.debug("Exchange done [topVer=" + topVer + ", fut=" + exchFut + ", err=" + err + ']'); + log.debug("Exchange done [topVer=" + topVer + ", err=" + err + ']'); if (err == null) { while (true) { @@ -1255,10 +1296,10 @@ public void onExchangeDone(GridDhtPartitionsExchangeFuture exchFut, @Nullable Th } else { for (Map.Entry entry : readyFuts.entrySet()) { - if (entry.getKey().compareTo(topVer) <= 0) { + if (entry.getKey().compareTo(initTopVer) <= 0) { if (log.isDebugEnabled()) log.debug("Completing created topology ready future with error " + - "[ver=" + topVer + ", fut=" + entry.getValue() + ']'); + "[ver=" + entry.getKey() + ", fut=" + entry.getValue() + ']'); entry.getValue().onDone(err); } @@ -1271,7 +1312,7 @@ public void onExchangeDone(GridDhtPartitionsExchangeFuture exchFut, @Nullable Th int skipped = 0; for (GridDhtPartitionsExchangeFuture fut : exchFuts0.values()) { - if (exchFut.exchangeId().topologyVersion().compareTo(fut.exchangeId().topologyVersion()) < 0) + if (initTopVer.compareTo(fut.exchangeId().topologyVersion()) < 0) continue; skipped++; @@ -1344,7 +1385,7 @@ else if (!grp.isLocal()) cctx.database().releaseHistoryForPreloading(); } else - exchangeFuture(msg.exchangeId(), null, null, null, null).onReceive(node, msg); + exchangeFuture(msg.exchangeId(), null, null, null, null).onReceiveFullMessage(node, msg); } finally { leaveBusy(); @@ -1384,7 +1425,7 @@ else if (!grp.isLocal()) top = grp.topology(); if (top != null) { - updated |= top.update(null, entry.getValue()); + updated |= top.update(null, entry.getValue(), false); cctx.affinity().checkRebalanceState(top, grpId); } @@ -1393,24 +1434,8 @@ else if (!grp.isLocal()) if (updated) scheduleResendPartitions(); } - else { - if (msg.client()) { - final GridDhtPartitionsExchangeFuture exchFut = exchangeFuture(msg.exchangeId(), - null, - null, - null, - null); - - exchFut.listen(new CI1>() { - @Override public void apply(IgniteInternalFuture fut) { - // Finished future should reply only to sender client node. - exchFut.onReceive(node, msg); - } - }); - } - else - exchangeFuture(msg.exchangeId(), null, null, null, null).onReceive(node, msg); - } + else + exchangeFuture(msg.exchangeId(), null, null, null, null).onReceiveSingleMessage(node, msg); } finally { leaveBusy(); @@ -1426,7 +1451,13 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi return; try { - sendLocalPartitions(node, msg.exchangeId()); + final GridDhtPartitionsExchangeFuture exchFut = exchangeFuture(msg.exchangeId(), + null, + null, + null, + null); + + exchFut.onReceivePartitionRequest(node, msg); } finally { leaveBusy(); @@ -1438,7 +1469,7 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi * @throws Exception If failed. */ public void dumpDebugInfo(@Nullable GridDhtPartitionsExchangeFuture exchFut) throws Exception { - AffinityTopologyVersion exchTopVer = exchFut != null ? exchFut.topologyVersion() : null; + AffinityTopologyVersion exchTopVer = exchFut != null ? exchFut.initialVersion() : null; U.warn(diagnosticLog, "Ready affinity version: " + readyTopVer.get()); @@ -1720,6 +1751,181 @@ private void dumpDiagnosticInfo(IgniteInternalFuture fut, ((IgniteDiagnosticAware)fut).addDiagnosticRequest(ctx); } + /** + * For testing only. + * + * @param exchMergeTestWaitVer Version to wait for. + */ + public void mergeExchangesTestWaitVersion(AffinityTopologyVersion exchMergeTestWaitVer) { + this.exchMergeTestWaitVer = exchMergeTestWaitVer; + } + + /** + * @param curFut Current exchange future. + * @param msg Message. + * @return {@code True} if node is stopping. + * @throws IgniteInterruptedCheckedException If interrupted. + */ + public boolean mergeExchanges(final GridDhtPartitionsExchangeFuture curFut, GridDhtPartitionsFullMessage msg) + throws IgniteInterruptedCheckedException { + AffinityTopologyVersion resVer = msg.resultTopologyVersion(); + + if (exchWorker.waitForExchangeFuture(resVer)) + return true; + + for (CachePartitionExchangeWorkerTask task : exchWorker.futQ) { + if (task instanceof GridDhtPartitionsExchangeFuture) { + GridDhtPartitionsExchangeFuture fut = (GridDhtPartitionsExchangeFuture) task; + + if (fut.initialVersion().compareTo(resVer) > 0) { + log.info("Merge exchange future on finish stop [curFut=" + curFut.initialVersion() + + ", resVer=" + resVer + + ", nextFutVer=" + fut.initialVersion() + ']'); + + break; + } + + log.info("Merge exchange future on finish [curFut=" + curFut.initialVersion() + + ", mergedFut=" + fut.initialVersion() + + ", evt=" + IgniteUtils.gridEventName(fut.firstEvent().type()) + + ", evtNode=" + fut.firstEvent().eventNode().id()+ + ", evtNodeClient=" + CU.clientNode(fut.firstEvent().eventNode())+ ']'); + + DiscoveryEvent evt = fut.firstEvent(); + + curFut.context().events().addEvent(fut.initialVersion(), + fut.firstEvent(), + fut.firstEventCache()); + + if (evt.type() == EVT_NODE_JOINED) { + final GridDhtPartitionsSingleMessage pendingMsg = fut.mergeJoinExchangeOnDone(curFut); + + if (pendingMsg != null) + curFut.waitAndReplyToNode(evt.eventNode().id(), pendingMsg); + } + } + } + + ExchangeDiscoveryEvents evts = curFut.context().events(); + + assert evts.topologyVersion().equals(resVer) : "Invalid exchange merge result [ver=" + evts.topologyVersion() + + ", expVer=" + resVer + ']'; + + return false; + } + + /** + * @param curFut Current active exchange future. + * @return {@code False} if need wait messages for merged exchanges. + */ + public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFut) { + if (IGNITE_EXCHANGE_MERGE_DELAY > 0) { + try { + U.sleep(IGNITE_EXCHANGE_MERGE_DELAY); + } + catch (IgniteInterruptedCheckedException e) { + U.warn(log, "Failed to wait for exchange merge, thread interrupted: " + e); + + return true; + } + } + + AffinityTopologyVersion exchMergeTestWaitVer = this.exchMergeTestWaitVer; + + if (exchMergeTestWaitVer != null) { + log.info("Exchange merge test, waiting for version [exch=" + curFut.initialVersion() + + ", waitVer=" + exchMergeTestWaitVer + ']'); + + long end = U.currentTimeMillis() + 10_000; + + while (U.currentTimeMillis() < end) { + boolean found = false; + + for (CachePartitionExchangeWorkerTask task : exchWorker.futQ) { + if (task instanceof GridDhtPartitionsExchangeFuture) { + GridDhtPartitionsExchangeFuture fut = (GridDhtPartitionsExchangeFuture)task; + + if (exchMergeTestWaitVer.equals(fut.initialVersion())) { + log.info("Exchange merge test, found awaited version: " + exchMergeTestWaitVer); + + found = true; + + break; + } + } + } + + if (found) + break; + else { + try { + U.sleep(100); + } + catch (IgniteInterruptedCheckedException e) { + break; + } + } + } + + this.exchMergeTestWaitVer = null; + } + + synchronized (curFut.mutex()) { + int awaited = 0; + + for (CachePartitionExchangeWorkerTask task : exchWorker.futQ) { + if (task instanceof GridDhtPartitionsExchangeFuture) { + GridDhtPartitionsExchangeFuture fut = (GridDhtPartitionsExchangeFuture)task; + + DiscoveryEvent evt = fut.firstEvent(); + + if (evt.type() == EVT_DISCOVERY_CUSTOM_EVT) { + log.info("Stop merge, custom event found: " + evt); + + break; + } + + ClusterNode node = evt.eventNode(); + + if (!curFut.context().supportsMergeExchanges(node)) { + log.info("Stop merge, node does not support merge: " + node); + + break; + } + if (evt.type() == EVT_NODE_JOINED && cctx.cache().hasCachesReceivedFromJoin(node)) { + log.info("Stop merge, received caches from node: " + node); + + break; + } + + log.info("Merge exchange future [curFut=" + curFut.initialVersion() + + ", mergedFut=" + fut.initialVersion() + + ", evt=" + IgniteUtils.gridEventName(fut.firstEvent().type()) + + ", evtNode=" + fut.firstEvent().eventNode().id() + + ", evtNodeClient=" + CU.clientNode(fut.firstEvent().eventNode())+ ']'); + + curFut.context().events().addEvent(fut.initialVersion(), + fut.firstEvent(), + fut.firstEventCache()); + + if (evt.type() == EVT_NODE_JOINED) { + if (fut.mergeJoinExchange(curFut)) + awaited++; + } + } + else { + if (!task.skipForExchangeMerge()) { + log.info("Stop merge, custom task found: " + task); + + break; + } + } + } + + return awaited == 0; + } + } + /** * Exchange future thread. All exchanges happen only by one thread and next * exchange will not start until previous one completes. @@ -1729,12 +1935,18 @@ private class ExchangeWorker extends GridWorker { private final LinkedBlockingDeque futQ = new LinkedBlockingDeque<>(); + /** */ + private AffinityTopologyVersion lastFutVer; + /** Busy flag used as performance optimization to stop current preloading. */ private volatile boolean busy; /** */ private boolean crd; + /** */ + private boolean stop; + /** * Constructor. */ @@ -1770,10 +1982,67 @@ void addExchangeFuture(GridDhtPartitionsExchangeFuture exchFut) { futQ.offer(exchFut); + synchronized (this) { + lastFutVer = exchFut.initialVersion(); + + notifyAll(); + } + if (log.isDebugEnabled()) log.debug("Added exchange future to exchange worker: " + exchFut); } + /** + * + */ + private void onKernalStop() { + synchronized (this) { + stop = true; + + notifyAll(); + } + } + + /** + * @param resVer Version to wait for. + * @return {@code True} if node is stopping. + * @throws IgniteInterruptedCheckedException If interrupted. + */ + private boolean waitForExchangeFuture(AffinityTopologyVersion resVer) throws IgniteInterruptedCheckedException { + synchronized (this) { + while (!stop && lastFutVer.compareTo(resVer) < 0) + U.wait(this); + + return stop; + } + } + + /** + * @param resVer Exchange result version. + * @param exchFut Exchange future. + * @throws IgniteInterruptedCheckedException If interrupted. + */ + private void removeMergedFutures(AffinityTopologyVersion resVer, GridDhtPartitionsExchangeFuture exchFut) + throws IgniteInterruptedCheckedException { + if (resVer.compareTo(exchFut.initialVersion()) != 0) { + waitForExchangeFuture(resVer); + + for (CachePartitionExchangeWorkerTask task : futQ) { + if (task instanceof GridDhtPartitionsExchangeFuture) { + GridDhtPartitionsExchangeFuture fut0 = (GridDhtPartitionsExchangeFuture)task; + + if (resVer.compareTo(fut0.initialVersion()) >= 0) { + fut0.finishMerged(); + + futQ.remove(fut0); + } + else + break; + } + } + } + } + /** {@inheritDoc} */ @Override public void cancel() { synchronized (interruptLock) { @@ -1907,6 +2176,8 @@ void dumpExchangeDebugInfo() { GridDhtPartitionsExchangeFuture exchFut = null; + AffinityTopologyVersion resVer = null; + try { if (isCancelled()) break; @@ -1933,7 +2204,7 @@ else if (task instanceof ForceRebalanceExchangeTask) { boolean newCrd = false; if (!crd) { - List srvNodes = exchFut.discoCache().serverNodes(); + List srvNodes = exchFut.firstEventCache().serverNodes(); crd = newCrd = !srvNodes.isEmpty() && srvNodes.get(0).isLocal(); } @@ -1948,13 +2219,13 @@ else if (task instanceof ForceRebalanceExchangeTask) { while (true) { try { - exchFut.get(futTimeout, TimeUnit.MILLISECONDS); + resVer = exchFut.get(futTimeout, TimeUnit.MILLISECONDS); break; } catch (IgniteFutureTimeoutCheckedException ignored) { U.warn(diagnosticLog, "Failed to wait for partition map exchange [" + - "topVer=" + exchFut.topologyVersion() + + "topVer=" + exchFut.initialVersion() + ", node=" + cctx.localNodeId() + "]. " + "Dumping pending objects that might be the cause: "); @@ -1977,6 +2248,7 @@ else if (task instanceof ForceRebalanceExchangeTask) { } } + removeMergedFutures(resVer, exchFut); if (log.isDebugEnabled()) log.debug("After waiting for exchange future [exchFut=" + exchFut + ", worker=" + @@ -2085,7 +2357,7 @@ else if (task instanceof ForceRebalanceExchangeTask) { if (assignsCancelled) { // Pending exchange. U.log(log, "Skipping rebalancing (obsolete exchange ID) " + - "[top=" + exchId.topologyVersion() + ", evt=" + exchId.discoveryEventName() + + "[top=" + resVer + ", evt=" + exchId.discoveryEventName() + ", node=" + exchId.nodeId() + ']'); } else if (r != null) { @@ -2095,19 +2367,19 @@ else if (r != null) { if (!hasPendingExchange()) { U.log(log, "Rebalancing started " + - "[top=" + exchId.topologyVersion() + ", evt=" + exchId.discoveryEventName() + + "[top=" + resVer + ", evt=" + exchId.discoveryEventName() + ", node=" + exchId.nodeId() + ']'); r.run(); // Starts rebalancing routine. } else U.log(log, "Skipping rebalancing (obsolete exchange ID) " + - "[top=" + exchId.topologyVersion() + ", evt=" + exchId.discoveryEventName() + + "[top=" + resVer + ", evt=" + exchId.discoveryEventName() + ", node=" + exchId.nodeId() + ']'); } else U.log(log, "Skipping rebalancing (nothing scheduled) " + - "[top=" + exchId.topologyVersion() + ", evt=" + exchId.discoveryEventName() + + "[top=" + resVer + ", evt=" + exchId.discoveryEventName() + ", node=" + exchId.nodeId() + ']'); } } @@ -2192,10 +2464,15 @@ private static class ExchangeFutureSet extends GridListSet() { @Override public int compare( GridDhtPartitionsExchangeFuture f1, @@ -2211,6 +2488,8 @@ private ExchangeFutureSet() { return t2.compareTo(t1); } }, /*not strict*/false); + + this.histSize = histSize; } /** @@ -2221,7 +2500,7 @@ private ExchangeFutureSet() { GridDhtPartitionsExchangeFuture fut) { GridDhtPartitionsExchangeFuture cur = super.addx(fut); - while (size() > EXCHANGE_HISTORY_SIZE) { + while (size() > histSize) { GridDhtPartitionsExchangeFuture last = last(); if (!last.isDone()) 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 2e543c735015f..bf3ee0d9abdee 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 @@ -1680,6 +1680,14 @@ public void startCachesOnLocalJoin(List topNodes) { grpValidRes.put(grp.groupId(), validateCacheGroup(grp,topNodes)); } + /** {@inheritDoc} */ + @Override public AffinityTopologyVersion initialVersion() { + return topVer; + } + + /** {@inheritDoc} */ + @Override public boolean exchangeDone() { + throw new UnsupportedOperationException(); + } + /** {@inheritDoc} */ @Override public AffinityTopologyVersion topologyVersion() { return topVer; @@ -73,6 +83,6 @@ public void validate(CacheGroupContext grp, Collection topNodes) { /** {@inheritDoc} */ @Override public String toString() { - return "ClientCacheDhtTopologyFuture [topVer=" + topologyVersion() + ']'; + return "ClientCacheDhtTopologyFuture [topVer=" + topVer + ']'; } } 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 1fb955703b203..b6db975b2ca12 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 @@ -36,6 +36,8 @@ import org.apache.ignite.internal.managers.discovery.DiscoCache; 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.ExchangeDiscoveryEvents; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap; @@ -118,33 +120,24 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology { /** * @param cctx Context. * @param grpId Group ID. - * @param exchFut Exchange ID. * @param similarAffKey Key to find caches with similar affinity. */ public GridClientPartitionTopology( - GridCacheSharedContext cctx, + GridCacheSharedContext cctx, int grpId, - GridDhtPartitionsExchangeFuture exchFut, Object similarAffKey ) { this.cctx = cctx; this.grpId = grpId; this.similarAffKey = similarAffKey; - topVer = exchFut.topologyVersion(); - - discoCache = exchFut.discoCache(); + topVer = AffinityTopologyVersion.NONE; log = cctx.logger(getClass()); - lock.writeLock().lock(); - - try { - beforeExchange0(cctx.localNode(), exchFut); - } - finally { - lock.writeLock().unlock(); - } + node2part = new GridDhtPartitionFullMap(cctx.localNode().id(), + cctx.localNode().order(), + updateSeq.get()); } /** @@ -197,9 +190,10 @@ private String mapString(GridDhtPartitionMap map) { U.writeLock(lock); try { - AffinityTopologyVersion exchTopVer = exchFut.topologyVersion(); + AffinityTopologyVersion exchTopVer = exchFut.initialVersion(); - assert exchTopVer.compareTo(topVer) > 0 : "Invalid topology version [topVer=" + topVer + + assert exchTopVer.compareTo(topVer) > 0 : "Invalid topology version [grp=" + grpId + + ", topVer=" + topVer + ", exchVer=" + exchTopVer + ']'; this.stopping = stopping; @@ -217,7 +211,7 @@ private String mapString(GridDhtPartitionMap map) { } /** {@inheritDoc} */ - @Override public AffinityTopologyVersion topologyVersion() { + @Override public AffinityTopologyVersion readyTopologyVersion() { lock.readLock().lock(); try { @@ -230,6 +224,11 @@ private String mapString(GridDhtPartitionMap map) { } } + /** {@inheritDoc} */ + @Override public AffinityTopologyVersion lastTopologyChangeVersion() { + throw new UnsupportedOperationException(); + } + /** {@inheritDoc} */ @Override public GridDhtTopologyFuture topologyVersionFuture() { assert topReadyFut != null; @@ -243,13 +242,17 @@ private String mapString(GridDhtPartitionMap map) { } /** {@inheritDoc} */ - @Override public void initPartitions(GridDhtPartitionsExchangeFuture exchFut) { + @Override public void initPartitionsWhenAffinityReady(AffinityTopologyVersion affVer, + GridDhtPartitionsExchangeFuture exchFut) { // No-op. } /** {@inheritDoc} */ - @Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean initParts) - throws IgniteCheckedException { + @Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, + boolean initParts, + boolean updateMoving) + throws IgniteCheckedException + { ClusterNode loc = cctx.localNode(); U.writeLock(lock); @@ -258,13 +261,53 @@ private String mapString(GridDhtPartitionMap map) { if (stopping) return; + discoCache = exchFut.events().discoveryCache(); + beforeExchange0(loc, exchFut); + + if (updateMoving) { + ExchangeDiscoveryEvents evts = exchFut.context().events(); + + GridAffinityAssignmentCache aff = cctx.affinity().affinity(grpId); + + assert aff.lastVersion().equals(evts.topologyVersion()); + + createMovingPartitions(aff.readyAffinity(evts.topologyVersion())); + } } finally { lock.writeLock().unlock(); } } + /** + * @param aff Affinity. + */ + private void createMovingPartitions(AffinityAssignment aff) { + for (Map.Entry e : node2part.entrySet()) { + GridDhtPartitionMap map = e.getValue(); + + addMoving(map, aff.backupPartitions(e.getKey())); + addMoving(map, aff.primaryPartitions(e.getKey())); + } + } + + /** + * @param map Node partition state map. + * @param parts Partitions assigned to node. + */ + private void addMoving(GridDhtPartitionMap map, Set parts) { + if (F.isEmpty(parts)) + return; + + for (Integer p : parts) { + GridDhtPartitionState state = map.get(p); + + if (state == null || state == EVICTED) + map.put(p, MOVING); + } + } + /** * @param loc Local node. * @param exchFut Exchange future. @@ -272,11 +315,16 @@ private String mapString(GridDhtPartitionMap map) { private void beforeExchange0(ClusterNode loc, GridDhtPartitionsExchangeFuture exchFut) { GridDhtPartitionExchangeId exchId = exchFut.exchangeId(); - assert topVer.equals(exchId.topologyVersion()) : "Invalid topology version [topVer=" + - topVer + ", exchId=" + exchId + ']'; + if (exchFut.context().events().hasServerLeft()) { + List evts0 = exchFut.context().events().events(); - if (exchId.isLeft() && exchFut.serverNodeDiscoveryEvent()) - removeNode(exchId.nodeId()); + for (int i = 0; i < evts0.size(); i++) { + DiscoveryEvent evt = evts0.get(i); + + if (ExchangeDiscoveryEvents.serverLeftEvent(evt)) + removeNode(evt.eventNode().id()); + } + } // In case if node joins, get topology at the time of joining node. ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache(); @@ -495,7 +543,7 @@ private List nodes(int p, AffinityTopologyVersion topVer, GridDhtPa List nodes = new ArrayList<>(size); for (UUID id : nodeIds) { - if (topVer.topologyVersion() > 0 && !allIds.contains(id)) + if (topVer.topologyVersion() > 0 && !F.contains(allIds, id)) continue; if (hasState(p, id, state, states)) { @@ -742,7 +790,8 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) @Override public boolean update( @Nullable GridDhtPartitionExchangeId exchId, - GridDhtPartitionMap parts + GridDhtPartitionMap parts, + boolean force ) { if (log.isDebugEnabled()) log.debug("Updating single partition map [exchId=" + exchId + ", parts=" + mapString(parts) + ']'); @@ -761,12 +810,14 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa if (stopping) return false; - if (lastExchangeVer != null && exchId != null && lastExchangeVer.compareTo(exchId.topologyVersion()) > 0) { - if (log.isDebugEnabled()) - log.debug("Stale exchange id for single partition map update (will ignore) [lastExchVer=" + - lastExchangeVer + ", exchId=" + exchId + ']'); + if (!force) { + if (lastExchangeVer != null && exchId != null && lastExchangeVer.compareTo(exchId.topologyVersion()) > 0) { + if (log.isDebugEnabled()) + log.debug("Stale exchange id for single partition map update (will ignore) [lastExchVer=" + + lastExchangeVer + ", exchId=" + exchId + ']'); - return false; + return false; + } } if (exchId != null) @@ -778,7 +829,11 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa GridDhtPartitionMap cur = node2part.get(parts.nodeId()); - if (isStaleUpdate(cur, parts)) { + if (force) { + if (cur != null && cur.topologyVersion().initialized()) + parts.updateSequence(cur.updateSequence(), cur.topologyVersion()); + } + else if (isStaleUpdate(cur, parts)) { if (log.isDebugEnabled()) log.debug("Stale update for single partition map update (will ignore) [exchId=" + exchId + ", curMap=" + cur + ", newMap=" + parts + ']'); @@ -840,19 +895,20 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa } /** {@inheritDoc} */ - @Override public void onExchangeDone(AffinityAssignment assignment, boolean updateRebalanceVer) { + @Override public void onExchangeDone(GridDhtPartitionsExchangeFuture fut, AffinityAssignment assignment, + boolean updateRebalanceVer) { // No-op. } /** {@inheritDoc} */ - @Override public boolean detectLostPartitions(DiscoveryEvent discoEvt) { + @Override public boolean detectLostPartitions(AffinityTopologyVersion affVer, DiscoveryEvent discoEvt) { assert false : "detectLostPartitions should never be called on client topology"; return false; } /** {@inheritDoc} */ - @Override public void resetLostPartitions() { + @Override public void resetLostPartitions(AffinityTopologyVersion affVer) { assert false : "resetLostPartitions should never be called on client topology"; } @@ -926,12 +982,10 @@ private void removeNode(UUID nodeId) { assert nodeId != null; assert lock.writeLock().isHeldByCurrentThread(); - ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache(); - ClusterNode loc = cctx.localNode(); if (node2part != null) { - if (oldest.equals(loc) && !node2part.nodeId().equals(loc.id())) { + if (!node2part.nodeId().equals(loc.id())) { updateSeq.setIfGreater(node2part.updateSequence()); node2part = new GridDhtPartitionFullMap(loc.id(), loc.order(), updateSeq.incrementAndGet(), 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 960b91a39b72c..1f67c1da0290f 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 @@ -433,7 +433,7 @@ public AffinityTopologyVersion beginMultiUpdate() throws IgniteCheckedException // While we are holding read lock, register lock future for partition release future. IgniteUuid lockId = IgniteUuid.fromUuid(ctx.localNodeId()); - topVer = top.topologyVersion(); + topVer = top.readyTopologyVersion(); MultiUpdateFuture fut = new MultiUpdateFuture(topVer); @@ -562,11 +562,11 @@ protected GridDistributedCacheEntry createEntry(KeyCacheObject key) { } // Version for all loaded entries. - final GridCacheVersion ver0 = ctx.shared().versions().nextForLoad(topology().topologyVersion()); + final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion(); - final boolean replicate = ctx.isDrEnabled(); + final GridCacheVersion ver0 = ctx.shared().versions().nextForLoad(topVer); - final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion(); + final boolean replicate = ctx.isDrEnabled(); final ExpiryPolicy plc0 = plc != null ? plc : ctx.expiry(); @@ -587,13 +587,13 @@ protected GridDistributedCacheEntry createEntry(KeyCacheObject key) { return; } + final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion(); + // Version for all loaded entries. - final GridCacheVersion ver0 = ctx.shared().versions().nextForLoad(topology().topologyVersion()); + final GridCacheVersion ver0 = ctx.shared().versions().nextForLoad(topVer); final boolean replicate = ctx.isDrEnabled(); - final AffinityTopologyVersion topVer = ctx.affinity().affinityTopologyVersion(); - CacheOperationContext opCtx = ctx.operationContextPerCall(); ExpiryPolicy plc0 = opCtx != null ? opCtx.expiry() : null; @@ -938,7 +938,7 @@ else if (req.needVersion()) res.setContainsValue(); } else { - AffinityTopologyVersion topVer = ctx.shared().exchange().lastTopologyFuture().topologyVersion(); + AffinityTopologyVersion topVer = ctx.shared().exchange().lastTopologyFuture().initialVersion(); assert topVer.compareTo(req.topologyVersion()) > 0 : "Wrong ready topology version for " + "invalid partitions response [topVer=" + topVer + ", req=" + req + ']'; @@ -1028,7 +1028,7 @@ protected void processNearGetRequest(final UUID nodeId, final GridNearGetRequest } if (!F.isEmpty(fut.invalidPartitions())) - res.invalidPartitions(fut.invalidPartitions(), ctx.shared().exchange().lastTopologyFuture().topologyVersion()); + res.invalidPartitions(fut.invalidPartitions(), ctx.shared().exchange().lastTopologyFuture().initialVersion()); try { ctx.io().send(nodeId, res, ctx.ioPolicy()); 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 dbfb426d717d1..4d1bb38e653e5 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 @@ -23,8 +23,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -47,9 +45,9 @@ import org.apache.ignite.internal.processors.cache.GridCacheMapEntryFactory; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; import org.apache.ignite.internal.processors.cache.KeyCacheObject; -import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader; import org.apache.ignite.internal.processors.cache.extras.GridCacheObsoleteEntryExtras; +import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.lang.GridIterator; 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 98a5e135d217a..f6e21a0ebcadb 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 @@ -69,11 +69,14 @@ public void updateTopologyVersion( ) throws IgniteInterruptedCheckedException; /** - * Topology version. - * - * @return Topology version. + * @return Result topology version of last finished exchange. */ - public AffinityTopologyVersion topologyVersion(); + public AffinityTopologyVersion readyTopologyVersion(); + + /** + * @return Start topology version of last exchange. + */ + public AffinityTopologyVersion lastTopologyChangeVersion(); /** * Gets a future that will be completed when partition exchange map for this @@ -98,16 +101,21 @@ public void updateTopologyVersion( * * @param exchFut Exchange future. * @param affReady Affinity ready flag. + * @param updateMoving * @throws IgniteCheckedException If failed. */ - public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affReady) + public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, + boolean affReady, + boolean updateMoving) throws IgniteCheckedException; /** + * @param affVer Affinity version. * @param exchFut Exchange future. * @throws IgniteInterruptedCheckedException If interrupted. */ - public void initPartitions(GridDhtPartitionsExchangeFuture exchFut) throws IgniteInterruptedCheckedException; + public void initPartitionsWhenAffinityReady(AffinityTopologyVersion affVer, GridDhtPartitionsExchangeFuture exchFut) + throws IgniteInterruptedCheckedException; /** * Post-initializes this topology. @@ -245,13 +253,14 @@ public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affR public void onRemoved(GridDhtCacheEntry e); /** - * @param exchangeVer Exchange version. + * @param exchangeResVer Result topology version for exchange. Value should be greater than previously passed. Null value + * means full map received is not related to exchange * @param partMap Update partition map. * @param cntrMap Partition update counters. * @return {@code True} if local state was changed. */ public boolean update( - @Nullable AffinityTopologyVersion exchangeVer, + @Nullable AffinityTopologyVersion exchangeResVer, GridDhtPartitionFullMap partMap, @Nullable Map> cntrMap, Set partsToReload); @@ -259,10 +268,12 @@ public boolean update( /** * @param exchId Exchange ID. * @param parts Partitions. + * @param force {@code True} to skip stale update check. * @return {@code True} if local state was changed. */ public boolean update(@Nullable GridDhtPartitionExchangeId exchId, - GridDhtPartitionMap parts); + GridDhtPartitionMap parts, + boolean force); /** * @param cntrMap Counters map. @@ -275,15 +286,18 @@ public boolean update(@Nullable GridDhtPartitionExchangeId exchId, *

    * This method should be called on topology coordinator after all partition messages are received. * + * @param resTopVer Exchange result version. * @param discoEvt Discovery event for which we detect lost partitions. * @return {@code True} if partitions state got updated. */ - public boolean detectLostPartitions(DiscoveryEvent discoEvt); + public boolean detectLostPartitions(AffinityTopologyVersion resTopVer, DiscoveryEvent discoEvt); /** * Resets the state of all LOST partitions to OWNING. + * + * @param resTopVer Exchange result version. */ - public void resetLostPartitions(); + public void resetLostPartitions(AffinityTopologyVersion resTopVer); /** * @return Collection of lost partitions, if any. @@ -344,5 +358,5 @@ public boolean update(@Nullable GridDhtPartitionExchangeId exchId, * @param assignment New affinity assignment. * @param updateRebalanceVer {@code True} if need check rebalance state. */ - public void onExchangeDone(AffinityAssignment assignment, boolean updateRebalanceVer); + public void onExchangeDone(GridDhtPartitionsExchangeFuture fut, AffinityAssignment assignment, boolean updateRebalanceVer); } 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 05d49b7f7809d..bb94da38448b7 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 @@ -41,6 +41,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheGroupContext; +import org.apache.ignite.internal.processors.cache.ExchangeDiscoveryEvents; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap; @@ -79,7 +80,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology { private static final boolean FULL_MAP_DEBUG = false; /** */ - private static final boolean FAST_DIFF_REBUILD = true; + private static final boolean FAST_DIFF_REBUILD = false; /** */ private static final Long ZERO = 0L; @@ -108,11 +109,11 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology { /** */ private volatile AffinityTopologyVersion diffFromAffinityVer = AffinityTopologyVersion.NONE; - /** */ - private AffinityTopologyVersion lastExchangeVer; + /** Last started exchange version (always >= readyTopVer). */ + private volatile AffinityTopologyVersion lastTopChangeVer = AffinityTopologyVersion.NONE; - /** */ - private volatile AffinityTopologyVersion topVer = AffinityTopologyVersion.NONE; + /** Last finished exchange version. */ + private volatile AffinityTopologyVersion readyTopVer = AffinityTopologyVersion.NONE; /** Discovery cache. */ private volatile DiscoCache discoCache; @@ -170,8 +171,6 @@ public void onReconnected() { diffFromAffinity.clear(); - lastExchangeVer = null; - updateSeq.set(1); topReadyFut = null; @@ -180,7 +179,9 @@ public void onReconnected() { rebalancedTopVer = AffinityTopologyVersion.NONE; - topVer = AffinityTopologyVersion.NONE; + readyTopVer = AffinityTopologyVersion.NONE; + + lastTopChangeVer = AffinityTopologyVersion.NONE; discoCache = ctx.discovery().discoCache(); } @@ -227,9 +228,9 @@ private String mapString(GridDhtPartitionMap map) { U.writeLock(lock); try { - AffinityTopologyVersion exchTopVer = exchFut.topologyVersion(); + AffinityTopologyVersion exchTopVer = exchFut.initialVersion(); - assert exchTopVer.compareTo(topVer) > 0 : "Invalid topology version [topVer=" + topVer + + assert exchTopVer.compareTo(readyTopVer) > 0 : "Invalid topology version [topVer=" + readyTopVer + ", exchTopVer=" + exchTopVer + ", fut=" + exchFut + ']'; @@ -241,7 +242,7 @@ private String mapString(GridDhtPartitionMap map) { rebalancedTopVer = AffinityTopologyVersion.NONE; - topVer = exchTopVer; + lastTopChangeVer = exchTopVer; this.discoCache = discoCache; } @@ -251,8 +252,18 @@ private String mapString(GridDhtPartitionMap map) { } /** {@inheritDoc} */ - @Override public AffinityTopologyVersion topologyVersion() { - AffinityTopologyVersion topVer = this.topVer; + @Override public AffinityTopologyVersion readyTopologyVersion() { + AffinityTopologyVersion topVer = this.readyTopVer; + + assert topVer.topologyVersion() > 0 : "Invalid topology version [topVer=" + topVer + + ", group=" + grp.cacheOrGroupName() + ']'; + + return topVer; + } + + /** {@inheritDoc} */ + @Override public AffinityTopologyVersion lastTopologyChangeVersion() { + AffinityTopologyVersion topVer = this.lastTopChangeVer; assert topVer.topologyVersion() > 0 : "Invalid topology version [topVer=" + topVer + ", group=" + grp.cacheOrGroupName() + ']'; @@ -273,8 +284,10 @@ private String mapString(GridDhtPartitionMap map) { } /** {@inheritDoc} */ - @Override public void initPartitions( - GridDhtPartitionsExchangeFuture exchFut) throws IgniteInterruptedCheckedException { + @Override public void initPartitionsWhenAffinityReady(AffinityTopologyVersion affVer, + GridDhtPartitionsExchangeFuture exchFut) + throws IgniteInterruptedCheckedException + { U.writeLock(lock); try { @@ -283,7 +296,7 @@ private String mapString(GridDhtPartitionMap map) { long updateSeq = this.updateSeq.incrementAndGet(); - initPartitions0(exchFut, updateSeq); + initPartitions0(affVer, exchFut, updateSeq); consistencyCheck(); } @@ -293,11 +306,12 @@ private String mapString(GridDhtPartitionMap map) { } /** + * @param affVer Affinity version to use. * @param exchFut Exchange future. * @param updateSeq Update sequence. */ - private void initPartitions0(GridDhtPartitionsExchangeFuture exchFut, long updateSeq) { - List> aff = grp.affinity().assignments(exchFut.topologyVersion()); + private void initPartitions0(AffinityTopologyVersion affVer, GridDhtPartitionsExchangeFuture exchFut, long updateSeq) { + List> aff = grp.affinity().readyAssignments(affVer); if (grp.affinityNode()) { ClusterNode loc = ctx.localNode(); @@ -306,16 +320,11 @@ private void initPartitions0(GridDhtPartitionsExchangeFuture exchFut, long updat GridDhtPartitionExchangeId exchId = exchFut.exchangeId(); - assert topVer.equals(exchFut.topologyVersion()) : - "Invalid topology [topVer=" + topVer + - ", grp=" + grp.cacheOrGroupName() + - ", futVer=" + exchFut.topologyVersion() + - ", fut=" + exchFut + ']'; - assert grp.affinity().lastVersion().equals(exchFut.topologyVersion()) : + assert grp.affinity().lastVersion().equals(affVer) : "Invalid affinity [topVer=" + grp.affinity().lastVersion() + - ", grp=" + grp.cacheOrGroupName() + - ", futVer=" + exchFut.topologyVersion() + - ", fut=" + exchFut + ']'; + ", grp=" + grp.cacheOrGroupName() + + ", affVer=" + affVer + + ", fut=" + exchFut + ']'; int num = grp.affinity().partitions(); @@ -339,18 +348,18 @@ private void initPartitions0(GridDhtPartitionsExchangeFuture exchFut, long updat if (log.isDebugEnabled()) log.debug("Owned partition for oldest node: " + locPart); - updateSeq = updateLocal(p, locPart.state(), updateSeq); + updateSeq = updateLocal(p, locPart.state(), updateSeq, affVer); } } } else - createPartitions(aff, updateSeq); + createPartitions(affVer, aff, updateSeq); } else { // If preloader is disabled, then we simply clear out // the partitions this node is not responsible for. for (int p = 0; p < num; p++) { - GridDhtLocalPartition locPart = localPartition0(p, topVer, false, true, false); + GridDhtLocalPartition locPart = localPartition0(p, affVer, false, true, false); boolean belongs = localNode(p, aff); @@ -361,7 +370,7 @@ private void initPartitions0(GridDhtPartitionsExchangeFuture exchFut, long updat if (state.active()) { locPart.rent(false); - updateSeq = updateLocal(p, locPart.state(), updateSeq); + updateSeq = updateLocal(p, locPart.state(), updateSeq, affVer); if (log.isDebugEnabled()) log.debug("Evicting partition with rebalancing disabled " + @@ -376,7 +385,7 @@ else if (belongs) { locPart.own(); - updateLocal(p, locPart.state(), updateSeq); + updateLocal(p, locPart.state(), updateSeq, affVer); } } } @@ -386,10 +395,11 @@ else if (belongs) { } /** + * @param affVer Affinity version. * @param aff Affinity assignments. * @param updateSeq Update sequence. */ - private void createPartitions(List> aff, long updateSeq) { + private void createPartitions(AffinityTopologyVersion affVer, List> aff, long updateSeq) { if (!grp.affinityNode()) return; @@ -402,7 +412,7 @@ private void createPartitions(List> aff, long updateSeq) { // will be created in MOVING state. GridDhtLocalPartition locPart = createPartition(p); - updateSeq = updateLocal(p, locPart.state(), updateSeq); + updateSeq = updateLocal(p, locPart.state(), updateSeq, affVer); } } // If this node's map is empty, we pre-create local partitions, @@ -413,7 +423,9 @@ else if (localNode(p, aff)) } /** {@inheritDoc} */ - @Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affReady) + @Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, + boolean affReady, + boolean updateMoving) throws IgniteCheckedException { ClusterNode loc = ctx.localNode(); @@ -430,18 +442,24 @@ else if (localNode(p, aff)) if (stopping) return; - GridDhtPartitionExchangeId exchId = exchFut.exchangeId(); + assert lastTopChangeVer.equals(exchFut.initialVersion()) : "Invalid topology version [topVer=" + lastTopChangeVer + + ", exchId=" + exchFut.exchangeId() + ']'; - assert topVer.equals(exchId.topologyVersion()) : "Invalid topology version [topVer=" + topVer + - ", exchId=" + exchId + ']'; + ExchangeDiscoveryEvents evts = exchFut.context().events(); + + if (affReady) { + assert grp.affinity().lastVersion().equals(evts.topologyVersion()) : "Invalid affinity version [" + + "grp=" + grp.cacheOrGroupName() + + ", affVer=" + grp.affinity().lastVersion() + + ", evtsVer=" + evts.topologyVersion() + ']'; + + lastTopChangeVer = readyTopVer = evts.topologyVersion(); + } - if (exchId.isLeft() && exchFut.serverNodeDiscoveryEvent()) - removeNode(exchId.nodeId()); - ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache(); if (log.isDebugEnabled()) { - log.debug("Partition map beforeExchange [exchId=" + exchId + + log.debug("Partition map beforeExchange [exchId=" + exchFut.exchangeId() + ", fullMap=" + fullMapString() + ']'); } @@ -458,7 +476,7 @@ else if (localNode(p, aff)) if (log.isDebugEnabled()) log.debug("Created brand new full topology map on oldest node [exchId=" + - exchId + ", fullMap=" + fullMapString() + ']'); + exchFut.exchangeId() + ", fullMap=" + fullMapString() + ']'); } else if (!node2part.valid()) { node2part = new GridDhtPartitionFullMap(oldest.id(), @@ -468,7 +486,7 @@ else if (!node2part.valid()) { false); if (log.isDebugEnabled()) { - log.debug("Created new full topology map on oldest node [exchId=" + exchId + + log.debug("Created new full topology map on oldest node [exchId=" + exchFut.exchangeId() + ", fullMap=" + node2part + ']'); } } @@ -481,27 +499,51 @@ else if (!node2part.nodeId().equals(loc.id())) { if (log.isDebugEnabled()) { log.debug("Copied old map into new map on oldest node (previous oldest node left) [" + - "exchId=" + exchId + ", fullMap=" + fullMapString() + ']'); + "exchId=" + exchFut.exchangeId() + ", fullMap=" + fullMapString() + ']'); } } } - if (grpStarted || - exchFut.discoveryEvent().type() == EVT_DISCOVERY_CUSTOM_EVT || - exchFut.serverNodeDiscoveryEvent()) { - if (affReady) - initPartitions0(exchFut, updateSeq); - else { - List> aff = grp.affinity().idealAssignment(); + if (evts.hasServerLeft()) { + List evts0 = evts.events(); - createPartitions(aff, updateSeq); + for (int i = 0; i < evts0.size(); i++) { + DiscoveryEvent evt = evts0.get(i); + + if (ExchangeDiscoveryEvents.serverLeftEvent(evt)) + removeNode(evt.eventNode().id()); + } + } + + if (grp.affinityNode()) { + if (grpStarted || + exchFut.firstEvent().type() == EVT_DISCOVERY_CUSTOM_EVT || + exchFut.serverNodeDiscoveryEvent()) { + if (affReady) { + assert grp.affinity().lastVersion().equals(evts.topologyVersion()); + + initPartitions0(evts.topologyVersion(), exchFut, updateSeq); + } + else { + assert !exchFut.context().mergeExchanges(); + + List> aff = grp.affinity().idealAssignment(); + + createPartitions(exchFut.initialVersion(), aff, updateSeq); + } } } consistencyCheck(); + if (updateMoving) { + assert grp.affinity().lastVersion().equals(evts.topologyVersion()); + + createMovingPartitions(grp.affinity().readyAffinity(evts.topologyVersion())); + } + if (log.isDebugEnabled()) { - log.debug("Partition map after beforeExchange [exchId=" + exchId + + log.debug("Partition map after beforeExchange [exchId=" + exchFut.exchangeId() + ", fullMap=" + fullMapString() + ']'); } } @@ -530,7 +572,7 @@ private boolean partitionLocalNode(int p, AffinityTopologyVersion topVer) { int num = grp.affinity().partitions(); - AffinityTopologyVersion topVer = exchFut.topologyVersion(); + AffinityTopologyVersion topVer = exchFut.context().events().topologyVersion(); assert grp.affinity().lastVersion().equals(topVer) : "Affinity is not initialized " + "[topVer=" + topVer + @@ -543,8 +585,8 @@ private boolean partitionLocalNode(int p, AffinityTopologyVersion topVer) { if (stopping) return false; - assert topVer.equals(exchFut.topologyVersion()) : "Invalid topology version [topVer=" + - topVer + ", exchId=" + exchFut.exchangeId() + ']'; + assert readyTopVer.initialized() : readyTopVer; + assert lastTopChangeVer.equals(readyTopVer); if (log.isDebugEnabled()) log.debug("Partition map before afterExchange [exchId=" + exchFut.exchangeId() + ", fullMap=" + @@ -578,12 +620,12 @@ private boolean partitionLocalNode(int p, AffinityTopologyVersion topVer) { assert owned : "Failed to own partition [grp=" + grp.cacheOrGroupName() + ", locPart=" + locPart + ']'; - updateSeq = updateLocal(p, locPart.state(), updateSeq); + updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); changed = true; if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) { - DiscoveryEvent discoEvt = exchFut.discoveryEvent(); + DiscoveryEvent discoEvt = exchFut.events().lastEvent(); grp.addRebalanceEvent(p, EVT_CACHE_REBALANCE_PART_DATA_LOST, @@ -600,7 +642,7 @@ else if (log.isDebugEnabled()) locPart + ", owners = " + owners + ']'); } else - updateSeq = updateLocal(p, locPart.state(), updateSeq); + updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); } } else { @@ -610,7 +652,7 @@ else if (log.isDebugEnabled()) if (state == MOVING) { locPart.rent(false); - updateSeq = updateLocal(p, locPart.state(), updateSeq); + updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); changed = true; @@ -621,12 +663,12 @@ else if (log.isDebugEnabled()) } } - List> aff = grp.affinity().assignments(topVer); + AffinityAssignment aff = grp.affinity().readyAffinity(topVer); if (node2part != null && node2part.valid()) changed |= checkEvictions(updateSeq, aff); - updateRebalanceVersion(aff); + updateRebalanceVersion(aff.assignment()); consistencyCheck(); } @@ -759,21 +801,23 @@ private GridDhtLocalPartition localPartition0(int p, locParts.set(p, loc = null); - if (!belongs) + if (!belongs) { throw new GridDhtInvalidPartitionException(p, "Adding entry to evicted partition " + "(often may be caused by inconsistent 'key.hashCode()' implementation) " + - "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']'); + "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.readyTopVer + ']'); + } } - else if (loc != null && state == RENTING && !showRenting) + else if (loc != null && state == RENTING && !showRenting) { throw new GridDhtInvalidPartitionException(p, "Adding entry to partition that is concurrently " + "evicted [part=" + p + ", shouldBeMoving=" + loc.reload() + ", belongs=" + belongs + - ", topVer=" + topVer + ", curTopVer=" + this.topVer + "]"); + ", topVer=" + topVer + ", curTopVer=" + this.readyTopVer + "]"); + } if (loc == null) { if (!belongs) throw new GridDhtInvalidPartitionException(p, "Creating partition which does not belong to " + "local node (often may be caused by inconsistent 'key.hashCode()' implementation) " + - "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']'); + "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.readyTopVer + ']'); locParts.set(p, loc = new GridDhtLocalPartition(ctx, grp, p)); @@ -852,7 +896,7 @@ else if (loc != null && state == RENTING && !showRenting) * =================================================== */ - GridDhtLocalPartition loc = localPartition(e.partition(), topologyVersion(), false); + GridDhtLocalPartition loc = localPartition(e.partition(), readyTopVer, false); if (loc != null) loc.onRemoved(e); @@ -876,7 +920,7 @@ else if (loc != null && state == RENTING && !showRenting) return new GridDhtPartitionMap(ctx.localNodeId(), updateSeq.get(), - topVer, + readyTopVer, map, true); } @@ -930,13 +974,16 @@ else if (loc != null && state == RENTING && !showRenting) * @return Nodes responsible for given partition (primary is first). */ @Nullable private List nodes0(int p, AffinityAssignment affAssignment, List affNodes) { + if (grp.isReplicated()) + return affNodes; + AffinityTopologyVersion topVer = affAssignment.topologyVersion(); lock.readLock().lock(); try { assert node2part != null && node2part.valid() : "Invalid node-to-partitions map [topVer1=" + topVer + - ", topVer2=" + this.topVer + + ", topVer2=" + this.readyTopVer + ", node=" + ctx.igniteInstanceName() + ", grp=" + grp.cacheOrGroupName() + ", node2part=" + node2part + ']'; @@ -1018,9 +1065,9 @@ private List nodes(int p, try { assert node2part != null && node2part.valid() : "Invalid node-to-partitions map [topVer=" + topVer + + ", grp=" + grp.cacheOrGroupName() + ", allIds=" + allIds + - ", node2part=" + node2part + - ", grp=" + grp.cacheOrGroupName() + ']'; + ", node2part=" + node2part + ']'; // Node IDs can be null if both, primary and backup, nodes disappear. List nodes = new ArrayList<>(); @@ -1129,7 +1176,7 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD lock.writeLock().lock(); try { - if (stopping) + if (stopping || !lastTopChangeVer.initialized()) return false; if (cntrMap != null) { @@ -1155,12 +1202,16 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } } - if (exchangeVer != null && lastExchangeVer != null && lastExchangeVer.compareTo(exchangeVer) >= 0) { - if (log.isDebugEnabled()) - log.debug("Stale exchange id for full partition map update (will ignore) [lastExch=" + - lastExchangeVer + ", exch=" + exchangeVer + ']'); + if (exchangeVer != null) { + // Ignore if exchange already finished or new exchange started. + if (readyTopVer.compareTo(exchangeVer) > 0 || lastTopChangeVer.compareTo(exchangeVer) > 0) { + U.warn(log, "Stale exchange id for full partition map update (will ignore) [" + + "lastTopChange=" + lastTopChangeVer + + ", readTopVer=" + readyTopVer + + ", exchVer=" + exchangeVer + ']'); - return false; + return false; + } } boolean fullMapUpdated = (node2part == null); @@ -1172,9 +1223,11 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD if (shouldOverridePartitionMap(part, newPart)) { fullMapUpdated = true; - if (log.isDebugEnabled()) - log.debug("Overriding partition map in full update map [exchId=" + exchangeVer + ", curPart=" + - mapString(part) + ", newPart=" + mapString(newPart) + ']'); + if (log.isDebugEnabled()) { + log.debug("Overriding partition map in full update map [exchVer=" + exchangeVer + + ", curPart=" + mapString(part) + + ", newPart=" + mapString(newPart) + ']'); + } } else { // If for some nodes current partition has a newer map, @@ -1206,22 +1259,27 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } if (!fullMapUpdated) { - if (log.isDebugEnabled()) - log.debug("No updates for full partition map (will ignore) [lastExch=" + - lastExchangeVer + ", exch=" + exchangeVer + ", curMap=" + node2part + ", newMap=" + partMap + ']'); + if (log.isDebugEnabled()) { + log.debug("No updates for full partition map (will ignore) [lastExch=" + lastTopChangeVer + + ", exchVer=" + exchangeVer + + ", curMap=" + node2part + + ", newMap=" + partMap + ']'); + } return false; } - if (exchangeVer != null) - lastExchangeVer = exchangeVer; + if (exchangeVer != null) { + assert exchangeVer.compareTo(readyTopVer) >= 0 && exchangeVer.compareTo(lastTopChangeVer) >= 0; - node2part = partMap; + lastTopChangeVer = readyTopVer = exchangeVer; + } - AffinityTopologyVersion affVer = grp.affinity().lastVersion(); + node2part = partMap; - if (affVer.topologyVersion() > 0 && diffFromAffinityVer.compareTo(affVer) <= 0) { - AffinityAssignment affAssignment = grp.affinity().cachedAffinity(affVer); + if (exchangeVer == null && !grp.isReplicated() && + (readyTopVer.initialized() && readyTopVer.compareTo(diffFromAffinityVer) >= 0)) { + AffinityAssignment affAssignment = grp.affinity().readyAffinity(readyTopVer); for (Map.Entry e : partMap.entrySet()) { for (Map.Entry e0 : e.getValue().entrySet()) { @@ -1246,14 +1304,14 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } } - diffFromAffinityVer = affVer; + diffFromAffinityVer = readyTopVer; } boolean changed = false; GridDhtPartitionMap nodeMap = partMap.get(ctx.localNodeId()); - if (nodeMap != null && ctx.database().persistenceEnabled()) { + if (nodeMap != null && ctx.database().persistenceEnabled() && readyTopVer.initialized()) { for (Map.Entry e : nodeMap.entrySet()) { int p = e.getKey(); GridDhtPartitionState state = e.getValue(); @@ -1261,7 +1319,7 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD if (state == OWNING) { GridDhtLocalPartition locPart = locParts.get(p); - assert locPart != null; + assert locPart != null : grp.cacheOrGroupName(); if (cntrMap != null) { T2 cntr = cntrMap.get(p); @@ -1320,13 +1378,13 @@ else if (locPart.state() == OWNING || locPart.state() == MOVING) { long updateSeq = this.updateSeq.incrementAndGet(); - if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) { - List> aff = grp.affinity().assignments(topVer); + if (readyTopVer.initialized() && readyTopVer.equals(lastTopChangeVer)) { + AffinityAssignment aff = grp.affinity().readyAffinity(readyTopVer); if (exchangeVer == null) changed |= checkEvictions(updateSeq, aff); - updateRebalanceVersion(aff); + updateRebalanceVersion(aff.assignment()); } consistencyCheck(); @@ -1408,7 +1466,8 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) @Override public boolean update( @Nullable GridDhtPartitionExchangeId exchId, - GridDhtPartitionMap parts + GridDhtPartitionMap parts, + boolean force ) { if (log.isDebugEnabled()) log.debug("Updating single partition map [exchId=" + exchId + ", parts=" + mapString(parts) + ']'); @@ -1427,27 +1486,31 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa if (stopping) return false; - if (lastExchangeVer != null && exchId != null && lastExchangeVer.compareTo(exchId.topologyVersion()) > 0) { - if (log.isDebugEnabled()) - log.debug("Stale exchange id for single partition map update (will ignore) [lastExch=" + - lastExchangeVer + ", exch=" + exchId.topologyVersion() + ']'); + if (!force) { + if (lastTopChangeVer.initialized() && exchId != null && lastTopChangeVer.compareTo(exchId.topologyVersion()) > 0) { + U.warn(log, "Stale exchange id for single partition map update (will ignore) [" + + "lastTopChange=" + lastTopChangeVer + + ", readTopVer=" + readyTopVer + + ", exch=" + exchId.topologyVersion() + ']'); - return false; + return false; + } } - if (exchId != null) - lastExchangeVer = exchId.topologyVersion(); - if (node2part == null) // Create invalid partition map. node2part = new GridDhtPartitionFullMap(); GridDhtPartitionMap cur = node2part.get(parts.nodeId()); - if (isStaleUpdate(cur, parts)) { - if (log.isDebugEnabled()) - log.debug("Stale update for single partition map update (will ignore) [exchId=" + exchId + - ", curMap=" + cur + ", newMap=" + parts + ']'); + if (force) { + if (cur != null && cur.topologyVersion().initialized()) + parts.updateSequence(cur.updateSequence(), cur.topologyVersion()); + } + else if (isStaleUpdate(cur, parts)) { + U.warn(log, "Stale update for single partition map update (will ignore) [exchId=" + exchId + + ", curMap=" + cur + + ", newMap=" + parts + ']'); return false; } @@ -1463,60 +1526,60 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa node2part.put(parts.nodeId(), parts); - AffinityTopologyVersion affVer = grp.affinity().lastVersion(); + // During exchange diff is calculated after all messages are received and affinity initialized. + if (exchId == null && !grp.isReplicated()) { + if (readyTopVer.initialized() && readyTopVer.compareTo(diffFromAffinityVer) >= 0) { + AffinityAssignment affAssignment = grp.affinity().readyAffinity(readyTopVer); - if (affVer.compareTo(diffFromAffinityVer) >= 0) { - AffinityAssignment affAssignment = grp.affinity().cachedAffinity(affVer); + // Add new mappings. + for (Map.Entry e : parts.entrySet()) { + int p = e.getKey(); - // Add new mappings. - for (Map.Entry e : parts.entrySet()) { - int p = e.getKey(); - - Set diffIds = diffFromAffinity.get(p); - - if ((e.getValue() == MOVING || e.getValue() == OWNING || e.getValue() == RENTING) - && !affAssignment.getIds(p).contains(parts.nodeId())) { - if (diffIds == null) - diffFromAffinity.put(p, diffIds = U.newHashSet(3)); + Set diffIds = diffFromAffinity.get(p); - if (diffIds.add(parts.nodeId())) - changed = true; - } - else { - if (diffIds != null && diffIds.remove(parts.nodeId())) { - changed = true; + if ((e.getValue() == MOVING || e.getValue() == OWNING || e.getValue() == RENTING) + && !affAssignment.getIds(p).contains(parts.nodeId())) { + if (diffIds == null) + diffFromAffinity.put(p, diffIds = U.newHashSet(3)); - if (diffIds.isEmpty()) - diffFromAffinity.remove(p); + if (diffIds.add(parts.nodeId())) + changed = true; } + else { + if (diffIds != null && diffIds.remove(parts.nodeId())) { + changed = true; + if (diffIds.isEmpty()) + diffFromAffinity.remove(p); + } + } } - } - // Remove obsolete mappings. - if (cur != null) { - for (Integer p : F.view(cur.keySet(), F0.notIn(parts.keySet()))) { - Set ids = diffFromAffinity.get(p); + // Remove obsolete mappings. + if (cur != null) { + for (Integer p : F.view(cur.keySet(), F0.notIn(parts.keySet()))) { + Set ids = diffFromAffinity.get(p); - if (ids != null && ids.remove(parts.nodeId())) { - changed = true; + if (ids != null && ids.remove(parts.nodeId())) { + changed = true; - if (ids.isEmpty()) - diffFromAffinity.remove(p); + if (ids.isEmpty()) + diffFromAffinity.remove(p); + } } } - } - diffFromAffinityVer = affVer; + diffFromAffinityVer = readyTopVer; + } } - if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) { - List> aff = grp.affinity().assignments(topVer); + if (readyTopVer.initialized() && readyTopVer.equals(lastTopChangeVer)) { + AffinityAssignment aff = grp.affinity().readyAffinity(readyTopVer); if (exchId == null) changed |= checkEvictions(updateSeq, aff); - updateRebalanceVersion(aff); + updateRebalanceVersion(aff.assignment()); } consistencyCheck(); @@ -1524,7 +1587,7 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa if (log.isDebugEnabled()) log.debug("Partition map after single update: " + fullMapString()); - if (changed) + if (changed && exchId == null) ctx.exchange().scheduleResendPartitions(); return changed; @@ -1535,12 +1598,26 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa } /** {@inheritDoc} */ - @Override public void onExchangeDone(AffinityAssignment assignment, boolean updateRebalanceVer) { + @Override public void onExchangeDone(GridDhtPartitionsExchangeFuture fut, AffinityAssignment assignment, boolean updateRebalanceVer) { lock.writeLock().lock(); try { - if (assignment.topologyVersion().compareTo(diffFromAffinityVer) >= 0) - rebuildDiff(assignment); + assert !(topReadyFut instanceof GridDhtPartitionsExchangeFuture) || + assignment.topologyVersion().equals(((GridDhtPartitionsExchangeFuture)topReadyFut).context().events().topologyVersion()); + + readyTopVer = lastTopChangeVer = assignment.topologyVersion(); + + if (!grp.isReplicated()) { + boolean rebuildDiff = fut == null || fut.localJoinExchange() || fut.serverNodeDiscoveryEvent() || + fut.firstEvent().type() == EVT_DISCOVERY_CUSTOM_EVT || !diffFromAffinityVer.initialized(); + + if (rebuildDiff) { + if (assignment.topologyVersion().compareTo(diffFromAffinityVer) >= 0) + rebuildDiff(assignment); + } + else + diffFromAffinityVer = readyTopVer; + } if (updateRebalanceVer) updateRebalanceVersion(assignment.assignment()); @@ -1550,6 +1627,34 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa } } + /** + * @param aff Affinity. + */ + private void createMovingPartitions(AffinityAssignment aff) { + for (Map.Entry e : node2part.entrySet()) { + GridDhtPartitionMap map = e.getValue(); + + addMoving(map, aff.backupPartitions(e.getKey())); + addMoving(map, aff.primaryPartitions(e.getKey())); + } + } + + /** + * @param map Node partition state map. + * @param parts Partitions assigned to node. + */ + private void addMoving(GridDhtPartitionMap map, Set parts) { + if (F.isEmpty(parts)) + return; + + for (Integer p : parts) { + GridDhtPartitionState state = map.get(p); + + if (state == null || state == EVICTED) + map.put(p, MOVING); + } + } + /** * @param affAssignment New affinity assignment. */ @@ -1581,13 +1686,13 @@ private void rebuildDiff(AffinityAssignment affAssignment) { UUID nodeId = e.getKey(); for (Map.Entry e0 : e.getValue().entrySet()) { - int p0 = e0.getKey(); + Integer p0 = e0.getKey(); GridDhtPartitionState state = e0.getValue(); Set ids = diffFromAffinity.get(p0); - if ((state == MOVING || state == OWNING) && !affAssignment.getIds(p0).contains(nodeId)) { + if ((state == MOVING || state == OWNING || state == RENTING) && !affAssignment.getIds(p0).contains(nodeId)) { if (ids == null) diffFromAffinity.put(p0, ids = U.newHashSet(3)); @@ -1605,7 +1710,7 @@ private void rebuildDiff(AffinityAssignment affAssignment) { } /** {@inheritDoc} */ - @Override public boolean detectLostPartitions(DiscoveryEvent discoEvt) { + @Override public boolean detectLostPartitions(AffinityTopologyVersion resTopVer, DiscoveryEvent discoEvt) { lock.writeLock().lock(); try { @@ -1641,13 +1746,13 @@ private void rebuildDiff(AffinityAssignment affAssignment) { for (Integer part : lost) { long updSeq = updateSeq.incrementAndGet(); - GridDhtLocalPartition locPart = localPartition(part, topVer, false); + GridDhtLocalPartition locPart = localPartition(part, resTopVer, false); if (locPart != null) { boolean marked = plc == PartitionLossPolicy.IGNORE ? locPart.own() : locPart.markLost(); if (marked) - updateLocal(locPart.id(), locPart.state(), updSeq); + updateLocal(locPart.id(), locPart.state(), updSeq, resTopVer); changed |= marked; } @@ -1683,7 +1788,7 @@ else if (plc != PartitionLossPolicy.IGNORE) { } /** {@inheritDoc} */ - @Override public void resetLostPartitions() { + @Override public void resetLostPartitions(AffinityTopologyVersion resTopVer) { lock.writeLock().lock(); try { @@ -1696,18 +1801,18 @@ else if (plc != PartitionLossPolicy.IGNORE) { e0.setValue(OWNING); - GridDhtLocalPartition locPart = localPartition(e0.getKey(), topVer, false); + GridDhtLocalPartition locPart = localPartition(e0.getKey(), resTopVer, false); if (locPart != null && locPart.state() == LOST) { boolean marked = locPart.own(); if (marked) - updateLocal(locPart.id(), locPart.state(), updSeq); + updateLocal(locPart.id(), locPart.state(), updSeq, resTopVer); } } } - checkEvictions(updSeq, grp.affinity().assignments(topVer)); + checkEvictions(updSeq, grp.affinity().readyAffinity(resTopVer)); grp.needsRecovery(false); } @@ -1808,7 +1913,7 @@ else if (plc != PartitionLossPolicy.IGNORE) { * @param aff Affinity assignments. * @return Checks if any of the local partitions need to be evicted. */ - private boolean checkEvictions(long updateSeq, List> aff) { + private boolean checkEvictions(long updateSeq, AffinityAssignment aff) { boolean changed = false; UUID locId = ctx.localNodeId(); @@ -1825,7 +1930,7 @@ private boolean checkEvictions(long updateSeq, List> aff) { List affNodes = aff.get(p); if (!affNodes.contains(ctx.localNode())) { - List nodes = nodes(p, topVer, OWNING, null); + List nodes = nodes(p, aff.topologyVersion(), OWNING, null); Collection nodeIds = F.nodeIds(nodes); // If all affinity nodes are owners, then evict partition from local node. @@ -1834,7 +1939,7 @@ private boolean checkEvictions(long updateSeq, List> aff) { part.rent(false); - updateSeq = updateLocal(part.id(), part.state(), updateSeq); + updateSeq = updateLocal(part.id(), part.state(), updateSeq, aff.topologyVersion()); changed = true; @@ -1859,7 +1964,10 @@ private boolean checkEvictions(long updateSeq, List> aff) { part.rent(false); - updateSeq = updateLocal(part.id(), part.state(), updateSeq); + updateSeq = updateLocal(part.id(), + part.state(), + updateSeq, + aff.topologyVersion()); changed = true; @@ -1885,10 +1993,11 @@ private boolean checkEvictions(long updateSeq, List> aff) { * @param p Partition. * @param state State. * @param updateSeq Update sequence. + * @param affVer Affinity version. * @return Update sequence. */ @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) - private long updateLocal(int p, GridDhtPartitionState state, long updateSeq) { + private long updateLocal(int p, GridDhtPartitionState state, long updateSeq, AffinityTopologyVersion affVer) { assert lock.isWriteLockedByCurrentThread(); ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache(); @@ -1896,7 +2005,7 @@ private long updateLocal(int p, GridDhtPartitionState state, long updateSeq) { assert oldest != null || ctx.kernalContext().clientNode(); // If this node became the oldest node. - if (ctx.localNode().equals(oldest)) { + if (ctx.localNode().equals(oldest) && node2part != null) { long seq = node2part.updateSequence(); if (seq != updateSeq) { @@ -1930,18 +2039,18 @@ private long updateLocal(int p, GridDhtPartitionState state, long updateSeq) { if (map == null) { map = new GridDhtPartitionMap(locNodeId, updateSeq, - topVer, + affVer, GridPartitionStateMap.EMPTY, false); node2part.put(locNodeId, map); } - map.updateSequence(updateSeq, topVer); + map.updateSequence(updateSeq, affVer); map.put(p, state); - if (state == MOVING || state == OWNING || state == RENTING) { + if (!grp.isReplicated() && (state == MOVING || state == OWNING || state == RENTING)) { AffinityAssignment assignment = grp.affinity().cachedAffinity(diffFromAffinityVer); if (!assignment.getIds(p).contains(ctx.localNodeId())) { @@ -1982,12 +2091,14 @@ private void removeNode(UUID nodeId) { GridDhtPartitionMap parts = node2part.remove(nodeId); - if (parts != null) { - for (Integer p : parts.keySet()) { - Set diffIds = diffFromAffinity.get(p); + if (!grp.isReplicated()) { + if (parts != null) { + for (Integer p : parts.keySet()) { + Set diffIds = diffFromAffinity.get(p); - if (diffIds != null) - diffIds.remove(nodeId); + if (diffIds != null) + diffIds.remove(nodeId); + } } } @@ -2001,7 +2112,9 @@ private void removeNode(UUID nodeId) { try { if (part.own()) { - updateLocal(part.id(), part.state(), updateSeq.incrementAndGet()); + assert lastTopChangeVer.initialized() : lastTopChangeVer; + + updateLocal(part.id(), part.state(), updateSeq.incrementAndGet(), lastTopChangeVer); consistencyCheck(); @@ -2032,7 +2145,9 @@ private void removeNode(UUID nodeId) { if (part.reload()) part = createPartition(part.id()); - updateLocal(part.id(), part.state(), seq); + assert lastTopChangeVer.initialized() : lastTopChangeVer; + + updateLocal(part.id(), part.state(), seq, lastTopChangeVer); consistencyCheck(); } @@ -2101,7 +2216,7 @@ private void removeNode(UUID nodeId) { /** {@inheritDoc} */ @Override public boolean rebalanceFinished(AffinityTopologyVersion topVer) { - AffinityTopologyVersion curTopVer = this.topVer; + AffinityTopologyVersion curTopVer = this.readyTopVer; return curTopVer.equals(topVer) && curTopVer.equals(rebalancedTopVer); } @@ -2185,7 +2300,7 @@ private boolean localNode(int part, List> aff) { * @param aff Affinity assignments. */ private void updateRebalanceVersion(List> aff) { - if (!rebalancedTopVer.equals(topVer)) { + if (!rebalancedTopVer.equals(readyTopVer)) { if (node2part == null || !node2part.valid()) return; @@ -2203,15 +2318,17 @@ private void updateRebalanceVersion(List> aff) { owners.add(node); } - Set diff = diffFromAffinity.get(i); + if (!grp.isReplicated()) { + Set diff = diffFromAffinity.get(i); - if (diff != null) { - for (UUID nodeId : diff) { - if (hasState(i, nodeId, OWNING)) { - ClusterNode node = ctx.discovery().node(nodeId); + if (diff != null) { + for (UUID nodeId : diff) { + if (hasState(i, nodeId, OWNING)) { + ClusterNode node = ctx.discovery().node(nodeId); - if (node != null) - owners.add(node); + if (node != null) + owners.add(node); + } } } } @@ -2220,7 +2337,7 @@ private void updateRebalanceVersion(List> aff) { return; } - rebalancedTopVer = topVer; + rebalancedTopVer = readyTopVer; if (log.isDebugEnabled()) log.debug("Updated rebalanced version [cache=" + grp.cacheOrGroupName() + ", ver=" + rebalancedTopVer + ']'); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java index 2f51c5a155c8f..de58188cc04d4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionsReservation.java @@ -205,7 +205,7 @@ private static void tryEvict(GridDhtLocalPartition part) { if (reservations.compareAndSet(r, r - 1)) { // If it was the last reservation and topology version changed -> attempt to evict partitions. if (r == 1 && !cctx.kernalContext().isStopping() && - !topVer.equals(cctx.topology().topologyVersion())) + !topVer.equals(cctx.topology().lastTopologyChangeVersion())) tryEvict(parts.get()); return; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTopologyFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTopologyFuture.java index aefc7c9c03597..0bcc4a80c9882 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTopologyFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTopologyFuture.java @@ -18,9 +18,13 @@ package org.apache.ignite.internal.processors.cache.distributed.dht; import java.util.Collection; +import org.apache.ignite.cache.affinity.Affinity; 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.GridCachePartitionExchangeManager; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture; +import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.jetbrains.annotations.Nullable; /** @@ -28,17 +32,43 @@ * safe to use when all transactions that involve moving primary partitions are completed and partition map * exchange is also completed. *

    - * When new new transaction is started, it will wait for this future before acquiring new locks on particular + * When new new cache operation is started, it will wait for this future before acquiring new locks on particular * topology version. */ public interface GridDhtTopologyFuture extends IgniteInternalFuture { /** - * Gets topology version of this future. + * Returns topology version when exchange started. It can differ from result topology version if exchanges for + * multiple discovery events are merged. Initial version should not be used as version for cache operation + * since it is possible affinity for this version is never calculated. * - * @return Topology version. + * @return Topology version when exchange started. + */ + public AffinityTopologyVersion initialVersion(); + + /** + * Gets result topology version of this future. Result version can differ from initial exchange version + * if excanges for multiple discovery events are merged, in this case result version is version of last + * discovery event. + *

    + * This method should be called only for finished topology future + * since result version is not known before exchange finished. + * + * @return Result topology version. */ public AffinityTopologyVersion topologyVersion(); + /** + * Ready affinity future ({@link GridCachePartitionExchangeManager#affinityReadyFuture(AffinityTopologyVersion)} + * is completed before {@link GridFutureAdapter#onDone(Object, Throwable)} is called on + * {@link GridDhtPartitionsExchangeFuture}, it is guaranteed that this method will return {@code true} + * if affinity ready future is finished. + *

    + * Also this method returns {@code false} for merged exchange futures. + * + * @return {@code True} if exchange is finished and result topology version can be used. + */ + public boolean exchangeDone(); + /** * Returns error is cache topology is not valid. * 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 063986f90e462..2c005097a47d1 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 @@ -74,6 +74,7 @@ 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.thread.IgniteThread; import org.apache.ignite.transactions.TransactionIsolation; import org.jetbrains.annotations.Nullable; @@ -645,7 +646,29 @@ private void processNearLockRequest(UUID nodeId, GridNearLockRequest req) { return; } - IgniteInternalFuture f = lockAllAsync(ctx, nearNode, req, null); + processNearLockRequest0(nearNode, req); + } + + /** + * @param nearNode + * @param req + */ + private void processNearLockRequest0(ClusterNode nearNode, GridNearLockRequest req) { + IgniteInternalFuture f; + + if (req.firstClientRequest()) { + for (;;) { + if (waitForExchangeFuture(nearNode, req)) + return; + + f = lockAllAsync(ctx, nearNode, req, null); + + if (f != null) + break; + } + } + else + f = lockAllAsync(ctx, nearNode, req, null); // Register listener just so we print out errors. // Exclude lock timeout exception since it's not a fatal exception. @@ -653,6 +676,48 @@ private void processNearLockRequest(UUID nodeId, GridNearLockRequest req) { GridDistributedLockCancelledException.class)); } + private boolean waitForExchangeFuture(final ClusterNode node, final GridNearLockRequest req) { + assert req.firstClientRequest() : req; + + GridDhtTopologyFuture topFut = ctx.shared().exchange().lastTopologyFuture(); + + if (!topFut.isDone()) { + Thread curThread = Thread.currentThread(); + + if (curThread instanceof IgniteThread) { + final IgniteThread thread = (IgniteThread)curThread; + + if (thread.cachePoolThread()) { + topFut.listen(new CI1>() { + @Override public void apply(IgniteInternalFuture fut) { + ctx.kernalContext().closure().runLocalWithThreadPolicy(thread, new Runnable() { + @Override public void run() { + try { + processNearLockRequest0(node, req); + } + finally { + ctx.io().onMessageProcessed(req); + } + } + }); + } + }); + + return true; + } + } + + try { + topFut.get(); + } + catch (IgniteCheckedException e) { + U.error(log, "Topology future failed: " + e, e); + } + } + + return false; + } + /** * @param nodeId Node ID. * @param res Response. @@ -846,21 +911,27 @@ public IgniteInternalFuture lockAllAsync( top = topology(); - topology().readLock(); + top.readLock(); + + if (!top.topologyVersionFuture().isDone()) { + top.readUnlock(); + + return null; + } } try { - if (top != null && needRemap(req.topologyVersion(), top.topologyVersion())) { + if (top != null && needRemap(req.topologyVersion(), top.readyTopologyVersion())) { if (log.isDebugEnabled()) { log.debug("Client topology version mismatch, need remap lock request [" + "reqTopVer=" + req.topologyVersion() + - ", locTopVer=" + top.topologyVersion() + + ", locTopVer=" + top.readyTopologyVersion() + ", req=" + req + ']'); } GridNearLockResponse res = sendClientLockRemapResponse(nearNode, req, - top.topologyVersion()); + top.lastTopologyChangeVersion()); return new GridFinishedFuture<>(res); } @@ -945,21 +1016,27 @@ public IgniteInternalFuture lockAllAsync( top = topology(); - topology().readLock(); + top.readLock(); + + if (!top.topologyVersionFuture().isDone()) { + top.readUnlock(); + + return null; + } } try { - if (top != null && needRemap(req.topologyVersion(), top.topologyVersion())) { + if (top != null && needRemap(req.topologyVersion(), top.readyTopologyVersion())) { if (log.isDebugEnabled()) { log.debug("Client topology version mismatch, need remap lock request [" + "reqTopVer=" + req.topologyVersion() + - ", locTopVer=" + top.topologyVersion() + + ", locTopVer=" + top.readyTopologyVersion() + ", req=" + req + ']'); } GridNearLockResponse res = sendClientLockRemapResponse(nearNode, req, - top.topologyVersion()); + top.lastTopologyChangeVersion()); return new GridFinishedFuture<>(res); } 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 deae46660f74d..e7e0e0668b5e1 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 @@ -311,7 +311,7 @@ private void map( remapKeys.add(key); } - AffinityTopologyVersion updTopVer = cctx.discovery().topologyVersionEx(); + AffinityTopologyVersion updTopVer = cctx.shared().exchange().readyAffinityVersion(); assert updTopVer.compareTo(topVer) > 0 : "Got invalid partitions for local node but topology version did " + "not change [topVer=" + topVer + ", updTopVer=" + updTopVer + @@ -551,7 +551,7 @@ private boolean localGet(KeyCacheObject key, int part, Map locVals) { return true; } - boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().topologyVersion()); + boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().lastTopologyChangeVersion()); // Entry not found, do not continue search if topology did not change and there is no store. if (!cctx.readThroughConfigured() && (topStable || partitionOwned(part))) { 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 d66afca93acf2..0828a80f6c6dc 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 @@ -236,7 +236,7 @@ private void map(final AffinityTopologyVersion topVer) { final Collection invalidParts = fut.invalidPartitions(); if (!F.isEmpty(invalidParts)) { - AffinityTopologyVersion updTopVer = cctx.discovery().topologyVersionEx(); + AffinityTopologyVersion updTopVer = cctx.shared().exchange().readyAffinityVersion(); assert updTopVer.compareTo(topVer) > 0 : "Got invalid partitions for local node but topology " + "version did not change [topVer=" + topVer + ", updTopVer=" + updTopVer + @@ -442,7 +442,7 @@ private boolean localGet(AffinityTopologyVersion topVer, int part) { return true; } - boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().topologyVersion()); + boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().lastTopologyChangeVersion()); // Entry not found, complete future with null result if topology did not change and there is no store. if (!cctx.readThroughConfigured() && (topStable || partitionOwned(part))) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateFuture.java index fb58e71210b71..e8824e7db689e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateFuture.java @@ -36,8 +36,8 @@ import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.GridCacheAtomicFuture; import org.apache.ignite.internal.processors.cache.GridCacheContext; -import org.apache.ignite.internal.processors.cache.GridCacheFutureAdapter; import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException; +import org.apache.ignite.internal.processors.cache.GridCacheFutureAdapter; import org.apache.ignite.internal.processors.cache.GridCacheReturn; import org.apache.ignite.internal.processors.cache.KeyCacheObject; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; 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 c87184c344d74..97656747cfba7 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 @@ -61,6 +61,7 @@ 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.distributed.dht.GridDhtTopologyFuture; 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; @@ -1754,8 +1755,6 @@ private void updateAllAsyncInternal0( GridDhtAtomicAbstractUpdateFuture dhtFut = null; - boolean remap = false; - IgniteCacheExpiryPolicy expiry = null; ctx.shared().database().checkpointReadLock(); @@ -1783,21 +1782,27 @@ private void updateAllAsyncInternal0( return; } + boolean remap = false; + // Do not check topology version if topology was locked on near node by // external transaction or explicit lock. - if (req.topologyLocked() || !needRemap(req.topologyVersion(), top.topologyVersion())) { + if (!req.topologyLocked()) { + // Can not wait for topology future since it will break + // GridNearAtomicCheckUpdateRequest processing. + remap = !top.topologyVersionFuture().exchangeDone() || + needRemap(req.topologyVersion(), top.readyTopologyVersion()); + } + + if (!remap) { DhtAtomicUpdateResult updRes = update(node, locked, req, res); dhtFut = updRes.dhtFuture(); deleted = updRes.deleted(); expiry = updRes.expiryPolicy(); } - else { + else // Should remap all keys. - remap = true; - - res.remapTopologyVersion(top.topologyVersion()); - } + res.remapTopologyVersion(top.lastTopologyChangeVersion()); } finally { top.readUnlock(); @@ -1831,9 +1836,7 @@ private void updateAllAsyncInternal0( if (log.isDebugEnabled()) log.debug("Caught invalid partition exception for cache entry (will remap update request): " + req); - remap = true; - - res.remapTopologyVersion(ctx.topology().topologyVersion()); + res.remapTopologyVersion(ctx.topology().lastTopologyChangeVersion()); } catch (Throwable e) { // At least RuntimeException can be thrown by the code above when GridCacheContext is cleaned and there is @@ -1853,7 +1856,7 @@ private void updateAllAsyncInternal0( ctx.shared().database().checkpointReadUnlock(); } - if (remap) { + if (res.remapTopologyVersion() != null) { assert dhtFut == null; completionCb.apply(req, res); @@ -1891,7 +1894,7 @@ private DhtAtomicUpdateResult update( boolean hasNear = req.nearCache(); // Assign next version for update inside entries lock. - GridCacheVersion ver = ctx.versions().next(top.topologyVersion()); + GridCacheVersion ver = ctx.versions().next(top.readyTopologyVersion()); if (hasNear) res.nearVersion(ver); @@ -3246,7 +3249,7 @@ && writeThrough() && !req.skipStore(), } } catch (NodeStoppingException e){ - U.error(log, "Failed to update key on backup (local node is stopping):" + key, e); + U.warn(log, "Failed to update key on backup (local node is stopping): " + key); return; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java index f5d89c482d9e0..67542ca1a0310 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java @@ -23,7 +23,6 @@ import java.util.UUID; 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.internal.IgniteInternalFuture; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java index 40eb371555509..7500549c421d2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java @@ -690,7 +690,7 @@ void map() { if (topVer != null) { for (GridDhtTopologyFuture fut : cctx.shared().exchange().exchangeFutures()) { - if (fut.topologyVersion().equals(topVer)) { + if (fut.exchangeDone() && fut.topologyVersion().equals(topVer)) { Throwable err = fut.validateCache(cctx, recovery, read, null, keys); if (err != null) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CacheGroupAffinityMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CacheGroupAffinityMessage.java new file mode 100644 index 0000000000000..8a1ffb4ebb22f --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CacheGroupAffinityMessage.java @@ -0,0 +1,339 @@ +/* + * 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.preloader; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.GridDirectCollection; +import org.apache.ignite.internal.GridDirectMap; +import org.apache.ignite.internal.managers.discovery.DiscoCache; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache; +import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; +import org.apache.ignite.internal.util.GridLongList; +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.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; + +/** + * Information about affinity assignment. + */ +public class CacheGroupAffinityMessage implements Message { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + @GridDirectCollection(GridLongList.class) + private List assigns; + + /** */ + @GridDirectCollection(GridLongList.class) + private List idealAssigns; + + /** */ + @GridDirectMap(keyType = Integer.class, valueType = GridLongList.class) + private Map assignsDiff; + + /** + * + */ + public CacheGroupAffinityMessage() { + // No-op. + } + + /** + * @param assign0 Assignment. + * @param idealAssign0 Ideal assignment. + * @param assignDiff0 Difference with ideal affinity assignment. + */ + private CacheGroupAffinityMessage(List> assign0, + List> idealAssign0, + Map> assignDiff0) { + if (assign0 != null) + assigns = createAssigns(assign0); + + if (idealAssign0 != null) + idealAssigns = createAssigns(idealAssign0); + + if (assignDiff0 != null) { + assignsDiff = U.newHashMap(assignDiff0.size()); + + for (Map.Entry> e : assignDiff0.entrySet()) { + List orders = e.getValue(); + + GridLongList l = new GridLongList(orders.size()); + + for (int n = 0; n < orders.size(); n++) + l.add(orders.get(n)); + + assignsDiff.put(e.getKey(), l); + } + } + } + + private List createAssigns(List> assign0) { + if (assign0 != null) { + List assigns = new ArrayList<>(assign0.size()); + + for (int i = 0; i < assign0.size(); i++) { + List nodes = assign0.get(i); + + GridLongList l = new GridLongList(nodes.size()); + + for (int n = 0; n < nodes.size(); n++) + l.add(nodes.get(n).order()); + + assigns.add(l); + } + + return assigns; + } + + return null; + } + + /** + * @param affDiff Affinity diff. + * @return Affinity diff messages. + */ + public static Map createAffinityDiffMessages( + Map>> affDiff) { + if (F.isEmpty(affDiff)) + return null; + + Map map = U.newHashMap(affDiff.size()); + + for (Map.Entry>> e : affDiff.entrySet()) + map.put(e.getKey(), new CacheGroupAffinityMessage(null, null, e.getValue())); + + return map; + } + + /** + * @param cctx Context. + * @param topVer Topology version. + * @param affReq Cache group IDs. + * @param cachesAff Optional already prepared affinity. + * @return Affinity. + */ + static Map createAffinityMessages( + GridCacheSharedContext cctx, + AffinityTopologyVersion topVer, + Collection affReq, + @Nullable Map cachesAff) { + assert !F.isEmpty(affReq) : affReq; + + if (cachesAff == null) + cachesAff = U.newHashMap(affReq.size()); + + for (Integer grpId : affReq) { + if (!cachesAff.containsKey(grpId)) { + GridAffinityAssignmentCache aff = cctx.affinity().affinity(grpId); + + List> assign = aff.readyAssignments(topVer); + + CacheGroupAffinityMessage msg = new CacheGroupAffinityMessage(assign, + aff.centralizedAffinityFunction() ? aff.idealAssignment() : null, + null); + + cachesAff.put(grpId, msg); + } + } + + return cachesAff; + } + + /** + * @param assign Nodes orders. + * @param nodesByOrder Nodes by order cache. + * @param discoCache Discovery data cache. + * @return Nodes list. + */ + public static List toNodes(GridLongList assign, Map nodesByOrder, DiscoCache discoCache) { + List assign0 = new ArrayList<>(assign.size()); + + for (int n = 0; n < assign.size(); n++) { + long order = assign.get(n); + + ClusterNode affNode = nodesByOrder.get(order); + + if (affNode == null) { + affNode = discoCache.serverNodeByOrder(order); + + assert affNode != null : "Failed to find node by order [order=" + order + + ", topVer=" + discoCache.version() + ']'; + + nodesByOrder.put(order, affNode); + } + + assign0.add(affNode); + } + + return assign0; + } + + /** + * @param nodesByOrder Nodes by order cache. + * @param discoCache Discovery data cache. + * @return Nodes list. + */ + @Nullable public List> createIdealAssignments(Map nodesByOrder, + DiscoCache discoCache) { + if (idealAssigns == null) + return null; + + return createAssignments(idealAssigns, nodesByOrder, discoCache); + } + + /** + * @param nodesByOrder Nodes by order cache. + * @param discoCache Discovery data cache. + * @return Assignments. + */ + public List> createAssignments(Map nodesByOrder, DiscoCache discoCache) { + return createAssignments(assigns, nodesByOrder, discoCache); + } + + /** + * @param assigns Nodes orders. + * @param nodesByOrder Nodes by order cache. + * @param discoCache Discovery data cache. + * @return Nodes list. + */ + private List> createAssignments(List assigns, + Map nodesByOrder, + DiscoCache discoCache) { + List> assignments0 = new ArrayList<>(assigns.size()); + + for (int p = 0; p < assigns.size(); p++) { + GridLongList assign = assigns.get(p); + + assignments0.add(toNodes(assign, nodesByOrder, discoCache)); + } + + return assignments0; + } + + /** + * @return Difference with ideal affinity assignment. + */ + public Map assignmentsDiff() { + return assignsDiff; + } + + /** {@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.writeCollection("assigns", assigns, MessageCollectionItemType.MSG)) + return false; + + writer.incrementState(); + + case 1: + if (!writer.writeMap("assignsDiff", assignsDiff, MessageCollectionItemType.INT, MessageCollectionItemType.MSG)) + return false; + + writer.incrementState(); + + case 2: + if (!writer.writeCollection("idealAssigns", idealAssigns, 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; + + switch (reader.state()) { + case 0: + assigns = reader.readCollection("assigns", MessageCollectionItemType.MSG); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 1: + assignsDiff = reader.readMap("assignsDiff", MessageCollectionItemType.INT, MessageCollectionItemType.MSG, false); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 2: + idealAssigns = reader.readCollection("idealAssigns", MessageCollectionItemType.MSG); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + } + + return reader.afterMessageRead(CacheGroupAffinityMessage.class); + } + + /** {@inheritDoc} */ + @Override public short directType() { + return 128; + } + + /** {@inheritDoc} */ + @Override public byte fieldsCount() { + return 3; + } + + /** {@inheritDoc} */ + @Override public void onAckReceived() { + // No-op. + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(CacheGroupAffinityMessage.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/ForceRebalanceExchangeTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/ForceRebalanceExchangeTask.java index c8201752a906e..dfa0e1e0fb335 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/ForceRebalanceExchangeTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/ForceRebalanceExchangeTask.java @@ -42,6 +42,11 @@ public ForceRebalanceExchangeTask(GridDhtPartitionExchangeId exchId, GridCompoun this.forcedRebFut = forcedRebFut; } + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return true; + } + /** * @return Exchange ID. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java index 0a4941533cedc..07daeda947f39 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java @@ -62,6 +62,17 @@ public class GridDhtPartitionExchangeId implements Message, Comparable> it = scMap.keySet().iterator(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java index 441952d482351..95c1a4f51f267 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java @@ -34,7 +34,10 @@ */ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage { /** */ - protected static final byte COMPRESSED_FLAG_MASK = 1; + private static final byte COMPRESSED_FLAG_MASK = 0x01; + + /** */ + private static final byte RESTORE_STATE_FLAG_MASK = 0x02; /** */ private static final long serialVersionUID = 0L; @@ -46,7 +49,7 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage private GridCacheVersion lastVer; /** */ - private byte flags; + protected byte flags; /** * Required by {@link Externalizable}. @@ -64,6 +67,15 @@ protected GridDhtPartitionsAbstractMessage() { this.lastVer = lastVer; } + /** + * @param msg Message. + */ + void copyStateTo(GridDhtPartitionsAbstractMessage msg) { + msg.exchId = exchId; + msg.lastVer = lastVer; + msg.flags = flags; + } + /** {@inheritDoc} */ @Override public boolean cacheGroupMessage() { return false; @@ -91,6 +103,13 @@ protected GridDhtPartitionsAbstractMessage() { return exchId; } + /** + * @param exchId Exchange ID. + */ + public void exchangeId(GridDhtPartitionExchangeId exchId) { + this.exchId = exchId; + } + /** * @param grpId Cache group ID. * @return Parition update counters. @@ -118,6 +137,20 @@ protected final void compressed(boolean compressed) { flags = compressed ? (byte)(flags | COMPRESSED_FLAG_MASK) : (byte)(flags & ~COMPRESSED_FLAG_MASK); } + /** + * @param restoreState Restore exchange state flag. + */ + void restoreState(boolean restoreState) { + flags = restoreState ? (byte)(flags | RESTORE_STATE_FLAG_MASK) : (byte)(flags & ~RESTORE_STATE_FLAG_MASK); + } + + /** + * @return Restore exchange state flag. + */ + public boolean restoreState() { + return (flags & RESTORE_STATE_FLAG_MASK) != 0; + } + /** {@inheritDoc} */ @Override public byte fieldsCount() { return 5; 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 1b5deb558d4d0..f4ecb8ed6ea1b 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 @@ -23,25 +23,26 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.IgniteSystemProperties; +import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.NearCacheConfiguration; -import org.apache.ignite.events.CacheEvent; import org.apache.ignite.events.DiscoveryEvent; -import org.apache.ignite.events.Event; -import org.apache.ignite.events.EventType; import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException; import org.apache.ignite.internal.IgniteDiagnosticAware; import org.apache.ignite.internal.IgniteDiagnosticPrepareContext; @@ -51,16 +52,20 @@ import org.apache.ignite.internal.IgniteNeedReconnectException; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.events.DiscoveryCustomEvent; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; import org.apache.ignite.internal.managers.discovery.DiscoCache; import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache; import org.apache.ignite.internal.processors.cache.CacheAffinityChangeMessage; import org.apache.ignite.internal.processors.cache.CacheGroupContext; +import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor; import org.apache.ignite.internal.processors.cache.CachePartitionExchangeWorkerTask; import org.apache.ignite.internal.processors.cache.DynamicCacheChangeBatch; import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor; import org.apache.ignite.internal.processors.cache.ExchangeActions; +import org.apache.ignite.internal.processors.cache.ExchangeContext; +import org.apache.ignite.internal.processors.cache.ExchangeDiscoveryEvents; import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; @@ -75,7 +80,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateFinishMessage; import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateMessage; -import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter; +import org.apache.ignite.internal.util.IgniteUtils; import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; @@ -84,7 +89,6 @@ import org.apache.ignite.internal.util.typedef.T2; 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.LT; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteInClosure; @@ -118,10 +122,14 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte /** */ @GridToStringExclude - private volatile DiscoCache discoCache; + private final Object mux = new Object(); - /** Discovery event. */ - private volatile DiscoveryEvent discoEvt; + /** */ + @GridToStringExclude + private volatile DiscoCache firstEvtDiscoCache; + + /** Discovery event triggered this exchange. */ + private volatile DiscoveryEvent firstDiscoEvt; /** */ @GridToStringExclude @@ -167,11 +175,18 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte /** Last committed cache version before next topology version use. */ private AtomicReference lastVer = new AtomicReference<>(); + /** + * Message received from node joining cluster (if this is 'node join' exchange), + * needed if this exchange is merged with another one. + */ + @GridToStringExclude + private GridDhtPartitionsSingleMessage pendingJoinMsg; + /** * Messages received on non-coordinator are stored in case if this node * becomes coordinator. */ - private final Map singleMsgs = new ConcurrentHashMap8<>(); + private final Map pendingSingleMsgs = new ConcurrentHashMap8<>(); /** Messages received from new coordinator. */ private final Map fullMsgs = new ConcurrentHashMap8<>(); @@ -208,6 +223,14 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte /** */ private ConcurrentMap msgs = new ConcurrentHashMap8<>(); + /** Single messages from merged 'node join' exchanges. */ + @GridToStringExclude + private Map mergedJoinExchMsgs; + + /** Number of awaited messages for merged 'node join' exchanges. */ + @GridToStringExclude + private int awaitMergedMsgs; + /** */ @GridToStringExclude private volatile IgniteDhtPartitionHistorySuppliersMap partHistSuppliers = new IgniteDhtPartitionHistorySuppliersMap(); @@ -222,6 +245,25 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte /** */ private final AtomicBoolean done = new AtomicBoolean(); + /** */ + private ExchangeLocalState state; + + /** */ + @GridToStringExclude + private ExchangeContext exchCtx; + + /** */ + @GridToStringExclude + private FinishState finishState; + + /** Initialized when node becomes new coordinator. */ + @GridToStringExclude + private InitNewCoordinatorFuture newCrdFut; + + /** */ + @GridToStringExclude + private GridDhtPartitionsExchangeFuture mergedWith; + /** * @param cctx Cache context. * @param busyLock Busy lock. @@ -250,12 +292,44 @@ public GridDhtPartitionsExchangeFuture( log = cctx.logger(getClass()); exchLog = cctx.logger(EXCHANGE_LOG); - initFut = new GridFutureAdapter<>(); + initFut = new GridFutureAdapter() { + @Override public IgniteLogger logger() { + return log; + } + }; if (log.isDebugEnabled()) log.debug("Creating exchange future [localNode=" + cctx.localNodeId() + ", fut=" + this + ']'); } + /** + * @return Future mutex. + */ + public Object mutex() { + return mux; + } + + /** + * @return Shared cache context. + */ + public GridCacheSharedContext sharedContext() { + return cctx; + } + + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return false; + } + + /** + * @return Exchange context. + */ + public ExchangeContext context() { + assert exchCtx != null : this; + + return exchCtx; + } + /** * @param exchActions Exchange actions. */ @@ -273,9 +347,22 @@ public void affinityChangeMessage(CacheAffinityChangeMessage affChangeMsg) { this.affChangeMsg = affChangeMsg; } + /** + * @return Initial exchange version. + */ + public AffinityTopologyVersion initialVersion() { + return exchId.topologyVersion(); + } + /** {@inheritDoc} */ @Override public AffinityTopologyVersion topologyVersion() { - return exchId.topologyVersion(); + /* + Should not be called before exchange is finished since result version can change in + case of merged exchanges. + */ + assert exchangeDone() : "Should not be called before exchange is finished"; + + return exchCtx.events().topologyVersion(); } /** @@ -287,20 +374,13 @@ public void affinityChangeMessage(CacheAffinityChangeMessage affChangeMsg) { return partHistSuppliers.getSupplier(grpId, partId); } - /** - * @return Discovery cache. - */ - public DiscoCache discoCache() { - return discoCache; - } - /** * @param cacheId Cache ID. * @param rcvdFrom Node ID cache was received from. * @return {@code True} if cache was added during this exchange. */ public boolean cacheAddedOnExchange(int cacheId, UUID rcvdFrom) { - return dynamicCacheStarted(cacheId) || (exchId.isJoined() && exchId.nodeId().equals(rcvdFrom)); + return dynamicCacheStarted(cacheId) || exchCtx.events().nodeJoined(rcvdFrom); } /** @@ -309,8 +389,7 @@ public boolean cacheAddedOnExchange(int cacheId, UUID rcvdFrom) { * @return {@code True} if cache group was added during this exchange. */ public boolean cacheGroupAddedOnExchange(int grpId, UUID rcvdFrom) { - return dynamicCacheGroupStarted(grpId) || - (exchId.isJoined() && exchId.nodeId().equals(rcvdFrom)); + return dynamicCacheGroupStarted(grpId) || exchCtx.events().nodeJoined(rcvdFrom); } /** @@ -347,8 +426,8 @@ public void onEvent(GridDhtPartitionExchangeId exchId, DiscoveryEvent discoEvt, assert exchId.equals(this.exchId); this.exchId.discoveryEvent(discoEvt); - this.discoEvt = discoEvt; - this.discoCache = discoCache; + this.firstDiscoEvt= discoEvt; + this.firstEvtDiscoCache = discoCache; evtLatch.countDown(); } @@ -375,10 +454,25 @@ private boolean deactivateCluster() { } /** - * @return Discovery event. + * @return First event discovery event. + * + */ + public DiscoveryEvent firstEvent() { + return firstDiscoEvt; + } + + /** + * @return Discovery cache for first event. + */ + public DiscoCache firstEventCache() { + return firstEvtDiscoCache; + } + + /** + * @return Events processed in this exchange. */ - public DiscoveryEvent discoveryEvent() { - return discoEvt; + public ExchangeDiscoveryEvents events() { + return exchCtx.events(); } /** @@ -408,6 +502,21 @@ private void leaveBusy() { busyLock.readLock().unlock(); } + /** + * @param newCrd {@code True} if node become coordinator on this exchange. + * @throws IgniteCheckedException If failed. + */ + private void initCoordinatorCaches(boolean newCrd) throws IgniteCheckedException { + if (newCrd) { + IgniteInternalFuture fut = cctx.affinity().initCoordinatorCaches(this, false); + + if (fut != null) + fut.get(); + + cctx.exchange().onCoordinatorInitialized(); + } + } + /** * Starts activity. * @@ -424,15 +533,13 @@ public void init(boolean newCrd) throws IgniteInterruptedCheckedException { U.await(evtLatch); - assert discoEvt != null : this; - assert exchId.nodeId().equals(discoEvt.eventNode().id()) : this; + assert firstDiscoEvt != null : this; + assert exchId.nodeId().equals(firstDiscoEvt.eventNode().id()) : this; try { - discoCache.updateAlives(cctx.discovery()); - - AffinityTopologyVersion topVer = topologyVersion(); + AffinityTopologyVersion topVer = initialVersion(); - srvNodes = new ArrayList<>(discoCache.serverNodes()); + srvNodes = new ArrayList<>(firstEvtDiscoCache.serverNodes()); remaining.addAll(F.nodeIds(F.view(srvNodes, F.remoteNodes(cctx.localNodeId())))); @@ -440,19 +547,30 @@ public void init(boolean newCrd) throws IgniteInterruptedCheckedException { boolean crdNode = crd != null && crd.isLocal(); - if (exchLog.isInfoEnabled()) + exchCtx = new ExchangeContext(crdNode, this); + + assert state == null : state; + + if (crdNode) + state = ExchangeLocalState.CRD; + else + state = cctx.kernalContext().clientNode() ? ExchangeLocalState.CLIENT : ExchangeLocalState.SRV; + + if (exchLog.isInfoEnabled()) { exchLog.info("Started exchange init [topVer=" + topVer + ", crd=" + crdNode + - ", evt=" + discoEvt.type() + - ", node=" + discoEvt.node() + - ", evtNode=" + discoEvt.node() + - ", customEvt=" + (discoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT ? ((DiscoveryCustomEvent)discoEvt).customMessage() : null) + - ']'); + ", evt=" + IgniteUtils.gridEventName(firstDiscoEvt.type()) + + ", evtNode=" + firstDiscoEvt.eventNode().id() + + ", customEvt=" + (firstDiscoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT ? ((DiscoveryCustomEvent)firstDiscoEvt).customMessage() : null) + + ", allowMerge=" + exchCtx.mergeExchanges() + ']'); + } ExchangeType exchange; - if (discoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT) { - DiscoveryCustomMessage msg = ((DiscoveryCustomEvent)discoEvt).customMessage(); + if (firstDiscoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT) { + assert !exchCtx.mergeExchanges(); + + DiscoveryCustomMessage msg = ((DiscoveryCustomEvent)firstDiscoEvt).customMessage(); if (msg instanceof ChangeGlobalStateMessage) { assert exchActions != null && !exchActions.empty(); @@ -465,7 +583,7 @@ else if (msg instanceof DynamicCacheChangeBatch) { exchange = onCacheChangeRequest(crdNode); } else if (msg instanceof SnapshotDiscoveryMessage) { - exchange = CU.clientNode(discoEvt.eventNode()) ? + exchange = CU.clientNode(firstDiscoEvt.eventNode()) ? onClientNodeEvent(crdNode) : onServerNodeEvent(crdNode); } @@ -474,12 +592,14 @@ else if (msg instanceof SnapshotDiscoveryMessage) { exchange = onAffinityChangeRequest(crdNode); } + + initCoordinatorCaches(newCrd); } else { - if (discoEvt.type() == EVT_NODE_JOINED) { - if (!discoEvt.eventNode().isLocal()) { + if (firstDiscoEvt.type() == EVT_NODE_JOINED) { + if (!firstDiscoEvt.eventNode().isLocal()) { Collection receivedCaches = cctx.cache().startReceivedCaches( - discoEvt.eventNode().id(), + firstDiscoEvt.eventNode().id(), topVer); cctx.affinity().initStartedCaches(crdNode, this, receivedCaches); @@ -488,16 +608,35 @@ else if (msg instanceof SnapshotDiscoveryMessage) { initCachesOnLocalJoin(); } - if (newCrd) { - IgniteInternalFuture fut = cctx.affinity().initCoordinatorCaches(this); + initCoordinatorCaches(newCrd); - if (fut != null) - fut.get(); - } + if (exchCtx.mergeExchanges()) { + if (localJoinExchange()) { + if (cctx.kernalContext().clientNode()) { + onClientNodeEvent(crdNode); + + exchange = ExchangeType.CLIENT; + } + else { + onServerNodeEvent(crdNode); + + exchange = ExchangeType.ALL; + } + } + else { + if (CU.clientNode(firstDiscoEvt.eventNode())) + exchange = onClientNodeEvent(crdNode); + else + exchange = cctx.kernalContext().clientNode() ? ExchangeType.CLIENT : ExchangeType.ALL; + } - exchange = CU.clientNode(discoEvt.eventNode()) ? - onClientNodeEvent(crdNode) : - onServerNodeEvent(crdNode); + if (exchId.isLeft()) + onLeft(); + } + else { + exchange = CU.clientNode(firstDiscoEvt.eventNode()) ? onClientNodeEvent(crdNode) : + onServerNodeEvent(crdNode); + } } updateTopologies(crdNode); @@ -510,7 +649,8 @@ else if (msg instanceof SnapshotDiscoveryMessage) { } case CLIENT: { - initTopologies(); + if (!exchCtx.mergeExchanges() && exchCtx.fetchAffinityOnJoin()) + initTopologies(); clientOnlyExchange(); @@ -576,7 +716,7 @@ private void initCachesOnLocalJoin() throws IgniteCheckedException { cctx.database().readCheckpointAndRestoreMemory(startDescs); } - cctx.cache().startCachesOnLocalJoin(caches, topologyVersion()); + cctx.cache().startCachesOnLocalJoin(caches, initialVersion()); } /** @@ -591,7 +731,7 @@ private void initTopologies() throws IgniteCheckedException { if (grp.isLocal()) continue; - grp.topology().beforeExchange(this, !centralizedAff); + grp.topology().beforeExchange(this, !centralizedAff, false); } } } @@ -619,7 +759,7 @@ private void updateTopologies(boolean crd) throws IgniteCheckedException { boolean updateTop = exchId.topologyVersion().equals(grp.localStartVersion()); if (updateTop && clientTop != null) { - top.update(topologyVersion(), + top.update(null, clientTop.partitionMap(true), clientTop.updateCounters(false), Collections.emptySet()); @@ -628,13 +768,13 @@ private void updateTopologies(boolean crd) throws IgniteCheckedException { top.updateTopologyVersion( this, - discoCache(), + events().discoveryCache(), updSeq, cacheGroupStopping(grp.groupId())); } for (GridClientPartitionTopology top : cctx.exchange().clientTopologies()) - top.updateTopologyVersion(this, discoCache(), -1, cacheGroupStopping(top.groupId())); + top.updateTopologyVersion(this, events().discoveryCache(), -1, cacheGroupStopping(top.groupId())); } /** @@ -652,7 +792,7 @@ private ExchangeType onClusterStateChangeRequest(boolean crd) { if (log.isInfoEnabled()) { log.info("Start activation process [nodeId=" + cctx.localNodeId() + ", client=" + cctx.kernalContext().clientNode() + - ", topVer=" + topologyVersion() + "]"); + ", topVer=" + initialVersion() + "]"); } try { @@ -672,18 +812,18 @@ private ExchangeType onClusterStateChangeRequest(boolean crd) { if (log.isInfoEnabled()) { log.info("Successfully activated caches [nodeId=" + cctx.localNodeId() + ", client=" + cctx.kernalContext().clientNode() + - ", topVer=" + topologyVersion() + "]"); + ", topVer=" + initialVersion() + "]"); } } catch (Exception e) { U.error(log, "Failed to activate node components [nodeId=" + cctx.localNodeId() + ", client=" + cctx.kernalContext().clientNode() + - ", topVer=" + topologyVersion() + "]", e); + ", topVer=" + initialVersion() + "]", e); changeGlobalStateE = e; if (crd) { - synchronized (this) { + synchronized (mux) { changeGlobalStateExceptions.put(cctx.localNodeId(), e); } } @@ -693,7 +833,7 @@ private ExchangeType onClusterStateChangeRequest(boolean crd) { if (log.isInfoEnabled()) { log.info("Start deactivation process [nodeId=" + cctx.localNodeId() + ", client=" + cctx.kernalContext().clientNode() + - ", topVer=" + topologyVersion() + "]"); + ", topVer=" + initialVersion() + "]"); } try { @@ -707,13 +847,13 @@ private ExchangeType onClusterStateChangeRequest(boolean crd) { log.info("Successfully deactivated data structures, services and caches [" + "nodeId=" + cctx.localNodeId() + ", client=" + cctx.kernalContext().clientNode() + - ", topVer=" + topologyVersion() + "]"); + ", topVer=" + initialVersion() + "]"); } } catch (Exception e) { U.error(log, "Failed to deactivate node components [nodeId=" + cctx.localNodeId() + ", client=" + cctx.kernalContext().clientNode() + - ", topVer=" + topologyVersion() + "]", e); + ", topVer=" + initialVersion() + "]", e); changeGlobalStateE = e; } @@ -759,19 +899,19 @@ private ExchangeType onAffinityChangeRequest(boolean crd) throws IgniteCheckedEx * @return Exchange type. */ private ExchangeType onClientNodeEvent(boolean crd) throws IgniteCheckedException { - assert CU.clientNode(discoEvt.eventNode()) : this; + assert CU.clientNode(firstDiscoEvt.eventNode()) : this; - if (discoEvt.type() == EVT_NODE_LEFT || discoEvt.type() == EVT_NODE_FAILED) { + if (firstDiscoEvt.type() == EVT_NODE_LEFT || firstDiscoEvt.type() == EVT_NODE_FAILED) { onLeft(); - assert !discoEvt.eventNode().isLocal() : discoEvt; + assert !firstDiscoEvt.eventNode().isLocal() : firstDiscoEvt; } else - assert discoEvt.type() == EVT_NODE_JOINED || discoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT : discoEvt; + assert firstDiscoEvt.type() == EVT_NODE_JOINED || firstDiscoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT : firstDiscoEvt; cctx.affinity().onClientEvent(this, crd); - return discoEvt.eventNode().isLocal() ? ExchangeType.CLIENT : ExchangeType.NONE; + return firstDiscoEvt.eventNode().isLocal() ? ExchangeType.CLIENT : ExchangeType.NONE; } /** @@ -780,12 +920,12 @@ private ExchangeType onClientNodeEvent(boolean crd) throws IgniteCheckedExceptio * @return Exchange type. */ private ExchangeType onServerNodeEvent(boolean crd) throws IgniteCheckedException { - assert !CU.clientNode(discoEvt.eventNode()) : this; + assert !CU.clientNode(firstDiscoEvt.eventNode()) : this; - if (discoEvt.type() == EVT_NODE_LEFT || discoEvt.type() == EVT_NODE_FAILED) { + if (firstDiscoEvt.type() == EVT_NODE_LEFT || firstDiscoEvt.type() == EVT_NODE_FAILED) { onLeft(); - warnNoAffinityNodes(); + exchCtx.events().warnNoAffinityNodes(cctx); centralizedAff = cctx.affinity().onServerLeft(this, crd); } @@ -814,12 +954,14 @@ private void clientOnlyExchange() throws IgniteCheckedException { for (CacheGroupContext grp : cctx.cache().cacheGroups()) { GridAffinityAssignmentCache aff = grp.affinity(); - aff.initialize(topologyVersion(), aff.idealAssignment()); + aff.initialize(initialVersion(), aff.idealAssignment()); } } + else + onAllServersLeft(); } - onDone(topologyVersion()); + onDone(initialVersion()); } /** @@ -844,7 +986,7 @@ private void distributedExchange() throws IgniteCheckedException { waitPartitionRelease(); - boolean topChanged = discoEvt.type() != EVT_DISCOVERY_CUSTOM_EVT || affChangeMsg != null; + boolean topChanged = firstDiscoEvt.type() != EVT_DISCOVERY_CUSTOM_EVT || affChangeMsg != null; for (GridCacheContext cacheCtx : cctx.cacheContexts()) { if (cacheCtx.isLocal() || cacheStopping(cacheCtx.cacheId())) @@ -856,18 +998,22 @@ private void distributedExchange() throws IgniteCheckedException { } } - for (CacheGroupContext grp : cctx.cache().cacheGroups()) { - if (grp.isLocal() || cacheGroupStopping(grp.groupId())) - continue; + if (!exchCtx.mergeExchanges()) { + for (CacheGroupContext grp : cctx.cache().cacheGroups()) { + if (grp.isLocal() || cacheGroupStopping(grp.groupId())) + continue; - grp.topology().beforeExchange(this, !centralizedAff); + // It is possible affinity is not initialized yet if node joins to cluster. + if (grp.affinity().lastVersion().topologyVersion() > 0) + grp.topology().beforeExchange(this, !centralizedAff, false); + } } cctx.database().beforeExchange(this); if (crd.isLocal()) { if (remaining.isEmpty()) - onAllReceived(); + onAllReceived(null); } else sendPartitions(crd); @@ -882,7 +1028,7 @@ private void tryToPerformLocalSnapshotOperation() { try { long start = U.currentTimeMillis(); - IgniteInternalFuture fut = cctx.snapshot().tryStartLocalSnapshotOperation(discoEvt); + IgniteInternalFuture fut = cctx.snapshot().tryStartLocalSnapshotOperation(firstDiscoEvt); if (fut != null) { fut.get(); @@ -909,7 +1055,7 @@ private void tryToPerformLocalSnapshotOperation() { * @throws IgniteCheckedException If failed. */ private void waitPartitionRelease() throws IgniteCheckedException { - IgniteInternalFuture partReleaseFut = cctx.partitionReleaseFuture(topologyVersion()); + IgniteInternalFuture partReleaseFut = cctx.partitionReleaseFuture(initialVersion()); // Assign to class variable so it will be included into toString() method. this.partReleaseFut = partReleaseFut; @@ -937,7 +1083,7 @@ private void waitPartitionRelease() throws IgniteCheckedException { catch (IgniteFutureTimeoutCheckedException ignored) { // Print pending transactions and locks that might have led to hang. if (nextDumpTime <= U.currentTimeMillis()) { - dumpPendingObjects(); + dumpPendingObjects(partReleaseFut); nextDumpTime = U.currentTimeMillis() + nextDumpTimeout(dumpCnt++, futTimeout); } @@ -1011,71 +1157,17 @@ private void onLeft() { } /** - * + * @param partReleaseFut Partition release future. */ - private void warnNoAffinityNodes() { - List cachesWithoutNodes = null; - - for (DynamicCacheDescriptor cacheDesc : cctx.cache().cacheDescriptors().values()) { - if (discoCache.cacheGroupAffinityNodes(cacheDesc.groupId()).isEmpty()) { - if (cachesWithoutNodes == null) - cachesWithoutNodes = new ArrayList<>(); - - cachesWithoutNodes.add(cacheDesc.cacheName()); - - // Fire event even if there is no client cache started. - if (cctx.gridEvents().isRecordable(EventType.EVT_CACHE_NODES_LEFT)) { - Event evt = new CacheEvent( - cacheDesc.cacheName(), - cctx.localNode(), - cctx.localNode(), - "All server nodes have left the cluster.", - EventType.EVT_CACHE_NODES_LEFT, - 0, - false, - null, - null, - null, - null, - false, - null, - false, - null, - null, - null - ); - - cctx.gridEvents().record(evt); - } - } - } - - if (cachesWithoutNodes != null) { - StringBuilder sb = - new StringBuilder("All server nodes for the following caches have left the cluster: "); - - for (int i = 0; i < cachesWithoutNodes.size(); i++) { - String cache = cachesWithoutNodes.get(i); - - sb.append('\'').append(cache).append('\''); - - if (i != cachesWithoutNodes.size() - 1) - sb.append(", "); - } - - U.quietAndWarn(log, sb.toString()); + private void dumpPendingObjects(IgniteInternalFuture partReleaseFut) { + U.warn(cctx.kernalContext().cluster().diagnosticLog(), + "Failed to wait for partition release future [topVer=" + initialVersion() + + ", node=" + cctx.localNodeId() + "]"); - U.quietAndWarn(log, "Must have server nodes for caches to operate."); - } - } + U.warn(log, "Partition release future: " + partReleaseFut); - /** - * - */ - private void dumpPendingObjects() { U.warn(cctx.kernalContext().cluster().diagnosticLog(), - "Failed to wait for partition release future [topVer=" + topologyVersion() + - ", node=" + cctx.localNodeId() + "]. Dumping pending objects that might be the cause: "); + "Dumping pending objects that might be the cause: "); try { cctx.exchange().dumpDebugInfo(this); @@ -1101,6 +1193,13 @@ private boolean cacheStopping(int cacheId) { return exchActions != null && exchActions.cacheStopped(cacheId); } + /** + * @return {@code True} if exchange for local node join. + */ + public boolean localJoinExchange() { + return firstDiscoEvt.type() == EVT_NODE_JOINED && firstDiscoEvt.eventNode().isLocal(); + } + /** * @param node Node. * @throws IgniteCheckedException If failed. @@ -1125,19 +1224,20 @@ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException true); } else { - msg = cctx.exchange().createPartitionsSingleMessage(node, - exchangeId(), + msg = cctx.exchange().createPartitionsSingleMessage(exchangeId(), false, true); - } - Map> partHistReserved0 = partHistReserved; + Map> partHistReserved0 = partHistReserved; - if (partHistReserved0 != null) - msg.partitionHistoryCounters(partHistReserved0); + if (partHistReserved0 != null) + msg.partitionHistoryCounters(partHistReserved0); + } if (stateChangeExchange() && changeGlobalStateE != null) msg.setError(changeGlobalStateE); + else if (localJoinExchange()) + msg.cacheGroupsAffinityRequest(exchCtx.groupsAffinityRequestOnJoin()); if (log.isDebugEnabled()) log.debug("Sending local partitions [nodeId=" + node.id() + ", exchId=" + exchId + ", msg=" + msg + ']'); @@ -1172,30 +1272,72 @@ private GridDhtPartitionsFullMessage createPartitionsMessage(boolean compress) { } /** + * @param msg Message to send. * @param nodes Nodes. - * @throws IgniteCheckedException If failed. + * @param mergedJoinExchMsgs Messages received from merged 'join node' exchanges. + * @param joinedNodeAff Affinity if was requested by some nodes. */ - private void sendAllPartitions(Collection nodes) throws IgniteCheckedException { - GridDhtPartitionsFullMessage m = createPartitionsMessage(true); + private void sendAllPartitions( + GridDhtPartitionsFullMessage msg, + Collection nodes, + Map mergedJoinExchMsgs, + Map joinedNodeAff) { + boolean singleNode = nodes.size() == 1; + + GridDhtPartitionsFullMessage joinedNodeMsg = null; assert !nodes.contains(cctx.localNode()); if (log.isDebugEnabled()) { log.debug("Sending full partition map [nodeIds=" + F.viewReadOnly(nodes, F.node2id()) + - ", exchId=" + exchId + ", msg=" + m + ']'); + ", exchId=" + exchId + ", msg=" + msg + ']'); } for (ClusterNode node : nodes) { + GridDhtPartitionsFullMessage sndMsg = msg; + + if (joinedNodeAff != null) { + if (singleNode) + msg.joinedNodeAffinity(joinedNodeAff); + else { + GridDhtPartitionsSingleMessage singleMsg = msgs.get(node.id()); + + if (singleMsg != null && singleMsg.cacheGroupsAffinityRequest() != null) { + if (joinedNodeMsg == null) { + joinedNodeMsg = msg.copy(); + + joinedNodeMsg.joinedNodeAffinity(joinedNodeAff); + } + + sndMsg = joinedNodeMsg; + } + } + } + try { - cctx.io().send(node, m, SYSTEM_POOL); + GridDhtPartitionExchangeId sndExchId = exchangeId(); + + if (mergedJoinExchMsgs != null) { + GridDhtPartitionsSingleMessage mergedMsg = mergedJoinExchMsgs.get(node.id()); + + if (mergedMsg != null) + sndExchId = mergedMsg.exchangeId(); + } + + if (sndExchId != null && !sndExchId.equals(exchangeId())) { + sndMsg = sndMsg.copy(); + + sndMsg.exchangeId(sndExchId); + } + + cctx.io().send(node, sndMsg, SYSTEM_POOL); + } + catch (ClusterTopologyCheckedException e) { + if (log.isDebugEnabled()) + log.debug("Failed to send partitions, node failed: " + node); } catch (IgniteCheckedException e) { - if (cctx.io().checkNodeLeft(node.id(), e, false)) { - if (log.isDebugEnabled()) - log.debug("Failed to send partitions, node failed: " + node); - } - else - U.error(log, "Failed to send partitions [node=" + node + ']', e); + U.error(log, "Failed to send partitions [node=" + node + ']', e); } } } @@ -1222,16 +1364,34 @@ private void sendPartitions(ClusterNode oldestNode) { * @return {@code True} if exchange triggered by server node join or fail. */ public boolean serverNodeDiscoveryEvent() { - assert discoEvt != null; + assert exchCtx != null; - return discoEvt.type() != EVT_DISCOVERY_CUSTOM_EVT && !CU.clientNode(discoEvt.eventNode()); + return exchCtx.events().hasServerJoin() || exchCtx.events().hasServerLeft(); + } + + /** {@inheritDoc} */ + @Override public boolean exchangeDone() { + return done.get(); + } + + /** + * Finish merged future to allow GridCachePartitionExchangeManager.ExchangeFutureSet cleanup. + */ + public void finishMerged() { + super.onDone(null, null); } /** {@inheritDoc} */ @Override public boolean onDone(@Nullable AffinityTopologyVersion res, @Nullable Throwable err) { - if (!done.compareAndSet(false, true)) + if (isDone() || !done.compareAndSet(false, true)) return false; + log.info("Finish exchange future [startVer=" + initialVersion() + + ", resVer=" + res + + ", err=" + err + ']'); + + assert res != null || err != null; + if (err == null && !cctx.kernalContext().clientNode() && (serverNodeDiscoveryEvent() || affChangeMsg != null)) { @@ -1239,18 +1399,20 @@ public boolean serverNodeDiscoveryEvent() { if (!cacheCtx.affinityNode() || cacheCtx.isLocal()) continue; - cacheCtx.continuousQueries().flushBackupQueue(exchId.topologyVersion()); + cacheCtx.continuousQueries().flushBackupQueue(res); } } if (err == null) { if (centralizedAff) { + assert !exchCtx.mergeExchanges(); + for (CacheGroupContext grp : cctx.cache().cacheGroups()) { if (grp.isLocal()) continue; try { - grp.topology().initPartitions(this); + grp.topology().initPartitionsWhenAffinityReady(res, this); } catch (IgniteInterruptedCheckedException e) { U.error(log, "Failed to initialize partitions.", e); @@ -1263,7 +1425,7 @@ public boolean serverNodeDiscoveryEvent() { if (drCacheCtx.isDrEnabled()) { try { - drCacheCtx.dr().onExchange(topologyVersion(), exchId.isLeft()); + drCacheCtx.dr().onExchange(res, exchId.isLeft()); } catch (IgniteCheckedException e) { U.error(log, "Failed to notify DR: " + e, e); @@ -1271,25 +1433,22 @@ public boolean serverNodeDiscoveryEvent() { } } - if (serverNodeDiscoveryEvent() && - (discoEvt.type() == EVT_NODE_LEFT || - discoEvt.type() == EVT_NODE_FAILED || - discoEvt.type() == EVT_NODE_JOINED)) - detectLostPartitions(); + if (serverNodeDiscoveryEvent()) + detectLostPartitions(res); Map m = U.newHashMap(cctx.cache().cacheGroups().size()); for (CacheGroupContext grp : cctx.cache().cacheGroups()) - m.put(grp.groupId(), validateCacheGroup(grp, discoEvt.topologyNodes())); + m.put(grp.groupId(), validateCacheGroup(grp, events().lastEvent().topologyNodes())); grpValidRes = m; } tryToPerformLocalSnapshotOperation(); - cctx.cache().onExchangeDone(exchId.topologyVersion(), exchActions, err); + cctx.cache().onExchangeDone(initialVersion(), exchActions, err); - cctx.exchange().onExchangeDone(this, err); + cctx.exchange().onExchangeDone(res, initialVersion(), err); if (exchActions != null && err == null) exchActions.completeRequestFutures(cctx); @@ -1316,7 +1475,7 @@ public boolean serverNodeDiscoveryEvent() { if (err == null) { for (CacheGroupContext grp : cctx.cache().cacheGroups()) { if (!grp.isLocal()) - grp.topology().onExchangeDone(grp.affinity().cachedAffinity(topologyVersion()), false); + grp.topology().onExchangeDone(this, grp.affinity().readyAffinity(res), false); } } @@ -1327,17 +1486,24 @@ public boolean serverNodeDiscoveryEvent() { initFut.onDone(err == null); - if (exchId.isLeft()) { - for (CacheGroupContext grp : cctx.cache().cacheGroups()) - grp.affinityFunction().removeNode(exchId.nodeId()); + if (exchCtx != null && exchCtx.events().hasServerLeft()) { + ExchangeDiscoveryEvents evts = exchCtx.events(); + + for (DiscoveryEvent evt : exchCtx.events().events()) { + if (evts.serverLeftEvent(evt)) { + for (CacheGroupContext grp : cctx.cache().cacheGroups()) + grp.affinityFunction().removeNode(evt.eventNode().id()); + } + } } exchActions = null; - if (discoEvt instanceof DiscoveryCustomEvent) - ((DiscoveryCustomEvent)discoEvt).customMessage(null); + if (firstDiscoEvt instanceof DiscoveryCustomEvent) + ((DiscoveryCustomEvent)firstDiscoEvt).customMessage(null); - cctx.exchange().lastFinishedFuture(this); + if (err == null) + cctx.exchange().lastFinishedFuture(this); return true; } @@ -1349,7 +1515,7 @@ public boolean serverNodeDiscoveryEvent() { * Cleans up resources to avoid excessive memory usage. */ public void cleanUp() { - singleMsgs.clear(); + pendingSingleMsgs.clear(); fullMsgs.clear(); msgs.clear(); changeGlobalStateExceptions.clear(); @@ -1357,6 +1523,10 @@ public void cleanUp() { partReleaseFut = null; changeGlobalStateE = null; exchActions = null; + mergedJoinExchMsgs = null; + pendingJoinMsg = null; + exchCtx = null; + newCrdFut = null; } /** @@ -1378,137 +1548,401 @@ private void updateLastVersion(GridCacheVersion ver) { } /** - * @param node Sender node. - * @param msg Single partition info. + * Records that this exchange if merged with another 'node join' exchange. + * + * @param node Joined node. + * @param msg Joined node message if already received. + * @return {@code True} if need to wait for message from joined server node. */ - public void onReceive(final ClusterNode node, final GridDhtPartitionsSingleMessage msg) { - assert msg != null; - assert msg.exchangeId().equals(exchId) : msg; + private boolean addMergedJoinExchange(ClusterNode node, @Nullable GridDhtPartitionsSingleMessage msg) { + assert Thread.holdsLock(mux); + assert node != null; + assert state == ExchangeLocalState.CRD : state; - if (!msg.client()) { - assert msg.lastVersion() != null : msg; + if (msg == null && newCrdFut != null) + msg = newCrdFut.joinExchangeMessage(node.id()); - updateLastVersion(msg.lastVersion()); - } + UUID nodeId = node.id(); - if (isDone()) { - if (log.isDebugEnabled()) - log.debug("Received message for finished future (will reply only to sender) [msg=" + msg + - ", fut=" + this + ']'); + boolean wait = false; - if (!centralizedAff) - sendAllPartitions(node.id(), cctx.gridConfig().getNetworkSendRetryCount()); + if (CU.clientNode(node)) { + if (msg != null) + waitAndReplyToNode(nodeId, msg); } else { - initFut.listen(new CI1>() { - @Override public void apply(IgniteInternalFuture f) { - try { - if (!f.get()) - return; - } - catch (IgniteCheckedException e) { - U.error(log, "Failed to initialize exchange future: " + this, e); + if (mergedJoinExchMsgs == null) + mergedJoinExchMsgs = new LinkedHashMap<>(); - return; - } + if (msg != null) { + assert msg.exchangeId().topologyVersion().equals(new AffinityTopologyVersion(node.order())); + + log.info("Merge server join exchange, message received [curFut=" + initialVersion() + + ", node=" + nodeId + ']'); + + mergedJoinExchMsgs.put(nodeId, msg); + } + else { + if (cctx.discovery().alive(nodeId)) { + log.info("Merge server join exchange, wait for message [curFut=" + initialVersion() + + ", node=" + nodeId + ']'); + + wait = true; + + mergedJoinExchMsgs.put(nodeId, null); - processMessage(node, msg); + awaitMergedMsgs++; } - }); + else { + log.info("Merge server join exchange, awaited node left [curFut=" + initialVersion() + + ", node=" + nodeId + ']'); + } + } } + + return wait; } /** - * @param node Sender node. - * @param msg Message. + * Merges this exchange with given one. + * + * @param fut Current exchange to merge with. + * @return {@code True} if need wait for message from joined server node. */ - private void processMessage(ClusterNode node, GridDhtPartitionsSingleMessage msg) { - boolean allReceived = false; - boolean updateSingleMap = false; + public boolean mergeJoinExchange(GridDhtPartitionsExchangeFuture fut) { + boolean wait; - synchronized (this) { - assert crd != null; + synchronized (mux) { + assert (!isDone() && !initFut.isDone()) || cctx.kernalContext().isStopping() : this; + assert (mergedWith == null && state == null) || cctx.kernalContext().isStopping() : this; - if (crd.isLocal()) { - if (remaining.remove(node.id())) { - updateSingleMap = true; + state = ExchangeLocalState.MERGED; - pendingSingleUpdates++; + mergedWith = fut; - if (stateChangeExchange() && msg.getError() != null) - changeGlobalStateExceptions.put(node.id(), msg.getError()); + ClusterNode joinedNode = firstDiscoEvt.eventNode(); - allReceived = remaining.isEmpty(); - } - } - else - singleMsgs.put(node, msg); + wait = fut.addMergedJoinExchange(joinedNode, pendingJoinMsg); } - if (updateSingleMap) { - try { - // Do not update partition map, in case cluster transitioning to inactive state. - if (!deactivateCluster()) - updatePartitionSingleMap(node, msg); - } - finally { - synchronized (this) { - assert pendingSingleUpdates > 0; + return wait; + } - pendingSingleUpdates--; + /** + * @param fut Current future. + * @return Pending join request if any. + */ + @Nullable public GridDhtPartitionsSingleMessage mergeJoinExchangeOnDone(GridDhtPartitionsExchangeFuture fut) { + synchronized (mux) { + assert !isDone(); + assert !initFut.isDone(); + assert mergedWith == null; + assert state == null; - if (pendingSingleUpdates == 0) - notifyAll(); - } - } - } + state = ExchangeLocalState.MERGED; - if (allReceived) { - awaitSingleMapUpdates(); + mergedWith = fut; - onAllReceived(); + return pendingJoinMsg; } } /** - * + * @param node Sender node. + * @param msg Message. */ - private synchronized void awaitSingleMapUpdates() { - try { - while (pendingSingleUpdates > 0) - U.wait(this); - } - catch (IgniteInterruptedCheckedException e) { - U.warn(log, "Failed to wait for partition map updates, thread was interrupted: " + e); + private void processMergedMessage(final ClusterNode node, final GridDhtPartitionsSingleMessage msg) { + if (msg.client()) { + waitAndReplyToNode(node.id(), msg); + + return; } - } - /** - * @param fut Affinity future. - */ - private void onAffinityInitialized(IgniteInternalFuture>>> fut) { - try { - assert fut.isDone(); + boolean done = false; - Map>> assignmentChange = fut.get(); + FinishState finishState0 = null; - GridDhtPartitionsFullMessage m = createPartitionsMessage(false); + synchronized (mux) { + if (state == ExchangeLocalState.DONE) { + assert finishState != null; - CacheAffinityChangeMessage msg = new CacheAffinityChangeMessage(exchId, m, assignmentChange); + finishState0 = finishState; + } + else { + boolean process = mergedJoinExchMsgs != null && + mergedJoinExchMsgs.containsKey(node.id()) && + mergedJoinExchMsgs.get(node.id()) == null; - if (log.isDebugEnabled()) - log.debug("Centralized affinity exchange, send affinity change message: " + msg); + log.info("Merge server join exchange, received message [curFut=" + initialVersion() + + ", node=" + node.id() + + ", msgVer=" + msg.exchangeId().topologyVersion() + + ", process=" + process + + ", awaited=" + awaitMergedMsgs + ']'); - cctx.discovery().sendCustomEvent(msg); + if (process) { + mergedJoinExchMsgs.put(node.id(), msg); + + assert awaitMergedMsgs > 0 : awaitMergedMsgs; + + awaitMergedMsgs--; + + done = awaitMergedMsgs == 0; + } + } } - catch (IgniteCheckedException e) { - onDone(e); + + if (finishState0 != null) { + sendAllPartitionsToNode(finishState0, msg, node.id()); + + return; } + + if (done) + finishExchangeOnCoordinator(null); } /** - * @param top Topology to assign. - */ + * @param node Sender node. + * @param msg Single partition info. + */ + public void onReceiveSingleMessage(final ClusterNode node, final GridDhtPartitionsSingleMessage msg) { + assert !node.isDaemon() : node; + assert msg != null; + assert exchId.equals(msg.exchangeId()) : msg; + assert !cctx.kernalContext().clientNode(); + + if (msg.restoreState()) { + InitNewCoordinatorFuture newCrdFut0; + + synchronized (mux) { + assert newCrdFut != null; + + newCrdFut0 = newCrdFut; + } + + newCrdFut0.onMessage(node, msg); + + return; + } + + if (!msg.client()) { + assert msg.lastVersion() != null : msg; + + updateLastVersion(msg.lastVersion()); + } + + GridDhtPartitionsExchangeFuture mergedWith0 = null; + + synchronized (mux) { + if (state == ExchangeLocalState.MERGED) { + assert mergedWith != null; + + mergedWith0 = mergedWith; + } + else { + assert state != ExchangeLocalState.CLIENT; + + if (exchangeId().isJoined() && node.id().equals(exchId.nodeId())) + pendingJoinMsg = msg; + } + } + + if (mergedWith0 != null) { + mergedWith0.processMergedMessage(node, msg); + + return; + } + + initFut.listen(new CI1>() { + @Override public void apply(IgniteInternalFuture f) { + try { + if (!f.get()) + return; + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to initialize exchange future: " + this, e); + + return; + } + + processSingleMessage(node.id(), msg); + } + }); + } + + /** + * @param nodeId Node ID. + * @param msg Client's message. + */ + public void waitAndReplyToNode(final UUID nodeId, final GridDhtPartitionsSingleMessage msg) { + listen(new CI1>() { + @Override public void apply(IgniteInternalFuture fut) { + if (cctx.kernalContext().isStopping()) + return; + + FinishState finishState0; + + synchronized (mux) { + finishState0 = finishState; + } + + if (finishState0 == null) { + assert firstDiscoEvt.type() == EVT_NODE_JOINED && CU.clientNode(firstDiscoEvt.eventNode()) : this; + + finishState0 = new FinishState(cctx.localNodeId(), + initialVersion(), + createPartitionsMessage(true)); + } + + sendAllPartitionsToNode(finishState0, msg, nodeId); + } + }); + } + + /** + * @param nodeId Sender node. + * @param msg Message. + */ + private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage msg) { + if (msg.client()) { + waitAndReplyToNode(nodeId, msg); + + return; + } + + boolean allReceived = false; + boolean updateSingleMap = false; + + FinishState finishState0 = null; + + synchronized (mux) { + assert crd != null; + + switch (state) { + case DONE: { + log.info("Received single message, already done [ver=" + initialVersion() + + ", node=" + nodeId + ']'); + + assert finishState != null; + + finishState0 = finishState; + + break; + } + + case CRD: { + assert crd.isLocal() : crd; + + if (remaining.remove(nodeId)) { + updateSingleMap = true; + + pendingSingleUpdates++; + + if (stateChangeExchange() && msg.getError() != null) + changeGlobalStateExceptions.put(nodeId, msg.getError()); + + allReceived = remaining.isEmpty(); + + log.info("Coordinator received single message [ver=" + initialVersion() + + ", node=" + nodeId + + ", allReceived=" + allReceived + ']'); + } + + break; + } + + case SRV: + case BECOME_CRD: { + log.info("Non-coordinator received single message [ver=" + initialVersion() + + ", node=" + nodeId + ", state=" + state + ']'); + + pendingSingleMsgs.put(nodeId, msg); + + break; + } + + default: + assert false : state; + } + } + + if (finishState0 != null) { + sendAllPartitionsToNode(finishState0, msg, nodeId); + + return; + } + + if (updateSingleMap) { + try { + // Do not update partition map, in case cluster transitioning to inactive state. + if (!deactivateCluster()) + updatePartitionSingleMap(nodeId, msg); + } + finally { + synchronized (mux) { + assert pendingSingleUpdates > 0; + + pendingSingleUpdates--; + + if (pendingSingleUpdates == 0) + mux.notifyAll(); + } + } + } + + if (allReceived) { + if (!awaitSingleMapUpdates()) + return; + + onAllReceived(null); + } + } + + /** + * @return {@code False} if interrupted. + */ + private boolean awaitSingleMapUpdates() { + try { + synchronized (mux) { + while (pendingSingleUpdates > 0) + U.wait(mux); + } + + return true; + } + catch (IgniteInterruptedCheckedException e) { + U.warn(log, "Failed to wait for partition map updates, thread was interrupted: " + e); + + return false; + } + } + + /** + * @param fut Affinity future. + */ + private void onAffinityInitialized(IgniteInternalFuture>>> fut) { + try { + assert fut.isDone(); + + Map>> assignmentChange = fut.get(); + + GridDhtPartitionsFullMessage m = createPartitionsMessage(false); + + CacheAffinityChangeMessage msg = new CacheAffinityChangeMessage(exchId, m, assignmentChange); + + if (log.isDebugEnabled()) + log.debug("Centralized affinity exchange, send affinity change message: " + msg); + + cctx.discovery().sendCustomEvent(msg); + } + catch (IgniteCheckedException e) { + onDone(e); + } + } + + /** + * @param top Topology to assign. + */ private void assignPartitionStates(GridDhtPartitionTopology top) { Map maxCntrs = new HashMap<>(); Map minCntrs = new HashMap<>(); @@ -1647,8 +2081,10 @@ else if (cntr == maxCntr.cnt) /** * Detect lost partitions. + * + * @param resTopVer Result topology version. */ - private void detectLostPartitions() { + private void detectLostPartitions(AffinityTopologyVersion resTopVer) { boolean detected = false; synchronized (cctx.exchange().interruptLock()) { @@ -1657,7 +2093,7 @@ private void detectLostPartitions() { for (CacheGroupContext grp : cctx.cache().cacheGroups()) { if (!grp.isLocal()) { - boolean detectedOnGrp = grp.topology().detectLostPartitions(discoEvt); + boolean detectedOnGrp = grp.topology().detectLostPartitions(resTopVer, events().lastEvent()); detected |= detectedOnGrp; } @@ -1672,6 +2108,8 @@ private void detectLostPartitions() { * @param cacheNames Cache names. */ private void resetLostPartitions(Collection cacheNames) { + assert !exchCtx.mergeExchanges(); + synchronized (cctx.exchange().interruptLock()) { if (Thread.currentThread().isInterrupted()) return; @@ -1682,7 +2120,7 @@ private void resetLostPartitions(Collection cacheNames) { for (String cacheName : cacheNames) { if (grp.hasCache(cacheName)) { - grp.topology().resetLostPartitions(); + grp.topology().resetLostPartitions(initialVersion()); break; } @@ -1692,49 +2130,122 @@ private void resetLostPartitions(Collection cacheNames) { } /** - * + * @param sndResNodes Additional nodes to send finish message to. */ - private void onAllReceived() { + private void onAllReceived(@Nullable Collection sndResNodes) { try { assert crd.isLocal(); - assert partHistSuppliers.isEmpty(); + assert partHistSuppliers.isEmpty() : partHistSuppliers; - if (!crd.equals(discoCache.serverNodes().get(0))) { + if (!exchCtx.mergeExchanges() && !crd.equals(events().discoveryCache().serverNodes().get(0))) { for (CacheGroupContext grp : cctx.cache().cacheGroups()) { if (!grp.isLocal()) - grp.topology().beforeExchange(this, !centralizedAff); + grp.topology().beforeExchange(this, !centralizedAff, false); } } - for (GridDhtPartitionsAbstractMessage msg : msgs.values()) { - if (msg instanceof GridDhtPartitionsSingleMessage) { - GridDhtPartitionsSingleMessage msg0 = (GridDhtPartitionsSingleMessage)msg; + if (exchCtx.mergeExchanges()) { + log.info("Coordinator received all messages, try merge [ver=" + initialVersion() + ']'); - for (Map.Entry entry : msg0.partitions().entrySet()) { - Integer grpId = entry.getKey(); - CacheGroupContext grp = cctx.cache().cacheGroup(grpId); + boolean finish = cctx.exchange().mergeExchangesOnCoordinator(this); - GridDhtPartitionTopology top = grp != null ? grp.topology() : - cctx.exchange().clientTopology(grpId, this); + if (!finish) + return; + } - Map> cntrs = msg0.partitionUpdateCounters(grpId); + finishExchangeOnCoordinator(sndResNodes); + } + catch (IgniteCheckedException e) { + if (reconnectOnError(e)) + onDone(new IgniteNeedReconnectException(cctx.localNode(), e)); + else + onDone(e); + } + } - if (cntrs != null) - top.applyUpdateCounters(cntrs); + /** + * @param sndResNodes Additional nodes to send finish message to. + */ + private void finishExchangeOnCoordinator(@Nullable Collection sndResNodes) { + try { + AffinityTopologyVersion resTopVer = exchCtx.events().topologyVersion(); + + log.info("finishExchangeOnCoordinator [topVer=" + initialVersion() + + ", resVer=" + resTopVer + ']'); + + Map idealAffDiff = null; + + if (exchCtx.mergeExchanges()) { + synchronized (mux) { + if (mergedJoinExchMsgs != null) { + for (Map.Entry e : mergedJoinExchMsgs.entrySet()) { + msgs.put(e.getKey(), e.getValue()); + + updatePartitionSingleMap(e.getKey(), e.getValue()); + } } } + + assert exchCtx.events().hasServerJoin() || exchCtx.events().hasServerLeft(); + + exchCtx.events().processEvents(this); + + if (exchCtx.events().hasServerLeft()) + idealAffDiff = cctx.affinity().onServerLeftWithExchangeMergeProtocol(this); + else + cctx.affinity().onServerJoinWithExchangeMergeProtocol(this, true); + + for (CacheGroupDescriptor desc : cctx.affinity().cacheGroups().values()) { + if (desc.config().getCacheMode() == CacheMode.LOCAL) + continue; + + CacheGroupContext grp = cctx.cache().cacheGroup(desc.groupId()); + + GridDhtPartitionTopology top = grp != null ? grp.topology() : + cctx.exchange().clientTopology(desc.groupId()); + + top.beforeExchange(this, true, true); + } + } + + Map joinedNodeAff = null; + + for (Map.Entry e : msgs.entrySet()) { + GridDhtPartitionsSingleMessage msg = e.getValue(); + + // Apply update counters after all single messages are received. + for (Map.Entry entry : msg.partitions().entrySet()) { + Integer grpId = entry.getKey(); + + CacheGroupContext grp = cctx.cache().cacheGroup(grpId); + + GridDhtPartitionTopology top = grp != null ? grp.topology() : + cctx.exchange().clientTopology(grpId); + + Map> cntrs = msg.partitionUpdateCounters(grpId); + + if (cntrs != null) + top.applyUpdateCounters(cntrs); + } + + Collection affReq = msg.cacheGroupsAffinityRequest(); + + if (affReq != null) { + joinedNodeAff = CacheGroupAffinityMessage.createAffinityMessages(cctx, + resTopVer, + affReq, + joinedNodeAff); + } } - if (discoEvt.type() == EVT_NODE_JOINED) - assignPartitionsStates(); - else if (discoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT) { - assert discoEvt instanceof DiscoveryCustomEvent; + if (firstDiscoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT) { + assert firstDiscoEvt instanceof DiscoveryCustomEvent; if (activateCluster()) assignPartitionsStates(); - if (((DiscoveryCustomEvent)discoEvt).customMessage() instanceof DynamicCacheChangeBatch) { + if (((DiscoveryCustomEvent)firstDiscoEvt).customMessage() instanceof DynamicCacheChangeBatch) { if (exchActions != null) { Set caches = exchActions.cachesToResetLostPartitions(); @@ -1743,14 +2254,40 @@ else if (discoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT) { } } } - else if (discoEvt.type() == EVT_NODE_LEFT || discoEvt.type() == EVT_NODE_FAILED) - detectLostPartitions(); + else { + if (exchCtx.events().hasServerJoin()) + assignPartitionsStates(); + + if (exchCtx.events().hasServerLeft()) + detectLostPartitions(resTopVer); + } updateLastVersion(cctx.versions().last()); cctx.versions().onExchange(lastVer.get().order()); + GridDhtPartitionsFullMessage msg = createPartitionsMessage(true); + + if (exchCtx.mergeExchanges()) { + assert !centralizedAff; + + msg.resultTopologyVersion(resTopVer); + + if (exchCtx.events().hasServerLeft()) + msg.idealAffinityDiff(idealAffDiff); + } + + msg.prepareMarshal(cctx); + + synchronized (mux) { + finishState = new FinishState(crd.id(), resTopVer, msg); + + state = ExchangeLocalState.DONE; + } + if (centralizedAff) { + assert !exchCtx.mergeExchanges(); + IgniteInternalFuture>>> fut = cctx.affinity().initAffinityOnNodeLeft(this); if (!fut.isDone()) { @@ -1764,12 +2301,32 @@ else if (discoEvt.type() == EVT_NODE_LEFT || discoEvt.type() == EVT_NODE_FAILED) onAffinityInitialized(fut); } else { - List nodes; + Set nodes; - synchronized (this) { + Map mergedJoinExchMsgs0; + + synchronized (mux) { srvNodes.remove(cctx.localNode()); - nodes = new ArrayList<>(srvNodes); + nodes = U.newHashSet(srvNodes.size()); + + nodes.addAll(srvNodes); + + mergedJoinExchMsgs0 = mergedJoinExchMsgs; + + if (mergedJoinExchMsgs != null) { + for (Map.Entry e : mergedJoinExchMsgs.entrySet()) { + if (e.getValue() != null) { + ClusterNode node = cctx.discovery().node(e.getKey()); + + if (node != null) + nodes.add(node); + } + } + } + + if (!F.isEmpty(sndResNodes)) + nodes.addAll(sndResNodes); } IgniteCheckedException err = null; @@ -1791,15 +2348,25 @@ else if (discoEvt.type() == EVT_NODE_LEFT || discoEvt.type() == EVT_NODE_FAILED) boolean active = !stateChangeErr && req.activate(); - ChangeGlobalStateFinishMessage msg = new ChangeGlobalStateFinishMessage(req.requestId(), active); + ChangeGlobalStateFinishMessage stateFinishMsg = new ChangeGlobalStateFinishMessage( + req.requestId(), + active); - cctx.discovery().sendCustomEvent(msg); + cctx.discovery().sendCustomEvent(stateFinishMsg); } if (!nodes.isEmpty()) - sendAllPartitions(nodes); + sendAllPartitions(msg, nodes, mergedJoinExchMsgs0, joinedNodeAff); + + onDone(exchCtx.events().topologyVersion(), err); + + for (Map.Entry e : pendingSingleMsgs.entrySet()) { + log.info("Process pending message on coordinator [node=" + e.getKey() + + ", ver=" + initialVersion() + + ", resVer=" + resTopVer + ']'); - onDone(exchangeId().topologyVersion(), err); + processSingleMessage(e.getKey(), e.getValue()); + } } } catch (IgniteCheckedException e) { @@ -1825,127 +2392,332 @@ private void assignPartitionsStates() { } /** - * @param nodeId Node ID. - * @param retryCnt Number of retries. + * @param finishState State. + * @param msg Request. + * @param nodeId Node ID. + */ + private void sendAllPartitionsToNode(FinishState finishState, GridDhtPartitionsSingleMessage msg, UUID nodeId) { + ClusterNode node = cctx.node(nodeId); + + if (node != null) { + GridDhtPartitionsFullMessage fullMsg = finishState.msg.copy(); + + Collection affReq = msg.cacheGroupsAffinityRequest(); + + if (affReq != null) { + Map aff = CacheGroupAffinityMessage.createAffinityMessages( + cctx, + finishState.resTopVer, + affReq, + null); + + fullMsg.joinedNodeAffinity(aff); + } + + if (!fullMsg.exchangeId().equals(msg.exchangeId())) { + fullMsg = fullMsg.copy(); + + fullMsg.exchangeId(msg.exchangeId()); + } + + try { + cctx.io().send(node, fullMsg, SYSTEM_POOL); + } + catch (ClusterTopologyCheckedException e) { + if (log.isDebugEnabled()) + log.debug("Failed to send partitions, node failed: " + node); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to send partitions [node=" + node + ']', e); + } + } + else if (log.isDebugEnabled()) + log.debug("Failed to send partitions, node failed: " + nodeId); + + } + + /** + * @param node Sender node. + * @param msg Full partition info. + */ + public void onReceiveFullMessage(final ClusterNode node, final GridDhtPartitionsFullMessage msg) { + assert msg != null; + assert msg.exchangeId() != null : msg; + assert !node.isDaemon() : node; + + initFut.listen(new CI1>() { + @Override public void apply(IgniteInternalFuture f) { + try { + if (!f.get()) + return; + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to initialize exchange future: " + this, e); + + return; + } + + processFullMessage(true, node, msg); + } + }); + } + + /** + * @param node Sender node. + * @param msg Message. + */ + public void onReceivePartitionRequest(final ClusterNode node, final GridDhtPartitionsSingleRequest msg) { + assert !cctx.kernalContext().clientNode() || msg.restoreState(); + assert !node.isDaemon() && !CU.clientNode(node) : node; + + initFut.listen(new CI1>() { + @Override public void apply(IgniteInternalFuture fut) { + processSinglePartitionRequest(node, msg); + } + }); + } + + /** + * @param node Sender node. + * @param msg Message. + */ + private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSingleRequest msg) { + FinishState finishState0 = null; + + synchronized (mux) { + if (crd == null) { + log.info("Ignore partitions request, no coordinator [node=" + node.id() + ']'); + + return; + } + + switch (state) { + case DONE: { + assert finishState != null; + + if (node.id().equals(finishState.crdId)) { + log.info("Ignore partitions request, finished exchange with this coordinator: " + msg); + + return; + } + + finishState0 = finishState; + + break; + } + + case CRD: + case BECOME_CRD: { + log.info("Ignore partitions request, node is coordinator: " + msg); + + return; + } + + case CLIENT: + case SRV: { + if (!cctx.discovery().alive(node)) { + log.info("Ignore partitions request, node is not alive [node=" + node.id() + ']'); + + return; + } + + if (msg.restoreState()) { + if (!node.equals(crd)) { + if (node.order() > crd.order()) { + log.info("Received partitions request, change coordinator [oldCrd=" + crd.id() + + ", newCrd=" + node.id() + ']'); + + crd = node; // Do not allow to process FullMessage from old coordinator. + } + else { + log.info("Ignore restore state request, coordinator changed [oldCrd=" + crd.id() + + ", newCrd=" + node.id() + ']'); + + return; + } + } + } + + break; + } + + default: + assert false : state; + } + } + + if (msg.restoreState()) { + try { + assert msg.restoreExchangeId() != null : msg; + + GridDhtPartitionsSingleMessage res = cctx.exchange().createPartitionsSingleMessage( + msg.restoreExchangeId(), + cctx.kernalContext().clientNode(), + true); + + if (localJoinExchange() && finishState0 == null) + res.cacheGroupsAffinityRequest(exchCtx.groupsAffinityRequestOnJoin()); + + res.restoreState(true); + + log.info("Send restore state response [node=" + node.id() + + ", exchVer=" + msg.restoreExchangeId().topologyVersion() + + ", hasState=" + (finishState0 != null) + + ", affReq=" + !F.isEmpty(res.cacheGroupsAffinityRequest()) + ']'); + + res.finishMessage(finishState0 != null ? finishState0.msg : null); + + cctx.io().send(node, res, SYSTEM_POOL); + } + catch (ClusterTopologyCheckedException ignored) { + if (log.isDebugEnabled()) + log.debug("Node left during partition exchange [nodeId=" + node.id() + ", exchId=" + exchId + ']'); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to send partitions message [node=" + node + ", msg=" + msg + ']', e); + } + + return; + } + + try { + sendLocalPartitions(node); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to send message to coordinator: " + e); + } + } + + /** + * @param node Sender node. + * @param msg Message. */ - private void sendAllPartitions(final UUID nodeId, final int retryCnt) { - ClusterNode n = cctx.node(nodeId); - + private void processFullMessage(boolean checkCrd, ClusterNode node, GridDhtPartitionsFullMessage msg) { try { - if (n != null) - sendAllPartitions(F.asList(n)); - } - catch (IgniteCheckedException e) { - if (e instanceof ClusterTopologyCheckedException || !cctx.discovery().alive(n)) { - if (log.isDebugEnabled()) - log.debug("Failed to send full partition map to node, node left grid " + - "[rmtNode=" + nodeId + ", exchangeId=" + exchId + ']'); + assert exchId.equals(msg.exchangeId()) : msg; + assert msg.lastVersion() != null : msg; - return; - } + if (checkCrd) { + assert node != null; - if (reconnectOnError(e)) { - onDone(new IgniteNeedReconnectException(cctx.localNode(), e)); + synchronized (mux) { + if (crd == null) { + log.info("Ignore full message, all server nodes left: " + msg); - return; - } + return; + } - if (retryCnt > 0) { - long timeout = cctx.gridConfig().getNetworkSendRetryDelay(); + switch (state) { + case CRD: + case BECOME_CRD: { + log.info("Ignore full message, node is coordinator: " + msg); - LT.error(log, e, "Failed to send full partition map to node (will retry after timeout) " + - "[node=" + nodeId + ", exchangeId=" + exchId + ", timeout=" + timeout + ']'); + return; + } - cctx.time().addTimeoutObject(new GridTimeoutObjectAdapter(timeout) { - @Override public void onTimeout() { - sendAllPartitions(nodeId, retryCnt - 1); - } - }); - } - else - U.error(log, "Failed to send full partition map [node=" + n + ", exchangeId=" + exchId + ']', e); - } - } + case DONE: { + log.info("Ignore full message, future is done: " + msg); - /** - * @param node Sender node. - * @param msg Full partition info. - */ - public void onReceive(final ClusterNode node, final GridDhtPartitionsFullMessage msg) { - assert msg != null; + return; + } - final UUID nodeId = node.id(); + case SRV: + case CLIENT: { + if (!crd.equals(node)) { + log.info("Received full message from non-coordinator [node=" + node.id() + + ", nodeOrder=" + node.order() + + ", crd=" + crd.id() + + ", crdOrder=" + crd.order() + ']'); - if (isDone()) { - if (log.isDebugEnabled()) - log.debug("Received message for finished future [msg=" + msg + ", fut=" + this + ']'); + if (node.order() > crd.order()) + fullMsgs.put(node, msg); - return; - } + return; + } + else { + AffinityTopologyVersion resVer = msg.resultTopologyVersion() != null ? msg.resultTopologyVersion() : initialVersion(); - if (log.isDebugEnabled()) - log.debug("Received full partition map from node [nodeId=" + nodeId + ", msg=" + msg + ']'); + log.info("Received full message, will finish exchange [node=" + node.id() + + ", resVer=" + resVer + ']'); - initFut.listen(new CI1>() { - @Override public void apply(IgniteInternalFuture f) { - try { - if (!f.get()) - return; - } - catch (IgniteCheckedException e) { - U.error(log, "Failed to initialize exchange future: " + this, e); + finishState = new FinishState(crd.id(), resVer, msg); - return; - } + state = ExchangeLocalState.DONE; - processMessage(node, msg); + break; + } + } + } + } } - }); - } + else + assert node == null : node; - /** - * @param node Sender node. - * @param msg Message. - */ - private void processMessage(ClusterNode node, GridDhtPartitionsFullMessage msg) { - assert exchId.equals(msg.exchangeId()) : msg; - assert msg.lastVersion() != null : msg; + AffinityTopologyVersion resTopVer = initialVersion(); - synchronized (this) { - if (crd == null) - return; + if (exchCtx.mergeExchanges()) { + if (msg.resultTopologyVersion() != null && !initialVersion().equals(msg.resultTopologyVersion())) { + log.info("Received full message, need merge [curFut=" + initialVersion() + + ", resVer=" + msg.resultTopologyVersion() + ']'); - if (!crd.equals(node)) { - if (log.isDebugEnabled()) - log.debug("Received full partition map from unexpected node [oldest=" + crd.id() + - ", nodeId=" + node.id() + ']'); + resTopVer = msg.resultTopologyVersion(); - if (node.order() > crd.order()) - fullMsgs.put(node, msg); + if (cctx.exchange().mergeExchanges(this, msg)) { + assert cctx.kernalContext().isStopping(); - return; + return; // Node is stopping, no need to further process exchange. + } + + assert resTopVer.equals(exchCtx.events().topologyVersion()) : "Unexpected result version [" + + "msgVer=" + resTopVer + + ", locVer=" + exchCtx.events().topologyVersion() + ']'; + } + + exchCtx.events().processEvents(this); + + if (localJoinExchange()) + cctx.affinity().onLocalJoin(this, msg, resTopVer); + else { + if (exchCtx.events().hasServerLeft()) + cctx.affinity().mergeExchangesOnServerLeft(this, msg); + else + cctx.affinity().onServerJoinWithExchangeMergeProtocol(this, false); + + for (CacheGroupContext grp : cctx.cache().cacheGroups()) { + if (grp.isLocal() || cacheGroupStopping(grp.groupId())) + continue; + + grp.topology().beforeExchange(this, true, false); + } + } } - } + else if (localJoinExchange() && !exchCtx.fetchAffinityOnJoin()) + cctx.affinity().onLocalJoin(this, msg, resTopVer); - updatePartitionFullMap(msg); + updatePartitionFullMap(resTopVer, msg); - IgniteCheckedException err = null; + IgniteCheckedException err = null; - if (stateChangeExchange() && !F.isEmpty(msg.getErrorsMap())) { - err = new IgniteCheckedException("Cluster state change failed"); + if (stateChangeExchange() && !F.isEmpty(msg.getErrorsMap())) { + err = new IgniteCheckedException("Cluster state change failed"); - cctx.kernalContext().state().onStateChangeError(msg.getErrorsMap(), exchActions.stateChangeRequest()); - } + cctx.kernalContext().state().onStateChangeError(msg.getErrorsMap(), exchActions.stateChangeRequest()); + } - onDone(exchId.topologyVersion(), err); + onDone(resTopVer, err); + } + catch (IgniteCheckedException e) { + onDone(e); + } } /** * Updates partition map in all caches. * + * @param resTopVer Result topology version. * @param msg Partitions full messages. */ - private void updatePartitionFullMap(GridDhtPartitionsFullMessage msg) { + private void updatePartitionFullMap(AffinityTopologyVersion resTopVer, GridDhtPartitionsFullMessage msg) { cctx.versions().onExchange(msg.lastVersion().order()); assert partHistSuppliers.isEmpty(); @@ -1960,7 +2732,7 @@ private void updatePartitionFullMap(GridDhtPartitionsFullMessage msg) { CacheGroupContext grp = cctx.cache().cacheGroup(grpId); if (grp != null) { - grp.topology().update(topologyVersion(), + grp.topology().update(resTopVer, entry.getValue(), cntrMap, msg.partsToReload(cctx.localNodeId(), grpId)); @@ -1969,7 +2741,7 @@ private void updatePartitionFullMap(GridDhtPartitionsFullMessage msg) { ClusterNode oldest = cctx.discovery().oldestAliveCacheServerNode(AffinityTopologyVersion.NONE); if (oldest != null && oldest.isLocal()) { - cctx.exchange().clientTopology(grpId, this).update(topologyVersion(), + cctx.exchange().clientTopology(grpId).update(resTopVer, entry.getValue(), cntrMap, Collections.emptySet()); @@ -1981,19 +2753,20 @@ private void updatePartitionFullMap(GridDhtPartitionsFullMessage msg) { /** * Updates partition map in all caches. * + * @param nodeId Node message received from. * @param msg Partitions single message. */ - private void updatePartitionSingleMap(ClusterNode node, GridDhtPartitionsSingleMessage msg) { - msgs.put(node.id(), msg); + private void updatePartitionSingleMap(UUID nodeId, GridDhtPartitionsSingleMessage msg) { + msgs.put(nodeId, msg); for (Map.Entry entry : msg.partitions().entrySet()) { Integer grpId = entry.getKey(); CacheGroupContext grp = cctx.cache().cacheGroup(grpId); GridDhtPartitionTopology top = grp != null ? grp.topology() : - cctx.exchange().clientTopology(grpId, this); + cctx.exchange().clientTopology(grpId); - top.update(exchId, entry.getValue()); + top.update(exchId, entry.getValue(), false); } } @@ -2015,6 +2788,8 @@ public void onAffinityChangeMessage(final ClusterNode node, final CacheAffinityC assert centralizedAff; if (crd.equals(node)) { + AffinityTopologyVersion resTopVer = initialVersion(); + cctx.affinity().onExchangeChangeAffinityMessage(GridDhtPartitionsExchangeFuture.this, crd.isLocal(), msg); @@ -2025,10 +2800,10 @@ public void onAffinityChangeMessage(final ClusterNode node, final CacheAffinityC assert partsMsg != null : msg; assert partsMsg.lastVersion() != null : partsMsg; - updatePartitionFullMap(partsMsg); + updatePartitionFullMap(resTopVer, partsMsg); } - onDone(topologyVersion()); + onDone(resTopVer); } else { if (log.isDebugEnabled()) { @@ -2089,6 +2864,26 @@ private void initDone() { initFut.onDone(true); } + /** + * + */ + private void onAllServersLeft() { + assert cctx.kernalContext().clientNode() : cctx.localNode(); + + List empty = Collections.emptyList(); + + for (CacheGroupContext grp : cctx.cache().cacheGroups()) { + List> affAssignment = new ArrayList<>(grp.affinity().partitions()); + + for (int i = 0; i < grp.affinity().partitions(); i++) + affAssignment.add(empty); + + grp.affinity().idealAssignment(affAssignment); + + grp.affinity().initialize(initialVersion(), affAssignment); + } + } + /** * Node left callback, processed from the same thread as {@link #onAffinityChangeMessage}. * @@ -2098,7 +2893,7 @@ public void onNodeLeft(final ClusterNode node) { if (isDone() || !enterBusy()) return; - cctx.mvcc().removeExplicitNodeLocks(node.id(), topologyVersion()); + cctx.mvcc().removeExplicitNodeLocks(node.id(), initialVersion()); try { onDiscoveryEvent(new IgniteRunnable() { @@ -2109,50 +2904,74 @@ public void onNodeLeft(final ClusterNode node) { try { boolean crdChanged = false; boolean allReceived = false; - Set reqFrom = null; ClusterNode crd0; - discoCache.updateAlives(node); + events().discoveryCache().updateAlives(node); + + InitNewCoordinatorFuture newCrdFut0; - synchronized (this) { + synchronized (mux) { + newCrdFut0 = newCrdFut; + } + + if (newCrdFut0 != null) + newCrdFut0.onNodeLeft(node.id()); + + synchronized (mux) { if (!srvNodes.remove(node)) return; boolean rmvd = remaining.remove(node.id()); + if (!rmvd) { + if (mergedJoinExchMsgs != null && mergedJoinExchMsgs.containsKey(node.id())) { + if (mergedJoinExchMsgs.get(node.id()) == null) { + mergedJoinExchMsgs.remove(node.id()); + + rmvd = true; + } + } + } + if (node.equals(crd)) { crdChanged = true; crd = !srvNodes.isEmpty() ? srvNodes.get(0) : null; } - if (crd != null && crd.isLocal()) { - if (rmvd) - allReceived = remaining.isEmpty(); + switch (state) { + case DONE: + return; - if (crdChanged && !remaining.isEmpty()) - reqFrom = new HashSet<>(remaining); - } + case CRD: + allReceived = rmvd && (remaining.isEmpty() && F.isEmpty(mergedJoinExchMsgs)); - crd0 = crd; - } + break; - if (crd0 == null) { - assert cctx.kernalContext().clientNode() || cctx.localNode().isDaemon() : cctx.localNode(); + case SRV: + assert crd != null; - List empty = Collections.emptyList(); + if (crdChanged && crd.isLocal()) { + state = ExchangeLocalState.BECOME_CRD; - for (CacheGroupContext grp : cctx.cache().cacheGroups()) { - List> affAssignment = new ArrayList<>(grp.affinity().partitions()); + newCrdFut = new InitNewCoordinatorFuture(cctx); + } + + break; + } - for (int i = 0; i < grp.affinity().partitions(); i++) - affAssignment.add(empty); + crd0 = crd; - grp.affinity().initialize(topologyVersion(), affAssignment); + if (crd0 == null) { + finishState = new FinishState(null, initialVersion(), null); } + } + + if (crd0 == null) { + onAllServersLeft(); - onDone(topologyVersion()); + onDone(initialVersion()); return; } @@ -2161,50 +2980,78 @@ public void onNodeLeft(final ClusterNode node) { if (stateChangeExchange() && changeGlobalStateE != null) changeGlobalStateExceptions.put(crd0.id(), changeGlobalStateE); - if (allReceived) { - awaitSingleMapUpdates(); + if (crdChanged) { + log.info("Coordinator failed, node is new coordinator [ver=" + initialVersion() + + ", prev=" + node.id() + ']'); - onAllReceived(); + assert newCrdFut != null; - return; - } + cctx.kernalContext().closure().callLocal(new Callable() { + @Override public Void call() throws Exception { + newCrdFut.init(GridDhtPartitionsExchangeFuture.this); - if (crdChanged && reqFrom != null) { - GridDhtPartitionsSingleRequest req = new GridDhtPartitionsSingleRequest(exchId); + newCrdFut.listen(new CI1() { + @Override public void apply(IgniteInternalFuture fut) { + if (isDone()) + return; - for (UUID nodeId : reqFrom) { - try { - // It is possible that some nodes finished exchange with previous coordinator. - cctx.io().send(nodeId, req, SYSTEM_POOL); - } - catch (ClusterTopologyCheckedException ignored) { - if (log.isDebugEnabled()) - log.debug("Node left during partition exchange [nodeId=" + nodeId + - ", exchId=" + exchId + ']'); - } - catch (IgniteCheckedException e) { - U.error(log, "Failed to request partitions from node: " + nodeId, e); + Lock lock = cctx.io().readLock(); + + if (lock == null) + return; + + try { + onBecomeCoordinator((InitNewCoordinatorFuture) fut); + } + finally { + lock.unlock(); + } + } + }); + + return null; } - } + }, GridIoPolicy.SYSTEM_POOL); + + return; } - for (Map.Entry m : singleMsgs.entrySet()) - processMessage(m.getKey(), m.getValue()); + if (allReceived) { + awaitSingleMapUpdates(); + + onAllReceived(null); + } } else { if (crdChanged) { - sendPartitions(crd0); + for (Map.Entry m : fullMsgs.entrySet()) { + if (crd0.equals(m.getKey())) { + log.info("Coordinator changed, process pending full message [" + + "ver=" + initialVersion() + + ", crd=" + node.id() + + ", pendingMsgNode=" + m.getKey() + ']'); + + processFullMessage(true, m.getKey(), m.getValue()); + + if (isDone()) + return; + } + } + + log.info("Coordinator changed, send partitions to new coordinator [" + + "ver=" + initialVersion() + + ", crd=" + node.id() + + ", newCrd=" + crd0.id() + ']'); - for (Map.Entry m : fullMsgs.entrySet()) - processMessage(m.getKey(), m.getValue()); + sendPartitions(crd0); } } } - catch (Exception e) { + catch (IgniteCheckedException e) { if (reconnectOnError(e)) onDone(new IgniteNeedReconnectException(cctx.localNode(), e)); else - throw e; + U.error(log, "Failed to process node left event: " + e, e); } finally { leaveBusy(); @@ -2217,6 +3064,140 @@ public void onNodeLeft(final ClusterNode node) { } } + /** + * @param newCrdFut Coordinator initialization future. + */ + private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { + boolean allRcvd = false; + + cctx.exchange().onCoordinatorInitialized(); + + if (newCrdFut.restoreState()) { + GridDhtPartitionsFullMessage fullMsg = newCrdFut.fullMessage(); + + assert msgs.isEmpty() : msgs; + + if (fullMsg != null) { + log.info("New coordinator restored state [ver=" + initialVersion() + + ", resVer=" + fullMsg.resultTopologyVersion() + ']'); + + synchronized (mux) { + state = ExchangeLocalState.DONE; + + finishState = new FinishState(crd.id(), fullMsg.resultTopologyVersion(), fullMsg); + } + + fullMsg.exchangeId(exchId); + + processFullMessage(false, null, fullMsg); + + Map msgs = newCrdFut.messages(); + + if (!F.isEmpty(msgs)) { + Map joinedNodeAff = null; + + for (Map.Entry e : msgs.entrySet()) { + this.msgs.put(e.getKey().id(), e.getValue()); + + GridDhtPartitionsSingleMessage msg = e.getValue(); + + Collection affReq = msg.cacheGroupsAffinityRequest(); + + if (!F.isEmpty(affReq)) { + joinedNodeAff = CacheGroupAffinityMessage.createAffinityMessages(cctx, + fullMsg.resultTopologyVersion(), + affReq, + joinedNodeAff); + } + } + + sendAllPartitions(fullMsg, msgs.keySet(), null, joinedNodeAff); + } + + return; + } + else { + log.info("New coordinator restore state finished [ver=" + initialVersion() + ']'); + + for (Map.Entry e : newCrdFut.messages().entrySet()) { + GridDhtPartitionsSingleMessage msg = e.getValue(); + + if (!msg.client()) { + msgs.put(e.getKey().id(), e.getValue()); + + updatePartitionSingleMap(e.getKey().id(), msg); + } + } + } + + allRcvd = true; + + synchronized (mux) { + remaining.clear(); // Do not process messages. + + assert crd != null && crd.isLocal(); + + state = ExchangeLocalState.CRD; + + assert mergedJoinExchMsgs == null; + } + } + else { + Set remaining0 = null; + + synchronized (mux) { + assert crd != null && crd.isLocal(); + + state = ExchangeLocalState.CRD; + + assert mergedJoinExchMsgs == null; + + log.info("New coordinator initialization finished [ver=" + initialVersion() + + ", remaining=" + remaining + ']'); + + if (!remaining.isEmpty()) + remaining0 = new HashSet<>(remaining); + } + + if (remaining0 != null) { + // It is possible that some nodes finished exchange with previous coordinator. + GridDhtPartitionsSingleRequest req = new GridDhtPartitionsSingleRequest(exchId); + + for (UUID nodeId : remaining0) { + try { + if (!pendingSingleMsgs.containsKey(nodeId)) { + log.info("New coordinator sends request [ver=" + initialVersion() + + ", node=" + nodeId + ']'); + + cctx.io().send(nodeId, req, SYSTEM_POOL); + } + } + catch (ClusterTopologyCheckedException ignored) { + if (log.isDebugEnabled()) + log.debug("Node left during partition exchange [nodeId=" + nodeId + + ", exchId=" + exchId + ']'); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to request partitions from node: " + nodeId, e); + } + } + + for (Map.Entry m : pendingSingleMsgs.entrySet()) { + log.info("New coordinator process pending message [ver=" + initialVersion() + + ", node=" + m.getKey() + ']'); + + processSingleMessage(m.getKey(), m.getValue()); + } + } + } + + if (allRcvd) { + awaitSingleMapUpdates(); + + onAllReceived(newCrdFut.messages().keySet()); + } + } + /** * @param e Exception. * @return {@code True} if local node should try reconnect in case of error. @@ -2236,6 +3217,9 @@ public boolean reconnectOnError(Throwable e) { if (this == o) return true; + if (o == null || o.getClass() != getClass()) + return false; + GridDhtPartitionsExchangeFuture fut = (GridDhtPartitionsExchangeFuture)o; return exchId.equals(fut.exchId); @@ -2246,39 +3230,27 @@ public boolean reconnectOnError(Throwable e) { return exchId.hashCode(); } - /** - * - */ - enum ExchangeType { - /** */ - CLIENT, - /** */ - ALL, - /** */ - NONE - } - /** {@inheritDoc} */ @Override public void addDiagnosticRequest(IgniteDiagnosticPrepareContext diagCtx) { if (!isDone()) { ClusterNode crd; Set remaining; - synchronized (this) { + synchronized (mux) { crd = this.crd; remaining = new HashSet<>(this.remaining); } if (crd != null) { if (!crd.isLocal()) { - diagCtx.exchangeInfo(crd.id(), topologyVersion(), "Exchange future waiting for coordinator " + - "response [crd=" + crd.id() + ", topVer=" + topologyVersion() + ']'); + diagCtx.exchangeInfo(crd.id(), initialVersion(), "Exchange future waiting for coordinator " + + "response [crd=" + crd.id() + ", topVer=" + initialVersion() + ']'); } else if (!remaining.isEmpty()){ UUID nodeId = remaining.iterator().next(); - diagCtx.exchangeInfo(nodeId, topologyVersion(), "Exchange future on coordinator waiting for " + - "server response [node=" + nodeId + ", topVer=" + topologyVersion() + ']'); + diagCtx.exchangeInfo(nodeId, initialVersion(), "Exchange future on coordinator waiting for " + + "server response [node=" + nodeId + ", topVer=" + initialVersion() + ']'); } } } @@ -2288,9 +3260,9 @@ else if (!remaining.isEmpty()){ * @return Short information string. */ public String shortInfo() { - return "GridDhtPartitionsExchangeFuture [topVer=" + topologyVersion() + - ", evt=" + (discoEvt != null ? discoEvt.type() : -1) + - ", evtNode=" + (discoEvt != null ? discoEvt.eventNode() : null) + + return "GridDhtPartitionsExchangeFuture [topVer=" + initialVersion() + + ", evt=" + (firstDiscoEvt != null ? IgniteUtils.gridEventName(firstDiscoEvt.type()) : -1) + + ", evtNode=" + (firstDiscoEvt != null ? firstDiscoEvt.eventNode() : null) + ", done=" + isDone() + ']'; } @@ -2298,7 +3270,7 @@ public String shortInfo() { @Override public String toString() { Set remaining; - synchronized (this) { + synchronized (mux) { remaining = new HashSet<>(this.remaining); } @@ -2356,4 +3328,68 @@ public static long nextDumpTimeout(int step, long timeout) { return nextTimeout <= limit ? nextTimeout : limit; } + + /** + * + */ + private static class FinishState { + /** */ + private final UUID crdId; + + /** */ + private final AffinityTopologyVersion resTopVer; + + /** */ + private final GridDhtPartitionsFullMessage msg; + + /** + * @param crdId Coordinator node. + * @param resTopVer Result version. + * @param msg Result message. + */ + FinishState(UUID crdId, AffinityTopologyVersion resTopVer, GridDhtPartitionsFullMessage msg) { + this.crdId = crdId; + this.resTopVer = resTopVer; + this.msg = msg; + } + } + + /** + * + */ + enum ExchangeType { + /** */ + CLIENT, + + /** */ + ALL, + + /** */ + NONE + } + /** + * + */ + private enum ExchangeLocalState { + /** Local node is coordinator. */ + CRD, + + /** Local node is non-coordinator server. */ + SRV, + + /** Local node is client node. */ + CLIENT, + + /** + * Previous coordinator failed before echange finished and + * local performs initialization to become new coordinator. + */ + BECOME_CRD, + + /** Exchange finished. */ + DONE, + + /** This exchange was merged with another one. */ + MERGED + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java index 75609b8ec49d0..3e348e3eaf2e8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java @@ -32,6 +32,7 @@ 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.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; @@ -99,6 +100,17 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa @GridDirectTransient private transient boolean compress; + /** */ + private AffinityTopologyVersion resTopVer; + + /** */ + @GridDirectMap(keyType = Integer.class, valueType = CacheGroupAffinityMessage.class) + private Map joinedNodeAff; + + /** */ + @GridDirectMap(keyType = Integer.class, valueType = CacheGroupAffinityMessage.class) + private Map idealAffDiff; + /** * Required by {@link Externalizable}. */ @@ -125,6 +137,83 @@ public GridDhtPartitionsFullMessage(@Nullable GridDhtPartitionExchangeId id, this.partsToReload = partsToReload; } + /** {@inheritDoc} */ + @Override void copyStateTo(GridDhtPartitionsAbstractMessage msg) { + super.copyStateTo(msg); + + GridDhtPartitionsFullMessage cp = (GridDhtPartitionsFullMessage)msg; + + cp.parts = parts; + cp.dupPartsData = dupPartsData; + cp.partsBytes = partsBytes; + cp.partCntrs = partCntrs; + cp.partCntrsBytes = partCntrsBytes; + cp.partHistSuppliers = partHistSuppliers; + cp.partHistSuppliersBytes = partHistSuppliersBytes; + cp.partsToReload = partsToReload; + cp.partsToReloadBytes = partsToReloadBytes; + cp.topVer = topVer; + cp.errs = errs; + cp.errsBytes = errsBytes; + cp.compress = compress; + cp.resTopVer = resTopVer; + cp.joinedNodeAff = joinedNodeAff; + cp.idealAffDiff = idealAffDiff; + } + + /** + * @return Message copy. + */ + GridDhtPartitionsFullMessage copy() { + GridDhtPartitionsFullMessage cp = new GridDhtPartitionsFullMessage(); + + copyStateTo(cp); + + return cp; + } + + /** + * @param resTopVer Result topology version. + */ + public void resultTopologyVersion(AffinityTopologyVersion resTopVer) { + this.resTopVer = resTopVer; + } + + /** + * @return Result topology version. + */ + public AffinityTopologyVersion resultTopologyVersion() { + return resTopVer; + } + + /** + * @return Caches affinity for joining nodes. + */ + @Nullable public Map joinedNodeAffinity() { + return joinedNodeAff; + } + + /** + * @param joinedNodeAff Caches affinity for joining nodes. + */ + void joinedNodeAffinity(Map joinedNodeAff) { + this.joinedNodeAff = joinedNodeAff; + } + + /** + * @return Difference with ideal affinity. + */ + @Nullable public Map idealAffinityDiff() { + return idealAffDiff; + } + + /** + * @param idealAffDiff Difference with ideal affinity. + */ + void idealAffinityDiff(Map idealAffDiff) { + this.idealAffDiff = idealAffDiff; + } + /** {@inheritDoc} */ @Override public int handlerId() { return 0; @@ -241,11 +330,11 @@ void setErrorsMap(Map errs) { @Override public void prepareMarshal(GridCacheSharedContext ctx) throws IgniteCheckedException { super.prepareMarshal(ctx); - boolean marshal = (parts != null && partsBytes == null) || - (partCntrs != null && partCntrsBytes == null) || + boolean marshal = (!F.isEmpty(parts) && partsBytes == null) || + (partCntrs != null && !partCntrs.empty() && partCntrsBytes == null) || (partHistSuppliers != null && partHistSuppliersBytes == null) || (partsToReload != null && partsToReloadBytes == null) || - (errs != null && errsBytes == null); + (!F.isEmpty(errs) && errsBytes == null); if (marshal) { byte[] partsBytes0 = null; @@ -254,10 +343,10 @@ void setErrorsMap(Map errs) { byte[] partsToReloadBytes0 = null; byte[] errsBytes0 = null; - if (parts != null && partsBytes == null) + if (!F.isEmpty(parts) && partsBytes == null) partsBytes0 = U.marshal(ctx, parts); - if (partCntrs != null && partCntrsBytes == null) + if (partCntrs != null && !partCntrs.empty() && partCntrsBytes == null) partCntrsBytes0 = U.marshal(ctx, partCntrs); if (partHistSuppliers != null && partHistSuppliersBytes == null) @@ -266,7 +355,7 @@ void setErrorsMap(Map errs) { if (partsToReload != null && partsToReloadBytes == null) partsToReloadBytes0 = U.marshal(ctx, partsToReload); - if (errs != null && errsBytes == null) + if (!F.isEmpty(errs) && errsBytes == null) errsBytes0 = U.marshal(ctx, errs); if (compress) { @@ -418,30 +507,48 @@ public void topologyVersion(AffinityTopologyVersion topVer) { writer.incrementState(); case 7: - if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes)) + if (!writer.writeMap("idealAffDiff", idealAffDiff, MessageCollectionItemType.INT, MessageCollectionItemType.MSG)) return false; writer.incrementState(); case 8: - if (!writer.writeByteArray("partHistSuppliersBytes", partHistSuppliersBytes)) + if (!writer.writeMap("joinedNodeAff", joinedNodeAff, MessageCollectionItemType.INT, MessageCollectionItemType.MSG)) return false; writer.incrementState(); case 9: - if (!writer.writeByteArray("partsBytes", partsBytes)) + if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes)) return false; writer.incrementState(); case 10: - if (!writer.writeByteArray("partsToReloadBytes", partsToReloadBytes)) + if (!writer.writeByteArray("partHistSuppliersBytes", partHistSuppliersBytes)) return false; writer.incrementState(); case 11: + if (!writer.writeByteArray("partsBytes", partsBytes)) + return false; + + writer.incrementState(); + + case 12: + if (!writer.writeByteArray("partsToReloadBytes", partsToReloadBytes)) + return false; + + writer.incrementState(); + + case 13: + if (!writer.writeMessage("resTopVer", resTopVer)) + return false; + + writer.incrementState(); + + case 14: if (!writer.writeMessage("topVer", topVer)) return false; @@ -480,7 +587,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 7: - partCntrsBytes = reader.readByteArray("partCntrsBytes"); + idealAffDiff = reader.readMap("idealAffDiff", MessageCollectionItemType.INT, MessageCollectionItemType.MSG, false); if (!reader.isLastRead()) return false; @@ -488,7 +595,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 8: - partHistSuppliersBytes = reader.readByteArray("partHistSuppliersBytes"); + joinedNodeAff = reader.readMap("joinedNodeAff", MessageCollectionItemType.INT, MessageCollectionItemType.MSG, false); if (!reader.isLastRead()) return false; @@ -496,7 +603,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 9: - partsBytes = reader.readByteArray("partsBytes"); + partCntrsBytes = reader.readByteArray("partCntrsBytes"); if (!reader.isLastRead()) return false; @@ -504,7 +611,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 10: - partsToReloadBytes = reader.readByteArray("partsToReloadBytes"); + partHistSuppliersBytes = reader.readByteArray("partHistSuppliersBytes"); if (!reader.isLastRead()) return false; @@ -512,6 +619,30 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 11: + partsBytes = reader.readByteArray("partsBytes"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 12: + partsToReloadBytes = reader.readByteArray("partsToReloadBytes"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 13: + resTopVer = reader.readMessage("resTopVer"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 14: topVer = reader.readMessage("topVer"); if (!reader.isLastRead()) @@ -531,7 +662,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { /** {@inheritDoc} */ @Override public byte fieldsCount() { - return 12; + return 15; } /** {@inheritDoc} */ 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 b4d25c41f4780..bc7d3146cea4e 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 @@ -17,12 +17,14 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.preloader; +import java.util.Collection; import java.util.Map; import java.util.HashMap; import java.nio.ByteBuffer; import java.util.Collections; import java.io.Externalizable; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.GridDirectCollection; import org.apache.ignite.internal.GridDirectMap; import org.apache.ignite.internal.GridDirectTransient; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; @@ -88,6 +90,16 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes @GridDirectTransient private transient boolean compress; + /** */ + @GridDirectCollection(Integer.class) + private Collection grpsAffRequest; + + /** + * Exchange finish message, sent to new coordinator when it tries to + * restore state after previous coordinator failed during exchange. + */ + private GridDhtPartitionsFullMessage finishMsg; + /** * Required by {@link Externalizable}. */ @@ -111,6 +123,34 @@ public GridDhtPartitionsSingleMessage(GridDhtPartitionExchangeId exchId, this.compress = compress; } + /** + * @param finishMsg Exchange finish message (used to restore exchange state on new coordinator). + */ + void finishMessage(GridDhtPartitionsFullMessage finishMsg) { + this.finishMsg = finishMsg; + } + + /** + * @return Exchange finish message (used to restore exchange state on new coordinator). + */ + GridDhtPartitionsFullMessage finishMessage() { + return finishMsg; + } + + /** + * @param grpsAffRequest Cache groups to get affinity for (affinity is requested when node joins cluster). + */ + void cacheGroupsAffinityRequest(Collection grpsAffRequest) { + this.grpsAffRequest = grpsAffRequest; + } + + /** + * @return Cache groups to get affinity for (affinity is requested when node joins cluster). + */ + @Nullable public Collection cacheGroupsAffinityRequest() { + return grpsAffRequest; + } + /** {@inheritDoc} */ @Override public int handlerId() { return 0; @@ -374,18 +414,30 @@ public void setError(Exception ex) { writer.incrementState(); case 8: - if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes)) + if (!writer.writeMessage("finishMsg", finishMsg)) return false; writer.incrementState(); case 9: - if (!writer.writeByteArray("partHistCntrsBytes", partHistCntrsBytes)) + if (!writer.writeCollection("grpsAffRequest", grpsAffRequest, MessageCollectionItemType.INT)) return false; writer.incrementState(); case 10: + if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes)) + return false; + + writer.incrementState(); + + case 11: + if (!writer.writeByteArray("partHistCntrsBytes", partHistCntrsBytes)) + return false; + + writer.incrementState(); + + case 12: if (!writer.writeByteArray("partsBytes", partsBytes)) return false; @@ -432,7 +484,7 @@ public void setError(Exception ex) { reader.incrementState(); case 8: - partCntrsBytes = reader.readByteArray("partCntrsBytes"); + finishMsg = reader.readMessage("finishMsg"); if (!reader.isLastRead()) return false; @@ -440,7 +492,7 @@ public void setError(Exception ex) { reader.incrementState(); case 9: - partHistCntrsBytes = reader.readByteArray("partHistCntrsBytes"); + grpsAffRequest = reader.readCollection("grpsAffRequest", MessageCollectionItemType.INT); if (!reader.isLastRead()) return false; @@ -448,6 +500,22 @@ public void setError(Exception ex) { reader.incrementState(); case 10: + partCntrsBytes = reader.readByteArray("partCntrsBytes"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 11: + partHistCntrsBytes = reader.readByteArray("partHistCntrsBytes"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 12: partsBytes = reader.readByteArray("partsBytes"); if (!reader.isLastRead()) @@ -467,7 +535,7 @@ public void setError(Exception ex) { /** {@inheritDoc} */ @Override public byte fieldsCount() { - return 11; + return 13; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java index 4b80ee05d00cc..6317fbc4c5192 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java @@ -33,6 +33,9 @@ public class GridDhtPartitionsSingleRequest extends GridDhtPartitionsAbstractMes /** */ private static final long serialVersionUID = 0L; + /** */ + private GridDhtPartitionExchangeId restoreExchId; + /** * Required by {@link Externalizable}. */ @@ -47,6 +50,28 @@ public GridDhtPartitionsSingleRequest() { super(id, null); } + /** + * @param msgExchId Exchange ID for message. + * @param restoreExchId Initial exchange ID for current exchange. + * @return Message. + */ + static GridDhtPartitionsSingleRequest restoreStateRequest(GridDhtPartitionExchangeId msgExchId, GridDhtPartitionExchangeId restoreExchId) { + GridDhtPartitionsSingleRequest msg = new GridDhtPartitionsSingleRequest(msgExchId); + + msg.restoreState(true); + + msg.restoreExchId = restoreExchId; + + return msg; + } + + /** + * @return ID of current exchange on new coordinator. + */ + GridDhtPartitionExchangeId restoreExchangeId() { + return restoreExchId; + } + /** {@inheritDoc} */ @Override public int handlerId() { return 0; @@ -71,6 +96,15 @@ public GridDhtPartitionsSingleRequest() { writer.onHeaderWritten(); } + switch (writer.state()) { + case 5: + if (!writer.writeMessage("restoreExchId", restoreExchId)) + return false; + + writer.incrementState(); + + } + return true; } @@ -84,6 +118,17 @@ public GridDhtPartitionsSingleRequest() { if (!super.readFrom(buf, reader)) return false; + switch (reader.state()) { + case 5: + restoreExchId = reader.readMessage("restoreExchId"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + } + return reader.afterMessageRead(GridDhtPartitionsSingleRequest.class); } @@ -94,7 +139,7 @@ public GridDhtPartitionsSingleRequest() { /** {@inheritDoc} */ @Override public byte fieldsCount() { - return 5; + return 6; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java index 305da92e86b7b..c8d104111b383 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java @@ -29,7 +29,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cluster.ClusterNode; -import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.NodeStoppingException; @@ -173,29 +172,31 @@ private IgniteCheckedException stopError() { /** {@inheritDoc} */ @Override public void onTopologyChanged(GridDhtPartitionsExchangeFuture lastFut) { - supplier.onTopologyChanged(lastFut.topologyVersion()); + supplier.onTopologyChanged(lastFut.initialVersion()); demander.onTopologyChanged(lastFut); } /** {@inheritDoc} */ @Override public GridDhtPreloaderAssignments assign(GridDhtPartitionExchangeId exchId, GridDhtPartitionsExchangeFuture exchFut) { + assert exchFut == null || exchFut.isDone(); + // No assignments for disabled preloader. GridDhtPartitionTopology top = grp.topology(); if (!grp.rebalanceEnabled()) - return new GridDhtPreloaderAssignments(exchId, top.topologyVersion()); + return new GridDhtPreloaderAssignments(exchId, top.readyTopologyVersion()); int partCnt = grp.affinity().partitions(); - assert exchFut == null || exchFut.topologyVersion().equals(top.topologyVersion()) : - "Topology version mismatch [exchId=" + exchId + - ", grp=" + grp.name() + - ", topVer=" + top.topologyVersion() + ']'; + AffinityTopologyVersion topVer = top.readyTopologyVersion(); - GridDhtPreloaderAssignments assigns = new GridDhtPreloaderAssignments(exchId, top.topologyVersion()); + assert exchFut == null || exchFut.context().events().topologyVersion().equals(top.readyTopologyVersion()) : + "Topology version mismatch [exchId=" + exchId + + ", grp=" + grp.name() + + ", topVer=" + top.readyTopologyVersion() + ']'; - AffinityTopologyVersion topVer = assigns.topologyVersion(); + GridDhtPreloaderAssignments assigns = new GridDhtPreloaderAssignments(exchId, topVer); AffinityAssignment aff = grp.affinity().cachedAffinity(topVer); @@ -242,7 +243,7 @@ private IgniteCheckedException stopError() { if (msg == null) { assigns.put(histSupplier, msg = new GridDhtPartitionDemandMessage( top.updateSequence(), - exchId.topologyVersion(), + assigns.topologyVersion(), grp.groupId())); } @@ -306,12 +307,12 @@ private IgniteCheckedException stopError() { GridDhtPartitionDemandMessage msg = assigns.get(n); - if (msg == null) { - assigns.put(n, msg = new GridDhtPartitionDemandMessage( - top.updateSequence(), - exchId.topologyVersion(), - grp.groupId())); - } + if (msg == null) { + assigns.put(n, msg = new GridDhtPartitionDemandMessage( + top.updateSequence(), + assigns.topologyVersion(), + grp.groupId())); + } msg.addPartition(p, false); } @@ -612,8 +613,15 @@ private GridDhtFuture request0(GridCacheContext cctx, Collection>> map; + /** + * @return {@code True} if map is empty. + */ + public synchronized boolean empty() { + return map == null || map.isEmpty(); + } + /** * @param cacheId Cache ID. * @param cntrMap Counters map. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java new file mode 100644 index 0000000000000..42ce9b988a22b --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java @@ -0,0 +1,307 @@ +/* + * 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.preloader; + +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.Set; +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.IgniteInternalFuture; +import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; +import org.apache.ignite.internal.managers.discovery.DiscoCache; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; +import org.apache.ignite.internal.util.future.GridCompoundFuture; +import org.apache.ignite.internal.util.future.GridFutureAdapter; +import org.apache.ignite.internal.util.typedef.F; +import org.jetbrains.annotations.Nullable; + +import static org.apache.ignite.events.EventType.EVT_NODE_JOINED; +import static org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager.*; + +/** + * + */ +public class InitNewCoordinatorFuture extends GridCompoundFuture { + /** */ + private GridDhtPartitionsFullMessage fullMsg; + + /** */ + private Set awaited = new HashSet<>(); + + /** */ + private Map msgs = new HashMap<>(); + + /** */ + private Map joinExchMsgs; + + /** */ + private GridFutureAdapter restoreStateFut; + + /** */ + private final IgniteLogger log; + + /** */ + private AffinityTopologyVersion initTopVer; + + /** */ + private Map joinedNodes; + + /** */ + private boolean restoreState; + + /** + * @param cctx Context. + */ + InitNewCoordinatorFuture(GridCacheSharedContext cctx) { + this.log = cctx.logger(getClass()); + } + + /** + * @param exchFut Current future. + * @throws IgniteCheckedException If failed. + */ + public void init(GridDhtPartitionsExchangeFuture exchFut) throws IgniteCheckedException { + initTopVer = exchFut.initialVersion(); + + GridCacheSharedContext cctx = exchFut.sharedContext(); + + restoreState = exchangeProtocolVersion(exchFut.context().events().discoveryCache().minimumNodeVersion()) > 1; + + boolean newAff = exchFut.localJoinExchange(); + + IgniteInternalFuture fut = cctx.affinity().initCoordinatorCaches(exchFut, newAff); + + if (fut != null) + add(fut); + + if (restoreState) { + DiscoCache curDiscoCache = cctx.discovery().discoCache(); + + DiscoCache discoCache = exchFut.events().discoveryCache(); + + List nodes = new ArrayList<>(); + + synchronized (this) { + for (ClusterNode node : discoCache.allNodes()) { + if (!node.isLocal() && cctx.discovery().alive(node)) { + awaited.add(node.id()); + + nodes.add(node); + } + } + + if (exchFut.context().mergeExchanges() && !curDiscoCache.version().equals(discoCache.version())) { + for (ClusterNode node : curDiscoCache.allNodes()) { + if (discoCache.node(node.id()) == null) { + if (exchangeProtocolVersion(node.version()) == 1) + break; + + awaited.add(node.id()); + + nodes.add(node); + + if (joinedNodes == null) + joinedNodes = new HashMap<>(); + + GridDhtPartitionExchangeId exchId = new GridDhtPartitionExchangeId(node.id(), + EVT_NODE_JOINED, + new AffinityTopologyVersion(node.order())); + + joinedNodes.put(node.id(), exchId); + } + } + } + + if (joinedNodes == null) + joinedNodes = Collections.emptyMap(); + + if (!awaited.isEmpty()) { + restoreStateFut = new GridFutureAdapter(); + + add(restoreStateFut); + } + } + + log.info("Try restore exchange result [allNodes=" + awaited + + ", joined=" + joinedNodes.keySet() + ']'); + + if (!nodes.isEmpty()) { + GridDhtPartitionsSingleRequest req = GridDhtPartitionsSingleRequest.restoreStateRequest(exchFut.exchangeId(), + exchFut.exchangeId()); + + for (ClusterNode node : nodes) { + try { + GridDhtPartitionsSingleRequest sndReq = req; + + if (joinedNodes.containsKey(node.id())) { + sndReq = GridDhtPartitionsSingleRequest.restoreStateRequest( + joinedNodes.get(node.id()), + exchFut.exchangeId()); + } + + cctx.io().send(node, sndReq, GridIoPolicy.SYSTEM_POOL); + } + catch (ClusterTopologyCheckedException e) { + if (log.isDebugEnabled()) + log.debug("Failed to send partitions request, node failed: " + node); + + onNodeLeft(node.id()); + } + } + } + } + + markInitialized(); + } + + boolean restoreState() { + return restoreState; + } + + /** + * @return Received messages. + */ + Map messages() { + return msgs; + } + + /** + * @return Full message is some of nodes received it from previous coordinator. + */ + GridDhtPartitionsFullMessage fullMessage() { + return fullMsg; + } + + /** + * @param node Node. + * @param msg Message. + */ + public void onMessage(ClusterNode node, GridDhtPartitionsSingleMessage msg) { + log.info("Init new coordinator, received response [node=" + node.id() + + ", fullMsg=" + (msg.finishMessage() != null) + + ", affReq=" + !F.isEmpty(msg.cacheGroupsAffinityRequest()) + ']'); + + assert msg.restoreState() : msg; + + boolean done = false; + + synchronized (this) { + if (awaited.remove(node.id())) { + GridDhtPartitionsFullMessage fullMsg0 = msg.finishMessage(); + + if (fullMsg0 != null) { + assert fullMsg == null || fullMsg.resultTopologyVersion().equals(fullMsg0.resultTopologyVersion()); + + fullMsg = fullMsg0; + } + else + msgs.put(node, msg); + + done = awaited.isEmpty(); + } + + if (done) + onAllReceived(); + } + + if (done) + restoreStateFut.onDone(); + } + + /** + * + */ + private void onAllReceived() { + if (fullMsg != null) { + AffinityTopologyVersion resVer = fullMsg.resultTopologyVersion(); + + for (Iterator> it = msgs.entrySet().iterator(); it.hasNext();) { + Map.Entry e = it.next(); + + GridDhtPartitionExchangeId msgVer = joinedNodes.get(e.getKey().id()); + + if (msgVer != null) { + assert msgVer.topologyVersion().compareTo(initTopVer) > 0 : msgVer; + + log.info("Process joined node message [resVer=" + resVer + + ", initTopVer=" + initTopVer + + ", msgVer=" + msgVer.topologyVersion() + ']'); + + if (msgVer.topologyVersion().compareTo(resVer) > 0) + it.remove(); + else + e.getValue().exchangeId(msgVer); + } + } + } + else { + for (Iterator> it = msgs.entrySet().iterator(); it.hasNext();) { + Map.Entry e = it.next(); + + GridDhtPartitionExchangeId msgVer = joinedNodes.get(e.getKey().id()); + + if (msgVer != null) { + it.remove(); + + assert msgVer.topologyVersion().compareTo(initTopVer) > 0 : msgVer; + + log.info("Process joined node message [initTopVer=" + initTopVer + + ", msgVer=" + msgVer.topologyVersion() + ']'); + + if (joinExchMsgs == null) + joinExchMsgs = new HashMap<>(); + + e.getValue().exchangeId(msgVer); + + joinExchMsgs.put(e.getKey().id(), e.getValue()); + } + } + + } + } + + @Nullable GridDhtPartitionsSingleMessage joinExchangeMessage(UUID nodeId) { + return joinExchMsgs != null ? joinExchMsgs.get(nodeId) : null; + } + + /** + * @param nodeId Failed node ID. + */ + public void onNodeLeft(UUID nodeId) { + log.info("Init new coordinator, node left [node=" + nodeId + ']'); + + boolean done; + + synchronized (this) { + done = awaited.remove(nodeId) && awaited.isEmpty(); + } + + if (done) + restoreStateFut.onDone(); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/RebalanceReassignExchangeTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/RebalanceReassignExchangeTask.java index 9a76a8e6760b6..7e473be2eafd8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/RebalanceReassignExchangeTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/RebalanceReassignExchangeTask.java @@ -35,6 +35,11 @@ public RebalanceReassignExchangeTask(GridDhtPartitionExchangeId exchId) { this.exchId = exchId; } + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return true; + } + /** * @return Exchange ID. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java index b27591ec14ee4..428355cc87054 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java @@ -323,7 +323,7 @@ protected void processGetResponse(UUID nodeId, GridNearGetResponse res) { * @return Near entries. */ public Set> nearEntries() { - final AffinityTopologyVersion topVer = ctx.discovery().topologyVersionEx(); + final AffinityTopologyVersion topVer = ctx.shared().exchange().readyAffinityVersion(); return super.entrySet(new CacheEntryPredicateAdapter() { @Override public boolean apply(GridCacheEntryEx entry) { 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 a49812e6d9aa5..9d9c682d9e5c0 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 @@ -333,7 +333,7 @@ private void map( remapKeys.add(key); } - AffinityTopologyVersion updTopVer = cctx.discovery().topologyVersionEx(); + AffinityTopologyVersion updTopVer = cctx.shared().exchange().readyAffinityVersion(); assert updTopVer.compareTo(topVer) > 0 : "Got invalid partitions for local node but topology version did " + "not change [topVer=" + topVer + ", updTopVer=" + updTopVer + @@ -626,7 +626,7 @@ private boolean localDhtGet(KeyCacheObject key, return true; } else { - boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().topologyVersion()); + boolean topStable = cctx.isReplicated() || topVer.equals(cctx.topology().lastTopologyChangeVersion()); // Entry not found, do not continue search if topology did not change and there is no store. return !cctx.readThroughConfigured() && (topStable || partitionOwned(part)); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java index db030b0a78f65..bb713371953d2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java @@ -787,7 +787,7 @@ void map() { if (topVer != null) { for (GridDhtTopologyFuture fut : cctx.shared().exchange().exchangeFutures()) { - if (fut.topologyVersion().equals(topVer)){ + if (fut.exchangeDone() && fut.topologyVersion().equals(topVer)){ Throwable err = fut.validateCache(cctx, recovery, read, null, keys); if (err != null) { 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 1995e2e148bad..69d0940487032 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 @@ -554,6 +554,7 @@ private GridNearTxPrepareRequest createRequest( tx.subjectId(), tx.taskNameHash(), m.clientFirst(), + txNodes.size() == 1, tx.activeCachesDeploymentEnabled()); for (IgniteTxEntry txEntry : writes) { 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 b763f02f2b047..2c23a7a50d3f9 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 @@ -540,6 +540,7 @@ private void proceedPrepare(GridDistributedTxMapping m, @Nullable final Queue> txNod tx.subjectId(), tx.taskNameHash(), false, + true, tx.activeCachesDeploymentEnabled()); for (IgniteTxEntry txEntry : writes) { 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 875f3975914c6..7deceb599f63f 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 @@ -54,6 +54,9 @@ public class GridNearTxPrepareRequest extends GridDistributedTxPrepareRequest { /** */ private static final int EXPLICIT_LOCK_FLAG_MASK = 0x08; + /** */ + private static final int ALLOW_WAIT_TOP_FUT_FLAG_MASK = 0x10; + /** Future ID. */ private IgniteUuid futId; @@ -97,6 +100,7 @@ public GridNearTxPrepareRequest() { * @param subjId Subject ID. * @param taskNameHash Task name hash. * @param firstClientReq {@code True} if first optimistic tx prepare request sent from client node. + * @param {@code True} if it is safe for first client request to wait for topology future. * @param addDepInfo Deployment info flag. */ public GridNearTxPrepareRequest( @@ -116,6 +120,7 @@ public GridNearTxPrepareRequest( @Nullable UUID subjId, int taskNameHash, boolean firstClientReq, + boolean allowWaitTopFut, boolean addDepInfo ) { super(tx, @@ -140,6 +145,15 @@ public GridNearTxPrepareRequest( setFlag(implicitSingle, IMPLICIT_SINGLE_FLAG_MASK); setFlag(explicitLock, EXPLICIT_LOCK_FLAG_MASK); setFlag(firstClientReq, FIRST_CLIENT_REQ_FLAG_MASK); + setFlag(allowWaitTopFut, ALLOW_WAIT_TOP_FUT_FLAG_MASK); + } + + /** + * @return {@code True} if it is safe for first client request to wait for topology future + * completion. + */ + public boolean allowWaitTopologyFuture() { + return isFlag(ALLOW_WAIT_TOP_FUT_FLAG_MASK); } /** 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 4e4048fa5ca52..6d19ffb1d166e 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 @@ -722,7 +722,7 @@ private void shutdownCheckpointer(boolean cancel) { /** {@inheritDoc} */ @Override public void beforeExchange(GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException { - DiscoveryEvent discoEvt = fut.discoveryEvent(); + DiscoveryEvent discoEvt = fut.firstEvent(); boolean joinEvt = discoEvt.type() == EventType.EVT_NODE_JOINED; @@ -738,7 +738,7 @@ private void shutdownCheckpointer(boolean cancel) { if (cctx.kernalContext().query().moduleEnabled()) { for (GridCacheContext cacheCtx : (Collection)cctx.cacheContexts()) { - if (cacheCtx.startTopologyVersion().equals(fut.topologyVersion()) && + if (cacheCtx.startTopologyVersion().equals(fut.initialVersion()) && !cctx.pageStore().hasIndexStore(cacheCtx.groupId()) && cacheCtx.affinityNode()) { final int cacheId = cacheCtx.cacheId(); @@ -1559,6 +1559,7 @@ private void restorePartitionState( long partMetaId = pageMem.partitionMetaPageId(grpId, i); long partMetaPage = pageMem.acquirePage(grpId, partMetaId); + try { long pageAddr = pageMem.writeLock(grpId, partMetaId, partMetaPage); @@ -1623,6 +1624,7 @@ private boolean updateState(GridDhtLocalPartition part, int stateId) { /** * @param cacheCtx Cache context to apply an update. * @param dataEntry Data entry to apply. + * @throws IgniteCheckedException If failed to restore. */ private void applyUpdate(GridCacheContext cacheCtx, DataEntry dataEntry) throws IgniteCheckedException { int partId = dataEntry.partitionId(); 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 87eed82a7e815..023c03c9409ae 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 @@ -668,7 +668,7 @@ private ScanQueryFallbackClosableIterator(int part, GridCacheQueryAdapter qry, this.cctx = cctx; this.part = part; - nodes = fallbacks(cctx.discovery().topologyVersionEx()); + nodes = fallbacks(cctx.shared().exchange().readyAffinityVersion()); if (F.isEmpty(nodes)) throw new ClusterTopologyException("Failed to execute the query " + @@ -826,7 +826,7 @@ else if (e.hasCause(ClusterGroupEmptyCheckedException.class)) { if (retryFut != null) retryFut.get(); - nodes = fallbacks(unreservedTopVer == null ? cctx.discovery().topologyVersionEx() : unreservedTopVer); + nodes = fallbacks(unreservedTopVer == null ? cctx.shared().exchange().readyAffinityVersion() : unreservedTopVer); unreservedTopVer = null; 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 61ca78cbd570d..65f8af247b002 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 @@ -475,7 +475,7 @@ protected void uncommit(boolean nodeStopping) { return topVer; } - return cctx.exchange().topologyVersion(); + return cctx.exchange().readyAffinityVersion(); } return res; 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 b53849d5192ac..de0e4151c303a 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 @@ -42,6 +42,7 @@ 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.distributed.dht.GridDhtTopologyFuture; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishFuture; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishRequest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFinishResponse; @@ -74,6 +75,7 @@ import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteFutureCancelledException; +import org.apache.ignite.thread.IgniteThread; import org.apache.ignite.transactions.TransactionState; import org.jetbrains.annotations.Nullable; @@ -109,10 +111,10 @@ public class IgniteTxHandler { private GridCacheSharedContext ctx; /** - * @param nearNodeId Node ID. + * @param nearNodeId Sender node ID. * @param req Request. */ - private void processNearTxPrepareRequest(final UUID nearNodeId, GridNearTxPrepareRequest req) { + private void processNearTxPrepareRequest(UUID nearNodeId, GridNearTxPrepareRequest req) { if (txPrepareMsgLog.isDebugEnabled()) { txPrepareMsgLog.debug("Received near prepare request [txId=" + req.version() + ", node=" + nearNodeId + ']'); @@ -130,7 +132,29 @@ private void processNearTxPrepareRequest(final UUID nearNodeId, GridNearTxPrepar return; } - IgniteInternalFuture fut = prepareNearTx(nearNode, req); + processNearTxPrepareRequest0(nearNode, req); + } + + /** + * @param nearNode Sender node. + * @param req Request. + */ + private void processNearTxPrepareRequest0(ClusterNode nearNode, GridNearTxPrepareRequest req) { + IgniteInternalFuture fut; + + if (req.firstClientRequest() && req.allowWaitTopologyFuture()) { + for (;;) { + if (waitForExchangeFuture(nearNode, req)) + return; + + fut = prepareNearTx(nearNode, req); + + if (fut != null) + break; + } + } + else + fut = prepareNearTx(nearNode, req); assert req.txState() != null || fut == null || fut.error() != null || (ctx.tm().tx(req.version()) == null && ctx.tm().nearTx(req.version()) == null); @@ -303,9 +327,9 @@ public IgniteInternalFuture prepareNearTxLocal(final /** * @param nearNode Node that initiated transaction. * @param req Near prepare request. - * @return Prepare future. + * @return Prepare future or {@code null} if need retry operation. */ - private IgniteInternalFuture prepareNearTx( + @Nullable private IgniteInternalFuture prepareNearTx( final ClusterNode nearNode, final GridNearTxPrepareRequest req ) { @@ -348,55 +372,90 @@ private IgniteInternalFuture prepareNearTx( top = firstEntry.context().topology(); top.readLock(); + + if (req.allowWaitTopologyFuture()) { + GridDhtTopologyFuture topFut = top.topologyVersionFuture(); + + if (!topFut.isDone()) { + top.readUnlock(); + + return null; + } + } } try { - if (top != null && needRemap(req.topologyVersion(), top.topologyVersion(), req)) { - if (txPrepareMsgLog.isDebugEnabled()) { - txPrepareMsgLog.debug("Topology version mismatch for near prepare, need remap transaction [" + - "txId=" + req.version() + - ", node=" + nearNode.id() + - ", reqTopVer=" + req.topologyVersion() + - ", locTopVer=" + top.topologyVersion() + - ", req=" + req + ']'); - } + if (top != null ) { + boolean retry = false; - GridNearTxPrepareResponse res = new GridNearTxPrepareResponse( - req.partition(), - req.version(), - req.futureId(), - req.miniId(), - req.version(), - req.version(), - null, - null, - top.topologyVersion(), - req.onePhaseCommit(), - req.deployInfo() != null); + GridDhtTopologyFuture topFut = top.topologyVersionFuture(); - try { - ctx.io().send(nearNode, res, req.policy()); + if (!req.allowWaitTopologyFuture() && !topFut.isDone()) { + retry = true; if (txPrepareMsgLog.isDebugEnabled()) { - txPrepareMsgLog.debug("Sent remap response for near prepare [txId=" + req.version() + - ", node=" + nearNode.id() + ']'); + txPrepareMsgLog.debug("Topology change is in progress, need remap transaction [" + + "txId=" + req.version() + + ", node=" + nearNode.id() + + ", reqTopVer=" + req.topologyVersion() + + ", locTopVer=" + top.readyTopologyVersion() + + ", req=" + req + ']'); } } - catch (ClusterTopologyCheckedException ignored) { + + if (!retry && needRemap(req.topologyVersion(), top.readyTopologyVersion(), req)) { + retry = true; + if (txPrepareMsgLog.isDebugEnabled()) { - txPrepareMsgLog.debug("Failed to send remap response for near prepare, node failed [" + + txPrepareMsgLog.debug("Topology version mismatch for near prepare, need remap transaction [" + "txId=" + req.version() + - ", node=" + nearNode.id() + ']'); + ", node=" + nearNode.id() + + ", reqTopVer=" + req.topologyVersion() + + ", locTopVer=" + top.readyTopologyVersion() + + ", req=" + req + ']'); } } - catch (IgniteCheckedException e) { - U.error(txPrepareMsgLog, "Failed to send remap response for near prepare " + - "[txId=" + req.version() + - ", node=" + nearNode.id() + - ", req=" + req + ']', e); + + if (retry) { + GridNearTxPrepareResponse res = new GridNearTxPrepareResponse( + req.partition(), + req.version(), + req.futureId(), + req.miniId(), + req.version(), + req.version(), + null, + null, + top.lastTopologyChangeVersion(), + req.onePhaseCommit(), + req.deployInfo() != null); + + try { + ctx.io().send(nearNode, res, req.policy()); + + if (txPrepareMsgLog.isDebugEnabled()) { + txPrepareMsgLog.debug("Sent remap response for near prepare [txId=" + req.version() + + ", node=" + nearNode.id() + ']'); + } + } + catch (ClusterTopologyCheckedException ignored) { + if (txPrepareMsgLog.isDebugEnabled()) { + txPrepareMsgLog.debug("Failed to send remap response for near prepare, node failed [" + + "txId=" + req.version() + + ", node=" + nearNode.id() + ']'); + } + } + catch (IgniteCheckedException e) { + U.error(txPrepareMsgLog, "Failed to send remap response for near prepare " + + "[txId=" + req.version() + + ", node=" + nearNode.id() + + ", req=" + req + ']', e); + } + + return new GridFinishedFuture<>(res); } - return new GridFinishedFuture<>(res); + assert topFut.isDone(); } tx = new GridDhtTxLocal( @@ -498,6 +557,53 @@ private IgniteInternalFuture prepareNearTx( return new GridFinishedFuture<>((GridNearTxPrepareResponse)null); } + /** + * @param node Sender node. + * @param req Request. + * @return {@code True} if update will be retried from future listener. + */ + private boolean waitForExchangeFuture(final ClusterNode node, final GridNearTxPrepareRequest req) { + assert req.firstClientRequest() : req; + + GridDhtTopologyFuture topFut = ctx.exchange().lastTopologyFuture(); + + if (!topFut.isDone()) { + Thread curThread = Thread.currentThread(); + + if (curThread instanceof IgniteThread) { + final IgniteThread thread = (IgniteThread)curThread; + + if (thread.cachePoolThread()) { + topFut.listen(new CI1>() { + @Override public void apply(IgniteInternalFuture fut) { + ctx.kernalContext().closure().runLocalWithThreadPolicy(thread, new Runnable() { + @Override public void run() { + try { + processNearTxPrepareRequest0(node, req); + } + finally { + ctx.io().onMessageProcessed(req); + } + } + }); + } + }); + + return true; + } + } + + try { + topFut.get(); + } + catch (IgniteCheckedException e) { + U.error(log, "Topology future failed: " + e, e); + } + } + + return false; + } + /** * @param expVer Expected topology version. * @param curVer Current topology version. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java index 01207e382607d..2ecea076126ad 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/closure/GridClosureProcessor.java @@ -69,6 +69,7 @@ import org.apache.ignite.lang.IgniteReducer; import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.resources.LoadBalancerResource; +import org.apache.ignite.thread.IgniteThread; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -502,7 +503,7 @@ public ComputeTaskInternalFuture affinityCall(@NotNull Collection final String cacheName = F.first(cacheNames); - final AffinityTopologyVersion mapTopVer = ctx.discovery().topologyVersionEx(); + final AffinityTopologyVersion mapTopVer = ctx.cache().context().exchange().readyAffinityVersion(); final ClusterNode node = ctx.affinity().mapPartitionToNode(cacheName, partId, mapTopVer); if (node == null) @@ -542,7 +543,7 @@ public ComputeTaskInternalFuture affinityRun(@NotNull Collection cach final String cacheName = F.first(cacheNames); - final AffinityTopologyVersion mapTopVer = ctx.discovery().topologyVersionEx(); + final AffinityTopologyVersion mapTopVer = ctx.cache().context().exchange().readyAffinityVersion(); final ClusterNode node = ctx.affinity().mapPartitionToNode(cacheName, partId, mapTopVer); if (node == null) @@ -779,13 +780,22 @@ public ComputeTaskInternalFuture callAsync(IgniteClosure } /** - * @param c Closure to execute. - * @param sys If {@code true}, then system pool will be used, otherwise public pool will be used. - * @return Future. - * @throws IgniteCheckedException Thrown in case of any errors. + * @param thread Thread. + * @param c Closure. */ - private IgniteInternalFuture runLocal(@Nullable final Runnable c, boolean sys) throws IgniteCheckedException { - return runLocal(c, sys ? GridIoPolicy.SYSTEM_POOL : GridIoPolicy.PUBLIC_POOL); + public void runLocalWithThreadPolicy(IgniteThread thread, Runnable c) { + assert thread.stripe() >= 0 || thread.policy() != GridIoPolicy.UNDEFINED : thread; + + if (thread.stripe() >= 0) + ctx.getStripedExecutorService().execute(thread.stripe(), c); + else { + try { + ctx.pools().poolForPolicy(thread.policy()).execute(c); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to get pool for policy: " + thread.policy(), e); + } + } } /** @@ -852,8 +862,8 @@ public IgniteInternalFuture runLocal(@Nullable final Runnable c, byte plc) } /** - * Executes closure on system pool. Companion to {@link #runLocal(Runnable, boolean)} but - * in case of rejected execution re-runs the closure in the current thread (blocking). + * Executes closure on system pool. In case of rejected execution re-runs the closure in the current + * thread (blocking). * * @param c Closure to execute. * @return Future. @@ -863,8 +873,7 @@ public IgniteInternalFuture runLocalSafe(Runnable c) { } /** - * Companion to {@link #runLocal(Runnable, boolean)} but in case of rejected execution re-runs - * the closure in the current thread (blocking). + * In case of rejected execution re-runs the closure in the current thread (blocking). * * @param c Closure to execute. * @param sys If {@code true}, then system pool will be used, otherwise public pool will be used. @@ -875,8 +884,7 @@ public IgniteInternalFuture runLocalSafe(Runnable c, boolean sys) { } /** - * Companion to {@link #runLocal(Runnable, boolean)} but in case of rejected execution re-runs - * the closure in the current thread (blocking). + * In case of rejected execution re-runs the closure in the current thread (blocking). * * @param c Closure to execute. * @param plc Policy to choose executor pool. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java index 13a889cd68991..3ad75cc9fddbd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java @@ -501,7 +501,7 @@ private void sendComputeChangeGlobalState(boolean activate, final GridFutureAdap log.info("Sending " + prettyStr(activate) + " request from node [id=" + ctx.localNodeId() + ", topVer=" + topVer + ", client=" + ctx.clientNode() + - ", daemon" + ctx.isDaemon() + "]"); + ", daemon=" + ctx.isDaemon() + "]"); } IgniteCompute comp = ((ClusterGroupAdapter)ctx.cluster().get().forServers()).compute(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessor.java index 84d536f8c5bfb..8b984c05c1988 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamProcessor.java @@ -321,8 +321,10 @@ private void localUpdate(final UUID nodeId, try { GridCacheAdapter cache = ctx.cache().internalCache(req.cacheName()); - if (cache == null) - throw new IgniteCheckedException("Cache not created or already destroyed."); + if (cache == null) { + throw new IgniteCheckedException("Cache not created or already destroyed: " + + req.cacheName()); + } GridCacheContext cctx = cache.context(); @@ -336,18 +338,33 @@ private void localUpdate(final UUID nodeId, GridDhtTopologyFuture topWaitFut = null; try { - GridDhtTopologyFuture fut = cctx.topologyVersionFuture(); + Exception remapErr = null; + + AffinityTopologyVersion streamerFutTopVer = null; + + if (!allowOverwrite) { + GridDhtTopologyFuture topFut = cctx.topologyVersionFuture(); - AffinityTopologyVersion topVer = fut.topologyVersion(); + AffinityTopologyVersion topVer = topFut.isDone() ? topFut.topologyVersion() : + topFut.initialVersion(); + + if (topVer.compareTo(req.topologyVersion()) > 0) { + remapErr = new ClusterTopologyCheckedException("DataStreamer will retry " + + "data transfer at stable topology [reqTop=" + req.topologyVersion() + + ", topVer=" + topFut.initialVersion() + ", node=remote]"); + } + else if (!topFut.isDone()) + topWaitFut = topFut; + else + streamerFutTopVer = topFut.topologyVersion(); + } - if (!allowOverwrite && !topVer.equals(req.topologyVersion())) { - Exception err = new ClusterTopologyCheckedException( - "DataStreamer will retry data transfer at stable topology " + - "[reqTop=" + req.topologyVersion() + ", topVer=" + topVer + ", node=remote]"); + if (remapErr != null) { + sendResponse(nodeId, topic, req.requestId(), remapErr, req.forceLocalDeployment()); - sendResponse(nodeId, topic, req.requestId(), err, req.forceLocalDeployment()); + return; } - else if (allowOverwrite || fut.isDone()) { + else if (topWaitFut == null) { job = new DataStreamerUpdateJob(ctx, log, req.cacheName(), @@ -357,10 +374,8 @@ else if (allowOverwrite || fut.isDone()) { req.keepBinary(), updater); - waitFut = allowOverwrite ? null : cctx.mvcc().addDataStreamerFuture(topVer); + waitFut = allowOverwrite ? null : cctx.mvcc().addDataStreamerFuture(streamerFutTopVer); } - else - topWaitFut = fut; } finally { if (!allowOverwrite) @@ -378,16 +393,14 @@ else if (allowOverwrite || fut.isDone()) { return; } - if (job != null) { - try { - job.call(); + try { + job.call(); - sendResponse(nodeId, topic, req.requestId(), null, req.forceLocalDeployment()); - } - finally { - if (waitFut != null) - waitFut.onDone(); - } + sendResponse(nodeId, topic, req.requestId(), null, req.forceLocalDeployment()); + } + finally { + if (waitFut != null) + waitFut.onDone(); } } catch (Throwable e) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java index df51fac168df5..e2ee0b1811aed 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java @@ -768,9 +768,12 @@ private void load0( } try { - AffinityTopologyVersion topVer = allowOverwrite() || cctx.isLocal() ? - ctx.cache().context().exchange().readyAffinityVersion() : - cctx.topology().topologyVersion(); + AffinityTopologyVersion topVer; + + if (!cctx.isLocal()) + topVer = ctx.cache().context().exchange().lastTopologyFuture().get(); + else + topVer = ctx.cache().context().exchange().readyAffinityVersion(); for (DataStreamerEntry entry : entries) { List nodes; @@ -1524,23 +1527,36 @@ private void localUpdate(final Collection entries, try { GridCacheContext cctx = ctx.cache().internalCache(cacheName).context(); - final boolean allowOverwrite = allowOverwrite(); - final boolean loc = cctx.isLocal(); + final boolean lockTop = !cctx.isLocal() && !allowOverwrite(); + + GridDhtTopologyFuture topWaitFut = null; - if (!loc && !allowOverwrite) + if (lockTop) cctx.topology().readLock(); try { - GridDhtTopologyFuture fut = loc ? null : cctx.topologyVersionFuture(); + AffinityTopologyVersion streamerFutTopVer = null; - AffinityTopologyVersion topVer = loc ? reqTopVer : fut.topologyVersion(); + if (lockTop) { + GridDhtTopologyFuture topFut = cctx.topologyVersionFuture(); - if (!allowOverwrite && !topVer.equals(reqTopVer)) { - curFut.onDone(new IgniteCheckedException( - "DataStreamer will retry data transfer at stable topology. " + - "[reqTop=" + reqTopVer + " ,topVer=" + topVer + ", node=local]")); + AffinityTopologyVersion topVer = topFut.isDone() ? topFut.topologyVersion() : + topFut.initialVersion(); + + if (topVer.compareTo(reqTopVer) > 0) { + curFut.onDone(new IgniteCheckedException("DataStreamer will retry data transfer " + + "at stable topology. reqTop=" + reqTopVer + ", topVer=" + topFut.initialVersion() + + ", node=local]")); + + return; + } + else if (!topFut.isDone()) + topWaitFut = topFut; + else + streamerFutTopVer = topFut.topologyVersion(); } - else if (loc || allowOverwrite || fut.isDone()) { + + if (topWaitFut == null) { IgniteInternalFuture callFut = ctx.closure().callLocalSafe( new DataStreamerUpdateJob( ctx, @@ -1555,9 +1571,8 @@ else if (loc || allowOverwrite || fut.isDone()) { locFuts.add(callFut); - final GridFutureAdapter waitFut = (loc || allowOverwrite) ? - null : - cctx.mvcc().addDataStreamerFuture(topVer); + final GridFutureAdapter waitFut = + lockTop ? cctx.mvcc().addDataStreamerFuture(streamerFutTopVer) : null; callFut.listen(new IgniteInClosure>() { @Override public void apply(IgniteInternalFuture t) { @@ -1578,18 +1593,20 @@ else if (loc || allowOverwrite || fut.isDone()) { } }); } - else { - fut.listen(new IgniteInClosure>() { - @Override public void apply(IgniteInternalFuture e) { - localUpdate(entries, reqTopVer, curFut, plc); - } - }); - } } finally { - if (!loc && !allowOverwrite) + if (lockTop) cctx.topology().readUnlock(); } + + if (topWaitFut != null) { + // Need call 'listen' after topology read lock is released. + topWaitFut.listen(new IgniteInClosure>() { + @Override public void apply(IgniteInternalFuture e) { + localUpdate(entries, reqTopVer, curFut, plc); + } + }); + } } catch (Throwable ex) { curFut.onDone(new IgniteCheckedException("DataStreamer data handling failed.", ex)); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/datastreamer/PlatformDataStreamer.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/datastreamer/PlatformDataStreamer.java index 7d71a9e6c08e4..fba0a4c8b5999 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/datastreamer/PlatformDataStreamer.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/datastreamer/PlatformDataStreamer.java @@ -209,7 +209,8 @@ else if (plc == PLC_FLUSH) GridDiscoveryManager discoMgr = platformCtx.kernalContext().discovery(); - AffinityTopologyVersion topVer = discoMgr.topologyVersionEx(); + AffinityTopologyVersion topVer = + platformCtx.kernalContext().cache().context().exchange().lastTopologyFuture().get(); int topSize = discoMgr.cacheNodes(cacheName, topVer).size(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java index f97f931ff838c..c16a7bacee219 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaExchangeWorkerTask.java @@ -39,6 +39,11 @@ public SchemaExchangeWorkerTask(SchemaAbstractDiscoveryMessage msg) { this.msg = msg; } + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return false; + } + /** * @return Message. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java index 79fbfcdf68c01..668a2c62b52f5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/schema/SchemaNodeLeaveExchangeWorkerTask.java @@ -39,6 +39,11 @@ public SchemaNodeLeaveExchangeWorkerTask(ClusterNode node) { this.node = node; } + /** {@inheritDoc} */ + @Override public boolean skipForExchangeMerge() { + return true; + } + /** * @return Node. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java index ec9500105c0f5..74fe57dec06aa 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/task/GridTaskWorker.java @@ -863,8 +863,8 @@ void onResponse(GridJobExecuteResponse msg) { assert affCacheIds != null; retry = true; - mapTopVer = U.max(res.getRetryTopologyVersion(), ctx.discovery().topologyVersionEx()); - affFut = ctx.cache().context().exchange().affinityReadyFuture(mapTopVer); + mapTopVer = U.max(res.getRetryTopologyVersion(), ctx.cache().context().exchange().readyAffinityVersion()); + affFut = ctx.cache().context().exchange().lastTopologyFuture(); if (affFut != null && !affFut.isDone()) { waitForAffTop = true; @@ -900,9 +900,9 @@ void onResponse(GridJobExecuteResponse msg) { case FAILOVER: { if (affCacheIds != null) { - mapTopVer = ctx.discovery().topologyVersionEx(); + mapTopVer = ctx.cache().context().exchange().readyAffinityVersion(); - affFut = ctx.cache().context().exchange().affinityReadyFuture(mapTopVer); + affFut = ctx.cache().context().exchange().lastTopologyFuture(); } if (affFut != null && !affFut.isDone()) { diff --git a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java index c814625d23fb3..83a0384cfcc28 100644 --- a/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java +++ b/modules/core/src/main/java/org/apache/ignite/thread/IgniteThread.java @@ -125,6 +125,15 @@ public int stripe() { return stripe; } + /** + * @return {@code True} if thread belongs to pool processing cache operations. + */ + public boolean cachePoolThread() { + return stripe >= 0 || + plc == GridIoPolicy.SYSTEM_POOL || + plc == GridIoPolicy.UTILITY_CACHE_POOL; + } + /** * Gets name of the Ignite instance this thread belongs to. * diff --git a/modules/core/src/test/java/org/apache/ignite/internal/TestDelayingCommunicationSpi.java b/modules/core/src/test/java/org/apache/ignite/internal/TestDelayingCommunicationSpi.java new file mode 100644 index 0000000000000..e49d5da497ea7 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/TestDelayingCommunicationSpi.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; + +import org.apache.ignite.IgniteException; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteInClosure; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.spi.IgniteSpiException; +import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; +import org.jsr166.ThreadLocalRandom8; + +/** + * + */ +public abstract class TestDelayingCommunicationSpi extends TcpCommunicationSpi { + /** {@inheritDoc} */ + @Override public void sendMessage(ClusterNode node, Message msg, IgniteInClosure ackC) + throws IgniteSpiException { + try { + GridIoMessage ioMsg = (GridIoMessage)msg; + + if (delayMessage(ioMsg.message(), ioMsg)) + U.sleep(ThreadLocalRandom8.current().nextInt(delayMillis()) + 1); + } + catch (IgniteInterruptedCheckedException e) { + throw new IgniteSpiException(e); + } + + super.sendMessage(node, msg, ackC); + } + + /** + * @param msg Message. + * @param ioMsg Wrapper message. + * @return {@code True} if need delay message. + */ + protected abstract boolean delayMessage(Message msg, GridIoMessage ioMsg); + + /** + * @return Max delay time. + */ + protected int delayMillis() { + return 250; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java index 9f4fc8040599b..bff63fb4e812f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java @@ -27,12 +27,12 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.TestRecordingCommunicationSpi; -import org.apache.ignite.internal.managers.communication.GridIoMessage; 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.GridDhtPartitionsAbstractMessage; 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.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.lang.IgniteBiPredicate; import org.apache.ignite.lang.IgnitePredicate; @@ -328,6 +328,13 @@ private void checkSingleMessage(String cache1, Map dupPartsData, GridDhtPartitionsSingleMessage msg) { + if (!F.isEmpty(msg.cacheGroupsAffinityRequest())) { + for (GridDhtPartitionMap map : msg.partitions().values()) + assertTrue(F.isEmpty(map.map())); + + return; + } + int cache1Grp = groupIdForCache(ignite(0), cache1); int cache2Grp = groupIdForCache(ignite(0), cache2); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java index f32e15fcc63a2..eda0a495c8818 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java @@ -287,7 +287,9 @@ public void testRebalanceState() throws Exception { for (String cacheName : cacheNames) { GridDhtPartitionTopology top = node.context().cache().internalCache(cacheName).context().topology(); - assertEquals(topVer, top.topologyVersion()); + waitForReadyTopology(top, topVer); + + assertEquals(topVer, top.readyTopologyVersion()); assertFalse(top.rebalanceFinished(topVer)); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java index 8a604be6b5c8f..a3e0d2ca422f4 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java @@ -1082,7 +1082,7 @@ private void stateChangeFailover2(boolean activate) throws Exception { client = false; - // Start one more nodes while transition is in progress. + // Start more nodes while transition is in progress. IgniteInternalFuture startFut1 = GridTestUtils.runAsync(new Callable() { @Override public Object call() throws Exception { startGrid(8); @@ -1101,7 +1101,7 @@ private void stateChangeFailover2(boolean activate) throws Exception { U.sleep(500); // Stop coordinator. - stopGrid(0); + stopGrid(getTestIgniteInstanceName(0), true, false); stopGrid(getTestIgniteInstanceName(1), true, false); stopGrid(getTestIgniteInstanceName(4), true, false); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicCacheStartSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicCacheStartSelfTest.java index 4f606c92b77ac..8f601f82ee8e8 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicCacheStartSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicCacheStartSelfTest.java @@ -324,9 +324,6 @@ private void checkStartStopCacheSimple(CacheAtomicityMode mode) throws Exception for (int g = 0; g < nodeCount(); g++) { IgniteEx kernal0 = grid(g); - for (IgniteInternalFuture f : kernal0.context().cache().context().exchange().exchangeFutures()) - f.get(); - info("Getting cache for node: " + g); assertNotNull(grid(g).cache(DYNAMIC_CACHE_NAME)); @@ -345,14 +342,13 @@ private void checkStartStopCacheSimple(CacheAtomicityMode mode) throws Exception kernal.destroyCache(DYNAMIC_CACHE_NAME); + awaitPartitionMapExchange(); + for (int g = 0; g < nodeCount(); g++) { final IgniteKernal kernal0 = (IgniteKernal)grid(g); final int idx = g; - for (IgniteInternalFuture f : kernal0.context().cache().context().exchange().exchangeFutures()) - f.get(); - assertNull(kernal0.cache(DYNAMIC_CACHE_NAME)); GridTestUtils.assertThrows(log, new Callable() { @@ -387,9 +383,6 @@ private void checkStartStopCachesSimple(CacheAtomicityMode mode) throws Exceptio for (int g = 0; g < nodeCount(); g++) { IgniteEx kernal0 = grid(g); - for (IgniteInternalFuture f : kernal0.context().cache().context().exchange().exchangeFutures()) - f.get(); - info("Getting cache for node: " + g); for (int i = 0; i < cacheCnt; i++) @@ -423,6 +416,8 @@ private void checkStartStopCachesSimple(CacheAtomicityMode mode) throws Exceptio kernal.destroyCaches(namesToDestroy); + awaitPartitionMapExchange(); + for (int g = 0; g < nodeCount(); g++) { final IgniteKernal kernal0 = (IgniteKernal)grid(g); @@ -430,9 +425,6 @@ private void checkStartStopCachesSimple(CacheAtomicityMode mode) throws Exceptio final int idx = g * nodeCount() + i; final int expVal = i; - for (IgniteInternalFuture f : kernal0.context().cache().context().exchange().exchangeFutures()) - f.get(); - assertNull(kernal0.cache(DYNAMIC_CACHE_NAME)); GridTestUtils.assertThrows(log, new Callable() { @@ -481,13 +473,12 @@ public void testStartStopCacheAddNode() throws Exception { startGrid(nodeCount() + 1); + awaitPartitionMapExchange(); + // Check that cache is not deployed on new node after undeploy. for (int g = 0; g < nodeCount() + 2; g++) { final IgniteKernal kernal0 = (IgniteKernal)grid(g); - for (IgniteInternalFuture f : kernal0.context().cache().context().exchange().exchangeFutures()) - f.get(); - assertNull(kernal0.cache(DYNAMIC_CACHE_NAME)); } } @@ -537,13 +528,12 @@ public void testDeployFilter() throws Exception { } } + awaitPartitionMapExchange(); + // Check that cache is not deployed on new node after undeploy. for (int g = 0; g < nodeCount() + 2; g++) { final IgniteKernal kernal0 = (IgniteKernal)grid(g); - for (IgniteInternalFuture f : kernal0.context().cache().context().exchange().exchangeFutures()) - f.get(); - if (g < nodeCount()) assertNotNull(grid(g).cache(DYNAMIC_CACHE_NAME)); else diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java index 2e551f946ed85..b78c9725a70e7 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java @@ -33,7 +33,7 @@ import org.apache.ignite.events.EventType; import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.IgniteKernal; -import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.lang.IgnitePredicate; import org.apache.ignite.lifecycle.LifecycleAware; @@ -271,9 +271,9 @@ private static class SplitAwareTopologyValidator implements TopologyValidator, L IgniteKernal kernal = (IgniteKernal)ignite; - GridDhtCacheAdapter dht = kernal.context().cache().internalCache(cacheName).context().dht(); + GridDhtPartitionsExchangeFuture curFut = kernal.context().cache().context().exchange().lastTopologyFuture(); - long cacheTopVer = dht.topology().topologyVersionFuture().topologyVersion().topologyVersion(); + long cacheTopVer = curFut.context().events().topologyVersion().topologyVersion(); if (hasSplit(nodes)) { boolean resolved = activatorTopVer != 0 && cacheTopVer >= activatorTopVer; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/NonAffinityCoordinatorDynamicStartStopTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/NonAffinityCoordinatorDynamicStartStopTest.java index a4733d5df0615..d0659417b7504 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/NonAffinityCoordinatorDynamicStartStopTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/NonAffinityCoordinatorDynamicStartStopTest.java @@ -117,7 +117,7 @@ public void testStartStop() throws Exception { cache.destroy(); - grid("dummy").createCache(CCFG); + grid(DUMMY_GRID_NAME).createCache(CCFG); } /** {@inheritDoc} */ 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 56561387815cb..782482e5715a1 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 @@ -767,7 +767,7 @@ private void doTestReentrantLock( return null; } - }); + }, "lock-thread"); // Wait until l.lock() has been called. while(!l.hasQueuedThreads() && !done.get()){ @@ -778,6 +778,8 @@ private void doTestReentrantLock( } }); + long endTime = System.currentTimeMillis() + getTestTimeout(); + while (!fut.isDone()) { try { lock.lock(); @@ -797,6 +799,9 @@ private void doTestReentrantLock( assertFalse(lock.isHeldByCurrentThread()); } } + + if (System.currentTimeMillis() > endTime) + fail("Failed to wait for topology change threads."); } fut.get(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheExchangeMergeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheExchangeMergeTest.java new file mode 100644 index 0000000000000..f93d60c072e49 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheExchangeMergeTest.java @@ -0,0 +1,1528 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +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 java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteSystemProperties; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.cluster.ClusterTopologyException; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.events.DiscoveryEvent; +import org.apache.ignite.events.Event; +import org.apache.ignite.events.EventType; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.IgniteInternalFuture; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.IgniteNodeAttributes; +import org.apache.ignite.internal.TestDelayingCommunicationSpi; +import org.apache.ignite.internal.TestRecordingCommunicationSpi; +import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.CacheGroupContext; +import org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsAbstractMessage; +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.distributed.dht.preloader.GridDhtPartitionsSingleRequest; +import org.apache.ignite.internal.util.future.GridCompoundFuture; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.G; +import org.apache.ignite.internal.util.typedef.PA; +import org.apache.ignite.lang.IgniteBiPredicate; +import org.apache.ignite.lang.IgniteClosure; +import org.apache.ignite.lang.IgnitePredicate; +import org.apache.ignite.plugin.extensions.communication.Message; +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.eclipse.jetty.util.ConcurrentHashSet; + +import static org.apache.ignite.IgniteSystemProperties.IGNITE_EXCHANGE_HISTORY_SIZE; +import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; +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.events.DiscoveryCustomEvent.EVT_DISCOVERY_CUSTOM_EVT; + +/** + * + */ +public class CacheExchangeMergeTest extends GridCommonAbstractTest { + /** */ + private static TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** */ + private static final long WAIT_SECONDS = 15; + + /** */ + private ThreadLocal client = new ThreadLocal<>(); + + /** */ + private boolean testSpi; + + /** */ + private boolean testDelaySpi; + + /** */ + private static String[] cacheNames = {"c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10"}; + + /** */ + private boolean cfgCache = true; + + /** */ + private IgniteClosure clientC; + + /** */ + private static ExecutorService executor; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder); + + if (testSpi) + cfg.setCommunicationSpi(new TestRecordingCommunicationSpi()); + else if (testDelaySpi) + cfg.setCommunicationSpi(new TestDelayExchangeMessagesSpi()); + + Boolean clientMode = client.get(); + + if (clientMode == null && clientC != null) + clientMode = clientC.apply(igniteInstanceName); + + if (clientMode != null) { + cfg.setClientMode(clientMode); + + client.set(null); + } + + if (cfgCache) { + cfg.setCacheConfiguration( + cacheConfiguration("c1", ATOMIC, PARTITIONED, 0), + cacheConfiguration("c2", ATOMIC, PARTITIONED, 1), + cacheConfiguration("c3", ATOMIC, PARTITIONED, 2), + cacheConfiguration("c4", ATOMIC, PARTITIONED, 10), + cacheConfiguration("c5", ATOMIC, REPLICATED, 0), + cacheConfiguration("c6", TRANSACTIONAL, PARTITIONED, 0), + cacheConfiguration("c7", TRANSACTIONAL, PARTITIONED, 1), + cacheConfiguration("c8", TRANSACTIONAL, PARTITIONED, 2), + cacheConfiguration("c9", TRANSACTIONAL, PARTITIONED, 10), + cacheConfiguration("c10", TRANSACTIONAL, REPLICATED, 0) + ); + } + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + if (executor != null) + executor.shutdown(); + + super.afterTestsStopped(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + + super.afterTest(); + } + + /** + * @param name Cache name. + * @param atomicityMode Cache atomicity mode. + * @param cacheMode Cache mode. + * @param backups Number of backups. + * @return Cache configuration. + */ + private CacheConfiguration cacheConfiguration(String name, + CacheAtomicityMode atomicityMode, + CacheMode cacheMode, + int backups) + { + CacheConfiguration ccfg = new CacheConfiguration(name); + + ccfg.setAtomicityMode(atomicityMode); + ccfg.setWriteSynchronizationMode(FULL_SYNC); + ccfg.setCacheMode(cacheMode); + + if (cacheMode == PARTITIONED) + ccfg.setBackups(backups); + + return ccfg; + } + + /** + * @throws Exception If failed. + */ + public void testDelayExchangeMessages() throws Exception { + testDelaySpi = true; + + System.setProperty(IgniteSystemProperties.IGNITE_EXCHANGE_MERGE_DELAY, "2000"); + + try { + final int srvs = 6; + final int clients = 3; + + startGridsMultiThreaded(srvs); + + for (int i = 0; i < clients; i++) { + client.set(true); + + startGrid(srvs + i); + } + + final int initNodes = srvs + clients; + + final AtomicInteger stopIdx = new AtomicInteger(); + + IgniteInternalFuture stopFut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + Thread.sleep(ThreadLocalRandom.current().nextLong(500) + 1); + + stopGrid(stopIdx.incrementAndGet()); + + return null; + } + }, 3, "stop-srv"); + + final AtomicInteger startIdx = new AtomicInteger(initNodes); + + IgniteInternalFuture startFut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + int nodeIdx = startIdx.incrementAndGet(); + + if (rnd.nextInt(3) == 0) { + log.info("Start client: " + nodeIdx); + + client.set(true); + } + else + log.info("Start server: " + nodeIdx); + + startGrid(nodeIdx); + + if (rnd.nextBoolean()) { + log.info("Stop started node: " + nodeIdx); + + stopGrid(nodeIdx); + } + + return null; + } + }, 5, "start-node"); + + stopFut.get(); + startFut.get(); + + checkCaches(); + } + finally { + System.clearProperty(IgniteSystemProperties.IGNITE_EXCHANGE_MERGE_DELAY); + } + } + + /** + * @throws Exception If failed. + */ + public void testMergeStartRandomClientsServers() throws Exception { + for (int iter = 0; iter < 3; iter++) { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + final int srvs = rnd.nextInt(3) + 1; + final int clients = rnd.nextInt(3); + + log.info("Iteration [iter=" + iter + ", srvs=" + srvs + ", clients=" + clients + ']'); + + Ignite srv0 = startGrids(srvs); + + for (int i = 0; i < clients; i++) { + client.set(true); + + startGrid(srvs + i); + } + + final int threads = 8; + + final int initNodes = srvs + clients; + + mergeExchangeWaitVersion(srv0, initNodes + threads); + + final AtomicInteger idx = new AtomicInteger(initNodes); + + IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + int nodeIdx = idx.incrementAndGet(); + + if (rnd.nextInt(3) == 0) { + log.info("Start client: " + nodeIdx); + + client.set(true); + } + else + log.info("Start server: " + nodeIdx); + + startGrid(nodeIdx); + + return null; + } + }, threads, "test-thread"); + + fut.get(); + + checkCaches(); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testMergeStartStopRandomClientsServers() throws Exception { + for (int iter = 0; iter < 3; iter++) { + log.info("Iteration: " + iter); + + final int srvs = 5; + final int clients = 5; + + Ignite srv0 = startGrids(srvs); + + for (int i = 0; i < clients; i++) { + client.set(true); + + startGrid(srvs + i); + } + + final int threads = 8; + + final int initNodes = srvs + clients; + + mergeExchangeWaitVersion(srv0, initNodes + threads); + + final AtomicInteger idx = new AtomicInteger(initNodes); + + final ConcurrentHashSet stopNodes = new ConcurrentHashSet<>(); + + IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + if (rnd.nextBoolean()) { + Integer stopIdx; + + for (;;) { + stopIdx = rnd.nextInt(initNodes - 1) + 1; + + if (stopNodes.add(stopIdx)) + break; + } + + log.info("Stop node: " + stopIdx); + + stopGrid(getTestIgniteInstanceName(stopIdx), true, false); + } + else { + int nodeIdx = idx.incrementAndGet(); + + if (rnd.nextInt(5) == 0) { + log.info("Start client: " + nodeIdx); + + client.set(true); + } + else + log.info("Start server: " + nodeIdx); + + startGrid(nodeIdx); + } + + return null; + } + }, threads, "test-thread"); + + fut.get(); + + checkCaches(); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testConcurrentStartServers() throws Exception { + concurrentStart(false); + } + + /** + * @throws Exception If failed. + */ + public void testConcurrentStartServersAndClients() throws Exception { + concurrentStart(true); + } + + /** + * @param withClients If {@code true} also starts client nodes. + * @throws Exception If failed. + */ + private void concurrentStart(final boolean withClients) throws Exception { + for (int i = 0; i < 5; i++) { + log.info("Iteration: " + i); + + startGrid(0); + + final AtomicInteger idx = new AtomicInteger(1); + + IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + if (withClients) + client.set(ThreadLocalRandom.current().nextBoolean()); + + int nodeIdx = idx.getAndIncrement(); + + Ignite node = startGrid(nodeIdx); + + checkNodeCaches(node, nodeIdx * 1000, 1000); + + return null; + } + }, 10, "start-node"); + + fut.get(); + + checkCaches(); + + startGrid(1000); + + checkCaches(); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testMergeServerAndClientJoin1() throws Exception { + final IgniteEx srv0 = startGrid(0); + + mergeExchangeWaitVersion(srv0, 3); + + IgniteInternalFuture fut1 = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + startGrid(1); + + return null; + } + }, 1, "start-srv"); + + waitForExchangeStart(srv0, 2); + + IgniteInternalFuture fut2 = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + client.set(true); + + startGrid(2); + + return null; + } + }, 1, "start-client"); + + fut1.get(); + fut2.get(); + + checkCaches(); + + checkExchanges(srv0, 1, 3); + checkExchanges(ignite(1), 3); + checkExchanges(ignite(2), 3); + } + + /** + * @throws Exception If failed. + */ + public void testStartCacheOnJoinAndJoinMerge_2_nodes() throws Exception { + startCacheOnJoinAndJoinMerge1(2, false); + } + + /** + * @throws Exception If failed. + */ + public void testStartCacheOnJoinAndJoinMerge_4_nodes() throws Exception { + startCacheOnJoinAndJoinMerge1(4, false); + } + + /** + * @throws Exception If failed. + */ + public void testStartCacheOnJoinAndJoinMerge_WithClients() throws Exception { + startCacheOnJoinAndJoinMerge1(5, true); + } + + /** + * @param nodes Number of nodes to start. + * @param withClients If {@code true} starts both servers and clients. + * @throws Exception If failed. + */ + private void startCacheOnJoinAndJoinMerge1(int nodes, boolean withClients) throws Exception { + cfgCache = false; + + final IgniteEx srv0 = startGrid(0); + + mergeExchangeWaitVersion(srv0, nodes + 1); + + if (withClients) { + clientC = new IgniteClosure() { + @Override public Boolean apply(String nodeName) { + return getTestIgniteInstanceIndex(nodeName) % 2 == 0; + } + }; + } + + cfgCache = true; + + IgniteInternalFuture fut = startGridsAsync(srv0, 1, nodes); + + fut.get(); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testMergeAndHistoryCleanup() throws Exception { + final int histSize = 5; + + String oldHistVal = System.getProperty(IGNITE_EXCHANGE_HISTORY_SIZE); + + System.setProperty(IGNITE_EXCHANGE_HISTORY_SIZE, String.valueOf(histSize)); + + try { + final Ignite srv0 = startGrid(0); + + int topVer = 1; + + for (int i = 0; i < 3; i++) { + mergeExchangeWaitVersion(srv0, topVer + 3); + + startGridsAsync(srv0, topVer, 3).get(); + + topVer += 3; + } + + checkHistorySize(histSize); + + awaitPartitionMapExchange(); + + checkHistorySize(histSize); + + mergeExchangeWaitVersion(srv0, topVer + 2); + + stopGrid(1); + stopGrid(2); + + checkHistorySize(histSize); + + awaitPartitionMapExchange(); + + checkHistorySize(histSize); + } + finally { + if (oldHistVal != null) + System.setProperty(IGNITE_EXCHANGE_HISTORY_SIZE, oldHistVal); + else + System.clearProperty(IGNITE_EXCHANGE_HISTORY_SIZE); + } + } + + /** + * @param histSize History size. + */ + private void checkHistorySize(int histSize) { + List nodes = G.allGrids(); + + assertTrue(nodes.size() > 0); + + for (Ignite node : nodes) { + List exchFuts = + ((IgniteEx)node).context().cache().context().exchange().exchangeFutures(); + + assertTrue("Unexpected size: " + exchFuts.size(), exchFuts.size() > 0 && exchFuts.size() <= histSize); + } + } + + /** + * @throws Exception If failed. + */ + public void testStartCacheOnJoinAndMergeWithFail() throws Exception { + cfgCache = false; + + final Ignite srv0 = startGrids(2); + + mergeExchangeWaitVersion(srv0, 5); + + cfgCache = true; + + IgniteInternalFuture fut = startGridsAsync(srv0, 2, 2); + + stopGrid(1); + + fut.get(); + + checkCaches(); + + checkExchanges(srv0, 1, 2, 3, 5); + checkExchanges(ignite(2), 3, 5); + checkExchanges(ignite(3), 5); + } + + /** + * @throws Exception If failed. + */ + public void testStartCacheOnJoinAndCoordinatorFailed1() throws Exception { + cfgCache = false; + + final Ignite srv0 = startGrids(2); + + mergeExchangeWaitVersion(srv0, 5); + + cfgCache = true; + + IgniteInternalFuture fut = startGridsAsync(srv0, 2, 2); + + stopGrid(0); + + fut.get(); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testStartCacheOnJoinAndCoordinatorFailed2() throws Exception { + cfgCache = false; + + final Ignite srv0 = startGrid(0); + + mergeExchangeWaitVersion(srv0, 3); + + cfgCache = true; + + IgniteInternalFuture fut = startGridsAsync(srv0, 1, 2); + + stopGrid(0); + + fut.get(); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testMergeServersJoin1() throws Exception { + IgniteEx srv0 = startGrid(0); + + mergeExchangeWaitVersion(srv0, 3); + + final AtomicInteger idx = new AtomicInteger(1); + + IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + startGrid(idx.getAndIncrement()); + + return null; + } + }, 2, "start-node"); + + fut.get(); + + checkCaches(); + + checkExchanges(srv0, 1, 3); + checkExchanges(ignite(1), 3); + checkExchanges(ignite(2), 3); + } + + /** + * @throws Exception If failed. + */ + public void testMergeServerJoin1ClientsInTopology() throws Exception { + IgniteEx srv0 = startGrid(0); + + client.set(true); + + startGrid(1); + + client.set(true); + + startGrid(2); + + mergeExchangeWaitVersion(srv0, 5); + + final AtomicInteger idx = new AtomicInteger(3); + + IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + startGrid(idx.getAndIncrement()); + + return null; + } + }, 2, "start-node"); + + fut.get(); + + checkCaches(); + + checkExchanges(srv0, 1, 2, 3, 5); + checkExchanges(ignite(1), 2, 3, 5); + checkExchanges(ignite(2), 3, 5); + checkExchanges(ignite(3), 5); + checkExchanges(ignite(4), 5); + } + + /** + * @throws Exception If failed. + */ + public void testMergeAndNewCoordinator() throws Exception { + final Ignite srv0 = startGrids(3); + + mergeExchangeWaitVersion(srv0, 6); + + IgniteInternalFuture fut = startGridsAsync(srv0, 3, 3); + + fut.get(); + + checkCaches(); + + stopGrid(0); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testMergeServersFail1_1() throws Exception { + mergeServersFail1(false); + } + + /** + * @throws Exception If failed. + */ + public void testMergeServersFail1_2() throws Exception { + mergeServersFail1(true); + } + + /** + * @param waitRebalance Wait for rebalance end before start tested topology change. + * @throws Exception If failed. + */ + private void mergeServersFail1(boolean waitRebalance) throws Exception { + final Ignite srv0 = startGrids(4); + + if (waitRebalance) + awaitPartitionMapExchange(); + + mergeExchangeWaitVersion(srv0, 6); + + stopGrid(getTestIgniteInstanceName(3), true, false); + stopGrid(getTestIgniteInstanceName(2), true, false); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testMergeServersAndClientsFail1() throws Exception { + mergeServersAndClientsFail(false); + } + + /** + * @throws Exception If failed. + */ + public void testMergeServersAndClientsFail2() throws Exception { + mergeServersAndClientsFail(true); + } + + + /** + * @param waitRebalance Wait for rebalance end before start tested topology change. + * @throws Exception If failed. + */ + private void mergeServersAndClientsFail(boolean waitRebalance) throws Exception { + clientC = new IgniteClosure() { + @Override public Boolean apply(String nodeName) { + return nodeName.equals(getTestIgniteInstanceName(2)) || nodeName.equals(getTestIgniteInstanceName(3)); + } + }; + + final Ignite srv0 = startGrids(6); + + if (waitRebalance) + awaitPartitionMapExchange(); + + mergeExchangeWaitVersion(srv0, 10); + + stopGrid(getTestIgniteInstanceName(1), true, false); + stopGrid(getTestIgniteInstanceName(2), true, false); + stopGrid(getTestIgniteInstanceName(3), true, false); + stopGrid(getTestIgniteInstanceName(4), true, false); + + checkAffinity(); + + mergeExchangeWaitVersion(srv0, 12); + + IgniteInternalFuture fut = startGridsAsync(srv0, 6, 2); + + fut.get(); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testJoinExchangeCoordinatorChange_NoMerge_1() throws Exception { + for (CoordinatorChangeMode mode : CoordinatorChangeMode.values()) { + exchangeCoordinatorChangeNoMerge(4, true, mode); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testJoinExchangeCoordinatorChange_NoMerge_2() throws Exception { + for (CoordinatorChangeMode mode : CoordinatorChangeMode.values()) { + exchangeCoordinatorChangeNoMerge(8, true, mode); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testFailExchangeCoordinatorChange_NoMerge_1() throws Exception { + for (CoordinatorChangeMode mode : CoordinatorChangeMode.values()) { + exchangeCoordinatorChangeNoMerge(5, false, mode); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testFailExchangeCoordinatorChange_NoMerge_2() throws Exception { + for (CoordinatorChangeMode mode : CoordinatorChangeMode.values()) { + exchangeCoordinatorChangeNoMerge(8, false, mode); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testMergeJoinExchangesCoordinatorChange1_4_servers() throws Exception { + for (CoordinatorChangeMode mode : CoordinatorChangeMode.values()) { + mergeJoinExchangesCoordinatorChange1(4, mode); + + stopAllGrids(); + } + } + + /** + * @throws Exception If failed. + */ + public void testMergeJoinExchangesCoordinatorChange1_8_servers() throws Exception { + for (CoordinatorChangeMode mode : CoordinatorChangeMode.values()) { + mergeJoinExchangesCoordinatorChange1(8, mode); + + stopAllGrids(); + } + } + + /** + * @param srvs Number of server nodes. + * @param mode Test mode. + * @throws Exception If failed. + */ + private void mergeJoinExchangesCoordinatorChange1(final int srvs, CoordinatorChangeMode mode) + throws Exception + { + log.info("Test mergeJoinExchangesCoordinatorChange1 [srvs=" + srvs + ", mode=" + mode + ']'); + + testSpi = true; + + Ignite srv0 = startGrids(srvs); + + mergeExchangeWaitVersion(srv0, 6); + + CountDownLatch latch = blockExchangeFinish(srvs, mode); + + IgniteInternalFuture fut = startGridsAsync(srv0, srvs, 2); + + if (latch != null && !latch.await(WAIT_SECONDS, TimeUnit.SECONDS)) + fail("Failed to wait for expected messages."); + + stopGrid(getTestIgniteInstanceName(0), true, false); + + fut.get(); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testMergeJoinExchangesCoordinatorChange2_4_servers() throws Exception { + mergeJoinExchangeCoordinatorChange2(4, 2, F.asList(1, 2, 3, 4), F.asList(5)); + + stopAllGrids(); + + mergeJoinExchangeCoordinatorChange2(4, 2, F.asList(1, 2, 3, 5), F.asList(4)); + } + + /** + * @param srvs Number of server nodes. + * @param startNodes Number of nodes to start. + * @param blockNodes Nodes which do not receive messages. + * @param waitMsgNodes Nodes which should receive messages. + * @throws Exception If failed. + */ + private void mergeJoinExchangeCoordinatorChange2(final int srvs, + final int startNodes, + List blockNodes, + List waitMsgNodes) throws Exception + { + testSpi = true; + + Ignite srv0 = startGrids(srvs); + + mergeExchangeWaitVersion(srv0, srvs + startNodes); + + CountDownLatch latch = blockExchangeFinish(srv0, srvs + 1, blockNodes, waitMsgNodes); + + IgniteInternalFuture fut = startGridsAsync(srv0, srvs, startNodes); + + if (latch != null && !latch.await(WAIT_SECONDS, TimeUnit.SECONDS)) + fail("Failed to wait for expected messages."); + + stopGrid(getTestIgniteInstanceName(0), true, false); + + fut.get(); + + checkCaches(); + } + + /** + * @throws Exception If failed. + */ + public void testMergeExchangeCoordinatorChange4() throws Exception { + testSpi = true; + + final int srvs = 4; + + Ignite srv0 = startGrids(srvs); + + mergeExchangeWaitVersion(srv0, 6); + + final AtomicInteger idx = new AtomicInteger(srvs); + + CountDownLatch latch = blockExchangeFinish(srv0, 5, F.asList(1, 2, 3, 4), F.asList(5)); + + IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable() { + @Override public Void call() throws Exception { + startGrid(idx.getAndIncrement()); + + return null; + } + }, 2, "start-node"); + + if (latch != null && !latch.await(WAIT_SECONDS, TimeUnit.SECONDS)) + fail("Failed to wait for expected messages."); + + stopGrid(getTestIgniteInstanceName(0), true, false); + + fut.get(); + + checkCaches(); + } + + /** + * @param srvs Number of servers. + * @param join If {@code true} starts new node, otherwise stops node. + * @param mode Tested scenario. + * @throws Exception If failed. + */ + private void exchangeCoordinatorChangeNoMerge(int srvs, final boolean join, CoordinatorChangeMode mode) throws Exception { + log.info("Test mergeJoinExchangeCoordinatorChange [nodes=" + srvs + ", mode=" + mode + ']'); + + testSpi = true; + + final int nodes = srvs; + + startGrids(nodes); + + CountDownLatch latch = blockExchangeFinish(srvs, mode); + + IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() { + @Override public Object call() throws Exception { + if (join) + startGrid(nodes); + else + stopGrid(nodes - 1); + + return null; + } + }); + + waitForExchangeStart(ignite(0), nodes + 1); + + if (latch != null && !latch.await(WAIT_SECONDS, TimeUnit.SECONDS)) + fail("Failed to wait for expected messages."); + + stopGrid(0); + + fut.get(); + + checkCaches(); + } + + /** + * @param srvs Number of server nodes. + * @param mode Test scenario. + * @return Awaited state latch. + * @throws Exception If failed. + */ + private CountDownLatch blockExchangeFinish(int srvs, CoordinatorChangeMode mode) throws Exception { + Ignite crd = ignite(0); + + long topVer = srvs + 1; + + switch (mode) { + case NOBODY_RCVD: { + blockExchangeFinish(crd, topVer); + + break; + } + + case NEW_CRD_RCDV: { + List finishNodes = F.asList(1); + + return blockExchangeFinish(crd, topVer, blockNodes(srvs, finishNodes), finishNodes); + } + + case NON_CRD_RCVD: { + assert srvs > 2 : srvs; + + List finishNodes = F.asList(2); + + return blockExchangeFinish(crd, topVer, blockNodes(srvs, finishNodes), finishNodes); + } + + default: + fail(); + } + + return null; + } + + /** + * @param srvs Number of servers. + * @param waitNodes Nodes which should receive message. + * @return Blocked nodes indexes. + */ + private List blockNodes(int srvs, List waitNodes) { + List block = new ArrayList<>(); + + for (int i = 0; i < srvs + 1; i++) { + if (!waitNodes.contains(i)) + block.add(i); + } + + return block; + } + + /** + * @param crd Exchange coordinator. + * @param topVer Exchange topology version. + */ + private void blockExchangeFinish(Ignite crd, long topVer) { + final AffinityTopologyVersion topVer0 = new AffinityTopologyVersion(topVer); + + TestRecordingCommunicationSpi.spi(crd).blockMessages(new IgniteBiPredicate() { + @Override public boolean apply(ClusterNode node, Message msg) { + if (msg instanceof GridDhtPartitionsFullMessage) { + GridDhtPartitionsFullMessage msg0 = (GridDhtPartitionsFullMessage)msg; + + return msg0.exchangeId() != null && msg0.exchangeId().topologyVersion().equals(topVer0); + } + + return false; + } + }); + } + + /** + * @param crd Exchange coordinator. + * @param topVer Exchange topology version. + * @param blockNodes Nodes which do not receive messages. + * @param waitMsgNodes Nodes which should receive messages. + * @return Awaited state latch. + */ + private CountDownLatch blockExchangeFinish(Ignite crd, + long topVer, + final List blockNodes, + final List waitMsgNodes) + { + log.info("blockExchangeFinish [crd=" + crd.cluster().localNode().id() + + ", block=" + blockNodes + + ", wait=" + waitMsgNodes + ']'); + + final AffinityTopologyVersion topVer0 = new AffinityTopologyVersion(topVer); + + final CountDownLatch latch = new CountDownLatch(waitMsgNodes.size()); + + TestRecordingCommunicationSpi.spi(crd).blockMessages(new IgniteBiPredicate() { + @Override public boolean apply(ClusterNode node, Message msg) { + if (msg instanceof GridDhtPartitionsFullMessage) { + GridDhtPartitionsFullMessage msg0 = (GridDhtPartitionsFullMessage)msg; + + if (msg0.exchangeId() == null || msg0.exchangeId().topologyVersion().compareTo(topVer0) < 0) + return false; + + String name = node.attribute(IgniteNodeAttributes.ATTR_IGNITE_INSTANCE_NAME); + + assert name != null : node; + + for (Integer idx : blockNodes) { + if (name.equals(getTestIgniteInstanceName(idx))) + return true; + } + + for (Integer idx : waitMsgNodes) { + if (name.equals(getTestIgniteInstanceName(idx))) { + log.info("Coordinators sends awaited message [node=" + node.id() + ']'); + + latch.countDown(); + } + } + } + + return false; + } + }); + + return latch; + } + + /** + * @param node Node. + * @param topVer Ready exchange version to wait for before trying to merge exchanges. + */ + private void mergeExchangeWaitVersion(Ignite node, long topVer) { + ((IgniteKernal)node).context().cache().context().exchange().mergeExchangesTestWaitVersion( + new AffinityTopologyVersion(topVer, 0)); + } + + /** + * @throws Exception If failed. + */ + private void checkCaches() throws Exception { + checkAffinity(); + + checkCaches0(); + + checkAffinity(); + + awaitPartitionMapExchange(); + + checkCaches0(); + } + + /** + * @throws Exception If failed. + */ + private void checkCaches0() throws Exception { + List nodes = G.allGrids(); + + assertTrue(nodes.size() > 0); + + for (Ignite node : nodes) + checkNodeCaches(node); + } + + /** + * @throws Exception If failed. + */ + private void checkAffinity() throws Exception { + List nodes = G.allGrids(); + + ClusterNode crdNode = null; + + for (Ignite node : nodes) { + ClusterNode locNode = node.cluster().localNode(); + + if (crdNode == null || locNode.order() < crdNode.order()) + crdNode = locNode; + } + + AffinityTopologyVersion topVer = ((IgniteKernal)grid(crdNode)). + context().cache().context().exchange().readyAffinityVersion(); + + Map>> affMap = new HashMap<>(); + + for (Ignite node : nodes) { + IgniteKernal node0 = (IgniteKernal)node; + + for (IgniteInternalCache cache : node0.context().cache().caches()) { + List> aff = affMap.get(cache.name()); + List> aff0 = cache.context().affinity().assignments(topVer); + + if (aff != null) + assertEquals(aff, aff0); + else + affMap.put(cache.name(), aff0); + } + } + } + + /** + * @param node Node. + * @param startKey Start key. + * @param keyRange Keys range. + */ + private void checkNodeCaches(Ignite node, int startKey, int keyRange) { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + for (String cacheName : cacheNames) { + String err = "Invalid value [node=" + node.name() + + ", client=" + node.configuration().isClientMode() + + ", order=" + node.cluster().localNode().order() + + ", cache=" + cacheName + ']'; + + IgniteCache cache = node.cache(cacheName); + + for (int i = 0; i < 10; i++) { + Integer key = rnd.nextInt(keyRange) + startKey; + + cache.put(key, i); + + Object val = cache.get(key); + + assertEquals(err, i, val); + } + } + } + + /** + * @param node Node. + * @throws Exception If failed. + */ + private void checkNodeCaches(final Ignite node) throws Exception { + log.info("Check node caches [node=" + node.name() + ']'); + + List> futs = new ArrayList<>(); + + for (final String cacheName : cacheNames) { + final IgniteCache cache = node.cache(cacheName); + + futs.add(executor.submit(new Runnable() { + @Override public void run() { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + assertNotNull("No cache [node=" + node.name() + + ", client=" + node.configuration().isClientMode() + + ", order=" + node.cluster().localNode().order() + + ", cache=" + cacheName + ']', cache); + + String err = "Invalid value [node=" + node.name() + + ", client=" + node.configuration().isClientMode() + + ", order=" + node.cluster().localNode().order() + + ", cache=" + cacheName + ']'; + + for (int i = 0; i < 5; i++) { + Integer key = rnd.nextInt(20_000); + + cache.put(key, i); + + Object val = cache.get(key); + + assertEquals(err, i, val); + } + + for (int i = 0; i < 5; i++) { + Map map = new TreeMap<>(); + + for (int j = 0; j < 10; j++) { + Integer key = rnd.nextInt(20_000); + + map.put(key, i); + } + + cache.putAll(map); + + Map res = cache.getAll(map.keySet()); + + for (Map.Entry e : map.entrySet()) + assertEquals(err, e.getValue(), res.get(e.getKey())); + } + + if (cache.getConfiguration(CacheConfiguration.class).getAtomicityMode() == TRANSACTIONAL) { + for (TransactionConcurrency concurrency : TransactionConcurrency.values()) { + for (TransactionIsolation isolation : TransactionIsolation.values()) + checkNodeCaches(err, node, cache, concurrency, isolation); + } + } + } + })); + } + + for (Future fut : futs) + fut.get(); + } + + /** + * @param err Error message. + * @param node Node. + * @param cache Cache. + * @param concurrency Transaction concurrency. + * @param isolation Transaction isolation. + */ + private void checkNodeCaches( + String err, + Ignite node, + IgniteCache cache, + TransactionConcurrency concurrency, + TransactionIsolation isolation) { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + Map map = new HashMap<>(); + + try { + try (Transaction tx = node.transactions().txStart(concurrency, isolation)) { + for (int i = 0; i < 5; i++) { + Integer key = rnd.nextInt(20_000); + + cache.put(key, i); + + Object val = cache.get(key); + + assertEquals(i, val); + + map.put(key, val); + } + + tx.commit(); + } + } + catch (ClusterTopologyException ignore) { + // No-op. + } + + for (Map.Entry e : map.entrySet()) + assertEquals(err, e.getValue(), cache.get(e.getKey())); + } + + /** + * @param node Node. + * @param vers Expected exchange versions. + */ + private void checkExchanges(Ignite node, long... vers) { + IgniteKernal node0 = (IgniteKernal)node; + + List expVers = new ArrayList<>(); + + for (long ver : vers) + expVers.add(new AffinityTopologyVersion(ver)); + + List doneVers = new ArrayList<>(); + + List futs = + node0.context().cache().context().exchange().exchangeFutures(); + + for (int i = futs.size() - 1; i >= 0; i--) { + GridDhtPartitionsExchangeFuture fut = futs.get(i); + + if (fut.exchangeDone() && fut.firstEvent().type() != EVT_DISCOVERY_CUSTOM_EVT) { + AffinityTopologyVersion resVer = fut.topologyVersion(); + + if (resVer != null) + doneVers.add(resVer); + } + } + + assertEquals(expVers, doneVers); + + for (CacheGroupContext grpCtx : node0.context().cache().cacheGroups()) { + for (AffinityTopologyVersion ver : grpCtx.affinity().cachedVersions()) { + if (ver.minorTopologyVersion() > 0) + continue; + + assertTrue("Unexpected version [ver=" + ver + ", exp=" + expVers + ']', + expVers.contains(ver)); + } + } + } + + /** + * @param node Node. + * @param topVer Exchange version. + * @throws Exception If failed. + */ + private void waitForExchangeStart(final Ignite node, final long topVer) throws Exception { + final GridCachePartitionExchangeManager exch = ((IgniteKernal)node).context().cache().context().exchange(); + + boolean wait = GridTestUtils.waitForCondition(new PA() { + @Override public boolean apply() { + return exch.lastTopologyFuture().initialVersion().topologyVersion() >= topVer; + } + }, 15_000); + + assertTrue(wait); + } + + /** + * Sequentially starts nodes so that node name is consistent with node order. + * + * @param node Some existing node. + * @param startIdx Start node index. + * @param cnt Number of nodes. + * @return Start future. + * @throws Exception If failed. + */ + private IgniteInternalFuture startGridsAsync(Ignite node, int startIdx, int cnt) throws Exception { + GridCompoundFuture fut = new GridCompoundFuture(); + + for (int i = 0; i < cnt; i++) { + final CountDownLatch latch = new CountDownLatch(1); + + node.events().localListen(new IgnitePredicate() { + @Override public boolean apply(Event evt) { + log.info("Got event: " + ((DiscoveryEvent)evt).eventNode().id()); + + latch.countDown(); + + return false; + } + }, EventType.EVT_NODE_JOINED); + + final int nodeIdx = startIdx + i; + + IgniteInternalFuture fut0 = GridTestUtils.runAsync(new Callable() { + @Override public Object call() throws Exception { + log.info("Start new node: " + nodeIdx); + + startGrid(nodeIdx); + + return null; + } + }, "start-node-" + nodeIdx); + + if (!latch.await(WAIT_SECONDS, TimeUnit.SECONDS)) + fail(); + + fut.add(fut0); + } + + fut.markInitialized(); + + return fut; + } + + /** + * + */ + enum CoordinatorChangeMode { + /** + * Coordinator failed, did not send full message. + */ + NOBODY_RCVD, + + /** + * Coordinator failed, but new coordinator received full message + * and finished exchange. + */ + NEW_CRD_RCDV, + + /** + * Coordinator failed, but one of servers (not new coordinator) received full message. + */ + NON_CRD_RCVD + } + + /** + * + */ + + static class TestDelayExchangeMessagesSpi extends TestDelayingCommunicationSpi { + /** {@inheritDoc} */ + @Override protected boolean delayMessage(Message msg, GridIoMessage ioMsg) { + if (msg instanceof GridDhtPartitionsAbstractMessage) + return ((GridDhtPartitionsAbstractMessage)msg).exchangeId() != null || (msg instanceof GridDhtPartitionsSingleRequest); + + return false; + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java index 7d8620a210502..695d8a6f5f627 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java @@ -37,6 +37,7 @@ import javax.cache.processor.MutableEntry; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteDataStreamer; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteServices; import org.apache.ignite.IgniteTransactions; @@ -54,6 +55,7 @@ import org.apache.ignite.internal.GridNodeOrderComparator; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.IgniteNodeAttributes; import org.apache.ignite.internal.TestRecordingCommunicationSpi; import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException; import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; @@ -69,6 +71,7 @@ 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.distributed.dht.preloader.GridDhtPartitionsSingleMessage; +import org.apache.ignite.internal.util.future.GridFinishedFuture; import org.apache.ignite.internal.util.lang.GridAbsPredicate; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.G; @@ -99,7 +102,7 @@ import static org.apache.ignite.cache.CacheRebalanceMode.ASYNC; import static org.apache.ignite.cache.CacheRebalanceMode.SYNC; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; -import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_IGNITE_INSTANCE_NAME; +import static org.apache.ignite.internal.processors.cache.ExchangeContext.IGNITE_EXCHANGE_COMPATIBILITY_VER_1; /** * @@ -381,52 +384,59 @@ public void testAffinitySimpleNoCacheOnCoordinator1() throws Exception { * @throws Exception If failed. */ public void testAffinitySimpleNoCacheOnCoordinator2() throws Exception { - cacheC = new IgniteClosure() { - @Override public CacheConfiguration[] apply(String igniteInstanceName) { - if (igniteInstanceName.equals(getTestIgniteInstanceName(1)) || - igniteInstanceName.equals(getTestIgniteInstanceName(2))) - return null; + System.setProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, "true"); - return new CacheConfiguration[]{cacheConfiguration()}; - } - }; + try { + cacheC = new IgniteClosure() { + @Override public CacheConfiguration[] apply(String igniteInstanceName) { + if (igniteInstanceName.equals(getTestIgniteInstanceName(1)) || + igniteInstanceName.equals(getTestIgniteInstanceName(2))) + return null; - cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(1), getTestIgniteInstanceName(2))); + return new CacheConfiguration[]{cacheConfiguration()}; + } + }; - startServer(0, 1); - startServer(1, 2); - startServer(2, 3); - startServer(3, 4); + cacheNodeFilter = new TestCacheNodeExcludingFilter(F.asList(getTestIgniteInstanceName(1), getTestIgniteInstanceName(2))); - for (int i = 0; i < 4; i++) { - TestRecordingCommunicationSpi spi = - (TestRecordingCommunicationSpi)ignite(i).configuration().getCommunicationSpi(); + startServer(0, 1); + startServer(1, 2); + startServer(2, 3); + startServer(3, 4); - // Prevent exchange finish while node0 or node1 is coordinator. - spi.blockMessages(GridDhtPartitionsSingleMessage.class, ignite(0).name()); - spi.blockMessages(GridDhtPartitionsSingleMessage.class, ignite(1).name()); - } + for (int i = 0; i < 4; i++) { + TestRecordingCommunicationSpi spi = + (TestRecordingCommunicationSpi)ignite(i).configuration().getCommunicationSpi(); - stopGrid(0); - stopGrid(1); + // Prevent exchange finish while node0 or node1 is coordinator. + spi.blockMessages(GridDhtPartitionsSingleMessage.class, ignite(0).name()); + spi.blockMessages(GridDhtPartitionsSingleMessage.class, ignite(1).name()); + } + + stopGrid(0); + stopGrid(1); - calculateAffinity(5); - calculateAffinity(6); + calculateAffinity(5); + calculateAffinity(6); - checkAffinity(2, topVer(6, 0), true); + checkAffinity(2, topVer(6, 0), true); - assertNull(((IgniteKernal)ignite(2)).context().cache().internalCache(CACHE_NAME1)); - assertNotNull(((IgniteKernal)ignite(3)).context().cache().internalCache(CACHE_NAME1)); + assertNull(((IgniteKernal)ignite(2)).context().cache().internalCache(CACHE_NAME1)); + assertNotNull(((IgniteKernal)ignite(3)).context().cache().internalCache(CACHE_NAME1)); - assertNotNull(ignite(2).cache(CACHE_NAME1)); + assertNotNull(ignite(2).cache(CACHE_NAME1)); - checkAffinity(2, topVer(6, 0), true); + checkAffinity(2, topVer(6, 0), true); - startServer(4, 7); + startServer(4, 7); - checkAffinity(3, topVer(7, 0), false); + checkAffinity(3, topVer(7, 0), false); - checkAffinity(3, topVer(7, 1), true); + checkAffinity(3, topVer(7, 1), true); + } + finally { + System.clearProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1); + } } /** @@ -579,20 +589,41 @@ private void cacheDestroyAndCreate(boolean cacheOnCrd) throws Exception { * * @throws Exception If failed. */ - public void testAffinitySimpleNodeLeave() throws Exception { - startServer(0, 1); + public void testAffinitySimpleNodeLeave1() throws Exception { + affinitySimpleNodeLeave(2); + } - startServer(1, 2); + /** + * Simple test, node leaves. + * + * @throws Exception If failed. + */ + public void testAffinitySimpleNodeLeave2() throws Exception { + affinitySimpleNodeLeave(4); + } - checkAffinity(2, topVer(2, 0), false); + /** + * @param cnt Count of server nodes. + * @throws Exception If failed. + */ + private void affinitySimpleNodeLeave(int cnt) throws Exception { + int topVer = 1; - checkAffinity(2, topVer(2, 1), true); + startServer(topVer - 1, topVer++); - stopNode(1, 3); + for (int i = 0; i < cnt - 1; i++, topVer++) { + startServer(topVer - 1, topVer); - checkAffinity(1, topVer(3, 0), true); + checkAffinity(topVer, topVer(topVer, 0), false); - checkNoExchange(1, topVer(3, 1)); + checkAffinity(topVer, topVer(topVer, 1), true); + } + + stopNode(1, topVer); + + checkAffinity(cnt - 1, topVer(topVer, 0), true); + + checkNoExchange(cnt - 1, topVer(topVer, 1)); awaitPartitionMapExchange(); } @@ -624,38 +655,45 @@ public void testAffinitySimpleNodeLeaveClientAffinity() throws Exception { * @throws Exception If failed. */ public void testNodeLeaveExchangeWaitAffinityMessage() throws Exception { - Ignite ignite0 = startServer(0, 1); + System.setProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, "true"); - startServer(1, 2); + try { + Ignite ignite0 = startServer(0, 1); - startServer(2, 3); + startServer(1, 2); - checkAffinity(3, topVer(3, 1), true); + startServer(2, 3); - checkOrderCounters(3, topVer(3, 1)); + checkAffinity(3, topVer(3, 1), true); - startClient(3, 4); + checkOrderCounters(3, topVer(3, 1)); - checkAffinity(4, topVer(4, 0), true); + startClient(3, 4); - TestTcpDiscoverySpi discoSpi = (TestTcpDiscoverySpi)ignite0.configuration().getDiscoverySpi(); + checkAffinity(4, topVer(4, 0), true); - discoSpi.blockCustomEvent(); + TestTcpDiscoverySpi discoSpi = (TestTcpDiscoverySpi)ignite0.configuration().getDiscoverySpi(); - stopGrid(1); + discoSpi.blockCustomEvent(); - List> futs = affFutures(3, topVer(5, 0)); + stopGrid(1); - U.sleep(1000); + List> futs = affFutures(3, topVer(5, 0)); - for (IgniteInternalFuture fut : futs) - assertFalse(fut.isDone()); + U.sleep(1000); + + for (IgniteInternalFuture fut : futs) + assertFalse(fut.isDone()); - discoSpi.stopBlock(); + discoSpi.stopBlock(); - checkAffinity(3, topVer(5, 0), false); + checkAffinity(3, topVer(5, 0), false); - checkOrderCounters(3, topVer(5, 0)); + checkOrderCounters(3, topVer(5, 0)); + } + finally { + System.clearProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1); + } } /** @@ -1021,6 +1059,222 @@ public void testDelayAssignmentCoordinatorLeave2() throws Exception { checkAffinity(2, topVer(4, 1), true); } + /** + * @throws Exception If failed. + */ + public void testBlockedFinishMsg1() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(4, 3, false, 2); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg2() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(4, 3, false); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg3() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(4, 3, false, 1); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg4() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(5, 3, false); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg5() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(5, 3, false, 1); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg6() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(5, 3, false, 2); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg7() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(5, 3, false, 2, 4); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg8() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(6, 3, false, 2, 4); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsg9() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(5, 1, false, 4); + } + + /** + * + * @throws Exception If failed. + */ + public void testBlockedFinishMsgForClient() throws Exception { + doTestCoordLeaveBlockedFinishExchangeMessage(5, 1, true, 4); + } + + /** + * Coordinator leaves without sending all {@link GridDhtPartitionsFullMessage} messages, + * exchange must be completed. + * + * @param cnt Number of nodes. + * @param stopId Node to stop. + * @param lastClient {@code True} if last started node is client. + * @param blockedIds Nodes not receiving exchange finish message. + * @throws Exception If failed. + */ + private void doTestCoordLeaveBlockedFinishExchangeMessage(int cnt, + int stopId, + boolean lastClient, + int... blockedIds) throws Exception + { + int ord = 1; + + for (int i = 0; i < cnt; i++) { + if (i == cnt - 1 && lastClient) + startClient(ord - 1, ord++); + else + startServer(ord - 1, ord++); + } + + awaitPartitionMapExchange(); + + TestRecordingCommunicationSpi spi0 = TestRecordingCommunicationSpi.spi(grid(0)); + + final Set blocked = new HashSet<>(); + + for (int id : blockedIds) { + String name = grid(id).name(); + + blocked.add(name); + } + + spi0.blockMessages(new IgniteBiPredicate() { + @Override public boolean apply(ClusterNode node, Message msg) { + return blocked.contains(node.attribute(IgniteNodeAttributes.ATTR_IGNITE_INSTANCE_NAME)) + && (msg instanceof GridDhtPartitionsFullMessage) + && (((GridDhtPartitionsFullMessage)msg).exchangeId() != null); + } + }); + + checkAffinity(cnt, topVer(ord - 1, 1), true); + + stopNode(stopId, ord); + + AffinityTopologyVersion topVer = topVer(ord, 0); + + List> futs = new ArrayList<>(cnt); + + List grids = G.allGrids(); + + for (Ignite ignite : grids) + futs.add(affinityReadyFuture(topVer, ignite)); + + assertEquals(futs.size(), grids.size()); + + for (int i = 0; i < futs.size(); i++) { + final IgniteInternalFuture fut = futs.get(i); + + Ignite ignite = grids.get(i); + + if (!blocked.contains(ignite.name())) { + GridTestUtils.waitForCondition(new GridAbsPredicate() { + @Override public boolean apply() { + return fut.isDone(); + } + }, 5000); + + assertTrue(ignite.name(), fut.isDone()); + } + else + assertFalse(ignite.name(), fut.isDone()); + } + + ord++; + + stopNode(0, ord); // Triggers exchange completion from new coordinator. + + checkAffinity(cnt - 2, topVer(ord - 1, 0), true, false); + + checkAffinity(cnt - 2, topVer(ord, 0), true); + + awaitPartitionMapExchange(); + } + + /** + * Assignment is delayed, coordinator leaves, nodes must complete exchange with same assignments. + * + * @throws Exception If failed. + */ + public void testCoordinatorLeaveAfterNodeLeavesDelayAssignment() throws Exception { + Ignite ignite0 = startServer(0, 1); + + startServer(1, 2); + + Ignite ignite2 = startServer(2, 3); + + Ignite ignite3 = startServer(3, 4); + + TestRecordingCommunicationSpi spi0 = + (TestRecordingCommunicationSpi) ignite0.configuration().getCommunicationSpi(), spi2, spi3; + + // Prevent exchange completion. + spi0.blockMessages(GridDhtPartitionsFullMessage.class, ignite2.name()); + + // Block rebalance. + blockSupplySend(spi0, CACHE_NAME1); + blockSupplySend((spi2 = TestRecordingCommunicationSpi.spi(ignite2)), CACHE_NAME1); + blockSupplySend((spi3 = TestRecordingCommunicationSpi.spi(ignite3)), CACHE_NAME1); + + stopNode(1, 5); + + AffinityTopologyVersion topVer = topVer(5, 0); + + IgniteInternalFuture fut0 = affinityReadyFuture(topVer, ignite0); + IgniteInternalFuture fut2 = affinityReadyFuture(topVer, ignite2); + IgniteInternalFuture fut3 = affinityReadyFuture(topVer, ignite3); + + U.sleep(1_000); + + assertTrue(fut0.isDone()); + assertFalse(fut2.isDone()); + assertTrue(fut3.isDone()); + + // Finish rebalance on ignite3. + spi2.stopBlock(true); + + stopNode(0, 6); + + spi3.stopBlock(true); + + checkAffinity(2, topVer, false); + } + /** * Coordinator leaves during node leave exchange. * @@ -1044,39 +1298,46 @@ public void testNodeLeftExchangeCoordinatorLeave2() throws Exception { * @throws Exception If failed. */ private void nodeLeftExchangeCoordinatorLeave(int nodes) throws Exception { - assert nodes > 2 : nodes; + System.setProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, "true"); - long topVer = 0; + try { + assert nodes > 2 : nodes; - for (int i = 0; i < nodes; i++) - startServer(i, ++topVer); + long topVer = 0; - Ignite ignite1 = grid(1); + for (int i = 0; i < nodes; i++) + startServer(i, ++topVer); - checkAffinity(nodes, topVer(nodes, 1), true); + Ignite ignite1 = grid(1); - TestRecordingCommunicationSpi spi1 = - (TestRecordingCommunicationSpi)ignite1.configuration().getCommunicationSpi(); + checkAffinity(nodes, topVer(nodes, 1), true); - // Prevent exchange finish while node0 is coordinator. - spi1.blockMessages(GridDhtPartitionsSingleMessage.class, ignite(0).name()); + TestRecordingCommunicationSpi spi1 = + (TestRecordingCommunicationSpi)ignite1.configuration().getCommunicationSpi(); - stopNode(2, ++topVer); // New exchange started. + // Prevent exchange finish while node0 is coordinator. + spi1.blockMessages(GridDhtPartitionsSingleMessage.class, ignite(0).name()); - stopGrid(0); // Stop coordinator while exchange in progress. + stopNode(2, ++topVer); // New exchange started. - Map>> aff = checkAffinity(nodes - 2, topVer(topVer, 0), false); + stopGrid(0); // Stop coordinator while exchange in progress. - topVer++; + Map>> aff = checkAffinity(nodes - 2, topVer(topVer, 0), false); - boolean primaryChanged = calculateAffinity(nodes + 2, false, aff); + topVer++; - checkAffinity(nodes - 2, topVer(topVer, 0), !primaryChanged); + boolean primaryChanged = calculateAffinity(nodes + 2, false, aff); - if (primaryChanged) - checkAffinity(nodes - 2, topVer(topVer, 1), true); + checkAffinity(nodes - 2, topVer(topVer, 0), !primaryChanged); - awaitPartitionMapExchange(); + if (primaryChanged) + checkAffinity(nodes - 2, topVer(topVer, 1), true); + + awaitPartitionMapExchange(); + } + finally { + System.clearProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1); + } } /** @@ -1184,69 +1445,76 @@ public void testDelayAssignmentAffinityChanged() throws Exception { * @throws Exception If failed. */ public void testDelayAssignmentAffinityChanged2() throws Exception { - Ignite ignite0 = startServer(0, 1); + System.setProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, "true"); - TestTcpDiscoverySpi discoSpi0 = - (TestTcpDiscoverySpi)ignite0.configuration().getDiscoverySpi(); - TestRecordingCommunicationSpi commSpi0 = - (TestRecordingCommunicationSpi)ignite0.configuration().getCommunicationSpi(); + try { + Ignite ignite0 = startServer(0, 1); - startClient(1, 2); + TestTcpDiscoverySpi discoSpi0 = + (TestTcpDiscoverySpi)ignite0.configuration().getDiscoverySpi(); + TestRecordingCommunicationSpi commSpi0 = + (TestRecordingCommunicationSpi)ignite0.configuration().getCommunicationSpi(); - checkAffinity(2, topVer(2, 0), true); + startClient(1, 2); - startServer(2, 3); + checkAffinity(2, topVer(2, 0), true); - checkAffinity(3, topVer(3, 1), false); + startServer(2, 3); - discoSpi0.blockCustomEvent(); + checkAffinity(3, topVer(3, 1), false); - stopNode(2, 4); + discoSpi0.blockCustomEvent(); - discoSpi0.waitCustomEvent(); + stopNode(2, 4); - blockSupplySend(commSpi0, CACHE_NAME1); + discoSpi0.waitCustomEvent(); - final IgniteInternalFuture startedFuture = multithreadedAsync(new Callable() { - @Override public Void call() throws Exception { - startServer(3, 5); + blockSupplySend(commSpi0, CACHE_NAME1); - return null; - } - }, 1, "server-starter"); + final IgniteInternalFuture startedFuture = multithreadedAsync(new Callable() { + @Override public Void call() throws Exception { + startServer(3, 5); - Thread.sleep(2_000); + return null; + } + }, 1, "server-starter"); - discoSpi0.stopBlock(); + Thread.sleep(2_000); - boolean started = GridTestUtils.waitForCondition(new GridAbsPredicate() { - @Override public boolean apply() { - return startedFuture.isDone(); - } - }, 10_000); + discoSpi0.stopBlock(); - if (!started) - startedFuture.cancel(); + boolean started = GridTestUtils.waitForCondition(new GridAbsPredicate() { + @Override public boolean apply() { + return startedFuture.isDone(); + } + }, 10_000); - assertTrue(started); + if (!started) + startedFuture.cancel(); - checkAffinity(3, topVer(5, 0), false); + assertTrue(started); - checkNoExchange(3, topVer(5, 1)); + checkAffinity(3, topVer(5, 0), false); - commSpi0.stopBlock(); + checkNoExchange(3, topVer(5, 1)); - checkAffinity(3, topVer(5, 1), true); + commSpi0.stopBlock(); + + checkAffinity(3, topVer(5, 1), true); - long nodeJoinTopVer = grid(3).context().discovery().localJoinEvent().topologyVersion(); + long nodeJoinTopVer = grid(3).context().discovery().localJoinEvent().topologyVersion(); - assertEquals(5, nodeJoinTopVer); + assertEquals(5, nodeJoinTopVer); - List exFutures = grid(3).context().cache().context().exchange().exchangeFutures(); + List exFutures = grid(3).context().cache().context().exchange().exchangeFutures(); - for (GridDhtPartitionsExchangeFuture f : exFutures) { - //Shouldn't contains staled futures. - assertTrue(f.topologyVersion().topologyVersion() >= nodeJoinTopVer); + for (GridDhtPartitionsExchangeFuture f : exFutures) { + //Shouldn't contains staled futures. + assertTrue(f.initialVersion().topologyVersion() >= nodeJoinTopVer); + } + } + finally { + System.clearProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1); } } @@ -1890,6 +2158,29 @@ public void testNoForceKeysRequests() throws Exception { assertFalse("Unexpected messages.", fail.get()); } + /** + * @throws Exception If failed. + */ + public void testStreamer1() throws Exception { + cacheC = new IgniteClosure() { + @Override public CacheConfiguration[] apply(String s) { + return null; + } + }; + + startServer(0, 1); + + cacheC = null; + cacheNodeFilter = new TestCacheNodeExcludingFilter(Collections.singletonList(getTestIgniteInstanceName(0))); + + startServer(1, 2); + + IgniteDataStreamer streamer = ignite(0).dataStreamer(CACHE_NAME1); + + streamer.addData(1, 1); + streamer.flush(); + } + /** * @param cache Cache */ @@ -2128,6 +2419,18 @@ private List> affFutures(int expNodes, AffinityTopologyV return futs; } + /** + * @param topVer Topology version. + * @param node Node. + * @return Exchange future. + */ + private IgniteInternalFuture affinityReadyFuture(AffinityTopologyVersion topVer, Ignite node) { + IgniteInternalFuture fut = ((IgniteKernal)node).context().cache().context().exchange(). + affinityReadyFuture(topVer); + + return fut != null ? fut : new GridFinishedFuture<>(); + } + /** * @param major Major version. * @param minor Minor version. @@ -2244,10 +2547,25 @@ private void checkOrderCounters(int expNodes, AffinityTopologyVersion topVer) th * @throws Exception If failed. * @return Affinity assignments. */ - @SuppressWarnings("unchecked") private Map>> checkAffinity(int expNodes, AffinityTopologyVersion topVer, boolean expIdeal) throws Exception { + return checkAffinity(expNodes, topVer, expIdeal, true); + } + + /** + * @param expNodes Expected nodes number. + * @param topVer Topology version. + * @param expIdeal If {@code true} expect ideal affinity assignment. + * @param checkPublicApi {@code True} to check {@link Affinity} API. + * @throws Exception If failed. + * @return Affinity assignments. + */ + @SuppressWarnings("unchecked") + private Map>> checkAffinity(int expNodes, + AffinityTopologyVersion topVer, + boolean expIdeal, + boolean checkPublicApi) throws Exception { List nodes = G.allGrids(); Map>> aff = new HashMap<>(); @@ -2279,35 +2597,37 @@ private Map>> checkAffinity(int expNodes, assertAffinity(ideal, aff2, node, cctx.name(), topVer); - Affinity cacheAff = node.affinity(cctx.name()); + if (checkPublicApi) { + Affinity cacheAff = node.affinity(cctx.name()); - for (int i = 0; i < 10; i++) { - int part = cacheAff.partition(i); + for (int i = 0; i < 10; i++) { + int part = cacheAff.partition(i); - List partNodes = ideal.get(part); + List partNodes = ideal.get(part); - if (partNodes.isEmpty()) { - try { - cacheAff.mapKeyToNode(i); + if (partNodes.isEmpty()) { + try { + cacheAff.mapKeyToNode(i); - fail(); - } - catch (IgniteException ignore) { - // No-op. + fail(); + } + catch (IgniteException ignore) { + // No-op. + } } - } - else { - ClusterNode primary = cacheAff.mapKeyToNode(i); + else { + ClusterNode primary = cacheAff.mapKeyToNode(i); - assertEquals(primary, partNodes.get(0)); + assertEquals(primary, partNodes.get(0)); + } } - } - for (int p = 0; p < ideal.size(); p++) { - List exp = ideal.get(p); - Collection partNodes = cacheAff.mapPartitionToPrimaryAndBackups(p); + for (int p = 0; p < ideal.size(); p++) { + List exp = ideal.get(p); + Collection partNodes = cacheAff.mapPartitionToPrimaryAndBackups(p); - assertEqualsCollections(exp, partNodes); + assertEqualsCollections(exp, partNodes); + } } } } @@ -2488,8 +2808,8 @@ private boolean calculateAffinity(long topVer, for (int i = futs.size() - 1; i >= 0; i--) { GridDhtPartitionsExchangeFuture fut = futs.get(i); - if (fut.topologyVersion().equals(topVer0)) { - evt = fut.discoveryEvent(); + if (fut.initialVersion().equals(topVer0)) { + evt = fut.firstEvent(); break; } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLoadingConcurrentGridStartSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLoadingConcurrentGridStartSelfTest.java index 68e88ce298530..1e046d41f4b05 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLoadingConcurrentGridStartSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLoadingConcurrentGridStartSelfTest.java @@ -29,6 +29,7 @@ import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteDataStreamer; +import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CachePeekMode; import org.apache.ignite.cache.store.CacheStoreAdapter; import org.apache.ignite.cluster.ClusterNode; @@ -48,6 +49,7 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.jetbrains.annotations.Nullable; +import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_IGNITE_INSTANCE_NAME; import static org.apache.ignite.testframework.GridTestUtils.runAsync; @@ -83,6 +85,8 @@ public class CacheLoadingConcurrentGridStartSelfTest extends GridCommonAbstractT CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME); + ccfg.setAtomicityMode(atomicityMode()); + ccfg.setCacheMode(PARTITIONED); ccfg.setBackups(1); @@ -112,6 +116,13 @@ public class CacheLoadingConcurrentGridStartSelfTest extends GridCommonAbstractT return cfg; } + /** + * @return Cache atomicity mode. + */ + protected CacheAtomicityMode atomicityMode() { + return ATOMIC; + } + /** {@inheritDoc} */ @Override protected void afterTest() throws Exception { stopAllGrids(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLockReleaseNodeLeaveTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLockReleaseNodeLeaveTest.java index 947754f1d2a2e..019e03028632e 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLockReleaseNodeLeaveTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLockReleaseNodeLeaveTest.java @@ -29,6 +29,7 @@ import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture; import org.apache.ignite.internal.util.lang.GridAbsPredicate; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; @@ -249,10 +250,10 @@ public void testLockRelease2() throws Exception { // Wait when affinity change exchange start. boolean wait = GridTestUtils.waitForCondition(new GridAbsPredicate() { @Override public boolean apply() { - AffinityTopologyVersion topVer0 = - ((IgniteKernal)ignite0).context().cache().context().exchange().topologyVersion(); + GridDhtTopologyFuture topFut = + ((IgniteKernal)ignite0).context().cache().context().exchange().lastTopologyFuture(); - return topVer.compareTo(topVer0) < 0; + return topFut != null && topVer.compareTo(topFut.initialVersion()) < 0; } }, 10_000); @@ -330,10 +331,10 @@ public void testTxLockRelease2() throws Exception { // Wait when affinity change exchange start. boolean wait = GridTestUtils.waitForCondition(new GridAbsPredicate() { @Override public boolean apply() { - AffinityTopologyVersion topVer0 = - ((IgniteKernal)ignite0).context().cache().context().exchange().topologyVersion(); + GridDhtTopologyFuture topFut = + ((IgniteKernal)ignite0).context().cache().context().exchange().lastTopologyFuture(); - return topVer.compareTo(topVer0) < 0; + return topFut != null && topVer.compareTo(topFut.initialVersion()) < 0; } }, 10_000); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java index c64ed0b6b533a..3b05ac35c537f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionStateTest.java @@ -303,7 +303,10 @@ private void checkNodePartitions(AffinityAssignment assign, nodeParts.addAll(assign.primaryPartitions(clusterNode.id())); nodeParts.addAll(assign.backupPartitions(clusterNode.id())); - log.info("Test state [node=" + clusterNode.id() + ", parts=" + nodeParts.size() + ", state=" + expState + ']'); + log.info("Test state [node=" + clusterNode.id() + + ", cache=" + cacheName + + ", parts=" + nodeParts.size() + + ", state=" + expState + ']'); if (grid(0).context().discovery().cacheAffinityNode(clusterNode, cacheName)) assertFalse(nodeParts.isEmpty()); @@ -324,13 +327,22 @@ private void checkNodePartitions(AffinityAssignment assign, for (int p = 0; p < aff.partitions(); p++) { if (nodeParts.contains(p)) { assertNotNull(partsMap); - assertEquals(expState, partsMap.get(p)); + + GridDhtPartitionState state = partsMap.get(p); + + assertEquals("Unexpected state [checkNode=" + clusterNode.id() + + ", node=" + node.name() + + ", state=" + state + ']', + expState, partsMap.get(p)); } else { if (partsMap != null) { GridDhtPartitionState state = partsMap.get(p); - assertTrue("Unexpected state: " + state, state == null || state == EVICTED); + assertTrue("Unexpected state [checkNode=" + clusterNode.id() + + ", node=" + node.name() + + ", state=" + state + ']', + state == null || state == EVICTED); } } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite.java new file mode 100644 index 0000000000000..f70b195bf3529 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite.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.internal.processors.cache.distributed; + +import org.apache.ignite.cache.CacheAtomicityMode; + +import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; + +/** + * + */ +public class CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite extends + CacheLoadingConcurrentGridStartSelfTestAllowOverwrite { + /** {@inheritDoc} */ + @Override protected CacheAtomicityMode atomicityMode() { + return TRANSACTIONAL; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodeChangingTopologyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodeChangingTopologyTest.java index 9fe41f270c954..c82b4b9b4bba3 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodeChangingTopologyTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodeChangingTopologyTest.java @@ -755,7 +755,8 @@ public void testPessimisticTx2() throws Exception { AffinityTopologyVersion topVer1 = new AffinityTopologyVersion(4, 0); - assertEquals(topVer1, ignite0.context().cache().internalCache(DEFAULT_CACHE_NAME).context().topology().topologyVersion()); + assertEquals(topVer1, + ignite0.context().cache().internalCache(DEFAULT_CACHE_NAME).context().topology().readyTopologyVersion()); TestCommunicationSpi spi = (TestCommunicationSpi)ignite3.configuration().getCommunicationSpi(); @@ -796,7 +797,7 @@ public void testPessimisticTx2() throws Exception { ignite0.context().cache().context().exchange().affinityReadyFuture(topVer2).get(); - assertEquals(topVer2, ignite0.context().cache().internalCache(DEFAULT_CACHE_NAME).context().topology().topologyVersion()); + assertEquals(topVer2, ignite0.context().cache().internalCache(DEFAULT_CACHE_NAME).context().topology().readyTopologyVersion()); GridCacheAffinityManager aff = ignite0.context().cache().internalCache(DEFAULT_CACHE_NAME).context().affinity(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodePartitionsExchangeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodePartitionsExchangeTest.java index 1f850b282109c..7c13e353f8f2e 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodePartitionsExchangeTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheClientNodePartitionsExchangeTest.java @@ -58,6 +58,8 @@ import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import static org.apache.ignite.internal.processors.cache.ExchangeContext.IGNITE_EXCHANGE_COMPATIBILITY_VER_1; + /** * */ @@ -197,31 +199,42 @@ public void testSkipPreload() throws Exception { * @throws Exception If failed. */ public void testPartitionsExchange() throws Exception { - partitionsExchange(); + partitionsExchange(false); + } + + /** + * @throws Exception If failed. + */ + public void testPartitionsExchangeCompatibilityMode() throws Exception { + System.setProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, "true"); + + try { + partitionsExchange(true); + } + finally { + System.clearProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1); + } } /** + * @param compatibilityMode {@code True} to test old exchange protocol. * @throws Exception If failed. */ - private void partitionsExchange() throws Exception { + private void partitionsExchange(boolean compatibilityMode) throws Exception { Ignite ignite0 = startGrid(0); TestCommunicationSpi spi0 = (TestCommunicationSpi)ignite0.configuration().getCommunicationSpi(); Ignite ignite1 = startGrid(1); - boolean lateAff = ignite1.configuration().isLateAffinityAssignment(); - - int minorVer = lateAff ? 1 : 0; - - waitForTopologyUpdate(2, new AffinityTopologyVersion(2, minorVer)); + waitForTopologyUpdate(2, new AffinityTopologyVersion(2, 1)); TestCommunicationSpi spi1 = (TestCommunicationSpi)ignite1.configuration().getCommunicationSpi(); assertEquals(0, spi0.partitionsSingleMessages()); - assertEquals(lateAff ? 2 : 1, spi0.partitionsFullMessages()); + assertEquals(2, spi0.partitionsFullMessages()); - assertEquals(lateAff ? 2 : 1, spi1.partitionsSingleMessages()); + assertEquals(2, spi1.partitionsSingleMessages()); assertEquals(0, spi1.partitionsFullMessages()); spi0.reset(); @@ -281,23 +294,23 @@ private void partitionsExchange() throws Exception { Ignite ignite4 = startGrid(4); - waitForTopologyUpdate(5, new AffinityTopologyVersion(5, lateAff ? 1 : 0)); + waitForTopologyUpdate(5, new AffinityTopologyVersion(5, 1)); TestCommunicationSpi spi4 = (TestCommunicationSpi)ignite4.configuration().getCommunicationSpi(); assertEquals(0, spi0.partitionsSingleMessages()); - assertEquals(lateAff ? 8 : 4, spi0.partitionsFullMessages()); + assertEquals(8, spi0.partitionsFullMessages()); - assertEquals(lateAff ? 2 : 1, spi1.partitionsSingleMessages()); + assertEquals(2, spi1.partitionsSingleMessages()); assertEquals(0, spi1.partitionsFullMessages()); - assertEquals(lateAff ? 2 : 1, spi2.partitionsSingleMessages()); + assertEquals(2, spi2.partitionsSingleMessages()); assertEquals(0, spi2.partitionsFullMessages()); - assertEquals(lateAff ? 2 : 1, spi3.partitionsSingleMessages()); + assertEquals(2, spi3.partitionsSingleMessages()); assertEquals(0, spi3.partitionsFullMessages()); - assertEquals(lateAff ? 2 : 1, spi4.partitionsSingleMessages()); + assertEquals(2, spi4.partitionsSingleMessages()); assertEquals(0, spi4.partitionsFullMessages()); spi0.reset(); @@ -307,9 +320,10 @@ private void partitionsExchange() throws Exception { log.info("Stop server node."); - ignite4.close(); // With late affinity exchange on server leave is completed by discovery message. + ignite4.close(); - if (lateAff) { + if (compatibilityMode) { + // With late affinity old protocol exchange on server leave is completed by discovery message. // With FairAffinityFunction affinity calculation is different, this causes one more topology change. boolean exchangeAfterRebalance = false; @@ -446,9 +460,11 @@ private void waitForTopologyUpdate(int expNodes, final AffinityTopologyVersion t for (IgniteInternalCache cache : kernal.context().cache().caches()) { GridDhtPartitionTopology top = cache.context().topology(); + waitForReadyTopology(top, topVer); + assertEquals("Unexpected topology version [node=" + ignite.name() + ", cache=" + cache.name() + ']', topVer, - top.topologyVersion()); + top.readyTopologyVersion()); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java index cf898c5065f6a..0d0cda4dba96c 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java @@ -362,11 +362,11 @@ private void primaryAndOriginatingNodeFailure(final boolean locBackupKey, Transaction tx = txs.txStart(optimistic ? OPTIMISTIC : PESSIMISTIC, REPEATABLE_READ); - log.info("Put key1: " + key1); + log.info("Put key1 [key1=" + key1 + ", nodes=" + U.nodeIds(aff.mapKeyToPrimaryAndBackups(key1)) + ']'); cache0.put(key1, key1); - log.info("Put key2: " + key2); + log.info("Put key2 [key2=" + key2 + ", nodes=" + U.nodeIds(aff.mapKeyToPrimaryAndBackups(key2)) + ']'); cache0.put(key2, key2); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridCacheAtomicInvalidPartitionHandlingSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridCacheAtomicInvalidPartitionHandlingSelfTest.java index f94e34b33c119..3f2fe8a4b7dd9 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridCacheAtomicInvalidPartitionHandlingSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridCacheAtomicInvalidPartitionHandlingSelfTest.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteDataStreamer; -import org.apache.ignite.IgniteException; import org.apache.ignite.cache.CachePartialUpdateException; import org.apache.ignite.cache.CacheWriteSynchronizationMode; import org.apache.ignite.cache.affinity.Affinity; @@ -37,8 +36,8 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.IgniteInternalFuture; -import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.TestDelayingCommunicationSpi; import org.apache.ignite.internal.managers.communication.GridIoMessage; import org.apache.ignite.internal.processors.cache.GridCacheAdapter; import org.apache.ignite.internal.processors.cache.GridCacheEntryEx; @@ -47,16 +46,12 @@ import org.apache.ignite.internal.util.lang.GridAbsPredicate; 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.plugin.extensions.communication.Message; -import org.apache.ignite.spi.IgniteSpiException; -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.jsr166.ThreadLocalRandom8; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheRebalanceMode.SYNC; @@ -367,32 +362,11 @@ private void checkRestarts(CacheWriteSynchronizationMode writeSync) /** * */ - private static class DelayCommunicationSpi extends TcpCommunicationSpi { + private static class DelayCommunicationSpi extends TestDelayingCommunicationSpi { /** {@inheritDoc} */ - @Override public void sendMessage(ClusterNode node, Message msg, IgniteInClosure ackC) - throws IgniteSpiException { - try { - if (delayMessage((GridIoMessage)msg)) - U.sleep(ThreadLocalRandom8.current().nextInt(250) + 1); - } - catch (IgniteInterruptedCheckedException e) { - throw new IgniteSpiException(e); - } - - super.sendMessage(node, msg, ackC); - } - - /** - * Checks if message should be delayed. - * - * @param msg Message to check. - * @return {@code True} if message should be delayed. - */ - private boolean delayMessage(GridIoMessage msg) { - Object origMsg = msg.message(); - - return delay && - ((origMsg instanceof GridNearAtomicAbstractUpdateRequest) || (origMsg instanceof GridDhtAtomicAbstractUpdateRequest)); + @Override protected boolean delayMessage(Message msg, GridIoMessage ioMsg) { + return delay && ((msg instanceof GridNearAtomicAbstractUpdateRequest) || + (msg instanceof GridDhtAtomicAbstractUpdateRequest)); } } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteChangeGlobalStateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteChangeGlobalStateTest.java index 80bf1fb399dc3..cbf0ec9256b2e 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteChangeGlobalStateTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/IgniteChangeGlobalStateTest.java @@ -573,23 +573,18 @@ public void testActivateAfterFailGetLock() throws Exception { stopPrimary(0); - boolean exc = false; - try { ig3CB.active(true); + + fail("Activation should fail"); } catch (IgniteException e) { - exc = true; - - log.error("stack trace from remote node", e); + log.error("Stack trace from remote node", e); for (Throwable t : e.getSuppressed()) assertTrue(t.getMessage().contains("can't get lock during")); } - if (!exc) - fail(); - assertTrue(!ig1B.active()); assertTrue(!ig2B.active()); assertTrue(!ig3B.active()); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/join/JoinInActiveNodeToActiveCluster.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/join/JoinInActiveNodeToActiveCluster.java index 45f8112d6e3df..d9b0dd4a48327 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/join/JoinInActiveNodeToActiveCluster.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/standbycluster/join/JoinInActiveNodeToActiveCluster.java @@ -294,7 +294,7 @@ public class JoinInActiveNodeToActiveCluster extends AbstractNodeJoinTemplate { Map desc = cacheDescriptors(ig); - Assert.assertEquals(4, desc.size()); + Assert.assertEquals(3, desc.size()); if (!ig.context().discovery().localNode().isClient()) { Assert.assertNotNull(ig.context().cache().cache(cache1)); @@ -307,7 +307,7 @@ public class JoinInActiveNodeToActiveCluster extends AbstractNodeJoinTemplate { Map caches = caches(ig); - Assert.assertEquals(4, caches.size()); + Assert.assertEquals(3, caches.size()); } } }); diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java index 014103dada7ea..307456c355796 100755 --- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java @@ -661,7 +661,7 @@ protected void awaitPartitionMapExchange( "igniteInstanceName=" + g.name() + ", cache=" + cfg.getName() + ", cacheId=" + dht.context().cacheId() + - ", topVer=" + top.topologyVersion() + + ", topVer=" + top.readyTopologyVersion() + ", p=" + p + ", affNodesCnt=" + affNodesCnt + ", ownersCnt=" + ownerNodesCnt + @@ -678,7 +678,7 @@ protected void awaitPartitionMapExchange( "igniteInstanceName=" + g.name() + ", cache=" + cfg.getName() + ", cacheId=" + dht.context().cacheId() + - ", topVer=" + top.topologyVersion() + + ", topVer=" + top.readyTopologyVersion() + ", started=" + dht.context().started() + ", p=" + p + ", readVer=" + readyVer + @@ -699,7 +699,7 @@ protected void awaitPartitionMapExchange( "igniteInstanceName=" + g.name() + ", cache=" + cfg.getName() + ", cacheId=" + dht.context().cacheId() + - ", topVer=" + top.topologyVersion() + + ", topVer=" + top.readyTopologyVersion() + ", p=" + p + ", readVer=" + readyVer + ", locNode=" + g.cluster().localNode() + ']'); @@ -739,7 +739,7 @@ protected void awaitPartitionMapExchange( "igniteInstanceName=" + g.name() + ", cache=" + cfg.getName() + ", cacheId=" + dht.context().cacheId() + - ", topVer=" + top.topologyVersion() + + ", topVer=" + top.readyTopologyVersion() + ", locNode=" + g.cluster().localNode() + ']'); } @@ -766,6 +766,20 @@ protected void awaitPartitionMapExchange( log.info("awaitPartitionMapExchange finished"); } + /** + * @param top Topology. + * @param topVer Version to wait for. + * @throws Exception If failed. + */ + protected final void waitForReadyTopology(final GridDhtPartitionTopology top, final AffinityTopologyVersion topVer) + throws Exception { + GridTestUtils.waitForCondition(new GridAbsPredicate() { + @Override public boolean apply() { + return topVer.compareTo(top.readyTopologyVersion()) <= 0; + } + }, 5000); + } + /** * @param c Cache proxy. */ diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java index 9ed7ee3780f5e..0e1aaec1e104d 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java @@ -51,10 +51,11 @@ import org.apache.ignite.internal.processors.cache.IgniteOnePhaseCommitNearReadersTest; import org.apache.ignite.internal.processors.cache.MemoryPolicyConfigValidationTest; import org.apache.ignite.internal.processors.cache.NonAffinityCoordinatorDynamicStartStopTest; -import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicyInitializationTest; import org.apache.ignite.internal.processors.cache.distributed.CacheLoadingConcurrentGridStartSelfTest; import org.apache.ignite.internal.processors.cache.distributed.CacheLoadingConcurrentGridStartSelfTestAllowOverwrite; import org.apache.ignite.internal.processors.cache.distributed.CacheLockReleaseNodeLeaveTest; +import org.apache.ignite.internal.processors.cache.distributed.CachePartitionStateTest; +import org.apache.ignite.internal.processors.cache.distributed.CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite; import org.apache.ignite.internal.processors.cache.distributed.GridCachePartitionNotLoadedEventSelfTest; import org.apache.ignite.internal.processors.cache.distributed.GridCachePartitionedNearDisabledTxMultiThreadedSelfTest; import org.apache.ignite.internal.processors.cache.distributed.GridCacheTransformEventSelfTest; @@ -136,6 +137,7 @@ import org.apache.ignite.internal.processors.cache.local.GridCacheLocalTxMultiThreadedSelfTest; import org.apache.ignite.internal.processors.cache.local.GridCacheLocalTxSingleThreadedSelfTest; import org.apache.ignite.internal.processors.cache.local.GridCacheLocalTxTimeoutSelfTest; +import org.apache.ignite.internal.processors.cache.persistence.MemoryPolicyInitializationTest; import org.apache.ignite.internal.processors.continuous.IgniteNoCustomEventsOnNodeStart; /** @@ -223,6 +225,7 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(GridCachePartitionedPreloadLifecycleSelfTest.class)); suite.addTest(new TestSuite(CacheLoadingConcurrentGridStartSelfTest.class)); suite.addTest(new TestSuite(CacheLoadingConcurrentGridStartSelfTestAllowOverwrite.class)); + suite.addTest(new TestSuite(CacheTxLoadingConcurrentGridStartSelfTestAllowOverwrite.class)); suite.addTest(new TestSuite(GridCacheDhtPreloadDelayedSelfTest.class)); suite.addTest(new TestSuite(GridPartitionedBackupLoadSelfTest.class)); suite.addTest(new TestSuite(GridCachePartitionedLoadCacheSelfTest.class)); @@ -286,6 +289,8 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(IgniteCacheClearDuringRebalanceTest.class)); + suite.addTest(new TestSuite(CachePartitionStateTest.class)); + return suite; } } diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java index 22890dfb766ad..e3b60fc096601 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java @@ -18,10 +18,10 @@ package org.apache.ignite.testsuites; import junit.framework.TestSuite; +import org.apache.ignite.internal.processors.cache.distributed.CacheExchangeMergeTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeMultiServerTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeTest; import org.apache.ignite.internal.processors.cache.distributed.IgnitePessimisticTxSuspendResumeTest; -import org.apache.ignite.internal.processors.cache.distributed.CachePartitionStateTest; /** * Test suite. @@ -38,7 +38,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteOptimisticTxSuspendResumeMultiServerTest.class); suite.addTestSuite(IgnitePessimisticTxSuspendResumeTest.class); - suite.addTestSuite(CachePartitionStateTest.class); + suite.addTestSuite(CacheExchangeMergeTest.class); return suite; } diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/WaitMapExchangeFinishCallable.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/WaitMapExchangeFinishCallable.java index 83c50bd8176d0..ebd2e4344b622 100644 --- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/WaitMapExchangeFinishCallable.java +++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/WaitMapExchangeFinishCallable.java @@ -52,7 +52,7 @@ public class WaitMapExchangeFinishCallable implements IgniteCallable { for (;;) { boolean success = true; - if (top.topologyVersion().topologyVersion() == ignite.cluster().topologyVersion()) { + if (top.readyTopologyVersion().topologyVersion() == ignite.cluster().topologyVersion()) { for (Map.Entry e : top.partitionMap(true).entrySet()) { for (Map.Entry p : e.getValue().entrySet()) { if (p.getValue() != GridDhtPartitionState.OWNING) { @@ -70,7 +70,7 @@ public class WaitMapExchangeFinishCallable implements IgniteCallable { } } else { - BenchmarkUtils.println("Topology version is different [cache=" + top.topologyVersion() + + BenchmarkUtils.println("Topology version is different [cache=" + top.readyTopologyVersion() + ", cluster=" + ignite.cluster().topologyVersion() + ']'); success = false; From e9f729fcb9ba7b555aed246a834e385ce2c88795 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Mon, 21 Aug 2017 13:49:22 +0300 Subject: [PATCH 073/145] ignite-5872 Replace standard java maps for partition counters with more effective data structures (cherry picked from commit 918c409) --- .../cache/CacheAffinitySharedManager.java | 2 +- .../GridCachePartitionExchangeManager.java | 43 ++--- .../cache/IgniteCacheOffheapManager.java | 2 +- .../cache/IgniteCacheOffheapManagerImpl.java | 7 +- .../dht/GridClientPartitionTopology.java | 58 ++++--- .../dht/GridDhtPartitionTopology.java | 24 ++- .../dht/GridDhtPartitionTopologyImpl.java | 150 +++++++++------- .../CachePartitionFullCountersMap.java | 99 +++++++++++ .../CachePartitionPartialCountersMap.java | 161 ++++++++++++++++++ .../GridDhtPartitionsAbstractMessage.java | 8 - .../GridDhtPartitionsExchangeFuture.java | 36 ++-- .../GridDhtPartitionsFullMessage.java | 13 +- .../GridDhtPartitionsSingleMessage.java | 21 +-- .../GridDhtPartitionsSingleRequest.java | 8 - .../IgniteDhtPartitionCountersMap.java | 14 +- .../persistence/GridCacheOffheapManager.java | 2 +- .../continuous/GridContinuousProcessor.java | 7 +- ...ntinuousQueryFailoverAbstractSelfTest.java | 11 +- .../testsuites/IgniteBasicTestSuite.java | 3 +- 19 files changed, 477 insertions(+), 192 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.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 138cb648da9a4..0e03a29624a0a 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 @@ -459,7 +459,7 @@ void onCacheGroupCreated(CacheGroupContext grp) { if (clientTop != null) { grp.topology().update(grpHolder.affinity().lastVersion(), clientTop.partitionMap(true), - clientTop.updateCounters(false), + clientTop.fullUpdateCounters(), Collections.emptySet()); } 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 1d7fed57bc598..16bd1602940d4 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 @@ -723,23 +723,21 @@ public GridDhtPartitionTopology clientTopology(int grpId) { if (top != null) return top; - Object affKey = null; - CacheGroupDescriptor grpDesc = cctx.affinity().cacheGroups().get(grpId); - if (grpDesc != null) { - CacheConfiguration ccfg = grpDesc.config(); + assert grpDesc != null : grpId; - AffinityFunction aff = ccfg.getAffinity(); + CacheConfiguration ccfg = grpDesc.config(); - affKey = cctx.kernalContext().affinity().similaryAffinityKey(aff, - ccfg.getNodeFilter(), - ccfg.getBackups(), - aff.partitions()); - } + AffinityFunction aff = ccfg.getAffinity(); + + Object affKey = cctx.kernalContext().affinity().similaryAffinityKey(aff, + ccfg.getNodeFilter(), + ccfg.getBackups(), + aff.partitions()); GridClientPartitionTopology old = clientTops.putIfAbsent(grpId, - top = new GridClientPartitionTopology(cctx, grpId, affKey)); + top = new GridClientPartitionTopology(cctx, grpId, aff.partitions(), affKey)); return old != null ? old : top; } @@ -998,6 +996,8 @@ private void sendAllPartitions(Collection nodes) { * finishUnmarshall methods are called). * @param exchId Non-null exchange ID if message is created for exchange. * @param lastVer Last version. + * @param partHistSuppliers Partition history suppliers map. + * @param partsToReload Partitions to reload map. * @return Message. */ public GridDhtPartitionsFullMessage createPartitionsFullMessage( @@ -1041,7 +1041,7 @@ public GridDhtPartitionsFullMessage createPartitionsFullMessage( } if (exchId != null) - m.addPartitionUpdateCounters(grp.groupId(), grp.topology().updateCounters(true)); + m.addPartitionUpdateCounters(grp.groupId(), grp.topology().fullUpdateCounters()); } } @@ -1059,7 +1059,7 @@ public GridDhtPartitionsFullMessage createPartitionsFullMessage( } if (exchId != null) - m.addPartitionUpdateCounters(top.groupId(), top.updateCounters(true)); + m.addPartitionUpdateCounters(top.groupId(), top.fullUpdateCounters()); } return m; @@ -1112,7 +1112,8 @@ private void addFullPartitionsMap(GridDhtPartitionsFullMessage m, private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExchangeId id) { GridDhtPartitionsSingleMessage m = createPartitionsSingleMessage(id, cctx.kernalContext().clientNode(), - false); + false, + null); if (log.isDebugEnabled()) log.debug("Sending local partitions [nodeId=" + node.id() + ", msg=" + m + ']'); @@ -1136,10 +1137,12 @@ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExc * @param sndCounters {@code True} if need send partition update counters. * @return Message. */ - public GridDhtPartitionsSingleMessage createPartitionsSingleMessage(@Nullable GridDhtPartitionExchangeId exchangeId, + public GridDhtPartitionsSingleMessage createPartitionsSingleMessage( + @Nullable GridDhtPartitionExchangeId exchangeId, boolean clientOnlyExchange, - boolean sndCounters) - { + boolean sndCounters, + ExchangeActions exchActions + ) { GridDhtPartitionsSingleMessage m = new GridDhtPartitionsSingleMessage(exchangeId, clientOnlyExchange, cctx.versions().last(), @@ -1148,7 +1151,7 @@ public GridDhtPartitionsSingleMessage createPartitionsSingleMessage(@Nullable Gr Map> dupData = new HashMap<>(); for (CacheGroupContext grp : cctx.cache().cacheGroups()) { - if (!grp.isLocal()) { + if (!grp.isLocal() && (exchActions == null || !exchActions.cacheGroupStopping(grp.groupId()))) { GridDhtPartitionMap locMap = grp.topology().localPartitionMap(); addPartitionMap(m, @@ -1159,7 +1162,7 @@ public GridDhtPartitionsSingleMessage createPartitionsSingleMessage(@Nullable Gr grp.affinity().similarAffinityKey()); if (sndCounters) - m.partitionUpdateCounters(grp.groupId(), grp.topology().updateCounters(true)); + m.partitionUpdateCounters(grp.groupId(), grp.topology().localUpdateCounters(true)); } } @@ -1177,7 +1180,7 @@ public GridDhtPartitionsSingleMessage createPartitionsSingleMessage(@Nullable Gr top.similarAffinityKey()); if (sndCounters) - m.partitionUpdateCounters(top.groupId(), top.updateCounters(true)); + m.partitionUpdateCounters(top.groupId(), top.localUpdateCounters(true)); } return m; 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 001848e60c244..4531802f56db2 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 @@ -414,7 +414,7 @@ interface CacheDataStore { /** * @return Initial update counter. */ - public Long initialUpdateCounter(); + public long initialUpdateCounter(); /** * @param cctx Cache context. 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 ba6c89d5f55e0..9e48d45c8dd3c 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 @@ -1054,8 +1054,8 @@ protected class CacheDataStoreImpl implements CacheDataStore { /** */ private final ConcurrentMap cacheSizes = new ConcurrentHashMap<>(); - /** Initialized update counter. */ - protected Long initCntr = 0L; + /** Initial update counter. */ + protected long initCntr; /** * @param partId Partition number. @@ -1600,7 +1600,7 @@ private void finishRemove(GridCacheContext cctx, KeyCacheObject key, @Nullable C } /** {@inheritDoc} */ - @Override public Long initialUpdateCounter() { + @Override public long initialUpdateCounter() { return initCntr; } @@ -1629,7 +1629,6 @@ private void finishRemove(GridCacheContext cctx, KeyCacheObject key, @Nullable C * @param key Key. * @param oldVal Old value. * @param newVal New value. - * @throws IgniteCheckedException If failed. */ private void updateIgfsMetrics( GridCacheContext cctx, 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 b6db975b2ca12..a8dda6823eb6f 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 @@ -39,6 +39,8 @@ import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache; import org.apache.ignite.internal.processors.cache.ExchangeDiscoveryEvents; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionFullCountersMap; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap; @@ -48,7 +50,6 @@ import org.apache.ignite.internal.util.GridPartitionStateMap; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.Nullable; @@ -72,9 +73,6 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology { /** Flag to control amount of output for full map. */ private static final boolean FULL_MAP_DEBUG = false; - /** */ - private static final Long ZERO = 0L; - /** Cache shared context. */ private GridCacheSharedContext cctx; @@ -109,7 +107,7 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); /** Partition update counters. */ - private Map> cntrMap = new HashMap<>(); + private CachePartitionFullCountersMap cntrMap; /** */ private final Object similarAffKey; @@ -120,11 +118,13 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology { /** * @param cctx Context. * @param grpId Group ID. + * @param parts Number of partitions in the group. * @param similarAffKey Key to find caches with similar affinity. */ public GridClientPartitionTopology( GridCacheSharedContext cctx, int grpId, + int parts, Object similarAffKey ) { this.cctx = cctx; @@ -138,6 +138,8 @@ public GridClientPartitionTopology( node2part = new GridDhtPartitionFullMap(cctx.localNode().id(), cctx.localNode().order(), updateSeq.get()); + + cntrMap = new CachePartitionFullCountersMap(parts); } /** @@ -644,7 +646,7 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD @Override public boolean update( @Nullable AffinityTopologyVersion exchangeVer, GridDhtPartitionFullMap partMap, - Map> cntrMap, + @Nullable CachePartitionFullCountersMap cntrMap, Set partsToReload ) { if (log.isDebugEnabled()) @@ -739,7 +741,7 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } if (cntrMap != null) - this.cntrMap = new HashMap<>(cntrMap); + this.cntrMap = new CachePartitionFullCountersMap(cntrMap); consistencyCheck(); @@ -754,17 +756,22 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } /** {@inheritDoc} */ - @Override public void applyUpdateCounters(Map> cntrMap) { + @Override public void collectUpdateCounters(CachePartitionPartialCountersMap cntrMap) { assert cntrMap != null; lock.writeLock().lock(); try { - for (Map.Entry> e : cntrMap.entrySet()) { - T2 cntr = this.cntrMap.get(e.getKey()); + for (int i = 0; i < cntrMap.size(); i++) { + int pId = cntrMap.partitionAt(i); - if (cntr == null || cntr.get2() < e.getValue().get2()) - this.cntrMap.put(e.getKey(), e.getValue()); + long initialUpdateCntr = cntrMap.initialUpdateCounterAt(i); + long updateCntr = cntrMap.updateCounterAt(i); + + if (this.cntrMap.updateCounter(pId) < updateCntr) { + this.cntrMap.initialUpdateCounter(pId, initialUpdateCntr); + this.cntrMap.updateCounter(pId, updateCntr); + } } } finally { @@ -772,6 +779,11 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } } + /** {@inheritDoc} */ + @Override public void applyUpdateCounters() { + // No-op on client topology. + } + /** * Method checks is new partition map more stale than current partition map * New partition map is stale if topology version or update sequence are less than of current map @@ -1092,28 +1104,22 @@ else if (owners.contains(e.getKey())) } /** {@inheritDoc} */ - @Override public Map> updateCounters(boolean skipZeros) { + @Override public CachePartitionFullCountersMap fullUpdateCounters() { lock.readLock().lock(); try { - if (skipZeros) { - Map> res = U.newHashMap(cntrMap.size()); - - for (Map.Entry> e : cntrMap.entrySet()) { - if (!e.getValue().equals(ZERO)) - res.put(e.getKey(), e.getValue()); - } - - return res; - } - else - return new HashMap<>(cntrMap); -} + return new CachePartitionFullCountersMap(cntrMap); + } finally { lock.readLock().unlock(); } } + /** {@inheritDoc} */ + @Override public CachePartitionPartialCountersMap localUpdateCounters(boolean skipZeros) { + return CachePartitionPartialCountersMap.EMPTY; + } + /** {@inheritDoc} */ @Override public boolean rebalanceFinished(AffinityTopologyVersion topVer) { assert false : "Should not be called on non-affinity node"; 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 f6e21a0ebcadb..f6b142bba62fe 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 @@ -19,7 +19,6 @@ import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.ignite.IgniteCheckedException; @@ -29,12 +28,13 @@ import org.apache.ignite.internal.managers.discovery.DiscoCache; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionFullCountersMap; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId; 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.util.tostring.GridToStringExclude; -import org.apache.ignite.internal.util.typedef.T2; import org.jetbrains.annotations.Nullable; /** @@ -257,12 +257,13 @@ public void initPartitionsWhenAffinityReady(AffinityTopologyVersion affVer, Grid * means full map received is not related to exchange * @param partMap Update partition map. * @param cntrMap Partition update counters. + * @param partsToReload Set of partitions that need to be reloaded. * @return {@code True} if local state was changed. */ public boolean update( @Nullable AffinityTopologyVersion exchangeResVer, GridDhtPartitionFullMap partMap, - @Nullable Map> cntrMap, + @Nullable CachePartitionFullCountersMap cntrMap, Set partsToReload); /** @@ -276,9 +277,16 @@ public boolean update(@Nullable GridDhtPartitionExchangeId exchId, boolean force); /** + * Collects update counters collected during exchange. Called on coordinator. + * * @param cntrMap Counters map. */ - public void applyUpdateCounters(Map> cntrMap); + public void collectUpdateCounters(CachePartitionPartialCountersMap cntrMap); + + /** + * Applies update counters collected during exchange on coordinator. Called on coordinator. + */ + public void applyUpdateCounters(); /** * Checks if there is at least one owner for each partition in the cache topology. @@ -305,10 +313,14 @@ public boolean update(@Nullable GridDhtPartitionExchangeId exchId, public Collection lostPartitions(); /** - * @param skipZeros If {@code true} then filters out zero counters. * @return Partition update counters. */ - public Map> updateCounters(boolean skipZeros); + public CachePartitionFullCountersMap fullUpdateCounters(); + + /** + * @return Partition update counters. + */ + public CachePartitionPartialCountersMap localUpdateCounters(boolean skipZeros); /** * @param part Partition to own. 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 bb94da38448b7..f9eafcc1b742f 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 @@ -43,6 +43,8 @@ import org.apache.ignite.internal.processors.cache.CacheGroupContext; import org.apache.ignite.internal.processors.cache.ExchangeDiscoveryEvents; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionFullCountersMap; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap; @@ -53,7 +55,6 @@ import org.apache.ignite.internal.util.StripedCompositeReadWriteLock; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.T2; 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.LT; @@ -131,7 +132,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology { private final StripedCompositeReadWriteLock lock = new StripedCompositeReadWriteLock(16); /** Partition update counter. */ - private Map> cntrMap = new HashMap<>(); + private final CachePartitionFullCountersMap cntrMap; /** */ private volatile AffinityTopologyVersion rebalancedTopVer = AffinityTopologyVersion.NONE; @@ -140,8 +141,10 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology { * @param ctx Cache shared context. * @param grp Cache group. */ - public GridDhtPartitionTopologyImpl(GridCacheSharedContext ctx, - CacheGroupContext grp) { + public GridDhtPartitionTopologyImpl( + GridCacheSharedContext ctx, + CacheGroupContext grp + ) { assert ctx != null; assert grp != null; @@ -153,6 +156,8 @@ public GridDhtPartitionTopologyImpl(GridCacheSharedContext ctx, timeLog = ctx.logger(GridDhtPartitionsExchangeFuture.EXCHANGE_LOG); locParts = new AtomicReferenceArray<>(grp.affinityFunction().partitions()); + + cntrMap = new CachePartitionFullCountersMap(locParts.length()); } /** {@inheritDoc} */ @@ -713,10 +718,10 @@ private GridDhtLocalPartition createPartition(int p) { locParts.set(p, loc = new GridDhtLocalPartition(ctx, grp, p)); - T2 cntr = cntrMap.get(p); + long updCntr = cntrMap.updateCounter(p); - if (cntr != null) - loc.updateCounter(cntr.get2()); + if (updCntr != 0) + loc.updateCounter(updCntr); if (ctx.pageStore() != null) { try { @@ -1165,9 +1170,8 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD @Override public boolean update( @Nullable AffinityTopologyVersion exchangeVer, GridDhtPartitionFullMap partMap, - @Nullable Map> cntrMap, - Set partsToReload - ) { + @Nullable CachePartitionFullCountersMap incomeCntrMap, + Set partsToReload) { if (log.isDebugEnabled()) log.debug("Updating full partition map [exchVer=" + exchangeVer + ", parts=" + fullMapString() + ']'); @@ -1179,15 +1183,7 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD if (stopping || !lastTopChangeVer.initialized()) return false; - if (cntrMap != null) { - // update local map partition counters - for (Map.Entry> e : cntrMap.entrySet()) { - T2 cntr = this.cntrMap.get(e.getKey()); - - if (cntr == null || cntr.get2() < e.getValue().get2()) - this.cntrMap.put(e.getKey(), e.getValue()); - } - + if (incomeCntrMap != null) { // update local counters in partitions for (int i = 0; i < locParts.length(); i++) { GridDhtLocalPartition part = locParts.get(i); @@ -1195,10 +1191,12 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD if (part == null) continue; - T2 cntr = cntrMap.get(part.id()); + if (part.state() == OWNING || part.state() == MOVING) { + long updCntr = incomeCntrMap.updateCounter(part.id()); - if (cntr != null) - part.updateCounter(cntr.get2()); + if (updCntr != 0 && updCntr > part.updateCounter()) + part.updateCounter(updCntr); + } } } @@ -1321,13 +1319,6 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD assert locPart != null : grp.cacheOrGroupName(); - if (cntrMap != null) { - T2 cntr = cntrMap.get(p); - - if (cntr != null && cntr.get2() > locPart.updateCounter()) - locPart.updateCounter(cntr.get2()); - } - if (locPart.state() == MOVING) { boolean success = locPart.own(); @@ -1347,13 +1338,6 @@ else if (state == MOVING) { changed = true; } - - if (cntrMap != null) { - T2 cntr = cntrMap.get(p); - - if (cntr != null && cntr.get2() > locPart.updateCounter()) - locPart.updateCounter(cntr.get2()); - } } else if (state == RENTING && partsToReload.contains(p)) { GridDhtLocalPartition locPart = locParts.get(p); @@ -1403,7 +1387,7 @@ else if (locPart.state() == OWNING || locPart.state() == MOVING) { } /** {@inheritDoc} */ - @Override public void applyUpdateCounters(Map> cntrMap) { + @Override public void collectUpdateCounters(CachePartitionPartialCountersMap cntrMap) { assert cntrMap != null; long now = U.currentTimeMillis(); @@ -1422,25 +1406,55 @@ else if (locPart.state() == OWNING || locPart.state() == MOVING) { if (stopping) return; - for (Map.Entry> e : cntrMap.entrySet()) { - T2 cntr = this.cntrMap.get(e.getKey()); + for (int i = 0; i < cntrMap.size(); i++) { + int pId = cntrMap.partitionAt(i); + + long initialUpdateCntr = cntrMap.initialUpdateCounterAt(i); + long updateCntr = cntrMap.updateCounterAt(i); + + if (this.cntrMap.updateCounter(pId) < updateCntr) { + this.cntrMap.initialUpdateCounter(pId, initialUpdateCntr); + this.cntrMap.updateCounter(pId, updateCntr); + } + } + } + finally { + lock.writeLock().unlock(); + } + } + + /** {@inheritDoc} */ + @Override public void applyUpdateCounters() { + long now = U.currentTimeMillis(); + + lock.writeLock().lock(); + + try { + long acquired = U.currentTimeMillis(); - if (cntr == null || cntr.get2() < e.getValue().get2()) - this.cntrMap.put(e.getKey(), e.getValue()); + if (acquired - now >= 100) { + if (timeLog.isInfoEnabled()) + timeLog.info("Waited too long to acquire topology write lock " + + "[cache=" + grp.groupId() + ", waitTime=" + (acquired - now) + ']'); } + if (stopping) + return; + for (int i = 0; i < locParts.length(); i++) { GridDhtLocalPartition part = locParts.get(i); if (part == null) continue; - T2 cntr = cntrMap.get(part.id()); + long updCntr = cntrMap.updateCounter(part.id()); - if (cntr != null && cntr.get2() > part.updateCounter()) - part.updateCounter(cntr.get2()); - else if (part.updateCounter() > 0) - this.cntrMap.put(part.id(), new T2<>(part.initialUpdateCounter(), part.updateCounter())); + if (updCntr > part.updateCounter()) + part.updateCounter(updCntr); + else if (part.updateCounter() > 0) { + cntrMap.initialUpdateCounter(part.id(), part.initialUpdateCounter()); + cntrMap.updateCounter(part.id(), part.updateCounter()); + } } } finally { @@ -2169,26 +2183,32 @@ private void removeNode(UUID nodeId) { } /** {@inheritDoc} */ - @Override public Map> updateCounters(boolean skipZeros) { + @Override public CachePartitionFullCountersMap fullUpdateCounters() { lock.readLock().lock(); try { - Map> res; + return new CachePartitionFullCountersMap(cntrMap); + } + finally { + lock.readLock().unlock(); + } + } - if (skipZeros) { - res = U.newHashMap(cntrMap.size()); + /** {@inheritDoc} */ + @Override public CachePartitionPartialCountersMap localUpdateCounters(boolean skipZeros) { + lock.readLock().lock(); - for (Map.Entry> e : cntrMap.entrySet()) { - Long cntr = e.getValue().get2(); + try { + int locPartCnt = 0; - if (ZERO.equals(cntr)) - continue; + for (int i = 0; i < locParts.length(); i++) { + GridDhtLocalPartition part = locParts.get(i); - res.put(e.getKey(), e.getValue()); - } + if (part != null) + locPartCnt++; } - else - res = new HashMap<>(cntrMap); + + CachePartitionPartialCountersMap res = new CachePartitionPartialCountersMap(locPartCnt); for (int i = 0; i < locParts.length(); i++) { GridDhtLocalPartition part = locParts.get(i); @@ -2196,17 +2216,17 @@ private void removeNode(UUID nodeId) { if (part == null) continue; - T2 cntr0 = res.get(part.id()); - Long initCntr = part.initialUpdateCounter(); + long updCntr = part.updateCounter(); + long initCntr = part.initialUpdateCounter(); - if (cntr0 == null || initCntr >= cntr0.get1()) { - if (skipZeros && initCntr == 0L && part.updateCounter() == 0L) - continue; + if (skipZeros && initCntr == 0L && updCntr == 0L) + continue; - res.put(part.id(), new T2<>(initCntr, part.updateCounter())); - } + res.add(part.id(), initCntr, updCntr); } + res.trim(); + return res; } finally { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java new file mode 100644 index 0000000000000..1384a558e4769 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java @@ -0,0 +1,99 @@ +/* + * 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.preloader; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * + */ +public class CachePartitionFullCountersMap implements Serializable { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private long[] initialUpdCntrs; + + /** */ + private long[] updCntrs; + + /** + * @param other Map to copy. + */ + public CachePartitionFullCountersMap(CachePartitionFullCountersMap other) { + initialUpdCntrs = Arrays.copyOf(other.initialUpdCntrs, other.initialUpdCntrs.length); + updCntrs = Arrays.copyOf(other.updCntrs, other.updCntrs.length); + } + + /** + * @param partsCnt Total number of partitions. + */ + public CachePartitionFullCountersMap(int partsCnt) { + initialUpdCntrs = new long[partsCnt]; + updCntrs = new long[partsCnt]; + } + + /** + * Gets an initial update counter by the partition ID. + * + * @param p Partition ID. + * @return Initial update counter for the partition with the given ID. + */ + public long initialUpdateCounter(int p) { + return initialUpdCntrs[p]; + } + + /** + * Gets an update counter by the partition ID. + * + * @param p Partition ID. + * @return Update counter for the partition with the given ID. + */ + public long updateCounter(int p) { + return updCntrs[p]; + } + + /** + * Sets an initial update counter by the partition ID. + * + * @param p Partition ID. + * @param initialUpdCntr Initial update counter to set. + */ + public void initialUpdateCounter(int p, long initialUpdCntr) { + initialUpdCntrs[p] = initialUpdCntr; + } + + /** + * Sets an update counter by the partition ID. + * + * @param p Partition ID. + * @param updCntr Update counter to set. + */ + public void updateCounter(int p, long updCntr) { + updCntrs[p] = updCntr; + } + + /** + * Clears full counters map. + */ + public void clear() { + Arrays.fill(initialUpdCntrs, 0); + Arrays.fill(updCntrs, 0); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.java new file mode 100644 index 0000000000000..851ffedfd3f1c --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.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.cache.distributed.dht.preloader; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.internal.U; + +/** + * + */ +public class CachePartitionPartialCountersMap implements Serializable { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + public static final CachePartitionPartialCountersMap EMPTY = new CachePartitionPartialCountersMap(); + + /** */ + private int[] partIds; + + /** */ + private long[] initialUpdCntrs; + + /** */ + private long[] updCntrs; + + /** */ + private int curIdx; + + /** */ + private CachePartitionPartialCountersMap() { + // Empty map. + } + + /** + * @param partsCnt Total number of partitions will be stored in the partial map. + */ + public CachePartitionPartialCountersMap(int partsCnt) { + partIds = new int[partsCnt]; + initialUpdCntrs = new long[partsCnt]; + updCntrs = new long[partsCnt]; + } + + /** + * @return Total number of partitions added to the map. + */ + public int size() { + return curIdx; + } + + /** + * Adds partition counters for a partition with the given ID. + * + * @param partId Partition ID to add. + * @param initialUpdCntr Partition initial update counter. + * @param updCntr Partition update counter. + */ + public void add(int partId, long initialUpdCntr, long updCntr) { + if (curIdx > 0) { + if (partIds[curIdx - 1] >= partId) + throw new IllegalArgumentException("Adding a partition in the wrong order " + + "[prevPart=" + partIds[curIdx - 1] + ", partId=" + partId + ']'); + } + + if (curIdx == partIds.length) + throw new IllegalStateException("Adding more partitions than reserved: " + partIds.length); + + partIds[curIdx] = partId; + initialUpdCntrs[curIdx] = initialUpdCntr; + updCntrs[curIdx] = updCntr; + + curIdx++; + } + + /** + * Cuts the array sizes according to curIdx. No more entries can be added to this map + * after this method is called. + */ + public void trim() { + if (curIdx < partIds.length) { + partIds = Arrays.copyOf(partIds, curIdx); + initialUpdCntrs = Arrays.copyOf(initialUpdCntrs, curIdx); + updCntrs = Arrays.copyOf(updCntrs, curIdx); + } + } + + /** + * @param partId Partition ID to search. + * @return Partition index in the array. + */ + public int partitionIndex(int partId) { + return Arrays.binarySearch(partIds, 0, curIdx, partId); + } + + /** + * Gets partition ID saved at the given index. + * + * @param idx Index to get value from. + * @return Partition ID. + */ + public int partitionAt(int idx) { + return partIds[idx]; + } + + /** + * Gets initial update counter saved at the given index. + * + * @param idx Index to get value from. + * @return Initial update counter. + */ + public long initialUpdateCounterAt(int idx) { + return initialUpdCntrs[idx]; + } + + /** + * Gets update counter saved at the given index. + * + * @param idx Index to get value from. + * @return Update counter. + */ + public long updateCounterAt(int idx) { + return updCntrs[idx]; + } + + + /** + * @param cntrsMap Partial local counters map. + * @return Partition ID to partition counters map. + */ + public static Map> toCountersMap(CachePartitionPartialCountersMap cntrsMap) { + if (cntrsMap.size() == 0) + return Collections.emptyMap(); + + Map> res = U.newHashMap(cntrsMap.size()); + + for (int idx = 0; idx < cntrsMap.size(); idx++) + res.put(cntrsMap.partitionAt(idx), + new T2<>(cntrsMap.initialUpdateCounterAt(idx), cntrsMap.updateCounterAt(idx))); + + return res; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java index 95c1a4f51f267..84cc792fe22ea 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java @@ -19,11 +19,9 @@ import java.io.Externalizable; import java.nio.ByteBuffer; -import java.util.Map; import org.apache.ignite.internal.managers.communication.GridIoMessage; import org.apache.ignite.internal.processors.cache.GridCacheMessage; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; -import org.apache.ignite.internal.util.typedef.T2; 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; @@ -110,12 +108,6 @@ public void exchangeId(GridDhtPartitionExchangeId exchId) { this.exchId = exchId; } - /** - * @param grpId Cache group ID. - * @return Parition update counters. - */ - public abstract Map> partitionUpdateCounters(int grpId); - /** * @return Last used version among all nodes. */ 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 f4ecb8ed6ea1b..11fdc45669f91 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 @@ -761,7 +761,7 @@ private void updateTopologies(boolean crd) throws IgniteCheckedException { if (updateTop && clientTop != null) { top.update(null, clientTop.partitionMap(true), - clientTop.updateCounters(false), + clientTop.fullUpdateCounters(), Collections.emptySet()); } } @@ -1209,7 +1209,7 @@ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException GridDhtPartitionsSingleMessage msg; - // Reset lost partition before send local partition to coordinator. + // Reset lost partitions before sending local partitions to coordinator. if (exchActions != null) { Set caches = exchActions.cachesToResetLostPartitions(); @@ -1226,7 +1226,8 @@ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException else { msg = cctx.exchange().createPartitionsSingleMessage(exchangeId(), false, - true); + true, + exchActions); Map> partHistReserved0 = partHistReserved; @@ -1948,10 +1949,12 @@ private void assignPartitionStates(GridDhtPartitionTopology top) { Map minCntrs = new HashMap<>(); for (Map.Entry e : msgs.entrySet()) { - assert e.getValue().partitionUpdateCounters(top.groupId()) != null; + CachePartitionPartialCountersMap nodeCntrs = e.getValue().partitionUpdateCounters(top.groupId()); + + assert nodeCntrs != null; - for (Map.Entry> e0 : e.getValue().partitionUpdateCounters(top.groupId()).entrySet()) { - int p = e0.getKey(); + for (int i = 0; i < nodeCntrs.size(); i++) { + int p = nodeCntrs.partitionAt(i); UUID uuid = e.getKey(); @@ -1960,10 +1963,9 @@ private void assignPartitionStates(GridDhtPartitionTopology top) { if (state != GridDhtPartitionState.OWNING && state != GridDhtPartitionState.MOVING) continue; - Long cntr = state == GridDhtPartitionState.MOVING ? e0.getValue().get1() : e0.getValue().get2(); - - if (cntr == null) - cntr = 0L; + long cntr = state == GridDhtPartitionState.MOVING ? + nodeCntrs.initialUpdateCounterAt(i) : + nodeCntrs.updateCounterAt(i); Long minCntr = minCntrs.get(p); @@ -2223,10 +2225,10 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe GridDhtPartitionTopology top = grp != null ? grp.topology() : cctx.exchange().clientTopology(grpId); - Map> cntrs = msg.partitionUpdateCounters(grpId); + CachePartitionPartialCountersMap cntrs = msg.partitionUpdateCounters(grpId); if (cntrs != null) - top.applyUpdateCounters(cntrs); + top.collectUpdateCounters(cntrs); } Collection affReq = msg.cacheGroupsAffinityRequest(); @@ -2239,6 +2241,11 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe } } + for (CacheGroupContext grpCtx : cctx.cache().cacheGroups()) { + if (!grpCtx.isLocal()) + grpCtx.topology().applyUpdateCounters(); + } + if (firstDiscoEvt.type() == EVT_DISCOVERY_CUSTOM_EVT) { assert firstDiscoEvt instanceof DiscoveryCustomEvent; @@ -2553,7 +2560,8 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi GridDhtPartitionsSingleMessage res = cctx.exchange().createPartitionsSingleMessage( msg.restoreExchangeId(), cctx.kernalContext().clientNode(), - true); + true, + exchActions); if (localJoinExchange() && finishState0 == null) res.cacheGroupsAffinityRequest(exchCtx.groupsAffinityRequestOnJoin()); @@ -2727,7 +2735,7 @@ private void updatePartitionFullMap(AffinityTopologyVersion resTopVer, GridDhtPa for (Map.Entry entry : msg.partitions().entrySet()) { Integer grpId = entry.getKey(); - Map> cntrMap = msg.partitionUpdateCounters(grpId); + CachePartitionFullCountersMap cntrMap = msg.partitionUpdateCounters(grpId); CacheGroupContext grp = cctx.cache().cacheGroup(grpId); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java index 3e348e3eaf2e8..39394147132b1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java @@ -33,7 +33,6 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -274,7 +273,7 @@ public void addFullPartitionsMap(int grpId, GridDhtPartitionFullMap fullMap, @Nu * @param grpId Cache group ID. * @param cntrMap Partition update counters. */ - public void addPartitionUpdateCounters(int grpId, Map> cntrMap) { + public void addPartitionUpdateCounters(int grpId, CachePartitionFullCountersMap cntrMap) { if (partCntrs == null) partCntrs = new IgniteDhtPartitionCountersMap(); @@ -285,14 +284,8 @@ public void addPartitionUpdateCounters(int grpId, Map> c * @param grpId Cache group ID. * @return Partition update counters. */ - @Override public Map> partitionUpdateCounters(int grpId) { - if (partCntrs != null) { - Map> res = partCntrs.get(grpId); - - return res != null ? res : Collections.>emptyMap(); - } - - return Collections.emptyMap(); + public CachePartitionFullCountersMap partitionUpdateCounters(int grpId) { + return partCntrs == null ? null : partCntrs.get(grpId); } /** 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 bc7d3146cea4e..44815cabdd549 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 @@ -18,11 +18,11 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.preloader; import java.util.Collection; -import java.util.Map; -import java.util.HashMap; +import java.io.Externalizable; import java.nio.ByteBuffer; import java.util.Collections; -import java.io.Externalizable; +import java.util.HashMap; +import java.util.Map; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.GridDirectCollection; import org.apache.ignite.internal.GridDirectMap; @@ -32,7 +32,6 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -62,7 +61,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes /** Partitions update counters. */ @GridToStringInclude @GridDirectTransient - private Map>> partCntrs; + private Map partCntrs; /** Serialized partitions counters. */ private byte[] partCntrsBytes; @@ -190,7 +189,7 @@ public void addLocalPartitionMap(int cacheId, GridDhtPartitionMap locMap, @Nulla * @param grpId Cache group ID. * @param cntrMap Partition update counters. */ - public void partitionUpdateCounters(int grpId, Map> cntrMap) { + public void partitionUpdateCounters(int grpId, CachePartitionPartialCountersMap cntrMap) { if (partCntrs == null) partCntrs = new HashMap<>(); @@ -201,14 +200,10 @@ public void partitionUpdateCounters(int grpId, Map> cntr * @param grpId Cache group ID. * @return Partition update counters. */ - @Override public Map> partitionUpdateCounters(int grpId) { - if (partCntrs != null) { - Map> res = partCntrs.get(grpId); - - return res != null ? res : Collections.>emptyMap(); - } + public CachePartitionPartialCountersMap partitionUpdateCounters(int grpId) { + CachePartitionPartialCountersMap res = partCntrs == null ? null : partCntrs.get(grpId); - return Collections.emptyMap(); + return res == null ? CachePartitionPartialCountersMap.EMPTY : res; } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java index 6317fbc4c5192..0be0f37aa22ac 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java @@ -19,9 +19,6 @@ import java.io.Externalizable; import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.Map; -import org.apache.ignite.internal.util.typedef.T2; 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; @@ -77,11 +74,6 @@ GridDhtPartitionExchangeId restoreExchangeId() { return 0; } - /** {@inheritDoc} */ - @Override public Map> partitionUpdateCounters(int cacheId) { - return Collections.emptyMap(); - } - /** {@inheritDoc} */ @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) { writer.setBuffer(buf); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java index dc2fbf879ccc8..e7954d960ce44 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java @@ -19,10 +19,8 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.preloader; import java.io.Serializable; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.apache.ignite.internal.util.typedef.T2; /** * Partition counters map. @@ -32,7 +30,7 @@ public class IgniteDhtPartitionCountersMap implements Serializable { private static final long serialVersionUID = 0L; /** */ - private Map>> map; + private Map map; /** * @return {@code True} if map is empty. @@ -45,7 +43,7 @@ public synchronized boolean empty() { * @param cacheId Cache ID. * @param cntrMap Counters map. */ - public synchronized void putIfAbsent(int cacheId, Map> cntrMap) { + public synchronized void putIfAbsent(int cacheId, CachePartitionFullCountersMap cntrMap) { if (map == null) map = new HashMap<>(); @@ -57,14 +55,14 @@ public synchronized void putIfAbsent(int cacheId, Map> c * @param cacheId Cache ID. * @return Counters map. */ - public synchronized Map> get(int cacheId) { + public synchronized CachePartitionFullCountersMap get(int cacheId) { if (map == null) - map = new HashMap<>(); + return null; - Map> cntrMap = map.get(cacheId); + CachePartitionFullCountersMap cntrMap = map.get(cacheId); if (cntrMap == null) - return Collections.emptyMap(); + return null; return cntrMap; } 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 ed6eee2f06d21..83a9f55786827 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 @@ -1167,7 +1167,7 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public Long initialUpdateCounter() { + @Override public long initialUpdateCounter() { try { CacheDataStore delegate0 = init0(true); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/continuous/GridContinuousProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/continuous/GridContinuousProcessor.java index 70623538987e6..fa52be2bf4883 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/continuous/GridContinuousProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/continuous/GridContinuousProcessor.java @@ -93,6 +93,7 @@ import static org.apache.ignite.internal.GridTopic.TOPIC_CACHE; import static org.apache.ignite.internal.GridTopic.TOPIC_CONTINUOUS; import static org.apache.ignite.internal.managers.communication.GridIoPolicy.SYSTEM_POOL; +import static org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap.toCountersMap; import static org.apache.ignite.internal.processors.continuous.GridContinuousMessageType.MSG_EVT_ACK; import static org.apache.ignite.internal.processors.continuous.GridContinuousMessageType.MSG_EVT_NOTIFICATION; @@ -202,7 +203,8 @@ public GridContinuousProcessor(GridKernalContext ctx) { GridCacheContext cctx = interCache != null ? interCache.context() : null; if (cctx != null && cntrsPerNode != null && !cctx.isLocal() && cctx.affinityNode()) - cntrsPerNode.put(ctx.localNodeId(), cctx.topology().updateCounters(false)); + cntrsPerNode.put(ctx.localNodeId(), + toCountersMap(cctx.topology().localUpdateCounters(false))); routine.handler().updateCounters(topVer, cntrsPerNode, cntrs); } @@ -1070,7 +1072,8 @@ private void processStartRequest(ClusterNode node, StartRoutineDiscoveryMessage GridCacheAdapter cache = ctx.cache().internalCache(hnd0.cacheName()); if (cache != null && !cache.isLocal() && cache.context().userCache()) - req.addUpdateCounters(ctx.localNodeId(), cache.context().topology().updateCounters(false)); + req.addUpdateCounters(ctx.localNodeId(), + toCountersMap(cache.context().topology().localUpdateCounters(false))); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java index 43069cd54d89a..f91c6896ccc83 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java @@ -75,6 +75,7 @@ import org.apache.ignite.internal.managers.communication.GridIoMessage; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap; import org.apache.ignite.internal.processors.continuous.GridContinuousHandler; import org.apache.ignite.internal.processors.continuous.GridContinuousMessage; import org.apache.ignite.internal.processors.continuous.GridContinuousProcessor; @@ -519,11 +520,15 @@ private void checkPartCounter(int nodes, int killedNodeIdx, Map u Affinity aff = grid(i).affinity(DEFAULT_CACHE_NAME); - Map> act = grid(i).cachex(DEFAULT_CACHE_NAME).context().topology().updateCounters(false); + CachePartitionPartialCountersMap act = grid(i).cachex(DEFAULT_CACHE_NAME).context().topology() + .localUpdateCounters(false); for (Map.Entry e : updCntrs.entrySet()) { - if (aff.mapPartitionToPrimaryAndBackups(e.getKey()).contains(grid(i).localNode())) - assertEquals(e.getValue(), act.get(e.getKey()).get2()); + if (aff.mapPartitionToPrimaryAndBackups(e.getKey()).contains(grid(i).localNode())) { + int partIdx = act.partitionIndex(e.getKey()); + + assertEquals(e.getValue(), (Long)act.updateCounterAt(partIdx)); + } } } } diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java index 2ec2c74d28f90..202486c5dbfcb 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java @@ -20,8 +20,6 @@ import java.util.Set; import junit.framework.TestSuite; import org.apache.ignite.GridSuppressedExceptionSelfTest; -import org.apache.ignite.internal.processors.database.SwapPathConstructionSelfTest; -import org.apache.ignite.util.AttributeNodeFilterSelfTest; import org.apache.ignite.internal.ClusterGroupHostsSelfTest; import org.apache.ignite.internal.ClusterGroupSelfTest; import org.apache.ignite.internal.GridFailFastNodeFailureDetectionSelfTest; @@ -57,6 +55,7 @@ import org.apache.ignite.internal.processors.database.FreeListImplSelfTest; import org.apache.ignite.internal.processors.database.MemoryMetricsSelfTest; import org.apache.ignite.internal.processors.database.MetadataStorageSelfTest; +import org.apache.ignite.internal.processors.database.SwapPathConstructionSelfTest; import org.apache.ignite.internal.processors.odbc.OdbcConfigurationValidationSelfTest; import org.apache.ignite.internal.processors.odbc.OdbcEscapeSequenceSelfTest; import org.apache.ignite.internal.processors.service.ClosureServiceClientsNodesTest; From 8e672d4a83cb20136d375818505cacccf6c9b4fd Mon Sep 17 00:00:00 2001 From: sboikov Date: Mon, 21 Aug 2017 16:39:29 +0300 Subject: [PATCH 074/145] Changed ignite version. --- examples/pom.xml | 2 +- modules/aop/pom.xml | 2 +- modules/apache-license-gen/pom.xml | 2 +- modules/aws/pom.xml | 2 +- modules/benchmarks/pom.xml | 2 +- modules/camel/pom.xml | 2 +- modules/cassandra/pom.xml | 2 +- modules/cassandra/serializers/pom.xml | 4 ++-- modules/cassandra/store/pom.xml | 4 ++-- modules/clients/pom.xml | 2 +- modules/cloud/pom.xml | 2 +- modules/codegen/pom.xml | 2 +- modules/core/pom.xml | 2 +- modules/core/src/main/resources/ignite.properties | 2 +- modules/extdata/p2p/pom.xml | 2 +- modules/extdata/uri/modules/uri-dependency/pom.xml | 2 +- modules/extdata/uri/pom.xml | 2 +- modules/flink/pom.xml | 2 +- modules/flume/pom.xml | 2 +- modules/gce/pom.xml | 2 +- modules/geospatial/pom.xml | 2 +- modules/hadoop/pom.xml | 2 +- modules/hibernate-4.2/pom.xml | 2 +- modules/hibernate-5.1/pom.xml | 2 +- modules/hibernate-core/pom.xml | 2 +- modules/indexing/pom.xml | 2 +- modules/jcl/pom.xml | 2 +- modules/jms11/pom.xml | 2 +- modules/jta/pom.xml | 2 +- modules/kafka/pom.xml | 2 +- modules/kubernetes/pom.xml | 2 +- modules/log4j/pom.xml | 2 +- modules/log4j2/pom.xml | 2 +- modules/mesos/pom.xml | 2 +- modules/ml/pom.xml | 5 ++--- modules/mqtt/pom.xml | 2 +- modules/osgi-karaf/pom.xml | 2 +- modules/osgi-paxlogging/pom.xml | 2 +- modules/osgi/pom.xml | 2 +- modules/platforms/cpp/common/configure.ac | 2 +- modules/platforms/cpp/configure.ac | 2 +- modules/platforms/cpp/configure.acrel | 2 +- modules/platforms/cpp/core-test/configure.ac | 2 +- modules/platforms/cpp/core/configure.ac | 2 +- modules/platforms/cpp/examples/configure.ac | 2 +- modules/platforms/cpp/ignite/configure.ac | 2 +- modules/platforms/cpp/odbc/install/ignite-odbc-amd64.wxs | 2 +- modules/platforms/cpp/odbc/install/ignite-odbc-x86.wxs | 2 +- .../Apache.Ignite.AspNet.Tests/Properties/AssemblyInfo.cs | 6 +++--- .../dotnet/Apache.Ignite.AspNet/Properties/AssemblyInfo.cs | 6 +++--- .../Apache.Ignite.Benchmarks/Properties/AssemblyInfo.cs | 6 +++--- .../Properties/AssemblyInfo.cs | 6 +++--- .../Properties/AssemblyInfo.cs | 6 +++--- .../Apache.Ignite.Core.Tests/Properties/AssemblyInfo.cs | 6 +++--- .../dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs | 6 +++--- .../Properties/AssemblyInfo.cs | 6 +++--- .../Properties/AssemblyInfo.cs | 6 +++--- .../dotnet/Apache.Ignite.Linq/Properties/AssemblyInfo.cs | 6 +++--- .../dotnet/Apache.Ignite.Log4Net/Properties/AssemblyInfo.cs | 6 +++--- .../dotnet/Apache.Ignite.NLog/Properties/AssemblyInfo.cs | 6 +++--- .../dotnet/Apache.Ignite/Properties/AssemblyInfo.cs | 6 +++--- modules/rest-http/pom.xml | 2 +- modules/rocketmq/pom.xml | 5 ++--- modules/scalar-2.10/pom.xml | 2 +- modules/scalar/pom.xml | 2 +- modules/schedule/pom.xml | 2 +- modules/slf4j/pom.xml | 2 +- modules/spark-2.10/pom.xml | 2 +- modules/spark/pom.xml | 2 +- modules/spring-data/pom.xml | 2 +- modules/spring/pom.xml | 2 +- modules/ssh/pom.xml | 2 +- modules/storm/pom.xml | 2 +- modules/tools/pom.xml | 2 +- modules/twitter/pom.xml | 2 +- modules/urideploy/pom.xml | 2 +- modules/visor-console-2.10/pom.xml | 2 +- modules/visor-console/pom.xml | 2 +- modules/visor-plugins/pom.xml | 2 +- modules/web-console/pom.xml | 2 +- modules/web-console/web-agent/pom.xml | 2 +- modules/web/ignite-appserver-test/pom.xml | 2 +- modules/web/ignite-websphere-test/pom.xml | 2 +- modules/web/pom.xml | 2 +- modules/yardstick/pom.xml | 2 +- modules/yarn/pom.xml | 2 +- modules/zeromq/pom.xml | 2 +- modules/zookeeper/pom.xml | 2 +- pom.xml | 2 +- 89 files changed, 119 insertions(+), 121 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index 60427816bdc61..f15edeb5ebcf7 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -28,7 +28,7 @@ ignite-examples - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT diff --git a/modules/aop/pom.xml b/modules/aop/pom.xml index 3d56599febd15..b86132e753c6c 100644 --- a/modules/aop/pom.xml +++ b/modules/aop/pom.xml @@ -31,7 +31,7 @@ ignite-aop - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/apache-license-gen/pom.xml b/modules/apache-license-gen/pom.xml index 0edff097ebf34..bf67794afa7d7 100644 --- a/modules/apache-license-gen/pom.xml +++ b/modules/apache-license-gen/pom.xml @@ -31,7 +31,7 @@ org.apache.ignite ignite-apache-license-gen - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/aws/pom.xml b/modules/aws/pom.xml index a23843e6c49d6..b9ae95918aab3 100644 --- a/modules/aws/pom.xml +++ b/modules/aws/pom.xml @@ -31,7 +31,7 @@ ignite-aws - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/benchmarks/pom.xml b/modules/benchmarks/pom.xml index af3e05469034f..b6f244ebe8bd5 100644 --- a/modules/benchmarks/pom.xml +++ b/modules/benchmarks/pom.xml @@ -31,7 +31,7 @@ ignite-benchmarks - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/camel/pom.xml b/modules/camel/pom.xml index 975c3914d7b4c..91342ba044802 100644 --- a/modules/camel/pom.xml +++ b/modules/camel/pom.xml @@ -31,7 +31,7 @@ ignite-camel - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/cassandra/pom.xml b/modules/cassandra/pom.xml index 83e1ce4f752bb..2a97e89f3c59c 100644 --- a/modules/cassandra/pom.xml +++ b/modules/cassandra/pom.xml @@ -32,7 +32,7 @@ ignite-cassandra pom - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/cassandra/serializers/pom.xml b/modules/cassandra/serializers/pom.xml index 3ef1e535a9a3e..0f1a35b6ed2dc 100644 --- a/modules/cassandra/serializers/pom.xml +++ b/modules/cassandra/serializers/pom.xml @@ -26,12 +26,12 @@ org.apache.ignite ignite-cassandra - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT .. ignite-cassandra-serializers - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/cassandra/store/pom.xml b/modules/cassandra/store/pom.xml index 16df518d5407f..32792dfcf0c03 100644 --- a/modules/cassandra/store/pom.xml +++ b/modules/cassandra/store/pom.xml @@ -26,12 +26,12 @@ org.apache.ignite ignite-cassandra - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT .. ignite-cassandra-store - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/clients/pom.xml b/modules/clients/pom.xml index edd20eee90221..7ef8a0aa440c4 100644 --- a/modules/clients/pom.xml +++ b/modules/clients/pom.xml @@ -31,7 +31,7 @@ ignite-clients - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/cloud/pom.xml b/modules/cloud/pom.xml index 6fd124823019f..db60839d6b63c 100644 --- a/modules/cloud/pom.xml +++ b/modules/cloud/pom.xml @@ -29,7 +29,7 @@ ignite-cloud - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/codegen/pom.xml b/modules/codegen/pom.xml index b6400faf774c4..933c295731ef1 100644 --- a/modules/codegen/pom.xml +++ b/modules/codegen/pom.xml @@ -31,7 +31,7 @@ ignite-codegen - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/core/pom.xml b/modules/core/pom.xml index 17e850d0a7fe8..8385dd8d46e30 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -31,7 +31,7 @@ ignite-core - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/core/src/main/resources/ignite.properties b/modules/core/src/main/resources/ignite.properties index 9e150b96b046c..81eb6724478a6 100644 --- a/modules/core/src/main/resources/ignite.properties +++ b/modules/core/src/main/resources/ignite.properties @@ -15,7 +15,7 @@ # limitations under the License. # -ignite.version=2.1.0-SNAPSHOT +ignite.version=2.2.0-SNAPSHOT ignite.build=0 ignite.revision=DEV ignite.rel.date=01011970 diff --git a/modules/extdata/p2p/pom.xml b/modules/extdata/p2p/pom.xml index cfe5961efe2a5..a6da85700c547 100644 --- a/modules/extdata/p2p/pom.xml +++ b/modules/extdata/p2p/pom.xml @@ -31,7 +31,7 @@ ignite-extdata-p2p - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT diff --git a/modules/extdata/uri/modules/uri-dependency/pom.xml b/modules/extdata/uri/modules/uri-dependency/pom.xml index a940ccb7f8665..1988981ba7993 100644 --- a/modules/extdata/uri/modules/uri-dependency/pom.xml +++ b/modules/extdata/uri/modules/uri-dependency/pom.xml @@ -27,7 +27,7 @@ ignite-extdata-uri-dep jar - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT 4.0.0 diff --git a/modules/extdata/uri/pom.xml b/modules/extdata/uri/pom.xml index 09d7826bb728c..8bb5768feef78 100644 --- a/modules/extdata/uri/pom.xml +++ b/modules/extdata/uri/pom.xml @@ -32,7 +32,7 @@ ignite-extdata-uri - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT diff --git a/modules/flink/pom.xml b/modules/flink/pom.xml index 439fe2053b385..ed55cd0990495 100644 --- a/modules/flink/pom.xml +++ b/modules/flink/pom.xml @@ -31,7 +31,7 @@ ignite-flink - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/flume/pom.xml b/modules/flume/pom.xml index 2e2fcbd575fbf..5b0076101c36b 100644 --- a/modules/flume/pom.xml +++ b/modules/flume/pom.xml @@ -31,7 +31,7 @@ ignite-flume - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/gce/pom.xml b/modules/gce/pom.xml index 34c6ea3b1a6c5..bf15021387b15 100644 --- a/modules/gce/pom.xml +++ b/modules/gce/pom.xml @@ -31,7 +31,7 @@ ignite-gce - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/geospatial/pom.xml b/modules/geospatial/pom.xml index 488a4a4a35edc..0e2edb3a9f791 100644 --- a/modules/geospatial/pom.xml +++ b/modules/geospatial/pom.xml @@ -31,7 +31,7 @@ ignite-geospatial - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/hadoop/pom.xml b/modules/hadoop/pom.xml index b6d4b10f387b3..beacda3d5c1b1 100644 --- a/modules/hadoop/pom.xml +++ b/modules/hadoop/pom.xml @@ -31,7 +31,7 @@ ignite-hadoop - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/hibernate-4.2/pom.xml b/modules/hibernate-4.2/pom.xml index e40a550579959..b15f2484f5cb0 100644 --- a/modules/hibernate-4.2/pom.xml +++ b/modules/hibernate-4.2/pom.xml @@ -31,7 +31,7 @@ ignite-hibernate_4.2 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/hibernate-5.1/pom.xml b/modules/hibernate-5.1/pom.xml index a99408eeb1424..6a928835a7a8f 100644 --- a/modules/hibernate-5.1/pom.xml +++ b/modules/hibernate-5.1/pom.xml @@ -31,7 +31,7 @@ ignite-hibernate_5.1 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/hibernate-core/pom.xml b/modules/hibernate-core/pom.xml index 624dcc878e31c..2f5f92402317b 100644 --- a/modules/hibernate-core/pom.xml +++ b/modules/hibernate-core/pom.xml @@ -31,7 +31,7 @@ ignite-hibernate-core - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/indexing/pom.xml b/modules/indexing/pom.xml index 62fc4028578e2..a10b97efe2486 100644 --- a/modules/indexing/pom.xml +++ b/modules/indexing/pom.xml @@ -31,7 +31,7 @@ ignite-indexing - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/jcl/pom.xml b/modules/jcl/pom.xml index ce68c7f7300cc..88c0df41ea87d 100644 --- a/modules/jcl/pom.xml +++ b/modules/jcl/pom.xml @@ -31,7 +31,7 @@ ignite-jcl - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/jms11/pom.xml b/modules/jms11/pom.xml index a226043a03f7b..b3607c4d5c84c 100644 --- a/modules/jms11/pom.xml +++ b/modules/jms11/pom.xml @@ -31,7 +31,7 @@ ignite-jms11 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/jta/pom.xml b/modules/jta/pom.xml index 254e146ddc15e..7831a46633c1c 100644 --- a/modules/jta/pom.xml +++ b/modules/jta/pom.xml @@ -31,7 +31,7 @@ ignite-jta - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/kafka/pom.xml b/modules/kafka/pom.xml index 1affd57ee8895..1dd2b585fcd77 100644 --- a/modules/kafka/pom.xml +++ b/modules/kafka/pom.xml @@ -31,7 +31,7 @@ ignite-kafka - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/kubernetes/pom.xml b/modules/kubernetes/pom.xml index 88884d222f2ef..17fcdb628c62c 100644 --- a/modules/kubernetes/pom.xml +++ b/modules/kubernetes/pom.xml @@ -31,7 +31,7 @@ ignite-kubernetes - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/log4j/pom.xml b/modules/log4j/pom.xml index 5a4170878642f..b55036d35a3ee 100644 --- a/modules/log4j/pom.xml +++ b/modules/log4j/pom.xml @@ -31,7 +31,7 @@ ignite-log4j - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/log4j2/pom.xml b/modules/log4j2/pom.xml index 9ae74aabf352e..983a4213cd1bf 100644 --- a/modules/log4j2/pom.xml +++ b/modules/log4j2/pom.xml @@ -31,7 +31,7 @@ ignite-log4j2 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/mesos/pom.xml b/modules/mesos/pom.xml index 2723334177da7..139aafb9b1f3c 100644 --- a/modules/mesos/pom.xml +++ b/modules/mesos/pom.xml @@ -31,7 +31,7 @@ ignite-mesos - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/ml/pom.xml b/modules/ml/pom.xml index d6197199fd6be..7f9411b905c30 100644 --- a/modules/ml/pom.xml +++ b/modules/ml/pom.xml @@ -19,8 +19,7 @@ - + 4.0.0 @@ -31,7 +30,7 @@ ignite-ml - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/mqtt/pom.xml b/modules/mqtt/pom.xml index 80fc7208ee820..b82707fd3446c 100644 --- a/modules/mqtt/pom.xml +++ b/modules/mqtt/pom.xml @@ -31,7 +31,7 @@ ignite-mqtt - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/osgi-karaf/pom.xml b/modules/osgi-karaf/pom.xml index 80b9fe928bd04..5be1aba7cf216 100644 --- a/modules/osgi-karaf/pom.xml +++ b/modules/osgi-karaf/pom.xml @@ -31,7 +31,7 @@ ignite-osgi-karaf - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT pom diff --git a/modules/osgi-paxlogging/pom.xml b/modules/osgi-paxlogging/pom.xml index 4852ed7a9ba23..a1112cc547b8e 100644 --- a/modules/osgi-paxlogging/pom.xml +++ b/modules/osgi-paxlogging/pom.xml @@ -31,7 +31,7 @@ ignite-osgi-paxlogging - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT jar diff --git a/modules/osgi/pom.xml b/modules/osgi/pom.xml index 74a2c3d36771d..cb242ff215b93 100644 --- a/modules/osgi/pom.xml +++ b/modules/osgi/pom.xml @@ -31,7 +31,7 @@ ignite-osgi - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/platforms/cpp/common/configure.ac b/modules/platforms/cpp/common/configure.ac index ab78388da2590..dacbad6595e6b 100644 --- a/modules/platforms/cpp/common/configure.ac +++ b/modules/platforms/cpp/common/configure.ac @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite JNI bridge for C++], [2.1.0.19388], [dev@ignite.apache.org], [ignite-common], [ignite.apache.org]) +AC_INIT([Apache Ignite JNI bridge for C++], [2.2.0.22385], [dev@ignite.apache.org], [ignite-common], [ignite.apache.org]) AC_CONFIG_SRCDIR(src) AC_CANONICAL_SYSTEM diff --git a/modules/platforms/cpp/configure.ac b/modules/platforms/cpp/configure.ac index 9dd52d7f50e33..e7f2b50e9110d 100644 --- a/modules/platforms/cpp/configure.ac +++ b/modules/platforms/cpp/configure.ac @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite C++], [2.1.0.19388], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) +AC_INIT([Apache Ignite C++], [2.2.0.22385], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) AC_CANONICAL_HOST AC_CONFIG_MACRO_DIR([m4]) diff --git a/modules/platforms/cpp/configure.acrel b/modules/platforms/cpp/configure.acrel index 5f8c4ed4a589c..1401e596b8c20 100644 --- a/modules/platforms/cpp/configure.acrel +++ b/modules/platforms/cpp/configure.acrel @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite C++], [2.1.0.19388], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) +AC_INIT([Apache Ignite C++], [2.2.0.22385], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) AC_CANONICAL_HOST AC_CONFIG_MACRO_DIR([m4]) diff --git a/modules/platforms/cpp/core-test/configure.ac b/modules/platforms/cpp/core-test/configure.ac index c7180552b9671..f7b0896cdf3ad 100644 --- a/modules/platforms/cpp/core-test/configure.ac +++ b/modules/platforms/cpp/core-test/configure.ac @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite C++ Test], [2.1.0.19388], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) +AC_INIT([Apache Ignite C++ Test], [2.2.0.22385], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) AC_CONFIG_SRCDIR(src) AC_CANONICAL_SYSTEM diff --git a/modules/platforms/cpp/core/configure.ac b/modules/platforms/cpp/core/configure.ac index 5f1881cf87e22..7342c864a24cb 100644 --- a/modules/platforms/cpp/core/configure.ac +++ b/modules/platforms/cpp/core/configure.ac @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite C++], [2.1.0.19388], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) +AC_INIT([Apache Ignite C++], [2.2.0.22385], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) AC_CONFIG_SRCDIR(src) AC_CANONICAL_SYSTEM diff --git a/modules/platforms/cpp/examples/configure.ac b/modules/platforms/cpp/examples/configure.ac index a72471d9a0dc8..3d02fe55b5cd8 100644 --- a/modules/platforms/cpp/examples/configure.ac +++ b/modules/platforms/cpp/examples/configure.ac @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite C++ Examples], [2.1.0.19388], [dev@ignite.apache.org], [ignite-examples], [ignite.apache.org]) +AC_INIT([Apache Ignite C++ Examples], [2.2.0.22385], [dev@ignite.apache.org], [ignite-examples], [ignite.apache.org]) AC_CANONICAL_HOST AC_CONFIG_MACRO_DIR([m4]) diff --git a/modules/platforms/cpp/ignite/configure.ac b/modules/platforms/cpp/ignite/configure.ac index 69b76e0df89c0..1ba4b0ca511d4 100644 --- a/modules/platforms/cpp/ignite/configure.ac +++ b/modules/platforms/cpp/ignite/configure.ac @@ -19,7 +19,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Apache Ignite C++ Runner], [2.1.0.19388], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) +AC_INIT([Apache Ignite C++ Runner], [2.2.0.22385], [dev@ignite.apache.org], [ignite], [ignite.apache.org]) AC_CONFIG_SRCDIR(src) AC_CANONICAL_SYSTEM diff --git a/modules/platforms/cpp/odbc/install/ignite-odbc-amd64.wxs b/modules/platforms/cpp/odbc/install/ignite-odbc-amd64.wxs index 186465c9cc5a0..590faf51a3df9 100644 --- a/modules/platforms/cpp/odbc/install/ignite-odbc-amd64.wxs +++ b/modules/platforms/cpp/odbc/install/ignite-odbc-amd64.wxs @@ -21,7 +21,7 @@ + Language='1033' Codepage='1252' Version='2.2.0.22385'> + Language='1033' Codepage='1252' Version='2.2.0.22385'> ignite-rest-http - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/rocketmq/pom.xml b/modules/rocketmq/pom.xml index 43b1ec4e50b9d..f453ea858b1a8 100644 --- a/modules/rocketmq/pom.xml +++ b/modules/rocketmq/pom.xml @@ -20,8 +20,7 @@ - + 4.0.0 @@ -32,7 +31,7 @@ ignite-rocketmq - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/scalar-2.10/pom.xml b/modules/scalar-2.10/pom.xml index ac3dadc157626..d389262f2125c 100644 --- a/modules/scalar-2.10/pom.xml +++ b/modules/scalar-2.10/pom.xml @@ -31,7 +31,7 @@ ignite-scalar_2.10 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/scalar/pom.xml b/modules/scalar/pom.xml index c6c3f93419c7a..c55fe81c4b2f2 100644 --- a/modules/scalar/pom.xml +++ b/modules/scalar/pom.xml @@ -31,7 +31,7 @@ ignite-scalar - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/schedule/pom.xml b/modules/schedule/pom.xml index 26867019c0b75..fffe30be19f99 100644 --- a/modules/schedule/pom.xml +++ b/modules/schedule/pom.xml @@ -31,7 +31,7 @@ ignite-schedule - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/slf4j/pom.xml b/modules/slf4j/pom.xml index 96b730932b9eb..9167bbe4195df 100644 --- a/modules/slf4j/pom.xml +++ b/modules/slf4j/pom.xml @@ -31,7 +31,7 @@ ignite-slf4j - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/spark-2.10/pom.xml b/modules/spark-2.10/pom.xml index 90f7d0bcdefc3..5d885baa5c7f7 100644 --- a/modules/spark-2.10/pom.xml +++ b/modules/spark-2.10/pom.xml @@ -31,7 +31,7 @@ ignite-spark_2.10 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/spark/pom.xml b/modules/spark/pom.xml index 51241ac33a691..be900f5fb0e82 100644 --- a/modules/spark/pom.xml +++ b/modules/spark/pom.xml @@ -31,7 +31,7 @@ ignite-spark - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/spring-data/pom.xml b/modules/spring-data/pom.xml index 8126fe8cbfcae..b15b490179987 100644 --- a/modules/spring-data/pom.xml +++ b/modules/spring-data/pom.xml @@ -31,7 +31,7 @@ ignite-spring-data - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/spring/pom.xml b/modules/spring/pom.xml index e7391182a4f3d..fd81aefc7deae 100644 --- a/modules/spring/pom.xml +++ b/modules/spring/pom.xml @@ -31,7 +31,7 @@ ignite-spring - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/ssh/pom.xml b/modules/ssh/pom.xml index 64ffabcc9b453..bb4ddbaa723b0 100644 --- a/modules/ssh/pom.xml +++ b/modules/ssh/pom.xml @@ -31,7 +31,7 @@ ignite-ssh - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/storm/pom.xml b/modules/storm/pom.xml index 444aac5aebaa5..e582162f76ede 100644 --- a/modules/storm/pom.xml +++ b/modules/storm/pom.xml @@ -31,7 +31,7 @@ ignite-storm - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/tools/pom.xml b/modules/tools/pom.xml index a5a6360a7b831..b821578c4a11f 100644 --- a/modules/tools/pom.xml +++ b/modules/tools/pom.xml @@ -31,7 +31,7 @@ ignite-tools - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/twitter/pom.xml b/modules/twitter/pom.xml index b6795f3e2a3a4..b45679044113c 100644 --- a/modules/twitter/pom.xml +++ b/modules/twitter/pom.xml @@ -31,7 +31,7 @@ ignite-twitter - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/urideploy/pom.xml b/modules/urideploy/pom.xml index d08372fb4d5b4..1f743f5204ed1 100644 --- a/modules/urideploy/pom.xml +++ b/modules/urideploy/pom.xml @@ -31,7 +31,7 @@ ignite-urideploy - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/visor-console-2.10/pom.xml b/modules/visor-console-2.10/pom.xml index 2cd6864fb172d..27617db0b6603 100644 --- a/modules/visor-console-2.10/pom.xml +++ b/modules/visor-console-2.10/pom.xml @@ -31,7 +31,7 @@ ignite-visor-console_2.10 - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/visor-console/pom.xml b/modules/visor-console/pom.xml index 794a22f3afb4d..9ae2bc479070a 100644 --- a/modules/visor-console/pom.xml +++ b/modules/visor-console/pom.xml @@ -31,7 +31,7 @@ ignite-visor-console - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/visor-plugins/pom.xml b/modules/visor-plugins/pom.xml index b9befadc0dbd4..676cbe1a08fcb 100644 --- a/modules/visor-plugins/pom.xml +++ b/modules/visor-plugins/pom.xml @@ -31,7 +31,7 @@ ignite-visor-plugins - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/web-console/pom.xml b/modules/web-console/pom.xml index f1f0a14408433..c6d9b10bf52ea 100644 --- a/modules/web-console/pom.xml +++ b/modules/web-console/pom.xml @@ -31,7 +31,7 @@ ignite-web-console - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/web-console/web-agent/pom.xml b/modules/web-console/web-agent/pom.xml index e715fbf0bc954..56b1ae44e5829 100644 --- a/modules/web-console/web-agent/pom.xml +++ b/modules/web-console/web-agent/pom.xml @@ -32,7 +32,7 @@ ignite-web-agent jar - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/web/ignite-appserver-test/pom.xml b/modules/web/ignite-appserver-test/pom.xml index 6ee8d158cf4f3..33be03e487528 100644 --- a/modules/web/ignite-appserver-test/pom.xml +++ b/modules/web/ignite-appserver-test/pom.xml @@ -30,7 +30,7 @@ ignite-appserver-test jar - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/web/ignite-websphere-test/pom.xml b/modules/web/ignite-websphere-test/pom.xml index 8d20041ef92bc..46fec6692f324 100644 --- a/modules/web/ignite-websphere-test/pom.xml +++ b/modules/web/ignite-websphere-test/pom.xml @@ -30,7 +30,7 @@ ignite-websphere-test war - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/web/pom.xml b/modules/web/pom.xml index 86d76343883ef..bf4ac88a5f04e 100644 --- a/modules/web/pom.xml +++ b/modules/web/pom.xml @@ -31,7 +31,7 @@ ignite-web - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/yardstick/pom.xml b/modules/yardstick/pom.xml index f496e02344a0e..56de4826ea208 100644 --- a/modules/yardstick/pom.xml +++ b/modules/yardstick/pom.xml @@ -31,7 +31,7 @@ ignite-yardstick - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/yarn/pom.xml b/modules/yarn/pom.xml index 8620ef5b4ddee..8f02d4e12a197 100644 --- a/modules/yarn/pom.xml +++ b/modules/yarn/pom.xml @@ -31,7 +31,7 @@ ignite-yarn - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/zeromq/pom.xml b/modules/zeromq/pom.xml index 85385821cd9a1..62de2c668189b 100644 --- a/modules/zeromq/pom.xml +++ b/modules/zeromq/pom.xml @@ -31,7 +31,7 @@ ignite-zeromq - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/modules/zookeeper/pom.xml b/modules/zookeeper/pom.xml index fd7d64881422e..96c5bb77adcd8 100644 --- a/modules/zookeeper/pom.xml +++ b/modules/zookeeper/pom.xml @@ -31,7 +31,7 @@ ignite-zookeeper - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT http://ignite.apache.org diff --git a/pom.xml b/pom.xml index 56b1a18ec827b..a456da47c0362 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.apache.ignite apache-ignite - 2.1.0-SNAPSHOT + 2.2.0-SNAPSHOT pom From fd5d83c44624baea9466c591f119195cb092df4c Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Mon, 21 Aug 2017 22:06:36 +0700 Subject: [PATCH 075/145] IGNITE-6104 Fixed target. (cherry picked from commit 8775d2c) --- .../components/web-console-footer-links/template.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug index c7c45722f0b07..5c0446786bb26 100644 --- a/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug +++ b/modules/web-console/frontend/app/components/web-console-footer/components/web-console-footer-links/template.pug @@ -14,4 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. -a(href="/api/v1/downloads/agent" target="_blank") Download Agent +a(href="/api/v1/downloads/agent" target="_self") Download Agent From cca9117c805d144ff451694e7324720f8d466d94 Mon Sep 17 00:00:00 2001 From: sboikov Date: Mon, 21 Aug 2017 18:39:12 +0300 Subject: [PATCH 076/145] ignite-5872 Fixed backward compatibility --- .../GridCachePartitionExchangeManager.java | 47 +++++++++-- .../dht/GridClientPartitionTopology.java | 9 ++ .../dht/GridDhtPartitionTopology.java | 5 ++ .../dht/GridDhtPartitionTopologyImpl.java | 5 ++ .../CachePartitionFullCountersMap.java | 36 ++++++++ .../CachePartitionPartialCountersMap.java | 23 +++++ .../GridDhtPartitionsExchangeFuture.java | 41 +++++++-- .../GridDhtPartitionsFullMessage.java | 84 ++++++++++++++++--- .../GridDhtPartitionsSingleMessage.java | 23 +++-- .../IgniteDhtPartitionCountersMap.java | 14 ++-- .../IgniteDhtPartitionCountersMap2.java | 69 +++++++++++++++ 11 files changed, 315 insertions(+), 41 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap2.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 16bd1602940d4..0bef8207dd118 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 @@ -65,6 +65,8 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridClientPartitionTopology; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionFullCountersMap; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.ForceRebalanceExchangeTask; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionDemandMessage; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId; @@ -969,7 +971,7 @@ private void refreshPartitions() { * @param nodes Nodes. */ private void sendAllPartitions(Collection nodes) { - GridDhtPartitionsFullMessage m = createPartitionsFullMessage(true, null, null, null, null); + GridDhtPartitionsFullMessage m = createPartitionsFullMessage(true, false, null, null, null, null); if (log.isDebugEnabled()) log.debug("Sending all partitions [nodeIds=" + U.nodeIds(nodes) + ", msg=" + m + ']'); @@ -994,6 +996,7 @@ private void sendAllPartitions(Collection nodes) { /** * @param compress {@code True} if possible to compress message (properly work only if prepareMarshall/ * finishUnmarshall methods are called). + * @param newCntrMap {@code True} if possible to use {@link CachePartitionFullCountersMap}. * @param exchId Non-null exchange ID if message is created for exchange. * @param lastVer Last version. * @param partHistSuppliers Partition history suppliers map. @@ -1002,6 +1005,7 @@ private void sendAllPartitions(Collection nodes) { */ public GridDhtPartitionsFullMessage createPartitionsFullMessage( boolean compress, + boolean newCntrMap, @Nullable final GridDhtPartitionExchangeId exchId, @Nullable GridCacheVersion lastVer, @Nullable IgniteDhtPartitionHistorySuppliersMap partHistSuppliers, @@ -1040,8 +1044,16 @@ public GridDhtPartitionsFullMessage createPartitionsFullMessage( affCache.similarAffinityKey()); } - if (exchId != null) - m.addPartitionUpdateCounters(grp.groupId(), grp.topology().fullUpdateCounters()); + if (exchId != null) { + CachePartitionFullCountersMap cntrsMap = grp.topology().fullUpdateCounters(); + + if (newCntrMap) + m.addPartitionUpdateCounters(grp.groupId(), cntrsMap); + else { + m.addPartitionUpdateCounters(grp.groupId(), + CachePartitionFullCountersMap.toCountersMap(cntrsMap)); + } + } } } @@ -1058,8 +1070,14 @@ public GridDhtPartitionsFullMessage createPartitionsFullMessage( top.similarAffinityKey()); } - if (exchId != null) - m.addPartitionUpdateCounters(top.groupId(), top.fullUpdateCounters()); + if (exchId != null) { + CachePartitionFullCountersMap cntrsMap = top.fullUpdateCounters(); + + if (newCntrMap) + m.addPartitionUpdateCounters(top.groupId(), cntrsMap); + else + m.addPartitionUpdateCounters(top.groupId(), CachePartitionFullCountersMap.toCountersMap(cntrsMap)); + } } return m; @@ -1113,6 +1131,7 @@ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExc GridDhtPartitionsSingleMessage m = createPartitionsSingleMessage(id, cctx.kernalContext().clientNode(), false, + false, null); if (log.isDebugEnabled()) @@ -1135,12 +1154,14 @@ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExc * @param exchangeId ID. * @param clientOnlyExchange Client exchange flag. * @param sndCounters {@code True} if need send partition update counters. + * @param newCntrMap {@code True} if possible to use {@link CachePartitionPartialCountersMap}. * @return Message. */ public GridDhtPartitionsSingleMessage createPartitionsSingleMessage( @Nullable GridDhtPartitionExchangeId exchangeId, boolean clientOnlyExchange, boolean sndCounters, + boolean newCntrMap, ExchangeActions exchActions ) { GridDhtPartitionsSingleMessage m = new GridDhtPartitionsSingleMessage(exchangeId, @@ -1161,8 +1182,12 @@ public GridDhtPartitionsSingleMessage createPartitionsSingleMessage( locMap, grp.affinity().similarAffinityKey()); - if (sndCounters) - m.partitionUpdateCounters(grp.groupId(), grp.topology().localUpdateCounters(true)); + if (sndCounters) { + CachePartitionPartialCountersMap cntrsMap = grp.topology().localUpdateCounters(true); + + m.addPartitionUpdateCounters(grp.groupId(), + newCntrMap ? cntrsMap : CachePartitionPartialCountersMap.toCountersMap(cntrsMap)); + } } } @@ -1179,8 +1204,12 @@ public GridDhtPartitionsSingleMessage createPartitionsSingleMessage( locMap, top.similarAffinityKey()); - if (sndCounters) - m.partitionUpdateCounters(top.groupId(), top.localUpdateCounters(true)); + if (sndCounters) { + CachePartitionPartialCountersMap cntrsMap = top.localUpdateCounters(true); + + m.addPartitionUpdateCounters(top.groupId(), + newCntrMap ? cntrsMap : CachePartitionPartialCountersMap.toCountersMap(cntrsMap)); + } } return m; 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 a8dda6823eb6f..10d08b9f8aaa5 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 @@ -115,6 +115,9 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology { /** */ private volatile DiscoCache discoCache; + /** */ + private final int parts; + /** * @param cctx Context. * @param grpId Group ID. @@ -130,6 +133,7 @@ public GridClientPartitionTopology( this.cctx = cctx; this.grpId = grpId; this.similarAffKey = similarAffKey; + this.parts = parts; topVer = AffinityTopologyVersion.NONE; @@ -142,6 +146,11 @@ public GridClientPartitionTopology( cntrMap = new CachePartitionFullCountersMap(parts); } + /** {@inheritDoc} */ + @Override public int partitions() { + return parts; + } + /** * @return Key to find caches with similar affinity. */ 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 f6b142bba62fe..f48bd352f1b67 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 @@ -42,6 +42,11 @@ */ @GridToStringExclude public interface GridDhtPartitionTopology { + /** + * @return Total cache partitions. + */ + public int partitions(); + /** * Locks the topology, usually during mapping on locks or transactions. */ 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 f9eafcc1b742f..6ab6bd852a3cf 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 @@ -160,6 +160,11 @@ public GridDhtPartitionTopologyImpl( cntrMap = new CachePartitionFullCountersMap(locParts.length()); } + /** {@inheritDoc} */ + @Override public int partitions() { + return grp.affinityFunction().partitions(); + } + /** {@inheritDoc} */ @Override public int groupId() { return grp.groupId(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java index 1384a558e4769..ebc993c3b8123 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionFullCountersMap.java @@ -19,6 +19,9 @@ import java.io.Serializable; import java.util.Arrays; +import java.util.Map; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.internal.U; /** * @@ -96,4 +99,37 @@ public void clear() { Arrays.fill(initialUpdCntrs, 0); Arrays.fill(updCntrs, 0); } + + /** + * @param map Full counters map. + * @return Regular java map with counters. + */ + public static Map> toCountersMap(CachePartitionFullCountersMap map) { + int partsCnt = map.updCntrs.length; + + Map> map0 = U.newHashMap(partsCnt); + + for (int p = 0; p < partsCnt; p++) + map0.put(p, new T2<>(map.initialUpdCntrs[p], map.updCntrs[p])); + + return map0; + } + + /** + * @param map Regular java map with counters. + * @param partsCnt Total cache partitions. + * @return Full counters map. + */ + static CachePartitionFullCountersMap fromCountersMap(Map> map, int partsCnt) { + CachePartitionFullCountersMap map0 = new CachePartitionFullCountersMap(partsCnt); + + for (Map.Entry> e : map.entrySet()) { + T2 cntrs = e.getValue(); + + map0.initialUpdCntrs[e.getKey()] = cntrs.get1(); + map0.updCntrs[e.getKey()] = cntrs.get2(); + } + + return map0; + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.java index 851ffedfd3f1c..83c0231cb688a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/CachePartitionPartialCountersMap.java @@ -21,8 +21,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.TreeMap; import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteProductVersion; /** * @@ -31,6 +33,9 @@ public class CachePartitionPartialCountersMap implements Serializable { /** */ private static final long serialVersionUID = 0L; + /** */ + static final IgniteProductVersion PARTIAL_COUNTERS_MAP_SINCE = IgniteProductVersion.fromString("2.1.4"); + /** */ public static final CachePartitionPartialCountersMap EMPTY = new CachePartitionPartialCountersMap(); @@ -158,4 +163,22 @@ public static Map> toCountersMap(CachePartitionPartialCo return res; } + + /** + * @param map Partition ID to partition counters map. + * @param partsCnt Total cache partitions. + * @return Partial local counters map. + */ + static CachePartitionPartialCountersMap fromCountersMap(Map> map, int partsCnt) { + CachePartitionPartialCountersMap map0 = new CachePartitionPartialCountersMap(partsCnt); + + TreeMap> sorted = new TreeMap<>(map); + + for (Map.Entry> e : sorted.entrySet()) + map0.add(e.getKey(), e.getValue().get1(), e.getValue().get2()); + + map0.trim(); + + return map0; + } } 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 11fdc45669f91..2249f80043bf4 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 @@ -92,6 +92,7 @@ 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.IgniteProductVersion; import org.apache.ignite.lang.IgniteRunnable; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; @@ -106,6 +107,7 @@ import static org.apache.ignite.events.EventType.EVT_NODE_LEFT; 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.preloader.CachePartitionPartialCountersMap.PARTIAL_COUNTERS_MAP_SINCE; /** * Future for exchanging partition maps. @@ -1227,6 +1229,7 @@ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException msg = cctx.exchange().createPartitionsSingleMessage(exchangeId(), false, true, + node.version().compareToIgnoreTimestamp(PARTIAL_COUNTERS_MAP_SINCE) >= 0, exchActions); Map> partHistReserved0 = partHistReserved; @@ -1254,13 +1257,16 @@ else if (localJoinExchange()) /** * @param compress Message compress flag. + * @param newCntrMap {@code True} if possible to use {@link CachePartitionFullCountersMap}. * @return Message. */ - private GridDhtPartitionsFullMessage createPartitionsMessage(boolean compress) { + private GridDhtPartitionsFullMessage createPartitionsMessage(boolean compress, + boolean newCntrMap) { GridCacheVersion last = lastVer.get(); GridDhtPartitionsFullMessage m = cctx.exchange().createPartitionsFullMessage( compress, + newCntrMap, exchangeId(), last != null ? last : cctx.versions().last(), partHistSuppliers, @@ -1790,9 +1796,14 @@ public void waitAndReplyToNode(final UUID nodeId, final GridDhtPartitionsSingleM if (finishState0 == null) { assert firstDiscoEvt.type() == EVT_NODE_JOINED && CU.clientNode(firstDiscoEvt.eventNode()) : this; + ClusterNode node = cctx.node(nodeId); + + if (node == null) + return; + finishState0 = new FinishState(cctx.localNodeId(), initialVersion(), - createPartitionsMessage(true)); + createPartitionsMessage(true, node.version().compareToIgnoreTimestamp(PARTIAL_COUNTERS_MAP_SINCE) >= 0)); } sendAllPartitionsToNode(finishState0, msg, nodeId); @@ -1927,7 +1938,7 @@ private void onAffinityInitialized(IgniteInternalFuture>> assignmentChange = fut.get(); - GridDhtPartitionsFullMessage m = createPartitionsMessage(false); + GridDhtPartitionsFullMessage m = createPartitionsMessage(false, false); CacheAffinityChangeMessage msg = new CacheAffinityChangeMessage(exchId, m, assignmentChange); @@ -1949,7 +1960,8 @@ private void assignPartitionStates(GridDhtPartitionTopology top) { Map minCntrs = new HashMap<>(); for (Map.Entry e : msgs.entrySet()) { - CachePartitionPartialCountersMap nodeCntrs = e.getValue().partitionUpdateCounters(top.groupId()); + CachePartitionPartialCountersMap nodeCntrs = e.getValue().partitionUpdateCounters(top.groupId(), + top.partitions()); assert nodeCntrs != null; @@ -2225,7 +2237,8 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe GridDhtPartitionTopology top = grp != null ? grp.topology() : cctx.exchange().clientTopology(grpId); - CachePartitionPartialCountersMap cntrs = msg.partitionUpdateCounters(grpId); + CachePartitionPartialCountersMap cntrs = msg.partitionUpdateCounters(grpId, + top.partitions()); if (cntrs != null) top.collectUpdateCounters(cntrs); @@ -2273,7 +2286,10 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe cctx.versions().onExchange(lastVer.get().order()); - GridDhtPartitionsFullMessage msg = createPartitionsMessage(true); + IgniteProductVersion minVer = exchCtx.events().discoveryCache().minimumNodeVersion(); + + GridDhtPartitionsFullMessage msg = createPartitionsMessage(true, + minVer.compareToIgnoreTimestamp(PARTIAL_COUNTERS_MAP_SINCE) >= 0); if (exchCtx.mergeExchanges()) { assert !centralizedAff; @@ -2561,6 +2577,7 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi msg.restoreExchangeId(), cctx.kernalContext().clientNode(), true, + node.version().compareToIgnoreTimestamp(PARTIAL_COUNTERS_MAP_SINCE) >= 0, exchActions); if (localJoinExchange() && finishState0 == null) @@ -2735,11 +2752,12 @@ private void updatePartitionFullMap(AffinityTopologyVersion resTopVer, GridDhtPa for (Map.Entry entry : msg.partitions().entrySet()) { Integer grpId = entry.getKey(); - CachePartitionFullCountersMap cntrMap = msg.partitionUpdateCounters(grpId); - CacheGroupContext grp = cctx.cache().cacheGroup(grpId); if (grp != null) { + CachePartitionFullCountersMap cntrMap = msg.partitionUpdateCounters(grpId, + grp.topology().partitions()); + grp.topology().update(resTopVer, entry.getValue(), cntrMap, @@ -2749,7 +2767,12 @@ private void updatePartitionFullMap(AffinityTopologyVersion resTopVer, GridDhtPa ClusterNode oldest = cctx.discovery().oldestAliveCacheServerNode(AffinityTopologyVersion.NONE); if (oldest != null && oldest.isLocal()) { - cctx.exchange().clientTopology(grpId).update(resTopVer, + GridDhtPartitionTopology top = cctx.exchange().clientTopology(grpId); + + CachePartitionFullCountersMap cntrMap = msg.partitionUpdateCounters(grpId, + top.partitions()); + + top.update(resTopVer, entry.getValue(), cntrMap, Collections.emptySet()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java index 39394147132b1..19ec0a8319ff1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java @@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -68,6 +69,14 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa /** Serialized partitions counters. */ private byte[] partCntrsBytes; + /** Partitions update counters. */ + @GridToStringInclude + @GridDirectTransient + private IgniteDhtPartitionCountersMap2 partCntrs2; + + /** Serialized partitions counters. */ + private byte[] partCntrsBytes2; + /** Partitions history suppliers. */ @GridToStringInclude @GridDirectTransient @@ -147,6 +156,8 @@ public GridDhtPartitionsFullMessage(@Nullable GridDhtPartitionExchangeId id, cp.partsBytes = partsBytes; cp.partCntrs = partCntrs; cp.partCntrsBytes = partCntrsBytes; + cp.partCntrs2 = partCntrs2; + cp.partCntrsBytes2 = partCntrsBytes2; cp.partHistSuppliers = partHistSuppliers; cp.partHistSuppliersBytes = partHistSuppliersBytes; cp.partsToReload = partsToReload; @@ -273,7 +284,7 @@ public void addFullPartitionsMap(int grpId, GridDhtPartitionFullMap fullMap, @Nu * @param grpId Cache group ID. * @param cntrMap Partition update counters. */ - public void addPartitionUpdateCounters(int grpId, CachePartitionFullCountersMap cntrMap) { + public void addPartitionUpdateCounters(int grpId, Map> cntrMap) { if (partCntrs == null) partCntrs = new IgniteDhtPartitionCountersMap(); @@ -282,10 +293,30 @@ public void addPartitionUpdateCounters(int grpId, CachePartitionFullCountersMap /** * @param grpId Cache group ID. + * @param cntrMap Partition update counters. + */ + public void addPartitionUpdateCounters(int grpId, CachePartitionFullCountersMap cntrMap) { + if (partCntrs2 == null) + partCntrs2 = new IgniteDhtPartitionCountersMap2(); + + partCntrs2.putIfAbsent(grpId, cntrMap); + } + + /** + * @param grpId Cache group ID. + * @param partsCnt Total cache partitions. * @return Partition update counters. */ - public CachePartitionFullCountersMap partitionUpdateCounters(int grpId) { - return partCntrs == null ? null : partCntrs.get(grpId); + public CachePartitionFullCountersMap partitionUpdateCounters(int grpId, int partsCnt) { + if (partCntrs2 != null) + return partCntrs2.get(grpId); + + if (partCntrs == null) + return null; + + Map> map = partCntrs.get(grpId); + + return map != null ? CachePartitionFullCountersMap.fromCountersMap(map, partsCnt) : null; } /** @@ -325,6 +356,7 @@ void setErrorsMap(Map errs) { boolean marshal = (!F.isEmpty(parts) && partsBytes == null) || (partCntrs != null && !partCntrs.empty() && partCntrsBytes == null) || + (partCntrs2 != null && !partCntrs2.empty() && partCntrsBytes2 == null) || (partHistSuppliers != null && partHistSuppliersBytes == null) || (partsToReload != null && partsToReloadBytes == null) || (!F.isEmpty(errs) && errsBytes == null); @@ -332,6 +364,7 @@ void setErrorsMap(Map errs) { if (marshal) { byte[] partsBytes0 = null; byte[] partCntrsBytes0 = null; + byte[] partCntrsBytes20 = null; byte[] partHistSuppliersBytes0 = null; byte[] partsToReloadBytes0 = null; byte[] errsBytes0 = null; @@ -342,6 +375,9 @@ void setErrorsMap(Map errs) { if (partCntrs != null && !partCntrs.empty() && partCntrsBytes == null) partCntrsBytes0 = U.marshal(ctx, partCntrs); + if (partCntrs2 != null && !partCntrs2.empty() && partCntrsBytes2 == null) + partCntrsBytes20 = U.marshal(ctx, partCntrs2); + if (partHistSuppliers != null && partHistSuppliersBytes == null) partHistSuppliersBytes0 = U.marshal(ctx, partHistSuppliers); @@ -357,12 +393,14 @@ void setErrorsMap(Map errs) { try { byte[] partsBytesZip = U.zip(partsBytes0); byte[] partCntrsBytesZip = U.zip(partCntrsBytes0); + byte[] partCntrsBytes2Zip = U.zip(partCntrsBytes20); byte[] partHistSuppliersBytesZip = U.zip(partHistSuppliersBytes0); byte[] partsToReloadBytesZip = U.zip(partsToReloadBytes0); byte[] exsBytesZip = U.zip(errsBytes0); partsBytes0 = partsBytesZip; partCntrsBytes0 = partCntrsBytesZip; + partCntrsBytes20 = partCntrsBytes2Zip; partHistSuppliersBytes0 = partHistSuppliersBytesZip; partsToReloadBytes0 = partsToReloadBytesZip; errsBytes0 = exsBytesZip; @@ -376,6 +414,7 @@ void setErrorsMap(Map errs) { partsBytes = partsBytes0; partCntrsBytes = partCntrsBytes0; + partCntrsBytes2 = partCntrsBytes20; partHistSuppliersBytes = partHistSuppliersBytes0; partsToReloadBytes = partsToReloadBytes0; errsBytes = errsBytes0; @@ -444,6 +483,13 @@ public void topologyVersion(AffinityTopologyVersion topVer) { partCntrs = U.unmarshal(ctx, partCntrsBytes, U.resolveClassLoader(ldr, ctx.gridConfig())); } + if (partCntrsBytes2 != null && partCntrs2 == null) { + if (compressed()) + partCntrs2 = U.unmarshalZip(ctx.marshaller(), partCntrsBytes2, U.resolveClassLoader(ldr, ctx.gridConfig())); + else + partCntrs2 = U.unmarshal(ctx, partCntrsBytes2, U.resolveClassLoader(ldr, ctx.gridConfig())); + } + if (partHistSuppliersBytes != null && partHistSuppliers == null) { if (compressed()) partHistSuppliers = U.unmarshalZip(ctx.marshaller(), partHistSuppliersBytes, U.resolveClassLoader(ldr, ctx.gridConfig())); @@ -518,30 +564,36 @@ public void topologyVersion(AffinityTopologyVersion topVer) { writer.incrementState(); case 10: - if (!writer.writeByteArray("partHistSuppliersBytes", partHistSuppliersBytes)) + if (!writer.writeByteArray("partCntrsBytes2", partCntrsBytes2)) return false; writer.incrementState(); case 11: - if (!writer.writeByteArray("partsBytes", partsBytes)) + if (!writer.writeByteArray("partHistSuppliersBytes", partHistSuppliersBytes)) return false; writer.incrementState(); case 12: - if (!writer.writeByteArray("partsToReloadBytes", partsToReloadBytes)) + if (!writer.writeByteArray("partsBytes", partsBytes)) return false; writer.incrementState(); case 13: - if (!writer.writeMessage("resTopVer", resTopVer)) + if (!writer.writeByteArray("partsToReloadBytes", partsToReloadBytes)) return false; writer.incrementState(); case 14: + if (!writer.writeMessage("resTopVer", resTopVer)) + return false; + + writer.incrementState(); + + case 15: if (!writer.writeMessage("topVer", topVer)) return false; @@ -604,7 +656,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 10: - partHistSuppliersBytes = reader.readByteArray("partHistSuppliersBytes"); + partCntrsBytes2 = reader.readByteArray("partCntrsBytes2"); if (!reader.isLastRead()) return false; @@ -612,7 +664,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 11: - partsBytes = reader.readByteArray("partsBytes"); + partHistSuppliersBytes = reader.readByteArray("partHistSuppliersBytes"); if (!reader.isLastRead()) return false; @@ -620,7 +672,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 12: - partsToReloadBytes = reader.readByteArray("partsToReloadBytes"); + partsBytes = reader.readByteArray("partsBytes"); if (!reader.isLastRead()) return false; @@ -628,7 +680,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 13: - resTopVer = reader.readMessage("resTopVer"); + partsToReloadBytes = reader.readByteArray("partsToReloadBytes"); if (!reader.isLastRead()) return false; @@ -636,6 +688,14 @@ public void topologyVersion(AffinityTopologyVersion topVer) { reader.incrementState(); case 14: + resTopVer = reader.readMessage("resTopVer"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 15: topVer = reader.readMessage("topVer"); if (!reader.isLastRead()) @@ -655,7 +715,7 @@ public void topologyVersion(AffinityTopologyVersion topVer) { /** {@inheritDoc} */ @Override public byte fieldsCount() { - return 15; + return 16; } /** {@inheritDoc} */ 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 44815cabdd549..0bad0a5754d6e 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 @@ -32,6 +32,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -61,7 +62,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes /** Partitions update counters. */ @GridToStringInclude @GridDirectTransient - private Map partCntrs; + private Map partCntrs; /** Serialized partitions counters. */ private byte[] partCntrsBytes; @@ -189,7 +190,7 @@ public void addLocalPartitionMap(int cacheId, GridDhtPartitionMap locMap, @Nulla * @param grpId Cache group ID. * @param cntrMap Partition update counters. */ - public void partitionUpdateCounters(int grpId, CachePartitionPartialCountersMap cntrMap) { + public void addPartitionUpdateCounters(int grpId, Object cntrMap) { if (partCntrs == null) partCntrs = new HashMap<>(); @@ -198,12 +199,24 @@ public void partitionUpdateCounters(int grpId, CachePartitionPartialCountersMap /** * @param grpId Cache group ID. + * @param partsCnt Total cache partitions. * @return Partition update counters. */ - public CachePartitionPartialCountersMap partitionUpdateCounters(int grpId) { - CachePartitionPartialCountersMap res = partCntrs == null ? null : partCntrs.get(grpId); + @SuppressWarnings("unchecked") + public CachePartitionPartialCountersMap partitionUpdateCounters(int grpId, int partsCnt) { + Object res = partCntrs == null ? null : partCntrs.get(grpId); - return res == null ? CachePartitionPartialCountersMap.EMPTY : res; + if (res == null) + return null; + + if (res instanceof CachePartitionPartialCountersMap) + return (CachePartitionPartialCountersMap)res; + + assert res instanceof Map : res; + + Map> map = (Map>)res; + + return CachePartitionPartialCountersMap.fromCountersMap(map, partsCnt); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java index e7954d960ce44..dc2fbf879ccc8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap.java @@ -19,8 +19,10 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.preloader; import java.io.Serializable; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.apache.ignite.internal.util.typedef.T2; /** * Partition counters map. @@ -30,7 +32,7 @@ public class IgniteDhtPartitionCountersMap implements Serializable { private static final long serialVersionUID = 0L; /** */ - private Map map; + private Map>> map; /** * @return {@code True} if map is empty. @@ -43,7 +45,7 @@ public synchronized boolean empty() { * @param cacheId Cache ID. * @param cntrMap Counters map. */ - public synchronized void putIfAbsent(int cacheId, CachePartitionFullCountersMap cntrMap) { + public synchronized void putIfAbsent(int cacheId, Map> cntrMap) { if (map == null) map = new HashMap<>(); @@ -55,14 +57,14 @@ public synchronized void putIfAbsent(int cacheId, CachePartitionFullCountersMap * @param cacheId Cache ID. * @return Counters map. */ - public synchronized CachePartitionFullCountersMap get(int cacheId) { + public synchronized Map> get(int cacheId) { if (map == null) - return null; + map = new HashMap<>(); - CachePartitionFullCountersMap cntrMap = map.get(cacheId); + Map> cntrMap = map.get(cacheId); if (cntrMap == null) - return null; + return Collections.emptyMap(); return cntrMap; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap2.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap2.java new file mode 100644 index 0000000000000..d1e6d99c4aa20 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtPartitionCountersMap2.java @@ -0,0 +1,69 @@ +/* + * 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.preloader; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Partition counters map. + */ +public class IgniteDhtPartitionCountersMap2 implements Serializable { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private Map map; + + /** + * @return {@code True} if map is empty. + */ + public synchronized boolean empty() { + return map == null || map.isEmpty(); + } + + /** + * @param cacheId Cache ID. + * @param cntrMap Counters map. + */ + public synchronized void putIfAbsent(int cacheId, CachePartitionFullCountersMap cntrMap) { + if (map == null) + map = new HashMap<>(); + + if (!map.containsKey(cacheId)) + map.put(cacheId, cntrMap); + } + + /** + * @param cacheId Cache ID. + * @return Counters map. + */ + public synchronized CachePartitionFullCountersMap get(int cacheId) { + if (map == null) + return null; + + CachePartitionFullCountersMap cntrMap = map.get(cacheId); + + if (cntrMap == null) + return null; + + return cntrMap; + } +} From 1072654c8762113bc49a658f2898475381b47832 Mon Sep 17 00:00:00 2001 From: Sergey Chugunov Date: Mon, 21 Aug 2017 20:05:05 +0300 Subject: [PATCH 077/145] IGNITE-6052 class rename was reverted --- .../processors/cluster/GridClusterStateProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java index 3ad75cc9fddbd..cdfab21c80854 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java @@ -506,7 +506,7 @@ private void sendComputeChangeGlobalState(boolean activate, final GridFutureAdap IgniteCompute comp = ((ClusterGroupAdapter)ctx.cluster().get().forServers()).compute(); - IgniteFuture fut = comp.runAsync(new ChangeGlobalStateComputeRequest(activate)); + IgniteFuture fut = comp.runAsync(new ClientChangeGlobalStateComputeRequest(activate)); fut.listen(new CI1() { @Override public void apply(IgniteFuture fut) { @@ -884,7 +884,7 @@ private void onAllReceived() { /** * */ - private static class ChangeGlobalStateComputeRequest implements IgniteRunnable { + private static class ClientChangeGlobalStateComputeRequest implements IgniteRunnable { /** */ private static final long serialVersionUID = 0L; @@ -898,7 +898,7 @@ private static class ChangeGlobalStateComputeRequest implements IgniteRunnable { /** * @param activate New cluster state. */ - private ChangeGlobalStateComputeRequest(boolean activate) { + private ClientChangeGlobalStateComputeRequest(boolean activate) { this.activate = activate; } From cf97f3dd903549d3b5968ceb5ca9eda29ef521cf Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Mon, 21 Aug 2017 20:04:04 +0300 Subject: [PATCH 078/145] ignite-6035 Clear indexes on cache clear/destroy Signed-off-by: Andrey Gura --- .../processors/cache/GridCacheProcessor.java | 4 ++-- .../cache/persistence/MetadataStorage.java | 2 +- .../processors/query/GridQueryIndexing.java | 3 ++- .../processors/query/GridQueryProcessor.java | 21 ++++++++++++------- ...niteClientCacheInitializationFailTest.java | 2 +- .../IgnitePersistentStoreCacheGroupsTest.java | 4 ++-- .../query/h2/opt/GridH2SpatialIndex.java | 4 ++-- .../processors/query/h2/H2Schema.java | 2 ++ .../query/h2/H2TableDescriptor.java | 3 +-- .../processors/query/h2/IgniteH2Indexing.java | 6 +++++- .../query/h2/database/H2TreeIndex.java | 16 +++++++------- .../query/h2/opt/GridH2IndexBase.java | 4 +++- .../processors/query/h2/opt/GridH2Table.java | 16 ++++++++++++-- 13 files changed, 55 insertions(+), 32 deletions(-) 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 bf3ee0d9abdee..bd950fa3bef4e 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 @@ -1016,7 +1016,7 @@ private void stopCacheOnReconnect(GridCacheContext cctx, List assert desc != null : cctx.name(); - ctx.query().onCacheStop0(cctx.name()); + ctx.query().onCacheStop0(cctx.name(), false); ctx.query().onCacheStart0(cctx, desc.schema()); } } @@ -1142,7 +1142,7 @@ private void stopCache(GridCacheAdapter cache, boolean cancel, boolean des cache.stop(); - ctx.kernalContext().query().onCacheStop(ctx); + ctx.kernalContext().query().onCacheStop(ctx, destroy); if (isNearEnabled(ctx)) { GridDhtCacheAdapter dht = ctx.near().dht(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java index 359e54ebe7cc9..e667807b6d6a4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MetadataStorage.java @@ -291,7 +291,7 @@ private static void storeRow( PageUtils.putUnsignedByte(dstPageAddr, dstOff, len); dstOff++; - PageHandler.copyMemory(srcPageAddr, dstPageAddr, srcOff, dstOff, len); + PageHandler.copyMemory(srcPageAddr, srcOff, dstPageAddr, dstOff, len); srcOff += len; dstOff += len; 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 9184327f75d8a..8eecfc21a4bdd 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 @@ -179,9 +179,10 @@ public void registerCache(String cacheName, String schemaName, GridCacheContext< * Unregisters cache. * * @param cacheName Cache name. + * @param destroy Destroy flag. * @throws IgniteCheckedException If failed to drop cache schema. */ - public void unregisterCache(String cacheName) throws IgniteCheckedException; + public void unregisterCache(String cacheName, boolean destroy) throws IgniteCheckedException; /** * Registers type if it was not known before or updates it otherwise. 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 1d154d39e69b9..d5ac8fcb39762 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 @@ -65,7 +65,6 @@ 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.StoredCacheData; import org.apache.ignite.internal.processors.cache.query.CacheQueryFuture; import org.apache.ignite.internal.processors.cache.query.CacheQueryType; import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType; @@ -836,8 +835,9 @@ public void onCacheStart(GridCacheContext cctx, QuerySchema schema) throws Ignit /** * @param cctx Cache context. + * @param destroy Destroy flag. */ - public void onCacheStop(GridCacheContext cctx) { + public void onCacheStop(GridCacheContext cctx, boolean destroy) { if (idx == null) return; @@ -845,7 +845,7 @@ public void onCacheStop(GridCacheContext cctx) { return; try { - onCacheStop0(cctx.name()); + onCacheStop0(cctx.name(), destroy); } finally { busyLock.leaveBusy(); @@ -1371,8 +1371,12 @@ public void dynamicTableDrop(String cacheName, String tblName, boolean ifExists) * @param cands Candidates. * @throws IgniteCheckedException If failed. */ - private void registerCache0(String cacheName, String schemaName, GridCacheContext cctx, - Collection cands) throws IgniteCheckedException { + private void registerCache0( + String cacheName, + String schemaName, + GridCacheContext cctx, + Collection cands + ) throws IgniteCheckedException { synchronized (stateMux) { if (idx != null) idx.registerCache(cacheName, schemaName, cctx); @@ -1412,7 +1416,7 @@ private void registerCache0(String cacheName, String schemaName, GridCacheContex cacheNames.add(CU.mask(cacheName)); } catch (IgniteCheckedException | RuntimeException e) { - onCacheStop0(cacheName); + onCacheStop0(cacheName, true); throw e; } @@ -1424,8 +1428,9 @@ private void registerCache0(String cacheName, String schemaName, GridCacheContex * Use with {@link #busyLock} where appropriate. * * @param cacheName Cache name. + * @param destroy Destroy flag. */ - public void onCacheStop0(String cacheName) { + public void onCacheStop0(String cacheName, boolean destroy) { if (idx == null) return; @@ -1463,7 +1468,7 @@ public void onCacheStop0(String cacheName) { // Notify indexing. try { - idx.unregisterCache(cacheName); + idx.unregisterCache(cacheName, destroy); } catch (Exception e) { U.error(log, "Failed to clear indexing on cache unregister (will ignore): " + cacheName, e); 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 51a806ea2ad45..fa6dd707c5f35 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 @@ -291,7 +291,7 @@ private static class FailedIndexing implements GridQueryIndexing { } /** {@inheritDoc} */ - @Override public void unregisterCache(String spaceName) throws IgniteCheckedException { + @Override public void unregisterCache(String spaceName, boolean destroy) throws IgniteCheckedException { // No-op } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java index b39b8cb6b6f9b..dc6517731e226 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePersistentStoreCacheGroupsTest.java @@ -186,12 +186,12 @@ public void testClusterRestartCachesWithH2Indexes() throws Exception { startGrids(3); - awaitPartitionMapExchange(); - node = ignite(0); node.active(true); + awaitPartitionMapExchange(); + checkPersons(caches, node); checkPersonsQuery(caches, node); 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 d83e860def35c..d1f775ea82559 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 @@ -249,7 +249,7 @@ private SpatialKey getEnvelope(SearchRow row, long rowId) { } /** {@inheritDoc} */ - @Override public void destroy() { + @Override public void destroy(boolean rmIndex) { Lock l = lock.writeLock(); l.lock(); @@ -263,7 +263,7 @@ private SpatialKey getEnvelope(SearchRow row, long rowId) { l.unlock(); } - super.destroy(); + super.destroy(rmIndex); } /** {@inheritDoc} */ diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java index deca4b2a2d9d8..9c110997c7d1b 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java @@ -114,6 +114,7 @@ public void drop(H2TableDescriptor tbl) { tbl.onDrop(); tbls.remove(tbl.tableName()); + typeToTbl.remove(tbl.typeName()); } @@ -125,6 +126,7 @@ public void dropAll() { tbl.onDrop(); tbls.clear(); + typeToTbl.clear(); } } diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java index 589f90ed6a4cb..5abfc4bb1c102 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java @@ -310,9 +310,8 @@ public GridH2IndexBase createUserIndex(GridQueryIndexDescriptor idxDesc) { return idx.createSortedIndex(schema, idxDesc.name(), tbl, false, cols, idxDesc.inlineSize()); } - else if (idxDesc.type() == QueryIndexType.GEOSPATIAL) { + else if (idxDesc.type() == QueryIndexType.GEOSPATIAL) return H2Utils.createSpatialIndex(tbl, idxDesc.name(), cols.toArray(new IndexColumn[cols.size()])); - } throw new IllegalStateException("Index type: " + idxDesc.type()); } 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 6896f18d6a175..0f97a4ba1e975 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 @@ -2156,7 +2156,7 @@ private boolean isDefaultSchema(String schemaName) { } /** {@inheritDoc} */ - @Override public void unregisterCache(String cacheName) { + @Override public void unregisterCache(String cacheName, boolean destroy) { String schemaName = schema(cacheName); boolean dflt = isDefaultSchema(schemaName); @@ -2176,6 +2176,10 @@ private boolean isDefaultSchema(String schemaName) { for (H2TableDescriptor tbl : schema.tables()) { if (F.eq(tbl.cache().name(), cacheName)) { try { + boolean removeIdx = !ctx.cache().context().database().persistenceEnabled() || destroy; + + tbl.table().setRemoveIndexOnDestroy(removeIdx); + dropTable(tbl); } catch (IgniteCheckedException e) { 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 eb579c34c1a44..35bfdc1d647fe 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 @@ -302,17 +302,15 @@ private List getAvailableInlineColumns(IndexColumn[] cols) { } /** {@inheritDoc} */ - @Override public void destroy() { + @Override public void destroy(boolean rmvIndex) { try { - if (cctx.affinityNode()) { - if (!cctx.kernalContext().cache().context().database().persistenceEnabled()) { - for (int i = 0; i < segments.length; i++) { - H2Tree tree = segments[i]; + if (cctx.affinityNode() && rmvIndex) { + for (int i = 0; i < segments.length; i++) { + H2Tree tree = segments[i]; - tree.destroy(); + tree.destroy(); - dropMetaPage(tree.getName(), i); - } + dropMetaPage(tree.getName(), i); } } } @@ -320,7 +318,7 @@ private List getAvailableInlineColumns(IndexColumn[] cols) { throw new IgniteException(e); } finally { - super.destroy(); + super.destroy(rmvIndex); } } 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 5d5fb56341d66..919ff58a00bfb 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 @@ -157,8 +157,10 @@ protected final void initDistributedJoinMessaging(GridH2Table tbl) { * Attempts to destroys index and release all the resources. * We use this method instead of {@link #close(Session)} because that method * is used by H2 internally. + * + * @param rmv Flag remove. */ - public void destroy() { + public void destroy(boolean rmv) { if (msgLsnr != null) kernalContext().io().removeMessageListener(msgTopic, msgLsnr); } 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 f76cb5fd42393..107e3bb93e709 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 @@ -109,6 +109,9 @@ public class GridH2Table extends TableBase { /** Identifier as string. */ private final String identifierStr; + /** Flag remove index or not when table will be destroyed. */ + private volatile boolean rmIndex; + /** * Creates table. * @@ -319,7 +322,7 @@ private void ensureNotDestroyed() { // We have to call destroy here if we are who has removed this index from the table. if (idx instanceof GridH2IndexBase) - ((GridH2IndexBase)idx).destroy(); + ((GridH2IndexBase)idx).destroy(rmIndex); } } @@ -355,13 +358,22 @@ public void destroy() { for (int i = 1, len = idxs.size(); i < len; i++) if (idxs.get(i) instanceof GridH2IndexBase) - index(i).destroy(); + index(i).destroy(rmIndex); } finally { unlock(true); } } + /** + * If flag {@code True}, index will be destroyed when table {@link #destroy()}. + * + * @param rmIndex Flag indicate remove index on destroy or not. + */ + public void setRemoveIndexOnDestroy(boolean rmIndex){ + this.rmIndex = rmIndex; + } + /** {@inheritDoc} */ @Override public void unlock(Session ses) { Boolean exclusive = sessions.remove(ses); From 47b84ec62fb988faad438bdfde4ee48165a4065f Mon Sep 17 00:00:00 2001 From: Andrey Gura Date: Mon, 21 Aug 2017 20:36:56 +0300 Subject: [PATCH 079/145] Visor compilation issues are fixed. --- .../scala/org/apache/ignite/visor/visor.scala | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala index 6db731b81aa59..943e13a6a7626 100644 --- a/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala +++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala @@ -29,12 +29,10 @@ import org.apache.ignite.internal.util.lang.{GridFunc => F} import org.apache.ignite.internal.util.typedef._ import org.apache.ignite.internal.util.{GridConfigurationFinder, IgniteUtils => U} import org.apache.ignite.lang._ -import org.apache.ignite.thread.IgniteThreadPoolExecutor +import org.apache.ignite.thread.{IgniteThreadFactory, IgniteThreadPoolExecutor} import org.apache.ignite.visor.commands.common.VisorTextTable - import jline.console.ConsoleReader import org.jetbrains.annotations.Nullable - import java.io._ import java.lang.{Boolean => JavaBoolean} import java.net._ @@ -221,7 +219,13 @@ object visor extends VisorTag { @volatile private var logStarted = false /** Internal thread pool. */ - @volatile var pool: ExecutorService = new IgniteThreadPoolExecutor() + @volatile var pool: ExecutorService = new IgniteThreadPoolExecutor( + 100, + 100, + 0, + new LinkedBlockingDeque[Runnable], + new IgniteThreadFactory(null, null) + ) /** Configuration file path, if any. */ @volatile var cfgPath: String = _ @@ -2173,7 +2177,13 @@ object visor extends VisorTag { Thread.currentThread.interrupt() } - pool = new IgniteThreadPoolExecutor() + pool = new IgniteThreadPoolExecutor( + 100, + 100, + 0, + new LinkedBlockingDeque[Runnable], + new IgniteThreadFactory(null, null) + ) } // Call all close callbacks. From 6e0fd7222e64b3a5e05204b300b62f60e3142e40 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Mon, 21 Aug 2017 21:12:41 +0300 Subject: [PATCH 080/145] ignite-gg-12639 Fill factor metric is not working with enabled persistence Signed-off-by: Andrey Gura --- .../GridCacheDatabaseSharedManager.java | 28 +++++++++++++++ .../persistence/GridCacheOffheapManager.java | 27 ++++++++++++++ .../IgniteCacheDatabaseSharedManager.java | 36 +++++++++++++++++-- .../cache/persistence/MemoryMetricsImpl.java | 26 +++++++------- .../persistence/freelist/FreeListImpl.java | 7 ++-- 5 files changed, 107 insertions(+), 17 deletions(-) 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 6d19ffb1d166e..b4f428bf7ab83 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 @@ -133,6 +133,7 @@ import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.internal.util.worker.GridWorker; import org.apache.ignite.lang.IgniteBiTuple; +import org.apache.ignite.lang.IgniteOutClosure; import org.apache.ignite.mxbean.PersistenceMetricsMXBean; import org.apache.ignite.thread.IgniteThread; import org.apache.ignite.thread.IgniteThreadPoolExecutor; @@ -515,6 +516,33 @@ private void unRegistrateMetricsMBean() { } } + /** {@inheritDoc} */ + @Override protected IgniteOutClosure fillFactorProvider(final String memPlcName) { + return new IgniteOutClosure() { + @Override public Float apply() { + long loadSize = 0L; + long totalSize = 0L; + + for (CacheGroupContext grpCtx : cctx.cache().cacheGroups()) { + if (!grpCtx.memoryPolicy().config().getName().equals(memPlcName)) + continue; + + assert grpCtx.offheap() instanceof GridCacheOffheapManager; + + T2 fillFactor = ((GridCacheOffheapManager)grpCtx.offheap()).fillFactor(); + + loadSize += fillFactor.get1(); + totalSize += fillFactor.get2(); + } + + if (totalSize == 0) + return (float) 0; + + return (float) loadSize / totalSize; + } + }; + } + /** {@inheritDoc} */ @Override public void readCheckpointAndRestoreMemory(List cachesToStart) throws IgniteCheckedException { checkpointReadLock(); 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 83a9f55786827..5cd12afc3bf89 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 @@ -66,6 +66,7 @@ import org.apache.ignite.internal.util.GridUnsafe; import org.apache.ignite.internal.util.lang.GridCursor; import org.apache.ignite.internal.util.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiTuple; @@ -596,6 +597,32 @@ private Metas getOrAllocateCacheMetas() throws IgniteCheckedException { } } + /** + * Calculates fill factor of all partition data stores. + * + * @return Tuple (numenator, denominator). + */ + T2 fillFactor() { + long loadSize = 0; + long totalSize = 0; + + for (CacheDataStore store : partDataStores.values()) { + assert store instanceof GridCacheDataStore; + + FreeListImpl freeList = ((GridCacheDataStore)store).freeList; + + if (freeList == null) + continue; + + T2 fillFactor = freeList.fillFactor(); + + loadSize += fillFactor.get1(); + totalSize += fillFactor.get2(); + } + + return new T2<>(loadSize, totalSize); + } + /** * */ 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 e07c51ea76a28..69feac8f29c15 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 @@ -55,9 +55,11 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList; import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiTuple; +import org.apache.ignite.lang.IgniteOutClosure; import org.apache.ignite.mxbean.MemoryMetricsMXBean; import org.jetbrains.annotations.Nullable; @@ -175,8 +177,6 @@ protected void initPageMemoryDataStructures(MemoryConfiguration dbCfg) throws Ig 0L, true); - memMetrics.freeList(freeList); - freeListMap.put(memPlcCfg.getName(), freeList); } @@ -273,7 +273,7 @@ private void addMemoryPolicy( if (dfltMemPlcName == null) dfltMemPlcName = DFLT_MEM_PLC_DEFAULT_NAME; - MemoryMetricsImpl memMetrics = new MemoryMetricsImpl(memPlcCfg); + MemoryMetricsImpl memMetrics = new MemoryMetricsImpl(memPlcCfg, fillFactorProvider(memPlcName)); MemoryPolicy memPlc = initMemory(memCfg, memPlcCfg, memMetrics); @@ -288,6 +288,36 @@ else if (memPlcName.equals(DFLT_MEM_PLC_DEFAULT_NAME)) "Please check Memory Policies configuration."); } + /** + * Closure that can be used to compute fill factor for provided memory policy. + * + * @param memPlcName Memory policy name. + * @return Closure. + */ + protected IgniteOutClosure fillFactorProvider(final String memPlcName) { + return new IgniteOutClosure() { + private FreeListImpl freeList; + + @Override public Float apply() { + if (freeList == null) { + FreeListImpl freeList0 = freeListMap.get(memPlcName); + + if (freeList0 == null) + return (float) 0; + + freeList = freeList0; + } + + T2 fillFactor = freeList.fillFactor(); + + if (fillFactor.get2() == 0) + return (float) 0; + + return (float) fillFactor.get1() / fillFactor.get2(); + } + }; + } + /** * @param memPlcsCfgs User-defined memory policy configurations. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MemoryMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MemoryMetricsImpl.java index 271767c546681..32618744a0643 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MemoryMetricsImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/MemoryMetricsImpl.java @@ -19,9 +19,10 @@ import org.apache.ignite.MemoryMetrics; import org.apache.ignite.configuration.MemoryPolicyConfiguration; import org.apache.ignite.internal.pagemem.PageMemory; -import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeListImpl; import org.apache.ignite.internal.processors.cache.ratemetrics.HitRateMetrics; import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteOutClosure; +import org.jetbrains.annotations.Nullable; import org.jsr166.LongAdder8; /** @@ -29,7 +30,7 @@ */ public class MemoryMetricsImpl implements MemoryMetrics { /** */ - private FreeListImpl freeList; + private final IgniteOutClosure fillFactorProvider; /** */ private final LongAdder8 totalAllocatedPages = new LongAdder8(); @@ -68,9 +69,17 @@ public class MemoryMetricsImpl implements MemoryMetrics { /** * @param memPlcCfg MemoryPolicyConfiguration. - */ + */ public MemoryMetricsImpl(MemoryPolicyConfiguration memPlcCfg) { + this(memPlcCfg, null); + } + + /** + * @param memPlcCfg MemoryPolicyConfiguration. + */ + public MemoryMetricsImpl(MemoryPolicyConfiguration memPlcCfg, @Nullable IgniteOutClosure fillFactorProvider) { this.memPlcCfg = memPlcCfg; + this.fillFactorProvider = fillFactorProvider; metricsEnabled = memPlcCfg.isMetricsEnabled(); @@ -114,10 +123,10 @@ public MemoryMetricsImpl(MemoryPolicyConfiguration memPlcCfg) { /** {@inheritDoc} */ @Override public float getPagesFillFactor() { - if (!metricsEnabled || freeList == null) + if (!metricsEnabled || fillFactorProvider == null) return 0; - return freeList.fillFactor(); + return fillFactorProvider.apply(); } /** {@inheritDoc} */ @@ -274,11 +283,4 @@ public void subIntervals(int subInts) { allocRate = new HitRateMetrics((int) rateTimeInterval, subInts); pageReplaceRate = new HitRateMetrics((int) rateTimeInterval, subInts); } - - /** - * @param freeList Free list. - */ - void freeList(FreeListImpl freeList) { - this.freeList = freeList; - } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListImpl.java index 53a33c12c0d64..3eb62ae937953 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListImpl.java @@ -41,6 +41,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseBag; 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.util.typedef.T2; import org.apache.ignite.internal.util.typedef.internal.U; /** @@ -359,8 +360,10 @@ public FreeListImpl( /** * Calculates average fill factor over FreeListImpl instance. + * + * @return Tuple (numenator, denominator). */ - public float fillFactor() { + public T2 fillFactor() { long pageSize = pageSize(); long totalSize = 0; @@ -376,7 +379,7 @@ public float fillFactor() { totalSize += pages * pageSize; } - return totalSize == 0 ? -1L : ((float) loadSize / totalSize); + return totalSize == 0 ? new T2<>(0L, 0L) : new T2<>(loadSize, totalSize); } /** {@inheritDoc} */ From bbafa1e946121b7143101d4060effc48ed74a0b3 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Tue, 22 Aug 2017 10:59:15 +0700 Subject: [PATCH 081/145] IGNITE-6136 Web Console: implemented universal version check. (cherry picked from commit 1a42e78) --- .../app/modules/agent/AgentManager.service.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js index 6bc31a09bc039..67e670487ff27 100644 --- a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js +++ b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js @@ -73,10 +73,10 @@ class ConnectionState { } export default class IgniteAgentManager { - static $inject = ['$rootScope', '$q', '$transitions', 'igniteSocketFactory', 'AgentModal', 'UserNotifications']; + static $inject = ['$rootScope', '$q', '$transitions', 'igniteSocketFactory', 'AgentModal', 'UserNotifications', 'IgniteVersion' ]; - constructor($root, $q, $transitions, socketFactory, AgentModal, UserNotifications) { - Object.assign(this, {$root, $q, $transitions, socketFactory, AgentModal, UserNotifications}); + constructor($root, $q, $transitions, socketFactory, AgentModal, UserNotifications, Version) { + Object.assign(this, {$root, $q, $transitions, socketFactory, AgentModal, UserNotifications, Version}); this.promises = new Set(); @@ -99,8 +99,7 @@ export default class IgniteAgentManager { this.connectionSbj = new BehaviorSubject(new ConnectionState(cluster)); - this.ignite2x = true; - this.ignite2_1 = true; + this.clusterVersion = '2.0.0'; if (!$root.IgniteDemoMode) { this.connectionSbj.subscribe({ @@ -110,13 +109,16 @@ export default class IgniteAgentManager { if (_.isEmpty(version)) return; - this.ignite2x = version.startsWith('2.'); - this.ignite2_1 = version.startsWith('2.1'); + this.clusterVersion = version; } }); } } + available(sinceVersion) { + return this.Version.since(this.clusterVersion, sinceVersion); + } + connect() { const self = this; @@ -491,7 +493,7 @@ export default class IgniteAgentManager { * @returns {Promise} */ querySql(nid, cacheName, query, nonCollocatedJoins, enforceJoinOrder, replicatedOnly, local, pageSz) { - if (this.ignite2x) { + if (this.available('2.0.0')) { return this.visorTask('querySqlX2', nid, cacheName, query, nonCollocatedJoins, enforceJoinOrder, replicatedOnly, local, pageSz) .then(({error, result}) => { if (_.isEmpty(error)) @@ -528,7 +530,7 @@ export default class IgniteAgentManager { * @returns {Promise} */ queryNextPage(nid, queryId, pageSize) { - if (this.ignite2x) + if (this.available('2.0.0')) return this.visorTask('queryFetchX2', nid, queryId, pageSize); return this.visorTask('queryFetch', nid, queryId, pageSize); @@ -572,7 +574,7 @@ export default class IgniteAgentManager { * @returns {Promise} */ queryClose(nid, queryId) { - if (this.ignite2x) { + if (this.available('2.0.0')) { return this.visorTask('queryCloseX2', nid, 'java.util.Map', 'java.util.UUID', 'java.util.Collection', nid + '=' + queryId); } @@ -592,7 +594,7 @@ export default class IgniteAgentManager { * @returns {Promise} */ queryScan(nid, cacheName, filter, regEx, caseSensitive, near, local, pageSize) { - if (this.ignite2x) { + if (this.available('2.0.0')) { return this.visorTask('queryScanX2', nid, cacheName, filter, regEx, caseSensitive, near, local, pageSize) .then(({error, result}) => { if (_.isEmpty(error)) From 2a2e8407aaa99a56f79a861fc1e201dd183eea11 Mon Sep 17 00:00:00 2001 From: vsisko Date: Tue, 22 Aug 2017 11:08:21 +0700 Subject: [PATCH 082/145] IGNITE-6131 Visor Cmd: Fixed "cache -a" command output cache entries count. (cherry picked from commit 1f5054a) --- .../ignite/visor/commands/cache/VisorCacheCommand.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala index 9d773ea23d515..42ff01c892270 100755 --- a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala +++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/cache/VisorCacheCommand.scala @@ -408,8 +408,8 @@ class VisorCacheCommand { formatDouble(nm.getCurrentCpuLoad * 100d) + " %", X.timeSpan2HMSM(nm.getUpTime), ( - "Total: " + (cm.getKeySize + cm.getOffHeapEntriesCount()), - " Heap: " + cm.getKeySize, + "Total: " + (cm.getHeapEntriesCount + cm.getOffHeapEntriesCount()), + " Heap: " + cm.getHeapEntriesCount, " Off-Heap: " + cm.getOffHeapEntriesCount(), " Off-Heap Memory: " + formatMemory(cm.getOffHeapAllocatedSize) ), From db64729fc9ebb0217f06b0cf9d5e53ab8d657510 Mon Sep 17 00:00:00 2001 From: sboikov Date: Tue, 22 Aug 2017 11:29:32 +0300 Subject: [PATCH 083/145] ignite-6124 Fixed NPE in GridDhtPartitionsExchangeFuture.topologyVersion after future cleanup. (cherry picked from commit 2c9057a) --- .../dht/preloader/GridDhtPartitionsExchangeFuture.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2249f80043bf4..7a1c8af69cd15 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 @@ -364,7 +364,7 @@ public AffinityTopologyVersion initialVersion() { */ assert exchangeDone() : "Should not be called before exchange is finished"; - return exchCtx.events().topologyVersion(); + return isDone() ? result() : exchCtx.events().topologyVersion(); } /** From 5b7724714264c14cc10f4b25abc9234387224e4b Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Tue, 22 Aug 2017 11:50:35 +0300 Subject: [PATCH 084/145] Fixed javadoc format. --- .../cache/distributed/near/GridNearTxPrepareRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7deceb599f63f..e352c8733dfe7 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 @@ -100,7 +100,7 @@ public GridNearTxPrepareRequest() { * @param subjId Subject ID. * @param taskNameHash Task name hash. * @param firstClientReq {@code True} if first optimistic tx prepare request sent from client node. - * @param {@code True} if it is safe for first client request to wait for topology future. + * @param allowWaitTopFut {@code True} if it is safe for first client request to wait for topology future. * @param addDepInfo Deployment info flag. */ public GridNearTxPrepareRequest( From 785a85eb0155444b3eef48cf373a990dc4c8c6dd Mon Sep 17 00:00:00 2001 From: sboikov Date: Tue, 22 Aug 2017 12:24:03 +0300 Subject: [PATCH 085/145] ignite-5872 GridDhtPartitionsSingleMessage.partitionUpdateCounters should not return null. --- .../dht/preloader/GridDhtPartitionsSingleMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0bad0a5754d6e..215152d771149 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 @@ -207,7 +207,7 @@ public CachePartitionPartialCountersMap partitionUpdateCounters(int grpId, int p Object res = partCntrs == null ? null : partCntrs.get(grpId); if (res == null) - return null; + return CachePartitionPartialCountersMap.EMPTY; if (res instanceof CachePartitionPartialCountersMap) return (CachePartitionPartialCountersMap)res; From 160d9b7c707efc359b4014aa1a481dc0fbbf596f Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Tue, 22 Aug 2017 14:10:10 +0300 Subject: [PATCH 086/145] Fixed flaky test. --- .../GridCacheAbstractLocalStoreSelfTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java index ae9986df7fbfe..c035c318fbc9a 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java @@ -47,9 +47,12 @@ import org.apache.ignite.configuration.NearCacheConfiguration; import org.apache.ignite.events.Event; import org.apache.ignite.events.EventType; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.store.CacheLocalStore; import org.apache.ignite.internal.util.lang.GridAbsPredicate; import org.apache.ignite.internal.util.typedef.G; +import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiInClosure; import org.apache.ignite.lang.IgniteBiTuple; @@ -289,13 +292,25 @@ public void testBackupRestorePrimary() throws Exception { * @throws Exception If failed. */ public void testBackupRestore() throws Exception { - Ignite ignite1 = startGrid(1); + final IgniteEx ignite1 = startGrid(1); Ignite ignite2 = startGrid(2); awaitPartitionMapExchange(); final String name = BACKUP_CACHE_2; + assertTrue(GridTestUtils.waitForCondition(new GridAbsPredicate() { + @Override public boolean apply() { + AffinityTopologyVersion topVer = ignite1.context().cache().context().cacheContext(CU.cacheId(name)) + .affinity().affinityTopologyVersion(); + + return topVer.topologyVersion() == 2 && topVer.minorTopologyVersion() == 1; + +// return ignite1.affinity(name).primaryPartitions(ignite1.cluster().localNode()).length < +// ignite1.affinity(name).partitions(); + } + }, 10_000)); + int key1 = -1; int key2 = -1; From 9ed4b72044ba1b2c105761b431625736166af7e7 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Tue, 1 Aug 2017 12:25:25 +0300 Subject: [PATCH 087/145] master - Fixed visor compilation after merge --- .../scala/org/apache/ignite/visor/visor.scala | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala index 943e13a6a7626..ffc7a00b64ae1 100644 --- a/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala +++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/visor.scala @@ -220,11 +220,11 @@ object visor extends VisorTag { /** Internal thread pool. */ @volatile var pool: ExecutorService = new IgniteThreadPoolExecutor( - 100, - 100, - 0, - new LinkedBlockingDeque[Runnable], - new IgniteThreadFactory(null, null) + Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().availableProcessors(), + 0L, + new LinkedBlockingQueue[Runnable](), + new IgniteThreadFactory("visorInstance", "visor") ) /** Configuration file path, if any. */ @@ -2178,11 +2178,11 @@ object visor extends VisorTag { } pool = new IgniteThreadPoolExecutor( - 100, - 100, - 0, - new LinkedBlockingDeque[Runnable], - new IgniteThreadFactory(null, null) + Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().availableProcessors(), + 0L, + new LinkedBlockingQueue[Runnable](), + new IgniteThreadFactory("visorInstance", "visor") ) } From 16b819a6131c95a30d8dfaefbac6f6593826258b Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Tue, 22 Aug 2017 16:40:02 +0300 Subject: [PATCH 088/145] Increased test timeout. --- .../processors/cache/CacheGroupsMetricsRebalanceTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java index a1a855a584f25..a49ce6145df95 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java @@ -66,6 +66,11 @@ public class CacheGroupsMetricsRebalanceTest extends GridCommonAbstractTest { stopAllGrids(); } + /** {@inheritDoc} */ + @Override protected long getTestTimeout() { + return 10 * 60 * 1000; + } + /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { From 9ab49d4a743c42b4a2f645b8af7611922629c9a3 Mon Sep 17 00:00:00 2001 From: oleg-ostanin Date: Tue, 22 Aug 2017 16:39:31 +0300 Subject: [PATCH 089/145] IGNITE-6155 added new jvm flag for printing gc date stamps (cherry picked from commit 03211d2) --- modules/yardstick/config/benchmark-bin-identity.properties | 1 + modules/yardstick/config/benchmark-cache-load.properties | 1 + modules/yardstick/config/benchmark-client-mode.properties | 1 + modules/yardstick/config/benchmark-failover.properties | 1 + modules/yardstick/config/benchmark-full.properties | 1 + modules/yardstick/config/benchmark-h2.properties | 1 + modules/yardstick/config/benchmark-multicast.properties | 1 + modules/yardstick/config/benchmark-mysql.properties | 1 + modules/yardstick/config/benchmark-pgsql.properties | 1 + modules/yardstick/config/benchmark-put-indexed-val.properties | 1 + .../yardstick/config/benchmark-query-put-separated.properties | 1 + modules/yardstick/config/benchmark-query.properties | 1 + modules/yardstick/config/benchmark-remote.properties | 1 + modules/yardstick/config/benchmark-sql-dml.properties | 1 + modules/yardstick/config/benchmark-store.properties | 1 + modules/yardstick/config/benchmark.properties | 1 + 16 files changed, 16 insertions(+) diff --git a/modules/yardstick/config/benchmark-bin-identity.properties b/modules/yardstick/config/benchmark-bin-identity.properties index d5d18b6d02276..426802f1736b2 100644 --- a/modules/yardstick/config/benchmark-bin-identity.properties +++ b/modules/yardstick/config/benchmark-bin-identity.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-cache-load.properties b/modules/yardstick/config/benchmark-cache-load.properties index c0556948ef657..f5463daa85a5b 100644 --- a/modules/yardstick/config/benchmark-cache-load.properties +++ b/modules/yardstick/config/benchmark-cache-load.properties @@ -28,6 +28,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-client-mode.properties b/modules/yardstick/config/benchmark-client-mode.properties index 923c96af9b057..85e0246e70a21 100644 --- a/modules/yardstick/config/benchmark-client-mode.properties +++ b/modules/yardstick/config/benchmark-client-mode.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version ver="RELEASE-" diff --git a/modules/yardstick/config/benchmark-failover.properties b/modules/yardstick/config/benchmark-failover.properties index a1b9d8fe4344e..e53792bffec01 100644 --- a/modules/yardstick/config/benchmark-failover.properties +++ b/modules/yardstick/config/benchmark-failover.properties @@ -34,6 +34,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-full.properties b/modules/yardstick/config/benchmark-full.properties index 96da0a6e4d390..59d6fe7a496e5 100644 --- a/modules/yardstick/config/benchmark-full.properties +++ b/modules/yardstick/config/benchmark-full.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-h2.properties b/modules/yardstick/config/benchmark-h2.properties index 67233441830b6..5426071235837 100644 --- a/modules/yardstick/config/benchmark-h2.properties +++ b/modules/yardstick/config/benchmark-h2.properties @@ -28,6 +28,7 @@ JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false \ -XX:SurvivorRatio=1024 \ -XX:+UseCMSInitiatingOccupancyOnly \ -XX:CMSInitiatingOccupancyFraction=60 \ + -XX:+PrintGCDateStamps \ " H2_PARAMS="-jdbcDrv org.h2.Driver -jdbc jdbc:h2:tcp://127.0.0.1/${SCRIPT_DIR}/../work/h2benchmark -sch ${SCRIPT_DIR}/../config/h2-schema.sql" diff --git a/modules/yardstick/config/benchmark-multicast.properties b/modules/yardstick/config/benchmark-multicast.properties index 5071a11e6b488..cc07921f537cc 100644 --- a/modules/yardstick/config/benchmark-multicast.properties +++ b/modules/yardstick/config/benchmark-multicast.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-mysql.properties b/modules/yardstick/config/benchmark-mysql.properties index c1fe393cfa998..e6dbee279f05e 100644 --- a/modules/yardstick/config/benchmark-mysql.properties +++ b/modules/yardstick/config/benchmark-mysql.properties @@ -28,6 +28,7 @@ JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false \ -XX:SurvivorRatio=1024 \ -XX:+UseCMSInitiatingOccupancyOnly \ -XX:CMSInitiatingOccupancyFraction=60 \ + -XX:+PrintGCDateStamps \ " MYSQL_PARAMS="-jdbcDrv com.mysql.jdbc.Driver -jdbc jdbc:mysql://127.0.0.1/?user=root\\&password=1\\&rewriteBatchedStatements=true -tempDb -sch ${SCRIPT_DIR}/../config/mysql-schema.sql" diff --git a/modules/yardstick/config/benchmark-pgsql.properties b/modules/yardstick/config/benchmark-pgsql.properties index 31ad775cd1ae7..3a7ac645bdb36 100644 --- a/modules/yardstick/config/benchmark-pgsql.properties +++ b/modules/yardstick/config/benchmark-pgsql.properties @@ -28,6 +28,7 @@ JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false \ -XX:SurvivorRatio=1024 \ -XX:+UseCMSInitiatingOccupancyOnly \ -XX:CMSInitiatingOccupancyFraction=60 \ + -XX:+PrintGCDateStamps \ " # Warning: database must be created and specified in URL explicitly, and there's no other way around as long as driver does not support changing DB in runtime diff --git a/modules/yardstick/config/benchmark-put-indexed-val.properties b/modules/yardstick/config/benchmark-put-indexed-val.properties index a2bc956fe822d..a8d530bd4cc80 100644 --- a/modules/yardstick/config/benchmark-put-indexed-val.properties +++ b/modules/yardstick/config/benchmark-put-indexed-val.properties @@ -29,6 +29,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-query-put-separated.properties b/modules/yardstick/config/benchmark-query-put-separated.properties index b4437bf368bc8..c2684a140ae0d 100644 --- a/modules/yardstick/config/benchmark-query-put-separated.properties +++ b/modules/yardstick/config/benchmark-query-put-separated.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-query.properties b/modules/yardstick/config/benchmark-query.properties index 2612ec96f6b7b..ee88beb1bd71f 100644 --- a/modules/yardstick/config/benchmark-query.properties +++ b/modules/yardstick/config/benchmark-query.properties @@ -29,6 +29,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-remote.properties b/modules/yardstick/config/benchmark-remote.properties index 4d671d3ad92ff..e9fde52803b9c 100644 --- a/modules/yardstick/config/benchmark-remote.properties +++ b/modules/yardstick/config/benchmark-remote.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-sql-dml.properties b/modules/yardstick/config/benchmark-sql-dml.properties index 3bb4107c4f3f1..119d7da2df644 100644 --- a/modules/yardstick/config/benchmark-sql-dml.properties +++ b/modules/yardstick/config/benchmark-sql-dml.properties @@ -29,6 +29,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark-store.properties b/modules/yardstick/config/benchmark-store.properties index 3146c2422a29a..a24e65d73755f 100644 --- a/modules/yardstick/config/benchmark-store.properties +++ b/modules/yardstick/config/benchmark-store.properties @@ -33,6 +33,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version diff --git a/modules/yardstick/config/benchmark.properties b/modules/yardstick/config/benchmark.properties index 2dd51eb136e0b..96a11d1f393cf 100644 --- a/modules/yardstick/config/benchmark.properties +++ b/modules/yardstick/config/benchmark.properties @@ -31,6 +31,7 @@ JVM_OPTS=${JVM_OPTS}" \ -verbose:gc \ -XX:+UseParNewGC \ -XX:+UseConcMarkSweepGC \ +-XX:+PrintGCDateStamps \ " #Ignite version From e780c6b98b5a09cff44ec5d2fa1fd30275ffc35f Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Tue, 22 Aug 2017 17:10:07 +0300 Subject: [PATCH 090/145] Fixed test to work with new update counter maps. --- ...cheExchangeMessageDuplicatedStateTest.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java index bff63fb4e812f..b3475b84d1449 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java @@ -27,6 +27,8 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.TestRecordingCommunicationSpi; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionFullCountersMap; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap; 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.GridDhtPartitionsAbstractMessage; @@ -244,12 +246,19 @@ private void checkFullMessage(GridDhtPartitionsFullMessage msg) { assertFalse(dupPartsData.containsKey(CU.cacheId(AFF3_CACHE1))); - Map> partCntrs = - getFieldValue(getFieldValue(msg, "partCntrs"), "map"); + Map partCntrs = + getFieldValue(getFieldValue(msg, "partCntrs2"), "map"); if (partCntrs != null) { - for (Map cntrs : partCntrs.values()) - assertTrue(cntrs.isEmpty()); + for (CachePartitionFullCountersMap cntrs : partCntrs.values()) { + long[] initialUpdCntrs = getFieldValue(cntrs, "initialUpdCntrs"); + long[] updCntrs = getFieldValue(cntrs, "updCntrs"); + + for (int i = 0; i < initialUpdCntrs.length; i++) { + assertEquals(0, initialUpdCntrs[i]); + assertEquals(0, updCntrs[i]); + } + } } } @@ -266,11 +275,11 @@ private void checkSingleMessage(GridDhtPartitionsSingleMessage msg) { assertFalse(dupPartsData.containsKey(CU.cacheId(AFF3_CACHE1))); - Map> partCntrs = getFieldValue(msg, "partCntrs"); + Map partCntrs = getFieldValue(msg, "partCntrs"); if (partCntrs != null) { - for (Map cntrs : partCntrs.values()) - assertTrue(cntrs.isEmpty()); + for (CachePartitionPartialCountersMap cntrs : partCntrs.values()) + assertEquals(0, cntrs.size()); } } From db6add1d2ee17381b810cff3ff978eef4cef51b0 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Tue, 22 Aug 2017 17:20:07 +0300 Subject: [PATCH 091/145] Removed explicit fail(). --- .../cache/distributed/dht/IgniteCacheMultiTxLockSelfTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheMultiTxLockSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheMultiTxLockSelfTest.java index 6fd5dd3356cfb..11b0eea9a36af 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheMultiTxLockSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheMultiTxLockSelfTest.java @@ -117,8 +117,6 @@ public void testExplicitLockManyKeys() throws Exception { * @throws Exception If failed. */ public void testExplicitLockManyKeysWithClient() throws Exception { - fail("https://issues.apache.org/jira/browse/IGNITE-3950"); - checkExplicitLock(4, true); } From bc1cc99eab9641753925ef2552ba29831640e9e1 Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Tue, 22 Aug 2017 16:34:31 +0300 Subject: [PATCH 092/145] IGNITE-6154 fix incorrect check checkpoint pages --- .../GridCacheDatabaseSharedManager.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) 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 b4f428bf7ab83..6775f07b035e6 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 @@ -2210,7 +2210,7 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws curr.cpBeginFut.onDone(); - if (!F.isEmpty(cpPagesTuple.get1())) { + if (hasPageForWrite(cpPagesTuple.get1())) { assert cpPtr != null; // Sync log outside the checkpoint write lock. @@ -2260,6 +2260,24 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws } } + /** + * Check that at least one collection is not empty. + * + * @param cpPagesCollWrapper Collection of {@link GridMultiCollectionWrapper} checkpoint pages. + */ + private boolean hasPageForWrite(Collection> cpPagesCollWrapper) { + boolean hasPages = false; + + for (Collection c : cpPagesCollWrapper) + if (!c.isEmpty()) { + hasPages = true; + + break; + } + + return hasPages; + } + /** * @return tuple with collections of FullPageIds obtained from each PageMemory and overall number of dirty pages. */ @@ -2335,7 +2353,8 @@ public void shutdownNow() { * @param cpPagesTuple Checkpoint pages tuple. */ private GridMultiCollectionWrapper splitAndSortCpPagesIfNeeded( - IgniteBiTuple>, Integer> cpPagesTuple) { + IgniteBiTuple>, Integer> cpPagesTuple + ) { List cpPagesList = new ArrayList<>(cpPagesTuple.get2()); for (GridMultiCollectionWrapper col : cpPagesTuple.get1()) { From 8dbdd03143362bb39d96242b23975efb22412709 Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Tue, 22 Aug 2017 17:03:42 +0300 Subject: [PATCH 093/145] IGNITE-6154 also fixed check for WAL record --- .../cache/persistence/GridCacheDatabaseSharedManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 6775f07b035e6..67ba6a9032a72 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 @@ -2130,6 +2130,8 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws tracker.onLockWaitStart(); + boolean hasPages; + checkpointLock.writeLock().lock(); try { @@ -2194,7 +2196,9 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws cpPagesTuple = beginAllCheckpoints(); - if (!F.isEmpty(cpPagesTuple.get1())) { + hasPages = hasPageForWrite(cpPagesTuple.get1()); + + if (hasPages) { // No page updates for this checkpoint are allowed from now on. cpPtr = cctx.wal().log(cpRec); @@ -2210,7 +2214,7 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws curr.cpBeginFut.onDone(); - if (hasPageForWrite(cpPagesTuple.get1())) { + if (hasPages) { assert cpPtr != null; // Sync log outside the checkpoint write lock. From afad8e0fc58160f7876925dc6c3051be7a168155 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Wed, 23 Aug 2017 12:18:44 +0300 Subject: [PATCH 094/145] Muted hanging test. --- .../GridCachePartitionEvictionDuringReadThroughSelfTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCachePartitionEvictionDuringReadThroughSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCachePartitionEvictionDuringReadThroughSelfTest.java index ee0e5368b28a7..c66744b8a4d7f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCachePartitionEvictionDuringReadThroughSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCachePartitionEvictionDuringReadThroughSelfTest.java @@ -76,6 +76,8 @@ public class GridCachePartitionEvictionDuringReadThroughSelfTest extends GridCom * @throws Exception if failed. */ public void testPartitionRent() throws Exception { + fail("https://issues.apache.org/jira/browse/IGNITE-5759"); + startGrid(0); final AtomicBoolean done = new AtomicBoolean(); From ad38f7b4b5e6845b2ccccd7eb888f805484504f5 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Wed, 23 Aug 2017 14:12:42 +0300 Subject: [PATCH 095/145] gg-12662 : Fixed JDBC backward compatibility. --- .../internal/jdbc/thin/JdbcThinTcpIo.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 e124921d0b5a3..b6bf222b4ad01 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 @@ -219,16 +219,20 @@ public void handshake() throws IOException, IgniteCheckedException { boolean accepted = reader.readBoolean(); if (accepted) { - byte maj = reader.readByte(); - byte min = reader.readByte(); - byte maintenance = reader.readByte(); + if (reader.available() > 3) { + byte maj = reader.readByte(); + byte min = reader.readByte(); + byte maintenance = reader.readByte(); - String stage = reader.readString(); + String stage = reader.readString(); - long ts = reader.readLong(); - byte[] hash = reader.readByteArray(); + long ts = reader.readLong(); + byte[] hash = reader.readByteArray(); - igniteVer = new IgniteProductVersion(maj, min, maintenance, stage, ts, hash); + igniteVer = new IgniteProductVersion(maj, min, maintenance, stage, ts, hash); + } + else + igniteVer = new IgniteProductVersion((byte)2, (byte)1, (byte)0, "Unknown 2.1 version", 0L, null); return; } From 28c906e3e0c51e6f1a4a95b2027d248f9b5035c2 Mon Sep 17 00:00:00 2001 From: Sergey Chugunov Date: Wed, 2 Aug 2017 18:14:46 +0300 Subject: [PATCH 096/145] IGNITE-5542 CacheGroup configuration from cluster is merged with local settings (cherry picked from commit 88818ec) --- .../cache/CacheGroupDescriptor.java | 20 ++- .../processors/cache/ClusterCachesInfo.java | 25 +++ .../processors/cache/GridCacheAttributes.java | 35 ++++ .../processors/cache/GridCacheUtils.java | 1 + .../CacheGroupLocalConfigurationSelfTest.java | 155 ++++++++++++++++++ .../testsuites/IgniteCacheTestSuite2.java | 2 + 6 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupLocalConfigurationSelfTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java index c4976e52ccce4..20301a60b0ee2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java @@ -83,7 +83,7 @@ public class CacheGroupDescriptor { this.rcvdFrom = rcvdFrom; this.startTopVer = startTopVer; this.deploymentId = deploymentId; - this.cacheCfg = cacheCfg; + this.cacheCfg = new CacheConfiguration<>(cacheCfg); this.caches = caches; } @@ -196,6 +196,24 @@ void receivedFromStartVersion(AffinityTopologyVersion rcvdFromVer) { this.rcvdFromVer = rcvdFromVer; } + /** + * Method to merge this CacheGroup descriptor with another one. + * + * @param otherDesc CacheGroup descriptor that must be merged with this one. + */ + void mergeWith(CacheGroupDescriptor otherDesc) { + assert otherDesc != null && otherDesc.config() != null: otherDesc; + + CacheConfiguration otherCfg = otherDesc.config(); + + cacheCfg.setRebalanceDelay(otherCfg.getRebalanceDelay()); + cacheCfg.setRebalanceBatchesPrefetchCount(otherCfg.getRebalanceBatchesPrefetchCount()); + cacheCfg.setRebalanceBatchSize(otherCfg.getRebalanceBatchSize()); + cacheCfg.setRebalanceOrder(otherCfg.getRebalanceOrder()); + cacheCfg.setRebalanceThrottle(otherCfg.getRebalanceThrottle()); + cacheCfg.setRebalanceTimeout(otherCfg.getRebalanceTimeout()); + } + /** * @return Start version for dynamically started group. */ 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 23454e83fd947..5e2c8db939a1e 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 @@ -285,6 +285,22 @@ private void checkCache(CacheJoinNodeDiscoveryData.CacheInfo locInfo, CacheData CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "preloadBatchSize", "Preload batch size", locAttr.rebalanceBatchSize(), rmtAttr.rebalanceBatchSize(), false); + CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "rebalanceDelay", + "Rebalance delay", locAttr.rebalanceDelay(), rmtAttr.rebalanceDelay(), false); + + CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "rebalanceBatchesPrefetchCount", + "Rebalance batches prefetch count", locAttr.rebalanceBatchesPrefetchCount(), + rmtAttr.rebalanceBatchesPrefetchCount(), false); + + CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "rebalanceOrder", + "Rebalance order", locAttr.rebalanceOrder(), rmtAttr.rebalanceOrder(), false); + + CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "rebalanceThrottle", + "Rebalance throttle", locAttr.rebalanceThrottle(), rmtAttr.rebalanceThrottle(), false); + + CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "rebalanceTimeout", + "Rebalance timeout", locAttr.rebalanceTimeout(), rmtAttr.rebalanceTimeout(), false); + CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeSynchronizationMode", "Write synchronization mode", locAttr.writeSynchronization(), rmtAttr.writeSynchronization(), true); @@ -958,6 +974,9 @@ public void onGridDataReceived(DiscoveryDataBag.GridDiscoveryData data) { CacheNodeCommonDiscoveryData cachesData = (CacheNodeCommonDiscoveryData)data.commonData(); + // CacheGroup configurations that were created from local node configuration. + Map locCacheGrps = new HashMap<>(registeredCacheGroups()); + // Replace locally registered data with actual data received from cluster. registeredCaches.clear(); registeredCacheGrps.clear(); @@ -973,6 +992,12 @@ public void onGridDataReceived(DiscoveryDataBag.GridDiscoveryData data) { grpData.deploymentId(), grpData.caches()); + if (locCacheGrps.containsKey(grpDesc.groupId())) { + CacheGroupDescriptor locGrpCfg = locCacheGrps.get(grpDesc.groupId()); + + grpDesc.mergeWith(locGrpCfg); + } + CacheGroupDescriptor old = registeredCacheGrps.put(grpDesc.groupId(), grpDesc); assert old == null : old; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java index 32871ea49c665..d64ee8b30fb23 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java @@ -202,6 +202,41 @@ public int rebalanceBatchSize() { return ccfg.getRebalanceBatchSize(); } + /** + * @return Rebalance delay. + */ + public long rebalanceDelay() { + return ccfg.getRebalanceDelay(); + } + + /** + * @return Rebalance prefetch count. + */ + public long rebalanceBatchesPrefetchCount() { + return ccfg.getRebalanceBatchesPrefetchCount(); + } + + /** + * @return Rebalance order. + */ + public int rebalanceOrder() { + return ccfg.getRebalanceOrder(); + } + + /** + * @return Rebalance throttle. + */ + public long rebalanceThrottle() { + return ccfg.getRebalanceThrottle(); + } + + /** + * @return Rebalance timeout. + */ + public long rebalanceTimeout() { + return ccfg.getRebalanceTimeout(); + } + /** * @return Synchronization mode. */ 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 4aec0c8b5ade9..f94cfb56290b1 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 @@ -1001,6 +1001,7 @@ public static void checkAttributeMismatch(IgniteLogger log, String cfgName, UUID } } } + /** * @param cfg1 Existing configuration. * @param cfg2 Cache configuration to start. diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupLocalConfigurationSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupLocalConfigurationSelfTest.java new file mode 100644 index 0000000000000..51f900165dbed --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupLocalConfigurationSelfTest.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; + +import java.util.Map; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.jetbrains.annotations.Nullable; + +/** + * + */ +public class CacheGroupLocalConfigurationSelfTest extends GridCommonAbstractTest { + /** */ + private static final String SECOND_NODE_NAME = "secondNode"; + + /** */ + private static final int NON_STANDARD_REBALANCE_VALUE = 101; + + /** */ + private static final String NON_DEFAULT_GROUP_NAME = "cacheGroup"; + + /** */ + private boolean useNonDfltCacheGrp = true; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + if (igniteInstanceName.equals(SECOND_NODE_NAME)) { + CacheConfiguration ccfg = new CacheConfiguration() + .setName(DEFAULT_CACHE_NAME) + .setRebalanceDelay(NON_STANDARD_REBALANCE_VALUE) + .setRebalanceBatchesPrefetchCount(NON_STANDARD_REBALANCE_VALUE) + .setRebalanceBatchSize(NON_STANDARD_REBALANCE_VALUE) + .setRebalanceOrder(NON_STANDARD_REBALANCE_VALUE) + .setRebalanceThrottle(NON_STANDARD_REBALANCE_VALUE) + .setRebalanceTimeout(NON_STANDARD_REBALANCE_VALUE); + + if (useNonDfltCacheGrp) + ccfg.setGroupName(NON_DEFAULT_GROUP_NAME); + + cfg.setCacheConfiguration(ccfg); + } + else { + CacheConfiguration ccfg = new CacheConfiguration() + .setName(DEFAULT_CACHE_NAME); + + if (useNonDfltCacheGrp) + ccfg.setGroupName(NON_DEFAULT_GROUP_NAME); + + cfg.setCacheConfiguration(ccfg); + } + + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** + * Test validates that all cache group configuration attributes from local config + * that must not be overwritten by grid config are preserved for default cache group. + * + * @throws Exception If failed. + */ + public void testDefaultGroupLocalAttributesPreserved() throws Exception { + useNonDfltCacheGrp = false; + + executeTest(); + } + + /** + * Test validates that all cache group configuration attributes from local config + * that must not be overwritten by grid config are preserved for non-default cache group. + * + * @throws Exception If failed. + */ + public void testNonDefaultGroupLocalAttributesPreserved() throws Exception { + useNonDfltCacheGrp = true; + + executeTest(); + } + + /** + * Executes actual test logic. + * + * @throws Exception If failed. + */ + private void executeTest() throws Exception { + startGrid(0); + + IgniteKernal ignite = (IgniteKernal) startGrid("secondNode"); + + GridCacheProcessor cacheProc = ignite.context().cache(); + + Map cacheGrps = U.field(cacheProc, "cacheGrps"); + + CacheConfiguration cacheGroupCfg = findGroupConfig(cacheGrps, + useNonDfltCacheGrp ? NON_DEFAULT_GROUP_NAME : DEFAULT_CACHE_NAME); + + assertNotNull("Default cache group must be presented", cacheGroupCfg); + + assertEquals("Rebalance delay", cacheGroupCfg.getRebalanceDelay(), NON_STANDARD_REBALANCE_VALUE); + + assertEquals("Rebalance batches prefetch count", + cacheGroupCfg.getRebalanceBatchesPrefetchCount(), + NON_STANDARD_REBALANCE_VALUE); + + assertEquals("Rebalance batch size", cacheGroupCfg.getRebalanceBatchSize(), NON_STANDARD_REBALANCE_VALUE); + + assertEquals("Rebalance order", cacheGroupCfg.getRebalanceOrder(), NON_STANDARD_REBALANCE_VALUE); + + assertEquals("Rebalance throttle", cacheGroupCfg.getRebalanceThrottle(), NON_STANDARD_REBALANCE_VALUE); + + assertEquals("Rebalance timeout", cacheGroupCfg.getRebalanceTimeout(), NON_STANDARD_REBALANCE_VALUE); + } + + /** + * @param cacheGrps All configured cache groups. + * @param groupName Name of group to find. + * @return Cache configuration. + */ + private CacheConfiguration findGroupConfig(Map cacheGrps, @Nullable String groupName) { + if (groupName == null) + groupName = DEFAULT_CACHE_NAME; + + for (CacheGroupContext grpCtx : cacheGrps.values()) { + if (groupName.equals(grpCtx.cacheOrGroupName())) + return grpCtx.config(); + } + + return null; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java index 0e1aaec1e104d..31ad015ad1c04 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite2.java @@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.cache.CacheEnumOperationsSingleNodeTest; import org.apache.ignite.internal.processors.cache.CacheEnumOperationsTest; import org.apache.ignite.internal.processors.cache.CacheExchangeMessageDuplicatedStateTest; +import org.apache.ignite.internal.processors.cache.CacheGroupLocalConfigurationSelfTest; import org.apache.ignite.internal.processors.cache.CacheMemoryPolicyConfigurationTest; import org.apache.ignite.internal.processors.cache.CacheOptimisticTransactionsWithFilterSingleServerTest; import org.apache.ignite.internal.processors.cache.CacheOptimisticTransactionsWithFilterTest; @@ -262,6 +263,7 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(MemoryPolicyConfigValidationTest.class)); suite.addTest(new TestSuite(MemoryPolicyInitializationTest.class)); suite.addTest(new TestSuite(CacheMemoryPolicyConfigurationTest.class)); + suite.addTest(new TestSuite(CacheGroupLocalConfigurationSelfTest.class)); suite.addTest(new TestSuite(CacheEnumOperationsSingleNodeTest.class)); suite.addTest(new TestSuite(CacheEnumOperationsTest.class)); suite.addTest(new TestSuite(IgniteCacheIncrementTxTest.class)); From caeb11936fa3534b9468d443c11744362044cae5 Mon Sep 17 00:00:00 2001 From: sboikov Date: Wed, 23 Aug 2017 15:19:52 +0300 Subject: [PATCH 097/145] ignite-6124 Guard logging with isInfoEnabled (cherry picked from commit bebe4d8) --- .../GridDhtPartitionsExchangeFuture.java | 203 ++++++++++++------ .../preloader/InitNewCoordinatorFuture.java | 38 +++- 2 files changed, 160 insertions(+), 81 deletions(-) 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 7a1c8af69cd15..704062998eb52 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 @@ -674,7 +674,8 @@ else if (msg instanceof SnapshotDiscoveryMessage) { if (cctx.localNode().isClient()) tryToPerformLocalSnapshotOperation(); - exchLog.info("Finished exchange init [topVer=" + topVer + ", crd=" + crdNode + ']'); + if (exchLog.isInfoEnabled()) + exchLog.info("Finished exchange init [topVer=" + topVer + ", crd=" + crdNode + ']'); } catch (IgniteInterruptedCheckedException e) { onDone(e); @@ -1393,9 +1394,11 @@ public void finishMerged() { if (isDone() || !done.compareAndSet(false, true)) return false; - log.info("Finish exchange future [startVer=" + initialVersion() + - ", resVer=" + res + - ", err=" + err + ']'); + if (log.isInfoEnabled()) { + log.info("Finish exchange future [startVer=" + initialVersion() + + ", resVer=" + res + + ", err=" + err + ']'); + } assert res != null || err != null; @@ -1584,15 +1587,19 @@ private boolean addMergedJoinExchange(ClusterNode node, @Nullable GridDhtPartiti if (msg != null) { assert msg.exchangeId().topologyVersion().equals(new AffinityTopologyVersion(node.order())); - log.info("Merge server join exchange, message received [curFut=" + initialVersion() + - ", node=" + nodeId + ']'); + if (log.isInfoEnabled()) { + log.info("Merge server join exchange, message received [curFut=" + initialVersion() + + ", node=" + nodeId + ']'); + } mergedJoinExchMsgs.put(nodeId, msg); } else { if (cctx.discovery().alive(nodeId)) { - log.info("Merge server join exchange, wait for message [curFut=" + initialVersion() + - ", node=" + nodeId + ']'); + if (log.isInfoEnabled()) { + log.info("Merge server join exchange, wait for message [curFut=" + initialVersion() + + ", node=" + nodeId + ']'); + } wait = true; @@ -1601,8 +1608,10 @@ private boolean addMergedJoinExchange(ClusterNode node, @Nullable GridDhtPartiti awaitMergedMsgs++; } else { - log.info("Merge server join exchange, awaited node left [curFut=" + initialVersion() + - ", node=" + nodeId + ']'); + if (log.isInfoEnabled()) { + log.info("Merge server join exchange, awaited node left [curFut=" + initialVersion() + + ", node=" + nodeId + ']'); + } } } } @@ -1680,11 +1689,13 @@ private void processMergedMessage(final ClusterNode node, final GridDhtPartition mergedJoinExchMsgs.containsKey(node.id()) && mergedJoinExchMsgs.get(node.id()) == null; - log.info("Merge server join exchange, received message [curFut=" + initialVersion() + - ", node=" + node.id() + - ", msgVer=" + msg.exchangeId().topologyVersion() + - ", process=" + process + - ", awaited=" + awaitMergedMsgs + ']'); + if (log.isInfoEnabled()) { + log.info("Merge server join exchange, received message [curFut=" + initialVersion() + + ", node=" + node.id() + + ", msgVer=" + msg.exchangeId().topologyVersion() + + ", process=" + process + + ", awaited=" + awaitMergedMsgs + ']'); + } if (process) { mergedJoinExchMsgs.put(node.id(), msg); @@ -1832,8 +1843,10 @@ private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage ms switch (state) { case DONE: { - log.info("Received single message, already done [ver=" + initialVersion() + - ", node=" + nodeId + ']'); + if (log.isInfoEnabled()) { + log.info("Received single message, already done [ver=" + initialVersion() + + ", node=" + nodeId + ']'); + } assert finishState != null; @@ -1855,9 +1868,11 @@ private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage ms allReceived = remaining.isEmpty(); - log.info("Coordinator received single message [ver=" + initialVersion() + - ", node=" + nodeId + - ", allReceived=" + allReceived + ']'); + if (log.isInfoEnabled()) { + log.info("Coordinator received single message [ver=" + initialVersion() + + ", node=" + nodeId + + ", allReceived=" + allReceived + ']'); + } } break; @@ -1865,8 +1880,10 @@ private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage ms case SRV: case BECOME_CRD: { - log.info("Non-coordinator received single message [ver=" + initialVersion() + - ", node=" + nodeId + ", state=" + state + ']'); + if (log.isInfoEnabled()) { + log.info("Non-coordinator received single message [ver=" + initialVersion() + + ", node=" + nodeId + ", state=" + state + ']'); + } pendingSingleMsgs.put(nodeId, msg); @@ -2160,7 +2177,8 @@ private void onAllReceived(@Nullable Collection sndResNodes) { } if (exchCtx.mergeExchanges()) { - log.info("Coordinator received all messages, try merge [ver=" + initialVersion() + ']'); + if (log.isInfoEnabled()) + log.info("Coordinator received all messages, try merge [ver=" + initialVersion() + ']'); boolean finish = cctx.exchange().mergeExchangesOnCoordinator(this); @@ -2185,8 +2203,10 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe try { AffinityTopologyVersion resTopVer = exchCtx.events().topologyVersion(); - log.info("finishExchangeOnCoordinator [topVer=" + initialVersion() + - ", resVer=" + resTopVer + ']'); + if (log.isInfoEnabled()) { + log.info("finishExchangeOnCoordinator [topVer=" + initialVersion() + + ", resVer=" + resTopVer + ']'); + } Map idealAffDiff = null; @@ -2384,9 +2404,11 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe onDone(exchCtx.events().topologyVersion(), err); for (Map.Entry e : pendingSingleMsgs.entrySet()) { - log.info("Process pending message on coordinator [node=" + e.getKey() + - ", ver=" + initialVersion() + - ", resVer=" + resTopVer + ']'); + if (log.isInfoEnabled()) { + log.info("Process pending message on coordinator [node=" + e.getKey() + + ", ver=" + initialVersion() + + ", resVer=" + resTopVer + ']'); + } processSingleMessage(e.getKey(), e.getValue()); } @@ -2509,7 +2531,8 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi synchronized (mux) { if (crd == null) { - log.info("Ignore partitions request, no coordinator [node=" + node.id() + ']'); + if (log.isInfoEnabled()) + log.info("Ignore partitions request, no coordinator [node=" + node.id() + ']'); return; } @@ -2519,7 +2542,8 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi assert finishState != null; if (node.id().equals(finishState.crdId)) { - log.info("Ignore partitions request, finished exchange with this coordinator: " + msg); + if (log.isInfoEnabled()) + log.info("Ignore partitions request, finished exchange with this coordinator: " + msg); return; } @@ -2531,7 +2555,8 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi case CRD: case BECOME_CRD: { - log.info("Ignore partitions request, node is coordinator: " + msg); + if (log.isInfoEnabled()) + log.info("Ignore partitions request, node is coordinator: " + msg); return; } @@ -2539,7 +2564,8 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi case CLIENT: case SRV: { if (!cctx.discovery().alive(node)) { - log.info("Ignore partitions request, node is not alive [node=" + node.id() + ']'); + if (log.isInfoEnabled()) + log.info("Ignore partitions request, node is not alive [node=" + node.id() + ']'); return; } @@ -2547,14 +2573,18 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi if (msg.restoreState()) { if (!node.equals(crd)) { if (node.order() > crd.order()) { - log.info("Received partitions request, change coordinator [oldCrd=" + crd.id() + - ", newCrd=" + node.id() + ']'); + if (log.isInfoEnabled()) { + log.info("Received partitions request, change coordinator [oldCrd=" + crd.id() + + ", newCrd=" + node.id() + ']'); + } crd = node; // Do not allow to process FullMessage from old coordinator. } else { - log.info("Ignore restore state request, coordinator changed [oldCrd=" + crd.id() + - ", newCrd=" + node.id() + ']'); + if (log.isInfoEnabled()) { + log.info("Ignore restore state request, coordinator changed [oldCrd=" + crd.id() + + ", newCrd=" + node.id() + ']'); + } return; } @@ -2585,10 +2615,12 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi res.restoreState(true); - log.info("Send restore state response [node=" + node.id() + - ", exchVer=" + msg.restoreExchangeId().topologyVersion() + - ", hasState=" + (finishState0 != null) + - ", affReq=" + !F.isEmpty(res.cacheGroupsAffinityRequest()) + ']'); + if (log.isInfoEnabled()) { + log.info("Send restore state response [node=" + node.id() + + ", exchVer=" + msg.restoreExchangeId().topologyVersion() + + ", hasState=" + (finishState0 != null) + + ", affReq=" + !F.isEmpty(res.cacheGroupsAffinityRequest()) + ']'); + } res.finishMessage(finishState0 != null ? finishState0.msg : null); @@ -2614,6 +2646,7 @@ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSi } /** + * @param checkCrd If {@code true} checks that local node is exchange coordinator. * @param node Sender node. * @param msg Message. */ @@ -2627,7 +2660,8 @@ private void processFullMessage(boolean checkCrd, ClusterNode node, GridDhtParti synchronized (mux) { if (crd == null) { - log.info("Ignore full message, all server nodes left: " + msg); + if (log.isInfoEnabled()) + log.info("Ignore full message, all server nodes left: " + msg); return; } @@ -2635,13 +2669,15 @@ private void processFullMessage(boolean checkCrd, ClusterNode node, GridDhtParti switch (state) { case CRD: case BECOME_CRD: { - log.info("Ignore full message, node is coordinator: " + msg); + if (log.isInfoEnabled()) + log.info("Ignore full message, node is coordinator: " + msg); return; } case DONE: { - log.info("Ignore full message, future is done: " + msg); + if (log.isInfoEnabled()) + log.info("Ignore full message, future is done: " + msg); return; } @@ -2649,10 +2685,12 @@ private void processFullMessage(boolean checkCrd, ClusterNode node, GridDhtParti case SRV: case CLIENT: { if (!crd.equals(node)) { - log.info("Received full message from non-coordinator [node=" + node.id() + - ", nodeOrder=" + node.order() + - ", crd=" + crd.id() + - ", crdOrder=" + crd.order() + ']'); + if (log.isInfoEnabled()) { + log.info("Received full message from non-coordinator [node=" + node.id() + + ", nodeOrder=" + node.order() + + ", crd=" + crd.id() + + ", crdOrder=" + crd.order() + ']'); + } if (node.order() > crd.order()) fullMsgs.put(node, msg); @@ -2662,8 +2700,10 @@ private void processFullMessage(boolean checkCrd, ClusterNode node, GridDhtParti else { AffinityTopologyVersion resVer = msg.resultTopologyVersion() != null ? msg.resultTopologyVersion() : initialVersion(); - log.info("Received full message, will finish exchange [node=" + node.id() + - ", resVer=" + resVer + ']'); + if (log.isInfoEnabled()) { + log.info("Received full message, will finish exchange [node=" + node.id() + + ", resVer=" + resVer + ']'); + } finishState = new FinishState(crd.id(), resVer, msg); @@ -2682,8 +2722,10 @@ private void processFullMessage(boolean checkCrd, ClusterNode node, GridDhtParti if (exchCtx.mergeExchanges()) { if (msg.resultTopologyVersion() != null && !initialVersion().equals(msg.resultTopologyVersion())) { - log.info("Received full message, need merge [curFut=" + initialVersion() + - ", resVer=" + msg.resultTopologyVersion() + ']'); + if (log.isInfoEnabled()) { + log.info("Received full message, need merge [curFut=" + initialVersion() + + ", resVer=" + msg.resultTopologyVersion() + ']'); + } resTopVer = msg.resultTopologyVersion(); @@ -3012,8 +3054,10 @@ public void onNodeLeft(final ClusterNode node) { changeGlobalStateExceptions.put(crd0.id(), changeGlobalStateE); if (crdChanged) { - log.info("Coordinator failed, node is new coordinator [ver=" + initialVersion() + - ", prev=" + node.id() + ']'); + if (log.isInfoEnabled()) { + log.info("Coordinator failed, node is new coordinator [ver=" + initialVersion() + + ", prev=" + node.id() + ']'); + } assert newCrdFut != null; @@ -3057,10 +3101,12 @@ public void onNodeLeft(final ClusterNode node) { if (crdChanged) { for (Map.Entry m : fullMsgs.entrySet()) { if (crd0.equals(m.getKey())) { - log.info("Coordinator changed, process pending full message [" + - "ver=" + initialVersion() + - ", crd=" + node.id() + - ", pendingMsgNode=" + m.getKey() + ']'); + if (log.isInfoEnabled()) { + log.info("Coordinator changed, process pending full message [" + + "ver=" + initialVersion() + + ", crd=" + node.id() + + ", pendingMsgNode=" + m.getKey() + ']'); + } processFullMessage(true, m.getKey(), m.getValue()); @@ -3069,10 +3115,12 @@ public void onNodeLeft(final ClusterNode node) { } } - log.info("Coordinator changed, send partitions to new coordinator [" + - "ver=" + initialVersion() + - ", crd=" + node.id() + - ", newCrd=" + crd0.id() + ']'); + if (log.isInfoEnabled()) { + log.info("Coordinator changed, send partitions to new coordinator [" + + "ver=" + initialVersion() + + ", crd=" + node.id() + + ", newCrd=" + crd0.id() + ']'); + } sendPartitions(crd0); } @@ -3109,8 +3157,10 @@ private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { assert msgs.isEmpty() : msgs; if (fullMsg != null) { - log.info("New coordinator restored state [ver=" + initialVersion() + - ", resVer=" + fullMsg.resultTopologyVersion() + ']'); + if (log.isInfoEnabled()) { + log.info("New coordinator restored state [ver=" + initialVersion() + + ", resVer=" + fullMsg.resultTopologyVersion() + ']'); + } synchronized (mux) { state = ExchangeLocalState.DONE; @@ -3142,13 +3192,20 @@ private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { } } + if (log.isInfoEnabled()) { + log.info("New coordinator sends full message [ver=" + initialVersion() + + ", resVer=" + fullMsg.resultTopologyVersion() + + ", nodes=" + F.nodeIds(msgs.keySet()) + ']'); + } + sendAllPartitions(fullMsg, msgs.keySet(), null, joinedNodeAff); } return; } else { - log.info("New coordinator restore state finished [ver=" + initialVersion() + ']'); + if (log.isInfoEnabled()) + log.info("New coordinator restore state finished [ver=" + initialVersion() + ']'); for (Map.Entry e : newCrdFut.messages().entrySet()) { GridDhtPartitionsSingleMessage msg = e.getValue(); @@ -3183,8 +3240,10 @@ private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { assert mergedJoinExchMsgs == null; - log.info("New coordinator initialization finished [ver=" + initialVersion() + - ", remaining=" + remaining + ']'); + if (log.isInfoEnabled()) { + log.info("New coordinator initialization finished [ver=" + initialVersion() + + ", remaining=" + remaining + ']'); + } if (!remaining.isEmpty()) remaining0 = new HashSet<>(remaining); @@ -3197,8 +3256,10 @@ private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { for (UUID nodeId : remaining0) { try { if (!pendingSingleMsgs.containsKey(nodeId)) { - log.info("New coordinator sends request [ver=" + initialVersion() + - ", node=" + nodeId + ']'); + if (log.isInfoEnabled()) { + log.info("New coordinator sends request [ver=" + initialVersion() + + ", node=" + nodeId + ']'); + } cctx.io().send(nodeId, req, SYSTEM_POOL); } @@ -3214,8 +3275,10 @@ private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { } for (Map.Entry m : pendingSingleMsgs.entrySet()) { - log.info("New coordinator process pending message [ver=" + initialVersion() + - ", node=" + m.getKey() + ']'); + if (log.isInfoEnabled()) { + log.info("New coordinator process pending message [ver=" + initialVersion() + + ", node=" + m.getKey() + ']'); + } processSingleMessage(m.getKey(), m.getValue()); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java index 42ce9b988a22b..d0e619b21d194 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java @@ -147,8 +147,10 @@ public void init(GridDhtPartitionsExchangeFuture exchFut) throws IgniteCheckedEx } } - log.info("Try restore exchange result [allNodes=" + awaited + - ", joined=" + joinedNodes.keySet() + ']'); + if (log.isInfoEnabled()) { + log.info("Try restore exchange result [allNodes=" + awaited + + ", joined=" + joinedNodes.keySet() + ']'); + } if (!nodes.isEmpty()) { GridDhtPartitionsSingleRequest req = GridDhtPartitionsSingleRequest.restoreStateRequest(exchFut.exchangeId(), @@ -179,6 +181,9 @@ public void init(GridDhtPartitionsExchangeFuture exchFut) throws IgniteCheckedEx markInitialized(); } + /** + * @return {@code True} if new coordinator tried to restore exchange state. + */ boolean restoreState() { return restoreState; } @@ -202,9 +207,11 @@ GridDhtPartitionsFullMessage fullMessage() { * @param msg Message. */ public void onMessage(ClusterNode node, GridDhtPartitionsSingleMessage msg) { - log.info("Init new coordinator, received response [node=" + node.id() + - ", fullMsg=" + (msg.finishMessage() != null) + - ", affReq=" + !F.isEmpty(msg.cacheGroupsAffinityRequest()) + ']'); + if (log.isInfoEnabled()) { + log.info("Init new coordinator, received response [node=" + node.id() + + ", fullMsg=" + (msg.finishMessage() != null) + + ", affReq=" + !F.isEmpty(msg.cacheGroupsAffinityRequest()) + ']'); + } assert msg.restoreState() : msg; @@ -248,9 +255,11 @@ private void onAllReceived() { if (msgVer != null) { assert msgVer.topologyVersion().compareTo(initTopVer) > 0 : msgVer; - log.info("Process joined node message [resVer=" + resVer + - ", initTopVer=" + initTopVer + - ", msgVer=" + msgVer.topologyVersion() + ']'); + if (log.isInfoEnabled()) { + log.info("Process joined node message [resVer=" + resVer + + ", initTopVer=" + initTopVer + + ", msgVer=" + msgVer.topologyVersion() + ']'); + } if (msgVer.topologyVersion().compareTo(resVer) > 0) it.remove(); @@ -270,8 +279,10 @@ private void onAllReceived() { assert msgVer.topologyVersion().compareTo(initTopVer) > 0 : msgVer; - log.info("Process joined node message [initTopVer=" + initTopVer + - ", msgVer=" + msgVer.topologyVersion() + ']'); + if (log.isInfoEnabled()) { + log.info("Process joined node message [initTopVer=" + initTopVer + + ", msgVer=" + msgVer.topologyVersion() + ']'); + } if (joinExchMsgs == null) joinExchMsgs = new HashMap<>(); @@ -285,6 +296,10 @@ private void onAllReceived() { } } + /** + * @param nodeId Node ID. + * @return Single message for node joined after exchange start. + */ @Nullable GridDhtPartitionsSingleMessage joinExchangeMessage(UUID nodeId) { return joinExchMsgs != null ? joinExchMsgs.get(nodeId) : null; } @@ -293,7 +308,8 @@ private void onAllReceived() { * @param nodeId Failed node ID. */ public void onNodeLeft(UUID nodeId) { - log.info("Init new coordinator, node left [node=" + nodeId + ']'); + if (log.isInfoEnabled()) + log.info("Init new coordinator, node left [node=" + nodeId + ']'); boolean done; From 6f407ebabb9dc27459fdbee6423640132b995b1d Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Wed, 23 Aug 2017 15:46:23 +0300 Subject: [PATCH 098/145] IGNITE-6169: Fixed thin JDBC driver compatibility problem. --- .../internal/jdbc/thin/JdbcThinTcpIo.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) 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 b6bf222b4ad01..fdec77e62f131 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 @@ -219,7 +219,7 @@ public void handshake() throws IOException, IgniteCheckedException { boolean accepted = reader.readBoolean(); if (accepted) { - if (reader.available() > 3) { + if (reader.available() > 0) { byte maj = reader.readByte(); byte min = reader.readByte(); byte maintenance = reader.readByte(); @@ -232,21 +232,20 @@ public void handshake() throws IOException, IgniteCheckedException { igniteVer = new IgniteProductVersion(maj, min, maintenance, stage, ts, hash); } else - igniteVer = new IgniteProductVersion((byte)2, (byte)1, (byte)0, "Unknown 2.1 version", 0L, null); - - return; + igniteVer = new IgniteProductVersion((byte)2, (byte)0, (byte)0, "Unknown", 0L, null); } + else { + short maj = reader.readShort(); + short min = reader.readShort(); + short maintenance = reader.readShort(); - short maj = reader.readShort(); - short min = reader.readShort(); - short maintenance = reader.readShort(); + String err = reader.readString(); - String err = reader.readString(); + SqlListenerProtocolVersion ver = SqlListenerProtocolVersion.create(maj, min, maintenance); - SqlListenerProtocolVersion ver = SqlListenerProtocolVersion.create(maj, min, maintenance); - - throw new IgniteCheckedException("Handshake failed [driverProtocolVer=" + CURRENT_VER + - ", remoteNodeProtocolVer=" + ver + ", err=" + err + ']'); + throw new IgniteCheckedException("Handshake failed [driverProtocolVer=" + CURRENT_VER + + ", remoteNodeProtocolVer=" + ver + ", err=" + err + ']'); + } } /** From a5e376f63886696331e5be0c457dc0624c49e3d4 Mon Sep 17 00:00:00 2001 From: sboikov Date: Wed, 23 Aug 2017 16:44:04 +0300 Subject: [PATCH 099/145] ignite-6124 Added missed initialization of merged join exchanges in GridDhtPartitionsExchangeFuture.onBecomeCoordinator (cherry picked from commit 0c5dca9) --- .../GridCachePartitionExchangeManager.java | 71 ++++++++++++------- .../GridDhtPartitionsExchangeFuture.java | 7 +- .../preloader/InitNewCoordinatorFuture.java | 22 +++++- 3 files changed, 71 insertions(+), 29 deletions(-) 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 0bef8207dd118..aad5b3580ca62 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 @@ -309,9 +309,11 @@ private void processEventInactive(DiscoveryEvent evt, DiscoCache cache) { if (!crdInitFut.isDone() && !msg.restoreState()) { GridDhtPartitionExchangeId exchId = msg.exchangeId(); - log.info("Waiting for coordinator initialization [node=" + node.id() + - ", nodeOrder=" + node.order() + - ", ver=" + (exchId != null ? exchId.topologyVersion() : null) + ']'); + if (log.isInfoEnabled()) { + log.info("Waiting for coordinator initialization [node=" + node.id() + + ", nodeOrder=" + node.order() + + ", ver=" + (exchId != null ? exchId.topologyVersion() : null) + ']'); + } crdInitFut.listen(new CI1() { @Override public void apply(IgniteInternalFuture fut) { @@ -1810,18 +1812,22 @@ public boolean mergeExchanges(final GridDhtPartitionsExchangeFuture curFut, Grid GridDhtPartitionsExchangeFuture fut = (GridDhtPartitionsExchangeFuture) task; if (fut.initialVersion().compareTo(resVer) > 0) { - log.info("Merge exchange future on finish stop [curFut=" + curFut.initialVersion() + - ", resVer=" + resVer + - ", nextFutVer=" + fut.initialVersion() + ']'); + if (log.isInfoEnabled()) { + log.info("Merge exchange future on finish stop [curFut=" + curFut.initialVersion() + + ", resVer=" + resVer + + ", nextFutVer=" + fut.initialVersion() + ']'); + } break; } - log.info("Merge exchange future on finish [curFut=" + curFut.initialVersion() + - ", mergedFut=" + fut.initialVersion() + - ", evt=" + IgniteUtils.gridEventName(fut.firstEvent().type()) + - ", evtNode=" + fut.firstEvent().eventNode().id()+ - ", evtNodeClient=" + CU.clientNode(fut.firstEvent().eventNode())+ ']'); + if (log.isInfoEnabled()) { + log.info("Merge exchange future on finish [curFut=" + curFut.initialVersion() + + ", mergedFut=" + fut.initialVersion() + + ", evt=" + IgniteUtils.gridEventName(fut.firstEvent().type()) + + ", evtNode=" + fut.firstEvent().eventNode().id()+ + ", evtNodeClient=" + CU.clientNode(fut.firstEvent().eventNode())+ ']'); + } DiscoveryEvent evt = fut.firstEvent(); @@ -1832,8 +1838,16 @@ public boolean mergeExchanges(final GridDhtPartitionsExchangeFuture curFut, Grid if (evt.type() == EVT_NODE_JOINED) { final GridDhtPartitionsSingleMessage pendingMsg = fut.mergeJoinExchangeOnDone(curFut); - if (pendingMsg != null) + if (pendingMsg != null) { + if (log.isInfoEnabled()) { + log.info("Merged join exchange future on finish, will reply to node [" + + "curFut=" + curFut.initialVersion() + + ", mergedFut=" + fut.initialVersion() + + ", evtNode=" + evt.eventNode().id() + ']'); + } + curFut.waitAndReplyToNode(evt.eventNode().id(), pendingMsg); + } } } } @@ -1865,8 +1879,10 @@ public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFu AffinityTopologyVersion exchMergeTestWaitVer = this.exchMergeTestWaitVer; if (exchMergeTestWaitVer != null) { - log.info("Exchange merge test, waiting for version [exch=" + curFut.initialVersion() + - ", waitVer=" + exchMergeTestWaitVer + ']'); + if (log.isInfoEnabled()) { + log.info("Exchange merge test, waiting for version [exch=" + curFut.initialVersion() + + ", waitVer=" + exchMergeTestWaitVer + ']'); + } long end = U.currentTimeMillis() + 10_000; @@ -1878,7 +1894,8 @@ public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFu GridDhtPartitionsExchangeFuture fut = (GridDhtPartitionsExchangeFuture)task; if (exchMergeTestWaitVer.equals(fut.initialVersion())) { - log.info("Exchange merge test, found awaited version: " + exchMergeTestWaitVer); + if (log.isInfoEnabled()) + log.info("Exchange merge test, found awaited version: " + exchMergeTestWaitVer); found = true; @@ -1912,7 +1929,8 @@ public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFu DiscoveryEvent evt = fut.firstEvent(); if (evt.type() == EVT_DISCOVERY_CUSTOM_EVT) { - log.info("Stop merge, custom event found: " + evt); + if (log.isInfoEnabled()) + log.info("Stop merge, custom event found: " + evt); break; } @@ -1920,21 +1938,25 @@ public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFu ClusterNode node = evt.eventNode(); if (!curFut.context().supportsMergeExchanges(node)) { - log.info("Stop merge, node does not support merge: " + 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)) { - log.info("Stop merge, received caches from node: " + node); + if (log.isInfoEnabled()) + log.info("Stop merge, received caches from node: " + node); break; } - log.info("Merge exchange future [curFut=" + curFut.initialVersion() + - ", mergedFut=" + fut.initialVersion() + - ", evt=" + IgniteUtils.gridEventName(fut.firstEvent().type()) + - ", evtNode=" + fut.firstEvent().eventNode().id() + - ", evtNodeClient=" + CU.clientNode(fut.firstEvent().eventNode())+ ']'); + if (log.isInfoEnabled()) { + log.info("Merge exchange future [curFut=" + curFut.initialVersion() + + ", mergedFut=" + fut.initialVersion() + + ", evt=" + IgniteUtils.gridEventName(fut.firstEvent().type()) + + ", evtNode=" + fut.firstEvent().eventNode().id() + + ", evtNodeClient=" + CU.clientNode(fut.firstEvent().eventNode())+ ']'); + } curFut.context().events().addEvent(fut.initialVersion(), fut.firstEvent(), @@ -1947,7 +1969,8 @@ public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFu } else { if (!task.skipForExchangeMerge()) { - log.info("Stop merge, custom task found: " + task); + if (log.isInfoEnabled()) + log.info("Stop merge, custom task found: " + task); break; } 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 704062998eb52..5755bc72cf44a 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 @@ -3192,13 +3192,16 @@ private void onBecomeCoordinator(InitNewCoordinatorFuture newCrdFut) { } } + Map mergedJoins = newCrdFut.mergedJoinExchangeMessages(); + if (log.isInfoEnabled()) { log.info("New coordinator sends full message [ver=" + initialVersion() + ", resVer=" + fullMsg.resultTopologyVersion() + - ", nodes=" + F.nodeIds(msgs.keySet()) + ']'); + ", nodes=" + F.nodeIds(msgs.keySet()) + + ", mergedJoins=" + (mergedJoins != null ? mergedJoins.keySet() : null) + ']'); } - sendAllPartitions(fullMsg, msgs.keySet(), null, joinedNodeAff); + sendAllPartitions(fullMsg, msgs.keySet(), mergedJoins, joinedNodeAff); } return; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java index d0e619b21d194..b5acd4b0e8fcf 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/InitNewCoordinatorFuture.java @@ -195,6 +195,13 @@ Map messages() { return msgs; } + /** + * @return Messages for merged join exchanges. + */ + Map mergedJoinExchangeMessages() { + return joinExchMsgs; + } + /** * @return Full message is some of nodes received it from previous coordinator. */ @@ -247,7 +254,8 @@ private void onAllReceived() { if (fullMsg != null) { AffinityTopologyVersion resVer = fullMsg.resultTopologyVersion(); - for (Iterator> it = msgs.entrySet().iterator(); it.hasNext();) { + for (Iterator> it = msgs.entrySet().iterator(); + it.hasNext();) { Map.Entry e = it.next(); GridDhtPartitionExchangeId msgVer = joinedNodes.get(e.getKey().id()); @@ -263,8 +271,16 @@ private void onAllReceived() { if (msgVer.topologyVersion().compareTo(resVer) > 0) it.remove(); - else - e.getValue().exchangeId(msgVer); + else { + GridDhtPartitionsSingleMessage msg = e.getValue(); + + msg.exchangeId(msgVer); + + if (joinExchMsgs == null) + joinExchMsgs = new HashMap<>(); + + joinExchMsgs.put(e.getKey().id(), msg); + } } } } From be5589db9e0600b295b745ddab5e7aae390ac7ae Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Wed, 23 Aug 2017 17:25:33 +0300 Subject: [PATCH 100/145] ignite-5986 : Fixed failing .NET test. --- .../query/h2/opt/GridLuceneDirectory.java | 9 +++++---- .../processors/query/h2/opt/GridLuceneFile.java | 6 +++++- .../query/h2/opt/GridLuceneIndex.java | 17 +++++++++++------ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java index 3ac9641f762db..f92b510cdfd2b 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java @@ -133,7 +133,7 @@ private void doDeleteFile(String name, boolean onClose) throws IOException { @Override public IndexOutput createOutput(final String name, final IOContext context) throws IOException { ensureOpen(); - GridLuceneFile file = newRAMFile(); + GridLuceneFile file = newRAMFile(name); // Lock for using in stream. Will be unlocked on stream closing. file.lockRef(); @@ -160,8 +160,8 @@ private void doDeleteFile(String name, boolean onClose) throws IOException { * * @return New ram file. */ - protected GridLuceneFile newRAMFile() { - return new GridLuceneFile(this); + protected GridLuceneFile newRAMFile(String filename) { + return new GridLuceneFile(this, filename); } /** {@inheritDoc} */ @@ -198,7 +198,8 @@ protected GridLuceneFile newRAMFile() { } catch (IOException e) { if (errs == null) - errs = new IgniteException("Error closing index directory."); + errs = new IgniteException("Failed to close index directory."+ + " Some index readers was closed properly, that may leads memory leak."); errs.addSuppressed(e); } diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java index d7ae132903b97..8f77f35173b09 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneFile.java @@ -36,6 +36,9 @@ public class GridLuceneFile implements Accountable { /** */ private long length; + /** */ + private final String name; + /** */ private final GridLuceneDirectory dir; @@ -53,8 +56,9 @@ public class GridLuceneFile implements Accountable { * * @param dir Directory. */ - GridLuceneFile(GridLuceneDirectory dir) { + GridLuceneFile(GridLuceneDirectory dir, String name) { this.dir = dir; + this.name = name; } /** diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java index c51eb5d37cf54..0776a540ee565 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java @@ -262,14 +262,18 @@ public GridCloseableIterator> query(String qry, throw new IgniteCheckedException(e); } - IndexSearcher searcher = new IndexSearcher(reader); - - MultiFieldQueryParser parser = new MultiFieldQueryParser(idxdFields, - writer.getAnalyzer()); + IndexSearcher searcher; TopDocs docs; try { + searcher = new IndexSearcher(reader); + + MultiFieldQueryParser parser = new MultiFieldQueryParser(idxdFields, + writer.getAnalyzer()); + + parser.setAllowLeadingWildcard(true); + // Filter expired items. Query filter = NumericRangeQuery.newLongRange(EXPIRATION_TIME_FIELD_NAME, U.currentTimeMillis(), null, false, false); @@ -282,6 +286,8 @@ public GridCloseableIterator> query(String qry, docs = searcher.search(query, Integer.MAX_VALUE); } catch (Exception e) { + U.closeQuiet(reader); + throw new IgniteCheckedException(e); } @@ -296,8 +302,7 @@ public GridCloseableIterator> query(String qry, /** {@inheritDoc} */ @Override public void close() { U.closeQuiet(writer); - - dir.close(); + U.close(dir, ctx.log(GridLuceneIndex.class)); } /** From 43e4ff2c0ecd1ef30d18cf1fbc9052f5ba703d05 Mon Sep 17 00:00:00 2001 From: sboikov Date: Tue, 18 Jul 2017 17:52:51 +0300 Subject: [PATCH 101/145] Fixed test IgniteClusterActivateDeactivateTestWithPersistence. (cherry picked from commit 54585ab) --- .../processors/cache/query/GridCacheQueryResponse.java | 4 ++-- .../cache/IgniteClusterActivateDeactivateTest.java | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java index 521aacf76b9c4..13e0915875b92 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java @@ -130,10 +130,10 @@ public GridCacheQueryResponse(int cacheId, long reqId, Throwable err, boolean ad if (err != null && errBytes == null) errBytes = U.marshal(ctx, err); - if (metaDataBytes == null) + if (metaDataBytes == null && metadata != null) metaDataBytes = marshalCollection(metadata, cctx); - if (dataBytes == null) + if (dataBytes == null && data != null) dataBytes = marshalCollection(data, cctx); if (addDepInfo && !F.isEmpty(data)) { diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java index a3e0d2ca422f4..4c9ad271fe96f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClusterActivateDeactivateTest.java @@ -366,13 +366,18 @@ private IgniteInternalFuture startNodesAndBlockStatusChange(int srvs, startWithCaches1(srvs, clients); - if (initiallyActive && persistenceEnabled()) + int minorVer = 1; + + if (initiallyActive && persistenceEnabled()) { ignite(0).active(true); + minorVer++; + } + if (blockMsgNodes.length == 0) blockMsgNodes = new int[]{1}; - final AffinityTopologyVersion STATE_CHANGE_TOP_VER = new AffinityTopologyVersion(srvs + clients, 1); + final AffinityTopologyVersion STATE_CHANGE_TOP_VER = new AffinityTopologyVersion(srvs + clients, minorVer); List spis = new ArrayList<>(); From d596b7806db3f002f83da5a02bc882d03dae3dfd Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Wed, 23 Aug 2017 18:23:06 +0300 Subject: [PATCH 102/145] Updated classnames.properties. --- .../resources/META-INF/classnames.properties | 76 +++++++++++++------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/modules/core/src/main/resources/META-INF/classnames.properties b/modules/core/src/main/resources/META-INF/classnames.properties index 675bd56c5bc64..2fb8f4b38d054 100644 --- a/modules/core/src/main/resources/META-INF/classnames.properties +++ b/modules/core/src/main/resources/META-INF/classnames.properties @@ -132,6 +132,7 @@ org.apache.ignite.compute.gridify.aop.GridifyDefaultRangeTask org.apache.ignite.compute.gridify.aop.GridifyDefaultTask org.apache.ignite.configuration.CacheConfiguration org.apache.ignite.configuration.CacheConfiguration$IgniteAllNodesPredicate +org.apache.ignite.configuration.CheckpointWriteOrder org.apache.ignite.configuration.CollectionConfiguration org.apache.ignite.configuration.DataPageEvictionMode org.apache.ignite.configuration.DeploymentMode @@ -303,6 +304,7 @@ org.apache.ignite.internal.executor.GridExecutorService org.apache.ignite.internal.executor.GridExecutorService$1 org.apache.ignite.internal.executor.GridExecutorService$TaskTerminateListener org.apache.ignite.internal.igfs.common.IgfsIpcCommand +org.apache.ignite.internal.jdbc2.JdbcBatchUpdateTask org.apache.ignite.internal.jdbc2.JdbcConnection$JdbcConnectionValidationTask org.apache.ignite.internal.jdbc2.JdbcDatabaseMetadata$UpdateMetadataTask org.apache.ignite.internal.jdbc2.JdbcQueryTask @@ -347,19 +349,25 @@ org.apache.ignite.internal.processors.affinity.GridAffinityAssignment org.apache.ignite.internal.processors.affinity.GridAffinityMessage org.apache.ignite.internal.processors.affinity.GridAffinityUtils$AffinityJob org.apache.ignite.internal.processors.cache.CacheAffinityChangeMessage +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$1 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$10 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$11 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$12 -org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$12$1 -org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$12$2 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$13 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$14 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$15 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$16 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$17 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$18 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$18$1 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$18$2 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$19 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$2 -org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$3 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$20 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$21 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$22 +org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$4 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$5 -org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$6 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$7 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$8 org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager$9 @@ -411,15 +419,16 @@ org.apache.ignite.internal.processors.cache.DynamicCacheChangeRequest org.apache.ignite.internal.processors.cache.EntryProcessorResourceInjectorProxy org.apache.ignite.internal.processors.cache.ExchangeActions$1 org.apache.ignite.internal.processors.cache.ExchangeActions$2 +org.apache.ignite.internal.processors.cache.GatewayProtectedCacheProxy org.apache.ignite.internal.processors.cache.GridCacheAdapter org.apache.ignite.internal.processors.cache.GridCacheAdapter$10 org.apache.ignite.internal.processors.cache.GridCacheAdapter$11 org.apache.ignite.internal.processors.cache.GridCacheAdapter$12 org.apache.ignite.internal.processors.cache.GridCacheAdapter$13 org.apache.ignite.internal.processors.cache.GridCacheAdapter$14 -org.apache.ignite.internal.processors.cache.GridCacheAdapter$15$1 +org.apache.ignite.internal.processors.cache.GridCacheAdapter$15 org.apache.ignite.internal.processors.cache.GridCacheAdapter$16 -org.apache.ignite.internal.processors.cache.GridCacheAdapter$17 +org.apache.ignite.internal.processors.cache.GridCacheAdapter$17$1 org.apache.ignite.internal.processors.cache.GridCacheAdapter$2 org.apache.ignite.internal.processors.cache.GridCacheAdapter$25$1 org.apache.ignite.internal.processors.cache.GridCacheAdapter$27 @@ -479,7 +488,7 @@ org.apache.ignite.internal.processors.cache.GridCacheEntryInfo org.apache.ignite.internal.processors.cache.GridCacheEntryRedeployException org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException org.apache.ignite.internal.processors.cache.GridCacheExplicitLockSpan -org.apache.ignite.internal.processors.cache.GridCacheExplicitLockSpan$1 +org.apache.ignite.internal.processors.cache.GridCacheExplicitLockSpan$2 org.apache.ignite.internal.processors.cache.GridCacheFilterFailedException org.apache.ignite.internal.processors.cache.GridCacheGateway$State org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage @@ -502,14 +511,15 @@ org.apache.ignite.internal.processors.cache.GridCacheMvccManager$7 org.apache.ignite.internal.processors.cache.GridCacheMvccManager$FinishLockFuture$1 org.apache.ignite.internal.processors.cache.GridCacheOperation org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$2 +org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$2$1 org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$3 org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$4 org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$5 org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$6 -org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$7 org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$ExchangeFutureSet org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$MessageHandler org.apache.ignite.internal.processors.cache.GridCacheProcessor$1 +org.apache.ignite.internal.processors.cache.GridCacheProcessor$10 org.apache.ignite.internal.processors.cache.GridCacheProcessor$3 org.apache.ignite.internal.processors.cache.GridCacheProcessor$4 org.apache.ignite.internal.processors.cache.GridCacheProcessor$5 @@ -557,15 +567,16 @@ org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$7 org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$9 org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl$1 org.apache.ignite.internal.processors.cache.IgniteCacheProxy -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$1 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$1$1 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$10 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$2 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$3 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$4 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$6 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$7 -org.apache.ignite.internal.processors.cache.IgniteCacheProxy$8 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$1 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$1$1 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$2 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$3 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$4 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$6 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$7 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$8 +org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl$9 org.apache.ignite.internal.processors.cache.IgniteRebalanceIterator org.apache.ignite.internal.processors.cache.KeyCacheObject org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl @@ -640,6 +651,7 @@ org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactional org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$11 org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$12 org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$13 +org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$14 org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$2 org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$3 org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter$4 @@ -748,6 +760,9 @@ org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtCol org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtColocatedLockFuture$4 org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtColocatedLockFuture$LockTimeoutObject$1 org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtColocatedLockFuture$MiniFuture$1 +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CacheGroupAffinityMessage +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionFullCountersMap +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysFuture$1 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysRequest org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysResponse @@ -764,11 +779,15 @@ org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPar org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplier$SupplyContextPhase org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsAbstractMessage -org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$1 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$2 +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$3 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$4 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$5 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$6 +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$7 +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$8 +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$8$1$1 +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$ExchangeLocalState org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture$ExchangeType org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsFullMessage org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsSingleMessage @@ -777,6 +796,7 @@ org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPre org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader$2 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloaderAssignments org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteDhtPartitionCountersMap +org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteDhtPartitionCountersMap2 org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteDhtPartitionHistorySuppliersMap org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteDhtPartitionsToReloadMap org.apache.ignite.internal.processors.cache.distributed.near.CacheVersionedValue @@ -865,10 +885,11 @@ org.apache.ignite.internal.processors.cache.local.atomic.GridLocalAtomicCache$5 org.apache.ignite.internal.processors.cache.local.atomic.GridLocalAtomicCache$8 org.apache.ignite.internal.processors.cache.local.atomic.GridLocalAtomicCache$9 org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter$RowData -org.apache.ignite.internal.processors.cache.persistence.FullPageIdIterableComparator -org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager$8 +org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager$6 +org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager$9 org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager$CheckpointEntryType org.apache.ignite.internal.processors.cache.persistence.GridCacheOffheapManager$RebalanceIteratorAdapter +org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager$1 org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory org.apache.ignite.internal.processors.cache.persistence.file.RandomAccessFileIOFactory org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl$Segment @@ -968,6 +989,7 @@ org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$14 org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$15 org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$16 org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$17 +org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$18 org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$2 org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$3 org.apache.ignite.internal.processors.cache.transactions.IgniteTxHandler$4 @@ -1041,15 +1063,17 @@ org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$1$1 org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$2 org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$3 org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$4 -org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$6 +org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$5 +org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$7 +org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$CheckGlobalStateComputeRequest org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor$ClientChangeGlobalStateComputeRequest org.apache.ignite.internal.processors.continuous.AbstractContinuousMessage org.apache.ignite.internal.processors.continuous.GridContinuousHandler org.apache.ignite.internal.processors.continuous.GridContinuousHandler$RegisterStatus org.apache.ignite.internal.processors.continuous.GridContinuousMessage org.apache.ignite.internal.processors.continuous.GridContinuousMessageType -org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$10$1 -org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$9 +org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$8 +org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$9$1 org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$DiscoveryData org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$DiscoveryDataItem org.apache.ignite.internal.processors.continuous.GridContinuousProcessor$LocalRoutineInfo @@ -1991,16 +2015,18 @@ org.apache.ignite.spi.checkpoint.sharedfs.SharedFsCheckpointData org.apache.ignite.spi.collision.jobstealing.JobStealingRequest org.apache.ignite.spi.collision.priorityqueue.PriorityQueueCollisionSpi$PriorityGridCollisionJobContextComparator org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$1 +org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$11 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$12 -org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$13 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$2$1 +org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$2$2 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$2$ConnectClosure org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$2$ConnectClosure$1 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$2$ConnectClosureNew org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$2$ConnectClosureNew$1 +org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$4 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$5 -org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$6 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$HandshakeClosure +org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$HandshakeException org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$HandshakeMessage org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$HandshakeMessage2 org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi$HandshakeTimeoutException From 3e08cd401d598a34832e72afc5e6c94a3a9ab081 Mon Sep 17 00:00:00 2001 From: sboikov Date: Wed, 23 Aug 2017 18:29:52 +0300 Subject: [PATCH 103/145] ignite-6174 Temporary changed test until issue not fixed (cherry picked from commit 4fe8f76) --- ...rimaryNodeFailureRecoveryAbstractTest.java | 111 ++++++++++-------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java index 0d0cda4dba96c..d147d31b1b08f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCachePrimaryNodeFailureRecoveryAbstractTest.java @@ -56,6 +56,7 @@ import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; import static org.apache.ignite.cache.CacheMode.PARTITIONED; +import static org.apache.ignite.internal.processors.cache.ExchangeContext.IGNITE_EXCHANGE_COMPATIBILITY_VER_1; 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; @@ -328,91 +329,99 @@ private void primaryAndOriginatingNodeFailure(final boolean locBackupKey, boolean optimistic) throws Exception { - IgniteCache cache0 = jcache(0); - IgniteCache cache2 = jcache(2); + // TODO IGNITE-6174: when exchanges can be merged test fails because of IGNITE-6174. + System.setProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1, "true"); - Affinity aff = ignite(0).affinity(DEFAULT_CACHE_NAME); + try { + IgniteCache cache0 = jcache(0); + IgniteCache cache2 = jcache(2); - Integer key0 = null; + Affinity aff = ignite(0).affinity(DEFAULT_CACHE_NAME); - for (int key = 0; key < 10_000; key++) { - if (aff.isPrimary(ignite(1).cluster().localNode(), key)) { - if (locBackupKey == aff.isBackup(ignite(0).cluster().localNode(), key)) { - key0 = key; + Integer key0 = null; - break; + for (int key = 0; key < 10_000; key++) { + if (aff.isPrimary(ignite(1).cluster().localNode(), key)) { + if (locBackupKey == aff.isBackup(ignite(0).cluster().localNode(), key)) { + key0 = key; + + break; + } } } - } - assertNotNull(key0); + assertNotNull(key0); - final Integer key1 = key0; - final Integer key2 = primaryKey(cache2); + final Integer key1 = key0; + final Integer key2 = primaryKey(cache2); - int backups = cache0.getConfiguration(CacheConfiguration.class).getBackups(); + int backups = cache0.getConfiguration(CacheConfiguration.class).getBackups(); - final Collection key1Nodes = - (locBackupKey && backups < 2) ? null : aff.mapKeyToPrimaryAndBackups(key1); - final Collection key2Nodes = aff.mapKeyToPrimaryAndBackups(key2); + final Collection key1Nodes = + (locBackupKey && backups < 2) ? null : aff.mapKeyToPrimaryAndBackups(key1); + final Collection key2Nodes = aff.mapKeyToPrimaryAndBackups(key2); - TestCommunicationSpi commSpi = (TestCommunicationSpi)ignite(0).configuration().getCommunicationSpi(); + TestCommunicationSpi commSpi = (TestCommunicationSpi)ignite(0).configuration().getCommunicationSpi(); - IgniteTransactions txs = ignite(0).transactions(); + IgniteTransactions txs = ignite(0).transactions(); - Transaction tx = txs.txStart(optimistic ? OPTIMISTIC : PESSIMISTIC, REPEATABLE_READ); + Transaction tx = txs.txStart(optimistic ? OPTIMISTIC : PESSIMISTIC, REPEATABLE_READ); - log.info("Put key1 [key1=" + key1 + ", nodes=" + U.nodeIds(aff.mapKeyToPrimaryAndBackups(key1)) + ']'); + log.info("Put key1 [key1=" + key1 + ", nodes=" + U.nodeIds(aff.mapKeyToPrimaryAndBackups(key1)) + ']'); - cache0.put(key1, key1); + cache0.put(key1, key1); - log.info("Put key2 [key2=" + key2 + ", nodes=" + U.nodeIds(aff.mapKeyToPrimaryAndBackups(key2)) + ']'); + log.info("Put key2 [key2=" + key2 + ", nodes=" + U.nodeIds(aff.mapKeyToPrimaryAndBackups(key2)) + ']'); - cache0.put(key2, key2); + cache0.put(key2, key2); - log.info("Start prepare."); + log.info("Start prepare."); - GridNearTxLocal txEx = ((TransactionProxyImpl)tx).tx(); + GridNearTxLocal txEx = ((TransactionProxyImpl)tx).tx(); - commSpi.blockMessages(ignite(2).cluster().localNode().id()); // Do not allow to finish prepare for key2. + commSpi.blockMessages(ignite(2).cluster().localNode().id()); // Do not allow to finish prepare for key2. - IgniteInternalFuture prepFut = txEx.prepareNearTxLocal(); + IgniteInternalFuture prepFut = txEx.prepareNearTxLocal(); - waitPrepared(ignite(1)); + waitPrepared(ignite(1)); - log.info("Stop one primary node."); + log.info("Stop one primary node."); - stopGrid(1); + stopGrid(1); - U.sleep(1000); // Wait some time to catch possible issues in tx recovery. + U.sleep(1000); // Wait some time to catch possible issues in tx recovery. - if (!rollback) { - commSpi.stopBlock(); + if (!rollback) { + commSpi.stopBlock(); - prepFut.get(10_000); - } + prepFut.get(10_000); + } - log.info("Stop originating node."); + log.info("Stop originating node."); - stopGrid(0); + stopGrid(0); - GridTestUtils.waitForCondition(new GridAbsPredicate() { - @Override public boolean apply() { - try { - checkKey(key1, rollback ? null : key1Nodes); - checkKey(key2, rollback ? null : key2Nodes); + GridTestUtils.waitForCondition(new GridAbsPredicate() { + @Override public boolean apply() { + try { + checkKey(key1, rollback ? null : key1Nodes); + checkKey(key2, rollback ? null : key2Nodes); - return true; - } catch (AssertionError e) { - log.info("Check failed: " + e); + return true; + } catch (AssertionError e) { + log.info("Check failed: " + e); - return false; + return false; + } } - } - }, 5000); + }, 5000); - checkKey(key1, rollback ? null : key1Nodes); - checkKey(key2, rollback ? null : key2Nodes); + checkKey(key1, rollback ? null : key1Nodes); + checkKey(key2, rollback ? null : key2Nodes); + } + finally { + System.clearProperty(IGNITE_EXCHANGE_COMPATIBILITY_VER_1); + } } /** From 44e0b4cd62142dce8cf39f826449b9a04e22e1cf Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 24 Aug 2017 14:57:36 +0700 Subject: [PATCH 104/145] IGNITE-6136 Fixed version for demo. (cherry picked from commit e1bf8d7) --- .../frontend/app/modules/agent/AgentManager.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js index 67e670487ff27..2b9d9459288f5 100644 --- a/modules/web-console/frontend/app/modules/agent/AgentManager.service.js +++ b/modules/web-console/frontend/app/modules/agent/AgentManager.service.js @@ -99,7 +99,7 @@ export default class IgniteAgentManager { this.connectionSbj = new BehaviorSubject(new ConnectionState(cluster)); - this.clusterVersion = '2.0.0'; + this.clusterVersion = '2.1.0'; if (!$root.IgniteDemoMode) { this.connectionSbj.subscribe({ From 8d1838b03d6c1e5f86dfbb7f41c59895775e20c1 Mon Sep 17 00:00:00 2001 From: Dmitry Pavlov Date: Thu, 27 Jul 2017 14:51:25 +0300 Subject: [PATCH 105/145] Adjusted memory policy to prevent OOM. --- .../internal/processors/cache/index/LongIndexNameTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java index 92883a488405c..d567ef1487700 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/LongIndexNameTest.java @@ -27,6 +27,8 @@ import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.MemoryConfiguration; +import org.apache.ignite.configuration.MemoryPolicyConfiguration; import org.apache.ignite.configuration.PersistentStoreConfiguration; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; @@ -43,6 +45,8 @@ public class LongIndexNameTest extends GridCommonAbstractTest { @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { return super.getConfiguration(igniteInstanceName) .setPersistentStoreConfiguration(new PersistentStoreConfiguration()) + .setMemoryConfiguration(new MemoryConfiguration().setMemoryPolicies( + new MemoryPolicyConfiguration().setMaxSize(10 * 1024 * 1024))) .setCacheConfiguration(new CacheConfiguration("cache") .setQueryEntities(getIndexCfg()) .setAffinity(new RendezvousAffinityFunction(false, 16))); From a3ec54b16bce1a569fbefba17188ccb4702b82a4 Mon Sep 17 00:00:00 2001 From: sboikov Date: Thu, 24 Aug 2017 14:09:12 +0300 Subject: [PATCH 106/145] ignite-6124 DataStreamerImpl: do not wait for exchange future inside cache gateway. (cherry picked from commit 3ab523c) --- .../processors/datastreamer/DataStreamerImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java index e2ee0b1811aed..66817106f7fe6 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerImpl.java @@ -761,6 +761,13 @@ private void load0( GridCacheGateway gate = null; + AffinityTopologyVersion topVer; + + if (!cctx.isLocal()) + topVer = ctx.cache().context().exchange().lastTopologyFuture().get(); + else + topVer = ctx.cache().context().exchange().readyAffinityVersion(); + if (!allowOverwrite() && !cctx.isLocal()) { // Cases where cctx required. gate = cctx.gate(); @@ -768,13 +775,6 @@ private void load0( } try { - AffinityTopologyVersion topVer; - - if (!cctx.isLocal()) - topVer = ctx.cache().context().exchange().lastTopologyFuture().get(); - else - topVer = ctx.cache().context().exchange().readyAffinityVersion(); - for (DataStreamerEntry entry : entries) { List nodes; From 41f574a7372ffc04b69809298798f24fb34c161f Mon Sep 17 00:00:00 2001 From: Dmitriy Govorukhin Date: Thu, 24 Aug 2017 15:58:27 +0300 Subject: [PATCH 107/145] Fixed test. --- .../cluster/GridClusterStateProcessor.java | 2 +- ...IgnitePdsCacheRebalancingAbstractTest.java | 3 +- .../wal/IgniteWalHistoryReservationsTest.java | 70 ++++++++++++++++--- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java index cdfab21c80854..64ee47221b214 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java @@ -430,7 +430,7 @@ public IgniteInternalFuture changeGlobalState(final boolean activate) { GridChangeGlobalStateFuture fut = stateChangeFut.get(); - while (fut == null) { + while (fut == null || fut.isDone()) { fut = new GridChangeGlobalStateFuture(UUID.randomUUID(), activate, ctx); if (stateChangeFut.compareAndSet(null, fut)) { diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java index 9ceb87c31285c..2f439519b45b0 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java @@ -558,9 +558,8 @@ public void testPartitionCounterConsistencyOnUnstableTopology() throws Exception cntrs.put(part.id(), part.updateCounter()); } - for (int k0 = 0; k0 < k; k0++) { + for (int k0 = 0; k0 < k; k0++) assertEquals(String.valueOf(k0), k0, ig0.cache(cacheName).get(k0)); - } } assertEquals(ig.affinity(cacheName).partitions(), cntrs.size()); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalHistoryReservationsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalHistoryReservationsTest.java index 4bea63fc0447a..94045ff2d05e2 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalHistoryReservationsTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalHistoryReservationsTest.java @@ -22,6 +22,7 @@ import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteDataStreamer; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheWriteSynchronizationMode; import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; @@ -57,8 +58,14 @@ public class IgniteWalHistoryReservationsTest extends GridCommonAbstractTest { MemoryConfiguration memCfg = new MemoryConfiguration(); - memCfg.setMemoryPolicies(new MemoryPolicyConfiguration().setInitialSize(200 * 1024 * 1024) - .setMaxSize(200 * 1024 * 1024).setName("dfltMemPlc")); + long memSize = 200L * 1024L * 1024L; + + memCfg.setMemoryPolicies( + new MemoryPolicyConfiguration() + .setInitialSize(memSize) + .setMaxSize(memSize) + .setName("dfltMemPlc") + ); memCfg.setDefaultMemoryPolicyName("dfltMemPlc"); @@ -110,24 +117,59 @@ public void testReservedOnExchange() throws Exception { ig0.active(true); - IgniteCache cache = ig0.cache("cache1"); + long start = U.currentTimeMillis(); - for (int k = 0; k < entryCnt; k++) - cache.put(k, k); + log.warning("Start loading"); + + try (IgniteDataStreamer st = ig0.dataStreamer("cache1")){ + for (int k = 0; k < entryCnt; k++){ + st.addData(k, k); + + printProgress(k); + } + } + + log.warning("Finish loading time:" + (U.currentTimeMillis() - start)); forceCheckpoint(); - for (int k = 0; k < entryCnt; k++) - cache.put(k, k * 2); + start = U.currentTimeMillis(); + + log.warning("Start loading"); + + try (IgniteDataStreamer st = ig0.dataStreamer("cache1")) { + st.allowOverwrite(true); + + for (int k = 0; k < entryCnt; k++) { + st.addData(k, k * 2); + + printProgress(k); + } + } + + log.warning("Finish loading time:" + (U.currentTimeMillis() - start)); forceCheckpoint(); - for (int k = 0; k < entryCnt; k++) - cache.put(k, k); + start = U.currentTimeMillis(); + + log.warning("Start loading"); + + try (IgniteDataStreamer st = ig0.dataStreamer("cache1")){ + st.allowOverwrite(true); + + for (int k = 0; k < entryCnt; k++){ + st.addData(k, k); + + printProgress(k); + } + } + + log.warning("Finish loading time:" + (U.currentTimeMillis() - start)); forceCheckpoint(); - Lock lock = cache.lock(0); + Lock lock = ig0.cache("cache1").lock(0); lock.lock(); @@ -194,6 +236,14 @@ public void testReservedOnExchange() throws Exception { assert released; } + /** + * + */ + private void printProgress(int k){ + if (k % 1000 == 0) + log.warning("Keys -> " + k); + } + /** * @throws Exception If failed. */ From 943736b36d67381157fc2807cd7af4b03d44fef3 Mon Sep 17 00:00:00 2001 From: nikolay_tikhonov Date: Thu, 24 Aug 2017 18:58:16 +0300 Subject: [PATCH 108/145] Revert "IGNITE-5947 Fixed "ClassCastException when two-dimensional array is fetched from cache". * Due to this changes break compatibility with .NET; * This fix doesn't cover all cases. Signed-off-by: nikolay_tikhonov --- .../processors/cache/CacheObjectUtils.java | 27 +- .../cache/CacheTwoDimensionalArrayTest.java | 309 ------------------ .../testsuites/IgniteCacheTestSuite.java | 2 - 3 files changed, 2 insertions(+), 336 deletions(-) delete mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java index 6a6376803d9fa..f9c76df5814bd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectUtils.java @@ -17,7 +17,6 @@ package org.apache.ignite.internal.processors.cache; -import java.lang.reflect.Array; import org.apache.ignite.internal.binary.BinaryUtils; import org.apache.ignite.internal.util.typedef.F; @@ -124,30 +123,8 @@ private static Object[] unwrapBinariesInArrayIfNeeded(CacheObjectValueContext ct Object[] res = new Object[arr.length]; - boolean canCastArray = true; - Class cls = null; - - for (int i = 0; i < arr.length; i++) { - Object obj = unwrapBinary(ctx, arr[i], keepBinary, cpy); - - res[i] = obj; - - if (canCastArray && obj != null) { - if (cls == null) - cls = obj.getClass(); - else if (cls != obj.getClass()) - canCastArray = false; - } - } - - // If array contains all element the same type then will create typed array. - if (canCastArray && cls != null) { - Object[] res0 = (Object[])Array.newInstance(cls, res.length); - - System.arraycopy(res, 0, res0, 0, res.length); - - res = res0; - } + for (int i = 0; i < arr.length; i++) + res[i] = unwrapBinary(ctx, arr[i], keepBinary, cpy); return res; } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java deleted file mode 100644 index edba357c8142e..0000000000000 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTwoDimensionalArrayTest.java +++ /dev/null @@ -1,309 +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; - -import org.apache.ignite.IgniteCache; -import org.apache.ignite.cache.CacheAtomicityMode; -import org.apache.ignite.cache.CacheMode; -import org.apache.ignite.configuration.CacheConfiguration; -import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; -import org.jetbrains.annotations.NotNull; - -import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; -import static org.apache.ignite.cache.CacheMode.PARTITIONED; -import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; -import static org.junit.Assert.assertArrayEquals; - -/** - * - */ -public class CacheTwoDimensionalArrayTest extends GridCommonAbstractTest { - /** */ - private static int NODES = 3; - - /** */ - private static int KEYS = 100; - - /** */ - private static int size = 5; - - /** {@inheritDoc} */ - @Override protected void beforeTestsStarted() throws Exception { - startGridsMultiThreaded(NODES); - } - - /** {@inheritDoc} */ - @Override protected void afterTestsStopped() throws Exception { - super.afterTestsStopped(); - - stopAllGrids(); - } - - /** - * @throws Exception If failed. - */ - public void testSimpleModel() throws Exception { - doTestSimpleModel(ATOMIC, PARTITIONED); - } - - /** - * @param atomicityMode Cache atomicity mode. - * @param cacheMode Cache mode. - * - * @throws Exception If failed. - */ - private void doTestSimpleModel(CacheAtomicityMode atomicityMode, CacheMode cacheMode) throws Exception { - CacheConfiguration ccfg = getConfiguration(atomicityMode, cacheMode); - - ignite(0).getOrCreateCache(ccfg); - - int n = size, m = size -1; - - // Object array with primitives. - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, new Object[]{1}); - - for (int key = 0; key < KEYS; key++) { - Object[] exp = new Object[]{1}; - - Object[] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - // Primitive empty array. - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, new int[n][m]); - - for (int key = 0; key < KEYS; key++) { - int[][] exp = new int[n][m]; - - int[][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - // Object empty array. - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, new Object[n][m]); - - for (int key = 0; key < KEYS; key++) { - Object[][] exp = new Object[n][m]; - - Object[][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, intArray(n, m, key)); - - for (int key = 0; key < KEYS; key++) { - int[][] exp = intArray(n, m, key); - - int[][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, new int[5][6][7]); - - for (int key = 0; key < KEYS; key++) { - int[][][] exp = new int[5][6][7]; - - int[][][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, objectArray(n, m, key)); - - for (int key = 0; key < KEYS; key++) { - Object[][] exp = objectArray(n, m, key); - - Object[][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, testObjectArray(n, m, key)); - - for (int key = 0; key < KEYS; key++) { - TestObject[][] exp = testObjectArray(n, m, key); - - TestObject[][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - - { - IgniteCache cache = ignite(0).cache(ccfg.getName()); - - for (int key = 0; key < KEYS; key++) - cache.put(key, testObjectArray(n, m, key)); - - for (int key = 0; key < KEYS; key++) { - TestObject[][] exp = testObjectArray(n, m, key); - - TestObject[][] act = cache.get(key); - - assertArrayEquals(exp, act); - } - - cache.removeAll(); - } - } - - /** - * @return Array. - */ - private int[][] intArray(int n, int m,int K) { - int[][] arr = new int[n][m]; - - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) - arr[i][j] = (i + j) * K; - } - - return arr; - } - - /** - * @return Array. - */ - private Object[][] objectArray(int n, int m, int K) { - Object[][] arr = new Object[n][m]; - - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) - arr[i][j] = ((n + m) % 2 == 0) ? (i + j) * K : new TestObject((i + j) * K); - } - - return arr; - } - - /** - * @return Array. - */ - private TestObject[][] testObjectArray(int n, int m, int K) { - TestObject[][] arr = new TestObject[n][m]; - - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) - arr[i][j] = new TestObject((i + j) * K); - } - - return arr; - } - - /** - * @param atomicityMode Atomicity mode. - * @param cacheMode Cache mode. - * - * @return Cache configuration. - */ - @NotNull private CacheConfiguration getConfiguration(CacheAtomicityMode atomicityMode, - CacheMode cacheMode) { - CacheConfiguration ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME); - - ccfg.setCacheMode(cacheMode); - ccfg.setAtomicityMode(atomicityMode); - ccfg.setWriteSynchronizationMode(FULL_SYNC); - ccfg.setBackups(1); - - return ccfg; - } - - /** - * - */ - private static class TestObject { - /** */ - private int val; - - /** - * @param val Value. - */ - public TestObject(int val) { - this.val = val; - } - - /** {@inheritDoc} */ - @Override public boolean equals(Object o) { - if (this == o) - return true; - - if (o == null || getClass() != o.getClass()) - return false; - - TestObject object = (TestObject)o; - - return val == object.val; - } - - /** {@inheritDoc} */ - @Override public int hashCode() { - return val; - } - } -} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java index a1e3d6a61c38b..dea0eb0d593a7 100755 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java @@ -46,7 +46,6 @@ import org.apache.ignite.internal.processors.cache.CacheNamesSelfTest; import org.apache.ignite.internal.processors.cache.CacheNamesWithSpecialCharactersTest; import org.apache.ignite.internal.processors.cache.CachePutEventListenerErrorSelfTest; -import org.apache.ignite.internal.processors.cache.CacheTwoDimensionalArrayTest; import org.apache.ignite.internal.processors.cache.CacheTxFastFinishTest; import org.apache.ignite.internal.processors.cache.GridCacheAffinityApiSelfTest; import org.apache.ignite.internal.processors.cache.GridCacheAffinityMapperSelfTest; @@ -314,7 +313,6 @@ public static TestSuite suite(Set ignoredTests) throws Exception { suite.addTestSuite(IgniteDiagnosticMessagesMultipleConnectionsTest.class); suite.addTestSuite(IgniteIncompleteCacheObjectSelfTest.class); - suite.addTestSuite(CacheTwoDimensionalArrayTest.class); return suite; } From c2e836b5b9b183404f4507c64c13ab5c05653d24 Mon Sep 17 00:00:00 2001 From: EdShangGG Date: Thu, 24 Aug 2017 19:15:24 +0300 Subject: [PATCH 109/145] ignite-6175 JVM Crash in Ignite Binary Objects Simple Mapper Basic suite Signed-off-by: Andrey Gura --- .../database/BPlusTreeSelfTest.java | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) 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 4a32df27c08aa..9c0d7918e20d4 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 @@ -54,6 +54,7 @@ import org.apache.ignite.internal.util.GridRandom; import org.apache.ignite.internal.util.GridStripedLock; import org.apache.ignite.internal.util.IgniteTree; +import org.apache.ignite.internal.util.future.GridCompoundFuture; import org.apache.ignite.internal.util.lang.GridCursor; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.SB; @@ -112,11 +113,11 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest { /** */ private static final Collection rmvdIds = new GridConcurrentHashSet<>(); + /** Stop. */ + private final AtomicBoolean stop = new AtomicBoolean(); -// /** {@inheritDoc} */ -// @Override protected long getTestTimeout() { -// return 25 * 60 * 1000; -// } + /** Future. */ + private volatile GridCompoundFuture asyncRunFut; /** * Check that we do not keep any locks at the moment. @@ -127,6 +128,8 @@ protected void assertNoLocks() { /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { + stop.set(false); + long seed = System.nanoTime(); X.println("Test seed: " + seed + "L; // "); @@ -156,6 +159,18 @@ protected ReuseList createReuseList(int cacheId, PageMemory pageMem, long rootId rnd = null; try { + if (asyncRunFut != null && !asyncRunFut.isDone()) { + stop.set(true); + + try { + asyncRunFut.cancel(); + asyncRunFut.get(60000); + } + catch (Throwable ex) { + //Ignore + } + } + if (reuseList != null) { long size = reuseList.recycledPagesCount(); @@ -1316,7 +1331,7 @@ private void doTestRandomPutRemoveMultithreaded(boolean canGetRow) throws Except IgniteInternalFuture fut = multithreadedAsync(new Callable() { @Override public Object call() throws Exception { - for (int i = 0; i < loops; i++) { + for (int i = 0; i < loops && !stop.get(); i++) { final Long x = (long)DataStructure.randomInt(CNT); final int op = DataStructure.randomInt(4); @@ -1402,8 +1417,6 @@ else if (op == 3) { } }, Runtime.getRuntime().availableProcessors(), "put-remove"); - final AtomicBoolean stop = new AtomicBoolean(); - IgniteInternalFuture fut2 = multithreadedAsync(new Callable() { @Override public Void call() throws Exception { while (!stop.get()) { @@ -1442,14 +1455,22 @@ else if (op == 3) { } }, 4, "find"); + + asyncRunFut = new GridCompoundFuture<>(); + + asyncRunFut.add((IgniteInternalFuture)fut); + asyncRunFut.add((IgniteInternalFuture)fut2); + asyncRunFut.add((IgniteInternalFuture)fut3); + + asyncRunFut.markInitialized(); + try { fut.get(getTestTimeout(), TimeUnit.MILLISECONDS); } finally { stop.set(true); - fut2.get(); - fut3.get(); + asyncRunFut.get(); } GridCursor cursor = tree.find(null, null); From b2b596b4f59bcf7a1b7397a6fd681a0ae47092db Mon Sep 17 00:00:00 2001 From: Andrey Novikov Date: Fri, 25 Aug 2017 10:48:15 +0700 Subject: [PATCH 110/145] IGNITE-5200 Web Console: Don't cache generated chunks in production. (cherry picked from commit e1eb1b9) --- modules/web-console/frontend/webpack/webpack.common.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/web-console/frontend/webpack/webpack.common.js b/modules/web-console/frontend/webpack/webpack.common.js index 5a3763e9929f8..48e1e9b93593b 100644 --- a/modules/web-console/frontend/webpack/webpack.common.js +++ b/modules/web-console/frontend/webpack/webpack.common.js @@ -37,7 +37,6 @@ const app = path.resolve('app'); const IgniteModules = process.env.IGNITE_MODULES ? path.join(process.env.IGNITE_MODULES, 'frontend') : path.resolve('ignite_modules'); export default { - cache: true, node: { fs: 'empty' }, From 9399610d2dd4b67b1da6475ce2141787fb8dbb0e Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Fri, 25 Aug 2017 13:12:32 +0300 Subject: [PATCH 111/145] ignite-6180: restoring marshaller mappings on node start is implemented --- .../internal/MarshallerContextImpl.java | 3 + .../internal/MarshallerMappingFileStore.java | 76 +++++++++++- ...rshallerMappingRestoreOnNodeStartTest.java | 116 ++++++++++++++++++ .../IgnitePdsWithIndexingCoreTestSuite.java | 2 + 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsMarshallerMappingRestoreOnNodeStartTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/MarshallerContextImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/MarshallerContextImpl.java index 6f1550792fd0d..33b6ff81ab7b2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/MarshallerContextImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/MarshallerContextImpl.java @@ -519,6 +519,9 @@ public void onMarshallerProcessorStarted( this.transport = transport; closProc = ctx.closure(); clientNode = ctx.clientNode(); + + if (ctx.config().isPersistentStoreEnabled()) + fileStore.restoreMappings(this); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/MarshallerMappingFileStore.java b/modules/core/src/main/java/org/apache/ignite/internal/MarshallerMappingFileStore.java index 03f79c99b91f1..e4a844e1fea3c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/MarshallerMappingFileStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/MarshallerMappingFileStore.java @@ -34,6 +34,7 @@ import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.util.GridStripedLock; import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.marshaller.MarshallerContext; /** * File-based persistence provider for {@link MarshallerContextImpl}. @@ -53,6 +54,9 @@ final class MarshallerMappingFileStore { /** */ private final File workDir; + /** */ + private final String FILE_EXTENSION = ".classname"; + /** * @param log Logger. */ @@ -136,12 +140,82 @@ String readMapping(byte platformId, int typeId) throws IgniteCheckedException { } } + /** + * Restores all mappings available in file system to marshaller context. + * This method should be used only on node startup. + * + * @param marshCtx Marshaller context to register mappings. + */ + void restoreMappings(MarshallerContext marshCtx) throws IgniteCheckedException { + for (File file : workDir.listFiles()) { + String name = file.getName(); + + byte platformId = getPlatformId(name); + + int typeId = getTypeId(name); + + try (FileInputStream in = new FileInputStream(file)) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + String className = reader.readLine(); + + marshCtx.registerClassNameLocally(platformId, typeId, className); + } + } + catch (IOException e) { + throw new IgniteCheckedException("Reading marshaller mapping from file " + + name + + " failed." + , e); + } + } + } + + /** + * @param fileName Name of file with marshaller mapping information. + * @throws IgniteCheckedException If file name format is broken. + */ + private byte getPlatformId(String fileName) throws IgniteCheckedException { + String lastSymbol = fileName.substring(fileName.length() - 1); + + byte platformId; + + try { + platformId = Byte.parseByte(lastSymbol); + } + catch (NumberFormatException e) { + throw new IgniteCheckedException("Reading marshaller mapping from file " + + fileName + + " failed; last symbol of file name is expected to be numeric.", e); + } + + return platformId; + } + + /** + * @param fileName Name of file with marshaller mapping information. + * @throws IgniteCheckedException If file name format is broken. + */ + private int getTypeId(String fileName) throws IgniteCheckedException { + int typeId; + + try { + typeId = Integer.parseInt(fileName.substring(0, fileName.indexOf(FILE_EXTENSION))); + } + catch (NumberFormatException e) { + throw new IgniteCheckedException("Reading marshaller mapping from file " + + fileName + + " failed; type ID is expected to be numeric.", e); + } + + return typeId; + } + /** * @param platformId Platform id. * @param typeId Type id. */ private String getFileName(byte platformId, int typeId) { - return typeId + ".classname" + platformId; + return typeId + FILE_EXTENSION + platformId; } /** diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsMarshallerMappingRestoreOnNodeStartTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsMarshallerMappingRestoreOnNodeStartTest.java new file mode 100644 index 0000000000000..517b9ea869acc --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsMarshallerMappingRestoreOnNodeStartTest.java @@ -0,0 +1,116 @@ +/* + * 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 java.nio.file.Paths; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.CacheWriteSynchronizationMode; +import org.apache.ignite.cache.affinity.AffinityKeyMapped; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * + */ +public class IgnitePdsMarshallerMappingRestoreOnNodeStartTest extends GridCommonAbstractTest { + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + int gridIndex = getTestIgniteInstanceIndex(gridName); + + IgniteConfiguration cfg = super.getConfiguration(gridName); + + String tmpDir = System.getProperty("java.io.tmpdir"); + + cfg.setWorkDirectory(Paths.get(tmpDir, "srv" + gridIndex).toString()); + + cfg.setPersistentStoreConfiguration( + new PersistentStoreConfiguration() + ); + + cfg.setCacheConfiguration(new CacheConfiguration() + .setName(DEFAULT_CACHE_NAME) + .setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC) + .setCacheMode(CacheMode.REPLICATED)); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + + String tmpDir = System.getProperty("java.io.tmpdir"); + + deleteRecursively(Paths.get(tmpDir, "srv0").toFile()); + deleteRecursively(Paths.get(tmpDir, "srv1").toFile()); + } + + /** + * Test verifies that binary metadata from regular java classes is saved and restored correctly + * on cluster restart. + */ + public void testStaticMetadataIsRestoredOnRestart() throws Exception { + startGrids(1); + + Ignite ignite0 = grid(0); + + ignite0.active(true); + + IgniteCache cache0 = ignite0.cache(DEFAULT_CACHE_NAME); + + cache0.put(0, new TestValue1(0)); + + stopAllGrids(); + + startGrids(1); + + ignite0 = grid(0); + + ignite0.active(true); + + Ignite ignite1 = startGrid(1); + + awaitPartitionMapExchange(); + + ignite1.cache(DEFAULT_CACHE_NAME).get(0); + } + + /** + * + */ + private static class TestValue1 { + /** */ + @AffinityKeyMapped + private final int val; + + /** + * @param val Value. + */ + TestValue1(int val) { + this.val = val; + } + + /** */ + int getValue() { + return val; + } + } +} diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java index bb9c9d12b6ceb..ae8ea1813a5b6 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java @@ -19,6 +19,7 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsAtomicCacheRebalancingTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsBinaryMetadataOnClusterRestartTest; +import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsMarshallerMappingRestoreOnNodeStartTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsTxCacheRebalancingTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePersistentStoreCacheGroupsTest; import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsMultiNodePutGetRestartTest; @@ -51,6 +52,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgnitePdsTxCacheRebalancingTest.class); suite.addTestSuite(IgnitePdsBinaryMetadataOnClusterRestartTest.class); + suite.addTestSuite(IgnitePdsMarshallerMappingRestoreOnNodeStartTest.class); return suite; } From 5bda4090f1580ea7b6557c8716e57a12572c322f Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Thu, 24 Aug 2017 18:18:31 +0300 Subject: [PATCH 112/145] IGNITE-6178 Make CheckpointWriteOrder.SEQUENTIAL and checkpointingThreads=4 default in persistent store confguration --- .../ignite/configuration/PersistentStoreConfiguration.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java index 5b902ac1e3adc..888bf42c7e3b6 100644 --- a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java +++ b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java @@ -16,12 +16,11 @@ */ package org.apache.ignite.configuration; +import java.io.Serializable; 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.util.typedef.internal.S; -import java.io.Serializable; - /** * Configures Apache Ignite Persistent store. */ @@ -45,10 +44,10 @@ public class PersistentStoreConfiguration implements Serializable { public static final int DFLT_RATE_TIME_INTERVAL_MILLIS = 60_000; /** Default number of checkpointing threads. */ - public static final int DFLT_CHECKPOINTING_THREADS = 1; + public static final int DFLT_CHECKPOINTING_THREADS = 4; /** Default checkpoint write order. */ - public static final CheckpointWriteOrder DFLT_CHECKPOINT_WRITE_ORDER = CheckpointWriteOrder.RANDOM; + public static final CheckpointWriteOrder DFLT_CHECKPOINT_WRITE_ORDER = CheckpointWriteOrder.SEQUENTIAL; /** Default number of checkpoints to be kept in WAL after checkpoint is finished */ public static final int DFLT_WAL_HISTORY_SIZE = 20; From 316312d2ae9015228e67f959e492b2c5c4a9366d Mon Sep 17 00:00:00 2001 From: Dmitry Pavlov Date: Thu, 27 Jul 2017 14:51:25 +0300 Subject: [PATCH 113/145] ignite-5682 Added stale version check for GridDhtPartFullMessage not related to exchange. (cherry picked from commit eb9d06d) --- .../cache/CacheAffinitySharedManager.java | 5 +- .../processors/cache/GridCacheIoManager.java | 2 +- .../GridCachePartitionExchangeManager.java | 42 +++++++++----- .../dht/GridClientPartitionTopology.java | 12 +++- .../dht/GridDhtPartitionTopology.java | 5 +- .../dht/GridDhtPartitionTopologyImpl.java | 12 +++- .../preloader/GridDhtPartitionExchangeId.java | 2 +- .../GridDhtPartitionsExchangeFuture.java | 36 ++++++++---- .../GridDhtPartitionsFullMessage.java | 4 +- ...ngDelayedPartitionMapExchangeSelfTest.java | 58 +++++++++++++++---- .../junits/common/GridCommonAbstractTest.java | 6 +- 11 files changed, 136 insertions(+), 48 deletions(-) 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 0e03a29624a0a..ffb55e4af4cc7 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 @@ -460,7 +460,8 @@ void onCacheGroupCreated(CacheGroupContext grp) { grp.topology().update(grpHolder.affinity().lastVersion(), clientTop.partitionMap(true), clientTop.fullUpdateCounters(), - Collections.emptySet()); + Collections.emptySet(), + null); } assert grpHolder.affinity().lastVersion().equals(grp.affinity().lastVersion()); @@ -518,7 +519,7 @@ else if (!fetchFuts.containsKey(grp.groupId())) { grp.topology().updateTopologyVersion(topFut, discoCache, -1, false); - grp.topology().update(topVer, partMap, null, Collections.emptySet()); + grp.topology().update(topVer, partMap, null, Collections.emptySet(), null); topFut.validate(grp, discoCache.allNodes()); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java index 45edc531faa6b..65297958b30c7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java @@ -551,7 +551,7 @@ public void writeUnlock() { } /** - * @param nodeId Node ID. + * @param nodeId Sender Node ID. * @param cacheMsg Cache message. * @param c Handler closure. * @param plc Message policy. 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 aad5b3580ca62..bd34a5feb9ee3 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 @@ -140,7 +140,7 @@ public class GridCachePartitionExchangeManager extends GridCacheSharedMana /** */ private static final IgniteProductVersion EXCHANGE_PROTOCOL_2_SINCE = IgniteProductVersion.fromString("2.1.4"); - /** Atomic reference for pending timeout object. */ + /** Atomic reference for pending partition resend timeout object. */ private AtomicReference pendingResend = new AtomicReference<>(); /** Partition resend timeout after eviction. */ @@ -161,7 +161,7 @@ public class GridCachePartitionExchangeManager extends GridCacheSharedMana private final ConcurrentMap clientTops = new ConcurrentHashMap8<>(); /** */ - private volatile GridDhtPartitionsExchangeFuture lastInitializedFut; + @Nullable private volatile GridDhtPartitionsExchangeFuture lastInitializedFut; /** */ private final AtomicReference lastFinishedFut = new AtomicReference<>(); @@ -921,6 +921,8 @@ public void scheduleResendPartitions() { /** * Partition refresh callback. + * For coordinator causes {@link GridDhtPartitionsFullMessage FullMessages} send, + * for non coordinator - {@link GridDhtPartitionsSingleMessage SingleMessages} send */ private void refreshPartitions() { ClusterNode oldest = cctx.discovery().oldestAliveCacheServerNode(AffinityTopologyVersion.NONE); @@ -958,7 +960,7 @@ private void refreshPartitions() { if (log.isDebugEnabled()) log.debug("Refreshing partitions from oldest node: " + cctx.localNodeId()); - sendAllPartitions(rmts); + sendAllPartitions(rmts, rmtTopVer); } else { if (log.isDebugEnabled()) @@ -971,10 +973,14 @@ private void refreshPartitions() { /** * @param nodes Nodes. + * @param msgTopVer Topology version. Will be added to full message. */ - private void sendAllPartitions(Collection nodes) { + private void sendAllPartitions(Collection nodes, + AffinityTopologyVersion msgTopVer) { GridDhtPartitionsFullMessage m = createPartitionsFullMessage(true, false, null, null, null, null); + m.topologyVersion(msgTopVer); + if (log.isDebugEnabled()) log.debug("Sending all partitions [nodeIds=" + U.nodeIds(nodes) + ", msg=" + m + ']'); @@ -1126,8 +1132,8 @@ private void addFullPartitionsMap(GridDhtPartitionsFullMessage m, } /** - * @param node Node. - * @param id ID. + * @param node Destination cluster node. + * @param id Exchange ID. */ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExchangeId id) { GridDhtPartitionsSingleMessage m = createPartitionsSingleMessage(id, @@ -1153,7 +1159,7 @@ private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExc } /** - * @param exchangeId ID. + * @param exchangeId Exchange ID. * @param clientOnlyExchange Client exchange flag. * @param sndCounters {@code True} if need send partition update counters. * @param newCntrMap {@code True} if possible to use {@link CachePartitionPartialCountersMap}. @@ -1372,7 +1378,7 @@ private boolean addFuture(GridDhtPartitionsExchangeFuture fut) { } /** - * @param node Node. + * @param node Sender cluster node. * @param msg Message. */ private void processFullPartitionUpdate(ClusterNode node, GridDhtPartitionsFullMessage msg) { @@ -1398,8 +1404,13 @@ private void processFullPartitionUpdate(ClusterNode node, GridDhtPartitionsFullM else if (!grp.isLocal()) top = grp.topology(); - if (top != null) - updated |= top.update(null, entry.getValue(), null, msg.partsToReload(cctx.localNodeId(), grpId)); + if (top != null) { + updated |= top.update(null, + entry.getValue(), + null, + msg.partsToReload(cctx.localNodeId(), grpId), + msg.topologyVersion()); + } } if (!cctx.kernalContext().clientNode() && updated) @@ -1427,7 +1438,7 @@ else if (!grp.isLocal()) } /** - * @param node Node ID. + * @param node Sender cluster node. * @param msg Message. */ private void processSinglePartitionUpdate(final ClusterNode node, final GridDhtPartitionsSingleMessage msg) { @@ -1477,7 +1488,7 @@ else if (!grp.isLocal()) } /** - * @param node Node ID. + * @param node Sender cluster node. * @param msg Message. */ private void processSinglePartitionRequest(ClusterNode node, GridDhtPartitionsSingleRequest msg) { @@ -2595,7 +2606,10 @@ private abstract class MessageHandler implements IgniteBiInClosure { /** */ private static final long serialVersionUID = 0L; - /** {@inheritDoc} */ + /** + * @param nodeId Sender node ID. + * @param msg Message. + */ @Override public void apply(UUID nodeId, M msg) { ClusterNode node = cctx.node(nodeId); @@ -2613,7 +2627,7 @@ private abstract class MessageHandler implements IgniteBiInClosure { } /** - * @param node Node. + * @param node Sender cluster node. * @param msg Message. */ protected abstract void onMessage(ClusterNode node, M msg); 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 10d08b9f8aaa5..c8856fdecb693 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 @@ -656,8 +656,8 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD @Nullable AffinityTopologyVersion exchangeVer, GridDhtPartitionFullMap partMap, @Nullable CachePartitionFullCountersMap cntrMap, - Set partsToReload - ) { + Set partsToReload, + @Nullable AffinityTopologyVersion msgTopVer) { if (log.isDebugEnabled()) log.debug("Updating full partition map [exchVer=" + exchangeVer + ", parts=" + fullMapString() + ']'); @@ -672,6 +672,14 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD return false; } + if (msgTopVer != null && lastExchangeVer != null && lastExchangeVer.compareTo(msgTopVer) > 0) { + if (log.isDebugEnabled()) + log.debug("Stale topology version for full partition map update message (will ignore) " + + "[lastExchId=" + lastExchangeVer + ", topVersion=" + msgTopVer + ']'); + + return false; + } + boolean fullMapUpdated = (node2part == null); if (node2part != null) { 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 f48bd352f1b67..4ae68ef739f8c 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 @@ -263,13 +263,16 @@ public void initPartitionsWhenAffinityReady(AffinityTopologyVersion affVer, Grid * @param partMap Update partition map. * @param cntrMap Partition update counters. * @param partsToReload Set of partitions that need to be reloaded. + * @param msgTopVer Topology version from incoming message. This value is not null only for case message is not + * related to exchange. Value should be not less than previous 'Topology version from exchange'. * @return {@code True} if local state was changed. */ public boolean update( @Nullable AffinityTopologyVersion exchangeResVer, GridDhtPartitionFullMap partMap, @Nullable CachePartitionFullCountersMap cntrMap, - Set partsToReload); + Set partsToReload, + @Nullable AffinityTopologyVersion msgTopVer); /** * @param exchId Exchange ID. 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 6ab6bd852a3cf..f25ae21d6dae8 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 @@ -1176,7 +1176,8 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD @Nullable AffinityTopologyVersion exchangeVer, GridDhtPartitionFullMap partMap, @Nullable CachePartitionFullCountersMap incomeCntrMap, - Set partsToReload) { + Set partsToReload, + @Nullable AffinityTopologyVersion msgTopVer) { if (log.isDebugEnabled()) log.debug("Updating full partition map [exchVer=" + exchangeVer + ", parts=" + fullMapString() + ']'); @@ -1217,6 +1218,15 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } } + if (msgTopVer != null && lastTopChangeVer.compareTo(msgTopVer) > 0) { + U.warn(log, "Stale version for full partition map update message (will ignore) [" + + "lastTopChange=" + lastTopChangeVer + + ", readTopVer=" + readyTopVer + + ", msgVer=" + msgTopVer + ']'); + + return false; + } + boolean fullMapUpdated = (node2part == null); if (node2part != null) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java index 07daeda947f39..33728d391c16d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionExchangeId.java @@ -51,7 +51,7 @@ public class GridDhtPartitionExchangeId implements Message, Comparable remaining = new HashSet<>(); - /** */ + /** Guarded by this */ @GridToStringExclude private int pendingSingleUpdates; @@ -164,7 +164,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte @GridToStringExclude private final CountDownLatch evtLatch = new CountDownLatch(1); - /** */ + /** Exchange future init method completes this future. */ private GridFutureAdapter initFut; /** */ @@ -213,7 +213,10 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte /** Init timestamp. Used to track the amount of time spent to complete the future. */ private long initTs; - /** */ + /** + * Centralized affinity assignment required. Activated for node left of failed. For this mode crd will send full + * partitions maps to nodes using discovery (ring) instead of communication. + */ private boolean centralizedAff; /** Change global state exception. */ @@ -765,7 +768,8 @@ private void updateTopologies(boolean crd) throws IgniteCheckedException { top.update(null, clientTop.partitionMap(true), clientTop.fullUpdateCounters(), - Collections.emptySet()); + Collections.emptySet(), + null); } } @@ -1204,7 +1208,7 @@ public boolean localJoinExchange() { } /** - * @param node Node. + * @param node Target Node. * @throws IgniteCheckedException If failed. */ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException { @@ -1351,7 +1355,7 @@ private void sendAllPartitions( } /** - * @param oldestNode Oldest node. + * @param oldestNode Oldest node. Target node to send message to. */ private void sendPartitions(ClusterNode oldestNode) { try { @@ -1720,6 +1724,9 @@ private void processMergedMessage(final ClusterNode node, final GridDhtPartition } /** + * Processing of received single message. Actual processing in future may be delayed if init method was not + * completed, see {@link #initDone()} + * * @param node Sender node. * @param msg Single partition info. */ @@ -1823,8 +1830,11 @@ public void waitAndReplyToNode(final UUID nodeId, final GridDhtPartitionsSingleM } /** + * Note this method performs heavy updatePartitionSingleMap operation, this operation is moved out from the + * synchronized block. Only count of such updates {@link #pendingSingleUpdates} is managed under critical section. + * * @param nodeId Sender node. - * @param msg Message. + * @param msg Partition single message. */ private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage msg) { if (msg.client()) { @@ -1833,7 +1843,7 @@ private void processSingleMessage(UUID nodeId, GridDhtPartitionsSingleMessage ms return; } - boolean allReceived = false; + boolean allReceived = false; // Received all expected messages. boolean updateSingleMap = false; FinishState finishState0 = null; @@ -2509,7 +2519,7 @@ public void onReceiveFullMessage(final ClusterNode node, final GridDhtPartitions /** * @param node Sender node. - * @param msg Message. + * @param msg Message with full partition info. */ public void onReceivePartitionRequest(final ClusterNode node, final GridDhtPartitionsSingleRequest msg) { assert !cctx.kernalContext().clientNode() || msg.restoreState(); @@ -2803,7 +2813,8 @@ private void updatePartitionFullMap(AffinityTopologyVersion resTopVer, GridDhtPa grp.topology().update(resTopVer, entry.getValue(), cntrMap, - msg.partsToReload(cctx.localNodeId(), grpId)); + msg.partsToReload(cctx.localNodeId(), grpId), + null); } else { ClusterNode oldest = cctx.discovery().oldestAliveCacheServerNode(AffinityTopologyVersion.NONE); @@ -2817,7 +2828,8 @@ private void updatePartitionFullMap(AffinityTopologyVersion resTopVer, GridDhtPa top.update(resTopVer, entry.getValue(), cntrMap, - Collections.emptySet()); + Collections.emptySet(), + null); } } } @@ -2912,7 +2924,7 @@ private void onDiscoveryEvent(IgniteRunnable c) { } /** - * + * Moves exchange future to state 'init done' using {@link #initFut}. */ private void initDone() { while (!isDone()) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java index 19ec0a8319ff1..edbfc23abec44 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java @@ -129,7 +129,9 @@ public GridDhtPartitionsFullMessage() { /** * @param id Exchange ID. * @param lastVer Last version. - * @param topVer Topology version. + * @param topVer Topology version. For messages not related to exchange may be {@link AffinityTopologyVersion#NONE}. + * @param partHistSuppliers + * @param partsToReload */ public GridDhtPartitionsFullMessage(@Nullable GridDhtPartitionExchangeId id, @Nullable GridCacheVersion lastVer, diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRabalancingDelayedPartitionMapExchangeSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRabalancingDelayedPartitionMapExchangeSelfTest.java index dc141db2e6032..f307b6a60ae3f 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRabalancingDelayedPartitionMapExchangeSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRabalancingDelayedPartitionMapExchangeSelfTest.java @@ -18,14 +18,17 @@ package org.apache.ignite.internal.processors.cache.distributed.rebalancing; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.ignite.IgniteException; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; -import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsAbstractMessage; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsFullMessage; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteInClosure; @@ -45,12 +48,19 @@ public class GridCacheRabalancingDelayedPartitionMapExchangeSelfTest extends Gri /** */ protected static TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); - /** */ + /** Map of destination node ID to runnable with logic for real message sending. + * To apply real message sending use run method */ private final ConcurrentHashMap8 rs = new ConcurrentHashMap8<>(); - /** */ + /** + * Flag to redirect {@link GridDhtPartitionsFullMessage}s from real communication channel to {@link #rs} map. + * Applied only to messages not related to particular exchange + */ private volatile boolean record = false; + /** */ + private AtomicBoolean replay = new AtomicBoolean(); + /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { IgniteConfiguration iCfg = super.getConfiguration(igniteInstanceName); @@ -74,13 +84,26 @@ public class DelayableCommunicationSpi extends TcpCommunicationSpi { final IgniteInClosure ackC) throws IgniteSpiException { final Object msg0 = ((GridIoMessage)msg).message(); + if (log.isDebugEnabled()) + log.debug("Message [thread=" + Thread.currentThread().getName() + ", msg=" + msg0 + ']'); + if (msg0 instanceof GridDhtPartitionsFullMessage && record && - ((GridDhtPartitionsFullMessage)msg0).exchangeId() == null) { - rs.putIfAbsent(node.id(), new Runnable() { + ((GridDhtPartitionsAbstractMessage)msg0).exchangeId() == null) { + if (log.isDebugEnabled()) + log.debug("Record message [toNode=" + node.id() + ", msg=" + msg + "]"); + + assert !replay.get() : "Record of message is not allowed after replay"; + + Runnable prevValue = rs.putIfAbsent(node.id(), new Runnable() { @Override public void run() { + if (log.isDebugEnabled()) + log.debug("Replay: " + msg); + DelayableCommunicationSpi.super.sendMessage(node, msg, ackC); } }); + + assert prevValue == null : "Duplicate message registered to [" + node.id() + "]"; } else try { @@ -94,10 +117,10 @@ public class DelayableCommunicationSpi extends TcpCommunicationSpi { } /** - * @throws Exception e. + * @throws Exception e if failed. */ public void test() throws Exception { - IgniteKernal ignite = (IgniteKernal)startGrid(0); + IgniteEx ignite = startGrid(0); CacheConfiguration cfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME); @@ -144,10 +167,7 @@ record = true; awaitPartitionMapExchange(); - for (Runnable r : rs.values()) - r.run(); - - U.sleep(10000); // Enough time to process delayed GridDhtPartitionsFullMessages. + replayMessages(); stopGrid(3); // Forces exchange at all nodes and cause assertion failure in case obsolete partition map accepted. @@ -167,6 +187,22 @@ record = true; assert grid(2).context().cache().context().exchange().readyAffinityVersion().topologyVersion() > topVer2; } + /** + * Replays all saved messages from map, actual sent is performed. + * + * @throws IgniteInterruptedCheckedException If interrupted. + */ + private void replayMessages() throws IgniteInterruptedCheckedException { + record = false; + + for (Runnable r : rs.values()) + r.run(); // Causes real messages sending. + + assertTrue(replay.compareAndSet(false, true)); + + U.sleep(10000); // Enough time to process delayed GridDhtPartitionsFullMessages. + } + /** {@inheritDoc} */ @Override protected void afterTest() throws Exception { super.afterTest(); diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java index 307456c355796..f5e8eefcc4d75 100755 --- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java @@ -524,7 +524,8 @@ protected void awaitPartitionMapExchange() throws InterruptedException { /** * @param waitEvicts If {@code true} will wait for evictions finished. * @param waitNode2PartUpdate If {@code true} will wait for nodes node2part info update finished. - * @param nodes Optional nodes. + * @param nodes Optional nodes. If {@code null} method will wait for all nodes, for non null collection nodes will + * be filtered * @throws InterruptedException If interrupted. */ @SuppressWarnings("BusyWait") @@ -546,7 +547,8 @@ protected long getPartitionMapExchangeTimeout() { /** * @param waitEvicts If {@code true} will wait for evictions finished. * @param waitNode2PartUpdate If {@code true} will wait for nodes node2part info update finished. - * @param nodes Optional nodes. + * @param nodes Optional nodes. If {@code null} method will wait for all nodes, for non null collection nodes will + * be filtered * @param printPartState If {@code true} will print partition state if evictions not happened. * @throws InterruptedException If interrupted. */ From 6f7011aa9c69280c76e03335ce4851a38cfc334e Mon Sep 17 00:00:00 2001 From: sboikov Date: Fri, 25 Aug 2017 16:59:02 +0300 Subject: [PATCH 114/145] Added test for rebalance after restart. --- ...IgnitePdsCacheRebalancingAbstractTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java index 2f439519b45b0..91838fcbe1b99 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java @@ -497,6 +497,43 @@ else if (nodesCnt.get() > maxNodesCount) assertEquals(Integer.toString(entry.getKey()), entry.getValue(), cache.get(entry.getKey())); } + /** + * @throws Exception If failed. + */ + public void testForceRebalance() throws Exception { + final Ignite ig = startGrids(4); + + ig.active(true); + + awaitPartitionMapExchange(); + + IgniteCache c = ig.cache(cacheName); + + Integer val = 0; + + for (int i = 0; i < 5; i++) { + Integer key = primaryKey(ignite(3).cache(cacheName)); + + c.put(key, val); + + stopGrid(3); + + val++; + + c.put(key, val); + + assertEquals(val, c.get(key)); + + startGrid(3); + + awaitPartitionMapExchange(); + + Object val0 = ignite(3).cache(cacheName).get(key); + + assertEquals(val, val0); + } + } + /** * @throws Exception If failed */ From d31c43c1465ec33f9a1be81dedb958296ecc5068 Mon Sep 17 00:00:00 2001 From: sboikov Date: Fri, 25 Aug 2017 17:50:01 +0300 Subject: [PATCH 115/145] Increment GridDhtPartitionMap update sequence when assign new state on coordinator. --- .../dht/GridDhtPartitionTopologyImpl.java | 18 ++++++++++++------ .../IgnitePdsCacheRebalancingAbstractTest.java | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) 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 f25ae21d6dae8..87b3670dd48d7 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 @@ -1186,7 +1186,9 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD lock.writeLock().lock(); try { - if (stopping || !lastTopChangeVer.initialized()) + if (stopping || !lastTopChangeVer.initialized() || + // Ignore message not-related to exchange if exchange is in progress. + (exchangeVer == null && !lastTopChangeVer.equals(readyTopVer))) return false; if (incomeCntrMap != null) { @@ -1909,20 +1911,24 @@ else if (plc != PartitionLossPolicy.IGNORE) { } for (Map.Entry e : node2part.entrySet()) { - if (!e.getValue().containsKey(p)) + GridDhtPartitionMap partMap = e.getValue(); + + if (!partMap.containsKey(p)) continue; - if (e.getValue().get(p) == OWNING && !owners.contains(e.getKey())) { + if (partMap.get(p) == OWNING && !owners.contains(e.getKey())) { if (haveHistory) - e.getValue().put(p, MOVING); + partMap.put(p, MOVING); else { - e.getValue().put(p, RENTING); + partMap.put(p, RENTING); result.add(e.getKey()); } + partMap.updateSequence(partMap.updateSequence() + 1, partMap.topologyVersion()); + U.warn(log, "Partition has been scheduled for rebalancing due to outdated update counter " + - "[nodeId=" + ctx.localNodeId() + ", cacheOrGroupName=" + grp.cacheOrGroupName() + + "[nodeId=" + e.getKey() + ", cacheOrGroupName=" + grp.cacheOrGroupName() + ", partId=" + p + ", haveHistory=" + haveHistory + "]"); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java index 91838fcbe1b99..7b047f88d4066 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java @@ -512,6 +512,8 @@ public void testForceRebalance() throws Exception { Integer val = 0; for (int i = 0; i < 5; i++) { + info("Iteration: " + i); + Integer key = primaryKey(ignite(3).cache(cacheName)); c.put(key, val); From 85fd8ce91f1e5827600aa32645552039e5a2298a Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Fri, 25 Aug 2017 21:23:19 +0300 Subject: [PATCH 116/145] Fixed update sequence. --- .../dht/GridDhtPartitionTopologyImpl.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 87b3670dd48d7..a881130bf5389 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 @@ -1910,6 +1910,14 @@ else if (plc != PartitionLossPolicy.IGNORE) { } } + long seqVal = 0; + + if (updateSeq) { + seqVal = this.updateSeq.incrementAndGet(); + + node2part = new GridDhtPartitionFullMap(node2part, seqVal); + } + for (Map.Entry e : node2part.entrySet()) { GridDhtPartitionMap partMap = e.getValue(); @@ -1925,16 +1933,14 @@ else if (plc != PartitionLossPolicy.IGNORE) { result.add(e.getKey()); } - partMap.updateSequence(partMap.updateSequence() + 1, partMap.topologyVersion()); + if (updateSeq) + partMap.updateSequence(seqVal, partMap.topologyVersion()); U.warn(log, "Partition has been scheduled for rebalancing due to outdated update counter " + "[nodeId=" + e.getKey() + ", cacheOrGroupName=" + grp.cacheOrGroupName() + ", partId=" + p + ", haveHistory=" + haveHistory + "]"); } } - - if (updateSeq) - node2part = new GridDhtPartitionFullMap(node2part, this.updateSeq.incrementAndGet()); } finally { lock.writeLock().unlock(); From 8c249b77533c95a4bef3d19ca583feb992322325 Mon Sep 17 00:00:00 2001 From: Eduard Shangareev Date: Sat, 26 Aug 2017 17:01:46 +0300 Subject: [PATCH 117/145] GG-12609: Fixed OOM at initiator during LIST --- .../tcp/internal/TcpDiscoveryNode.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNode.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNode.java index 688282193b9c2..b2797a40f227c 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNode.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNode.java @@ -649,4 +649,24 @@ public boolean isCacheClient() { @Override public String toString() { return S.toString(TcpDiscoveryNode.class, this, "isClient", isClient()); } + + /** + * IMPORTANT! + * Only purpose of this constructor is creating node which contains only necessary data to store on disc + * @param node to copy data from + */ + public TcpDiscoveryNode( + ClusterNode node + ) { + this.id = node.id(); + this.consistentId = node.consistentId(); + this.addrs = node.addresses(); + this.hostNames = node.hostNames(); + this.order = node.order(); + this.ver = node.version(); + this.daemon = node.isDaemon(); + this.clientRouterNodeId = node.isClient() ? node.id() : null; + + attrs = Collections.emptyMap(); + } } From a857c5ba5a24f41b5ebfaef0a15fde2906a7e0fd Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Sat, 26 Aug 2017 17:21:44 +0300 Subject: [PATCH 118/145] Fixed update sequence. --- .../dht/GridDhtPartitionTopologyImpl.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) 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 a881130bf5389..e0f54b33d3c3e 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 @@ -521,7 +521,7 @@ else if (!node2part.nodeId().equals(loc.id())) { DiscoveryEvent evt = evts0.get(i); if (ExchangeDiscoveryEvents.serverLeftEvent(evt)) - removeNode(evt.eventNode().id()); + updateSeq = removeNode(evt.eventNode().id(), updateSeq); } } @@ -1379,6 +1379,8 @@ else if (locPart.state() == OWNING || locPart.state() == MOVING) { long updateSeq = this.updateSeq.incrementAndGet(); + node2part.newUpdateSequence(updateSeq); + if (readyTopVer.initialized() && readyTopVer.equals(lastTopChangeVer)) { AffinityAssignment aff = grp.affinity().readyAffinity(readyTopVer); @@ -1535,8 +1537,11 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa GridDhtPartitionMap cur = node2part.get(parts.nodeId()); if (force) { - if (cur != null && cur.topologyVersion().initialized()) + if (cur != null && cur.topologyVersion().initialized()) { parts.updateSequence(cur.updateSequence(), cur.topologyVersion()); + + this.updateSeq.setIfGreater(cur.updateSequence()); + } } else if (isStaleUpdate(cur, parts)) { U.warn(log, "Stale update for single partition map update (will ignore) [exchId=" + exchId + @@ -1546,10 +1551,6 @@ else if (isStaleUpdate(cur, parts)) { return false; } - long updateSeq = this.updateSeq.incrementAndGet(); - - node2part.newUpdateSequence(updateSeq); - boolean changed = false; if (cur == null || !cur.equals(parts)) @@ -1557,6 +1558,10 @@ else if (isStaleUpdate(cur, parts)) { node2part.put(parts.nodeId(), parts); + this.updateSeq.setIfGreater(parts.updateSequence()); + + long updateSeq = this.updateSeq.incrementAndGet(); + // During exchange diff is calculated after all messages are received and affinity initialized. if (exchId == null && !grp.isReplicated()) { if (readyTopVer.initialized() && readyTopVer.compareTo(diffFromAffinityVer) >= 0) { @@ -2110,8 +2115,11 @@ private long updateLocal(int p, GridDhtPartitionState state, long updateSeq, Aff /** * @param nodeId Node to remove. + * @param updateSeq Update sequence. + * + * @return Update sequence. */ - private void removeNode(UUID nodeId) { + private long removeNode(UUID nodeId, long updateSeq) { assert nodeId != null; assert lock.isWriteLockedByCurrentThread(); @@ -2122,11 +2130,14 @@ private void removeNode(UUID nodeId) { ClusterNode loc = ctx.localNode(); if (node2part != null) { - updateSeq.setIfGreater(node2part.updateSequence()); + assert updateSeq >= node2part.updateSequence(); - if (loc.equals(oldest) && !node2part.nodeId().equals(loc.id())) - node2part = new GridDhtPartitionFullMap(loc.id(), loc.order(), updateSeq.incrementAndGet(), + if (loc.equals(oldest) && !node2part.nodeId().equals(loc.id())) { + updateSeq++; + + node2part = new GridDhtPartitionFullMap(loc.id(), loc.order(), updateSeq, node2part, false); + } else node2part = new GridDhtPartitionFullMap(node2part, node2part.updateSequence()); @@ -2145,6 +2156,8 @@ private void removeNode(UUID nodeId) { consistencyCheck(); } + + return updateSeq; } /** {@inheritDoc} */ From fc55ade9b5a586fb39701b4e8b7ce2105bff2fd0 Mon Sep 17 00:00:00 2001 From: Sergey Chugunov Date: Sat, 26 Aug 2017 21:44:13 +0300 Subject: [PATCH 119/145] IGNITE-6124: fix for lastVer field on GridDhtPartitionsSingleMessage message --- .../dht/preloader/GridDhtPartitionsExchangeFuture.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6decb44e26caf..299284d411108 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 @@ -1227,7 +1227,7 @@ private void sendLocalPartitions(ClusterNode node) throws IgniteCheckedException if (cctx.kernalContext().clientNode()) { msg = new GridDhtPartitionsSingleMessage(exchangeId(), true, - null, + cctx.versions().last(), true); } else { From a01837b5028fc8739e16658d85ffe64aad01afdb Mon Sep 17 00:00:00 2001 From: Eduard Shangareev Date: Sun, 27 Aug 2017 17:30:21 +0300 Subject: [PATCH 120/145] GG-12682 Restart cluster during snapshot RESTORE fails --- .../cache/persistence/file/FilePageStoreManager.java | 3 +++ 1 file changed, 3 insertions(+) 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 13bcd2a9d9df1..d60151af2ea5c 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 @@ -381,6 +381,9 @@ private CacheStoreHolder initForCache(CacheGroupDescriptor grpDesc, CacheConfigu return new CacheStoreHolder(idxStore, partStores); } + /** + * @param cacheWorkDir Cache work directory. + */ private boolean checkAndInitCacheWorkDir(File cacheWorkDir) throws IgniteCheckedException { boolean dirExisted = false; From 4907f7d87a08fb5ca5d4d74ef8b61db0fd207855 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Mon, 28 Aug 2017 14:58:50 +0300 Subject: [PATCH 121/145] Fixed update sequence. --- .../dht/GridDhtPartitionTopologyImpl.java | 59 ++++++++----------- 1 file changed, 25 insertions(+), 34 deletions(-) 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 e0f54b33d3c3e..f7f71a18c4c4f 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 @@ -521,7 +521,7 @@ else if (!node2part.nodeId().equals(loc.id())) { DiscoveryEvent evt = evts0.get(i); if (ExchangeDiscoveryEvents.serverLeftEvent(evt)) - updateSeq = removeNode(evt.eventNode().id(), updateSeq); + removeNode(evt.eventNode().id()); } } @@ -1243,6 +1243,9 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD ", curPart=" + mapString(part) + ", newPart=" + mapString(newPart) + ']'); } + + if (newPart.nodeId().equals(ctx.localNodeId())) + updateSeq.setIfGreater(newPart.updateSequence()); } else { // If for some nodes current partition has a newer map, @@ -1272,6 +1275,12 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD } } } + else { + GridDhtPartitionMap locNodeMap = partMap.get(ctx.localNodeId()); + + if (locNodeMap != null) + updateSeq.setIfGreater(locNodeMap.updateSequence()); + } if (!fullMapUpdated) { if (log.isDebugEnabled()) { @@ -1379,8 +1388,6 @@ else if (locPart.state() == OWNING || locPart.state() == MOVING) { long updateSeq = this.updateSeq.incrementAndGet(); - node2part.newUpdateSequence(updateSeq); - if (readyTopVer.initialized() && readyTopVer.equals(lastTopChangeVer)) { AffinityAssignment aff = grp.affinity().readyAffinity(readyTopVer); @@ -1537,11 +1544,8 @@ private boolean isStaleUpdate(GridDhtPartitionMap currentMap, GridDhtPartitionMa GridDhtPartitionMap cur = node2part.get(parts.nodeId()); if (force) { - if (cur != null && cur.topologyVersion().initialized()) { + if (cur != null && cur.topologyVersion().initialized()) parts.updateSequence(cur.updateSequence(), cur.topologyVersion()); - - this.updateSeq.setIfGreater(cur.updateSequence()); - } } else if (isStaleUpdate(cur, parts)) { U.warn(log, "Stale update for single partition map update (will ignore) [exchId=" + exchId + @@ -1551,6 +1555,10 @@ else if (isStaleUpdate(cur, parts)) { return false; } + long updateSeq = this.updateSeq.incrementAndGet(); + + node2part.newUpdateSequence(updateSeq); + boolean changed = false; if (cur == null || !cur.equals(parts)) @@ -1558,10 +1566,6 @@ else if (isStaleUpdate(cur, parts)) { node2part.put(parts.nodeId(), parts); - this.updateSeq.setIfGreater(parts.updateSequence()); - - long updateSeq = this.updateSeq.incrementAndGet(); - // During exchange diff is calculated after all messages are received and affinity initialized. if (exchId == null && !grp.isReplicated()) { if (readyTopVer.initialized() && readyTopVer.compareTo(diffFromAffinityVer) >= 0) { @@ -1915,14 +1919,6 @@ else if (plc != PartitionLossPolicy.IGNORE) { } } - long seqVal = 0; - - if (updateSeq) { - seqVal = this.updateSeq.incrementAndGet(); - - node2part = new GridDhtPartitionFullMap(node2part, seqVal); - } - for (Map.Entry e : node2part.entrySet()) { GridDhtPartitionMap partMap = e.getValue(); @@ -1938,14 +1934,19 @@ else if (plc != PartitionLossPolicy.IGNORE) { result.add(e.getKey()); } - if (updateSeq) - partMap.updateSequence(seqVal, partMap.topologyVersion()); + partMap.updateSequence(partMap.updateSequence() + 1, partMap.topologyVersion()); + + if (partMap.nodeId().equals(ctx.localNodeId())) + this.updateSeq.setIfGreater(partMap.updateSequence()); U.warn(log, "Partition has been scheduled for rebalancing due to outdated update counter " + "[nodeId=" + e.getKey() + ", cacheOrGroupName=" + grp.cacheOrGroupName() + ", partId=" + p + ", haveHistory=" + haveHistory + "]"); } } + + if (updateSeq) + node2part = new GridDhtPartitionFullMap(node2part, this.updateSeq.incrementAndGet()); } finally { lock.writeLock().unlock(); @@ -2115,11 +2116,8 @@ private long updateLocal(int p, GridDhtPartitionState state, long updateSeq, Aff /** * @param nodeId Node to remove. - * @param updateSeq Update sequence. - * - * @return Update sequence. */ - private long removeNode(UUID nodeId, long updateSeq) { + private void removeNode(UUID nodeId) { assert nodeId != null; assert lock.isWriteLockedByCurrentThread(); @@ -2130,14 +2128,9 @@ private long removeNode(UUID nodeId, long updateSeq) { ClusterNode loc = ctx.localNode(); if (node2part != null) { - assert updateSeq >= node2part.updateSequence(); - - if (loc.equals(oldest) && !node2part.nodeId().equals(loc.id())) { - updateSeq++; - - node2part = new GridDhtPartitionFullMap(loc.id(), loc.order(), updateSeq, + if (loc.equals(oldest) && !node2part.nodeId().equals(loc.id())) + node2part = new GridDhtPartitionFullMap(loc.id(), loc.order(), updateSeq.get(), node2part, false); - } else node2part = new GridDhtPartitionFullMap(node2part, node2part.updateSequence()); @@ -2156,8 +2149,6 @@ private long removeNode(UUID nodeId, long updateSeq) { consistencyCheck(); } - - return updateSeq; } /** {@inheritDoc} */ From 5a6808f13e1f55ae76afb8f6b201396f3340a351 Mon Sep 17 00:00:00 2001 From: "Andrey V. Mashenkov" Date: Mon, 28 Aug 2017 12:31:15 +0300 Subject: [PATCH 122/145] Disable leading wildcard in query. (cherry picked from commit 75cc957) --- .../internal/processors/query/h2/opt/GridLuceneIndex.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java index 0776a540ee565..c9d0159fcce80 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneIndex.java @@ -272,7 +272,7 @@ public GridCloseableIterator> query(String qry, MultiFieldQueryParser parser = new MultiFieldQueryParser(idxdFields, writer.getAnalyzer()); - parser.setAllowLeadingWildcard(true); +// parser.setAllowLeadingWildcard(true); // Filter expired items. Query filter = NumericRangeQuery.newLongRange(EXPIRATION_TIME_FIELD_NAME, U.currentTimeMillis(), From ac7f21298f8fd49c2a42034fb955d3c78089ed70 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Mon, 28 Aug 2017 20:31:18 +0300 Subject: [PATCH 123/145] gg-12688 : Fixed updateSequence in ClientTopology. --- .../dht/GridClientPartitionTopology.java | 18 +++++--- .../GridDhtPartitionsExchangeFuture.java | 15 +++++-- ...IgnitePdsCacheRebalancingAbstractTest.java | 44 ++++++++++++++++++- 3 files changed, 66 insertions(+), 11 deletions(-) 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 c8856fdecb693..299394f71b488 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 @@ -1092,20 +1092,26 @@ private void removeNode(UUID nodeId) { try { for (Map.Entry e : node2part.entrySet()) { - if (!e.getValue().containsKey(p)) + GridDhtPartitionMap partMap = e.getValue(); + + if (!partMap.containsKey(p)) continue; - if (e.getValue().get(p) == OWNING && !owners.contains(e.getKey())) { + if (partMap.get(p) == OWNING && !owners.contains(e.getKey())) { if (haveHistory) - e.getValue().put(p, MOVING); + partMap.put(p, MOVING); else { - e.getValue().put(p, RENTING); + partMap.put(p, RENTING); result.add(e.getKey()); } + + partMap.updateSequence(partMap.updateSequence() + 1, partMap.topologyVersion()); + + U.warn(log, "Partition has been scheduled for rebalancing due to outdated update counter " + + "[nodeId=" + e.getKey() + ", groupId=" + grpId + + ", partId=" + p + ", haveHistory=" + haveHistory + "]"); } - else if (owners.contains(e.getKey())) - e.getValue().put(p, OWNING); } part2node.put(p, owners); 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 299284d411108..240b5f0af1802 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 @@ -2437,11 +2437,20 @@ private void finishExchangeOnCoordinator(@Nullable Collection sndRe */ private void assignPartitionsStates() { if (cctx.database().persistenceEnabled()) { - for (CacheGroupContext grp : cctx.cache().cacheGroups()) { - if (grp.isLocal()) + for (Map.Entry e : cctx.affinity().cacheGroups().entrySet()) { + if (e.getValue().config().getCacheMode() == CacheMode.LOCAL) continue; - assignPartitionStates(grp.topology()); + GridDhtPartitionTopology top; + + CacheGroupContext grpCtx = cctx.cache().cacheGroup(e.getKey()); + + if (grpCtx != null) + top = grpCtx.topology(); + else + top = cctx.exchange().clientTopology(e.getKey()); + + assignPartitionStates(top); } } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java index 7b047f88d4066..2d237cbf30a59 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsCacheRebalancingAbstractTest.java @@ -35,6 +35,7 @@ import org.apache.ignite.cache.PartitionLossPolicy; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.QueryIndex; +import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.configuration.MemoryConfiguration; @@ -46,6 +47,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition; import org.apache.ignite.internal.util.typedef.G; import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgnitePredicate; 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; @@ -96,7 +98,19 @@ public abstract class IgnitePdsCacheRebalancingAbstractTest extends GridCommonAb ccfg2.setQueryEntities(Collections.singleton(qryEntity)); - cfg.setCacheConfiguration(ccfg1, ccfg2); + // Do not start filtered cache on coordinator. + if (gridName.endsWith("0")) { + cfg.setCacheConfiguration(ccfg1, ccfg2); + } + else { + CacheConfiguration ccfg3 = cacheConfiguration("filtered"); + ccfg3.setPartitionLossPolicy(PartitionLossPolicy.READ_ONLY_SAFE); + ccfg3.setBackups(1); + ccfg3.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); + ccfg3.setNodeFilter(new CoordinatorNodeFilter()); + + cfg.setCacheConfiguration(ccfg1, ccfg2, ccfg3); + } MemoryConfiguration memCfg = new MemoryConfiguration(); @@ -501,7 +515,23 @@ else if (nodesCnt.get() > maxNodesCount) * @throws Exception If failed. */ public void testForceRebalance() throws Exception { - final Ignite ig = startGrids(4); + testForceRebalance(cacheName); + } + + /** + * @throws Exception If failed. + */ + public void testForceRebalanceClientTopology() throws Exception { + testForceRebalance("filtered"); + } + + /** + * @throws Exception If failed. + */ + private void testForceRebalance(String cacheName) throws Exception { + startGrids(4); + + final Ignite ig = grid(1); ig.active(true); @@ -653,4 +683,14 @@ private TestValue(int v1, int v2) { '}'; } } + + /** + * + */ + private static class CoordinatorNodeFilter implements IgnitePredicate { + /** {@inheritDoc} */ + @Override public boolean apply(ClusterNode node) { + return node.order() > 1; + } + } } From 5df5e2cc0e42a073de9107014876a4b646dd1b65 Mon Sep 17 00:00:00 2001 From: Andrey Gura Date: Tue, 29 Aug 2017 13:14:02 +0300 Subject: [PATCH 124/145] Minor. Typo fixed. --- .../internal/processors/query/h2/opt/GridLuceneDirectory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java index f92b510cdfd2b..96869c4d8244f 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridLuceneDirectory.java @@ -199,7 +199,7 @@ protected GridLuceneFile newRAMFile(String filename) { catch (IOException e) { if (errs == null) errs = new IgniteException("Failed to close index directory."+ - " Some index readers was closed properly, that may leads memory leak."); + " Some index readers weren't closed properly, that may leads memory leak."); errs.addSuppressed(e); } From 6c45edeaf83f94a9c0539d9768dd8be0e25dc36e Mon Sep 17 00:00:00 2001 From: Pavel Tupitsyn Date: Tue, 29 Aug 2017 18:35:49 +0300 Subject: [PATCH 125/145] IGNITE-6178 .NET: Fix PersistentStoreConfiguration.CheckpointingThreads default value This closes #2511 (cherry picked from commit 08a831f) --- .../PersistentStore/PersistentStoreConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/PersistentStore/PersistentStoreConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/PersistentStore/PersistentStoreConfiguration.cs index cac6cc83a191a..8ba45e5bd796e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/PersistentStore/PersistentStoreConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/PersistentStore/PersistentStoreConfiguration.cs @@ -32,7 +32,7 @@ public class PersistentStoreConfiguration /// /// Default value for . /// - public const int DefaultCheckpointingThreads = 1; + public const int DefaultCheckpointingThreads = 4; /// /// Default value for . From 01c627a0a36692a045a646d4d165820bb3b7cf04 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Wed, 30 Aug 2017 16:58:32 +0300 Subject: [PATCH 126/145] gg-12686 : Fixed null CacheMapHolder if node is restarted with partition in RENTING state. Signed-off-by: Andrey Gura --- .../cache/distributed/dht/GridDhtLocalPartition.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 4d1bb38e653e5..f0e0d47a27565 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 @@ -932,12 +932,8 @@ public void clearAll() throws NodeStoppingException { try { CacheDataRow row = it0.next(); - if (grp.sharedGroup() && (hld == null || hld.cctx.cacheId() != row.cacheId())) { - hld = cacheMaps.get(row.cacheId()); - - if (hld == null) - continue; - } + if (grp.sharedGroup() && (hld == null || hld.cctx.cacheId() != row.cacheId())) + hld = cacheMapHolder(ctx.cacheContext(row.cacheId())); assert hld != null; From 1e297429c66115e40db3fcb08304faaaed10d287 Mon Sep 17 00:00:00 2001 From: Aleksei Scherbakov Date: Tue, 12 Sep 2017 14:22:40 +0300 Subject: [PATCH 127/145] GG-12764 Fixed. --- .../cache/GridCachePartitionExchangeManager.java | 11 +++++++++++ ...IgniteTopologyValidatorGridSplitCacheTest.java | 15 +++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) 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 bd34a5feb9ee3..5c5e07d32fcd4 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 @@ -1992,6 +1992,17 @@ public boolean mergeExchangesOnCoordinator(GridDhtPartitionsExchangeFuture curFu } } + /** + * Returns topology version of last initialized exchange. + * @return Version. + */ + public AffinityTopologyVersion topologyVersion() { + GridDhtPartitionsExchangeFuture lastInitializedFut0 = lastInitializedFut; + + return lastInitializedFut0 != null + ? lastInitializedFut0.exchangeId().topologyVersion() : AffinityTopologyVersion.NONE; + } + /** * Exchange future thread. All exchanges happen only by one thread and next * exchange will not start until previous one completes. diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java index b78c9725a70e7..03a1c73a61584 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java @@ -31,8 +31,11 @@ import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.events.Event; import org.apache.ignite.events.EventType; +import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.lang.IgnitePredicate; @@ -269,14 +272,14 @@ private static class SplitAwareTopologyValidator implements TopologyValidator, L }).isEmpty()) return false; - IgniteKernal kernal = (IgniteKernal)ignite; - - GridDhtPartitionsExchangeFuture curFut = kernal.context().cache().context().exchange().lastTopologyFuture(); - - long cacheTopVer = curFut.context().events().topologyVersion().topologyVersion(); + GridKernalContext igniteContext = ((IgniteEx) ignite).context(); + GridCacheSharedContext cacheContext = igniteContext.cache().context(); + GridCachePartitionExchangeManager exchMgr = cacheContext.exchange(); + AffinityTopologyVersion topVer = exchMgr.topologyVersion(); + long cacheMajorTopVer = topVer.topologyVersion(); if (hasSplit(nodes)) { - boolean resolved = activatorTopVer != 0 && cacheTopVer >= activatorTopVer; + boolean resolved = activatorTopVer != 0 && cacheMajorTopVer >= activatorTopVer; if (!resolved) log.info("Grid segmentation is detected, switching to inoperative state."); From 7e00a556e752fa72c140b5ca2facad44d3665cce Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Wed, 13 Sep 2017 13:42:31 +0300 Subject: [PATCH 128/145] IGNITE-6356 - Fixed overflow when using number of partitions greater than Short.MAX_VALUE --- .../configuration/CacheConfiguration.java | 2 +- .../dht/preloader/GridDhtPartitionMap.java | 4 +- .../distributed/Cache64kPartitionsTest.java | 86 +++++++++++++++++++ .../testsuites/IgniteCacheTestSuite5.java | 4 +- 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/Cache64kPartitionsTest.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 2b4ec1d86e304..6c43d13bf8065 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 @@ -77,7 +77,7 @@ public class CacheConfiguration extends MutableConfiguration { private static final long serialVersionUID = 0L; /** Maximum number of partitions. */ - public static final int MAX_PARTITIONS_COUNT = 0xFFFF; + public static final int MAX_PARTITIONS_COUNT = 65000; /** Default size of rebalance thread pool. */ @Deprecated diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionMap.java index 410caf6ec7932..ca21cfcf48071 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionMap.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionMap.java @@ -277,8 +277,8 @@ public AffinityTopologyVersion topologyVersion() { map = new GridPartitionStateMap(); for (int i = 0; i < size; i++) { - int ordinal = in.readByte(); - int part = in.readShort(); + int ordinal = in.readUnsignedByte(); + int part = in.readUnsignedShort(); put(part, GridDhtPartitionState.fromOrdinal(ordinal)); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/Cache64kPartitionsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/Cache64kPartitionsTest.java new file mode 100644 index 0000000000000..fe139bae6c447 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/Cache64kPartitionsTest.java @@ -0,0 +1,86 @@ +/* + * 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; + +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * + */ +public class Cache64kPartitionsTest extends GridCommonAbstractTest { + /** */ + private boolean persistenceEnabled; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + CacheConfiguration ccfg = new CacheConfiguration("default"); + + ccfg.setAffinity(new RendezvousAffinityFunction(false, CacheConfiguration.MAX_PARTITIONS_COUNT)); + + cfg.setCacheConfiguration(ccfg); + + cfg.setActiveOnStart(false); + + if (persistenceEnabled) + cfg.setPersistentStoreConfiguration(new PersistentStoreConfiguration()); + + return cfg; + } + + /** + * @throws Exception if failed. + */ + public void testManyPartitionsNoPersistence() throws Exception { + checkManyPartitions(); + } + + /** + * @throws Exception if failed. + */ + public void testManyPartitionsWithPersistence() throws Exception { + persistenceEnabled = true; + + checkManyPartitions(); + } + + /** + * @throws Exception if failed. + */ + private void checkManyPartitions() throws Exception { + try { + startGrids(4); + + grid(0).active(true); + } + finally { + stopAllGrids(); + } + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + GridTestUtils.deleteDbFiles(); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java index dab2b19911a28..fb82873ab3b2c 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java @@ -35,9 +35,9 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheReadThroughEvictionsVariationsSuite; import org.apache.ignite.internal.processors.cache.IgniteCacheStoreCollectionTest; import org.apache.ignite.internal.processors.cache.PartitionsExchangeOnDiscoveryHistoryOverflowTest; +import org.apache.ignite.internal.processors.cache.distributed.Cache64kPartitionsTest; import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinityAssignmentNodeJoinValidationTest; import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinityAssignmentTest; -import org.apache.ignite.internal.processors.cache.distributed.GridCachePartitionEvictionDuringReadThroughSelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheGroupsPartitionLossPolicySelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCachePartitionLossPolicySelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheTxIteratorSelfTest; @@ -97,6 +97,8 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(GridCachePartitionEvictionDuringReadThroughSelfTest.class); suite.addTestSuite(NotMappedPartitionInTxTest.class); + suite.addTestSuite(Cache64kPartitionsTest.class); + return suite; } } From 5da0d62c44daa8aa20c5d5b9408bd48395940ea1 Mon Sep 17 00:00:00 2001 From: Aleksei Scherbakov Date: Wed, 13 Sep 2017 14:08:28 +0300 Subject: [PATCH 129/145] IGNITE-6356 Fix compilation after cherry-pick. --- .../java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java index fb82873ab3b2c..4ea02977746f4 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java @@ -38,6 +38,7 @@ import org.apache.ignite.internal.processors.cache.distributed.Cache64kPartitionsTest; import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinityAssignmentNodeJoinValidationTest; import org.apache.ignite.internal.processors.cache.distributed.CacheLateAffinityAssignmentTest; +import org.apache.ignite.internal.processors.cache.distributed.GridCachePartitionEvictionDuringReadThroughSelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheGroupsPartitionLossPolicySelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCachePartitionLossPolicySelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheTxIteratorSelfTest; From a4abf6e15d344c21cbceff7707983bfbfabe4ace Mon Sep 17 00:00:00 2001 From: sboikov Date: Fri, 22 Sep 2017 11:20:16 +0300 Subject: [PATCH 130/145] ignite-6181 Tx rollback on timeout (cherry picked from commit 5af30cf) --- .../IgniteDiagnosticPrepareContext.java | 4 +- .../processors/cache/CacheMetricsImpl.java | 2 +- .../processors/cache/GridCacheAdapter.java | 60 +- .../processors/cache/GridCacheMapEntry.java | 9 +- .../cache/GridCacheMvccManager.java | 5 +- .../GridDistributedTxRemoteAdapter.java | 2 +- .../dht/GridDhtTxFinishFuture.java | 2 +- .../cache/distributed/dht/GridDhtTxLocal.java | 6 +- .../dht/GridDhtTxLocalAdapter.java | 4 +- .../colocated/GridDhtColocatedLockFuture.java | 79 ++- .../distributed/near/GridNearLockFuture.java | 116 ++-- .../GridNearOptimisticTxPrepareFuture.java | 3 +- .../near/GridNearTransactionalCache.java | 3 - .../near/GridNearTxFastFinishFuture.java | 82 +++ .../near/GridNearTxFinishFuture.java | 23 +- .../distributed/near/GridNearTxLocal.java | 303 +++++--- .../distributed/near/NearTxFinishFuture.java | 31 + .../cache/transactions/IgniteInternalTx.java | 2 +- .../cache/transactions/IgniteTxAdapter.java | 13 +- .../cache/transactions/IgniteTxHandler.java | 26 +- .../transactions/IgniteTxLocalAdapter.java | 10 +- .../cache/transactions/IgniteTxLocalEx.java | 7 +- .../cache/transactions/IgniteTxManager.java | 80 +-- .../timeout/GridTimeoutProcessor.java | 23 +- .../cache/CacheTxFastFinishTest.java | 9 +- .../cache/IgniteTxConfigCacheSelfTest.java | 14 + .../IgniteCacheThreadLocalTxTest.java | 223 ++++++ .../IgniteOptimisticTxSuspendResumeTest.java | 6 +- ...nedMultiNodeLongTxTimeout2FullApiTest.java | 34 + .../TxRollbackOnTimeoutNearCacheTest.java | 28 + ...lbackOnTimeoutNoDeadlockDetectionTest.java | 47 ++ .../transactions/TxRollbackOnTimeoutTest.java | 655 ++++++++++++++++++ .../IgniteCacheFullApiSelfTestSuite.java | 2 + .../testsuites/IgniteCacheTestSuite6.java | 12 + .../hadoop/impl/HadoopTxConfigCacheTest.java | 4 +- 35 files changed, 1651 insertions(+), 278 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheThreadLocalTxTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/CachePartitionedMultiNodeLongTxTimeout2FullApiTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNearCacheTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNoDeadlockDetectionTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticPrepareContext.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticPrepareContext.java index 378dc7416fcaf..ed8d35e63945d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticPrepareContext.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteDiagnosticPrepareContext.java @@ -75,7 +75,7 @@ public void exchangeInfo(UUID nodeId, AffinityTopologyVersion topVer, String msg * @param keys Entry keys. * @param msg Initial message. */ - public void txKeyInfo(UUID nodeId, int cacheId, Collection keys, String msg) { + public void txKeyInfo(UUID nodeId, int cacheId, Collection keys, String msg) { closure(nodeId).add(msg, new TxEntriesInfoClosure(cacheId, keys)); } @@ -280,4 +280,4 @@ public void add(String msg, @Nullable DiagnosticBaseClosure c) { } } } -} +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java index d03a6f8e952f1..413b60df36df9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java @@ -328,7 +328,7 @@ public void delegate(CacheMetricsImpl delegate) { /** {@inheritDoc} */ @Override public int getTxDhtThreadMapSize() { - return cctx.isNear() && dhtCtx != null ? dhtCtx.tm().threadMapSize() : -1; + return cctx.tm().threadMapSize(); } /** {@inheritDoc} */ 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 fed716c84437b..a6f60f8ae78af 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 @@ -78,6 +78,7 @@ import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.IgniteTransactionsEx; import org.apache.ignite.internal.IgnitionEx; +import org.apache.ignite.internal.NodeStoppingException; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException; import org.apache.ignite.internal.cluster.IgniteClusterEx; @@ -1857,7 +1858,7 @@ protected final IgniteInternalFuture> getAllAsync0( return new GridFinishedFuture<>(e); } - tx = ctx.tm().threadLocalTx(ctx.systemTx() ? ctx : null); + tx = ctx.tm().threadLocalTx(ctx); } if (tx == null || tx.implicit()) { @@ -4060,7 +4061,7 @@ public void awaitLastFut() { return t; } - catch (IgniteInterruptedCheckedException | IgniteTxHeuristicCheckedException e) { + catch (IgniteInterruptedCheckedException | IgniteTxHeuristicCheckedException | NodeStoppingException e) { throw e; } catch (IgniteCheckedException e) { @@ -4074,7 +4075,8 @@ public void awaitLastFut() { catch (IgniteCheckedException | AssertionError | RuntimeException e1) { U.error(log, "Failed to rollback transaction (cache may contain stale locks): " + tx, e1); - e.addSuppressed(e1); + if (e != e1) + e.addSuppressed(e1); } } @@ -4198,21 +4200,24 @@ protected IgniteInternalFuture asyncOp( return new GridFinishedFuture<>( new IgniteCheckedException("Operation has been cancelled (node is stopping).")); - return op.op(tx0, opCtx).chain(new CX1, T>() { - @Override public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { - try { - return tFut.get(); - } - catch (IgniteTxRollbackCheckedException e) { - throw e; - } - catch (IgniteCheckedException e1) { + + return op.op(tx0, opCtx).chain(new CX1, T>() { + @Override + public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { try { - tx0.rollbackNearTxLocalAsync(); + return tFut.get(); } - catch (Throwable e2) { - e1.addSuppressed(e2); + catch (IgniteTxRollbackCheckedException | NodeStoppingExceptione) { + throw e; } + catch (IgniteCheckedException e1) { + try { + tx0.rollbackNearTxLocalAsync(); + } + catch (Throwable e2) { + if (e1 !=e2) + e1.addSuppressed(e2); + } throw e1; } @@ -4229,21 +4234,22 @@ protected IgniteInternalFuture asyncOp( return f; } - final IgniteInternalFuture f = op.op(tx, opCtx).chain(new CX1, T>() { - @Override public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { - try { - return tFut.get(); - } - catch (IgniteTxRollbackCheckedException e) { - throw e; - } - catch (IgniteCheckedException e1) { + finalIgniteInternalFuture + f = op.op(tx, opCtx).chain(new CX1, T>() { + @Override public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { try { - tx0.rollbackNearTxLocalAsync(); + return tFut.get(); } - catch (Throwable e2) { - e1.addSuppressed(e2); + catch (IgniteTxRollbackCheckedException | NodeStoppingExceptione) { + throw e; } + catch (IgniteCheckedException e1) { + try { + tx0.rollbackNearTxLocalAsync(); + } + catch (Throwable e2) {if (e2 != e1) + e1.addSuppressed(e2); + } throw e1; } 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 d991c86f9bd6b..0f547625955f4 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 @@ -2451,7 +2451,7 @@ int hash() { /** {@inheritDoc} */ @Nullable @Override public CacheObject peek(@Nullable IgniteCacheExpiryPolicy plc) throws GridCacheEntryRemovedException, IgniteCheckedException { - IgniteInternalTx tx = cctx.tm().localTxx(); + IgniteInternalTx tx = cctx.tm().localTx(); AffinityTopologyVersion topVer = tx != null ? tx.topologyVersion() : cctx.affinity().affinityTopologyVersion(); @@ -3098,10 +3098,7 @@ private boolean onExpired(CacheObject expiredVal, GridCacheVersion obsoleteVer) * @return Current transaction. */ private IgniteTxLocalAdapter currentTx() { - if (cctx.isDht()) - return cctx.dht().near().context().tm().localTx(); - else - return cctx.tm().localTx(); + return cctx.tm().localTx(); } /** {@inheritDoc} */ @@ -3479,7 +3476,7 @@ public final boolean visitable(CacheEntryPredicate[] filter) { } } - IgniteInternalTx tx = cctx.tm().localTxx(); + IgniteInternalTx tx = cctx.tm().localTx(); if (tx != null) { IgniteTxEntry e = tx.entry(txKey()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccManager.java index 09bf76210eb8d..e00e52254cb33 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvccManager.java @@ -478,13 +478,14 @@ public Collection dataStreamerFutures() { /** * @param fut Future. * @param futId Future ID. + * @return {@code True} if added. */ - public void addFuture(final GridCacheFuture fut, final IgniteUuid futId) { + public boolean addFuture(final GridCacheFuture fut, final IgniteUuid futId) { GridCacheFuture old = futs.put(futId, fut); assert old == null : old; - onFutureAdded(fut); + return onFutureAdded(fut); } /** 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 ea6461db6c17a..e5bcc46c1fec9 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 @@ -855,7 +855,7 @@ public void forceCommit() throws IgniteCheckedException { // Note that we don't evict near entries here - // they will be deleted by their corresponding transactions. if (state(ROLLING_BACK) || state() == UNKNOWN) { - cctx.tm().rollbackTx(this); + cctx.tm().rollbackTx(this, false); state(ROLLED_BACK); } 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 5311ddc84495d..63807107fcb84 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 @@ -227,7 +227,7 @@ public void onResult(UUID nodeId, GridDhtTxFinishResponse res) { try { boolean nodeStop = err != null && X.hasCause(err, NodeStoppingException.class); - this.tx.tmFinish(err == null, nodeStop); + this.tx.tmFinish(err == null, nodeStop, false); } catch (IgniteCheckedException finishErr) { U.error(log, "Failed to finish tx: " + tx, e); 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 5b8a7b5f779b1..d2fa81f8e6447 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 @@ -450,7 +450,7 @@ private void finishTx(boolean commit, @Nullable IgniteInternalFuture prepFut, Gr if (prepFut != null) prepFut.get(); // Check for errors. - boolean finished = localFinish(commit); + boolean finished = localFinish(commit, false); if (!finished) err = new IgniteCheckedException("Failed to finish transaction [commit=" + commit + @@ -556,7 +556,7 @@ public IgniteInternalFuture rollbackDhtLocalAsync() { /** {@inheritDoc} */ @SuppressWarnings({"CatchGenericClass", "ThrowableInstanceNeverThrown"}) - @Override public boolean localFinish(boolean commit) throws IgniteCheckedException { + @Override public boolean localFinish(boolean commit, boolean clearThreadMap) throws IgniteCheckedException { assert nearFinFutId != null || isInvalidate() || !commit || isSystemInvalidate() || onePhaseCommit() || state() == PREPARED : "Invalid state [nearFinFutId=" + nearFinFutId + ", isInvalidate=" + isInvalidate() + ", commit=" + commit + @@ -564,7 +564,7 @@ public IgniteInternalFuture rollbackDhtLocalAsync() { assert nearMiniId != 0; - return super.localFinish(commit); + return super.localFinish(commit, clearThreadMap); } /** {@inheritDoc} */ 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 86eac42e7e3ed..e4a7141f4809e 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 @@ -734,7 +734,7 @@ private IgniteInternalFuture obtainLockAsync( /** {@inheritDoc} */ @SuppressWarnings({"CatchGenericClass", "ThrowableInstanceNeverThrown"}) - @Override public boolean localFinish(boolean commit) throws IgniteCheckedException { + @Override public boolean localFinish(boolean commit, boolean clearThreadMap) throws IgniteCheckedException { if (log.isDebugEnabled()) log.debug("Finishing dht local tx [tx=" + this + ", commit=" + commit + "]"); @@ -773,7 +773,7 @@ private IgniteInternalFuture obtainLockAsync( if (commit && !isRollbackOnly()) userCommit(); else - userRollback(); + userRollback(clearThreadMap); } catch (IgniteCheckedException e) { err = e; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java index 7500549c421d2..c1e2e4748e52c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedLockFuture.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReference; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; @@ -54,6 +55,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLockRequest; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLockResponse; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; +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.TxDeadlock; @@ -99,6 +101,10 @@ public final class GridDhtColocatedLockFuture extends GridCacheCompoundIdentityF /** Logger. */ private static IgniteLogger msgLog; + /** Done field updater. */ + private static final AtomicIntegerFieldUpdater DONE_UPD = + AtomicIntegerFieldUpdater.newUpdater(GridDhtColocatedLockFuture.class, "done"); + /** Cache registry. */ @GridToStringExclude private final GridCacheContext cctx; @@ -146,6 +152,10 @@ public final class GridDhtColocatedLockFuture extends GridCacheCompoundIdentityF /** Map of current values. */ private final Map> valMap; + /** */ + @SuppressWarnings("UnusedDeclaration") + private volatile int done; + /** Trackable flag (here may be non-volatile). */ private boolean trackable; @@ -226,12 +236,6 @@ public GridDhtColocatedLockFuture( log = U.logger(cctx.kernalContext(), logRef, GridDhtColocatedLockFuture.class); } - if (timeout > 0) { - timeoutObj = new LockTimeoutObject(); - - cctx.time().addTimeoutObject(timeoutObj); - } - valMap = new ConcurrentHashMap8<>(); } @@ -322,6 +326,8 @@ private boolean implicitTx() { else { IgniteTxEntry txEntry = tx.entry(txKey); + assert txEntry != null; + txEntry.cached(entry); // Check transaction entries (corresponding tx entries must be enlisted in transaction). @@ -332,7 +338,7 @@ private boolean implicitTx() { threadId, lockVer, true, - tx.entry(txKey).locked(), + txEntry.locked(), inTx(), inTx() && tx.implicitSingle(), false, @@ -399,7 +405,7 @@ else if (log.isDebugEnabled()) * @param success Success flag. */ public void complete(boolean success) { - onComplete(success, true); + onComplete(success, true, true); } /** @@ -533,7 +539,7 @@ private synchronized void onError(Throwable t) { /** {@inheritDoc} */ @Override public boolean cancel() { if (onCancelled()) - onComplete(false, true); + onComplete(false, true, true); return isCancelled(); } @@ -556,7 +562,7 @@ private synchronized void onError(Throwable t) { if (err != null) success = false; - return onComplete(success, true); + return onComplete(success, true, true); } /** @@ -564,19 +570,32 @@ private synchronized void onError(Throwable t) { * * @param success {@code True} if lock was acquired. * @param distribute {@code True} if need to distribute lock removal in case of failure. + * @param restoreTimeout {@code True} if need restore tx timeout callback. * @return {@code True} if complete by this operation. */ - private boolean onComplete(boolean success, boolean distribute) { - if (log.isDebugEnabled()) + private boolean onComplete(boolean success, boolean distribute, boolean restoreTimeout) { + if (log.isDebugEnabled()) { log.debug("Received onComplete(..) callback [success=" + success + ", distribute=" + distribute + ", fut=" + this + ']'); + } + + if (!DONE_UPD.compareAndSet(this, 0, 1)) + return false; if (!success) undoLocks(distribute, true); - if (tx != null) + if (tx != null) { cctx.tm().txContext(tx); + if (restoreTimeout && tx.trackTimeout()) { + // Need restore timeout before onDone is called and next tx operation can proceed. + boolean add = tx.addTimeoutHandler(); + + assert add; + } + } + if (super.onDone(success, err)) { if (log.isDebugEnabled()) log.debug("Completing future: " + this); @@ -675,6 +694,30 @@ private boolean isMini(IgniteInternalFuture f) { * part. Note that if primary node leaves grid, the future will fail and transaction will be rolled back. */ void map() { + if (tx != null && tx.trackTimeout()) { + if (!tx.removeTimeoutHandler()) { + tx.finishFuture().listen(new IgniteInClosure>() { + @Override public void apply(IgniteInternalFuture fut) { + IgniteTxTimeoutCheckedException err = new IgniteTxTimeoutCheckedException("Failed to " + + "acquire lock, transaction was rolled back on timeout [timeout=" + tx.timeout() + + ", tx=" + tx + ']'); + + onError(err); + + onComplete(false, false, false); + } + }); + + return; + } + } + + if (timeout > 0) { + timeoutObj = new LockTimeoutObject(); + + cctx.time().addTimeoutObject(timeoutObj); + } + // Obtain the topology version to use. AffinityTopologyVersion topVer = cctx.mvcc().lastExplicitLockTopologyVersion(threadId); @@ -930,7 +973,7 @@ private synchronized void map0( if (log.isDebugEnabled()) log.debug("Entry being locked did not pass filter (will not lock): " + entry); - onComplete(false, false); + onComplete(false, false, true); return; } @@ -1307,7 +1350,7 @@ private boolean addLocalKey( if (log.isDebugEnabled()) log.debug("Entry being locked did not pass filter (will not lock): " + entry); - onComplete(false, false); + onComplete(false, false, true); return false; } @@ -1419,12 +1462,12 @@ private class LockTimeoutObject extends GridTimeoutObjectAdapter { U.warn(log, "Failed to detect deadlock.", e); } - onComplete(false, true); + onComplete(false, true, true); } }); } else - onComplete(false, true); + onComplete(false, true, true); } /** {@inheritDoc} */ @@ -1673,4 +1716,4 @@ private void remap() { return S.toString(MiniFuture.class, this, "node", node.id(), "super", super.toString()); } } -} +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java index bb713371953d2..93c7d64b3bb1c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearLockFuture.java @@ -27,6 +27,7 @@ import java.util.Queue; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReference; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; @@ -50,6 +51,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTransactionalCacheAdapter; +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.TxDeadlock; @@ -89,6 +91,10 @@ public final class GridNearLockFuture extends GridCacheCompoundIdentityFuture logRef = new AtomicReference<>(); + /** Done field updater. */ + private static final AtomicIntegerFieldUpdater DONE_UPD = + AtomicIntegerFieldUpdater.newUpdater(GridNearLockFuture.class, "done"); + /** */ private static IgniteLogger log; @@ -142,8 +148,9 @@ public final class GridNearLockFuture extends GridCacheCompoundIdentityFuture> valMap; - /** Trackable flag. */ - private boolean trackable = true; + /** */ + @SuppressWarnings("UnusedDeclaration") + private volatile int done; /** Keys locked so far. */ @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) @@ -184,6 +191,7 @@ public final class GridNearLockFuture extends GridCacheCompoundIdentityFuture cctx, @@ -230,12 +238,6 @@ public GridNearLockFuture( if (log == null) log = U.logger(cctx.kernalContext(), logRef, GridNearLockFuture.class); - if (timeout > 0) { - timeoutObj = new LockTimeoutObject(); - - cctx.time().addTimeoutObject(timeoutObj); - } - valMap = new ConcurrentHashMap8<>(); } @@ -260,12 +262,12 @@ public synchronized List entriesCopy() { /** {@inheritDoc} */ @Override public boolean trackable() { - return trackable; + return true; } /** {@inheritDoc} */ @Override public void markNotTrackable() { - trackable = false; + // No-op. } /** @@ -434,7 +436,7 @@ private void onFailed(boolean dist) { * @param success Success flag. */ public void complete(boolean success) { - onComplete(success, true); + onComplete(success, true, true); } /** @@ -655,7 +657,7 @@ private boolean checkLocks() { log.debug("Local lock acquired for entries [fut=" + this + ", entries=" + entries + "]"); } - onComplete(true, true); + onComplete(true, true, true); return true; } @@ -666,7 +668,7 @@ private boolean checkLocks() { /** {@inheritDoc} */ @Override public boolean cancel() { if (onCancelled()) - onComplete(false, true); + onComplete(false, true, true); return isCancelled(); } @@ -690,7 +692,7 @@ private boolean checkLocks() { if (err != null) success = false; - return onComplete(success, true); + return onComplete(success, true, true); } /** @@ -698,19 +700,32 @@ private boolean checkLocks() { * * @param success {@code True} if lock was acquired. * @param distribute {@code True} if need to distribute lock removal in case of failure. + * @param restoreTimeout {@code True} if need restore tx timeout callback. * @return {@code True} if complete by this operation. */ - private boolean onComplete(boolean success, boolean distribute) { - if (log.isDebugEnabled()) + private boolean onComplete(boolean success, boolean distribute, boolean restoreTimeout) { + if (log.isDebugEnabled()) { log.debug("Received onComplete(..) callback [success=" + success + ", distribute=" + distribute + ", fut=" + this + ']'); + } + + if (!DONE_UPD.compareAndSet(this, 0, 1)) + return false; if (!success) undoLocks(distribute, true); - if (tx != null) + if (tx != null) { cctx.tm().txContext(tx); + if (restoreTimeout && tx.trackTimeout()) { + // Need restore timeout before onDone is called and next tx operation can proceed. + boolean add = tx.addTimeoutHandler(); + + assert add; + } + } + if (super.onDone(success, err)) { if (log.isDebugEnabled()) log.debug("Completing future: " + this); @@ -770,6 +785,34 @@ private boolean isMini(IgniteInternalFuture f) { * part. Note that if primary node leaves grid, the future will fail and transaction will be rolled back. */ void map() { + if (tx != null && tx.trackTimeout()) { + if (!tx.removeTimeoutHandler()) { + tx.finishFuture().listen(new IgniteInClosure>() { + @Override public void apply(IgniteInternalFuture fut) { + IgniteTxTimeoutCheckedException err = new IgniteTxTimeoutCheckedException("Failed to " + + "acquire lock, transaction was rolled back on timeout [timeout=" + tx.timeout() + + ", tx=" + tx + ']'); + + onError(err); + + onComplete(false, false, false); + } + }); + + return; + } + } + + if (timeout > 0) { + timeoutObj = new LockTimeoutObject(); + + cctx.time().addTimeoutObject(timeoutObj); + } + + boolean added = cctx.mvcc().addFuture(this); + + assert added : this; + // Obtain the topology version to use. long threadId = Thread.currentThread().getId(); @@ -971,19 +1014,13 @@ private void map(Iterable keys, boolean remap, boolean topLocked GridNearCacheEntry entry = null; try { - entry = cctx.near().entryExx( - key, - topVer); + entry = cctx.near().entryExx(key, topVer); - if (!cctx.isAll( - entry, - filter)) { + if (!cctx.isAll(entry, filter)) { if (log.isDebugEnabled()) log.debug("Entry being locked did not pass filter (will not lock): " + entry); - onComplete( - false, - false); + onComplete(false, false, true); return; } @@ -1004,10 +1041,7 @@ private void map(Iterable keys, boolean remap, boolean topLocked if (cand != null) { if (tx == null && !cand.reentry()) - cctx.mvcc().addExplicitLock( - threadId, - cand, - topVer); + cctx.mvcc().addExplicitLock(threadId,cand,topVer); IgniteBiTuple val = entry.versionedValue(); @@ -1032,9 +1066,7 @@ private void map(Iterable keys, boolean remap, boolean topLocked if (val != null) { dhtVer = val.get1(); - valMap.put( - key, - val); + valMap.put(key, val); } if (!cand.reentry()) { @@ -1083,9 +1115,7 @@ private void map(Iterable keys, boolean remap, boolean topLocked distributedKeys.add(key); if (tx != null) - tx.addKeyMapping( - txKey, - mapping.node()); + tx.addKeyMapping(txKey, mapping.node()); req.addKeyBytes( key, @@ -1098,14 +1128,16 @@ private void map(Iterable keys, boolean remap, boolean topLocked if (cand.reentry()) explicit = tx != null && !entry.hasLockCandidate(tx.xidVersion()); } - else + else { + if (timedOut) + return; + // Ignore reentries within transactions. explicit = tx != null && !entry.hasLockCandidate(tx.xidVersion()); + } if (explicit) - tx.addKeyMapping( - txKey, - mapping.node()); + tx.addKeyMapping(txKey, mapping.node()); break; } @@ -1476,12 +1508,12 @@ private class LockTimeoutObject extends GridTimeoutObjectAdapter { U.warn(log, "Failed to detect deadlock.", e); } - onComplete(false, true); + onComplete(false, true, true); } }); } else - onComplete(false, true); + onComplete(false, true, true); } /** {@inheritDoc} */ 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 2c23a7a50d3f9..822651c62932b 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 @@ -913,8 +913,7 @@ void onResult(final GridNearTxPrepareResponse res) { return; if (RCV_RES_UPD.compareAndSet(this, 0, 1)) { - if (parent.cctx.tm().deadlockDetectionEnabled() && - (parent.tx.remainingTime() == -1 || res.error() instanceof IgniteTxTimeoutCheckedException)) { + if (parent.tx.remainingTime() == -1 || res.error() instanceof IgniteTxTimeoutCheckedException) { parent.onTimeout(); return; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java index a691cbc0f4f43..c55250ba02342 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java @@ -467,9 +467,6 @@ private void processLockResponse(UUID nodeId, GridNearLockResponse res) { opCtx != null && opCtx.isKeepBinary(), opCtx != null && opCtx.recovery()); - if (!ctx.mvcc().addFuture(fut)) - throw new IllegalStateException("Duplicate future ID: " + fut); - fut.map(); return fut; 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 new file mode 100644 index 0000000000000..72226973009c5 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFastFinishFuture.java @@ -0,0 +1,82 @@ +/* + * 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.internal.processors.cache.transactions.IgniteInternalTx; +import org.apache.ignite.internal.util.future.GridFutureAdapter; + +import static org.apache.ignite.transactions.TransactionState.COMMITTED; +import static org.apache.ignite.transactions.TransactionState.COMMITTING; +import static org.apache.ignite.transactions.TransactionState.PREPARED; +import static org.apache.ignite.transactions.TransactionState.PREPARING; +import static org.apache.ignite.transactions.TransactionState.ROLLED_BACK; +import static org.apache.ignite.transactions.TransactionState.ROLLING_BACK; + +/** + * + */ +public class GridNearTxFastFinishFuture extends GridFutureAdapter implements NearTxFinishFuture { + /** */ + private final GridNearTxLocal tx; + + /** */ + private final boolean commit; + + /** + * @param tx Transaction. + * @param commit Commit flag. + */ + GridNearTxFastFinishFuture(GridNearTxLocal tx, boolean commit) { + this.tx = tx; + this.commit = commit; + } + + /** {@inheritDoc} */ + @Override public boolean commit() { + return commit; + } + + /** + * + */ + public void finish() { + try { + if (commit) { + tx.state(PREPARING); + tx.state(PREPARED); + tx.state(COMMITTING); + + tx.context().tm().fastFinishTx(tx, true); + + tx.state(COMMITTED); + } + else { + tx.state(PREPARING); + tx.state(PREPARED); + tx.state(ROLLING_BACK); + + tx.context().tm().fastFinishTx(tx, false); + + tx.state(ROLLED_BACK); + } + } + finally { + onDone(tx); + } + } +} 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 c45eb7bd26178..b6a885553f86b 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 @@ -67,7 +67,7 @@ * */ public final class GridNearTxFinishFuture extends GridCacheCompoundIdentityFuture - implements GridCacheFuture { + implements GridCacheFuture, NearTxFinishFuture { /** */ private static final long serialVersionUID = 0L; @@ -135,6 +135,11 @@ public GridNearTxFinishFuture(GridCacheSharedContext cctx, GridNearTxLocal } } + /** {@inheritDoc} */ + @Override public boolean commit() { + return commit; + } + /** {@inheritDoc} */ @Override public IgniteUuid futureId() { return futId; @@ -278,6 +283,13 @@ else if (fut.getClass() == CheckRemoteTxMiniFuture.class) { } } + /** + * + */ + void forceFinish() { + super.onDone(tx, null, false); + } + /** {@inheritDoc} */ @Override public boolean onDone(IgniteInternalTx tx0, Throwable err) { if (isDone()) @@ -310,7 +322,7 @@ else if (err != null) err = new TransactionRollbackException("Failed to commit transaction.", err); try { - tx.localFinish(err == null); + tx.localFinish(err == null, true); } catch (IgniteCheckedException e) { if (err != null) @@ -327,7 +339,7 @@ else if (err != null) finishOnePhase(commit); try { - tx.tmFinish(commit, nodeStop); + tx.tmFinish(commit, nodeStop, true); } catch (IgniteCheckedException e) { U.error(log, "Failed to finish tx: " + tx, e); @@ -386,9 +398,10 @@ private boolean isMini(IgniteInternalFuture fut) { * Initializes future. * * @param commit Commit flag. + * @param clearThreadMap If {@code true} removes {@link GridNearTxLocal} from thread map. */ @SuppressWarnings("ForLoopReplaceableByForEach") - void finish(boolean commit) { + public void finish(boolean commit, boolean clearThreadMap) { if (tx.onNeedCheckBackup()) { assert tx.onePhaseCommit(); @@ -402,7 +415,7 @@ void finish(boolean commit) { } try { - if (tx.localFinish(commit) || (!commit && tx.state() == UNKNOWN)) { + if (tx.localFinish(commit, clearThreadMap) || (!commit && tx.state() == UNKNOWN)) { if ((tx.onePhaseCommit() && needFinishOnePhase(commit)) || (!tx.onePhaseCommit() && mappings != null)) { if (mappings.single()) { GridDistributedTxMapping mapping = mappings.singleMapping(); 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 2fb0ff32c180a..9673955b45ade 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 @@ -67,6 +67,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.version.GridCacheVersion; +import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.transactions.IgniteTxOptimisticCheckedException; import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException; import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException; @@ -89,6 +90,7 @@ import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.lang.IgniteBiClosure; import org.apache.ignite.lang.IgniteClosure; +import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgniteUuid; import org.apache.ignite.plugin.security.SecurityPermission; import org.apache.ignite.transactions.TransactionConcurrency; @@ -108,7 +110,7 @@ import static org.apache.ignite.transactions.TransactionState.ACTIVE; import static org.apache.ignite.transactions.TransactionState.COMMITTED; import static org.apache.ignite.transactions.TransactionState.COMMITTING; -import static org.apache.ignite.transactions.TransactionState.PREPARED; +import static org.apache.ignite.transactions.TransactionState.MARKED_ROLLBACK; import static org.apache.ignite.transactions.TransactionState.PREPARING; import static org.apache.ignite.transactions.TransactionState.ROLLED_BACK; import static org.apache.ignite.transactions.TransactionState.ROLLING_BACK; @@ -119,7 +121,7 @@ * Replicated user transaction. */ @SuppressWarnings("unchecked") -public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoCloseable { +public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeoutObject, AutoCloseable { /** */ private static final long serialVersionUID = 0L; @@ -128,12 +130,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoClosea AtomicReferenceFieldUpdater.newUpdater(GridNearTxLocal.class, IgniteInternalFuture.class, "prepFut"); /** Prepare future updater. */ - private static final AtomicReferenceFieldUpdater COMMIT_FUT_UPD = - AtomicReferenceFieldUpdater.newUpdater(GridNearTxLocal.class, GridNearTxFinishFuture.class, "commitFut"); - - /** Rollback future updater. */ - private static final AtomicReferenceFieldUpdater ROLLBACK_FUT_UPD = - AtomicReferenceFieldUpdater.newUpdater(GridNearTxLocal.class, GridNearTxFinishFuture.class, "rollbackFut"); + private static final AtomicReferenceFieldUpdater FINISH_FUT_UPD = + AtomicReferenceFieldUpdater.newUpdater(GridNearTxLocal.class, NearTxFinishFuture.class, "finishFut"); /** DHT mappings. */ private IgniteTxMappings mappings; @@ -146,12 +144,7 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoClosea /** Commit future. */ @SuppressWarnings("UnusedDeclaration") @GridToStringExclude - private volatile GridNearTxFinishFuture commitFut; - - /** Rollback future. */ - @SuppressWarnings("UnusedDeclaration") - @GridToStringExclude - private volatile GridNearTxFinishFuture rollbackFut; + private volatile NearTxFinishFuture finishFut; /** True if transaction contains near cache entries mapped to local node. */ private boolean nearLocallyMapped; @@ -171,6 +164,9 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoClosea /** If this transaction contains transform entries. */ protected boolean transform; + /** */ + private boolean trackTimeout; + /** */ @GridToStringExclude private TransactionProxyImpl proxy; @@ -231,6 +227,9 @@ public GridNearTxLocal( mappings = implicitSingle ? new IgniteTxMappingsSingleImpl() : new IgniteTxMappingsImpl(); initResult(); + + if (timeout() > 0 && !implicit()) + trackTimeout = cctx.time().addTimeoutObject(this); } /** {@inheritDoc} */ @@ -3036,7 +3035,7 @@ private void readyNearLock(IgniteTxEntry txEntry, /** {@inheritDoc} */ @SuppressWarnings({"CatchGenericClass", "ThrowableInstanceNeverThrown"}) - @Override public boolean localFinish(boolean commit) throws IgniteCheckedException { + @Override public boolean localFinish(boolean commit, boolean clearThreadMap) throws IgniteCheckedException { if (log.isDebugEnabled()) log.debug("Finishing near local tx [tx=" + this + ", commit=" + commit + "]"); @@ -3072,7 +3071,7 @@ private void readyNearLock(IgniteTxEntry txEntry, if (commit && !isRollbackOnly()) userCommit(); else - userRollback(); + userRollback(clearThreadMap); } catch (IgniteCheckedException e) { err = e; @@ -3138,6 +3137,9 @@ public IgniteInternalFuture prepareNearTxLocal() { if (!PREP_FUT_UPD.compareAndSet(this, null, fut)) return prepFut; + if (trackTimeout) + removeTimeoutHandler(); + if (timeout == -1) { fut.onDone(this, timeoutException()); @@ -3166,14 +3168,7 @@ public IgniteInternalFuture prepareNearTxLocal() { * @throws IgniteCheckedException If failed. */ public final void prepare() throws IgniteCheckedException { - prepareAsync().get(); - } - - /** - * @return Prepare future. - */ - private IgniteInternalFuture prepareAsync() { - return prepareNearTxLocal(); + prepareNearTxLocal().get(); } /** @@ -3190,42 +3185,43 @@ public IgniteInternalFuture commitNearTxLocalAsync() { if (log.isDebugEnabled()) log.debug("Committing near local tx: " + this); + NearTxFinishFuture fut = finishFut; + + if (fut != null) + return chainFinishFuture(fut, true); + if (fastFinish()) { - state(PREPARING); - state(PREPARED); - state(COMMITTING); + GridNearTxFastFinishFuture fut0; - cctx.tm().fastFinishTx(this, true); + if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFastFinishFuture(this, true))) + return chainFinishFuture(finishFut, true); - state(COMMITTED); + fut0.finish(); - return new GridFinishedFuture<>((IgniteInternalTx)this); + return fut0; } - final IgniteInternalFuture prepareFut = prepareNearTxLocal(); + final GridNearTxFinishFuture fut0; - GridNearTxFinishFuture fut = commitFut; + if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFinishFuture<>(cctx, this, true))) + return chainFinishFuture(finishFut, true); - if (fut == null && - !COMMIT_FUT_UPD.compareAndSet(this, null, fut = new GridNearTxFinishFuture<>(cctx, this, true))) - return commitFut; + cctx.mvcc().addFuture(fut0, fut0.futureId()); - cctx.mvcc().addFuture(fut, fut.futureId()); + final IgniteInternalFuture prepareFut = prepareNearTxLocal(); prepareFut.listen(new CI1>() { @Override public void apply(IgniteInternalFuture f) { - GridNearTxFinishFuture fut0 = commitFut; - try { // Make sure that here are no exceptions. prepareFut.get(); - fut0.finish(true); + fut0.finish(true, true); } catch (Error | RuntimeException e) { COMMIT_ERR_UPD.compareAndSet(GridNearTxLocal.this, null, e); - fut0.finish(false); + fut0.finish(false, true); throw e; } @@ -3233,12 +3229,12 @@ public IgniteInternalFuture commitNearTxLocalAsync() { COMMIT_ERR_UPD.compareAndSet(GridNearTxLocal.this, null, e); if (!(e instanceof NodeStoppingException)) - fut0.finish(false); + fut0.finish(false, true); } } }); - return fut; + return fut0; } /** {@inheritDoc} */ @@ -3257,30 +3253,42 @@ public void rollback() throws IgniteCheckedException { * @return Rollback future. */ public IgniteInternalFuture rollbackNearTxLocalAsync() { + return rollbackNearTxLocalAsync(false); + } + + /** + * @param onTimeout {@code True} if rolled back asynchronously on timeout. + * @return Rollback future. + */ + private IgniteInternalFuture rollbackNearTxLocalAsync(final boolean onTimeout) { if (log.isDebugEnabled()) log.debug("Rolling back near tx: " + this); + if (!onTimeout && trackTimeout) + removeTimeoutHandler(); + + NearTxFinishFuture fut = finishFut; + + if (fut != null) + return chainFinishFuture(finishFut, false); + if (fastFinish()) { - state(PREPARING); - state(PREPARED); - state(ROLLING_BACK); + GridNearTxFastFinishFuture fut0; - cctx.tm().fastFinishTx(this, false); + if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFastFinishFuture(this, false))) + return chainFinishFuture(finishFut, false); - state(ROLLED_BACK); + fut0.finish(); - return new GridFinishedFuture<>((IgniteInternalTx)this); + return fut0; } - GridNearTxFinishFuture fut = rollbackFut; + final GridNearTxFinishFuture fut0; - if (fut != null) - return fut; + if (!FINISH_FUT_UPD.compareAndSet(this, null, fut0 = new GridNearTxFinishFuture<>(cctx, this, false))) + return chainFinishFuture(finishFut, false); - if (!ROLLBACK_FUT_UPD.compareAndSet(this, null, fut = new GridNearTxFinishFuture<>(cctx, this, false))) - return rollbackFut; - - cctx.mvcc().addFuture(fut, fut.futureId()); + cctx.mvcc().addFuture(fut0, fut0.futureId()); IgniteInternalFuture prepFut = this.prepFut; @@ -3295,7 +3303,7 @@ public IgniteInternalFuture rollbackNearTxLocalAsync() { log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']'); } - fut.finish(false); + fut0.finish(false, !onTimeout); } else { prepFut.listen(new CI1>() { @@ -3309,14 +3317,12 @@ public IgniteInternalFuture rollbackNearTxLocalAsync() { log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']'); } - GridNearTxFinishFuture fut0 = rollbackFut; - - fut0.finish(false); + fut0.finish(false, !onTimeout); } }); } - return fut; + return fut0; } /** {@inheritDoc} */ @@ -3324,6 +3330,64 @@ public IgniteInternalFuture rollbackNearTxLocalAsync() { return rollbackNearTxLocalAsync(); } + /** + * @param fut Already started finish future. + * @param commit Commit flag. + * @return Finish future. + */ + private IgniteInternalFuture chainFinishFuture(final NearTxFinishFuture fut, final boolean commit) { + assert fut != null; + + if (fut.commit() != commit) { + final GridNearTxLocal tx = this; + + if (!commit) { + final GridNearTxFinishFuture rollbackFut = new GridNearTxFinishFuture<>(cctx, this, false); + + fut.listen(new IgniteInClosure>() { + @Override public void apply(IgniteInternalFuture fut0) { + if (FINISH_FUT_UPD.compareAndSet(tx, fut, rollbackFut)) { + if (tx.state() == COMMITTED) { + if (log.isDebugEnabled()) + log.debug("Failed to rollback, transaction is already committed: " + tx); + + rollbackFut.forceFinish(); + + assert rollbackFut.isDone() : rollbackFut; + } + else { + if (!cctx.mvcc().addFuture(rollbackFut, rollbackFut.futureId())) + return; + + rollbackFut.finish(false, true); + } + } + } + }); + + return rollbackFut; + } + else { + final GridFutureAdapter fut0 = new GridFutureAdapter<>(); + + fut.listen(new IgniteInClosure>() { + @Override public void apply(IgniteInternalFuture fut) { + if (timedOut()) + fut0.onDone(new IgniteTxTimeoutCheckedException("Failed to commit transaction, " + + "transaction is concurrently rolled back on timeout: " + tx)); + else + fut0.onDone(new IgniteCheckedException("Failed to commit transaction, " + + "transaction is concurrently rolled back: " + tx)); + } + }); + + return fut0; + } + } + + return fut; + } + /** * @return {@code True} if 'fast finish' path can be used for transaction completion. */ @@ -3689,38 +3753,53 @@ public IgniteInternalFuture lockAllAsync(GridCacheContext c @Override public void close() throws IgniteCheckedException { TransactionState state = state(); - if (state != ROLLING_BACK && state != ROLLED_BACK && state != COMMITTING && state != COMMITTED) - rollback(); + try { + if (state == COMMITTED || state == ROLLED_BACK) + return; - synchronized (this) { - try { - while (!done()) - wait(); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); + if (trackTimeout) + removeTimeoutHandler(); - if (!done()) - throw new IgniteCheckedException("Got interrupted while waiting for transaction to complete: " + - this, e); + if (state != ROLLING_BACK && state != COMMITTING) + rollback(); + + synchronized (this) { + try { + while (!done()) + wait(); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + if (!done()) + throw new IgniteCheckedException("Got interrupted while waiting for transaction to complete: " + + this, e); + } } } + finally { + // It is possible tx was rolled back asynchronously on timeout and thread map is not cleared yet. + boolean cleanup = state == ROLLED_BACK && timedOut(); - if (accessMap != null) { - assert optimistic(); + if (cleanup) + cctx.tm().clearThreadMap(this); - for (Map.Entry e : accessMap.entrySet()) { - if (e.getValue().entries() != null) { - GridCacheContext cctx0 = cctx.cacheContext(e.getKey().cacheId()); + if (accessMap != null) { + assert optimistic(); - if (cctx0.isNear()) - cctx0.near().dht().sendTtlUpdateRequest(e.getValue()); - else - cctx0.dht().sendTtlUpdateRequest(e.getValue()); + for (Map.Entry e : accessMap.entrySet()) { + if (e.getValue().entries() != null) { + GridCacheContext cctx0 = cctx.cacheContext(e.getKey().cacheId()); + + if (cctx0.isNear()) + cctx0.near().dht().sendTtlUpdateRequest(e.getValue()); + else + cctx0.dht().sendTtlUpdateRequest(e.getValue()); + } } - } - accessMap = null; + accessMap = null; + } } } @@ -3996,12 +4075,68 @@ private IgniteInternalFuture nonInterruptable(IgniteInternalFuture fut /** * @param threadId new owner of transaction. - * @throws IgniteCheckedException if method executed not in the middle of resume or suspend. */ public void threadId(long threadId) { this.threadId = threadId; } + /** + * @return {@code True} if need register callback which cancels tx on timeout. + */ + public boolean trackTimeout() { + return trackTimeout; + } + + /** + * Removes timeout handler. + * + * @return {@code True} if handler was removed. + */ + public boolean removeTimeoutHandler() { + assert trackTimeout; + + return cctx.time().removeTimeoutObject(this); + } + + /** + * @return {@code True} if handler was added. + */ + public boolean addTimeoutHandler() { + assert trackTimeout; + + return cctx.time().addTimeoutObject(this); + } + + /** {@inheritDoc} */ + @Override public IgniteUuid timeoutId() { + return xid(); + } + + /** {@inheritDoc} */ + @Override public long endTime() { + return startTime() + timeout(); + } + + /** {@inheritDoc} */ + @Override public void onTimeout() { + if (state(MARKED_ROLLBACK, true) || (state() == MARKED_ROLLBACK)) { + if (log.isDebugEnabled()) + log.debug("Will rollback tx on timeout: " + this); + + cctx.kernalContext().closure().runLocalSafe(new Runnable() { + @Override public void run() { + // Note: if rollback asynchonously on timeout should not clear thread map + // since thread started tx still should be able to see this tx. + rollbackNearTxLocalAsync(true); + } + }); + } + else { + if (log.isDebugEnabled()) + log.debug("Skip rollback tx on timeout: " + this); + } + } + /** * Post-lock closure. * 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 new file mode 100644 index 0000000000000..132c7549657cb --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/NearTxFinishFuture.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.distributed.near; + +import org.apache.ignite.internal.IgniteInternalFuture; +import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; + +/** + * + */ +public interface NearTxFinishFuture extends IgniteInternalFuture { + /** + * @return Commit flag. + */ + boolean commit(); +} 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 75980030238bf..9e06d9d017bae 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 @@ -634,4 +634,4 @@ public void completedVersions(GridCacheVersion base, * @param e Commit error. */ public void commitError(Throwable e); -} +} \ No newline at end of file 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 65f8af247b002..9f71968b43a35 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 @@ -356,6 +356,13 @@ protected IgniteTxAdapter( log = U.logger(cctx.kernalContext(), logRef, this); } + /** + * @return Shared cache context. + */ + public GridCacheSharedContext context() { + return cctx; + } + /** {@inheritDoc} */ @Override public boolean localResult() { assert originatingNodeId() != null; @@ -966,7 +973,7 @@ public void onePhaseCommit(boolean onePhaseCommit) { * @return {@code True} if state changed. */ @SuppressWarnings({"TooBroadScope"}) - protected boolean state(TransactionState state, boolean timedOut) { + protected final boolean state(TransactionState state, boolean timedOut) { boolean valid = false; TransactionState prev; @@ -1047,7 +1054,9 @@ protected boolean state(TransactionState state, boolean timedOut) { if (valid) { this.state = state; - this.timedOut = timedOut; + + if (timedOut) + this.timedOut = true; if (log.isDebugEnabled()) log.debug("Changed transaction state [prev=" + prev + ", new=" + this.state + ", tx=" + this + ']'); 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 de0e4151c303a..3bd4e8ab73730 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 @@ -968,28 +968,30 @@ private IgniteInternalFuture finishDhtLocal(UUID nodeId, } } catch (Throwable e) { - tx.commitError(e); + U.error(log, "Failed completing transaction [commit=" + req.commit() + ", tx=" + tx + ']', e); - tx.systemInvalidate(true); + if (tx != null) { + tx.commitError(e); - U.error(log, "Failed completing transaction [commit=" + req.commit() + ", tx=" + tx + ']', e); + tx.systemInvalidate(true); - IgniteInternalFuture res = null; + try { + IgniteInternalFuture res = tx.rollbackDhtLocalAsync(); - try { - res = tx.rollbackDhtLocalAsync(); + // Only for error logging. + res.listen(CU.errorLogger(log)); - // Only for error logging. - res.listen(CU.errorLogger(log)); - } - catch (Throwable e1) { - e.addSuppressed(e1); + return res; + } + catch (Throwable e1) { + e.addSuppressed(e1); + } } if (e instanceof Error) throw (Error)e; - return res; + return new GridFinishedFuture<>(e); } } 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 e7ebaaea7670d..96147b74f20ad 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 @@ -54,6 +54,7 @@ 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.near.GridNearTxLocal; 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; @@ -898,10 +899,11 @@ assert ownsLock(txEntry.cached()): * Commits transaction to transaction manager. Used for one-phase commit transactions only. * * @param commit If {@code true} commits transaction, otherwise rollbacks. + * @param clearThreadMap If {@code true} removes {@link GridNearTxLocal} from thread map. * @param nodeStop If {@code true} tx is cancelled on node stop. * @throws IgniteCheckedException If failed. */ - public void tmFinish(boolean commit, boolean nodeStop) throws IgniteCheckedException { + public void tmFinish(boolean commit, boolean nodeStop, boolean clearThreadMap) throws IgniteCheckedException { assert onePhaseCommit(); if (DONE_FLAG_UPD.compareAndSet(this, 0, 1)) { @@ -910,7 +912,7 @@ public void tmFinish(boolean commit, boolean nodeStop) throws IgniteCheckedExcep if (commit) cctx.tm().commitTx(this); else - cctx.tm().rollbackTx(this); + cctx.tm().rollbackTx(this, clearThreadMap); } state(commit ? COMMITTED : ROLLED_BACK); @@ -957,7 +959,7 @@ public Collection rolledbackVersions() { } /** {@inheritDoc} */ - @Override public void userRollback() throws IgniteCheckedException { + @Override public void userRollback(boolean clearThreadMap) throws IgniteCheckedException { TransactionState state = state(); if (state != ROLLING_BACK && state != ROLLED_BACK) { @@ -975,7 +977,7 @@ public Collection rolledbackVersions() { } if (DONE_FLAG_UPD.compareAndSet(this, 0, 1)) { - cctx.tm().rollbackTx(this); + cctx.tm().rollbackTx(this, clearThreadMap); if (!internal()) { Collection stores = txState.stores(cctx); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalEx.java index 307c348efd6e1..b61b1a9a629b5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalEx.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalEx.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.cache.transactions; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.jetbrains.annotations.Nullable; @@ -41,16 +42,18 @@ public interface IgniteTxLocalEx extends IgniteInternalTx { public void userCommit() throws IgniteCheckedException; /** + * @param clearThreadMap If {@code true} removes {@link GridNearTxLocal} from thread map. * @throws IgniteCheckedException If rollback failed. */ - public void userRollback() throws IgniteCheckedException; + public void userRollback(boolean clearThreadMap) throws IgniteCheckedException; /** * Finishes transaction (either commit or rollback). * * @param commit {@code True} if commit, {@code false} if rollback. + * @param clearThreadMap If {@code true} removes {@link GridNearTxLocal} from thread map. * @return {@code True} if state has been changed. * @throws IgniteCheckedException If finish failed. */ - public boolean localFinish(boolean commit) throws IgniteCheckedException; + public boolean localFinish(boolean commit, boolean clearThreadMap) throws IgniteCheckedException; } 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 6c5970676ccbc..e9f36e20b1b03 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 @@ -284,7 +284,7 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter { public void rollbackTransactionsForCache(int cacheId) { rollbackTransactionsForCache(cacheId, nearIdMap); - rollbackTransactionsForCache(cacheId, threadMap); + rollbackTransactionsForCache(cacheId, idMap); } /** @@ -297,7 +297,7 @@ private void rollbackTransactionsForCache(int cacheId, ConcurrentMap e : threadMap.entrySet()) - rollbackTx(e.getValue()); + for (IgniteInternalTx tx : idMap.values()) + rollbackTx(tx, true); + for (IgniteInternalTx tx : nearIdMap.values()) + rollbackTx(tx, true); IgniteClientDisconnectedException err = new IgniteClientDisconnectedException(reconnectFut, "Client node disconnected."); @@ -371,6 +373,7 @@ private void salvageTx(IgniteInternalTx tx, IgniteInternalTx.FinalizationStatus X.println(">>> Transaction manager memory stats [igniteInstanceName=" + cctx.igniteInstanceName() + ']'); X.println(">>> threadMapSize: " + threadMap.size()); X.println(">>> idMap [size=" + idMap.size() + ']'); + X.println(">>> nearIdMap [size=" + nearIdMap.size() + ']'); X.println(">>> completedVersSortedSize: " + completedVersSorted.size()); X.println(">>> completedVersHashMapSize: " + completedVersHashMap.sizex()); } @@ -483,14 +486,15 @@ public GridNearTxLocal newTx( IgniteInternalTx t; if ((t = txIdMap.putIfAbsent(tx.xidVersion(), tx)) == null) { - // Add both, explicit and implicit transactions. - // Do not add remote and dht local transactions as remote node may have the same thread ID - // and overwrite local transaction. if (tx.local() && !tx.dht()) { - if (cacheCtx == null || !cacheCtx.systemTx()) - threadMap.put(tx.threadId(), tx); - else - sysThreadMap.put(new TxThreadKey(tx.threadId(), cacheCtx.cacheId()), tx); + assert tx instanceof GridNearTxLocal : tx; + + if (!tx.implicit()) { + if (cacheCtx == null || !cacheCtx.systemTx()) + threadMap.put(tx.threadId(), tx); + else + sysThreadMap.put(new TxThreadKey(tx.threadId(), cacheCtx.cacheId()), tx); + } } // Handle mapped versions. @@ -626,11 +630,10 @@ public void addAlternateVersion(GridCacheVersion ver, IgniteInternalTx tx) { /** * @return Local transaction. */ - @SuppressWarnings({"unchecked"}) - @Nullable public T localTx() { - IgniteInternalTx tx = tx(); + @Nullable public IgniteTxLocalAdapter localTx() { + IgniteTxLocalAdapter tx = tx(); - return tx != null && tx.local() ? (T)tx : null; + return tx != null && tx.local() ? tx : null; } /** @@ -711,15 +714,6 @@ public boolean setTxTopologyHint(@Nullable AffinityTopologyVersion topVer) { return false; } - /** - * @return Local transaction. - */ - @Nullable public IgniteInternalTx localTxx() { - IgniteInternalTx tx = tx(); - - return tx != null && tx.local() ? tx : null; - } - /** * @return User transaction for current thread. */ @@ -1208,32 +1202,32 @@ public void commitTx(IgniteInternalTx tx) throws IgniteCheckedException { collectPendingVersions(dhtTxLoc); } - // 4. Unlock write resources. + // 3. Unlock write resources. unlockMultiple(tx, tx.writeEntries()); - // 5. Unlock read resources if required. + // 4. Unlock read resources if required. if (unlockReadEntries(tx)) unlockMultiple(tx, tx.readEntries()); - // 6. Notify evictions. + // 5. Notify evictions. notifyEvictions(tx); - // 7. Remove obsolete entries from cache. + // 6. Remove obsolete entries from cache. removeObsolete(tx); - // 8. Assign transaction number at the end of transaction. + // 7. Assign transaction number at the end of transaction. tx.endVersion(cctx.versions().next(tx.topologyVersion())); - // 9. Remove from per-thread storage. + // 8. Remove from per-thread storage. clearThreadMap(tx); - // 10. Unregister explicit locks. + // 9. Unregister explicit locks. if (!tx.alternateVersions().isEmpty()) { for (GridCacheVersion ver : tx.alternateVersions()) idMap.remove(ver); } - // 11. Remove Near-2-DHT mappings. + // 10. Remove Near-2-DHT mappings. if (tx instanceof GridCacheMappedVersion) { GridCacheVersion mapped = ((GridCacheMappedVersion)tx).mappedVersion(); @@ -1241,10 +1235,10 @@ public void commitTx(IgniteInternalTx tx) throws IgniteCheckedException { mappedVers.remove(mapped); } - // 12. Clear context. + // 11. Clear context. resetContext(); - // 14. Update metrics. + // 12. Update metrics. if (!tx.dht() && tx.local()) { if (!tx.system()) cctx.txMetrics().onTxCommit(); @@ -1268,8 +1262,9 @@ else if (log.isDebugEnabled()) * Rolls back a transaction. * * @param tx Transaction to rollback. + * @param clearThreadMap {@code True} if need remove tx from thread map. */ - public void rollbackTx(IgniteInternalTx tx) { + public void rollbackTx(IgniteInternalTx tx, boolean clearThreadMap) { assert tx != null; if (log.isDebugEnabled()) @@ -1295,7 +1290,8 @@ public void rollbackTx(IgniteInternalTx tx) { removeObsolete(tx); // 6. Remove from per-thread storage. - clearThreadMap(tx); + if (clearThreadMap) + clearThreadMap(tx); // 7. Unregister explicit locks. if (!tx.alternateVersions().isEmpty()) @@ -1420,8 +1416,10 @@ else if (log.isDebugEnabled()) /** * @param tx Transaction to clear. */ - private void clearThreadMap(IgniteInternalTx tx) { + public void clearThreadMap(IgniteInternalTx tx) { if (tx.local() && !tx.dht()) { + assert tx instanceof GridNearTxLocal : tx; + if (!tx.system()) threadMap.remove(tx.threadId(), tx); else { @@ -2250,6 +2248,7 @@ public Collection> deadlockDetectionFutures() { * @see #resumeTx(GridNearTxLocal) * @see GridNearTxLocal#suspend() * @see GridNearTxLocal#resume() + * @throws IgniteCheckedException If failed to suspend transaction. */ public void suspendTx(final GridNearTxLocal tx) throws IgniteCheckedException { assert tx != null && !tx.system() : tx; @@ -2273,6 +2272,7 @@ public void suspendTx(final GridNearTxLocal tx) throws IgniteCheckedException { * @see #suspendTx(GridNearTxLocal) * @see GridNearTxLocal#suspend() * @see GridNearTxLocal#resume() + * @throws IgniteCheckedException If failed to resume tx. */ public void resumeTx(GridNearTxLocal tx) throws IgniteCheckedException { assert tx != null && !tx.system() : tx; @@ -2280,7 +2280,7 @@ public void resumeTx(GridNearTxLocal tx) throws IgniteCheckedException { assert !transactionMap(tx).containsValue(tx) : tx; assert !haveSystemTxForThread(Thread.currentThread().getId()); - if(!tx.state(ACTIVE)) { + if (!tx.state(ACTIVE)) { throw new IgniteCheckedException("Trying to resume transaction with incorrect state " + "[expected=" + SUSPENDED + ", actual=" + tx.state() + ']'); } @@ -2288,10 +2288,10 @@ public void resumeTx(GridNearTxLocal tx) throws IgniteCheckedException { long threadId = Thread.currentThread().getId(); if (threadMap.putIfAbsent(threadId, tx) != null) - throw new IgniteCheckedException("Thread already start a transaction."); + throw new IgniteCheckedException("Thread already has started a transaction."); if (transactionMap(tx).putIfAbsent(tx.xidVersion(), tx) != null) - throw new IgniteCheckedException("Thread already start a transaction."); + throw new IgniteCheckedException("Thread already has started a transaction."); tx.threadId(threadId); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java index 8c71f76e0caa5..ff6beb417991e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/timeout/GridTimeoutProcessor.java @@ -84,12 +84,13 @@ public GridTimeoutProcessor(GridKernalContext ctx) { /** * @param timeoutObj Timeout object. + * @return {@code True} if object was added. */ @SuppressWarnings({"NakedNotify", "CallToNotifyInsteadOfNotifyAll"}) - public void addTimeoutObject(GridTimeoutObject timeoutObj) { + public boolean addTimeoutObject(GridTimeoutObject timeoutObj) { if (timeoutObj.endTime() <= 0 || timeoutObj.endTime() == Long.MAX_VALUE) // Timeout will never happen. - return; + return false; boolean added = timeoutObjs.add(timeoutObj); @@ -100,6 +101,8 @@ public void addTimeoutObject(GridTimeoutObject timeoutObj) { mux.notify(); // No need to notifyAll since we only have one thread. } } + + return true; } /** @@ -124,9 +127,10 @@ public CancelableTask schedule(Runnable task, long delay, long period) { /** * @param timeoutObj Timeout object. + * @return {@code True} if timeout object was removed. */ - public void removeTimeoutObject(GridTimeoutObject timeoutObj) { - timeoutObjs.remove(timeoutObj); + public boolean removeTimeoutObject(GridTimeoutObject timeoutObj) { + return timeoutObjs.remove(timeoutObj); } /** @@ -149,13 +153,14 @@ private class TimeoutWorker extends GridWorker { GridTimeoutObject timeoutObj = iter.next(); if (timeoutObj.endTime() <= now) { - iter.remove(); + try { + boolean rmvd = timeoutObjs.remove(timeoutObj); - if (log.isDebugEnabled()) - log.debug("Timeout has occurred: " + timeoutObj); + if (log.isDebugEnabled()) + log.debug("Timeout has occurred [obj=" + timeoutObj + ", process=" + rmvd + ']'); - try { - timeoutObj.onTimeout(); + if (rmvd) + timeoutObj.onTimeout(); } catch (Throwable e) { if (isCancelled() && !(e instanceof Error)){ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTxFastFinishTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTxFastFinishTest.java index 9f4910abddf58..79316bfa457b4 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTxFastFinishTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheTxFastFinishTest.java @@ -24,6 +24,7 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.configuration.NearCacheConfiguration; +import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxFastFinishFuture; import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; import org.apache.ignite.internal.processors.cache.transactions.TransactionProxyImpl; import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; @@ -210,8 +211,7 @@ private void checkFastTxFinish(Transaction tx, boolean commit) { IgniteInternalTx tx0 = ((TransactionProxyImpl)tx).tx(); assertNull(fieldValue(tx0, "prepFut")); - assertNull(fieldValue(tx0, "commitFut")); - assertNull(fieldValue(tx0, "rollbackFut")); + assertTrue(fieldValue(tx0, "finishFut") instanceof GridNearTxFastFinishFuture); } /** @@ -225,12 +225,13 @@ private void checkNormalTxFinish(Transaction tx, boolean commit) { tx.commit(); assertNotNull(fieldValue(tx0, "prepFut")); - assertNotNull(fieldValue(tx0, "commitFut")); + assertNotNull(fieldValue(tx0, "finishFut")); } else { tx.rollback(); - assertNotNull(fieldValue(tx0, "rollbackFut")); + assertNull(fieldValue(tx0, "prepFut")); + assertNotNull(fieldValue(tx0, "finishFut")); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxConfigCacheSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxConfigCacheSelfTest.java index 2efa0cb0b7b2b..f2e17e440ef0c 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxConfigCacheSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxConfigCacheSelfTest.java @@ -185,6 +185,8 @@ protected void checkExplicitTxTimeout(final IgniteCache cache, f try (final Transaction tx = ignite.transactions().txStart()) { assert tx != null; + cache.put("key0", "val0"); + sleepForTxFailure(); cache.put("key", "val"); @@ -195,7 +197,19 @@ protected void checkExplicitTxTimeout(final IgniteCache cache, f assert e.getCause() instanceof TransactionTimeoutException; } + assertNull(ignite.transactions().tx()); + + assert !cache.containsKey("key0"); assert !cache.containsKey("key"); + + // New transaction must succeed. + try (final Transaction tx = ignite.transactions().txStart()) { + cache.put("key", "val"); + + tx.commit(); + } + + assert cache.containsKey("key"); } /** diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheThreadLocalTxTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheThreadLocalTxTest.java new file mode 100644 index 0000000000000..c8eac20782fe5 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheThreadLocalTxTest.java @@ -0,0 +1,223 @@ +/* + * 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; + +import java.util.concurrent.ThreadLocalRandom; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteTransactions; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.util.typedef.G; +import org.apache.ignite.lang.IgniteFuture; +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 static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; + +/** + * + */ +public class IgniteCacheThreadLocalTxTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** */ + private boolean client; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder); + + cfg.setClientMode(client); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + + super.afterTest(); + } + + /** + * @throws Exception If failed. + */ + public void testSingleNode() throws Exception { + threadLocalTx(startGrid(0)); + } + + /** + * @throws Exception If failed. + */ + public void testMultiNode() throws Exception { + startGridsMultiThreaded(4); + + client = true; + + startGrid(4); + + for (Ignite node : G.allGrids()) + threadLocalTx(node); + } + + /** + * @param node Node. + * @throws Exception If failed. + */ + @SuppressWarnings("unchecked") + private void threadLocalTx(Ignite node) throws Exception { + CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME); + + ccfg.setAtomicityMode(TRANSACTIONAL); + ccfg.setBackups(2); + + IgniteCache cache = node.getOrCreateCache(ccfg); + + checkNoTx(node); + + boolean[] reads = {true, false}; + boolean[] writes = {true, false}; + int endOps = 5; + + for (TransactionConcurrency concurrency : TransactionConcurrency.values()) { + for (TransactionIsolation isolation : TransactionIsolation.values()) { + for (boolean read : reads) { + for (boolean write : writes) { + for (int i = 0; i < endOps; i++) + checkTx(concurrency, isolation, node, cache, read, write, i); + } + } + } + } + + checkNoTx(node); + + cache.put(1, 1); + + checkNoTx(node); + } + + /** + * @param concurrency Tx concurrency. + * @param isolation Tx isolation. + * @param node Node. + * @param cache Cache. + * @param read {@code True} if read in tx. + * @param write {@code True} if write in tx. + * @param endOp Operation to test. + */ + private void checkTx(TransactionConcurrency concurrency, + TransactionIsolation isolation, + Ignite node, + IgniteCache cache, + boolean read, + boolean write, + int endOp) { + IgniteTransactions txs = node.transactions(); + + checkNoTx(node); + + Transaction tx = txs.txStart(concurrency, isolation); + + assertEquals(tx, txs.tx()); + + try { + txs.txStart(concurrency, isolation); + + fail(); + } + catch (IllegalStateException expected) { + // No-op. + } + + if (read) + cache.get(ThreadLocalRandom.current().nextInt(100_000)); + + if (write) + cache.put(ThreadLocalRandom.current().nextInt(100_000), 1); + + + try { + txs.txStart(concurrency, isolation); + + fail(); + } + catch (IllegalStateException expected) { + // No-op. + } + + assertEquals(tx, txs.tx()); + + IgniteFuture fut = null; + + switch (endOp) { + case 0: + tx.commit(); + + break; + + case 1: + fut = tx.commitAsync(); + + break; + + case 2: + tx.rollback(); + + break; + + case 3: + fut = tx.rollbackAsync(); + + break; + + case 4: + tx.close(); + + break; + + default: + fail(); + } + + if (fut != null) + fut.get(); + + checkNoTx(node); + } + + /** + * @param node Node. + */ + private void checkNoTx(Ignite node) { + IgniteTransactions txs = node.transactions(); + + assertNull(txs.tx()); + assertNull(((IgniteKernal)node).context().cache().context().tm().tx()); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java index d16aebd61ca9e..51e39a7f46897 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteOptimisticTxSuspendResumeTest.java @@ -45,7 +45,6 @@ import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC; import static org.apache.ignite.transactions.TransactionState.ACTIVE; import static org.apache.ignite.transactions.TransactionState.COMMITTED; -import static org.apache.ignite.transactions.TransactionState.MARKED_ROLLBACK; import static org.apache.ignite.transactions.TransactionState.ROLLED_BACK; import static org.apache.ignite.transactions.TransactionState.SUSPENDED; @@ -59,6 +58,7 @@ public class IgniteOptimisticTxSuspendResumeTest extends GridCommonAbstractTest /** Future timeout */ private static final int FUT_TIMEOUT = 5000; + /** */ private boolean client = false; /** @@ -438,7 +438,7 @@ public void testTxTimeoutOnResumed() throws Exception { } }, TransactionTimeoutException.class); - assertEquals(MARKED_ROLLBACK, tx.state()); + assertEquals(ROLLED_BACK, tx.state()); tx.close(); } @@ -469,7 +469,7 @@ public void testTxTimeoutOnSuspend() throws Exception { } }, TransactionTimeoutException.class); - assertEquals(MARKED_ROLLBACK, tx.state()); + assertEquals(ROLLED_BACK, tx.state()); tx.close(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/CachePartitionedMultiNodeLongTxTimeout2FullApiTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/CachePartitionedMultiNodeLongTxTimeout2FullApiTest.java new file mode 100644 index 0000000000000..aef63d0e6f819 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/CachePartitionedMultiNodeLongTxTimeout2FullApiTest.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.distributed.near; + +import org.apache.ignite.configuration.IgniteConfiguration; + +/** + * + */ +public class CachePartitionedMultiNodeLongTxTimeout2FullApiTest extends GridCachePartitionedMultiNodeFullApiSelfTest { + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.getTransactionConfiguration().setDefaultTxTimeout(Long.MAX_VALUE / 2); + + return cfg; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNearCacheTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNearCacheTest.java new file mode 100644 index 0000000000000..7c1a6dd6fc1e6 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNearCacheTest.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.transactions; + +/** + * + */ +public class TxRollbackOnTimeoutNearCacheTest extends TxRollbackOnTimeoutTest { + /** {@inheritDoc} */ + @Override protected boolean nearCacheEnabled() { + return true; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNoDeadlockDetectionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNoDeadlockDetectionTest.java new file mode 100644 index 0000000000000..51233294f41ff --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutNoDeadlockDetectionTest.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.cache.transactions; + +import org.apache.ignite.transactions.TransactionTimeoutException; + +import static org.apache.ignite.IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_MAX_ITERS; + +/** + * Tests an ability to eagerly rollback timed out transactions. + */ +public class TxRollbackOnTimeoutNoDeadlockDetectionTest extends TxRollbackOnTimeoutTest { + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + System.setProperty(IGNITE_TX_DEADLOCK_DETECTION_MAX_ITERS, "0"); + + super.beforeTestsStarted(); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + super.afterTestsStopped(); + + System.clearProperty(IGNITE_TX_DEADLOCK_DETECTION_MAX_ITERS); + } + + /** */ + @Override protected void validateDeadlockException(Exception e) { + assertEquals("TimeoutException is expected", + TransactionTimeoutException.class, e.getCause().getClass()); + } +} \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java new file mode 100644 index 0000000000000..e1c6c1079666c --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java @@ -0,0 +1,655 @@ +/* + * 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.transactions; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import javax.cache.CacheException; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.NearCacheConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.IgniteInternalFuture; +import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; +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.U; +import org.apache.ignite.lang.IgniteInClosure; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +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.TransactionDeadlockException; +import org.apache.ignite.transactions.TransactionIsolation; +import org.apache.ignite.transactions.TransactionTimeoutException; + +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; + +/** + * Tests an ability to eagerly rollback timed out transactions. + */ +public class TxRollbackOnTimeoutTest extends GridCommonAbstractTest { + /** */ + private static final long TX_MIN_TIMEOUT = 1; + + /** */ + private static final String CACHE_NAME = "test"; + + /** IP finder. */ + private static final TcpDiscoveryVmIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private static final int GRID_CNT = 3; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER); + + boolean client = "client".equals(igniteInstanceName); + + cfg.setClientMode(client); + + if (!client) { + CacheConfiguration ccfg = new CacheConfiguration(CACHE_NAME); + + if (nearCacheEnabled()) + ccfg.setNearConfiguration(new NearCacheConfiguration()); + + ccfg.setAtomicityMode(TRANSACTIONAL); + ccfg.setBackups(2); + ccfg.setWriteSynchronizationMode(FULL_SYNC); + + cfg.setCacheConfiguration(ccfg); + } + + return cfg; + } + + /** + * @return Near cache flag. + */ + protected boolean nearCacheEnabled() { + return false; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + startGridsMultiThreaded(GRID_CNT); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + stopAllGrids(); + } + + /** + * @throws Exception If f nodeailed. + * @return Started client. + */ + private Ignite startClient() throws Exception { + Ignite client = startGrid("client"); + + assertTrue(client.configuration().isClientMode()); + + if (nearCacheEnabled()) + client.createNearCache(CACHE_NAME, new NearCacheConfiguration<>()); + else + assertNotNull(client.cache(CACHE_NAME)); + + return client; + } + + /** + * @param e Exception. + */ + protected void validateDeadlockException(Exception e) { + assertEquals("Deadlock report is expected", + TransactionDeadlockException.class, e.getCause().getCause().getClass()); + } + + /** + * @throws Exception If failed. + */ + public void testLockAndConcurrentTimeout() throws Exception { + startClient(); + + for (Ignite node : G.allGrids()) { + log.info("Test with node: " + node.name()); + + lock(node, false); + + lock(node, false); + + lock(node, true); + } + } + + /** + * @param node Node. + * @param retry {@code True} + * @throws Exception If failed. + */ + private void lock(final Ignite node, final boolean retry) throws Exception { + final IgniteCache cache = node.cache(CACHE_NAME); + + final int KEYS_PER_THREAD = 10_000; + + GridTestUtils.runMultiThreaded(new IgniteInClosure() { + @Override public void apply(Integer idx) { + int start = idx * KEYS_PER_THREAD; + int end = start + KEYS_PER_THREAD; + + int locked = 0; + + try { + try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 500, 0)) { + for (int i = start; i < end; i++) { + cache.get(i); + + locked++; + } + + tx.commit(); + } + } + catch (Exception e) { + info("Expected error: " + e); + } + + info("Done, locked: " + locked); + + if (retry) { + try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 10 * 60_000, 0)) { + for (int i = start; i < end; i++) + cache.get(i); + + cache.put(start, 0); + + tx.commit(); + } + } + } + }, Math.min(4, Runtime.getRuntime().availableProcessors()), "tx-thread"); + } + + /** + * Tests if timeout on first tx unblocks second tx waiting for the locked key. + * + * @throws Exception If failed. + */ + public void testWaitingTxUnblockedOnTimeout() throws Exception { + waitingTxUnblockedOnTimeout(grid(0), grid(0)); + + waitingTxUnblockedOnTimeout(grid(0), grid(1)); + + Ignite client = startClient(); + + waitingTxUnblockedOnTimeout(grid(0), client); + + waitingTxUnblockedOnTimeout(grid(1), client); + + waitingTxUnblockedOnTimeout(client, grid(0)); + + waitingTxUnblockedOnTimeout(client, grid(1)); + + waitingTxUnblockedOnTimeout(client, client); + } + + /** + * Tests if timeout on first tx unblocks second tx waiting for the locked key. + * + * @throws Exception If failed. + */ + public void testWaitingTxUnblockedOnThreadDeath() throws Exception { + waitingTxUnblockedOnThreadDeath(grid(0), grid(0)); + + waitingTxUnblockedOnThreadDeath(grid(0), grid(1)); + + Ignite client = startClient(); + + waitingTxUnblockedOnThreadDeath(grid(0), client); + + waitingTxUnblockedOnThreadDeath(grid(1), client); + + waitingTxUnblockedOnThreadDeath(client, grid(0)); + + waitingTxUnblockedOnThreadDeath(client, grid(1)); + + waitingTxUnblockedOnThreadDeath(client, client); + } + + /** + * Tests if deadlock is resolved on timeout with correct message. + * + * @throws Exception If failed. + */ + public void testDeadlockUnblockedOnTimeout() throws Exception { + deadlockUnblockedOnTimeout(ignite(0), ignite(1)); + + deadlockUnblockedOnTimeout(ignite(0), ignite(0)); + + Ignite client = startClient(); + + deadlockUnblockedOnTimeout(ignite(0), client); + + deadlockUnblockedOnTimeout(client, ignite(0)); + } + + /** + * Tests if deadlock is resolved on timeout with correct message. + * + * @param node1 First node. + * @param node2 Second node. + * @throws Exception If failed. + */ + private void deadlockUnblockedOnTimeout(final Ignite node1, final Ignite node2) throws Exception { + info("Start test [node1=" + node1.name() + ", node2=" + node2.name() + ']'); + + final CountDownLatch l = new CountDownLatch(2); + + IgniteInternalFuture fut1 = GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + try { + try (Transaction tx = node1.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 5000, 2)) { + node1.cache(CACHE_NAME).put(1, 10); + + l.countDown(); + + U.awaitQuiet(l); + + node1.cache(CACHE_NAME).put(2, 20); + + tx.commit(); + + fail(); + } + } + catch (CacheException e) { + // No-op. + validateDeadlockException(e); + } + } + }, "First"); + + IgniteInternalFuture fut2 = GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + try (Transaction tx = node2.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 0, 2)) { + node2.cache(CACHE_NAME).put(2, 2); + + l.countDown(); + + U.awaitQuiet(l); + + node2.cache(CACHE_NAME).put(1, 1); + + tx.commit(); + } + } + }, "Second"); + + fut1.get(); + fut2.get(); + + assertTrue("Expecting committed key 2", node1.cache(CACHE_NAME).get(2) != null); + assertTrue("Expecting committed key 1", node1.cache(CACHE_NAME).get(1) != null); + + node1.cache(CACHE_NAME).removeAll(F.asSet(1, 2)); + } + + /** + * Tests timeout object cleanup on tx commit. + * + * @throws Exception If failed. + */ + public void testTimeoutRemoval() throws Exception { + IgniteEx client = (IgniteEx)startClient(); + + final long TX_TIMEOUT = 250; + + final int modesCnt = 5; + + for (int i = 0; i < modesCnt; i++) + testTimeoutRemoval0(grid(0), i, TX_TIMEOUT); + + for (int i = 0; i < modesCnt; i++) + testTimeoutRemoval0(client, i, TX_TIMEOUT); + + for (int i = 0; i < modesCnt; i++) + testTimeoutRemoval0(grid(0), i, TX_MIN_TIMEOUT); + + for (int i = 0; i < modesCnt; i++) + testTimeoutRemoval0(client, i, TX_MIN_TIMEOUT); + + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + + // Repeat with more iterations to make sure everything is cleared. + for (int i = 0; i < 500; i++) + testTimeoutRemoval0(client, rnd.nextInt(modesCnt), TX_MIN_TIMEOUT); + } + + /** + * Tests timeouts in all tx configurations. + * + * @throws Exception If failed. + */ + public void testSimple() throws Exception { + for (TransactionConcurrency concurrency : TransactionConcurrency.values()) + for (TransactionIsolation isolation : TransactionIsolation.values()) { + for (int op = 0; op < 4; op++) + testSimple0(concurrency, isolation, op); + } + } + + /** + * @param concurrency Concurrency. + * @param isolation Isolation. + * @param op Operation to test. + * @throws Exception If failed. + */ + private void testSimple0(TransactionConcurrency concurrency, TransactionIsolation isolation, int op) throws Exception { + Ignite near = grid(0); + + final int key = 1, val = 1; + + final long TX_TIMEOUT = 250; + + IgniteCache cache = near.cache(CACHE_NAME); + + try (Transaction tx = near.transactions().txStart(concurrency, isolation, TX_TIMEOUT, 1)) { + cache.put(key, val); + + U.sleep(TX_TIMEOUT * 2); + + try { + switch (op) { + case 0: + cache.put(key + 1, val); + + break; + + case 1: + cache.remove(key + 1); + + break; + + case 2: + cache.get(key + 1); + + break; + + case 3: + tx.commit(); + + break; + + default: + fail(); + } + + fail("Tx must timeout"); + } + catch (CacheException | IgniteException e) { + assertTrue("Expected exception: " + e, X.hasCause(e, TransactionTimeoutException.class)); + } + } + + assertFalse("Must be removed by rollback on timeout", near.cache(CACHE_NAME).containsKey(key)); + assertFalse("Must be removed by rollback on timeout", near.cache(CACHE_NAME).containsKey(key + 1)); + + assertNull(near.transactions().tx()); + } + + /** + * @param near Node. + * @param mode Test mode. + * + * @param timeout Tx timeout. + * @throws Exception If failed. + */ + private void testTimeoutRemoval0(IgniteEx near, int mode, long timeout) throws Exception { + Throwable saved = null; + + try (Transaction tx = near.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, timeout, 1)) { + near.cache(CACHE_NAME).put(1, 1); + + switch (mode) { + case 0: + tx.commit(); + break; + + case 1: + tx.commitAsync().get(); + break; + + case 2: + tx.rollback(); + break; + + case 3: + tx.rollbackAsync().get(); + break; + + case 4: + break; + + default: + fail(); + } + } + catch (Throwable t) { + saved = t; + } + + Collection set = U.field(near.context().cache().context().time(), "timeoutObjs"); + + for (Object obj : set) { + if (obj.getClass().isAssignableFrom(GridNearTxLocal.class)) { + log.error("Last saved exception: " + saved, saved); + + fail("Not removed [mode=" + mode + ", timeout=" + timeout + ", tx=" + obj +']'); + } + } + } + + /** + * @param near Node starting tx which is timed out. + * @param other Node starting second tx. + * @throws Exception If failed. + */ + private void waitingTxUnblockedOnTimeout(final Ignite near, final Ignite other) throws Exception { + waitingTxUnblockedOnTimeout(near, other, 1000); + + waitingTxUnblockedOnTimeout(near, other, 50); + } + + /** + * @param near Node starting tx which is timed out. + * @param other Node starting second tx. + * @param timeout Timeout. + * @throws Exception If failed. + */ + private void waitingTxUnblockedOnTimeout(final Ignite near, final Ignite other, final long timeout) throws Exception { + info("Start test [node1=" + near.name() + ", node2=" + other.name() + ']'); + + final CountDownLatch blocked = new CountDownLatch(1); + + final CountDownLatch unblocked = new CountDownLatch(1); + + final int recordsCnt = 5; + + IgniteInternalFuture fut1 = GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + try (Transaction tx = near.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, timeout, 0)) { + try { + for (int i = 0; i < recordsCnt; i++) + near.cache(CACHE_NAME).put(i, i); + + info("Locked all keys."); + } + catch (CacheException e) { + info("Failed to lock keys: " + e); + } + finally { + blocked.countDown(); + } + + // Will be unblocked after tx timeout occurs. + U.awaitQuiet(unblocked); + + try { + near.cache(CACHE_NAME).put(0, 0); + + fail(); + } + catch (CacheException e) { + log.info("Expecting error: " + e.getMessage()); + } + + try { + tx.commit(); + + fail(); + } + catch (IgniteException e) { + log.info("Expecting error: " + e.getMessage()); + } + } + + // Check thread is able to start new tx. + try (Transaction tx = near.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 60_000, 0)) { + for (int i = 0; i < recordsCnt; i++) + near.cache(CACHE_NAME).put(i, i); + + tx.commit(); + } + } + }, "First"); + + IgniteInternalFuture fut2 = GridTestUtils.runAsync(new Runnable() { + @Override public void run() { + U.awaitQuiet(blocked); + + try (Transaction tx = other.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 0, 1)) { + for (int i = 0; i < recordsCnt; i++) + other.cache(CACHE_NAME).put(i, i); + + // Will wait until timeout on first tx will unblock put. + tx.commit(); + } + } + }, "Second"); + + fut2.get(); + + unblocked.countDown(); + + fut1.get(); + } + + /** + * @param near Node starting tx which is timed out. + * @param other Node starting second tx. + * @throws Exception If failed. + */ + private void waitingTxUnblockedOnThreadDeath(final Ignite near, final Ignite other) throws Exception { + waitingTxUnblockedOnThreadDeath0(near, other, 10, 1000); // Try provoke timeout after all keys are locked. + + waitingTxUnblockedOnThreadDeath0(near, other, 1000, 100); // Try provoke timeout while trying to lock keys. + } + + /** + * @param near Node starting tx which is timed out. + * @param other Node starting second tx. + * @param recordsCnt Number of records to locks. + * @param timeout Transaction timeout. + * @throws Exception If failed. + */ + private void waitingTxUnblockedOnThreadDeath0(final Ignite near, + final Ignite other, + final int recordsCnt, + final long timeout) + throws Exception + { + info("Start test [node1=" + near.name() + ", node2=" + other.name() + ']'); + + final CountDownLatch blocked = new CountDownLatch(1); + + IgniteInternalFuture fut1 = multithreadedAsync(new Runnable() { + @Override public void run() { + near.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, timeout, recordsCnt); + + try { + for (int i = 0; i < recordsCnt; i++) + near.cache(CACHE_NAME).put(i, i); + + log.info("Locked all records."); + } + catch (Exception e) { + log.info("Failed to locked all records: " + e); + } + finally { + blocked.countDown(); + } + + throw new IgniteException("Failure"); + } + }, 1, "First"); + + IgniteInternalFuture fut2 = multithreadedAsync(new Runnable() { + @Override public void run() { + U.awaitQuiet(blocked); + + try (Transaction tx = other.transactions().txStart(PESSIMISTIC, REPEATABLE_READ, 0, recordsCnt)) { + for (int i = 0; i < recordsCnt; i++) + other.cache(CACHE_NAME).put(i, i); + + // Will wait until timeout on first tx will unblock put. + tx.commit(); + } + } + }, 1, "Second"); + + try { + fut1.get(); + + fail(); + } + catch (IgniteCheckedException e) { + // No-op. + } + + fut2.get(); + } +} \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java index 164ff6a551a7b..b380ebc7a89c8 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java @@ -31,6 +31,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledOnheapFullApiSelfTest; import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledOnheapMultiNodeFullApiSelfTest; +import org.apache.ignite.internal.processors.cache.distributed.near.CachePartitionedMultiNodeLongTxTimeout2FullApiTest; import org.apache.ignite.internal.processors.cache.distributed.near.CachePartitionedMultiNodeLongTxTimeoutFullApiTest; import org.apache.ignite.internal.processors.cache.distributed.near.CachePartitionedNearEnabledMultiNodeLongTxTimeoutFullApiTest; import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicClientOnlyMultiNodeFullApiSelfTest; @@ -123,6 +124,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(GridCacheAtomicMultiNodeP2PDisabledFullApiSelfTest.class); suite.addTestSuite(GridCacheAtomicNearEnabledMultiNodeFullApiSelfTest.class); suite.addTestSuite(CachePartitionedMultiNodeLongTxTimeoutFullApiTest.class); + suite.addTestSuite(CachePartitionedMultiNodeLongTxTimeout2FullApiTest.class); suite.addTestSuite(CachePartitionedNearEnabledMultiNodeLongTxTimeoutFullApiTest.class); suite.addTestSuite(GridCachePartitionedNearDisabledMultiNodeFullApiSelfTest.class); diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java index e3b60fc096601..bdff5688dd589 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java @@ -19,9 +19,16 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.distributed.CacheExchangeMergeTest; +import org.apache.ignite.internal.processors.cache.distributed.CachePartitionStateTest; +import org.apache.ignite.internal.processors.cache.distributed.GridCachePartitionEvictionDuringReadThroughSelfTest; +import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheThreadLocalTxTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeMultiServerTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeTest; import org.apache.ignite.internal.processors.cache.distributed.IgnitePessimisticTxSuspendResumeTest; +import org.apache.ignite.internal.processors.cache.transactions.TxRollbackOnTimeoutNearCacheTest; +import org.apache.ignite.internal.processors.cache.transactions.TxRollbackOnTimeoutNoDeadlockDetectionTest; +import org.apache.ignite.internal.processors.cache.transactions.TxRollbackOnTimeoutTest; + /** * Test suite. @@ -40,6 +47,11 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(CacheExchangeMergeTest.class); + suite.addTestSuite(TxRollbackOnTimeoutTest.class); + suite.addTestSuite(TxRollbackOnTimeoutNoDeadlockDetectionTest.class); + suite.addTestSuite(TxRollbackOnTimeoutNearCacheTest.class); + suite.addTestSuite(IgniteCacheThreadLocalTxTest.class); + return suite; } } diff --git a/modules/hadoop/src/test/java/org/apache/ignite/internal/processors/hadoop/impl/HadoopTxConfigCacheTest.java b/modules/hadoop/src/test/java/org/apache/ignite/internal/processors/hadoop/impl/HadoopTxConfigCacheTest.java index e85baed8404b6..f404732346b83 100644 --- a/modules/hadoop/src/test/java/org/apache/ignite/internal/processors/hadoop/impl/HadoopTxConfigCacheTest.java +++ b/modules/hadoop/src/test/java/org/apache/ignite/internal/processors/hadoop/impl/HadoopTxConfigCacheTest.java @@ -25,11 +25,11 @@ /** * Test checks whether hadoop system cache doesn't use user defined TX config. */ -public class HadoopTxConfigCacheTest extends IgniteTxConfigCacheSelfTest { +public class HadoopTxConfigCacheTest extends IgniteTxConfigCacheSelfTest { /** * Success if system caches weren't timed out. * - * @throws Exception + * @throws Exception If failed. */ public void testSystemCacheTx() throws Exception { final Ignite ignite = grid(0); From 840941f15ac3be382eaf06da1b4874b1422c3bad Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Fri, 22 Sep 2017 17:21:06 +0300 Subject: [PATCH 131/145] ignite-6181 Fix compilation after cherry-pick --- .../processors/cache/GridCacheAdapter.java | 37 +++++++++---------- .../testsuites/IgniteCacheTestSuite6.java | 2 - 2 files changed, 18 insertions(+), 21 deletions(-) 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 a6f60f8ae78af..cdc25e05b6410 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 @@ -84,13 +84,13 @@ import org.apache.ignite.internal.cluster.IgniteClusterEx; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.affinity.GridCacheAffinityImpl; -import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; import org.apache.ignite.internal.processors.cache.distributed.IgniteExternalizableExpiryPolicy; 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.dht.GridDhtTopologyFuture; 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.persistence.CacheDataRow; import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalAdapter; import org.apache.ignite.internal.processors.cache.transactions.IgniteTxLocalEx; @@ -4200,24 +4200,22 @@ protected IgniteInternalFuture asyncOp( return new GridFinishedFuture<>( new IgniteCheckedException("Operation has been cancelled (node is stopping).")); - - return op.op(tx0, opCtx).chain(new CX1, T>() { - @Override - public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { + return op.op(tx0, opCtx).chain(new CX1, T>() { + @Override public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { + try { + return tFut.get(); + } + catch (IgniteTxRollbackCheckedException | NodeStoppingException e) { + throw e; + } + catch (IgniteCheckedException e1) { try { - return tFut.get(); - } - catch (IgniteTxRollbackCheckedException | NodeStoppingExceptione) { - throw e; + tx0.rollbackNearTxLocalAsync(); } - catch (IgniteCheckedException e1) { - try { - tx0.rollbackNearTxLocalAsync(); - } - catch (Throwable e2) { - if (e1 !=e2) + catch (Throwable e2) { + if (e1 != e2) e1.addSuppressed(e2); - } + } throw e1; } @@ -4227,20 +4225,21 @@ public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { } }); } - }); + } + ); saveFuture(holder, f, retry); return f; } - finalIgniteInternalFuture + final IgniteInternalFuture f = op.op(tx, opCtx).chain(new CX1, T>() { @Override public T applyx(IgniteInternalFuture tFut) throws IgniteCheckedException { try { return tFut.get(); } - catch (IgniteTxRollbackCheckedException | NodeStoppingExceptione) { + catch (IgniteTxRollbackCheckedException | NodeStoppingException e) { throw e; } catch (IgniteCheckedException e1) { diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java index bdff5688dd589..56da654653a22 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite6.java @@ -19,8 +19,6 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.distributed.CacheExchangeMergeTest; -import org.apache.ignite.internal.processors.cache.distributed.CachePartitionStateTest; -import org.apache.ignite.internal.processors.cache.distributed.GridCachePartitionEvictionDuringReadThroughSelfTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheThreadLocalTxTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeMultiServerTest; import org.apache.ignite.internal.processors.cache.distributed.IgniteOptimisticTxSuspendResumeTest; From 96c1bff8632bc6e6d34586d62574dcebcd2741c0 Mon Sep 17 00:00:00 2001 From: Alexei Scherbakov Date: Thu, 21 Sep 2017 17:40:16 +0300 Subject: [PATCH 132/145] IGNITE-6228 Avoid closing page store by thread interruption. Fixes #2715 (cherry picked from commit e8379ae) --- .../apache/ignite/IgniteSystemProperties.java | 5 + .../PersistentStoreConfiguration.java | 6 +- .../GridCacheDatabaseSharedManager.java | 65 +++++- .../cache/persistence/file/AsyncFileIO.java | 218 ++++++++++++++++++ .../persistence/file/AsyncFileIOFactory.java | 52 +++++ .../cache/persistence/file/FileIOFactory.java | 25 +- .../cache/persistence/file/FilePageStore.java | 5 +- .../file/FileVersionCheckingFactory.java | 2 +- .../persistence/file/RandomAccessFileIO.java | 48 ++-- .../file/RandomAccessFileIOFactory.java | 14 +- .../wal/AbstractWalRecordsIterator.java | 2 +- .../cache/persistence/wal/FileInput.java | 7 + .../wal/FileWriteAheadLogManager.java | 8 +- .../reader/StandaloneWalRecordsIterator.java | 4 +- .../util/future/GridFutureAdapter.java | 16 ++ .../resources/META-INF/classnames.properties | 2 + .../file/IgnitePdsThreadInterruptionTest.java | 205 ++++++++++++++++ .../db/wal/IgniteWalFlushFailoverTest.java | 22 +- .../db/wal/crc/IgniteDataIntegrityTests.java | 39 ++-- .../IgnitePdsWithIndexingCoreTestSuite.java | 2 + 20 files changed, 665 insertions(+), 82 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIO.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIOFactory.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.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 d3cba2bd3da2a..0cbe9d60aa821 100644 --- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java +++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java @@ -705,6 +705,11 @@ public final class IgniteSystemProperties { */ public static final String IGNITE_ENABLE_FORCIBLE_NODE_KILL = "IGNITE_ENABLE_FORCIBLE_NODE_KILL"; + /** + * If this property is set, then Ignite will use Async File IO factory by default. + */ + public static final String IGNITE_USE_ASYNC_FILE_IO_FACTORY = "IGNITE_USE_ASYNC_FILE_IO_FACTORY"; + /** * Enforces singleton. */ diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java index 888bf42c7e3b6..abca5a598d84d 100644 --- a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java +++ b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java @@ -17,6 +17,8 @@ package org.apache.ignite.configuration; import java.io.Serializable; +import org.apache.ignite.IgniteSystemProperties; +import org.apache.ignite.internal.processors.cache.persistence.file.AsyncFileIOFactory; 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.util.typedef.internal.S; @@ -137,7 +139,9 @@ public class PersistentStoreConfiguration implements Serializable { private boolean alwaysWriteFullPages = DFLT_WAL_ALWAYS_WRITE_FULL_PAGES; /** Factory to provide I/O interface for files */ - private FileIOFactory fileIOFactory = new RandomAccessFileIOFactory(); + private FileIOFactory fileIOFactory = + IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_USE_ASYNC_FILE_IO_FACTORY, false) ? + new AsyncFileIOFactory() : new RandomAccessFileIOFactory(); /** * Number of sub-intervals the whole {@link #setRateTimeInterval(long)} will be split into to calculate 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 67ba6a9032a72..091b8020ee91f 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 @@ -140,6 +140,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static java.nio.file.StandardOpenOption.READ; import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_SKIP_CRC; import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_WAL_REBALANCE_THRESHOLD; @@ -692,6 +693,66 @@ private long[] calculateFragmentSizes(int concLvl, long cacheSize) { throw new IgniteCheckedException("Page eviction is not compatible with persistence: " + plcCfg.getName()); } + /** {@inheritDoc} */ + @Override protected void checkPageSize(MemoryConfiguration memCfg) { + if (memCfg.getPageSize() == 0) { + try { + assert cctx.pageStore() instanceof FilePageStoreManager : + "Invalid page store manager was created: " + cctx.pageStore(); + + Path anyIdxPartFile = IgniteUtils.searchFileRecursively( + ((FilePageStoreManager)cctx.pageStore()).workDir().toPath(), FilePageStoreManager.INDEX_FILE_NAME); + + if (anyIdxPartFile != null) { + memCfg.setPageSize(resolvePageSizeFromPartitionFile(anyIdxPartFile)); + + return; + } + } + catch (IgniteCheckedException | IOException | IllegalArgumentException e) { + U.quietAndWarn(log, "Attempt to resolve pageSize from store files failed: " + e.getMessage()); + + U.quietAndWarn(log, "Default page size will be used: " + MemoryConfiguration.DFLT_PAGE_SIZE + " bytes"); + } + + memCfg.setPageSize(MemoryConfiguration.DFLT_PAGE_SIZE); + } + } + + /** + * @param partFile Partition file. + */ + private int resolvePageSizeFromPartitionFile(Path partFile) throws IOException, IgniteCheckedException { + try (FileIO fileIO = persistenceCfg.getFileIOFactory().create(partFile.toFile())) { + int minimalHdr = FilePageStore.HEADER_SIZE; + + if (fileIO.size() < minimalHdr) + throw new IgniteCheckedException("Partition file is too small: " + partFile); + + ByteBuffer hdr = ByteBuffer.allocate(minimalHdr).order(ByteOrder.LITTLE_ENDIAN); + + while (hdr.remaining() > 0) + fileIO.read(hdr); + + hdr.rewind(); + + hdr.getLong(); // Read signature. + + hdr.getInt(); // Read version. + + hdr.get(); // Read type. + + int pageSize = hdr.getInt(); + + if (pageSize == 2048) { + U.quietAndWarn(log, "You are currently using persistent store with 2K pages (MemoryConfiguration#" + + "pageSize). If you use SSD disk, consider migrating to 4K pages for better IO performance."); + } + + return pageSize; + } + } + /** * @param cancel Cancel flag. */ @@ -881,7 +942,7 @@ private void shutdownCheckpointer(boolean cancel) { checkpointLock.readLock().unlock(); try { - checkpointer.wakeupForCheckpoint(0, "too many dirty pages").cpBeginFut.get(); + checkpointer.wakeupForCheckpoint(0, "too many dirty pages").cpBeginFut.getUninterruptibly(); } catch (IgniteCheckedException e) { throw new IgniteException("Failed to wait for checkpoint begin.", e); @@ -1310,7 +1371,7 @@ else if (type == CheckpointEntryType.END && ts > lastEndTs) { private WALPointer readPointer(File cpMarkerFile, ByteBuffer buf) throws IgniteCheckedException { buf.position(0); - try (FileChannel ch = FileChannel.open(cpMarkerFile.toPath(), StandardOpenOption.READ)) { + try (FileChannel ch = FileChannel.open(cpMarkerFile.toPath(), READ)) { ch.read(buf); buf.flip(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIO.java new file mode 100644 index 0000000000000..8fad7a5109d01 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIO.java @@ -0,0 +1,218 @@ +/* + * 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.file; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.CompletionHandler; +import java.nio.file.OpenOption; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.util.GridConcurrentHashSet; +import org.apache.ignite.internal.util.future.GridFutureAdapter; + +/** + * File I/O implementation based on {@link AsynchronousFileChannel}. + */ +public class AsyncFileIO implements FileIO { + /** + * File channel associated with {@code file} + */ + private final AsynchronousFileChannel ch; + + /** + * Channel's position. + */ + private volatile long position; + + /** */ + private final ThreadLocal holder; + + /** */ + private GridConcurrentHashSet asyncFuts = new GridConcurrentHashSet<>(); + + /** + * Creates I/O implementation for specified {@code file} + * @param file Random access file + * @param modes Open modes. + */ + public AsyncFileIO(File file, ThreadLocal holder, OpenOption... modes) throws IOException { + this.ch = AsynchronousFileChannel.open(file.toPath(), modes); + + this.holder = holder; + } + + /** {@inheritDoc} */ + @Override public long position() throws IOException { + return position; + } + + /** {@inheritDoc} */ + @Override public void position(long newPosition) throws IOException { + this.position = newPosition; + } + + /** {@inheritDoc} */ + @Override public int read(ByteBuffer destinationBuffer) throws IOException { + ChannelOpFuture fut = holder.get(); + fut.reset(); + + ch.read(destinationBuffer, position, this, fut); + + try { + return fut.getUninterruptibly(); + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + } + + /** {@inheritDoc} */ + @Override public int read(ByteBuffer destinationBuffer, long position) throws IOException { + ChannelOpFuture fut = holder.get(); + fut.reset(); + + ch.read(destinationBuffer, position, null, fut); + + try { + return fut.getUninterruptibly(); + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + finally { + asyncFuts.remove(fut); + } + } + + /** {@inheritDoc} */ + @Override public int read(byte[] buffer, int offset, int length) throws IOException { + ChannelOpFuture fut = holder.get(); + fut.reset(); + + ch.read(ByteBuffer.wrap(buffer, offset, length), position, this, fut); + + try { + return fut.getUninterruptibly(); + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + } + + /** {@inheritDoc} */ + @Override public int write(ByteBuffer sourceBuffer) throws IOException { + ChannelOpFuture fut = holder.get(); + fut.reset(); + + ch.write(sourceBuffer, position, this, fut); + + try { + return fut.getUninterruptibly(); + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + } + + /** {@inheritDoc} */ + @Override public int write(ByteBuffer sourceBuffer, long position) throws IOException { + ChannelOpFuture fut = holder.get(); + fut.reset(); + + asyncFuts.add(fut); + + ch.write(sourceBuffer, position, null, fut); + + try { + return fut.getUninterruptibly(); + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + finally { + asyncFuts.remove(fut); + } + } + + /** {@inheritDoc} */ + @Override public void write(byte[] buffer, int offset, int length) throws IOException { + ChannelOpFuture fut = holder.get(); + fut.reset(); + + ch.write(ByteBuffer.wrap(buffer, offset, length), position, this, fut); + + try { + fut.getUninterruptibly(); + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + } + + /** {@inheritDoc} */ + @Override public void force() throws IOException { + ch.force(false); + } + + /** {@inheritDoc} */ + @Override public long size() throws IOException { + return ch.size(); + } + + /** {@inheritDoc} */ + @Override public void clear() throws IOException { + ch.truncate(0); + + this.position = 0; + } + + /** {@inheritDoc} */ + @Override public void close() throws IOException { + for (ChannelOpFuture asyncFut : asyncFuts) { + try { + asyncFut.getUninterruptibly(); // Ignore interrupts while waiting for channel close. + } + catch (IgniteCheckedException e) { + throw new IOException(e); + } + } + + ch.close(); + } + + /** */ + static class ChannelOpFuture extends GridFutureAdapter implements CompletionHandler { + /** {@inheritDoc} */ + @Override public void completed(Integer result, AsyncFileIO attachment) { + if (attachment != null) { + if (result != -1) + attachment.position += result; + } + + // Release waiter and allow next operation to begin. + super.onDone(result, null); + } + + /** {@inheritDoc} */ + @Override public void failed(Throwable exc, AsyncFileIO attachment) { + super.onDone(exc); + } + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIOFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIOFactory.java new file mode 100644 index 0000000000000..0fb30520b94ac --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/AsyncFileIOFactory.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.persistence.file; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.file.OpenOption; + +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; + +/** + * File I/O factory which uses {@link AsynchronousFileChannel} based implementation of FileIO. + */ +public class AsyncFileIOFactory implements FileIOFactory { + /** */ + private static final long serialVersionUID = 0L; + + /** {@inheritDoc} */ + @Override public FileIO create(File file) throws IOException { + return create(file, CREATE, READ, WRITE); + } + + /** */ + private ThreadLocal holder = new ThreadLocal() { + @Override protected AsyncFileIO.ChannelOpFuture initialValue() { + return new AsyncFileIO.ChannelOpFuture(); + } + }; + + /** {@inheritDoc} */ + @Override public FileIO create(File file, OpenOption... modes) throws IOException { + return new AsyncFileIO(file, holder, modes); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileIOFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileIOFactory.java index 0ffc65373a2fa..c3a75f5310e4f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileIOFactory.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileIOFactory.java @@ -20,26 +20,29 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.nio.file.OpenOption; +/** + * {@link FileIO} factory definition. + */ public interface FileIOFactory extends Serializable { - /** - * Creates I/O interface for file with default I/O mode + * Creates I/O interface for file with default I/O mode. * - * @param file File - * @return File I/O interface - * @throws IOException If I/O interface creation was failed + * @param file File. + * @return File I/O interface. + * @throws IOException If I/O interface creation was failed. */ - FileIO create(File file) throws IOException; + public FileIO create(File file) throws IOException; /** - * Creates I/O interface for file with specified mode + * Creates I/O interface for file with specified mode. * * @param file File - * @param mode I/O mode in - * @return File I/O interface - * @throws IOException If I/O interface creation was failed + * @param modes Open modes. + * @return File I/O interface. + * @throws IOException If I/O interface creation was failed. */ - FileIO create(File file, String mode) throws IOException; + public FileIO create(File file, OpenOption... modes) throws IOException; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java index e6c5379a4b5b9..fa38b512b1edb 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java @@ -35,6 +35,9 @@ import org.apache.ignite.internal.processors.cache.persistence.wal.crc.PureJavaCrc32; import org.apache.ignite.internal.util.typedef.internal.U; +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_PDS_SKIP_CRC; /** @@ -400,7 +403,7 @@ private void init() throws IgniteCheckedException { IgniteCheckedException err = null; try { - this.fileIO = fileIO = ioFactory.create(cfgFile, "rw"); + this.fileIO = fileIO = ioFactory.create(cfgFile, CREATE, READ, WRITE); if (cfgFile.length() == 0) allocated.set(initFile()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java index 53bd802c77aae..40870dce4b63a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FileVersionCheckingFactory.java @@ -55,7 +55,7 @@ public FileVersionCheckingFactory( if (!file.exists()) return createPageStore(type, file, latestVersion()); - try (FileIO fileIO = fileIOFactory.create(file, "r")) { + try (FileIO fileIO = fileIOFactory.create(file)) { int minHdr = FilePageStore.HEADER_SIZE; if (fileIO.size() < minHdr) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIO.java index 73a560a19602c..55849fe0cf0b5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIO.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIO.java @@ -17,94 +17,88 @@ package org.apache.ignite.internal.processors.cache.persistence.file; +import java.io.File; import java.io.IOException; -import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import java.nio.file.OpenOption; /** - * File I/O implementation based on {@code java.io.RandomAccessFile}. + * File I/O implementation based on {@link FileChannel}. */ public class RandomAccessFileIO implements FileIO { - - /** - * Random access file associated with this I/O - */ - private final RandomAccessFile file; - /** - * File channel associated with {@code file} + * File channel. */ - private final FileChannel channel; + private final FileChannel ch; /** * Creates I/O implementation for specified {@code file} * - * @param file Random access file + * @param file File. + * @param modes Open modes. */ - public RandomAccessFileIO(RandomAccessFile file) { - this.file = file; - this.channel = file.getChannel(); + public RandomAccessFileIO(File file, OpenOption... modes) throws IOException { + ch = FileChannel.open(file.toPath(), modes); } /** {@inheritDoc} */ @Override public long position() throws IOException { - return channel.position(); + return ch.position(); } /** {@inheritDoc} */ @Override public void position(long newPosition) throws IOException { - channel.position(newPosition); + ch.position(newPosition); } /** {@inheritDoc} */ @Override public int read(ByteBuffer destinationBuffer) throws IOException { - return channel.read(destinationBuffer); + return ch.read(destinationBuffer); } /** {@inheritDoc} */ @Override public int read(ByteBuffer destinationBuffer, long position) throws IOException { - return channel.read(destinationBuffer, position); + return ch.read(destinationBuffer, position); } /** {@inheritDoc} */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { - return file.read(buffer, offset, length); + return ch.read(ByteBuffer.wrap(buffer, offset, length)); } /** {@inheritDoc} */ @Override public int write(ByteBuffer sourceBuffer) throws IOException { - return channel.write(sourceBuffer); + return ch.write(sourceBuffer); } /** {@inheritDoc} */ @Override public int write(ByteBuffer sourceBuffer, long position) throws IOException { - return channel.write(sourceBuffer, position); + return ch.write(sourceBuffer, position); } /** {@inheritDoc} */ @Override public void write(byte[] buffer, int offset, int length) throws IOException { - file.write(buffer, offset, length); + ch.write(ByteBuffer.wrap(buffer, offset, length)); } /** {@inheritDoc} */ @Override public void force() throws IOException { - channel.force(false); + ch.force(false); } /** {@inheritDoc} */ @Override public long size() throws IOException { - return channel.size(); + return ch.size(); } /** {@inheritDoc} */ @Override public void clear() throws IOException { - channel.position(0); - file.setLength(0); + ch.truncate(0); } /** {@inheritDoc} */ @Override public void close() throws IOException { - file.close(); + ch.close(); } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIOFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIOFactory.java index 6b731f2ad7e8f..856ba1c749b45 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIOFactory.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/RandomAccessFileIOFactory.java @@ -19,7 +19,11 @@ import java.io.File; import java.io.IOException; -import java.io.RandomAccessFile; +import java.nio.file.OpenOption; + +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; /** * File I/O factory which provides RandomAccessFileIO implementation of FileIO. @@ -30,13 +34,11 @@ public class RandomAccessFileIOFactory implements FileIOFactory { /** {@inheritDoc} */ @Override public FileIO create(File file) throws IOException { - return create(file, "rw"); + return create(file, CREATE, READ, WRITE); } /** {@inheritDoc} */ - @Override public FileIO create(File file, String mode) throws IOException { - RandomAccessFile rf = new RandomAccessFile(file, mode); - - return new RandomAccessFileIO(rf); + @Override public FileIO create(File file, OpenOption... modes) throws IOException { + return new RandomAccessFileIO(file, modes); } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java index db949c32e271a..051843d2d8a5a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalRecordsIterator.java @@ -243,7 +243,7 @@ protected FileWriteAheadLogManager.ReadFileHandle initReadHandle( @Nullable final FileWALPointer start) throws IgniteCheckedException, FileNotFoundException { try { - FileIO fileIO = ioFactory.create(desc.file, "r"); + FileIO fileIO = ioFactory.create(desc.file); try { FileInput in = new FileInput(fileIO, buf); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileInput.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileInput.java index 74edbfa3f9944..3b20fce9a754b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileInput.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileInput.java @@ -62,6 +62,13 @@ public FileInput(FileIO io, ByteBufferExpander buf) throws IOException { clearBuffer(); } + /** + * File I/O. + */ + public FileIO io() { + return io; + } + /** * Clear buffer. */ 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 a9327a0d99936..73a4639622c9b 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 @@ -78,6 +78,10 @@ import org.jetbrains.annotations.NotNull; 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; + /** * File WAL manager. */ @@ -939,7 +943,7 @@ private void formatFile(File file) throws IgniteCheckedException { if (log.isDebugEnabled()) log.debug("Formatting file [exists=" + file.exists() + ", file=" + file.getAbsolutePath() + ']'); - try (FileIO fileIO = ioFactory.create(file, "rw")) { + try (FileIO fileIO = ioFactory.create(file, CREATE, READ, WRITE)) { int left = psCfg.getWalSegmentSize(); if (mode == WALMode.DEFAULT) { @@ -1356,7 +1360,7 @@ private SegmentArchiveResult archiveSegment(long absIdx) throws IgniteCheckedExc Files.move(dstTmpFile.toPath(), dstFile.toPath()); if (mode == WALMode.DEFAULT) { - try (FileIO f0 = ioFactory.create(dstFile, "rw")) { + try (FileIO f0 = ioFactory.create(dstFile, CREATE, READ, WRITE)) { f0.force(); } } 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 cd0f8ab3651b8..2aa7b6b3e217c 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 @@ -160,7 +160,7 @@ private void init( /** * This methods checks all provided files to be correct WAL segment. - * Header record and its position is checked. WAL position is used to deremine real index. + * Header record and its position is checked. WAL position is used to determine real index. * File index from file name is ignored. * * @param allFiles files to scan @@ -181,7 +181,7 @@ private List scanIndexesFromFileHeaders FileWALPointer ptr; try ( - FileIO fileIO = ioFactory.create(file, "r"); + FileIO fileIO = ioFactory.create(file); ByteBufferExpander buf = new ByteBufferExpander(HEADER_RECORD_SIZE, ByteOrder.nativeOrder()) ) { final DataInput in = new FileInput(fileIO, buf); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridFutureAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridFutureAdapter.java index 323babda95171..f8c0b14bcba13 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridFutureAdapter.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/future/GridFutureAdapter.java @@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.concurrent.locks.LockSupport; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.IgniteFutureCancelledCheckedException; import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException; @@ -498,6 +499,21 @@ protected boolean onDone(@Nullable R res, @Nullable Throwable err, boolean cance } } + /** + * Resets future for subsequent reuse. + */ + public void reset() { + final Object oldState = state; + + if (oldState == INIT) + return; + + if (!isDone(oldState)) + throw new IgniteException("Illegal state"); + + compareAndSetState(oldState, INIT); + } + /** * Callback to notify that future is cancelled. * diff --git a/modules/core/src/main/resources/META-INF/classnames.properties b/modules/core/src/main/resources/META-INF/classnames.properties index 2fb8f4b38d054..6582649346444 100644 --- a/modules/core/src/main/resources/META-INF/classnames.properties +++ b/modules/core/src/main/resources/META-INF/classnames.properties @@ -2090,3 +2090,5 @@ org.apache.ignite.transactions.TransactionRollbackException org.apache.ignite.transactions.TransactionState org.apache.ignite.transactions.TransactionTimeoutException org.apache.ignite.util.AttributeNodeFilter +org.apache.ignite.internal.processors.cache.persistence.file.AsyncFileIO +org.apache.ignite.internal.processors.cache.persistence.file.AsyncFileIOFactory \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.java new file mode 100644 index 0000000000000..aab569a07534a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.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.persistence.db.file; + +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.MemoryConfiguration; +import org.apache.ignite.configuration.MemoryPolicyConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +import org.apache.ignite.configuration.WALMode; +import org.apache.ignite.internal.processors.cache.persistence.file.AsyncFileIOFactory; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.jsr166.ThreadLocalRandom8; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Test what interruptions of writing threads do not affect PDS. + */ +public class IgnitePdsThreadInterruptionTest extends GridCommonAbstractTest { + /** */ + private static final int PAGE_SIZE = 1 << 12; // 4096 + + /** */ + public static final int THREADS_CNT = 1; + + /** + * Cache name. + */ + private final String cacheName = "cache"; + + /** */ + private volatile boolean stop = false; + + /** + * {@inheritDoc} + */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + final IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setPersistentStoreConfiguration(storeConfiguration()); + + cfg.setMemoryConfiguration(memoryConfiguration()); + + cfg.setCacheConfiguration(new CacheConfiguration<>(cacheName)); + + return cfg; + } + + /** + * @return Store config. + */ + private PersistentStoreConfiguration storeConfiguration() { + PersistentStoreConfiguration cfg = new PersistentStoreConfiguration(); + + cfg.setWalMode(WALMode.LOG_ONLY); + + cfg.setWalFsyncDelayNanos(0); + + cfg.setFileIOFactory(new AsyncFileIOFactory()); + + return cfg; + } + + /** + * @return Memory config. + */ + private MemoryConfiguration memoryConfiguration() { + final MemoryConfiguration memCfg = new MemoryConfiguration(); + + MemoryPolicyConfiguration memPlcCfg = new MemoryPolicyConfiguration(); + // memPlcCfg.setPageEvictionMode(RANDOM_LRU); TODO Fix NPE on start. + memPlcCfg.setName("dfltMemPlc"); + + memCfg.setPageSize(PAGE_SIZE); + memCfg.setConcurrencyLevel(1); + memCfg.setMemoryPolicies(memPlcCfg); + memCfg.setDefaultMemoryPolicyName("dfltMemPlc"); + + return memCfg; + } + + /** + * {@inheritDoc} + */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + deleteWorkFiles(); + } + + /** + * {@inheritDoc} + */ + @Override protected void afterTestsStopped() throws Exception { + super.afterTestsStopped(); + + stopAllGrids(); + + deleteWorkFiles(); + } + + /** + * Tests interruptions on WAL write. + * + * @throws Exception + */ + public void testInterruptsOnWALWrite() throws Exception { + final Ignite ignite = startGrid(); + + ignite.active(true); + + final int valLen = 8192; + + final byte[] payload = new byte[valLen]; + + final int maxKey = 100_000; + + Thread[] workers = new Thread[THREADS_CNT]; + + final AtomicReference fail = new AtomicReference<>(); + + Runnable clo = new Runnable() { + @Override public void run() { + IgniteCache cache = ignite.cache(cacheName); + + while (!stop) + cache.put(ThreadLocalRandom8.current().nextInt(maxKey), payload); + } + }; + + for (int i = 0; i < workers.length; i++) { + workers[i] = new Thread(clo); + workers[i].setName("writer-" + i); + workers[i].setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override public void uncaughtException(Thread t, Throwable e) { + fail.compareAndSet(null, e); + } + }); + } + + for (Thread worker : workers) + worker.start(); + + Thread.sleep(3_000); + + // Interrupts should not affect writes. + for (Thread worker : workers) + worker.interrupt(); + + Thread.sleep(3_000); + + stop = true; + + for (Thread worker : workers) + worker.join(); + + Throwable t = fail.get(); + + assert t == null : t; + + IgniteCache cache = ignite.cache(cacheName); + + int verifiedKeys = 0; + + // Post check. + for (int i = 0; i < maxKey; i++) { + byte[] val = (byte[]) cache.get(i); + + if (val != null) { + assertEquals("Illegal length", valLen, val.length); + + verifiedKeys++; + } + } + + log.info("Verified keys: " + verifiedKeys); + } + + /** + * @throws IgniteCheckedException If fail. + */ + private void deleteWorkFiles() throws IgniteCheckedException { + deleteRecursively(U.resolveWorkDirectory(U.defaultWorkDirectory(), "db", false)); + } +} \ No newline at end of file diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalFlushFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalFlushFailoverTest.java index cad10ae0e15bb..048e8bfaed57a 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalFlushFailoverTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/IgniteWalFlushFailoverTest.java @@ -42,12 +42,16 @@ import org.apache.ignite.transactions.Transaction; import org.apache.ignite.transactions.TransactionConcurrency; import org.apache.ignite.transactions.TransactionIsolation; +import java.nio.file.OpenOption; + +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; /** * */ public class IgniteWalFlushFailoverTest extends GridCommonAbstractTest { - /** */ private static final String TEST_CACHE = "testCache"; @@ -168,22 +172,22 @@ private void deleteWorkFiles() throws IgniteCheckedException { private static class FailingFileIOFactory implements FileIOFactory { private static final long serialVersionUID = 0L; + /** */ private final FileIOFactory delegateFactory = new RandomAccessFileIOFactory(); - @Override - public FileIO create(File file) throws IOException { - return create(file, "rw"); + /** {@inheritDoc} */ + @Override public FileIO create(File file) throws IOException { + return create(file, CREATE, READ, WRITE); } - @Override - public FileIO create(File file, String mode) throws IOException { - FileIO delegate = delegateFactory.create(file, mode); + /** {@inheritDoc} */ + @Override public FileIO create(File file, OpenOption... modes) throws IOException { + FileIO delegate = delegateFactory.create(file, modes); return new FileIODecorator(delegate) { int writeAttempts = 2; - @Override - public int write(ByteBuffer sourceBuffer) throws IOException { + @Override public int write(ByteBuffer sourceBuffer) throws IOException { if (--writeAttempts == 0) throw new RuntimeException("Test exception. Unable to write to file."); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java index e4874d97b47e3..3d5250703de5a 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/crc/IgniteDataIntegrityTests.java @@ -21,11 +21,11 @@ import java.io.EOFException; import java.io.File; import java.io.IOException; -import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.ThreadLocalRandom; -import org.apache.ignite.internal.processors.cache.persistence.file.RandomAccessFileIO; +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.ByteBufferExpander; import org.apache.ignite.internal.processors.cache.persistence.wal.FileInput; import org.apache.ignite.internal.processors.cache.persistence.wal.crc.IgniteDataIntegrityViolationException; @@ -38,9 +38,6 @@ public class IgniteDataIntegrityTests extends TestCase { /** File input. */ private FileInput fileInput; - /** Random access file. */ - private RandomAccessFile randomAccessFile; - /** Buffer expander. */ private ByteBufferExpander expBuf; @@ -51,13 +48,13 @@ public class IgniteDataIntegrityTests extends TestCase { File file = File.createTempFile("integrity", "dat"); file.deleteOnExit(); - randomAccessFile = new RandomAccessFile(file, "rw"); - expBuf = new ByteBufferExpander(1024, ByteOrder.BIG_ENDIAN); + FileIOFactory factory = new RandomAccessFileIOFactory(); + fileInput = new FileInput( - new RandomAccessFileIO(randomAccessFile), - expBuf + factory.create(file), + expBuf ); ByteBuffer buf = ByteBuffer.allocate(1024); @@ -71,13 +68,15 @@ public class IgniteDataIntegrityTests extends TestCase { buf.putInt(PureJavaCrc32.calcCrc32(buf, 12)); } - randomAccessFile.write(buf.array()); - randomAccessFile.getFD().sync(); + buf.rewind(); + + fileInput.io().write(buf); + fileInput.io().force(); } /** {@inheritDoc} */ @Override protected void tearDown() throws Exception { - randomAccessFile.close(); + fileInput.io().close(); expBuf.close(); } @@ -177,22 +176,24 @@ public void testExpandBuffer() { */ private void toggleOneRandomBit(int rangeFrom, int rangeTo) throws IOException { int pos = ThreadLocalRandom.current().nextInt(rangeFrom, rangeTo); - randomAccessFile.seek(pos); + fileInput.io().position(pos); + + byte[] buf = new byte[1]; - byte b = randomAccessFile.readByte(); + fileInput.io().read(buf, 0, 1); - b ^= (1 << 3); + buf[0] ^= (1 << 3); - randomAccessFile.seek(pos); - randomAccessFile.writeByte(b); - randomAccessFile.getFD().sync(); + fileInput.io().position(pos); + fileInput.io().write(buf, 0, 1); + fileInput.io().force(); } /** * */ private void checkIntegrity() throws Exception { - randomAccessFile.seek(0); + fileInput.io().position(0); for (int i = 0; i < 1024 / 16; i++) { try(FileInput.Crc32CheckingFileInput in = fileInput.startRead(false)) { diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java index ae8ea1813a5b6..cfbe2e090c2ac 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgnitePdsWithIndexingCoreTestSuite.java @@ -26,6 +26,7 @@ import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsPageEvictionTest; import org.apache.ignite.internal.processors.cache.persistence.db.file.IgnitePdsCacheIntegrationTest; import org.apache.ignite.internal.processors.cache.persistence.db.file.IgnitePdsNoActualWalHistoryTest; +import org.apache.ignite.internal.processors.cache.persistence.db.file.IgnitePdsThreadInterruptionTest; import org.apache.ignite.internal.processors.cache.persistence.db.wal.IgniteWalRecoveryTest; import org.apache.ignite.internal.processors.cache.persistence.db.wal.WalRecoveryTxLogicalRecordsTest; @@ -53,6 +54,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgnitePdsBinaryMetadataOnClusterRestartTest.class); suite.addTestSuite(IgnitePdsMarshallerMappingRestoreOnNodeStartTest.class); + suite.addTestSuite(IgnitePdsThreadInterruptionTest.class); return suite; } From 297f86e52114dae41dcd41f5d582f0afab09feb5 Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Fri, 22 Sep 2017 17:32:03 +0300 Subject: [PATCH 133/145] IGNITE-6228 Compilation fix after cherry-pick --- .../GridCacheDatabaseSharedManager.java | 61 ------------------- 1 file changed, 61 deletions(-) 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 091b8020ee91f..b3760f9511aff 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 @@ -93,7 +93,6 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.PageDeltaRecord; import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionDestroyRecord; import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionMetaStateRecord; -import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheGroupContext; import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor; import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor; @@ -693,66 +692,6 @@ private long[] calculateFragmentSizes(int concLvl, long cacheSize) { throw new IgniteCheckedException("Page eviction is not compatible with persistence: " + plcCfg.getName()); } - /** {@inheritDoc} */ - @Override protected void checkPageSize(MemoryConfiguration memCfg) { - if (memCfg.getPageSize() == 0) { - try { - assert cctx.pageStore() instanceof FilePageStoreManager : - "Invalid page store manager was created: " + cctx.pageStore(); - - Path anyIdxPartFile = IgniteUtils.searchFileRecursively( - ((FilePageStoreManager)cctx.pageStore()).workDir().toPath(), FilePageStoreManager.INDEX_FILE_NAME); - - if (anyIdxPartFile != null) { - memCfg.setPageSize(resolvePageSizeFromPartitionFile(anyIdxPartFile)); - - return; - } - } - catch (IgniteCheckedException | IOException | IllegalArgumentException e) { - U.quietAndWarn(log, "Attempt to resolve pageSize from store files failed: " + e.getMessage()); - - U.quietAndWarn(log, "Default page size will be used: " + MemoryConfiguration.DFLT_PAGE_SIZE + " bytes"); - } - - memCfg.setPageSize(MemoryConfiguration.DFLT_PAGE_SIZE); - } - } - - /** - * @param partFile Partition file. - */ - private int resolvePageSizeFromPartitionFile(Path partFile) throws IOException, IgniteCheckedException { - try (FileIO fileIO = persistenceCfg.getFileIOFactory().create(partFile.toFile())) { - int minimalHdr = FilePageStore.HEADER_SIZE; - - if (fileIO.size() < minimalHdr) - throw new IgniteCheckedException("Partition file is too small: " + partFile); - - ByteBuffer hdr = ByteBuffer.allocate(minimalHdr).order(ByteOrder.LITTLE_ENDIAN); - - while (hdr.remaining() > 0) - fileIO.read(hdr); - - hdr.rewind(); - - hdr.getLong(); // Read signature. - - hdr.getInt(); // Read version. - - hdr.get(); // Read type. - - int pageSize = hdr.getInt(); - - if (pageSize == 2048) { - U.quietAndWarn(log, "You are currently using persistent store with 2K pages (MemoryConfiguration#" + - "pageSize). If you use SSD disk, consider migrating to 4K pages for better IO performance."); - } - - return pageSize; - } - } - /** * @param cancel Cancel flag. */ From 876d7d5ace41b9923b88546012592d4c62d9be7c Mon Sep 17 00:00:00 2001 From: Alexei Scherbakov Date: Fri, 22 Sep 2017 16:36:38 +0300 Subject: [PATCH 134/145] IGNITE-6484 writeComplete conditional wait is made uninterruptable (cherry picked from commit 54a00fc) --- .../cache/persistence/wal/FileWriteAheadLogManager.java | 4 ++-- .../org/apache/ignite/internal/util/IgniteUtils.java | 9 +++++++++ .../db/file/IgnitePdsThreadInterruptionTest.java | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) 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 73a4639622c9b..06d754ccb0412 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 @@ -1819,7 +1819,7 @@ else if (stop) { try { while (written < expWritten && envFailed == null) - U.await(writeComplete); + U.awaitQuiet(writeComplete); } finally { lock.unlock(); @@ -2154,7 +2154,7 @@ private void awaitNext() throws IgniteCheckedException { try { while (fileIO != null) - U.await(nextSegment); + U.awaitQuiet(nextSegment); } finally { lock.unlock(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java index abdab698fd869..c73d979552fff 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java @@ -7395,6 +7395,15 @@ public static void await(Condition cond) throws IgniteInterruptedCheckedExceptio } } + /** + * Awaits for condition ignoring interrupts. + * + * @param cond Condition to await for. + */ + public static void awaitQuiet(Condition cond) { + cond.awaitUninterruptibly(); + } + /** * Awaits for condition. * diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.java index aab569a07534a..6f11d4dce927b 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/file/IgnitePdsThreadInterruptionTest.java @@ -41,7 +41,7 @@ public class IgnitePdsThreadInterruptionTest extends GridCommonAbstractTest { private static final int PAGE_SIZE = 1 << 12; // 4096 /** */ - public static final int THREADS_CNT = 1; + public static final int THREADS_CNT = 10; /** * Cache name. From da682b42526b3fea256b94783a6467b85fb74dce Mon Sep 17 00:00:00 2001 From: Eduard Shangareev Date: Fri, 22 Sep 2017 16:17:42 +0300 Subject: [PATCH 135/145] IGNITE-6434 Fixed compilation on merge. --- .../dht/GridDhtPartitionTopologyImpl.java | 495 ++++++++++-------- .../GridCacheDatabaseSharedManager.java | 5 +- ...IgnitePdsExchangeDuringCheckpointTest.java | 135 +++++ ...gniteCacheDataStructuresSelfTestSuite.java | 4 +- .../testsuites/IgnitePdsTestSuite2.java | 5 + 5 files changed, 408 insertions(+), 236 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.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 f7f71a18c4c4f..936ccb307f0cd 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 @@ -298,20 +298,27 @@ private String mapString(GridDhtPartitionMap map) { GridDhtPartitionsExchangeFuture exchFut) throws IgniteInterruptedCheckedException { - U.writeLock(lock); + ctx.database().checkpointReadLock(); try { - if (stopping) - return; + U.writeLock(lock); - long updateSeq = this.updateSeq.incrementAndGet(); + try { + if (stopping) + return; - initPartitions0(affVer, exchFut, updateSeq); + long updateSeq = this.updateSeq.incrementAndGet(); - consistencyCheck(); + initPartitions0(affVer, exchFut, updateSeq); + + consistencyCheck(); + } + finally { + lock.writeLock().unlock(); + } } finally { - lock.writeLock().unlock(); + ctx.database().checkpointReadUnlock(); } } @@ -589,101 +596,109 @@ private boolean partitionLocalNode(int p, AffinityTopologyVersion topVer) { ", affVer=" + grp.affinity().lastVersion() + ", fut=" + exchFut + ']'; - lock.writeLock().lock(); + ctx.database().checkpointReadLock(); try { - if (stopping) - return false; - assert readyTopVer.initialized() : readyTopVer; - assert lastTopChangeVer.equals(readyTopVer); + lock.writeLock().lock(); - if (log.isDebugEnabled()) - log.debug("Partition map before afterExchange [exchId=" + exchFut.exchangeId() + ", fullMap=" + - fullMapString() + ']'); + try { + if (stopping) + return false; - long updateSeq = this.updateSeq.incrementAndGet(); + assert readyTopVer.initialized() : readyTopVer; + assert lastTopChangeVer.equals(readyTopVer); - for (int p = 0; p < num; p++) { - GridDhtLocalPartition locPart = localPartition0(p, topVer, false, false, false); + if (log.isDebugEnabled()) + log.debug("Partition map before afterExchange [exchId=" + exchFut.exchangeId() + ", fullMap=" + + fullMapString() + ']'); - if (partitionLocalNode(p, topVer)) { - // This partition will be created during next topology event, - // which obviously has not happened at this point. - if (locPart == null) { - if (log.isDebugEnabled()) - log.debug("Skipping local partition afterExchange (will not create): " + p); + long updateSeq = this.updateSeq.incrementAndGet(); - continue; - } + for (int p = 0; p < num; p++) { + GridDhtLocalPartition locPart = localPartition0(p, topVer, false, false, false); - GridDhtPartitionState state = locPart.state(); + if (partitionLocalNode(p, topVer)) { + // This partition will be created during next topology event, + // which obviously has not happened at this point. + if (locPart == null) { + if (log.isDebugEnabled()) + log.debug("Skipping local partition afterExchange (will not create): " + p); - if (state == MOVING) { - if (grp.rebalanceEnabled()) { - Collection owners = owners(p); + continue; + } - // If there are no other owners, then become an owner. - if (F.isEmpty(owners)) { - boolean owned = locPart.own(); + GridDhtPartitionState state = locPart.state(); - assert owned : "Failed to own partition [grp=" + grp.cacheOrGroupName() + ", locPart=" + - locPart + ']'; + if (state == MOVING) { + if (grp.rebalanceEnabled()) { + Collection owners = owners(p); - updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); + // If there are no other owners, then become an owner. + if (F.isEmpty(owners)) { + boolean owned = locPart.own(); - changed = true; + assert owned : "Failed to own partition [grp=" + grp.cacheOrGroupName() + ", locPart=" + + locPart + ']'; - if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) { - DiscoveryEvent discoEvt = exchFut.events().lastEvent(); + updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); - grp.addRebalanceEvent(p, - EVT_CACHE_REBALANCE_PART_DATA_LOST, - discoEvt.eventNode(), - discoEvt.type(), - discoEvt.timestamp()); - } + changed = true; - if (log.isDebugEnabled()) - log.debug("Owned partition: " + locPart); + if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) { + DiscoveryEvent discoEvt = exchFut.events().lastEvent(); + + grp.addRebalanceEvent(p, + EVT_CACHE_REBALANCE_PART_DATA_LOST, + discoEvt.eventNode(), + discoEvt.type(), + discoEvt.timestamp()); + } + + if (log.isDebugEnabled()) + log.debug("Owned partition: " + locPart); + } + else if (log.isDebugEnabled()) + log.debug("Will not own partition (there are owners to rebalance from) [locPart=" + + locPart + ", owners = " + owners + ']'); } - else if (log.isDebugEnabled()) - log.debug("Will not own partition (there are owners to rebalance from) [locPart=" + - locPart + ", owners = " + owners + ']'); + else + updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); } - else - updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); } - } - else { - if (locPart != null) { - GridDhtPartitionState state = locPart.state(); + else { + if (locPart != null) { + GridDhtPartitionState state = locPart.state(); - if (state == MOVING) { - locPart.rent(false); + if (state == MOVING) { + locPart.rent(false); - updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); + updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer); - changed = true; + changed = true; - if (log.isDebugEnabled()) - log.debug("Evicting moving partition (it does not belong to affinity): " + locPart); + if (log.isDebugEnabled()) + log.debug("Evicting moving partition (it does not belong to affinity): " + locPart); + } } } } - } - AffinityAssignment aff = grp.affinity().readyAffinity(topVer); + AffinityAssignment aff = grp.affinity().readyAffinity(topVer); - if (node2part != null && node2part.valid()) - changed |= checkEvictions(updateSeq, aff); + if (node2part != null && node2part.valid()) + changed |= checkEvictions(updateSeq, aff); - updateRebalanceVersion(aff.assignment()); + updateRebalanceVersion(aff.assignment()); - consistencyCheck(); + consistencyCheck(); + } + finally { + lock.writeLock().unlock(); + } } finally { - lock.writeLock().unlock(); + ctx.database().checkpointReadUnlock(); } return changed; @@ -709,6 +724,8 @@ else if (log.isDebugEnabled()) private GridDhtLocalPartition createPartition(int p) { assert lock.isWriteLockedByCurrentThread(); + assert ctx.database().checkpointLockIsHeldByThread(); + GridDhtLocalPartition loc = locParts.get(p); if (loc == null || loc.state() == EVICTED) { @@ -1183,232 +1200,239 @@ private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridD assert partMap != null; - lock.writeLock().lock(); + ctx.database().checkpointReadLock(); try { - if (stopping || !lastTopChangeVer.initialized() || - // Ignore message not-related to exchange if exchange is in progress. - (exchangeVer == null && !lastTopChangeVer.equals(readyTopVer))) - return false; + lock.writeLock().lock(); - if (incomeCntrMap != null) { - // update local counters in partitions - for (int i = 0; i < locParts.length(); i++) { - GridDhtLocalPartition part = locParts.get(i); + try { + if (stopping || !lastTopChangeVer.initialized() || + // Ignore message not-related to exchange if exchange is in progress. + (exchangeVer == null && !lastTopChangeVer.equals(readyTopVer))) + return false; - if (part == null) - continue; + if (incomeCntrMap != null) { + // update local counters in partitions + for (int i = 0; i < locParts.length(); i++) { + GridDhtLocalPartition part = locParts.get(i); + + if (part == null) + continue; - if (part.state() == OWNING || part.state() == MOVING) { - long updCntr = incomeCntrMap.updateCounter(part.id()); + if (part.state() == OWNING || part.state() == MOVING) { + long updCntr = incomeCntrMap.updateCounter(part.id()); - if (updCntr != 0 && updCntr > part.updateCounter()) - part.updateCounter(updCntr); + if (updCntr != 0 && updCntr > part.updateCounter()) + part.updateCounter(updCntr); + } } } - } - if (exchangeVer != null) { - // Ignore if exchange already finished or new exchange started. - if (readyTopVer.compareTo(exchangeVer) > 0 || lastTopChangeVer.compareTo(exchangeVer) > 0) { - U.warn(log, "Stale exchange id for full partition map update (will ignore) [" + + if (exchangeVer != null) { + // Ignore if exchange already finished or new exchange started. + if (readyTopVer.compareTo(exchangeVer) > 0 || lastTopChangeVer.compareTo(exchangeVer) > 0) { + U.warn(log, "Stale exchange id for full partition map update (will ignore) [" + + "lastTopChange=" + lastTopChangeVer + + ", readTopVer=" + readyTopVer + + ", exchVer=" + exchangeVer + ']'); + + return false; + } + } + + if (msgTopVer != null && lastTopChangeVer.compareTo(msgTopVer) > 0) { + U.warn(log, "Stale version for full partition map update message (will ignore) [" + "lastTopChange=" + lastTopChangeVer + ", readTopVer=" + readyTopVer + - ", exchVer=" + exchangeVer + ']'); + ", msgVer=" + msgTopVer + ']'); return false; } - } - - if (msgTopVer != null && lastTopChangeVer.compareTo(msgTopVer) > 0) { - U.warn(log, "Stale version for full partition map update message (will ignore) [" + - "lastTopChange=" + lastTopChangeVer + - ", readTopVer=" + readyTopVer + - ", msgVer=" + msgTopVer + ']'); - return false; - } + boolean fullMapUpdated = (node2part == null); - boolean fullMapUpdated = (node2part == null); + if (node2part != null) { + for (GridDhtPartitionMap part : node2part.values()) { + GridDhtPartitionMap newPart = partMap.get(part.nodeId()); - if (node2part != null) { - for (GridDhtPartitionMap part : node2part.values()) { - GridDhtPartitionMap newPart = partMap.get(part.nodeId()); + if (shouldOverridePartitionMap(part, newPart)) { + fullMapUpdated = true; - if (shouldOverridePartitionMap(part, newPart)) { - fullMapUpdated = true; + if (log.isDebugEnabled()) { + log.debug("Overriding partition map in full update map [exchVer=" + exchangeVer + + ", curPart=" + mapString(part) + + ", newPart=" + mapString(newPart) + ']'); + } - if (log.isDebugEnabled()) { - log.debug("Overriding partition map in full update map [exchVer=" + exchangeVer + - ", curPart=" + mapString(part) + - ", newPart=" + mapString(newPart) + ']'); + if (newPart.nodeId().equals(ctx.localNodeId())) + updateSeq.setIfGreater(newPart.updateSequence()); + } + else { + // If for some nodes current partition has a newer map, + // then we keep the newer value. + partMap.put(part.nodeId(), part); } - - if (newPart.nodeId().equals(ctx.localNodeId())) - updateSeq.setIfGreater(newPart.updateSequence()); - } - else { - // If for some nodes current partition has a newer map, - // then we keep the newer value. - partMap.put(part.nodeId(), part); } - } - // Check that we have new nodes. - for (GridDhtPartitionMap part : partMap.values()) { - if (fullMapUpdated) - break; + // Check that we have new nodes. + for (GridDhtPartitionMap part : partMap.values()) { + if (fullMapUpdated) + break; - fullMapUpdated = !node2part.containsKey(part.nodeId()); - } + fullMapUpdated = !node2part.containsKey(part.nodeId()); + } - // Remove entry if node left. - for (Iterator it = partMap.keySet().iterator(); it.hasNext(); ) { - UUID nodeId = it.next(); + // Remove entry if node left. + for (Iterator it = partMap.keySet().iterator(); it.hasNext(); ) { + UUID nodeId = it.next(); - if (!ctx.discovery().alive(nodeId)) { - if (log.isDebugEnabled()) - log.debug("Removing left node from full map update [nodeId=" + nodeId + ", partMap=" + - partMap + ']'); + if (!ctx.discovery().alive(nodeId)) { + if (log.isDebugEnabled()) + log.debug("Removing left node from full map update [nodeId=" + nodeId + ", partMap=" + + partMap + ']'); - it.remove(); + it.remove(); + } } } - } - else { - GridDhtPartitionMap locNodeMap = partMap.get(ctx.localNodeId()); - - if (locNodeMap != null) - updateSeq.setIfGreater(locNodeMap.updateSequence()); - } + else { + GridDhtPartitionMap locNodeMap = partMap.get(ctx.localNodeId()); - if (!fullMapUpdated) { - if (log.isDebugEnabled()) { - log.debug("No updates for full partition map (will ignore) [lastExch=" + lastTopChangeVer + - ", exchVer=" + exchangeVer + - ", curMap=" + node2part + - ", newMap=" + partMap + ']'); + if (locNodeMap != null) + updateSeq.setIfGreater(locNodeMap.updateSequence()); } - return false; - } + if (!fullMapUpdated) { + if (log.isDebugEnabled()) { + log.debug("No updates for full partition map (will ignore) [lastExch=" + lastTopChangeVer + + ", exchVer=" + exchangeVer + + ", curMap=" + node2part + + ", newMap=" + partMap + ']'); + } - if (exchangeVer != null) { - assert exchangeVer.compareTo(readyTopVer) >= 0 && exchangeVer.compareTo(lastTopChangeVer) >= 0; + return false; + } - lastTopChangeVer = readyTopVer = exchangeVer; - } + if (exchangeVer != null) { + assert exchangeVer.compareTo(readyTopVer) >= 0 && exchangeVer.compareTo(lastTopChangeVer) >= 0; - node2part = partMap; + lastTopChangeVer = readyTopVer = exchangeVer; + } - if (exchangeVer == null && !grp.isReplicated() && - (readyTopVer.initialized() && readyTopVer.compareTo(diffFromAffinityVer) >= 0)) { - AffinityAssignment affAssignment = grp.affinity().readyAffinity(readyTopVer); + node2part = partMap; - for (Map.Entry e : partMap.entrySet()) { - for (Map.Entry e0 : e.getValue().entrySet()) { - int p = e0.getKey(); + if (exchangeVer == null && !grp.isReplicated() && + (readyTopVer.initialized() && readyTopVer.compareTo(diffFromAffinityVer) >= 0)) { + AffinityAssignment affAssignment = grp.affinity().readyAffinity(readyTopVer); - Set diffIds = diffFromAffinity.get(p); + for (Map.Entry e : partMap.entrySet()) { + for (Map.Entry e0 : e.getValue().entrySet()) { + int p = e0.getKey(); - if ((e0.getValue() == MOVING || e0.getValue() == OWNING || e0.getValue() == RENTING) && - !affAssignment.getIds(p).contains(e.getKey())) { + Set diffIds = diffFromAffinity.get(p); - if (diffIds == null) - diffFromAffinity.put(p, diffIds = U.newHashSet(3)); + if ((e0.getValue() == MOVING || e0.getValue() == OWNING || e0.getValue() == RENTING) && + !affAssignment.getIds(p).contains(e.getKey())) { - diffIds.add(e.getKey()); - } - else { - if (diffIds != null && diffIds.remove(e.getKey())) { - if (diffIds.isEmpty()) - diffFromAffinity.remove(p); + if (diffIds == null) + diffFromAffinity.put(p, diffIds = U.newHashSet(3)); + + diffIds.add(e.getKey()); + } + else { + if (diffIds != null && diffIds.remove(e.getKey())) { + if (diffIds.isEmpty()) + diffFromAffinity.remove(p); + } } } } - } - diffFromAffinityVer = readyTopVer; - } + diffFromAffinityVer = readyTopVer; + } - boolean changed = false; + boolean changed = false; - GridDhtPartitionMap nodeMap = partMap.get(ctx.localNodeId()); + GridDhtPartitionMap nodeMap = partMap.get(ctx.localNodeId()); - if (nodeMap != null && ctx.database().persistenceEnabled() && readyTopVer.initialized()) { - for (Map.Entry e : nodeMap.entrySet()) { - int p = e.getKey(); - GridDhtPartitionState state = e.getValue(); + if (nodeMap != null && ctx.database().persistenceEnabled() && readyTopVer.initialized()) { + for (Map.Entry e : nodeMap.entrySet()) { + int p = e.getKey(); + GridDhtPartitionState state = e.getValue(); - if (state == OWNING) { - GridDhtLocalPartition locPart = locParts.get(p); + if (state == OWNING) { + GridDhtLocalPartition locPart = locParts.get(p); - assert locPart != null : grp.cacheOrGroupName(); + assert locPart != null : grp.cacheOrGroupName(); - if (locPart.state() == MOVING) { - boolean success = locPart.own(); + if (locPart.state() == MOVING) { + boolean success = locPart.own(); - assert success : locPart; + assert success : locPart; - changed |= success; + changed |= success; + } } - } - else if (state == MOVING) { - GridDhtLocalPartition locPart = locParts.get(p); + else if (state == MOVING) { + GridDhtLocalPartition locPart = locParts.get(p); - if (locPart == null || locPart.state() == EVICTED) - locPart = createPartition(p); + if (locPart == null || locPart.state() == EVICTED) + locPart = createPartition(p); - if (locPart.state() == OWNING) { - locPart.moving(); + if (locPart.state() == OWNING) { + locPart.moving(); - changed = true; + changed = true; + } } - } - else if (state == RENTING && partsToReload.contains(p)) { - GridDhtLocalPartition locPart = locParts.get(p); + else if (state == RENTING && partsToReload.contains(p)) { + GridDhtLocalPartition locPart = locParts.get(p); - if (locPart == null || locPart.state() == EVICTED) { - createPartition(p); + if (locPart == null || locPart.state() == EVICTED) { + createPartition(p); - changed = true; - } - else if (locPart.state() == OWNING || locPart.state() == MOVING) { - locPart.reload(true); + changed = true; + } + else if (locPart.state() == OWNING || locPart.state() == MOVING) { + locPart.reload(true); - locPart.rent(false); + locPart.rent(false); - changed = true; + changed = true; + } + else + locPart.reload(true); } - else - locPart.reload(true); } } - } - long updateSeq = this.updateSeq.incrementAndGet(); + long updateSeq = this.updateSeq.incrementAndGet(); - if (readyTopVer.initialized() && readyTopVer.equals(lastTopChangeVer)) { - AffinityAssignment aff = grp.affinity().readyAffinity(readyTopVer); + if (readyTopVer.initialized() && readyTopVer.equals(lastTopChangeVer)) { + AffinityAssignment aff = grp.affinity().readyAffinity(readyTopVer); - if (exchangeVer == null) - changed |= checkEvictions(updateSeq, aff); + if (exchangeVer == null) + changed |= checkEvictions(updateSeq, aff); - updateRebalanceVersion(aff.assignment()); - } + updateRebalanceVersion(aff.assignment()); + } - consistencyCheck(); + consistencyCheck(); - if (log.isDebugEnabled()) - log.debug("Partition map after full update: " + fullMapString()); + if (log.isDebugEnabled()) + log.debug("Partition map after full update: " + fullMapString()); - if (changed) - ctx.exchange().scheduleResendPartitions(); + if (changed) + ctx.exchange().scheduleResendPartitions(); - return changed; + return changed; + } + finally { + lock.writeLock().unlock(); + } } finally { - lock.writeLock().unlock(); + ctx.database().checkpointReadUnlock(); } } @@ -2177,27 +2201,34 @@ private void removeNode(UUID nodeId) { /** {@inheritDoc} */ @Override public void onEvicted(GridDhtLocalPartition part, boolean updateSeq) { - lock.writeLock().lock(); + ctx.database().checkpointReadLock(); try { - if (stopping) - return; + lock.writeLock().lock(); - assert part.state() == EVICTED; + try { + if (stopping) + return; - long seq = updateSeq ? this.updateSeq.incrementAndGet() : this.updateSeq.get(); + assert part.state() == EVICTED; - if (part.reload()) - part = createPartition(part.id()); + long seq = updateSeq ? this.updateSeq.incrementAndGet() : this.updateSeq.get(); - assert lastTopChangeVer.initialized() : lastTopChangeVer; + if (part.reload()) + part = createPartition(part.id()); - updateLocal(part.id(), part.state(), seq, lastTopChangeVer); + assert lastTopChangeVer.initialized() : lastTopChangeVer; - consistencyCheck(); + updateLocal(part.id(), part.state(), seq, lastTopChangeVer); + + consistencyCheck(); + } + finally { + lock.writeLock().unlock(); + } } finally { - lock.writeLock().unlock(); + ctx.database().checkpointReadUnlock(); } } 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 b3760f9511aff..fc34163fbbcbb 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 @@ -2172,6 +2172,9 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws for (DbCheckpointListener lsnr : lsnrs) lsnr.onCheckpointBegin(ctx0); + if (curr.nextSnapshot) + snapshotMgr.onMarkCheckPointBegin(curr.snapshotOperation, map); + for (CacheGroupContext grp : cctx.cache().cacheGroups()) { if (grp.isLocal()) continue; @@ -2191,8 +2194,6 @@ private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws cpRec.addCacheGroupState(grp.groupId(), state); } - if (curr.nextSnapshot) - snapshotMgr.onMarkCheckPointBegin(curr.snapshotOperation, map); cpPagesTuple = beginAllCheckpoints(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java new file mode 100644 index 0000000000000..3969fb6e3f510 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java @@ -0,0 +1,135 @@ +/* + * 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.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.MemoryConfiguration; +import org.apache.ignite.configuration.MemoryPolicyConfiguration; +import org.apache.ignite.configuration.PersistentStoreConfiguration; +import org.apache.ignite.configuration.WALMode; +import org.apache.ignite.internal.IgniteEx; +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.junits.common.GridCommonAbstractTest; + +/** + * + */ +public class IgnitePdsExchangeDuringCheckpointTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** + * + */ + public void testExchangeOnNodeLeft() throws Exception { + for (int i = 0; i < 5; i++) { + startGrids(3); + IgniteEx ignite = grid(1); + ignite.active(true); + + awaitPartitionMapExchange(); + + stopGrid(0, true); + + awaitPartitionMapExchange(); + + ignite.context().cache().context().database().wakeupForCheckpoint("test").get(10000); + + afterTest(); + } + } + + /** + * + */ + public void testExchangeOnNodeJoin() throws Exception { + for (int i = 0; i < 5; i++) { + startGrids(2); + IgniteEx ignite = grid(1); + ignite.active(true); + + awaitPartitionMapExchange(); + + IgniteEx ex = startGrid(2); + + awaitPartitionMapExchange(); + + ex.context().cache().context().database().wakeupForCheckpoint("test").get(10000); + + afterTest(); + } + } + + /** + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + MemoryConfiguration memCfg = new MemoryConfiguration(); + + MemoryPolicyConfiguration memPlcCfg = new MemoryPolicyConfiguration(); + + memPlcCfg.setName("dfltMemPlc"); + memPlcCfg.setInitialSize(100 * 1024 * 1024); + memPlcCfg.setMaxSize(1000 * 1024 * 1024); + + memCfg.setDefaultMemoryPolicyName("dfltMemPlc"); + memCfg.setMemoryPolicies(memPlcCfg); + + cfg.setMemoryConfiguration(memCfg); + + CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME); + + ccfg.setAffinity(new RendezvousAffinityFunction(false, 4096)); + + cfg.setCacheConfiguration(ccfg); + + PersistentStoreConfiguration psiCfg = new PersistentStoreConfiguration() + .setCheckpointingThreads(1) + .setCheckpointingFrequency(1) + .setWalMode(WALMode.LOG_ONLY); + + cfg.setPersistentStoreConfiguration(psiCfg); + + TcpDiscoverySpi discoSpi = new TcpDiscoverySpi(); + + discoSpi.setIpFinder(IP_FINDER); + + cfg.setDiscoverySpi(discoSpi); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + stopAllGrids(); + deleteRecursively(U.resolveWorkDirectory(U.defaultWorkDirectory(), "db", false)); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + deleteRecursively(U.resolveWorkDirectory(U.defaultWorkDirectory(), "db", false)); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheDataStructuresSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheDataStructuresSelfTestSuite.java index 568af94adaa9f..6e16d2eee471e 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheDataStructuresSelfTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheDataStructuresSelfTestSuite.java @@ -25,6 +25,7 @@ import org.apache.ignite.internal.processors.cache.datastructures.IgniteClientDiscoveryDataStructuresTest; import org.apache.ignite.internal.processors.cache.datastructures.IgniteDataStructureUniqueNameTest; import org.apache.ignite.internal.processors.cache.datastructures.IgniteDataStructureWithJobTest; +import org.apache.ignite.internal.processors.cache.datastructures.SemaphoreFailoverSafeReleasePermitsTest; import org.apache.ignite.internal.processors.cache.datastructures.local.GridCacheLocalAtomicQueueApiSelfTest; import org.apache.ignite.internal.processors.cache.datastructures.local.GridCacheLocalAtomicSetSelfTest; import org.apache.ignite.internal.processors.cache.datastructures.local.GridCacheLocalQueueApiSelfTest; @@ -129,8 +130,7 @@ public static TestSuite suite() throws Exception { suite.addTest(new TestSuite(IgnitePartitionedCountDownLatchSelfTest.class)); suite.addTest(new TestSuite(IgniteDataStructureWithJobTest.class)); suite.addTest(new TestSuite(IgnitePartitionedSemaphoreSelfTest.class)); - // TODO https://issues.apache.org/jira/browse/IGNITE-4173, enable when fixed. - // suite.addTest(new TestSuite(SemaphoreFailoverSafeReleasePermitsTest.class)); + suite.addTest(new TestSuite(SemaphoreFailoverSafeReleasePermitsTest.class)); // TODO IGNITE-3141, enabled when fixed. // suite.addTest(new TestSuite(IgnitePartitionedLockSelfTest.class)); 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 4f4dedfe879af..06bb4ebeb32f0 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 @@ -19,6 +19,8 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTest; +import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes; +import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsExchangeDuringCheckpointTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsPageSizesTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsRecoveryAfterFileCorruptionTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePersistenceMetricsSelfTest; @@ -70,6 +72,9 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteWalFlushFailoverTest.class); suite.addTestSuite(IgniteWalReaderTest.class); + + suite.addTestSuite(IgnitePdsExchangeDuringCheckpointTest.class); + return suite; } } From a3f98cd5fe8600962acc0b2e0d14a599a725427b Mon Sep 17 00:00:00 2001 From: Aleksei Scherbakov Date: Fri, 22 Sep 2017 19:53:43 +0300 Subject: [PATCH 136/145] IGNITE-6434 Fixed compilation on merge. --- .../persistence/IgnitePdsExchangeDuringCheckpointTest.java | 2 +- .../java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java index 3969fb6e3f510..9ad8ca7adbf4c 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsExchangeDuringCheckpointTest.java @@ -74,7 +74,7 @@ public void testExchangeOnNodeJoin() throws Exception { awaitPartitionMapExchange(); - ex.context().cache().context().database().wakeupForCheckpoint("test").get(10000); + ex.context().cache().context().database().wakeupForCheckpoint("test").get(100000); afterTest(); } 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 06bb4ebeb32f0..8c3b82586b3fb 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 @@ -19,7 +19,6 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTest; -import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsExchangeDuringCheckpointTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsPageSizesTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsRecoveryAfterFileCorruptionTest; From 55e78668d0260040017a77f63caf61f201d6d300 Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Thu, 21 Sep 2017 16:37:42 +0300 Subject: [PATCH 137/145] IGNITE-6342 Fixed exchange hanging on eviction when partition is lost (cherry picked from commit 07f5b0c) --- .../discovery/GridDiscoveryManager.java | 18 ++--- .../dht/GridDhtPartitionTopologyImpl.java | 13 +++- .../GridDhtPartitionsExchangeFuture.java | 2 +- .../GridDhtPartitionsFullMessage.java | 2 +- .../persistence/GridCacheOffheapManager.java | 2 +- .../IgniteCacheDatabaseSharedManager.java | 2 +- .../cache/persistence/tree/io/IOVersions.java | 3 + .../ignite/spi/discovery/tcp/ServerImpl.java | 3 +- .../IgnitePdsContinuousRestartTest.java | 45 ++++++++++++- ...sRestartTestWithSharedGroupAndIndexes.java | 67 +++++++++++++++++++ .../testsuites/IgnitePdsTestSuite2.java | 4 +- 11 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes.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 49278e6d8d10e..007c12a321c25 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 @@ -1237,7 +1237,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx "(all nodes in topology should have identical value) " + "[locPreferIpV4=" + locPreferIpV4 + ", rmtPreferIpV4=" + rmtPreferIpV4 + ", locId8=" + U.id8(locNode.id()) + ", rmtId8=" + U.id8(n.id()) + - ", rmtAddrs=" + U.addressesAsString(n) + ']', + ", rmtAddrs=" + U.addressesAsString(n) + ", rmtNode=" + U.toShortString(n) + "]", "Local and remote 'java.net.preferIPv4Stack' system properties do not match."); ipV4Warned = true; @@ -1252,7 +1252,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx throw new IgniteCheckedException("Remote node has deployment mode different from local " + "[locId8=" + U.id8(locNode.id()) + ", locMode=" + locMode + ", rmtId8=" + U.id8(n.id()) + ", rmtMode=" + rmtMode + - ", rmtAddrs=" + U.addressesAsString(n) + ']'); + ", rmtAddrs=" + U.addressesAsString(n) + ", rmtNode=" + U.toShortString(n) + "]"); boolean rmtP2pEnabled = n.attribute(ATTR_PEER_CLASSLOADING); @@ -1260,7 +1260,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx throw new IgniteCheckedException("Remote node has peer class loading enabled flag different from" + " local [locId8=" + U.id8(locNode.id()) + ", locPeerClassLoading=" + locP2pEnabled + ", rmtId8=" + U.id8(n.id()) + ", rmtPeerClassLoading=" + rmtP2pEnabled + - ", rmtAddrs=" + U.addressesAsString(n) + ']'); + ", rmtAddrs=" + U.addressesAsString(n) + ", rmtNode=" + U.toShortString(n) + "]"); } Boolean rmtMarshUseDfltSuid = n.attribute(ATTR_MARSHALLER_USE_DFLT_SUID); @@ -1274,7 +1274,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx "[locMarshUseDfltSuid=" + locMarshUseDfltSuid + ", rmtMarshUseDfltSuid=" + rmtMarshUseDfltSuid + ", locNodeAddrs=" + U.addressesAsString(locNode) + ", rmtNodeAddrs=" + U.addressesAsString(n) + - ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ']'); + ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ", rmtNode=" + U.toShortString(n) + "]"); } Boolean rmtMarshStrSerVer2 = n.attribute(ATTR_MARSHALLER_USE_BINARY_STRING_SER_VER_2); @@ -1288,7 +1288,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx "[locMarshStrSerVer2=" + locMarshStrSerVer2 + ", rmtMarshStrSerVer2=" + rmtMarshStrSerVer2 + ", locNodeAddrs=" + U.addressesAsString(locNode) + ", rmtNodeAddrs=" + U.addressesAsString(n) + - ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ']'); + ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ", rmtNode=" + U.toShortString(n) + "]"); } boolean rmtLateAssign = n.attribute(ATTR_LATE_AFFINITY_ASSIGNMENT); @@ -1299,7 +1299,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx ", locDelayAssign=" + locDelayAssign + ", rmtId8=" + U.id8(n.id()) + ", rmtLateAssign=" + rmtLateAssign + - ", rmtAddrs=" + U.addressesAsString(n) + ']'); + ", rmtAddrs=" + U.addressesAsString(n) + ", rmtNode=" + U.toShortString(n) + "]"); } Boolean rmtSrvcCompatibilityEnabled = n.attribute(ATTR_SERVICES_COMPATIBILITY_MODE); @@ -1313,7 +1313,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx ", rmtSrvcCompatibilityEnabled=" + rmtSrvcCompatibilityEnabled + ", locNodeAddrs=" + U.addressesAsString(locNode) + ", rmtNodeAddrs=" + U.addressesAsString(n) + - ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ']'); + ", locNodeId=" + locNode.id() + ", rmtNode=" + U.toShortString(n) + "]"); } if (n.version().compareToIgnoreTimestamp(SERVICE_PERMISSIONS_SINCE) >= 0 @@ -1330,7 +1330,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx ", rmtSecurityCompatibilityEnabled=" + rmtSecurityCompatibilityEnabled + ", locNodeAddrs=" + U.addressesAsString(locNode) + ", rmtNodeAddrs=" + U.addressesAsString(n) + - ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ']'); + ", locNodeId=" + locNode.id() + ", rmtNode=" + U.toShortString(n) + "]"); } } @@ -1344,7 +1344,7 @@ private void checkAttributes(Iterable nodes) throws IgniteCheckedEx ", locNodeAddrs=" + U.addressesAsString(locNode) + ", rmtNodeAddrs=" + U.addressesAsString(n) + ", locNodeId=" + locNode.id() + ", rmtNodeId=" + n.id() + ", " + - ", rmtNodeVer" + n.version() + ']'); + ", rmtNodeVer" + n.version() + ", rmtNode=" + U.toShortString(n) + "]"); } } 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 936ccb307f0cd..5a1e0509c5b4f 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 @@ -1810,11 +1810,22 @@ private void rebuildDiff(AffinityAssignment affAssignment) { for (Integer part : lost) { long updSeq = updateSeq.incrementAndGet(); - GridDhtLocalPartition locPart = localPartition(part, resTopVer, false); + GridDhtLocalPartition locPart = localPartition(part, resTopVer, false, true); if (locPart != null) { boolean marked = plc == PartitionLossPolicy.IGNORE ? locPart.own() : locPart.markLost(); + if (!marked && locPart.state() == RENTING) + try { + //TODO https://issues.apache.org/jira/browse/IGNITE-6433 + locPart.tryEvict(); + locPart.rent(false).get(); + } + catch (IgniteCheckedException e) { + U.error(log, "Failed to wait for RENTING partition eviction after partition LOST event", + e); + } + if (marked) updateLocal(locPart.id(), locPart.state(), updSeq, resTopVer); 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 240b5f0af1802..0ec0f82f3eac9 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 @@ -1415,7 +1415,7 @@ public void finishMerged() { cacheCtx.continuousQueries().flushBackupQueue(res); } - } + } if (err == null) { if (centralizedAff) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java index edbfc23abec44..4a449d18a09d8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java @@ -49,7 +49,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa /** */ private static final long serialVersionUID = 0L; - /** */ + /** grpId -> FullMap */ @GridToStringInclude @GridDirectTransient private Map parts; 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 5cd12afc3bf89..4f48bbf4f16e8 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 @@ -194,7 +194,7 @@ private boolean saveStoreMetadata(CacheDataStore store, Context ctx, boolean sav else { // localPartition will not acquire writeLock here because create=false. GridDhtLocalPartition part = grp.topology().localPartition(store.partId(), - AffinityTopologyVersion.NONE, false); + AffinityTopologyVersion.NONE, false, true); if (part != null && part.state() != GridDhtPartitionState.EVICTED) state = part.state(); 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 69feac8f29c15..1981c2e588476 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 @@ -724,7 +724,7 @@ public boolean persistenceEnabled() { /** {@inheritDoc} */ @Override public boolean checkpointLockIsHeldByThread() { - return false; + return true; } /** 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 48b0da161b0b9..d74d34462ba79 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 @@ -79,6 +79,9 @@ public V latest() { * @return IO. */ public V forVersion(int ver) { + if (ver == 0) + throw new IllegalStateException("Failed to get page IO instance (page content is corrupted)"); + return vers[ver - 1]; } diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java index e0be62f5d7e3d..295c183e61c5e 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java @@ -928,7 +928,8 @@ else if (spiState == LOOPBACK_PROBLEM) { " node is configured to use loopback address, but " + secondNode + " node is not " + "(consider changing 'localAddress' configuration parameter) " + "[locNodeAddrs=" + U.addressesAsString(locNode) + ", rmtNodeAddrs=" + - U.addressesAsString(msg.addresses(), msg.hostNames()) + ']'); + U.addressesAsString(msg.addresses(), msg.hostNames()) + + ", creatorNodeId=" + msg.creatorNodeId() + ']'); } else LT.warn(log, "Node has not been connected to topology and will repeat join process. " + diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java index 238e1d17a857b..df62f3af2fb39 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java @@ -22,6 +22,7 @@ import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; @@ -54,6 +55,25 @@ public class IgnitePdsContinuousRestartTest extends GridCommonAbstractTest { /** */ public static final String CACHE_NAME = "cache1"; + /** Checkpoint delay. */ + private volatile int checkpointDelay = -1; + + /** */ + private boolean cancel = false; + + /** + * Default constructor. + */ + public IgnitePdsContinuousRestartTest() { + } + + /** + * @param cancel Cancel. + */ + public IgnitePdsContinuousRestartTest(boolean cancel) { + this.cancel = cancel; + } + /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { IgniteConfiguration cfg = super.getConfiguration(gridName); @@ -84,6 +104,7 @@ public class IgnitePdsContinuousRestartTest extends GridCommonAbstractTest { cfg.setPersistentStoreConfiguration( new PersistentStoreConfiguration() .setWalMode(WALMode.LOG_ONLY) + .setCheckpointingFrequency(checkpointDelay) ); return cfg; @@ -194,6 +215,27 @@ public void testRebalancingDuringLoad_8000_8000_8_16() throws Exception { checkRebalancingDuringLoad(8000, 8000, 8, 16); } + /** + * + * @throws Exception if failed. + */ + public void testRebalncingDuringLoad_10_10_1_1() throws Exception { + checkRebalancingDuringLoad(10, 10, 1, 1); + } + + /** + * + * @throws Exception if failed. + */ + public void testRebalncingDuringLoad_10_500_8_16() throws Exception { + checkRebalancingDuringLoad(10, 500, 8, 16); + } + + /** {@inheritDoc} */ + @Override protected long getTestTimeout() { + return TimeUnit.MINUTES.toMillis(3); + } + /** * @throws Exception if failed. */ @@ -203,6 +245,7 @@ private void checkRebalancingDuringLoad( int threads, final int batch ) throws Exception { + this.checkpointDelay = checkpointDelay; startGrids(GRID_CNT); @@ -245,7 +288,7 @@ private void checkRebalancingDuringLoad( while (System.currentTimeMillis() < end) { int idx = rnd.nextInt(GRID_CNT - 1) + 1; - stopGrid(idx); + stopGrid(idx, cancel); U.sleep(restartDelay); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes.java new file mode 100644 index 0000000000000..110e67708af40 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes.java @@ -0,0 +1,67 @@ +/* + * 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.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheWriteSynchronizationMode; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +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; + +/** + * Adding shared group and indexes to testing. It would impact how we evict partitions. + */ +public class IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes extends IgnitePdsContinuousRestartTest { + /** Ip finder. */ + private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** Cache 2 singleton group name. */ + public static final String CACHE_GROUP_NAME = "Group2"; + + /** + * Default constructor. + */ + public IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes() { + super(false); + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + TcpDiscoverySpi discoverySpi = (TcpDiscoverySpi)cfg.getDiscoverySpi(); + discoverySpi.setIpFinder(ipFinder); + + CacheConfiguration ccfg2 = new CacheConfiguration(); + + ccfg2.setName(CACHE_NAME); + ccfg2.setGroupName(CACHE_GROUP_NAME); + ccfg2.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); + ccfg2.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); + ccfg2.setAffinity(new RendezvousAffinityFunction(false, 32)); + ccfg2.setIndexedTypes(Integer.class, Integer.class); + ccfg2.setBackups(2); + + cfg.setCacheConfiguration(ccfg2); + + return cfg; + } +} 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 8c3b82586b3fb..ad29000316f14 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 @@ -19,6 +19,7 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTest; +import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsExchangeDuringCheckpointTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsPageSizesTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsRecoveryAfterFileCorruptionTest; @@ -71,9 +72,6 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteWalFlushFailoverTest.class); suite.addTestSuite(IgniteWalReaderTest.class); - - suite.addTestSuite(IgnitePdsExchangeDuringCheckpointTest.class); - return suite; } } From a6a444cb3fae84efb3b8a9db5d5a0c33cab1b6d5 Mon Sep 17 00:00:00 2001 From: Ivan Rakov Date: Mon, 25 Sep 2017 12:55:33 +0300 Subject: [PATCH 138/145] IGNITE-6342 Code style fix after cherry-pick --- .../java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ad29000316f14..4aa5ebdf86247 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 @@ -19,8 +19,6 @@ import junit.framework.TestSuite; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTest; -import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsContinuousRestartTestWithSharedGroupAndIndexes; -import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsExchangeDuringCheckpointTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsPageSizesTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePdsRecoveryAfterFileCorruptionTest; import org.apache.ignite.internal.processors.cache.persistence.IgnitePersistenceMetricsSelfTest; @@ -72,6 +70,7 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(IgniteWalFlushFailoverTest.class); suite.addTestSuite(IgniteWalReaderTest.class); + return suite; } } From 0931fdadc50b35a6302badd54a26043b4c1112f4 Mon Sep 17 00:00:00 2001 From: EdShangGG Date: Thu, 21 Sep 2017 18:36:37 +0300 Subject: [PATCH 139/145] ignite-6470 Fixed wrong casting og long value to int Signed-off-by: Andrey Gura (cherry picked from commit b98ffab) --- .../processors/cache/persistence/file/FilePageStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java index fa38b512b1edb..3122181248e14 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/file/FilePageStore.java @@ -538,6 +538,6 @@ private long allocPage() { if (!inited) return 0; - return (int)(allocated.get() - headerSize()) / pageSize; + return (int)((allocated.get() - headerSize()) / pageSize); } } From 048f4d55af11adececf44e49743ba175f08349b2 Mon Sep 17 00:00:00 2001 From: Aleksei Scherbakov Date: Thu, 2 Nov 2017 17:53:47 +0300 Subject: [PATCH 140/145] GG-13005 Sequences are not restored after snapshot / stop / cleanup / restore scenario. --- .../processors/cache/GridCacheProcessor.java | 31 ++- .../AtomicDataStructureProxy.java | 189 ++++++++++++++++++ .../DataStructuresProcessor.java | 20 ++ .../GridCacheAtomicLongImpl.java | 120 ++--------- .../GridCacheAtomicReferenceImpl.java | 114 ++--------- .../GridCacheAtomicSequenceImpl.java | 113 ++--------- .../GridCacheAtomicStampedImpl.java | 115 ++--------- .../GridCacheCountDownLatchImpl.java | 73 +------ .../datastructures/GridCacheLockImpl.java | 91 ++------- .../datastructures/GridCacheRemovable.java | 6 + .../GridCacheSemaphoreImpl.java | 85 ++------ .../AtomicCacheAffinityConfigurationTest.java | 7 +- 12 files changed, 345 insertions(+), 619 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/AtomicDataStructureProxy.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 bd950fa3bef4e..2ffadf5e1f0b1 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 @@ -1637,6 +1637,26 @@ public Collection publicCacheNames() { ); } + /** + * Gets a collection of currently started public cache names. + * + * @return Collection of currently started public cache names + */ + public Collection publicAndDsCacheNames() { + return F.viewReadOnly(cacheDescriptors().values(), + new IgniteClosure() { + @Override public String apply(DynamicCacheDescriptor desc) { + return desc.cacheConfiguration().getName(); + } + }, + new IgnitePredicate() { + @Override public boolean apply(DynamicCacheDescriptor desc) { + return desc.cacheType().userCache() || desc.cacheType() == CacheType.DATA_STRUCTURES; + } + } + ); + } + /** * Gets cache mode. * @@ -1811,8 +1831,12 @@ else if (CU.affinityNode(ctx.discovery().localNode(), desc.groupDescriptor().con onKernalStart(cache); - if (proxyRestart) + if (proxyRestart) { proxy.onRestarted(cacheCtx, cache); + + if (cacheCtx.dataStructuresCache()) + ctx.dataStructures().restart(proxy.internalProxy()); + } } /** @@ -1897,6 +1921,9 @@ private void stopGateway(DynamicCacheChangeRequest req) { // Break the proxy before exchange future is done. if (req.restart()) { + if (DataStructuresProcessor.isDataStructureCache(req.cacheName())) + ctx.dataStructures().suspend(req.cacheName()); + proxy = jCacheProxies.get(req.cacheName()); if (proxy != null) @@ -2573,6 +2600,8 @@ private IgniteInternalFuture dynamicStartCaches( ct = CacheType.UTILITY; else if (internalCaches.contains(ccfg.getName())) ct = CacheType.INTERNAL; + else if (DataStructuresProcessor.isDataStructureCache(ccfg.getName())) + ct = CacheType.DATA_STRUCTURES; else ct = CacheType.USER; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/AtomicDataStructureProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/AtomicDataStructureProxy.java new file mode 100644 index 0000000000000..214672abd1e3f --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/AtomicDataStructureProxy.java @@ -0,0 +1,189 @@ +/* + * 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.datastructures; + +import java.io.Externalizable; +import org.apache.ignite.IgniteCacheRestartingException; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.processors.cache.GridCacheContext; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport; +import org.apache.ignite.internal.util.future.GridFutureAdapter; +import org.apache.ignite.internal.util.future.IgniteFutureImpl; +import org.apache.ignite.internal.util.typedef.internal.U; + +public abstract class AtomicDataStructureProxy + implements GridCacheRemovable,IgniteChangeGlobalStateSupport { + /** Logger. */ + protected IgniteLogger log; + + /** Removed flag. */ + protected volatile boolean rmvd; + + /** Suspended future. */ + private volatile GridFutureAdapter suspendFut; + + /** Check removed flag. */ + private boolean rmvCheck; + + /** Structure name. */ + protected String name; + + /** Structure key. */ + protected GridCacheInternalKey key; + + /** Structure projection. */ + protected IgniteInternalCache cacheView; + + /** Cache context. */ + protected volatile GridCacheContext ctx; + + /** + * Empty constructor required by {@link Externalizable}. + */ + public AtomicDataStructureProxy() { + // No-op. + } + + /** + * Default constructor. + * + * @param name Structure name. + * @param key Structure key. + * @param cacheView Cache projection. + */ + public AtomicDataStructureProxy(String name, + GridCacheInternalKey key, + IgniteInternalCache cacheView) + { + assert key != null; + assert cacheView != null; + + this.ctx = cacheView.context(); + this.key = key; + this.cacheView = cacheView; + this.name = name; + + log = ctx.logger(getClass()); + } + + /** {@inheritDoc} */ + public String name() { + return name; + } + + /** {@inheritDoc} */ + public GridCacheInternalKey key() { + return key; + } + + /** {@inheritDoc} */ + public boolean removed() { + return rmvd; + } + + /** + * Check removed status. + * + * @throws IllegalStateException If removed. + */ + protected void checkRemoved() throws IllegalStateException { + if (rmvd) + throw removedError(); + + GridFutureAdapter suspendFut0 = suspendFut; + + if (suspendFut0 != null && !suspendFut0.isDone()) + throw suspendedError(); + + if (rmvCheck) { + try { + rmvd = cacheView.get(key) == null; + } + catch (IgniteCheckedException e) { + throw U.convertException(e); + } + + rmvCheck = false; + + if (rmvd) { + ctx.kernalContext().dataStructures().onRemoved(key, this); + + throw removedError(); + } + } + } + + /** + * @return Error. + */ + private IllegalStateException removedError() { + return new IllegalStateException("Sequence was removed from cache: " + name); + } + + /** + * @return Error. + */ + private IllegalStateException suspendedError() { + throw new IgniteCacheRestartingException(new IgniteFutureImpl<>(suspendFut), "Underlying cache is restarting: " + ctx.name()); + } + + /** {@inheritDoc} */ + @Override public boolean onRemoved() { + return rmvd = true; + } + + /** {@inheritDoc} */ + @Override public void needCheckNotRemoved() { + rmvCheck = true; + } + + /** {@inheritDoc} */ + @Override public void suspend() { + suspendFut = new GridFutureAdapter<>(); + } + + /** {@inheritDoc} */ + @Override public void restart(IgniteInternalCache cache) { + invalidateLocalState(); + + cacheView = cache; + ctx = cache.context(); + rmvCheck = true; + suspendFut.onDone(); + } + + /** {@inheritDoc} */ + @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { + this.ctx = kctx.cache().context().cacheContext(ctx.cacheId()); + this.cacheView = ctx.cache(); + } + + /** {@inheritDoc} */ + @Override public void onDeActivate(GridKernalContext kctx) { + // No-op. + } + + protected void invalidateLocalState() { + // No-op + } + +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/DataStructuresProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/DataStructuresProcessor.java index 33a2fd2133362..42b12985a9ce3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/DataStructuresProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/DataStructuresProcessor.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.datastructures; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -72,6 +73,7 @@ import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.A; +import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.GPR; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; @@ -662,6 +664,24 @@ private void removeDataStructure(@Nullable final IgnitePredicateX e : dsMap.entrySet()) { + String cacheName0 = ATOMICS_CACHE_NAME + "@" + e.getKey().groupName(); + + if (cacheName0.equals(cacheName)) + e.getValue().suspend(); + } + } + + public void restart(IgniteInternalCache cache) { + for (Map.Entry e : dsMap.entrySet()) { + String cacheName0 = ATOMICS_CACHE_NAME + "@" + e.getKey().groupName(); + + if (cacheName0.equals(cache.name())) + e.getValue().restart(cache); + } + } + /** * Gets an atomic reference from cache or creates one if it's not cached. * diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicLongImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicLongImpl.java index 0bc0c63b5e72d..8e6f9131de423 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicLongImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicLongImpl.java @@ -40,7 +40,8 @@ /** * Cache atomic long implementation. */ -public final class GridCacheAtomicLongImpl implements GridCacheAtomicLongEx, IgniteChangeGlobalStateSupport, Externalizable { +public final class GridCacheAtomicLongImpl extends AtomicDataStructureProxy + implements GridCacheAtomicLongEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; @@ -52,24 +53,6 @@ public final class GridCacheAtomicLongImpl implements GridCacheAtomicLongEx, Ign } }; - /** Atomic long name. */ - private String name; - - /** Removed flag.*/ - private volatile boolean rmvd; - - /** Check removed flag. */ - private boolean rmvCheck; - - /** Atomic long key. */ - private GridCacheInternalKey key; - - /** Atomic long projection. */ - private IgniteInternalCache atomicView; - - /** Cache context. */ - private GridCacheContext ctx; - /** * Empty constructor required by {@link Externalizable}. */ @@ -87,19 +70,7 @@ public GridCacheAtomicLongImpl() { public GridCacheAtomicLongImpl(String name, GridCacheInternalKey key, IgniteInternalCache atomicView) { - assert key != null; - assert atomicView != null; - assert name != null; - - this.ctx = atomicView.context(); - this.key = key; - this.atomicView = atomicView; - this.name = name; - } - - /** {@inheritDoc} */ - @Override public String name() { - return name; + super(name, key, atomicView); } /** {@inheritDoc} */ @@ -107,7 +78,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - GridCacheAtomicLongValue val = atomicView.get(key); + GridCacheAtomicLongValue val = cacheView.get(key); if (val == null) throw new IgniteException("Failed to find atomic long: " + name); @@ -124,7 +95,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try{ - EntryProcessorResult res = atomicView.invoke(key, IncrementAndGetProcessor.INSTANCE); + EntryProcessorResult res = cacheView.invoke(key, IncrementAndGetProcessor.INSTANCE); assert res != null && res.get() != null : res; @@ -143,7 +114,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, GetAndIncrementProcessor.INSTANCE); + EntryProcessorResult res = cacheView.invoke(key, GetAndIncrementProcessor.INSTANCE); assert res != null && res.get() != null : res; @@ -162,7 +133,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, new AddAndGetProcessor(l)); + EntryProcessorResult res = cacheView.invoke(key, new AddAndGetProcessor(l)); assert res != null && res.get() != null : res; @@ -181,7 +152,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, new GetAndAddProcessor(l)); + EntryProcessorResult res = cacheView.invoke(key, new GetAndAddProcessor(l)); assert res != null && res.get() != null : res; @@ -200,7 +171,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, DecrementAndGetProcessor.INSTANCE); + EntryProcessorResult res = cacheView.invoke(key, DecrementAndGetProcessor.INSTANCE); assert res != null && res.get() != null : res; @@ -219,7 +190,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, GetAndDecrementProcessor.INSTANCE); + EntryProcessorResult res = cacheView.invoke(key, GetAndDecrementProcessor.INSTANCE); assert res != null && res.get() != null : res; @@ -238,7 +209,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, new GetAndSetProcessor(l)); + EntryProcessorResult res = cacheView.invoke(key, new GetAndSetProcessor(l)); assert res != null && res.get() != null : res; @@ -257,7 +228,7 @@ public GridCacheAtomicLongImpl(String name, checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, new CompareAndSetProcessor(expVal, newVal)); + EntryProcessorResult res = cacheView.invoke(key, new CompareAndSetProcessor(expVal, newVal)); assert res != null && res.get() != null : res; @@ -280,7 +251,7 @@ public long compareAndSetAndGet(long expVal, long newVal) { checkRemoved(); try { - EntryProcessorResult res = atomicView.invoke(key, new CompareAndSetProcessor(expVal, newVal)); + EntryProcessorResult res = cacheView.invoke(key, new CompareAndSetProcessor(expVal, newVal)); assert res != null && res.get() != null : res; @@ -294,60 +265,6 @@ public long compareAndSetAndGet(long expVal, long newVal) { } } - /** - * Check removed flag. - * - * @throws IllegalStateException If removed. - */ - private void checkRemoved() throws IllegalStateException { - if (rmvd) - throw removedError(); - - if (rmvCheck) { - try { - rmvd = atomicView.get(key) == null; - } - catch (IgniteCheckedException e) { - throw U.convertException(e); - } - - rmvCheck = false; - - if (rmvd) { - ctx.kernalContext().dataStructures().onRemoved(key, this); - - throw removedError(); - } - } - } - - /** - * @return Error. - */ - private IllegalStateException removedError() { - return new IllegalStateException("Atomic long was removed from cache: " + name); - } - - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - - /** {@inheritDoc} */ - @Override public void needCheckNotRemoved() { - rmvCheck = true; - } - - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; - } - /** {@inheritDoc} */ @Override public void close() { if (rmvd) @@ -361,17 +278,6 @@ private IllegalStateException removedError() { } } - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { - this.ctx = kctx.cache().context().cacheContext(ctx.cacheId()); - this.atomicView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. - } - /** {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(ctx.kernalContext()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicReferenceImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicReferenceImpl.java index 42f16f2eaa34a..df126d9a19bfb 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicReferenceImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicReferenceImpl.java @@ -31,7 +31,6 @@ import org.apache.ignite.IgniteException; import org.apache.ignite.cache.CacheEntryProcessor; import org.apache.ignite.internal.GridKernalContext; -import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.IgniteInternalCache; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport; @@ -47,7 +46,7 @@ /** * Cache atomic reference implementation. */ -public final class GridCacheAtomicReferenceImpl implements GridCacheAtomicReferenceEx, +public final class GridCacheAtomicReferenceImpl extends AtomicDataStructureProxy> implements GridCacheAtomicReferenceEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; @@ -60,24 +59,6 @@ public final class GridCacheAtomicReferenceImpl implements GridCacheAtomicRef } }; - /** Atomic reference name. */ - private String name; - - /** Status.*/ - private volatile boolean rmvd; - - /** Check removed flag. */ - private boolean rmvCheck; - - /** Atomic reference key. */ - private GridCacheInternalKey key; - - /** Atomic reference projection. */ - private IgniteInternalCache> atomicView; - - /** Cache context. */ - private GridCacheContext> ctx; - /** * Empty constructor required by {@link Externalizable}. */ @@ -95,14 +76,7 @@ public GridCacheAtomicReferenceImpl() { public GridCacheAtomicReferenceImpl(String name, GridCacheInternalKey key, IgniteInternalCache> atomicView) { - assert key != null; - assert atomicView != null; - assert name != null; - - this.ctx = atomicView.context(); - this.key = key; - this.atomicView = atomicView; - this.name = name; + super(name, key, atomicView); } /** {@inheritDoc} */ @@ -115,7 +89,7 @@ public GridCacheAtomicReferenceImpl(String name, checkRemoved(); try { - GridCacheAtomicReferenceValue ref = atomicView.get(key); + GridCacheAtomicReferenceValue ref = cacheView.get(key); if (ref == null) throw new IgniteCheckedException("Failed to find atomic reference with given name: " + name); @@ -133,17 +107,17 @@ public GridCacheAtomicReferenceImpl(String name, try { if (ctx.dataStructures().knownType(val)) - atomicView.invoke(key, new ReferenceSetEntryProcessor<>(val)); + cacheView.invoke(key, new ReferenceSetEntryProcessor<>(val)); else { CU.retryTopologySafe(new Callable() { @Override public Void call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, atomicView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheAtomicReferenceValue ref = atomicView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheAtomicReferenceValue ref = cacheView.get(key); if (ref == null) throw new IgniteException("Failed to find atomic reference with given name: " + name); - atomicView.put(key, new GridCacheAtomicReferenceValue<>(val)); + cacheView.put(key, new GridCacheAtomicReferenceValue<>(val)); tx.commit(); } @@ -168,7 +142,7 @@ public GridCacheAtomicReferenceImpl(String name, try { if (ctx.dataStructures().knownType(expVal) && ctx.dataStructures().knownType(newVal)) { EntryProcessorResult res = - atomicView.invoke(key, new ReferenceCompareAndSetEntryProcessor<>(expVal, newVal)); + cacheView.invoke(key, new ReferenceCompareAndSetEntryProcessor<>(expVal, newVal)); assert res != null && res.get() != null : res; @@ -177,8 +151,8 @@ public GridCacheAtomicReferenceImpl(String name, else { return CU.retryTopologySafe(new Callable() { @Override public Boolean call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, atomicView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheAtomicReferenceValue ref = atomicView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheAtomicReferenceValue ref = cacheView.get(key); if (ref == null) throw new IgniteException("Failed to find atomic reference with given name: " + name); @@ -188,7 +162,7 @@ public GridCacheAtomicReferenceImpl(String name, if (!F.eq(expVal, curVal)) return false; else { - atomicView.put(key, new GridCacheAtomicReferenceValue<>(newVal)); + cacheView.put(key, new GridCacheAtomicReferenceValue<>(newVal)); tx.commit(); @@ -220,7 +194,7 @@ public T compareAndSetAndGet(final T newVal, final T expVal) { try { if (ctx.dataStructures().knownType(expVal) && ctx.dataStructures().knownType(newVal)) { EntryProcessorResult res = - atomicView.invoke(key, new ReferenceCompareAndSetAndGetEntryProcessor(expVal, newVal)); + cacheView.invoke(key, new ReferenceCompareAndSetAndGetEntryProcessor(expVal, newVal)); assert res != null; @@ -229,8 +203,8 @@ public T compareAndSetAndGet(final T newVal, final T expVal) { else { return CU.retryTopologySafe(new Callable() { @Override public T call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, atomicView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheAtomicReferenceValue ref = atomicView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheAtomicReferenceValue ref = cacheView.get(key); if (ref == null) throw new IgniteException("Failed to find atomic reference with given name: " + name); @@ -240,7 +214,7 @@ public T compareAndSetAndGet(final T newVal, final T expVal) { if (!F.eq(expVal, curVal)) return curVal; else { - atomicView.put(key, new GridCacheAtomicReferenceValue<>(newVal)); + cacheView.put(key, new GridCacheAtomicReferenceValue<>(newVal)); tx.commit(); @@ -259,26 +233,6 @@ public T compareAndSetAndGet(final T newVal, final T expVal) { } } - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - - /** {@inheritDoc} */ - @Override public void needCheckNotRemoved() { - rmvCheck = true; - } - - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; - } - /** {@inheritDoc} */ @Override public void close() { if (rmvd) @@ -292,44 +246,6 @@ public T compareAndSetAndGet(final T newVal, final T expVal) { } } - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { - this.ctx = kctx.cache().>context().cacheContext(ctx.cacheId()); - this.atomicView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. - } - - /** - * Check removed status. - * - * @throws IllegalStateException If removed. - */ - private void checkRemoved() throws IllegalStateException { - if (rmvd) - throw removedError(); - - if (rmvCheck) { - try { - rmvd = atomicView.get(key) == null; - } - catch (IgniteCheckedException e) { - throw U.convertException(e); - } - - rmvCheck = false; - - if (rmvd) { - ctx.kernalContext().dataStructures().onRemoved(key, this); - - throw removedError(); - } - } - } - /** * @return Error. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicSequenceImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicSequenceImpl.java index 019de3c464652..fd4db4a097c82 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicSequenceImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicSequenceImpl.java @@ -26,6 +26,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import org.apache.ignite.IgniteCacheRestartingException; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; @@ -34,6 +35,8 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.IgniteInternalCache; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; +import org.apache.ignite.internal.util.future.GridFutureAdapter; +import org.apache.ignite.internal.util.future.IgniteFutureImpl; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.CU; @@ -49,7 +52,8 @@ /** * Cache sequence implementation. */ -public final class GridCacheAtomicSequenceImpl implements GridCacheAtomicSequenceEx, IgniteChangeGlobalStateSupport, Externalizable { +public final class GridCacheAtomicSequenceImpl extends AtomicDataStructureProxy + implements GridCacheAtomicSequenceEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; @@ -61,27 +65,6 @@ public final class GridCacheAtomicSequenceImpl implements GridCacheAtomicSequenc } }; - /** Logger. */ - private IgniteLogger log; - - /** Sequence name. */ - private String name; - - /** Removed flag. */ - private volatile boolean rmvd; - - /** Check removed flag. */ - private boolean rmvCheck; - - /** Sequence key. */ - private GridCacheInternalKey key; - - /** Sequence projection. */ - private IgniteInternalCache seqView; - - /** Cache context. */ - private volatile GridCacheContext ctx; - /** Local value of sequence. */ @GridToStringInclude(sensitive = true) private volatile long locVal; @@ -131,24 +114,13 @@ public GridCacheAtomicSequenceImpl(String name, long locVal, long upBound) { - assert key != null; - assert seqView != null; + super(name, key, seqView); + assert locVal <= upBound; this.batchSize = batchSize; - this.ctx = seqView.context(); - this.key = key; - this.seqView = seqView; this.upBound = upBound; this.locVal = locVal; - this.name = name; - - log = ctx.logger(getClass()); - } - - /** {@inheritDoc} */ - @Override public String name() { - return name; } /** {@inheritDoc} */ @@ -291,58 +263,10 @@ private long internalUpdate(long l, @Nullable Callable updateCall, boolean } } - /** - * Check removed status. - * - * @throws IllegalStateException If removed. - */ - private void checkRemoved() throws IllegalStateException { - if (rmvd) - throw removedError(); - - if (rmvCheck) { - try { - rmvd = seqView.get(key) == null; - } - catch (IgniteCheckedException e) { - throw U.convertException(e); - } - - rmvCheck = false; - - if (rmvd) { - ctx.kernalContext().dataStructures().onRemoved(key, this); - - throw removedError(); - } - } - } - - /** - * @return Error. - */ - private IllegalStateException removedError() { - return new IllegalStateException("Sequence was removed from cache: " + name); - } - - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - /** {@inheritDoc} */ - @Override public void needCheckNotRemoved() { - rmvCheck = true; - } - - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; + @Override protected void invalidateLocalState() { + locVal = 0; + upBound = -1; } /** {@inheritDoc} */ @@ -371,8 +295,8 @@ private Callable internalUpdate(final long l, final boolean updated) { @Override public Long call() throws Exception { assert distUpdateFreeTop.isHeldByCurrentThread() || distUpdateLockedTop.isHeldByCurrentThread(); - try (GridNearTxLocal tx = CU.txStartInternal(ctx, seqView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheAtomicSequenceValue seq = seqView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheAtomicSequenceValue seq = cacheView.get(key); checkRemoved(); @@ -428,7 +352,7 @@ private Callable internalUpdate(final long l, final boolean updated) { // Global counter must be more than reserved upper bound. seq.set(newUpBound + 1); - seqView.put(key, seq); + cacheView.put(key, seq); tx.commit(); @@ -443,17 +367,6 @@ private Callable internalUpdate(final long l, final boolean updated) { }; } - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) { - ctx = kctx.cache().context().cacheContext(ctx.cacheId()); - seqView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. - } - /** {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(ctx.kernalContext()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicStampedImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicStampedImpl.java index ed7a225c2bc3c..70f3b483f13a1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicStampedImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheAtomicStampedImpl.java @@ -47,7 +47,8 @@ /** * Cache atomic stamped implementation. */ -public final class GridCacheAtomicStampedImpl implements GridCacheAtomicStampedEx, IgniteChangeGlobalStateSupport, Externalizable { +public final class GridCacheAtomicStampedImpl extends AtomicDataStructureProxy> + implements GridCacheAtomicStampedEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; @@ -59,24 +60,6 @@ public final class GridCacheAtomicStampedImpl implements GridCacheAtomicSt } }; - /** Atomic stamped name. */ - private String name; - - /** Removed flag.*/ - private volatile boolean rmvd; - - /** Check removed flag. */ - private boolean rmvCheck; - - /** Atomic stamped key. */ - private GridCacheInternalKey key; - - /** Atomic stamped projection. */ - private IgniteInternalCache> atomicView; - - /** Cache context. */ - private GridCacheContext> ctx; - /** * Empty constructor required by {@link Externalizable}. */ @@ -94,19 +77,7 @@ public GridCacheAtomicStampedImpl() { public GridCacheAtomicStampedImpl(String name, GridCacheInternalKey key, IgniteInternalCache> atomicView) { - assert key != null; - assert atomicView != null; - assert name != null; - - this.ctx = atomicView.context(); - this.key = key; - this.atomicView = atomicView; - this.name = name; - } - - /** {@inheritDoc} */ - @Override public String name() { - return name; + super(name, key, atomicView); } /** {@inheritDoc} */ @@ -114,7 +85,7 @@ public GridCacheAtomicStampedImpl(String name, checkRemoved(); try { - GridCacheAtomicStampedValue stmp = atomicView.get(key); + GridCacheAtomicStampedValue stmp = cacheView.get(key); if (stmp == null) throw new IgniteCheckedException("Failed to find atomic stamped with given name: " + name); @@ -132,17 +103,17 @@ public GridCacheAtomicStampedImpl(String name, try { if (ctx.dataStructures().knownType(val) && ctx.dataStructures().knownType(stamp)) - atomicView.invoke(key, new StampedSetEntryProcessor<>(val, stamp)); + cacheView.invoke(key, new StampedSetEntryProcessor<>(val, stamp)); else { CU.retryTopologySafe(new Callable() { @Override public Void call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, atomicView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheAtomicStampedValue ref = atomicView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheAtomicStampedValue ref = cacheView.get(key); if (ref == null) throw new IgniteException("Failed to find atomic stamped with given name: " + name); - atomicView.put(key, new GridCacheAtomicStampedValue<>(val, stamp)); + cacheView.put(key, new GridCacheAtomicStampedValue<>(val, stamp)); tx.commit(); } @@ -170,7 +141,7 @@ public GridCacheAtomicStampedImpl(String name, ctx.dataStructures().knownType(expStamp) && ctx.dataStructures().knownType(newStamp)) { EntryProcessorResult res = - atomicView.invoke(key, new StampedCompareAndSetEntryProcessor<>(expVal, expStamp, newVal, newStamp)); + cacheView.invoke(key, new StampedCompareAndSetEntryProcessor<>(expVal, expStamp, newVal, newStamp)); assert res != null && res.get() != null : res; @@ -179,14 +150,14 @@ public GridCacheAtomicStampedImpl(String name, else { return CU.retryTopologySafe(new Callable() { @Override public Boolean call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, atomicView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheAtomicStampedValue val = atomicView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheAtomicStampedValue val = cacheView.get(key); if (val == null) throw new IgniteException("Failed to find atomic stamped with given name: " + name); if (F.eq(expVal, val.value()) && F.eq(expStamp, val.stamp())) { - atomicView.put(key, new GridCacheAtomicStampedValue<>(newVal, newStamp)); + cacheView.put(key, new GridCacheAtomicStampedValue<>(newVal, newStamp)); tx.commit(); @@ -212,7 +183,7 @@ public GridCacheAtomicStampedImpl(String name, checkRemoved(); try { - GridCacheAtomicStampedValue stmp = atomicView.get(key); + GridCacheAtomicStampedValue stmp = cacheView.get(key); if (stmp == null) throw new IgniteCheckedException("Failed to find atomic stamped with given name: " + name); @@ -229,7 +200,7 @@ public GridCacheAtomicStampedImpl(String name, checkRemoved(); try { - GridCacheAtomicStampedValue stmp = atomicView.get(key); + GridCacheAtomicStampedValue stmp = cacheView.get(key); if (stmp == null) throw new IgniteCheckedException("Failed to find atomic stamped with given name: " + name); @@ -241,26 +212,6 @@ public GridCacheAtomicStampedImpl(String name, } } - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - - /** {@inheritDoc} */ - @Override public void needCheckNotRemoved() { - rmvCheck = true; - } - - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; - } - /** {@inheritDoc} */ @Override public void close() { if (rmvd) @@ -309,44 +260,6 @@ private Object readResolve() throws ObjectStreamException { } } - /** - * Check removed status. - * - * @throws IllegalStateException If removed. - */ - private void checkRemoved() throws IllegalStateException { - if (rmvd) - throw removedError(); - - if (rmvCheck) { - try { - rmvd = atomicView.get(key) == null; - } - catch (IgniteCheckedException e) { - throw U.convertException(e); - } - - rmvCheck = false; - - if (rmvd) { - ctx.kernalContext().dataStructures().onRemoved(key, this); - - throw removedError(); - } - } - } - - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { - this.ctx = kctx.cache().>context().cacheContext(ctx.cacheId()); - this.atomicView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. - } - /** * @return Error. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheCountDownLatchImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheCountDownLatchImpl.java index 7f331c339a3ab..72311c6da5503 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheCountDownLatchImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheCountDownLatchImpl.java @@ -47,7 +47,8 @@ /** * Cache count down latch implementation. */ -public final class GridCacheCountDownLatchImpl implements GridCacheCountDownLatchEx, IgniteChangeGlobalStateSupport, Externalizable { +public final class GridCacheCountDownLatchImpl extends AtomicDataStructureProxy + implements GridCacheCountDownLatchEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; @@ -68,24 +69,6 @@ public final class GridCacheCountDownLatchImpl implements GridCacheCountDownLatc } }; - /** Logger. */ - private IgniteLogger log; - - /** Latch name. */ - private String name; - - /** Removed flag.*/ - private volatile boolean rmvd; - - /** Latch key. */ - private GridCacheInternalKey key; - - /** Latch projection. */ - private IgniteInternalCache latchView; - - /** Cache context. */ - private GridCacheContext ctx; - /** Initial count. */ private int initCnt; @@ -126,30 +109,20 @@ public GridCacheCountDownLatchImpl(String name, GridCacheInternalKey key, IgniteInternalCache latchView) { + super(name, key, latchView); + assert name != null; - assert initCnt >= 0; assert key != null; assert latchView != null; - this.name = name; this.initCnt = initCnt; this.autoDel = autoDel; - this.key = key; - this.latchView = latchView; - this.ctx = latchView.context(); - - log = ctx.logger(getClass()); - } - - /** {@inheritDoc} */ - @Override public String name() { - return name; } /** {@inheritDoc} */ @Override public int count() { try { - GridCacheCountDownLatchValue latchVal = latchView.get(key); + GridCacheCountDownLatchValue latchVal = cacheView.get(key); return latchVal == null ? 0 : latchVal.get(); } @@ -224,26 +197,11 @@ public GridCacheCountDownLatchImpl(String name, } } - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - /** {@inheritDoc} */ @Override public void needCheckNotRemoved() { // No-op. } - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; - } - /** {@inheritDoc} */ @Override public void onUpdate(int cnt) { assert cnt >= 0; @@ -280,8 +238,8 @@ private void initializeLatch() throws IgniteCheckedException { try { internalLatch = retryTopologySafe(new Callable() { @Override public CountDownLatch call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, latchView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheCountDownLatchValue val = latchView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheCountDownLatchValue val = cacheView.get(key); if (val == null) { if (log.isDebugEnabled()) @@ -333,17 +291,6 @@ private void initializeLatch() throws IgniteCheckedException { } } - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { - this.ctx = kctx.cache().context().cacheContext(ctx.cacheId()); - this.latchView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. - } - /** {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(ctx.kernalContext()); @@ -402,8 +349,8 @@ private CountDownCallable(int val) { /** {@inheritDoc} */ @Override public Integer call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, latchView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheCountDownLatchValue latchVal = latchView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheCountDownLatchValue latchVal = cacheView.get(key); if (latchVal == null) { if (log.isDebugEnabled()) @@ -425,7 +372,7 @@ private CountDownCallable(int val) { latchVal.set(retVal); - latchView.put(key, latchVal); + cacheView.put(key, latchVal); tx.commit(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheLockImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheLockImpl.java index fac7eaf0203cd..f677ff55c7b0b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheLockImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheLockImpl.java @@ -67,31 +67,14 @@ /** * Cache reentrant lock implementation based on AbstractQueuedSynchronizer. */ -public final class GridCacheLockImpl implements GridCacheLockEx, IgniteChangeGlobalStateSupport, Externalizable { +public final class GridCacheLockImpl extends AtomicDataStructureProxy + implements GridCacheLockEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; /** Deserialization stash. */ private static final ThreadLocal stash = new ThreadLocal<>(); - /** Logger. */ - private IgniteLogger log; - - /** Reentrant lock name. */ - private String name; - - /** Removed flag. */ - private volatile boolean rmvd; - - /** Reentrant lock key. */ - private GridCacheInternalKey key; - - /** Reentrant lock projection. */ - private IgniteInternalCache lockView; - - /** Cache context. */ - private GridCacheContext ctx; - /** Initialization guard. */ private final AtomicBoolean initGuard = new AtomicBoolean(); @@ -513,7 +496,7 @@ final int getHoldCount() { } final boolean isLocked() throws IgniteCheckedException { - return getState() != 0 || lockView.get(key).get() != 0; + return getState() != 0 || cacheView.get(key).get() != 0; } /** @@ -524,8 +507,8 @@ boolean compareAndSetGlobalState(final int expVal, final int newVal, try { return retryTopologySafe(new Callable() { @Override public Boolean call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, lockView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheLockState val = lockView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheLockState val = cacheView.get(key); if (val == null) throw new IgniteCheckedException("Failed to find reentrant lock with given name: " + name); @@ -555,7 +538,7 @@ boolean compareAndSetGlobalState(final int expVal, final int newVal, val.setChanged(true); - lockView.put(key, val); + cacheView.put(key, val); tx.commit(); @@ -609,8 +592,8 @@ boolean synchronizeQueue(final boolean cancelled, final Thread thread) { try { return retryTopologySafe(new Callable() { @Override public Boolean call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, lockView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheLockState val = lockView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheLockState val = cacheView.get(key); if (val == null) throw new IgniteCheckedException("Failed to find reentrant lock with given name: " + name); @@ -622,7 +605,7 @@ boolean synchronizeQueue(final boolean cancelled, final Thread thread) { val.setChanged(false); - lockView.put(key, val); + cacheView.put(key, val); tx.commit(); @@ -640,7 +623,7 @@ boolean synchronizeQueue(final boolean cancelled, final Thread thread) { nodes.removeLastOccurrence(thisNode); - lockView.put(key, val); + cacheView.put(key, val); tx.commit(); @@ -705,8 +688,8 @@ protected boolean setGlobalState(final int newVal, try { return retryTopologySafe(new Callable() { @Override public Boolean call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, lockView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheLockState val = lockView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheLockState val = cacheView.get(key); if (val == null) throw new IgniteCheckedException("Failed to find reentrant lock with given name: " + name); @@ -797,7 +780,7 @@ protected boolean setGlobalState(final int newVal, val.setConditionMap(condMap); - lockView.put(key, val); + cacheView.put(key, val); tx.commit(); @@ -1054,16 +1037,7 @@ protected IgniteConditionObject(String name, ConditionObject obj) { public GridCacheLockImpl(String name, GridCacheInternalKey key, IgniteInternalCache lockView) { - assert name != null; - assert key != null; - assert lockView != null; - - this.name = name; - this.key = key; - this.lockView = lockView; - this.ctx = lockView.context(); - - log = ctx.logger(getClass()); + super(name, key, lockView); } /** @@ -1074,8 +1048,8 @@ private void initializeReentrantLock() throws IgniteCheckedException { try { sync = retryTopologySafe(new Callable() { @Override public Sync call() throws Exception { - try (GridNearTxLocal tx = CU.txStartInternal(ctx, lockView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheLockState val = lockView.get(key); + try (GridNearTxLocal tx = CU.txStartInternal(ctx, cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheLockState val = cacheView.get(key); if (val == null) { if (log.isDebugEnabled()) @@ -1183,11 +1157,6 @@ private void initializeReentrantLock() throws IgniteCheckedException { sync.release(0); } - /** {@inheritDoc} */ - @Override public String name() { - return name; - } - /** {@inheritDoc} */ @Override public void lock() { ctx.kernalContext().gateway().readLock(); @@ -1453,35 +1422,9 @@ private void initializeReentrantLock() throws IgniteCheckedException { } } - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; - } - - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - /** {@inheritDoc} */ @Override public void needCheckNotRemoved() { - - } - - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { - this.ctx = kctx.cache().context().cacheContext(ctx.cacheId()); - this.lockView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. + // no-op } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheRemovable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheRemovable.java index e222e5777eab7..d26a153f0289d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheRemovable.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheRemovable.java @@ -17,6 +17,8 @@ package org.apache.ignite.internal.processors.datastructures; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; + /** * Provides callback for marking object as removed. */ @@ -32,4 +34,8 @@ public interface GridCacheRemovable { * */ public void needCheckNotRemoved(); + + public void suspend(); + + public void restart(IgniteInternalCache cache); } \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java index 4abefc970d9ff..9502a6fd3cd5c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastructures/GridCacheSemaphoreImpl.java @@ -59,7 +59,8 @@ * acquired by the failing node. In case this parameter is false, IgniteInterruptedException is called on every node * waiting on this semaphore. */ -public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, IgniteChangeGlobalStateSupport, Externalizable { +public final class GridCacheSemaphoreImpl extends AtomicDataStructureProxy + implements GridCacheSemaphoreEx, IgniteChangeGlobalStateSupport, Externalizable { /** */ private static final long serialVersionUID = 0L; @@ -71,24 +72,6 @@ public final class GridCacheSemaphoreImpl implements GridCacheSemaphoreEx, Ignit } }; - /** Logger. */ - private IgniteLogger log; - - /** Semaphore name. */ - private String name; - - /** Removed flag. */ - private volatile boolean rmvd; - - /** Semaphore key. */ - private GridCacheInternalKey key; - - /** Semaphore projection. */ - private IgniteInternalCache semView; - - /** Cache context. */ - private GridCacheContext ctx; - /** Initialization guard. */ private final AtomicBoolean initGuard = new AtomicBoolean(); @@ -290,10 +273,10 @@ boolean compareAndSetGlobalState(final int expVal, final int newVal, final boole return retryTopologySafe(new Callable() { @Override public Boolean call() throws Exception { try (GridNearTxLocal tx = CU.txStartInternal(ctx, - semView, + cacheView, PESSIMISTIC, REPEATABLE_READ) ) { - GridCacheSemaphoreState val = semView.get(key); + GridCacheSemaphoreState val = cacheView.get(key); if (val == null) throw new IgniteCheckedException("Failed to find semaphore with given name: " + @@ -328,7 +311,7 @@ boolean compareAndSetGlobalState(final int expVal, final int newVal, final boole val.setCount(newVal); - semView.put(key, val); + cacheView.put(key, val); tx.commit(); } @@ -370,10 +353,10 @@ boolean releaseFailedNode(final UUID nodeId, final boolean broken) { @Override public Boolean call() throws Exception { try ( GridNearTxLocal tx = CU.txStartInternal(ctx, - semView, + cacheView, PESSIMISTIC, REPEATABLE_READ) ) { - GridCacheSemaphoreState val = semView.get(key); + GridCacheSemaphoreState val = cacheView.get(key); if (val == null) throw new IgniteCheckedException("Failed to find semaphore with given name: " + @@ -391,7 +374,7 @@ boolean releaseFailedNode(final UUID nodeId, final boolean broken) { if (broken) { val.setBroken(true); - semView.put(key, val); + cacheView.put(key, val); tx.commit(); @@ -415,7 +398,7 @@ boolean releaseFailedNode(final UUID nodeId, final boolean broken) { val.setWaiters(map); - semView.put(key, val); + cacheView.put(key, val); sync.nodeMap = map; @@ -457,16 +440,7 @@ public GridCacheSemaphoreImpl( GridCacheInternalKey key, IgniteInternalCache semView ) { - assert name != null; - assert key != null; - assert semView != null; - - this.name = name; - this.key = key; - this.semView = semView; - this.ctx = semView.context(); - - log = ctx.logger(getClass()); + super(name, key, semView); } /** @@ -478,8 +452,8 @@ private void initializeSemaphore() throws IgniteCheckedException { sync = retryTopologySafe(new Callable() { @Override public Sync call() throws Exception { try (GridNearTxLocal tx = CU.txStartInternal(ctx, - semView, PESSIMISTIC, REPEATABLE_READ)) { - GridCacheSemaphoreState val = semView.get(key); + cacheView, PESSIMISTIC, REPEATABLE_READ)) { + GridCacheSemaphoreState val = cacheView.get(key); if (val == null) { if (log.isDebugEnabled()) @@ -520,26 +494,6 @@ private void initializeSemaphore() throws IgniteCheckedException { } } - /** {@inheritDoc} */ - @Override public String name() { - return name; - } - - /** {@inheritDoc} */ - @Override public GridCacheInternalKey key() { - return key; - } - - /** {@inheritDoc} */ - @Override public boolean removed() { - return rmvd; - } - - /** {@inheritDoc} */ - @Override public boolean onRemoved() { - return rmvd = true; - } - /** {@inheritDoc} */ @Override public void onUpdate(GridCacheSemaphoreState val) { if (sync == null) @@ -722,9 +676,9 @@ private void initializeSemaphore() throws IgniteCheckedException { @Override public Integer call() throws Exception { try ( GridNearTxLocal tx = CU.txStartInternal(ctx, - semView, PESSIMISTIC, REPEATABLE_READ) + cacheView, PESSIMISTIC, REPEATABLE_READ) ) { - GridCacheSemaphoreState val = semView.get(key); + GridCacheSemaphoreState val = cacheView.get(key); if (val == null) throw new IgniteException("Failed to find semaphore with given name: " + name); @@ -961,17 +915,6 @@ private void initializeSemaphore() throws IgniteCheckedException { } } - /** {@inheritDoc} */ - @Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException { - this.ctx = kctx.cache().context().cacheContext(ctx.cacheId()); - this.semView = ctx.cache(); - } - - /** {@inheritDoc} */ - @Override public void onDeActivate(GridKernalContext kctx) { - // No-op. - } - /** {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(ctx.kernalContext()); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/AtomicCacheAffinityConfigurationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/AtomicCacheAffinityConfigurationTest.java index 623b0768c246a..3fd4e1f476807 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/AtomicCacheAffinityConfigurationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/AtomicCacheAffinityConfigurationTest.java @@ -26,6 +26,7 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.processors.datastructures.AtomicDataStructureProxy; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; @@ -58,7 +59,7 @@ public void testRendezvousAffinity() throws Exception { IgniteAtomicLong atomic = igniteEx.atomicLong("test", 0, true); - GridCacheContext cctx = GridTestUtils.getFieldValue(atomic, "ctx"); + GridCacheContext cctx = GridTestUtils.getFieldValue(atomic, AtomicDataStructureProxy.class, "ctx"); AffinityFunction aff = cctx.config().getAffinity(); @@ -90,7 +91,7 @@ public void testTestAffinity() throws Exception { IgniteAtomicLong atomic = igniteEx.atomicLong("test", 0, true); - GridCacheContext cctx = GridTestUtils.getFieldValue(atomic, "ctx"); + GridCacheContext cctx = GridTestUtils.getFieldValue(atomic, AtomicDataStructureProxy.class, "ctx"); TestAffinityFunction aff = (TestAffinityFunction) cctx.config().getAffinity(); @@ -122,7 +123,7 @@ public void testDefaultAffinity() throws Exception { IgniteAtomicLong atomic = igniteEx.atomicLong("test", 0, true); - GridCacheContext cctx = GridTestUtils.getFieldValue(atomic, "ctx"); + GridCacheContext cctx = GridTestUtils.getFieldValue(atomic, AtomicDataStructureProxy.class, "ctx"); AffinityFunction aff = cctx.config().getAffinity(); From 4e9a1bee1958995b95c4cbf86e1cbac435d697cc Mon Sep 17 00:00:00 2001 From: Alexey Goncharuk Date: Wed, 18 Oct 2017 10:59:53 +0300 Subject: [PATCH 141/145] IGNITE-6595 Cleanup entries after index rebuild --- .../internal/processors/query/h2/IgniteH2Indexing.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 0f97a4ba1e975..decfbcc5c35c1 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 @@ -1793,8 +1793,10 @@ private void cleanupStatementCache() { KeyCacheObject key = keyIter.next(); while (true) { + GridCacheEntryEx entry = null; + try { - GridCacheEntryEx entry = cctx.isNear() ? + entry = cctx.isNear() ? cctx.near().dht().entryEx(key) : cctx.cache().entryEx(key); entry.ensureIndexed(); @@ -1807,6 +1809,9 @@ private void cleanupStatementCache() { catch (GridDhtInvalidPartitionException ignore) { break; } + finally { + entry.context().evicts().touch(entry, AffinityTopologyVersion.NONE); + } } } finally { From fdc4dbc93457ed796de3dfb21c2bd9a4ad1de237 Mon Sep 17 00:00:00 2001 From: Ilya Lantukh Date: Thu, 14 Sep 2017 10:44:29 +0300 Subject: [PATCH 142/145] IGNITE-6340 Fixed incorrect client cache creation on server side. Fixes #2635 --- .../processors/cache/GridCacheProcessor.java | 2 +- .../cache/ConcurrentCacheStartTest.java | 71 +++++++++++++++++++ .../testsuites/IgniteCacheTestSuite5.java | 3 + 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/ConcurrentCacheStartTest.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 2ffadf5e1f0b1..9f1dfbbf58b4b 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 @@ -3805,7 +3805,7 @@ private DynamicCacheChangeRequest prepareCacheChangeRequest( // If local node has near cache, return success. req.clientStartOnly(true); } - else + else if (!CU.affinityNode(ctx.discovery().localNode(), descCfg.getNodeFilter())) req.clientStartOnly(true); req.deploymentId(desc.deploymentId()); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/ConcurrentCacheStartTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/ConcurrentCacheStartTest.java new file mode 100644 index 0000000000000..1d97ed922f239 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/ConcurrentCacheStartTest.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; + +import java.util.ArrayList; +import java.util.List; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * + */ +public class ConcurrentCacheStartTest extends GridCommonAbstractTest { + /** + * @throws Exception If failed. + */ + public void test() throws Exception { + try { + final IgniteEx ignite = (IgniteEx) startGrids(4); + + for (int k = 0; k < 100; k++) { + final String cacheName = "cache" + k; + + GridTestUtils.runMultiThreaded(new Runnable() { + @Override public void run() { + try { + ignite.context().cache().dynamicStartCache( + new CacheConfiguration().setName(cacheName), + cacheName, + null, + false, + false, + false + ).get(); + + assertNotNull(ignite.context().cache().cache(cacheName)); + } + catch (IgniteCheckedException ex) { + throw new IgniteException(ex); + } + } + }, 10, "cache-start"); + } + } + finally { + stopAllGrids(); + } + + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java index 4ea02977746f4..6303180c11c12 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java @@ -30,6 +30,7 @@ import org.apache.ignite.internal.processors.cache.CacheSerializableTransactionsTest; import org.apache.ignite.internal.processors.cache.ClusterStatePartitionedSelfTest; import org.apache.ignite.internal.processors.cache.ClusterStateReplicatedSelfTest; +import org.apache.ignite.internal.processors.cache.ConcurrentCacheStartTest; import org.apache.ignite.internal.processors.cache.EntryVersionConsistencyReadThroughTest; import org.apache.ignite.internal.processors.cache.IgniteCachePutStackOverflowSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheReadThroughEvictionsVariationsSuite; @@ -98,6 +99,8 @@ public static TestSuite suite() throws Exception { suite.addTestSuite(GridCachePartitionEvictionDuringReadThroughSelfTest.class); suite.addTestSuite(NotMappedPartitionInTxTest.class); + suite.addTestSuite(ConcurrentCacheStartTest.class); + suite.addTestSuite(Cache64kPartitionsTest.class); return suite; From 24151538d58f15e25aa0f0f9b98dc52af0deed50 Mon Sep 17 00:00:00 2001 From: Pavel Tupitsyn Date: Wed, 6 Sep 2017 16:54:25 +0300 Subject: [PATCH 143/145] IGNITE-6253 .NET: Use DocFX for API documentation This closes #2588 --- modules/clients/pom.xml | 6 +- .../Resource/InstanceResourceAttribute.cs | 3 +- modules/platforms/dotnet/docfx/.gitignore | 9 ++ .../dotnet/docfx/Apache.Ignite.docfx.json | 88 ++++++++++++++++++ modules/platforms/dotnet/docfx/README.txt | 2 + modules/platforms/dotnet/docfx/api/index.md | 5 + .../platforms/dotnet/docfx/filterConfig.yml | 3 + .../platforms/dotnet/docfx/generate-docs.cmd | 1 + .../platforms/dotnet/docfx/images/favicon.ico | Bin 0 -> 1150 bytes .../dotnet/docfx/images/logo_ignite_32_32.png | Bin 0 -> 1676 bytes modules/platforms/dotnet/docfx/index.md | 10 ++ modules/platforms/dotnet/docfx/toc.yml | 3 + parent/pom.xml | 1 + 13 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 modules/platforms/dotnet/docfx/.gitignore create mode 100644 modules/platforms/dotnet/docfx/Apache.Ignite.docfx.json create mode 100644 modules/platforms/dotnet/docfx/README.txt create mode 100644 modules/platforms/dotnet/docfx/api/index.md create mode 100644 modules/platforms/dotnet/docfx/filterConfig.yml create mode 100644 modules/platforms/dotnet/docfx/generate-docs.cmd create mode 100644 modules/platforms/dotnet/docfx/images/favicon.ico create mode 100644 modules/platforms/dotnet/docfx/images/logo_ignite_32_32.png create mode 100644 modules/platforms/dotnet/docfx/index.md create mode 100644 modules/platforms/dotnet/docfx/toc.yml diff --git a/modules/clients/pom.xml b/modules/clients/pom.xml index 7ef8a0aa440c4..e0ba552349928 100644 --- a/modules/clients/pom.xml +++ b/modules/clients/pom.xml @@ -173,11 +173,9 @@ prepare-package - - + + - - diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Resource/InstanceResourceAttribute.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Resource/InstanceResourceAttribute.cs index 8b34c10d12f84..d63163ccbb65a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Resource/InstanceResourceAttribute.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Resource/InstanceResourceAttribute.cs @@ -19,11 +19,10 @@ namespace Apache.Ignite.Core.Resource { using System; using Apache.Ignite.Core.Compute; - using Apache.Ignite.Core.Impl.Compute; /// /// Attribute which injects instance. Can be defined inside - /// implementors of and interfaces. + /// implementors of and interfaces. /// Can be applied to non-static fields, properties and methods returning void and /// accepting a single parameter. /// diff --git a/modules/platforms/dotnet/docfx/.gitignore b/modules/platforms/dotnet/docfx/.gitignore new file mode 100644 index 0000000000000..4378419e7fcbf --- /dev/null +++ b/modules/platforms/dotnet/docfx/.gitignore @@ -0,0 +1,9 @@ +############### +# folder # +############### +/**/DROP/ +/**/TEMP/ +/**/packages/ +/**/bin/ +/**/obj/ +_site diff --git a/modules/platforms/dotnet/docfx/Apache.Ignite.docfx.json b/modules/platforms/dotnet/docfx/Apache.Ignite.docfx.json new file mode 100644 index 0000000000000..3161ae209655a --- /dev/null +++ b/modules/platforms/dotnet/docfx/Apache.Ignite.docfx.json @@ -0,0 +1,88 @@ +{ + "metadata": [ + { + "src": [ + { + "src": "../", + "files": [ + "**/Apache.Ignite.Core.csproj", + "**/Apache.Ignite.Linq.csproj", + "**/Apache.Ignite.AspNet.csproj", + "**/Apache.Ignite.EntityFramework.csproj", + "**/Apache.Ignite.NLog.csproj", + "**/Apache.Ignite.Log4Net.csproj" + ], + "exclude": [ + "**/obj/**", + "**/bin/**", + ] + } + ], + "dest": "api", + "filter": "filterConfig.yml" + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ], + "exclude": [ + "obj/**", + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ], + "exclude": [ + "obj/**", + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + ] + } + ], + "dest": "../../../clients/target/dotnetdoc", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default" + ], + "postProcessors": [], + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "globalMetadata": { + "_gitContribute": { + "repo": "https://github.com/apache/ignite", + "branch": "master" + }, + "_enableSearch": true, + "_appTitle": "Apache Ignite.NET", + "_appFaviconPath": "images/favicon.ico", + "_appLogoPath": "images/logo_ignite_32_32.png", + "_appFooter": "© 2015 - 2017 The Apache Software Foundation", + "_disableContribution": true + } + } +} \ No newline at end of file diff --git a/modules/platforms/dotnet/docfx/README.txt b/modules/platforms/dotnet/docfx/README.txt new file mode 100644 index 0000000000000..4698356946c8e --- /dev/null +++ b/modules/platforms/dotnet/docfx/README.txt @@ -0,0 +1,2 @@ +Apache Ignite.NET DocFX Project +See https://dotnet.github.io/docfx/ for more details. \ No newline at end of file diff --git a/modules/platforms/dotnet/docfx/api/index.md b/modules/platforms/dotnet/docfx/api/index.md new file mode 100644 index 0000000000000..97b7f3ad63cad --- /dev/null +++ b/modules/platforms/dotnet/docfx/api/index.md @@ -0,0 +1,5 @@ +# Apache Ignite.NET API Documentation + +Click namespaces on the left to browse the API, or use the search box on top of the page. + +[Ignition](Apache.Ignite.Core.Ignition.html) class is the API entry point, see various `Start` overloads. \ No newline at end of file diff --git a/modules/platforms/dotnet/docfx/filterConfig.yml b/modules/platforms/dotnet/docfx/filterConfig.yml new file mode 100644 index 0000000000000..98b1615b18acd --- /dev/null +++ b/modules/platforms/dotnet/docfx/filterConfig.yml @@ -0,0 +1,3 @@ +apiRules: + - exclude: + uidRegex: \.Impl\. \ No newline at end of file diff --git a/modules/platforms/dotnet/docfx/generate-docs.cmd b/modules/platforms/dotnet/docfx/generate-docs.cmd new file mode 100644 index 0000000000000..4babc29dcb4ab --- /dev/null +++ b/modules/platforms/dotnet/docfx/generate-docs.cmd @@ -0,0 +1 @@ +docfx Apache.Ignite.docfx.json --build \ No newline at end of file diff --git a/modules/platforms/dotnet/docfx/images/favicon.ico b/modules/platforms/dotnet/docfx/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b36f8d724e668c50b141347d08a4aad0fb504435 GIT binary patch literal 1150 zcmb8u&npCB9LMqRBE(@=3CXV{oJiS&)0UGs_*opdxLA90z)22Lk^@&RF3Lgv05>No z4oE2_R^hT*Y}vz__5QZ=)HKGhyS;X1zTfBbJTv?3iUfFvLc(X0M2keKM5G;*sO0J# zMCj%7YN<;Upccbu{42|tLbPHG^Vr2Xwy=bX-{zpFYsC=97qWQ91~mVIuus08{w*|n zhDG#a2cJk|5H((NP}lq^=Fos~=)GIedwGK9w^8YygIY0q(20HMnwnEOhd#$FJ}}_U z>oxT-u3&O{e8W11pwFkZ8F!vq5Jz_3^ZD{E@>yKspq=)Rk0dFOo8 zjPpA(PWK>!J(&Cf=cRr*XOFP~Ov3yIV$S}5&UHPkK)-_@`<=%eQSu82cxAF#5i25+ g*J_b8l5HaQCn66eBJm*Tr5KliwVb2bZ;-JN`2YX_ literal 0 HcmV?d00001 diff --git a/modules/platforms/dotnet/docfx/images/logo_ignite_32_32.png b/modules/platforms/dotnet/docfx/images/logo_ignite_32_32.png new file mode 100644 index 0000000000000000000000000000000000000000..c5e405645f09769fc1f38850a2040846f5853b02 GIT binary patch literal 1676 zcmZuyYc!kb7XD~lD$)o_7?LzvVj@AMl$;<_nNq5#YrE!1hPbq;Yg!YUP|?*v8g*+0 z<)BT~sJe{Q?If*|C2mDbr>;?S&`xWjsOD79iSv7Y?ES2FKR@>SKJT;k&JFNC3DGmx z0{{TxT0VqzD;mT4WL-^X)*w`6o9|bwA5!*ly}f+07$n6fUFz0RY}o0l=*c08EoAy8{2ACiW+JN2CG(*l;g2CgwO_)I~EUC7gLSF@~8&OQ8cv zwD<%jK7r0e5%3OpM;E7r>L>uvn)C4@2gjWl3G@BgBE5j#e$3fxJlh~Oo>xqZeGlqsQdY{ zCdqB;p_X#|LfJBlW%u~H?D+Ht^tx&@dyjky;n|XsoyTSVe6ae3EA*Po7G_ zlJZuwEq^t%&VVssB0~PtaCn&T`48tYh*?&ibh2$u@7^&wWhuDu8K9~NvoAqVjyH2( zPZjXkiQBF)*`G-nvU4E1crPvxHjIxtpMhD| z3&cB5zA4a$j@>vWV~`JDOr8p^;NW(8zKbL_LDk||R65S=)8@pwmBhDeerBA)l|uxc zxBZ{E#LH?cWG|WBcII0cR<0PE8=P+Z8i+(2m}Wh1B!Y@;oyc$I?8=O!5~SIe^7~X_ z#?;Fw>A+jEs$*+KetqvESAYa5yn~+ICAUF$2G+fIS_4syJf!jT_t(-)M{5~A^|3_f zs-d@mYT@?HxFUaI8fif#ew5%_7LJbO@TfzO>cA zZu?-7Ry1x4&(vir**L6?&S$>zH%jQJGCU$zoEcD<78>+58t@`oT(4 zBmt}GcOY(W;{yFZ=Jvui?wY??er5m>klvMBE4X7<#FC-^4i>}bKY3V~Xc0>Z)9vbQ O0QgY+y_&wGX8#XW-Thwx literal 0 HcmV?d00001 diff --git a/modules/platforms/dotnet/docfx/index.md b/modules/platforms/dotnet/docfx/index.md new file mode 100644 index 0000000000000..d240c0b0e6aec --- /dev/null +++ b/modules/platforms/dotnet/docfx/index.md @@ -0,0 +1,10 @@ +# Apache Ignite.NET In-Memory Data Fabric + + + +Apache Ignite In-Memory Data Fabric is designed to deliver uncompromised performance for a wide set of in-memory computing use cases from +[high performance computing](https://ignite.apache.org/features.html), to the industry most advanced [data grid](https://ignite.apache.org/features.html), +highly available [service grid](https://ignite.apache.org/features.html), and [streaming](https://ignite.apache.org/features.html). + +* [API Documentation](/api) +* [Gettting Started](https://apacheignite-net.readme.io/docs/getting-started) \ No newline at end of file diff --git a/modules/platforms/dotnet/docfx/toc.yml b/modules/platforms/dotnet/docfx/toc.yml new file mode 100644 index 0000000000000..7ad0d0dec4205 --- /dev/null +++ b/modules/platforms/dotnet/docfx/toc.yml @@ -0,0 +1,3 @@ +- name: Apache Ignite.NET API Documentation + href: api/ + homepage: api/index.md diff --git a/parent/pom.xml b/parent/pom.xml index 7443753a18d01..e716254d93772 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -42,6 +42,7 @@ UTF-8 MMMM d yyyy doxygen + docfx git 2.5.4 -XDenableSunApiLintControl From bf2fbcea6010b6de13ecd8fb7797c4a0ffbc97a0 Mon Sep 17 00:00:00 2001 From: Ilya Kasnacheev Date: Thu, 7 Sep 2017 16:41:44 +0300 Subject: [PATCH 144/145] Fixed Licenses & Javadoc tests. Signed-off-by: nikolay_tikhonov # Conflicts: # modules/clients/src/test/keystore/ca/oneca.cnf # modules/clients/src/test/keystore/ca/twoca.cnf --- .../platforms/dotnet/docfx/filterConfig.yml | 18 +++++++++++++++++- modules/platforms/dotnet/docfx/toc.yml | 16 ++++++++++++++++ parent/pom.xml | 6 ++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/modules/platforms/dotnet/docfx/filterConfig.yml b/modules/platforms/dotnet/docfx/filterConfig.yml index 98b1615b18acd..5d249d13e57d7 100644 --- a/modules/platforms/dotnet/docfx/filterConfig.yml +++ b/modules/platforms/dotnet/docfx/filterConfig.yml @@ -1,3 +1,19 @@ + +# 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. + apiRules: - exclude: - uidRegex: \.Impl\. \ No newline at end of file + uidRegex: \.Impl\. diff --git a/modules/platforms/dotnet/docfx/toc.yml b/modules/platforms/dotnet/docfx/toc.yml index 7ad0d0dec4205..a501df799cec5 100644 --- a/modules/platforms/dotnet/docfx/toc.yml +++ b/modules/platforms/dotnet/docfx/toc.yml @@ -1,3 +1,19 @@ + +# 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. + - name: Apache Ignite.NET API Documentation href: api/ homepage: api/index.md diff --git a/parent/pom.xml b/parent/pom.xml index e716254d93772..19d3498ee25d2 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -799,12 +799,18 @@ **/*.sql **/*README*.txt **/*README*.md + **/*index*.md **/*.timestamp **/*.iml **/pom-installed.xml **/keystore/*.jks **/keystore/*.pem **/keystore/*.pfx + **/keystore/ca/*.jks + **/keystore/ca/*.key + **/keystore/ca/*.txt + **/keystore/ca/*.txt.attr + **/keystore/ca/*serial **/META-INF/services/** idea/ignite_codeStyle.xml From be591fcd1f378647b41e2c9cc0cb4aa3f95d0a7a Mon Sep 17 00:00:00 2001 From: Pavel Tupitsyn Date: Fri, 29 Sep 2017 15:03:17 +0300 Subject: [PATCH 145/145] .NET: Fix API docs link --- modules/platforms/dotnet/docfx/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/platforms/dotnet/docfx/index.md b/modules/platforms/dotnet/docfx/index.md index d240c0b0e6aec..4472cff1cd87b 100644 --- a/modules/platforms/dotnet/docfx/index.md +++ b/modules/platforms/dotnet/docfx/index.md @@ -6,5 +6,5 @@ Apache Ignite In-Memory Data Fabric is designed to deliver uncompromised perform [high performance computing](https://ignite.apache.org/features.html), to the industry most advanced [data grid](https://ignite.apache.org/features.html), highly available [service grid](https://ignite.apache.org/features.html), and [streaming](https://ignite.apache.org/features.html). -* [API Documentation](/api) +* [API Documentation](api/) * [Gettting Started](https://apacheignite-net.readme.io/docs/getting-started) \ No newline at end of file