You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ti...@apache.org on 2022/03/28 13:52:50 UTC
[ignite] branch ignite-2.13 updated: IGNITE-15650 : Introduce statistics for platform services (#9768)
This is an automated email from the ASF dual-hosted git repository.
timoninmaxim pushed a commit to branch ignite-2.13
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/ignite-2.13 by this push:
new 16b329c IGNITE-15650 : Introduce statistics for platform services (#9768)
16b329c is described below
commit 16b329c0c0de56e16b6d9c16d5f47c9bbfc5540f
Author: Vladimir Steshin <vl...@gmail.com>
AuthorDate: Mon Mar 28 13:23:35 2022 +0300
IGNITE-15650 : Introduce statistics for platform services (#9768)
---
.../services/PlatformServiceConfiguration.java | 52 ++++++
.../platform/services/PlatformServices.java | 14 +-
.../processors/service/GridServiceProxy.java | 18 +-
.../processors/service/IgniteServiceProcessor.java | 36 +++-
.../service/LazyServiceConfiguration.java | 16 ++
.../processors/service/GridServiceMetricsTest.java | 2 +-
.../ignite/platform/PlatformDeployServiceTask.java | 122 +++++++++++-
.../Services/IJavaService.cs | 12 ++
.../Services/ServicesAsyncWrapper.cs | 3 +-
.../Services/ServicesTest.cs | 206 +++++++++++++++++++--
.../Apache.Ignite.Core/Impl/Services/Services.cs | 16 --
.../Apache.Ignite.Core/Services/IServices.cs | 41 ++--
.../Services/ServiceConfiguration.cs | 33 ++++
13 files changed, 496 insertions(+), 75 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServiceConfiguration.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServiceConfiguration.java
new file mode 100644
index 0000000..b77f06f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServiceConfiguration.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.platform.services;
+
+import org.apache.ignite.services.ServiceConfiguration;
+
+/**
+ * Extended service configuration. Keeps known method names of service to build proper service statistics.
+ */
+public class PlatformServiceConfiguration extends ServiceConfiguration {
+ /** */
+ private static final long serialVersionUID = 1L;
+
+ /** Known method names of platform service. */
+ private String[] mtdNames;
+
+ /**
+ * Constr.
+ */
+ PlatformServiceConfiguration() {
+ mtdNames(null);
+ }
+
+ /**
+ * @return Known method names of platform service.
+ */
+ public String[] mtdNames() {
+ return mtdNames;
+ }
+
+ /**
+ * Sets known method names of platform service.
+ */
+ void mtdNames(String[] mtdNames) {
+ this.mtdNames = mtdNames;
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
index 9178acc..06ec10d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/services/PlatformServices.java
@@ -484,8 +484,8 @@ public class PlatformServices extends PlatformAbstractTarget {
* @param reader Binary reader,
* @return Service configuration.
*/
- @NotNull private ServiceConfiguration dotnetConfiguration(BinaryRawReaderEx reader) {
- ServiceConfiguration cfg = new ServiceConfiguration();
+ @NotNull private PlatformServiceConfiguration dotnetConfiguration(BinaryRawReaderEx reader) {
+ PlatformServiceConfiguration cfg = new PlatformServiceConfiguration();
cfg.setName(reader.readString());
cfg.setService(new PlatformDotNetServiceImpl(reader.readObjectDetached(), platformCtx, srvKeepBinary));
@@ -499,6 +499,11 @@ public class PlatformServices extends PlatformAbstractTarget {
if (filter != null)
cfg.setNodeFilter(platformCtx.createClusterNodeFilter(filter));
+ cfg.setStatisticsEnabled(reader.readBoolean());
+
+ if (cfg.isStatisticsEnabled())
+ cfg.mtdNames(reader.readStringArray());
+
return cfg;
}
@@ -513,9 +518,8 @@ public class PlatformServices extends PlatformAbstractTarget {
List<ServiceConfiguration> cfgs = new ArrayList<>(numServices);
- for (int i = 0; i < numServices; i++) {
+ for (int i = 0; i < numServices; i++)
cfgs.add(dotnetConfiguration(reader));
- }
return cfgs;
}
@@ -822,5 +826,7 @@ public class PlatformServices extends PlatformAbstractTarget {
if (svcCfg.getNodeFilter() instanceof PlatformClusterNodeFilterImpl)
dotnetFilter = ((PlatformClusterNodeFilterImpl)svcCfg.getNodeFilter()).getInternalPredicate();
w.writeObjectDetached(dotnetFilter);
+
+ w.writeBoolean(svcCfg.isStatisticsEnabled());
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
index 8f60b2a..39e7122 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/GridServiceProxy.java
@@ -209,7 +209,7 @@ public class GridServiceProxy<T> implements Serializable {
if (svc != null) {
HistogramMetricImpl hist = svcCtx.isStatisticsEnabled() ?
- svcCtx.metrics().findMetric(mtd.getName()) : null;
+ invocationHistogramm(svcCtx, mtd.getName(), args) : null;
return hist == null ? callServiceLocally(svc, mtd, args, callAttrs) :
measureCall(hist, () -> callServiceLocally(svc, mtd, args, callAttrs));
@@ -564,7 +564,7 @@ public class GridServiceProxy<T> implements Serializable {
Method mtd = ctx.method(key);
- HistogramMetricImpl hist = ctx.isStatisticsEnabled() ? ctx.metrics().findMetric(mtd.getName()) : null;
+ HistogramMetricImpl hist = ctx.isStatisticsEnabled() ? invocationHistogramm(ctx, mtdName, args) : null;
Object res = hist == null ? callService(ctx, mtd) : measureCall(hist, () -> callService(ctx, mtd));
@@ -630,6 +630,20 @@ public class GridServiceProxy<T> implements Serializable {
}
/**
+ * @return Invocation histogramm for the method.
+ */
+ private static HistogramMetricImpl invocationHistogramm(ServiceContextImpl ctx, String mtdName, Object[] args) {
+ if (ctx.service() instanceof PlatformService) {
+ assert args.length > 0 && args[0] instanceof String;
+ assert ctx.metrics() != null;
+
+ return ctx.metrics().findMetric((String)args[0]);
+ }
+ else
+ return ctx.metrics().findMetric(mtdName);
+ }
+
+ /**
* Exception class that wraps an exception thrown by the service implementation.
*/
private static class ServiceProxyException extends RuntimeException {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
index edafbf4..5d5fb3d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
@@ -71,6 +71,7 @@ import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
import org.apache.ignite.internal.processors.metric.MetricRegistry;
import org.apache.ignite.internal.processors.platform.services.PlatformService;
+import org.apache.ignite.internal.processors.platform.services.PlatformServiceConfiguration;
import org.apache.ignite.internal.processors.security.OperationSecurityContext;
import org.apache.ignite.internal.processors.security.SecurityContext;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
@@ -554,6 +555,9 @@ public class IgniteServiceProcessor extends GridProcessorAdapter implements Igni
ensure(c.getService() != null, "getService() != null", c.getService());
ensure(c.getTotalCount() > 0 || c.getMaxPerNodeCount() > 0,
"c.getTotalCount() > 0 || c.getMaxPerNodeCount() > 0", null);
+ ensure(!c.isStatisticsEnabled() || !(c.getService() instanceof PlatformService) ||
+ c instanceof PlatformServiceConfiguration, "The service is a platform service and has statistics" +
+ "enabled. Service configuration must be PlatformServiceConfiguration.", null);
}
/**
@@ -664,7 +668,10 @@ public class IgniteServiceProcessor extends GridProcessorAdapter implements Igni
try {
byte[] srvcBytes = U.marshal(marsh, cfg.getService());
- cfgsCp.add(new LazyServiceConfiguration(cfg, srvcBytes));
+ String[] knownSvcMdtNames = cfg instanceof PlatformServiceConfiguration ?
+ ((PlatformServiceConfiguration)cfg).mtdNames() : null;
+
+ cfgsCp.add(new LazyServiceConfiguration(cfg, srvcBytes).platformMtdNames(knownSvcMdtNames));
}
catch (Exception e) {
U.error(log, "Failed to marshal service with configured marshaller " +
@@ -1302,7 +1309,7 @@ public class IgniteServiceProcessor extends GridProcessorAdapter implements Igni
if (cfg.isStatisticsEnabled()) {
if (invocationMetrics == null)
- invocationMetrics = createServiceMetrics(srvcCtx);
+ invocationMetrics = createServiceMetrics(srvcCtx, cfg);
srvcCtx.metrics(invocationMetrics);
}
@@ -1982,18 +1989,27 @@ public class IgniteServiceProcessor extends GridProcessorAdapter implements Igni
* Creates metrics registry for the invocation histograms.
*
* @param srvcCtx ServiceContext.
+ * @param cfg Service configuration.
* @return Created metric registry.
*/
- private ReadOnlyMetricRegistry createServiceMetrics(ServiceContextImpl srvcCtx) {
+ private ReadOnlyMetricRegistry createServiceMetrics(ServiceContextImpl srvcCtx, ServiceConfiguration cfg) {
MetricRegistry metricRegistry = ctx.metric().registry(serviceMetricRegistryName(srvcCtx.name()));
- for (Class<?> itf : allInterfaces(srvcCtx.service().getClass())) {
- for (Method mtd : itf.getMethods()) {
- if (metricIgnored(mtd.getDeclaringClass()))
- continue;
+ if (cfg instanceof LazyServiceConfiguration && ((LazyServiceConfiguration)cfg).platformMtdNames() != null) {
+ for (String definedMtdName : ((LazyServiceConfiguration)cfg).platformMtdNames()) {
+ metricRegistry.histogram(definedMtdName, DEFAULT_INVOCATION_BOUNDS,
+ DESCRIPTION_OF_INVOCATION_METRIC_PREF + '\'' + definedMtdName + "()'");
+ }
+ }
+ else {
+ for (Class<?> itf : allInterfaces(srvcCtx.service().getClass())) {
+ for (Method mtd : itf.getMethods()) {
+ if (metricIgnored(mtd.getDeclaringClass()))
+ continue;
- metricRegistry.histogram(mtd.getName(), DEFAULT_INVOCATION_BOUNDS, DESCRIPTION_OF_INVOCATION_METRIC_PREF +
- '\'' + mtd.getName() + "()'");
+ metricRegistry.histogram(mtd.getName(), DEFAULT_INVOCATION_BOUNDS,
+ DESCRIPTION_OF_INVOCATION_METRIC_PREF + '\'' + mtd.getName() + "()'");
+ }
}
}
@@ -2013,7 +2029,7 @@ public class IgniteServiceProcessor extends GridProcessorAdapter implements Igni
* @param srvcName Name of the service.
* @return registry name for service {@code srvcName}.
*/
- static String serviceMetricRegistryName(String srvcName) {
+ public static String serviceMetricRegistryName(String srvcName) {
return metricName(SERVICE_METRIC_REGISTRY, srvcName);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java
index 7d34665..d0d4df1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/LazyServiceConfiguration.java
@@ -40,6 +40,10 @@ public class LazyServiceConfiguration extends ServiceConfiguration {
/** */
private byte[] srvcBytes;
+ /** Names of platform service methods to build service statistics. */
+ @GridToStringExclude
+ private String[] platformMtdNames;
+
/**
* Default constructor.
*/
@@ -120,6 +124,18 @@ public class LazyServiceConfiguration extends ServiceConfiguration {
return true;
}
+ /** */
+ LazyServiceConfiguration platformMtdNames(String[] platformMtdNames) {
+ this.platformMtdNames = platformMtdNames;
+
+ return this;
+ }
+
+ /** @return Names of known service methods. */
+ String[] platformMtdNames() {
+ return platformMtdNames;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
String svcCls = srvc == null ? "" : srvc.getClass().getSimpleName();
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/GridServiceMetricsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/GridServiceMetricsTest.java
index 9b036cf..ddbbc49 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/GridServiceMetricsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/GridServiceMetricsTest.java
@@ -299,7 +299,7 @@ public class GridServiceMetricsTest extends GridCommonAbstractTest {
* @param histogram Histogram to traverse.
* @return Sum of all entries of {@code histogram} buckets.
*/
- private static long sumHistogramEntries(HistogramMetric histogram) {
+ public static long sumHistogramEntries(HistogramMetric histogram) {
if (histogram == null)
return 0;
diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
index 0436d4c..275d905 100644
--- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
+++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java
@@ -30,6 +30,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
@@ -39,7 +41,10 @@ import org.apache.ignite.compute.ComputeJob;
import org.apache.ignite.compute.ComputeJobAdapter;
import org.apache.ignite.compute.ComputeJobResult;
import org.apache.ignite.compute.ComputeTaskAdapter;
+import org.apache.ignite.compute.ComputeTaskSplitAdapter;
+import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.binary.BinaryArray;
+import org.apache.ignite.internal.util.lang.IgnitePair;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.platform.model.ACL;
@@ -55,11 +60,17 @@ import org.apache.ignite.platform.model.User;
import org.apache.ignite.platform.model.Value;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceConfiguration;
import org.apache.ignite.services.ServiceContext;
+import org.apache.ignite.spi.metric.HistogramMetric;
+import org.apache.ignite.spi.metric.Metric;
+import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static java.util.Calendar.JANUARY;
+import static org.apache.ignite.internal.processors.service.GridServiceMetricsTest.sumHistogramEntries;
+import static org.apache.ignite.internal.processors.service.IgniteServiceProcessor.serviceMetricRegistryName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -102,7 +113,14 @@ public class PlatformDeployServiceTask extends ComputeTaskAdapter<String, Object
/** {@inheritDoc} */
@Override public Object execute() throws IgniteException {
- ignite.services().deployNodeSingleton(serviceName, new PlatformTestService());
+ ServiceConfiguration svcCfg = new ServiceConfiguration();
+
+ svcCfg.setStatisticsEnabled(true);
+ svcCfg.setName(serviceName);
+ svcCfg.setMaxPerNodeCount(1);
+ svcCfg.setService(new PlatformTestService());
+
+ ignite.services().deploy(svcCfg);
return null;
}
@@ -111,10 +129,10 @@ public class PlatformDeployServiceTask extends ComputeTaskAdapter<String, Object
/**
* Test service.
*/
- public static class PlatformTestService implements Service {
+ public static class PlatformTestService implements Service, PlatformHelperService {
/** */
@IgniteInstanceResource
- private Ignite ignite;
+ private IgniteEx ignite;
/** */
private boolean isCancelled;
@@ -671,10 +689,96 @@ public class PlatformDeployServiceTask extends ComputeTaskAdapter<String, Object
}
}
+ /** {@inheritDoc} */
+ @Override public int testNumberOfInvocations(String svcName, String histName) {
+ return ignite.compute().execute(new CountServiceMetricsTask(), new IgnitePair<>(svcName, histName));
+ }
+
/** */
public Object contextAttribute(String name) {
return svcCtx.currentCallContext().attribute(name);
}
+
+ /**
+ * Calculates number of registered values among the service statistics. Can process all service metrics or
+ * certain named one.
+ */
+ private static class CountServiceMetricsTask extends ComputeTaskSplitAdapter<IgnitePair<String>, Integer> {
+ /** {@inheritDoc} */
+ @Override public Integer reduce(List<ComputeJobResult> results) throws IgniteException {
+ int cnt = 0;
+
+ for (ComputeJobResult res : results) {
+ if (res.isCancelled()) {
+ throw new IgniteException("Unable to count invocations in service metrics. Job was canceled " +
+ "on node [" + res.getNode() + "].");
+ }
+
+ if (res.getException() != null) {
+ throw new IgniteException("Unable to count invocations in service metrics. Job failed on " +
+ "node [" + res.getNode() + "]: " + res.getException().getMessage(), res.getException());
+ }
+
+ if (res.getData() == null)
+ continue;
+
+ cnt += (int)res.getData();
+ }
+
+ return cnt;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Collection<? extends ComputeJob> split(int gridSize,
+ IgnitePair<String> arg) throws IgniteException {
+ return Stream.generate(() -> new CountServiceMetricsLocallyJob(arg.get1(), arg.get2())).limit(gridSize).
+ collect(Collectors.toList());
+ }
+
+ /** Summs invocation of service methods by service statistics on certain node. */
+ private static class CountServiceMetricsLocallyJob extends ComputeJobAdapter {
+ /** Service name. */
+ private final String svcName;
+
+ /** Name of the histogramm. If {@code null}, every histogram in the service metric is processed. */
+ @Nullable private final String histName;
+
+ /** */
+ @IgniteInstanceResource
+ private IgniteEx ignite;
+
+ /**
+ * @param svcName Service name.
+ * @param histName Name of the histogramm. If {@code null}, every histogram in the service metric is
+ * processed.
+ */
+ private CountServiceMetricsLocallyJob(String svcName, @Nullable String histName) {
+ this.svcName = svcName;
+ this.histName = histName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Integer execute() throws IgniteException {
+ ReadOnlyMetricRegistry metrics = ignite.context().metric().registry(
+ serviceMetricRegistryName(svcName));
+
+ if (histName != null && !histName.isEmpty()) {
+ HistogramMetric hist = metrics.findMetric(histName);
+
+ return hist == null ? 0 : (int)sumHistogramEntries(hist);
+ }
+
+ int cnt = 0;
+
+ for (Metric metric : metrics) {
+ if (metric instanceof HistogramMetric)
+ cnt += sumHistogramEntries((HistogramMetric)metric);
+ }
+
+ return cnt;
+ }
+ }
+ }
}
/** */
@@ -700,4 +804,16 @@ public class PlatformDeployServiceTask extends ComputeTaskAdapter<String, Object
super(msg);
}
}
+
+ /**
+ * Platform helper service.
+ */
+ private interface PlatformHelperService {
+ /**
+ * Calculates number of registered values among the service statistics.
+ *
+ * @return Number of registered values among the service statistics.
+ */
+ int testNumberOfInvocations(String svcName, String histName);
+ }
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
index 12fd52d..c1f7591 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs
@@ -216,4 +216,16 @@ namespace Apache.Ignite.Core.Tests.Services
/** */
object contextAttribute(string name);
}
+
+ /// <summary>
+ /// Interface for the methods that are available only on Java side.
+ /// </summary>
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public interface IJavaOnlyService : IJavaService
+ {
+ /// <summary>
+ /// Returns number of measured by service metrics invocations of all service's methods or of its certain method.
+ /// </summary>
+ int testNumberOfInvocations(string svcName, string histName = null);
+ }
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs
index 6b834bf..42fbb3f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesAsyncWrapper.cs
@@ -169,17 +169,18 @@ namespace Apache.Ignite.Core.Tests.Services
return _services.GetServiceDescriptors();
}
+#pragma warning disable 618
/** <inheritDoc /> */
public T GetService<T>(string name)
{
return _services.GetService<T>(name);
}
-
/** <inheritDoc /> */
public ICollection<T> GetServices<T>(string name)
{
return _services.GetServices<T>(name);
}
+#pragma warning restore 618
/** <inheritDoc /> */
public T GetServiceProxy<T>(string name) where T : class
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index 798a98e..a89a539 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -41,6 +41,7 @@ namespace Apache.Ignite.Core.Tests.Services
/// <summary>
/// Services tests.
/// </summary>
+#pragma warning disable 618
public class ServicesTest
{
/** */
@@ -410,10 +411,6 @@ namespace Apache.Ignite.Core.Tests.Services
Assert.AreEqual(43, svc.TestOverload(2, ServicesTypeAutoResolveTest.Param));
- // Check local scenario (proxy should not be created for local instance)
- Assert.IsTrue(ReferenceEquals(Grid2.GetServices().GetService<ITestIgniteService>(SvcName),
- Grid2.GetServices().GetServiceProxy<ITestIgniteService>(SvcName)));
-
// Check sticky = false: call multiple times, check that different nodes get invoked
var invokedIds = Enumerable.Range(1, 100).Select(x => prx.NodeId).Distinct().ToList();
Assert.AreEqual(2, invokedIds.Count);
@@ -490,10 +487,111 @@ namespace Apache.Ignite.Core.Tests.Services
// Make sure there is an instance on grid1.
var svcInst = Grid1.GetServices().GetService<ITestIgniteService>(SvcName);
Assert.IsNotNull(svcInst);
+ }
+
+ /// <summary>
+ /// Tests statistics of pure Java service. Call the service itself.
+ /// </summary>
+ [Test]
+ public void TestJavaServiceStatistics()
+ {
+ // Java service itself.
+ var helperSvc = Grid1.GetServices().GetServiceProxy<IJavaOnlyService>(_javaSvcName, false);
+
+ // Check metrics of pure java service. There were no invocations yet.
+ Assert.AreEqual(0, helperSvc.testNumberOfInvocations(_javaSvcName));
+ // Now we did 1 invocation of pure Java service just before.
+ Assert.AreEqual(1, helperSvc.testNumberOfInvocations(_javaSvcName));
+ // In total we did 2 calls by now.
+ Assert.AreEqual(2, helperSvc.testNumberOfInvocations(_javaSvcName));
+ }
+
+ /// <summary>
+ /// Tests statistics of a platform service from client/remote node.
+ /// </summary>
+ [Test]
+ public void TestStatisticsRemote()
+ {
+ DoTestMetrics(Grid1.GetServices(), _client.GetServices(), null, false);
+
+ DoTestMetrics(_client.GetServices(), _client.GetServices(), null, false);
+ }
+
+ /// <summary>
+ /// Tests statistics of a platform service from client/remote node using a call context.
+ /// </summary>
+ [Test]
+ public void TestStatisticsRemoteWithCallCtx()
+ {
+ DoTestMetrics(Grid1.GetServices(), _client.GetServices(), callContext(), false);
+
+ DoTestMetrics(_client.GetServices(), _client.GetServices(), callContext(), false);
+ }
+
+ /// <summary>
+ /// Tests statistics of a dynamically-proxied platform service from client/remote node.
+ /// </summary>
+ [Test]
+ public void TestStatisticsRemoteDynamically()
+ {
+ DoTestMetrics(Grid1.GetServices(), _client.GetServices(), null, true);
+
+ DoTestMetrics(_client.GetServices(), _client.GetServices(), null, true);
+ }
+
+ /// <summary>
+ /// Tests statistics of a dynamically-proxied platform service from client/remote node using a call context.
+ /// </summary>
+ [Test]
+ public void TestStatisticsRemoteDynamicallyWithCallContext()
+ {
+ DoTestMetrics(Grid1.GetServices(), _client.GetServices(), callContext(), true);
+
+ DoTestMetrics(_client.GetServices(), _client.GetServices(), callContext(), true);
+ }
+
+ /// <summary>
+ /// Tests statistics of a platform service from server/local node.
+ /// </summary>
+ [Test]
+ public void TestStatisticsLocal()
+ {
+ DoTestMetrics(Grid1.GetServices(), Grid1.GetServices(), null, false);
+
+ DoTestMetrics(_client.GetServices(), Grid1.GetServices(), null, false);
+ }
+
+ /// <summary>
+ /// Tests statistics of a platform service from server/local node using the call context.
+ /// </summary>
+ [Test]
+ public void TestStatisticsLocalWithCallContext()
+ {
+ DoTestMetrics(Grid1.GetServices(), Grid1.GetServices(), callContext(), false);
- // Get dynamic proxy that simply wraps the service instance.
- var prx = Grid1.GetServices().GetDynamicServiceProxy(SvcName);
- Assert.AreSame(prx, svcInst);
+ DoTestMetrics(_client.GetServices(), Grid1.GetServices(), callContext(), false);
+ }
+
+ /// <summary>
+ /// Tests statistics of a dynamically-proxied platform service from server/local node.
+ /// </summary>
+ [Test]
+ public void TestStatisticsLocalDynamic()
+ {
+ DoTestMetrics(Grid1.GetServices(), Grid1.GetServices(), null, true);
+
+ DoTestMetrics(_client.GetServices(), Grid1.GetServices(), null, true);
+ }
+
+ /// <summary>
+ /// Tests statistics of a dynamically-proxied platform service from server/local node using a call context.
+ /// </summary>
+ [Test]
+ public void TestStatisticsLocalDynamicWithCallContext()
+ {
+ DoTestMetrics(Grid1.GetServices(), Grid1.GetServices(), callContext(), true);
+
+ DoTestMetrics(_client.GetServices(), Grid1.GetServices(), callContext(), true);
}
/// <summary>
@@ -1287,7 +1385,7 @@ namespace Apache.Ignite.Core.Tests.Services
}
/// <summary>
- /// Tets binary methods in services.
+ /// Tests binary methods in services.
/// </summary>
private void DoTestBinary(IJavaService svc, IJavaService binSvc, bool isPlatform)
{
@@ -1331,6 +1429,88 @@ namespace Apache.Ignite.Core.Tests.Services
}
/// <summary>
+ /// Tests platform service statistics.
+ /// </summary>
+ private void DoTestMetrics(IServices producer, IServices consumer, IServiceCallContext callCtx, bool dyn)
+ {
+ var cfg = new ServiceConfiguration
+ {
+ Name = "TestMetricsSrv",
+ MaxPerNodeCount = 1,
+ TotalCount = 3,
+ Service = new PlatformTestService(),
+ };
+
+ producer.Deploy(cfg);
+
+ var svc = dyn
+ ? consumer.GetDynamicServiceProxy(cfg.Name, false, callCtx)
+ : consumer.GetServiceProxy<IJavaService>(cfg.Name, false, callCtx);
+
+ // Subject service, calculates invocations.
+ var helperSvc = producer.GetServiceProxy<IJavaOnlyService>(_javaSvcName, false);
+
+ // Do 4 invocations.
+ Assert.AreEqual(3, dyn ? svc.testOverload(1, 2) : ((IJavaService)svc).testOverload(1, 2));
+ Assert.AreEqual(2, dyn ? svc.test(1) : ((IJavaService)svc).test(1));
+ Assert.AreEqual(true, dyn ? svc.test(false) : ((IJavaService)svc).test(false));
+ Assert.AreEqual(null, dyn ? svc.testNull(null) : ((IJavaService)svc).testNull(null));
+
+ // Service stats. is not enabled.
+ Assert.AreEqual(0, helperSvc.testNumberOfInvocations(cfg.Name));
+
+ producer.Cancel(cfg.Name);
+
+ AssertNoService(cfg.Name);
+
+ // Redeploy service with enabled stats.
+ cfg.StatisticsEnabled = true;
+ cfg.Service = new PlatformTestService();
+ producer.Deploy(cfg);
+
+ svc = dyn
+ ? consumer.GetDynamicServiceProxy(cfg.Name, false, callCtx)
+ : consumer.GetServiceProxy<IJavaService>(cfg.Name, false, callCtx);
+
+ // Service metrics exists but holds no values.
+ Assert.AreEqual(0, helperSvc.testNumberOfInvocations(cfg.Name));
+
+ // One invocation.
+ Assert.AreEqual(2, dyn ? svc.test(1) : ((IJavaService)svc).test(1));
+
+ // There should be just one certain and one total invocation.
+ Assert.AreEqual(1, helperSvc.testNumberOfInvocations(cfg.Name, "test"));
+ Assert.AreEqual(1, helperSvc.testNumberOfInvocations(cfg.Name));
+
+ // Do 4 more invocations.
+ Assert.AreEqual(3, dyn ? svc.testOverload(1, 2) : ((IJavaService)svc).testOverload(1, 2));
+ Assert.AreEqual(2, dyn ? svc.test(1) : ((IJavaService)svc).test(1));
+ Assert.AreEqual(true, dyn ? svc.test(false) : ((IJavaService)svc).test(false));
+ Assert.AreEqual(null, dyn ? svc.testNull(null) : ((IJavaService)svc).testNull(null));
+
+ // We did 3 invocations of method named 'test(...)' in total.
+ Assert.AreEqual(3, helperSvc.testNumberOfInvocations(cfg.Name, "test"));
+
+ // We did 1 invocations of method named 'testOverload(...)' in total.
+ Assert.AreEqual(1, helperSvc.testNumberOfInvocations(cfg.Name, "testOverload"));
+
+ Assert.AreEqual(1, helperSvc.testNumberOfInvocations(cfg.Name, "testNull"));
+
+ // We did 5 total invocations.
+ Assert.AreEqual(5, helperSvc.testNumberOfInvocations(cfg.Name));
+
+ // Check side methods are not measured. We still have only 5 invocations.
+ Assert.AreEqual("Apache.Ignite.Core.Tests.Services.PlatformTestService", svc.ToString());
+ Assert.AreEqual(5, helperSvc.testNumberOfInvocations(cfg.Name));
+ // 'ToString' must not be measured. Like Java service metrics, it's not declared as a service interface.
+ Assert.AreEqual(0, helperSvc.testNumberOfInvocations(cfg.Name, "ToString"));
+
+ // Undeploy again.
+ producer.Cancel(cfg.Name);
+ AssertNoService(cfg.Name);
+ }
+
+ /// <summary>
/// Tests the footer setting.
/// </summary>
[Test]
@@ -1964,14 +2144,4 @@ namespace Apache.Ignite.Core.Tests.Services
}
#endif
}
-
- /// <summary> Tests with UseBinaryArray = true. </summary>
- public class ServicesTestBinaryArrays : ServicesTest
- {
- /** */
- public ServicesTestBinaryArrays() : base(true)
- {
- // No-op.
- }
- }
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
index eeb4b96..1d986f4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Services/Services.cs
@@ -378,13 +378,6 @@ namespace Apache.Ignite.Core.Impl.Services
IgniteArgumentCheck.Ensure(typeof(T).IsInterface, "T",
"Service proxy type should be an interface: " + typeof(T));
- T locInst;
-
- // In local scenario try to return service instance itself instead of a proxy
- // Get as object because proxy interface may be different from real interface
- if (callCtx == null && (locInst = GetService<object>(name) as T) != null)
- return locInst;
-
var javaProxy = DoOutOpObject(OpServiceProxy, w =>
{
w.WriteString(name);
@@ -415,15 +408,6 @@ namespace Apache.Ignite.Core.Impl.Services
{
IgniteArgumentCheck.NotNullOrEmpty(name, "name");
- // In local scenario try to return service instance itself instead of a proxy
- if (callCtx == null)
- {
- var locInst = GetService<object>(name);
-
- if (locInst != null)
- return locInst;
- }
-
var javaProxy = DoOutOpObject(OpServiceProxy, w =>
{
w.WriteString(name);
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Services/IServices.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Services/IServices.cs
index 569712e..07a36ec 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Services/IServices.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Services/IServices.cs
@@ -17,6 +17,7 @@
namespace Apache.Ignite.Core.Services
{
+ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
@@ -240,6 +241,8 @@ namespace Apache.Ignite.Core.Services
/// <typeparam name="T">Service type.</typeparam>
/// <param name="name">Service name.</param>
/// <returns>Deployed service with specified name.</returns>
+ [Obsolete("Corrupts the service statistics. Use the proxies like GetServiceProxy() or " +
+ "GetDynamicServiceProxy() instead.")]
T GetService<T>(string name);
/// <summary>
@@ -248,33 +251,33 @@ namespace Apache.Ignite.Core.Services
/// <typeparam name="T">Service type.</typeparam>
/// <param name="name">Service name.</param>
/// <returns>All deployed services with specified name.</returns>
+ [Obsolete("Corrupts the service statistics. Use the proxies like GetServiceProxy() or " +
+ "GetDynamicServiceProxy() instead.")]
ICollection<T> GetServices<T>(string name);
/// <summary>
- /// Gets a remote handle on the service. If service is available locally,
- /// then local instance is returned, otherwise, a remote proxy is dynamically
- /// created and provided for the specified service.
+ /// Gets a handle on remote or local service. The proxy is dynamically created and provided for the specified
+ /// service.
/// </summary>
/// <typeparam name="T">Service type.</typeparam>
/// <param name="name">Service name.</param>
- /// <returns>Either proxy over remote service or local service if it is deployed locally.</returns>
+ /// <returns>Proxy over service.</returns>
T GetServiceProxy<T>(string name) where T : class;
/// <summary>
- /// Gets a remote handle on the service. If service is available locally,
- /// then local instance is returned, otherwise, a remote proxy is dynamically
- /// created and provided for the specified service.
+ /// Gets a handle on remote or local service. The proxy is dynamically created and provided for the specified
+ /// service.
/// </summary>
/// <typeparam name="T">Service type.</typeparam>
/// <param name="name">Service name.</param>
/// <param name="sticky">Whether or not Ignite should always contact the same remote
/// service or try to load-balance between services.</param>
- /// <returns>Either proxy over remote service or local service if it is deployed locally.</returns>
+ /// <returns>Proxy over service.</returns>
T GetServiceProxy<T>(string name, bool sticky) where T : class;
/// <summary>
- /// Gets a remote handle on the service with the specified caller context.
- /// The proxy is dynamically created and provided for the specified service.
+ /// Gets a handle on remote or local service with the specified caller context. The proxy is dynamically
+ /// created and provided for the specified service.
/// </summary>
/// <typeparam name="T">Service type.</typeparam>
/// <param name="name">Service name.</param>
@@ -287,21 +290,19 @@ namespace Apache.Ignite.Core.Services
T GetServiceProxy<T>(string name, bool sticky, IServiceCallContext callCtx) where T : class;
/// <summary>
- /// Gets a remote handle on the service as a dynamic object. If service is available locally,
- /// then local instance is returned, otherwise, a remote proxy is dynamically
- /// created and provided for the specified service.
+ /// Gets a handle on remote or local service as a dynamic object. The proxy is dynamically created and provided
+ /// for the specified service.
/// <para />
/// This method utilizes <c>dynamic</c> feature of the language and does not require any
/// service interfaces or classes. Java services can be accessed as well as .NET services.
/// </summary>
/// <param name="name">Service name.</param>
- /// <returns>Either proxy over remote service or local service if it is deployed locally.</returns>
+ /// <returns>Proxy over service.</returns>
dynamic GetDynamicServiceProxy(string name);
/// <summary>
- /// Gets a remote handle on the service as a dynamic object. If service is available locally,
- /// then local instance is returned, otherwise, a remote proxy is dynamically
- /// created and provided for the specified service.
+ /// Gets a handle on remote or local service as a dynamic object. The proxy is dynamically created and provided
+ /// for the specified service.
/// <para />
/// This method utilizes <c>dynamic</c> feature of the language and does not require any
/// service interfaces or classes. Java services can be accessed as well as .NET services.
@@ -309,12 +310,12 @@ namespace Apache.Ignite.Core.Services
/// <param name="name">Service name.</param>
/// <param name="sticky">Whether or not Ignite should always contact the same remote
/// service or try to load-balance between services.</param>
- /// <returns>Either proxy over remote service or local service if it is deployed locally.</returns>
+ /// <returns>Proxy over service.</returns>
dynamic GetDynamicServiceProxy(string name, bool sticky);
/// <summary>
- /// Gets a remote handle on the service with the specified caller context.
- /// The proxy is dynamically created and provided for the specified service.
+ /// Gets a handle on remote or local service as a dynamic object with the specified caller context. The proxy
+ /// is dynamically created and provided for the specified service.
/// <para />
/// This method utilizes <c>dynamic</c> feature of the language and does not require any
/// service interfaces or classes. Java services can be accessed as well as .NET services.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Services/ServiceConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Services/ServiceConfiguration.cs
index a7b9e7f..9753b98 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Services/ServiceConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Services/ServiceConfiguration.cs
@@ -20,6 +20,8 @@ namespace Apache.Ignite.Core.Services
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Reflection;
using Apache.Ignite.Core.Binary;
using Apache.Ignite.Core.Cluster;
@@ -63,6 +65,12 @@ namespace Apache.Ignite.Core.Services
/// Gets or sets node filter used to filter nodes on which the service will be deployed.
/// </summary>
public IClusterNodeFilter NodeFilter { get; set; }
+
+ /// <summary>
+ /// Enables or disables service statistics.
+ /// NOTE: Service statistics work only via service proxies. <see cref="IServices.GetServiceProxy{T}(string)"/>
+ /// </summary>
+ public bool StatisticsEnabled { get; set; }
/// <summary>
/// Serializes the Service configuration using IBinaryRawWriter
@@ -83,6 +91,29 @@ namespace Apache.Ignite.Core.Services
w.WriteObject(NodeFilter);
else
w.WriteObject<object>(null);
+
+ w.WriteBoolean(StatisticsEnabled);
+
+ WriteExtraDescription(w);
+ }
+
+ /// <summary>
+ /// Provides extra info about platform service to avoid on-demand creation of service statistics on any
+ /// out-of-interface calls or things like 'ToString()'.
+ /// </summary>
+ private void WriteExtraDescription(IBinaryRawWriter writer)
+ {
+ if (StatisticsEnabled)
+ {
+ // Methods names of user interfaces of the service.
+ var mtdNames = Service.GetType().GetInterfaces()
+ // No need to measure methods of these interface.
+ .Where(t => t != typeof(IService))
+ .SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly |
+ BindingFlags.Public ).Select(mtd => mtd.Name)).Distinct();
+
+ writer.WriteStringArray(mtdNames.ToArray());
+ }
}
/// <summary>
@@ -125,6 +156,8 @@ namespace Apache.Ignite.Core.Services
{
// Ignore exceptions in user deserealization code.
}
+
+ StatisticsEnabled = r.ReadBoolean();
}
}
}
\ No newline at end of file