This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 60f0f5697fb IGNITE-28833 Add system views for cache locks - Fixes
#13347.
60f0f5697fb is described below
commit 60f0f5697fb814753605bb6c88c53f2255deb4da
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Jul 17 09:16:18 2026 +0300
IGNITE-28833 Add system views for cache locks - Fixes #13347.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
docs/_docs/monitoring-metrics/system-views.adoc | 34 ++
.../apache/ignite/internal/binary/BinaryUtils.java | 4 +-
.../internal/jdbc2/JdbcMetadataSelfTest.java | 4 +-
.../ignite/jdbc/thin/JdbcThinMetadataSelfTest.java | 22 +-
.../apache/ignite/util/SystemViewCommandTest.java | 4 +-
.../cache/GridCacheExplicitLockSpan.java | 16 +
.../internal/processors/cache/GridCacheMvcc.java | 36 +-
.../processors/cache/GridCacheMvccManager.java | 50 +++
.../spi/systemview/view/CacheExplicitLockView.java | 73 ++++
.../ignite/spi/systemview/view/CacheLockView.java | 115 ++++++
.../spi/systemview/view/TransactionView.java | 10 +-
.../internal/metric/SystemViewLocksTest.java | 446 +++++++++++++++++++++
.../ignite/testsuites/IgniteCacheTestSuite13.java | 2 +
.../cache/metric/SqlViewExporterSpiTest.java | 4 +-
.../processors/query/SqlSystemViewsSelfTest.java | 55 +++
15 files changed, 863 insertions(+), 12 deletions(-)
diff --git a/docs/_docs/monitoring-metrics/system-views.adoc
b/docs/_docs/monitoring-metrics/system-views.adoc
index b6c684e513b..9f3a4fc7424 100644
--- a/docs/_docs/monitoring-metrics/system-views.adoc
+++ b/docs/_docs/monitoring-metrics/system-views.adoc
@@ -275,6 +275,7 @@ Each row in this view represents a transaction object on
the node where the view
|ORIGINATING_NODE_ID | UUID | ID of the node that initiated the current
transaction object. For a transaction object mapped to a primary partition,
this is the transaction initiator node; for a transaction object mapped to a
backup partition, this is the node that owns the primary partition
|STATE | string | Current transaction state
|XID | UUID | Unique transaction identifier
+|ORIGINATING_XID | UUID | Ttransaction ID on originating node
|LABEL | string | Transaction label
|START_TIME | long | Start time of the transaction on this node
|ISOLATION | string | Transaction isolation level
@@ -299,6 +300,39 @@ Each row in this view represents a transaction object on
the node where the view
|TOP_VER | string | Topology version assigned to the transaction. If not
assigned explicitly, it is a topology version of the latest partition exchange.
|===
+== CACHE_EXPLICIT_LOCKS
+
+This view exposes information about explicit locks acquired on the current
node.
+Explicit locks are acquired using the `Cache.lock(key)` API.
+
+[{table_opts}]
+|===
+|NAME | TYPE | DESCRIPTION
+|CACHE_ID | int | Cache ID
+|KEY | string | Locked key
+|THREAD_ID | long | ID of the thread that acquired the lock
+|XID | UUID | Lock ID (unique identifier)
+|===
+
+== CACHE_LOCKS
+
+This view exposes information about all locks (both explicit and
transactional) requested for keys hosted on the current node.
+This includes locks from remote nodes trying to access primary keys on this
node.
+
+[{table_opts}]
+|===
+|NAME | TYPE | DESCRIPTION
+|CACHE_ID | int | Cache ID
+|KEY | string | Locked key
+|NODE_ID | UUID | ID of the node where the lock is hosted
+|ORIGINATING_NODE_ID | UUID | ID of the node that initiated the lock request
+|IS_OWNER | boolean | `True` if the lock is currently owned
+|IS_TX | boolean | `True` if the lock is part of a transaction
+|FLAGS | short | All flags for the lock. This field represents a bit mask with
possible values: LOCAL(0x01), OWNER(0x02), READY(0x04), REENTRY(0x08),
USED(0x10), TX(0x40), SINGLE_IMPLICIT(0x80), DHT_LOCAL(0x100),
NEAR_LOCAL(0x200), REMOVED(0x400), READ(0x800);
+|XID | UUID | Current transaction ID (or explicit lock ID)
+|ORIGINATING_XID | UUID | Originating transaction ID (or explicit lock ID)
+|===
+
== NODES
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
index fcbd74fe0c5..14a97520c81 100644
---
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
+++
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
@@ -3291,7 +3291,7 @@ public class BinaryUtils {
* @param ver Cache version.
* @return Version represented as {@code IgniteUuid}.
*/
- public static IgniteUuid asIgniteUuid(GridCacheVersion ver) {
- return new IgniteUuid(new UUID(ver.topologyVersion(),
ver.nodeOrderAndDrIdRaw()), ver.order());
+ public static @Nullable IgniteUuid asIgniteUuid(@Nullable GridCacheVersion
ver) {
+ return ver == null ? null : new IgniteUuid(new
UUID(ver.topologyVersion(), ver.nodeOrderAndDrIdRaw()), ver.order());
}
}
diff --git
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
index 3f989f2e6ae..0604f506b8c 100755
---
a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
+++
b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcMetadataSelfTest.java
@@ -369,7 +369,9 @@ public class JdbcMetadataSelfTest extends
GridCommonAbstractTest {
"STATISTICS_LOCAL_DATA",
"STATISTICS_GLOBAL_DATA",
"SQL_PLANS_HISTORY",
- "IGNITE_PLUGINS"
+ "IGNITE_PLUGINS",
+ "CACHE_EXPLICIT_LOCKS",
+ "CACHE_LOCKS"
));
Set<String> actViews = new TreeSet<>();
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 b1bd8246ff9..075c75161cf 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
@@ -466,7 +466,9 @@ public class JdbcThinMetadataSelfTest extends
JdbcThinAbstractSelfTest {
"SYS.STATISTICS_CONFIGURATION",
"SYS.PAGES_TIMESTAMP_HISTOGRAM",
"SYS.SQL_PLANS_HISTORY",
- "SYS.IGNITE_PLUGINS"
+ "SYS.IGNITE_PLUGINS",
+ "SYS.CACHE_EXPLICIT_LOCKS",
+ "SYS.CACHE_LOCKS"
))
);
}
@@ -955,6 +957,7 @@ public class JdbcThinMetadataSelfTest extends
JdbcThinAbstractSelfTest {
"SYS.TRANSACTIONS.LOCAL_NODE_ID.null",
"SYS.TRANSACTIONS.STATE.null",
"SYS.TRANSACTIONS.XID.null",
+ "SYS.TRANSACTIONS.ORIGINATING_XID.null",
"SYS.TRANSACTIONS.LABEL.null",
"SYS.TRANSACTIONS.START_TIME.null",
"SYS.TRANSACTIONS.ISOLATION.null",
@@ -1178,8 +1181,21 @@ public class JdbcThinMetadataSelfTest extends
JdbcThinAbstractSelfTest {
"SYS.IGNITE_PLUGINS.NAME.null",
"SYS.IGNITE_PLUGINS.INFO.null",
"SYS.IGNITE_PLUGINS.VERSION.null",
- "SYS.IGNITE_PLUGINS.CLASS_NAME.null"
- ));
+ "SYS.IGNITE_PLUGINS.CLASS_NAME.null",
+ "SYS.CACHE_EXPLICIT_LOCKS.CACHE_ID.null",
+ "SYS.CACHE_EXPLICIT_LOCKS.KEY.null",
+ "SYS.CACHE_EXPLICIT_LOCKS.THREAD_ID.null",
+ "SYS.CACHE_EXPLICIT_LOCKS.XID.null",
+ "SYS.CACHE_LOCKS.CACHE_ID.null",
+ "SYS.CACHE_LOCKS.KEY.null",
+ "SYS.CACHE_LOCKS.NODE_ID.null",
+ "SYS.CACHE_LOCKS.ORIGINATING_NODE_ID.null",
+ "SYS.CACHE_LOCKS.IS_OWNER.null",
+ "SYS.CACHE_LOCKS.IS_TX.null",
+ "SYS.CACHE_LOCKS.FLAGS.null",
+ "SYS.CACHE_LOCKS.XID.null",
+ "SYS.CACHE_LOCKS.ORIGINATING_XID.null"
+ ));
Assert.assertEquals(expectedCols, actualSysCols);
}
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
index d927b361a75..42bbfa10683 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
@@ -502,7 +502,9 @@ public class SystemViewCommandTest extends
GridCommandHandlerClusterByClassAbstr
"DS_QUEUES",
"PAGES_TIMESTAMP_HISTOGRAM",
"SQL_PLANS_HISTORY",
- "IGNITE_PLUGINS"
+ "IGNITE_PLUGINS",
+ "CACHE_EXPLICIT_LOCKS",
+ "CACHE_LOCKS"
));
Set<String> viewNames = new TreeSet<>();
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheExplicitLockSpan.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheExplicitLockSpan.java
index e0c6d9b4240..53c8ad6f577 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheExplicitLockSpan.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheExplicitLockSpan.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.processors.cache;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
@@ -216,6 +218,20 @@ public class GridCacheExplicitLockSpan extends
ReentrantLock {
}
}
+ /**
+ * @return Lock candidates.
+ */
+ public Collection<GridCacheMvccCandidate> candidates() {
+ lock();
+
+ try {
+ return new ArrayList<>(F.flatCollections(cands.values()));
+ }
+ finally {
+ unlock();
+ }
+ }
+
/**
* Marks all candidates added for given key as owned.
*
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvcc.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvcc.java
index 984c468f8ce..541f360e7d9 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvcc.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMvcc.java
@@ -1372,6 +1372,19 @@ public final class GridCacheMvcc {
return candidates(locs, reentry, false, cctx.emptyVersion());
}
+ /**
+ * @param dst Destination collection.
+ * @return Copied collection of local candidates.
+ */
+ public List<GridCacheMvccCandidate>
localCandidatesCopy(List<GridCacheMvccCandidate> dst) {
+ List<GridCacheMvccCandidate> src = locs;
+
+ if (src == null)
+ return Collections.emptyList();
+
+ return candidatesCopy(src, dst, false);
+ }
+
/**
* @param excludeVers Exclude versions.
* @return Collection of local candidates.
@@ -1417,13 +1430,30 @@ public final class GridCacheMvcc {
List<GridCacheMvccCandidate> cands = new ArrayList<>(col.size());
- for (GridCacheMvccCandidate c : col) {
+ return candidatesCopy(col, cands, reentries, excludeVers);
+ }
+
+ /**
+ * Copy candidates collection from {@code src} to {@code dst}.
+ * @param src Source collection.
+ * @param dst Destination collection.
+ * @param reentries Flag to include reentries.
+ * @param excludeVers Exclude versions.
+ * @return Copied collection of candidates.
+ */
+ private List<GridCacheMvccCandidate> candidatesCopy(
+ List<GridCacheMvccCandidate> src,
+ List<GridCacheMvccCandidate> dst,
+ boolean reentries,
+ GridCacheVersion... excludeVers
+ ) {
+ for (GridCacheMvccCandidate c : src) {
// Don't include reentries.
if ((reentries || !c.reentry()) &&
!U.containsObjectArray(excludeVers, c.version()))
- cands.add(c);
+ dst.add(c);
}
- return cands;
+ return dst;
}
/**
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 9ca50367803..9fe3462af05 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
@@ -20,10 +20,12 @@ package org.apache.ignite.internal.processors.cache;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Deque;
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;
@@ -49,6 +51,8 @@ import
org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFini
import
org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.systemview.CacheExplicitLockViewWalker;
+import org.apache.ignite.internal.systemview.CacheLockViewWalker;
import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashSet;
import org.apache.ignite.internal.util.GridConcurrentFactory;
import org.apache.ignite.internal.util.GridConcurrentHashSet;
@@ -67,6 +71,8 @@ import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.lang.IgniteUuid;
+import org.apache.ignite.spi.systemview.view.CacheExplicitLockView;
+import org.apache.ignite.spi.systemview.view.CacheLockView;
import org.apache.ignite.util.deque.FastSizeDeque;
import org.jetbrains.annotations.Nullable;
@@ -94,6 +100,18 @@ public class GridCacheMvccManager extends
GridCacheSharedManagerAdapter {
private static final int MAX_NESTED_LSNR_CALLS =
getInteger(IGNITE_MAX_NESTED_LISTENER_CALLS,
DFLT_MAX_NESTED_LISTENER_CALLS);
+ /** System view name for cache explicit locks. */
+ public static final String CACHE_EXPLICIT_LOCKS_VIEW =
"cacheExplicitLocks";
+
+ /** System view description for cache explicit locks. */
+ public static final String CACHE_EXPLICIT_LOCKS_VIEW_DESC = "Explicit
locks on cache keys";
+
+ /** System view name for cache locks. */
+ public static final String CACHE_LOCKS_VIEW = "cacheLocks";
+
+ /** System view description for cache keys locks. */
+ public static final String CACHE_LOCKS_VIEW_DESC = "Held and queued locks
on cache keys";
+
/** Pending locks per thread. */
private final ThreadLocal<Deque<GridCacheMvccCandidate>> pending = new
ThreadLocal<>();
@@ -282,6 +300,38 @@ public class GridCacheMvccManager extends
GridCacheSharedManagerAdapter {
pendingExplicit = GridConcurrentFactory.newMap();
cctx.gridEvents().addLocalEventListener(discoLsnr, EVT_NODE_FAILED,
EVT_NODE_LEFT);
+
+ cctx.kernalContext().systemView().registerInnerCollectionView(
+ CACHE_EXPLICIT_LOCKS_VIEW,
+ CACHE_EXPLICIT_LOCKS_VIEW_DESC,
+ new CacheExplicitLockViewWalker(),
+ pendingExplicit.values(),
+ GridCacheExplicitLockSpan::candidates,
+ (threadId, lock) -> new CacheExplicitLockView(lock)
+ );
+
+ List<GridCacheMvccCandidate> locks = new ArrayList<>();
+
+ cctx.kernalContext().systemView().registerInnerCollectionView(
+ CACHE_LOCKS_VIEW,
+ CACHE_LOCKS_VIEW_DESC,
+ new CacheLockViewWalker(),
+ locked.values(),
+ entry -> {
+ entry.lockEntry();
+ try {
+ locks.clear();
+
+ GridCacheMvcc mvcc = entry.mvccExtras();
+
+ return mvcc != null ? mvcc.localCandidatesCopy(locks) :
Collections.emptyList();
+ }
+ finally {
+ entry.unlockEntry();
+ }
+ },
+ (entry, cand) -> new CacheLockView(cand)
+ );
}
/** {@inheritDoc} */
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheExplicitLockView.java
b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheExplicitLockView.java
new file mode 100644
index 00000000000..8d2a36d6703
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheExplicitLockView.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.spi.systemview.view;
+
+import org.apache.ignite.internal.binary.BinaryUtils;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
+import org.apache.ignite.internal.systemview.Order;
+import org.apache.ignite.internal.systemview.SystemViewDescriptor;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Cache explicit lock representation for a {@link SystemView}.
+ * Shows locks acquired on current node (not hosted by current node).
+ */
+@SystemViewDescriptor
+public class CacheExplicitLockView {
+ /** */
+ private final GridCacheMvccCandidate cand;
+
+ /**
+ * @param cand Lock candidate
+ */
+ public CacheExplicitLockView(GridCacheMvccCandidate cand) {
+ this.cand = cand;
+ }
+
+ /**
+ * @return Cache ID.
+ */
+ @Order
+ public int cacheId() {
+ return cand.key().cacheId();
+ }
+
+ /**
+ * @return Key.
+ */
+ @Order(1)
+ public String key() {
+ return cand.key().key().toString();
+ }
+
+ /**
+ * @return Thread ID.
+ */
+ @Order(2)
+ public long threadId() {
+ return cand.threadId();
+ }
+
+ /**
+ * @return Version.
+ */
+ @Order(3)
+ public IgniteUuid xid() {
+ return BinaryUtils.asIgniteUuid(cand.version());
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheLockView.java
b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheLockView.java
new file mode 100644
index 00000000000..dbee3e2e34a
--- /dev/null
+++
b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/CacheLockView.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.spi.systemview.view;
+
+import java.util.UUID;
+import org.apache.ignite.internal.binary.BinaryUtils;
+import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
+import org.apache.ignite.internal.systemview.Order;
+import org.apache.ignite.internal.systemview.SystemViewDescriptor;
+import org.apache.ignite.lang.IgniteUuid;
+
+/**
+ * Cache key lock representation for a {@link SystemView}.
+ * Shows held cache key locks and queued cache key locks hosted on current
node,
+ * including explicit locks and transactional locks.
+ */
+@SystemViewDescriptor
+public class CacheLockView {
+ /** Lock candidate. */
+ private final GridCacheMvccCandidate cand;
+
+ /**
+ * @param cand Lock candidate
+ */
+ public CacheLockView(GridCacheMvccCandidate cand) {
+ this.cand = cand;
+ }
+
+ /**
+ * @return Cache ID.
+ */
+ @Order
+ public int cacheId() {
+ return cand.key().cacheId();
+ }
+
+ /**
+ * @return Key.
+ */
+ @Order(1)
+ public String key() {
+ return cand.key().key().toString();
+ }
+
+ /**
+ * @return Node ID.
+ */
+ @Order(2)
+ public UUID nodeId() {
+ return cand.nodeId();
+ }
+
+ /**
+ * @return Originating node ID.
+ */
+ @Order(3)
+ public UUID originatingNodeId() {
+ return cand.otherNodeId();
+ }
+
+ /**
+ * @return "OWNER" flag.
+ */
+ @Order(4)
+ public boolean isOwner() {
+ return cand.owner();
+ }
+
+ /**
+ * @return "TX" flag.
+ */
+ @Order(5)
+ public boolean isTx() {
+ return cand.tx();
+ }
+
+ /**
+ * @return All enabled flags.
+ */
+ @Order(6)
+ public short flags() {
+ return cand.flags();
+ }
+
+ /**
+ * @return Transaction ID.
+ */
+ @Order(7)
+ public IgniteUuid xid() {
+ return BinaryUtils.asIgniteUuid(cand.version());
+ }
+
+ /**
+ * @return Originating transaction ID.
+ */
+ @Order(8)
+ public IgniteUuid originatingXid() {
+ return BinaryUtils.asIgniteUuid(cand.otherVersion());
+ }
+}
diff --git
a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/TransactionView.java
b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/TransactionView.java
index bc09a478c99..547715fee2c 100644
---
a/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/TransactionView.java
+++
b/modules/core/src/main/java/org/apache/ignite/spi/systemview/view/TransactionView.java
@@ -20,6 +20,7 @@ package org.apache.ignite.spi.systemview.view;
import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
+import org.apache.ignite.internal.binary.BinaryUtils;
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.IgniteTxState;
@@ -125,6 +126,14 @@ public class TransactionView {
return tx.xid();
}
+ /**
+ * @return Near transaction ID.
+ * @see IgniteInternalTx#nearXidVersion() ()
+ */
+ public IgniteUuid originatingXid() {
+ return BinaryUtils.asIgniteUuid(tx.nearXidVersion());
+ }
+
/**
* @return {@code True} if transaction is started for system cache.
* @see IgniteInternalTx#system()
@@ -284,6 +293,5 @@ public class TransactionView {
catch (Throwable e) {
return null;
}
-
}
}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewLocksTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewLocksTest.java
new file mode 100644
index 00000000000..e7b6758819c
--- /dev/null
+++
b/modules/core/src/test/java/org/apache/ignite/internal/metric/SystemViewLocksTest.java
@@ -0,0 +1,446 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.metric;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.Lock;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.client.Person;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.systemview.view.CacheExplicitLockView;
+import org.apache.ignite.spi.systemview.view.CacheLockView;
+import org.apache.ignite.spi.systemview.view.SystemView;
+import org.apache.ignite.spi.systemview.view.TransactionView;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.WithSystemProperty;
+import org.apache.ignite.transactions.Transaction;
+import org.junit.Test;
+
+import static
org.apache.ignite.IgniteCommonsSystemProperties.IGNITE_TO_STRING_INCLUDE_SENSITIVE;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static
org.apache.ignite.internal.processors.cache.GridCacheMvccManager.CACHE_EXPLICIT_LOCKS_VIEW;
+import static
org.apache.ignite.internal.processors.cache.GridCacheMvccManager.CACHE_LOCKS_VIEW;
+import static
org.apache.ignite.internal.processors.cache.transactions.IgniteTxManager.TXS_MON_LIST;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static
org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static
org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+
+/** Tests for {@link SystemView} for locks. */
+public class SystemViewLocksTest extends SystemViewAbstractTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ IgniteEx ignite = startGrids(3);
+
+ ignite.getOrCreateCache(
+ new CacheConfiguration<Integer, Integer>(DEFAULT_CACHE_NAME)
+ .setAtomicityMode(TRANSACTIONAL)
+ .setCacheMode(PARTITIONED)
+ .setBackups(1)
+ );
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ stopAllGrids();
+
+ super.afterTest();
+ }
+
+ /** */
+ @Test
+ public void testExplicitLocks() throws Exception {
+ IgniteEx ignite0 = grid(0);
+ IgniteEx ignite1 = grid(1);
+ IgniteEx ignite2 = grid(2);
+
+ CountDownLatch finishLatch = new CountDownLatch(1);
+
+ try {
+ Integer key0 = primaryKey(ignite0.cache(DEFAULT_CACHE_NAME));
+ Integer key1 = primaryKey(ignite1.cache(DEFAULT_CACHE_NAME));
+ Integer key2 = primaryKey(ignite2.cache(DEFAULT_CACHE_NAME));
+
+ CountDownLatch firstNodeLatch = new CountDownLatch(1);
+
+ // Block key0 and key1 from node0.
+ Runnable task0 = explicitLockTask(ignite0, () -> {
+ firstNodeLatch.countDown();
+ U.awaitQuiet(finishLatch);
+ }, key0, key1);
+
+ // Block key2 and wait for key0 from node1.
+ Runnable task1 = explicitLockTask(ignite1, () -> {}, key2, key0);
+
+ // Wait for key0 from node2.
+ Runnable task2a = explicitLockTask(ignite2, () -> {}, key0);
+
+ // Wait for key1 from node2.
+ Runnable task2b = explicitLockTask(ignite2, () -> {}, key1);
+
+ IgniteInternalFuture<?> fut0 = GridTestUtils.runAsync(task0);
+ firstNodeLatch.await();
+ IgniteInternalFuture<?> fut1 = GridTestUtils.runAsync(task1);
+ IgniteInternalFuture<?> fut2a = GridTestUtils.runAsync(task2a);
+ IgniteInternalFuture<?> fut2b = GridTestUtils.runAsync(task2b);
+
+ List<CacheExplicitLockView> explicitLocks0 = viewContent(ignite0,
CACHE_EXPLICIT_LOCKS_VIEW, 2);
+ List<CacheExplicitLockView> explicitLocks1 = viewContent(ignite1,
CACHE_EXPLICIT_LOCKS_VIEW, 2);
+ List<CacheExplicitLockView> explicitLocks2 = viewContent(ignite2,
CACHE_EXPLICIT_LOCKS_VIEW, 2);
+
+ List<CacheLockView> keyLocks0 = viewContent(ignite0,
CACHE_LOCKS_VIEW, 3);
+ List<CacheLockView> keyLocks1 = viewContent(ignite1,
CACHE_LOCKS_VIEW, 2);
+ List<CacheLockView> keyLocks2 = viewContent(ignite2,
CACHE_LOCKS_VIEW, 1);
+
+ // Check threads.
+ assertEquals(explicitLocks0.get(0).threadId(),
explicitLocks0.get(1).threadId());
+ assertEquals(explicitLocks1.get(0).threadId(),
explicitLocks1.get(1).threadId());
+ assertNotSame(explicitLocks2.get(0).threadId(),
explicitLocks2.get(1).threadId());
+
+ // Check lock owners.
+ CacheLockView owner0 = F.find(keyLocks0, null,
CacheLockView::isOwner);
+ CacheLockView owner1 = F.find(keyLocks1, null,
CacheLockView::isOwner);
+ CacheLockView owner2 = F.find(keyLocks2, null,
CacheLockView::isOwner);
+
+ assertNotNull(owner0);
+ assertNotNull(owner1);
+ assertNotNull(owner2);
+
+ assertEquals(owner0.originatingNodeId(), ignite0.localNode().id());
+ assertEquals(1, F.size(explicitLocks0, l ->
l.xid().equals(owner0.originatingXid())));
+
+ assertEquals(owner1.originatingNodeId(), ignite0.localNode().id());
+ assertEquals(1, F.size(explicitLocks0, l ->
l.xid().equals(owner1.originatingXid())));
+
+ assertEquals(owner2.originatingNodeId(), ignite1.localNode().id());
+ assertEquals(1, F.size(explicitLocks1, l ->
l.xid().equals(owner2.originatingXid())));
+
+ // Check waiting locks.
+ assertEquals(1, F.size(keyLocks0,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite1.localNode().id())));
+ assertEquals(1, F.size(keyLocks0,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite2.localNode().id())));
+ assertEquals(1, F.size(keyLocks1,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite2.localNode().id())));
+
+ finishLatch.countDown();
+
+ fut0.get();
+ fut1.get();
+ fut2a.get();
+ fut2b.get();
+ }
+ finally {
+ finishLatch.countDown();
+ }
+ }
+
+ /** */
+ @Test
+ public void testTxLocks() throws Exception {
+ IgniteEx ignite0 = grid(0);
+ IgniteEx ignite1 = grid(1);
+ IgniteEx ignite2 = grid(2);
+
+ CountDownLatch finishLatch = new CountDownLatch(1);
+
+ try {
+ Integer key0 = primaryKey(ignite0.cache(DEFAULT_CACHE_NAME));
+ Integer key1 = primaryKey(ignite1.cache(DEFAULT_CACHE_NAME));
+ Integer key2 = primaryKey(ignite2.cache(DEFAULT_CACHE_NAME));
+
+ CountDownLatch firstNodeLatch = new CountDownLatch(1);
+
+ // Block key0 and key1 from node0.
+ Runnable task0 = txLockTask(ignite0, () -> {
+ firstNodeLatch.countDown();
+ U.awaitQuiet(finishLatch);
+ }, key0, key1);
+
+ // Block key2 and wait for key0 from node1.
+ Runnable task1 = txLockTask(ignite1, () -> {}, key2, key0);
+
+ // Wait for key0 from node2.
+ Runnable task2a = txLockTask(ignite2, () -> {}, key0);
+
+ // Wait for key1 from node2.
+ Runnable task2b = txLockTask(ignite2, () -> {}, key1);
+
+ IgniteInternalFuture<?> fut0 = GridTestUtils.runAsync(task0);
+ firstNodeLatch.await();
+ IgniteInternalFuture<?> fut1 = GridTestUtils.runAsync(task1);
+ IgniteInternalFuture<?> fut2a = GridTestUtils.runAsync(task2a);
+ IgniteInternalFuture<?> fut2b = GridTestUtils.runAsync(task2b);
+
+ List<TransactionView> txs0 = viewContent(ignite0, TXS_MON_LIST,
3); // 1 near + 2 dht.
+ List<TransactionView> txs1 = viewContent(ignite1, TXS_MON_LIST,
3); // 1 near + 2 dht
+ List<TransactionView> txs2 = viewContent(ignite2, TXS_MON_LIST,
3); // 2 near + 1 dht
+
+ List<CacheLockView> keyLocks0 = viewContent(ignite0,
CACHE_LOCKS_VIEW, 3);
+ List<CacheLockView> keyLocks1 = viewContent(ignite1,
CACHE_LOCKS_VIEW, 2);
+ List<CacheLockView> keyLocks2 = viewContent(ignite2,
CACHE_LOCKS_VIEW, 1);
+
+ // Check lock owners.
+ CacheLockView owner0 = F.find(keyLocks0, null,
CacheLockView::isOwner);
+ CacheLockView owner1 = F.find(keyLocks1, null,
CacheLockView::isOwner);
+ CacheLockView owner2 = F.find(keyLocks2, null,
CacheLockView::isOwner);
+
+ assertNotNull(owner0);
+ assertNotNull(owner1);
+ assertNotNull(owner2);
+
+ assertEquals(owner0.originatingNodeId(), ignite0.localNode().id());
+ assertEquals(1, F.size(txs0, l ->
l.xid().equals(owner0.originatingXid()) && l.xid().equals(owner0.xid())));
+
+ assertEquals(owner1.originatingNodeId(), ignite0.localNode().id());
+ assertEquals(1, F.size(txs0, l ->
l.xid().equals(owner1.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs1, l -> l.xid().equals(owner1.xid())));
// Dht.
+
+ assertEquals(owner2.originatingNodeId(), ignite1.localNode().id());
+ assertEquals(1, F.size(txs1, l ->
l.xid().equals(owner2.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs2, l -> l.xid().equals(owner2.xid())));
// Dht.
+
+ // Check waiting locks.
+ CacheLockView waiting1on0 = F.find(keyLocks0, null,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite1.localNode().id()));
+ CacheLockView waiting2on0 = F.find(keyLocks0, null,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite2.localNode().id()));
+ CacheLockView waiting2on1 = F.find(keyLocks1, null,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite2.localNode().id()));
+
+ assertNotNull(waiting1on0);
+ assertNotNull(waiting2on0);
+ assertNotNull(waiting2on1);
+
+ assertEquals(1, F.size(txs1, l ->
l.xid().equals(waiting1on0.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs0, l ->
l.xid().equals(waiting1on0.xid()))); // Dht.
+
+ assertEquals(1, F.size(txs2, l ->
l.xid().equals(waiting2on0.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs0, l ->
l.xid().equals(waiting2on0.xid()))); // Dht.
+
+ assertEquals(1, F.size(txs2, l ->
l.xid().equals(waiting2on1.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs1, l ->
l.xid().equals(waiting2on1.xid()))); // Dht.
+
+ finishLatch.countDown();
+
+ fut0.get();
+ fut1.get();
+ fut2a.get();
+ fut2b.get();
+ }
+ finally {
+ finishLatch.countDown();
+ }
+ }
+
+ /** */
+ @Test
+ public void testMixedExplicitTxLocks() throws Exception {
+ IgniteEx ignite0 = grid(0);
+ IgniteEx ignite1 = grid(1);
+ IgniteEx ignite2 = grid(2);
+
+ CountDownLatch finishLatch = new CountDownLatch(1);
+
+ try {
+ Integer key1 = primaryKey(ignite1.cache(DEFAULT_CACHE_NAME));
+ Integer key2 = primaryKey(ignite2.cache(DEFAULT_CACHE_NAME));
+
+ CountDownLatch firstNodeLatch = new CountDownLatch(1);
+ CountDownLatch secondNodeLatch = new CountDownLatch(1);
+
+ // Block key1 from node0.
+ Runnable task0 = explicitLockTask(ignite0, () -> {
+ firstNodeLatch.countDown();
+ U.awaitQuiet(finishLatch);
+ }, key1);
+
+ // Block key2 from node1.
+ Runnable task1 = txLockTask(ignite1, () -> {
+ secondNodeLatch.countDown();
+ U.awaitQuiet(finishLatch);
+ }, key2);
+
+ // Wait for key2 from node2.
+ Runnable task2a = explicitLockTask(ignite2, () -> {}, key2);
+
+ // Wait for key1 from node2.
+ Runnable task2b = txLockTask(ignite2, () -> {}, key1);
+
+ IgniteInternalFuture<?> fut0 = GridTestUtils.runAsync(task0);
+ firstNodeLatch.await();
+ IgniteInternalFuture<?> fut1 = GridTestUtils.runAsync(task1);
+ secondNodeLatch.await();
+ IgniteInternalFuture<?> fut2a = GridTestUtils.runAsync(task2a);
+ IgniteInternalFuture<?> fut2b = GridTestUtils.runAsync(task2b);
+
+ List<CacheExplicitLockView> explicitLocks0 = viewContent(ignite0,
CACHE_EXPLICIT_LOCKS_VIEW, 1);
+ List<CacheExplicitLockView> explicitLocks2 = viewContent(ignite2,
CACHE_EXPLICIT_LOCKS_VIEW, 1);
+ List<TransactionView> txs1 = viewContent(ignite1, TXS_MON_LIST,
2); // 1 near + 1 dht
+ List<TransactionView> txs2 = viewContent(ignite2, TXS_MON_LIST,
2); // 1 near + 1 dht
+
+ List<CacheLockView> keyLocks1 = viewContent(ignite1,
CACHE_LOCKS_VIEW, 2);
+ List<CacheLockView> keyLocks2 = viewContent(ignite2,
CACHE_LOCKS_VIEW, 2);
+
+ // Check lock owners.
+ CacheLockView owner1 = F.find(keyLocks1, null,
CacheLockView::isOwner);
+ CacheLockView owner2 = F.find(keyLocks2, null,
CacheLockView::isOwner);
+
+ assertNotNull(owner1);
+ assertNotNull(owner2);
+
+ assertEquals(owner1.originatingNodeId(), ignite0.localNode().id());
+ assertEquals(1, F.size(explicitLocks0, l ->
l.xid().equals(owner1.originatingXid())));
+
+ assertEquals(owner2.originatingNodeId(), ignite1.localNode().id());
+ assertEquals(1, F.size(txs1, l ->
l.xid().equals(owner2.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs2, l -> l.xid().equals(owner2.xid())));
// Dht.
+
+ // Check waiting locks.
+ CacheLockView waiting2on1 = F.find(keyLocks1, null,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite2.localNode().id()));
+ CacheLockView waiting2on2 = F.find(keyLocks2, null,
+ l -> !l.isOwner() &&
l.originatingNodeId().equals(ignite2.localNode().id()));
+
+ assertNotNull(waiting2on1);
+ assertNotNull(waiting2on2);
+
+ assertEquals(1, F.size(txs2, l ->
l.xid().equals(waiting2on1.originatingXid()))); // Near.
+ assertEquals(1, F.size(txs1, l ->
l.xid().equals(waiting2on1.xid()))); // Dht.
+
+ assertEquals(1, F.size(explicitLocks2, l ->
l.xid().equals(waiting2on2.originatingXid())));
+
+ finishLatch.countDown();
+
+ fut0.get();
+ fut1.get();
+ fut2a.get();
+ fut2b.get();
+ }
+ finally {
+ finishLatch.countDown();
+ }
+ }
+
+ /** */
+ @Test
+ public void testKeyTextIncludeSensitive() throws Exception {
+ checkKeyText(true);
+ }
+
+ /** */
+ @Test
+ @WithSystemProperty(key = IGNITE_TO_STRING_INCLUDE_SENSITIVE, value =
"false")
+ public void testKeyTextExcludeSensitive() throws Exception {
+ checkKeyText(false);
+ }
+
+ /** */
+ public void checkKeyText(boolean includeSensitive) throws Exception {
+ IgniteEx ignite = grid(0);
+ IgniteCache<Object, Object> cache = ignite.cache(DEFAULT_CACHE_NAME);
+
+ // Check primitive object.
+ String key1 = "testKey";
+
+ Lock lock1 = cache.lock(key1);
+
+ lock1.lock();
+
+ try {
+ List<CacheExplicitLockView> explicitLocks = viewContent(ignite,
CACHE_EXPLICIT_LOCKS_VIEW, 1);
+ // Sensitive data included: KeyCacheObjectImpl [part=859,
val=testKey, hasValBytes=false]
+ // Sensitive data excluded: KeyCacheObject [hasValBytes=false]
+ assertEquals(includeSensitive,
explicitLocks.get(0).key().contains(key1));
+ }
+ finally {
+ lock1.unlock();
+ }
+
+ // Check binary object.
+ Person key2 = new Person(12345, "testName");
+
+ Lock lock2 = cache.lock(key2);
+
+ lock2.lock();
+
+ try {
+ List<CacheExplicitLockView> explicitLocks = viewContent(ignite,
CACHE_EXPLICIT_LOCKS_VIEW, 1);
+ // Sensitive data included: org.apache.ignite.client.Person
[idHash=..., hash=-1202253851, id=12345, name=testName]
+ // Sensitive data excluded: BinaryObject [idHash=...,
hash=-1202253851]
+ assertEquals(includeSensitive,
explicitLocks.get(0).key().contains(key2.getClass().getName()));
+ assertEquals(includeSensitive,
explicitLocks.get(0).key().contains("id=" + key2.getId()));
+ assertEquals(includeSensitive,
explicitLocks.get(0).key().contains("name=" + key2.getName()));
+ }
+ finally {
+ lock2.unlock();
+ }
+ }
+
+ /** */
+ private static Runnable explicitLockTask(IgniteEx ignite, Runnable body,
int... keysToLock) {
+ return () -> {
+ Lock[] locks = new Lock[keysToLock.length];
+
+ for (int i = 0; i < keysToLock.length; i++) {
+ locks[i] =
ignite.cache(DEFAULT_CACHE_NAME).lock(keysToLock[i]);
+ locks[i].lock();
+ }
+
+ try {
+ body.run();
+ }
+ finally {
+ for (int i = keysToLock.length - 1; i >= 0; i--)
+ locks[i].unlock();
+ }
+ };
+ }
+
+ /** */
+ private static Runnable txLockTask(IgniteEx ignite, Runnable body, int...
keysToLock) {
+ return () -> {
+ try (Transaction tx = ignite.transactions().txStart(PESSIMISTIC,
REPEATABLE_READ)) {
+ for (int i = 0; i < keysToLock.length; i++)
+ ignite.cache(DEFAULT_CACHE_NAME).put(keysToLock[i], 0);
+
+ body.run();
+
+ tx.commit();
+ }
+ };
+ }
+
+ /** */
+ private static <T> List<T> viewContent(IgniteEx ignite, String viewName,
int expSize) throws Exception {
+ SystemView<T> view = ignite.context().systemView().view(viewName);
+
+ assertTrue("Failed to wait for view size [ignite=" + ignite.name() +
", viewName=" + viewName +
+ ", expSize=" + expSize + ", actSize=" +
F.size(view.iterator()),
+ waitForCondition(() -> F.size(view.iterator()) == expSize,
1_000L));
+
+ return U.arrayList(view.iterator(), expSize);
+ }
+}
diff --git
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite13.java
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite13.java
index 2f4d1787be3..4426afc2053 100644
---
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite13.java
+++
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite13.java
@@ -45,6 +45,7 @@ import
org.apache.ignite.internal.metric.SystemViewComputeTaskTest;
import org.apache.ignite.internal.metric.SystemViewConfigurationTest;
import org.apache.ignite.internal.metric.SystemViewDSTest;
import org.apache.ignite.internal.metric.SystemViewExecutorsTest;
+import org.apache.ignite.internal.metric.SystemViewLocksTest;
import org.apache.ignite.internal.metric.SystemViewMetastorageTest;
import org.apache.ignite.internal.metric.SystemViewNodesTest;
import org.apache.ignite.internal.metric.SystemViewPageListsTest;
@@ -122,6 +123,7 @@ public class IgniteCacheTestSuite13 {
GridTestUtils.addTestIfNeeded(suite,
SystemViewConfigurationTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, SystemViewPluginTest.class,
ignoredTests);
GridTestUtils.addTestIfNeeded(suite, SystemViewQueriesTest.class,
ignoredTests);
+ GridTestUtils.addTestIfNeeded(suite, SystemViewLocksTest.class,
ignoredTests);
GridTestUtils.addTestIfNeeded(suite, CacheMetricsAddRemoveTest.class,
ignoredTests);
GridTestUtils.addTestIfNeeded(suite,
CacheMetricsConflictResolverTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, JmxExporterSpiTest.class,
ignoredTests);
diff --git
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
index 15a512dbaab..e98770d4a5b 100644
---
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
+++
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/metric/SqlViewExporterSpiTest.java
@@ -465,7 +465,9 @@ public class SqlViewExporterSpiTest extends
AbstractExporterSpiTest {
"DS_QUEUES",
"PAGES_TIMESTAMP_HISTOGRAM",
"SQL_PLANS_HISTORY",
- "IGNITE_PLUGINS"
+ "IGNITE_PLUGINS",
+ "CACHE_LOCKS",
+ "CACHE_EXPLICIT_LOCKS"
));
Set<String> actViews = new TreeSet<>();
diff --git
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
index 5c74bdf53aa..87f000f2738 100644
---
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
+++
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/SqlSystemViewsSelfTest.java
@@ -32,6 +32,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
@@ -81,6 +82,7 @@ import org.apache.ignite.internal.util.lang.GridNodePredicate;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.lang.IgnitePredicate;
@@ -92,6 +94,7 @@ import org.apache.ignite.spi.systemview.view.SqlQueryView;
import org.apache.ignite.spi.systemview.view.SystemView;
import org.apache.ignite.spi.systemview.view.sql.SqlTableView;
import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.transactions.Transaction;
import org.junit.Assert;
import org.junit.Test;
@@ -102,6 +105,8 @@ import static
org.apache.ignite.internal.processors.cache.persistence.metastorag
import static
org.apache.ignite.internal.processors.query.running.RunningQueryManager.SQL_QRY_VIEW;
import static org.apache.ignite.internal.util.IgniteUtils.MB;
import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static
org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static
org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
import static org.junit.Assert.assertNotEquals;
/**
@@ -1896,6 +1901,56 @@ public class SqlSystemViewsSelfTest extends
AbstractIndexingCommonTest {
}
}
+ /** */
+ @Test
+ public void testLocksView() throws Exception {
+ try (IgniteEx ignite = startGrid()) {
+ IgniteCache<Object, Object> cache = ignite.getOrCreateCache(new
CacheConfiguration<>(DEFAULT_CACHE_NAME)
+ .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL));
+
+ Lock lock1 = cache.lock(1);
+ Lock lock2 = cache.lock(2);
+
+ lock1.lock();
+ lock2.lock();
+ try (Transaction ignored =
ignite.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+ cache.put(3, 3);
+
+ // Join explicit locks with key locks.
+ List<List<?>> res = execSql("SELECT el.cache_id, el.thread_id,
kl.is_owner, kl.is_tx, kl.originating_node_id " +
+ "FROM SYS.CACHE_EXPLICIT_LOCKS el JOIN SYS.CACHE_LOCKS kl
ON el.xid = kl.xid");
+
+ assertEquals(2, res.size());
+
+ for (List<?> lock : res) {
+ assertEquals(CU.cacheId(DEFAULT_CACHE_NAME), lock.get(0));
+ assertEquals(Thread.currentThread().getId(), lock.get(1));
+ assertEquals(true, lock.get(2));
+ assertEquals(false, lock.get(3));
+ assertEquals(ignite.localNode().id(), lock.get(4));
+ }
+
+ // Join transactions with key locks.
+ res = execSql("SELECT kl.cache_id, tx.thread_id, kl.is_owner,
kl.is_tx, kl.originating_node_id " +
+ "FROM SYS.TRANSACTIONS tx JOIN SYS.CACHE_LOCKS kl ON
tx.xid = kl.xid");
+
+ assertEquals(1, res.size());
+
+ for (List<?> lock : res) {
+ assertEquals(CU.cacheId(DEFAULT_CACHE_NAME), lock.get(0));
+ assertEquals(Thread.currentThread().getId(), lock.get(1));
+ assertEquals(true, lock.get(2));
+ assertEquals(true, lock.get(3));
+ assertEquals(ignite.localNode().id(), lock.get(4));
+ }
+ }
+ finally {
+ lock2.unlock();
+ lock1.unlock();
+ }
+ }
+ }
+
/**
* Mock for {@link ClusterMetricsImpl} that always returns big (more than
24h) duration for all duration metrics.
*/