Skip to content
2 changes: 2 additions & 0 deletions docs/_docs/services/services.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ The `Service` interface has three methods:

You can deploy your service either programmatically at runtime, or by providing a service configuration as part of the node configuration.
In the latter case, the service is deployed when the cluster starts.
If deploying several related services you can specify start order with the `ServiceConfiguration.setLocalStartOrder(int)`.
Start order applied on node level during service initialization. Services with lower `localStartOrder` will be inited first.

=== Deploying Services at Runtime

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,11 +444,14 @@ public void deployMultiple(String name, Service svc, int totalCnt, int maxPerNod
* of failed services will be thrown. It is guaranteed that all services that were provided to this method and are
* not present in the list of failed services are successfully deployed by the moment of the exception being thrown.
* Note that if exception is thrown, then partial deployment may have occurred.
* Note, start order guarantees not provided, by default.
* Node local start order can be forced with the {@link ServiceConfiguration#setLocalStartOrder(int)}.
*
* @param cfgs {@link Collection} of service configurations to be deployed.
* @throws ServiceDeploymentException If failed to deploy services.
* @see IgniteServices#deploy(ServiceConfiguration)
* @see IgniteServices#deployAllAsync(Collection)
* @see ServiceConfiguration#setLocalStartOrder(int)
*/
public void deployAll(Collection<ServiceConfiguration> cfgs) throws ServiceDeploymentException;

Expand All @@ -463,11 +466,14 @@ public void deployMultiple(String name, Service svc, int totalCnt, int maxPerNod
* guaranteed that all services, that were provided to this method and are not present in the list of failed
* services, are successfully deployed by the moment of the exception being thrown. Note that if exception is
* thrown, then partial deployment may have occurred.
* Note, start order guarantees not provided, by default.
* Node local start order can be forced with the {@link ServiceConfiguration#setLocalStartOrder(int)}.
*
* @param cfgs {@link Collection} of service configurations to be deployed.
* @return a Future representing pending completion of the operation.
* @see IgniteServices#deploy(ServiceConfiguration)
* @see IgniteServices#deployAll(Collection)
* @see ServiceConfiguration#setLocalStartOrder(int)
*/
public IgniteFuture<Void> deployAllAsync(Collection<ServiceConfiguration> cfgs);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public LazyServiceConfiguration(
isStatisticsEnabled = cfg.isStatisticsEnabled();
interceptors = cfg.getInterceptors();
this.interceptorsBytes = interceptorsBytes;
locStartOrder = cfg.getLocalStartOrder();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -307,33 +308,38 @@ private void processDeploymentActions(@NotNull ServiceDeploymentActions depActio
if (!depActions.servicesToDeploy().isEmpty()) {
final Collection<UUID> evtTopNodes = nodeIds(ctx.discovery().nodes(evtTopVer));

depActions.servicesToDeploy().forEach((srvcId, desc) -> {
try {
ServiceConfiguration cfg = desc.configuration();
depActions.servicesToDeploy().entrySet().stream()
.sorted(Comparator.comparingInt(e -> e.getValue().configuration().getLocalStartOrder()))
.forEach(entry -> {
IgniteUuid srvcId = entry.getKey();
ServiceInfo desc = entry.getValue();

TreeMap<UUID, Integer> oldTop = filterDeadNodes(evtTopNodes, desc.topologySnapshot());
try {
ServiceConfiguration cfg = desc.configuration();

Map<UUID, Integer> top = reassign(srvcId, cfg, evtTopVer, oldTop);
TreeMap<UUID, Integer> oldTop = filterDeadNodes(evtTopNodes, desc.topologySnapshot());

expDeps.put(srvcId, top);
Map<UUID, Integer> top = reassign(srvcId, cfg, evtTopVer, oldTop);

Integer expCnt = top.getOrDefault(ctx.localNodeId(), 0);
expDeps.put(srvcId, top);

if (expCnt > srvcProc.localInstancesCount(srvcId)) {
srvcProc.deployment().deployerBlockingSectionBegin();
Integer expCnt = top.getOrDefault(ctx.localNodeId(), 0);

try {
srvcProc.redeploy(srvcId, cfg, top);
}
finally {
srvcProc.deployment().deployerBlockingSectionEnd();
if (expCnt > srvcProc.localInstancesCount(srvcId)) {
srvcProc.deployment().deployerBlockingSectionBegin();

try {
srvcProc.redeploy(srvcId, cfg, top);
}
finally {
srvcProc.deployment().deployerBlockingSectionEnd();
}
}
}
}
catch (IgniteCheckedException e) {
depErrors.computeIfAbsent(srvcId, c -> new ArrayList<>()).add(e);
}
});
catch (IgniteCheckedException e) {
depErrors.computeIfAbsent(srvcId, c -> new ArrayList<>()).add(e);
}
});
}

createAndSendSingleDeploymentsMessage(depId, depErrors);
Expand Down Expand Up @@ -484,25 +490,28 @@ protected void onReceiveFullDeploymentsMessage(ServiceClusterDeploymentResultBat

final Map<IgniteUuid, ServiceInfo> services = srvcProc.deployedServices();

fullTops.forEach((srvcId, top) -> {
Integer expCnt = top.snapshot().getOrDefault(ctx.localNodeId(), 0);
fullTops.entrySet().stream()
.sorted(Comparator.comparingInt(e -> services.get(e.getKey()).configuration().getLocalStartOrder()))
.forEach(entry -> {
IgniteUuid srvcId = entry.getKey();
ServiceTopology top = entry.getValue();

if (expCnt < srvcProc.localInstancesCount(srvcId)) { // Undeploy exceed instances
ServiceInfo desc = services.get(srvcId);
Integer expCnt = top.snapshot().getOrDefault(ctx.localNodeId(), 0);

assert desc != null;
if (expCnt < srvcProc.localInstancesCount(srvcId)) { // Undeploy exceed instances
ServiceInfo desc = services.get(srvcId);

ServiceConfiguration cfg = desc.configuration();
ServiceConfiguration cfg = desc.configuration();

try {
srvcProc.redeploy(srvcId, cfg, top.snapshot());
try {
srvcProc.redeploy(srvcId, cfg, top.snapshot());
}
catch (IgniteCheckedException e) {
log.error("Error occured during cancel exceed service instances: " +
"[srvcId=" + srvcId + ", name=" + desc.name() + ']', e);
}
}
catch (IgniteCheckedException e) {
log.error("Error occured during cancel exceed service instances: " +
"[srvcId=" + srvcId + ", name=" + desc.name() + ']', e);
}
}
});
});

completeSuccess();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import java.io.Externalizable;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import org.apache.ignite.IgniteServices;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.processors.service.IgniteServiceProcessor;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.internal.S;
Expand All @@ -35,14 +37,14 @@
* <pre name="code" class="java">
* IgniteConfiguration gridCfg = new IgniteConfiguration();
*
* GridServiceConfiguration svcCfg1 = new GridServiceConfiguration();
* ServiceConfiguration svcCfg1 = new ServiceConfiguration();
*
* svcCfg1.setName("myClusterSingletonService");
* svcCfg1.setMaxPerNodeCount(1);
* svcCfg1.setTotalCount(1);
* svcCfg1.setService(new MyClusterSingletonService());
*
* GridServiceConfiguration svcCfg2 = new GridServiceConfiguration();
* ServiceConfiguration svcCfg2 = new ServiceConfiguration();
*
* svcCfg2.setName("myNodeSingletonService");
* svcCfg2.setMaxPerNodeCount(1);
Expand Down Expand Up @@ -88,6 +90,19 @@ public class ServiceConfiguration implements Serializable {
@GridToStringExclude
protected ServiceCallInterceptor[] interceptors;

/**
* Node local start order.
* Note:
* <p>
* In case static service configuration {@link IgniteConfiguration#setServiceConfiguration(ServiceConfiguration...)}
* order will be applied on node start.
* </p>
* <p>
* In case deploying by the {@link IgniteServices#deployAll(Collection)}, order will be applied for deployed services.
* </p>
*/
protected int locStartOrder;

/**
* Gets service name.
* <p>
Expand Down Expand Up @@ -318,6 +333,34 @@ public ServiceConfiguration setInterceptors(ServiceCallInterceptor... intercepto
return this;
}

/**
* <p>
* In case static service configuration {@link IgniteConfiguration#setServiceConfiguration(ServiceConfiguration...)}
* order will be applied on node start.
* </p>
* <p>
* In case deploying by the {@link IgniteServices#deployAll(Collection)}, order will be applied for deployed services.
* </p>
*
* @return Node local start order. Greater value means service started later.
*/
public int getLocalStartOrder() {
return locStartOrder;
}

/**
* Sets node local start order.
* Greater value means service started later.
*
* @param locStartOrder Node local start order.
* @return {@code this} for chaining.
*/
public ServiceConfiguration setLocalStartOrder(int locStartOrder) {
this.locStartOrder = locStartOrder;

return this;
}

/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (!equalsIgnoreNodeFilter(o))
Expand Down Expand Up @@ -370,6 +413,9 @@ public boolean equalsIgnoreNodeFilter(Object o) {
if (svc != null ? !svc.getClass().equals(that.svc.getClass()) : that.svc != null)
return false;

if (locStartOrder != that.locStartOrder)
return false;

return Arrays.deepEquals(interceptors, that.interceptors);
}

Expand All @@ -383,6 +429,9 @@ public boolean equalsIgnoreNodeFilter(Object o) {
String svcCls = svc == null ? "" : svc.getClass().getSimpleName();
String nodeFilterCls = nodeFilter == null ? "" : nodeFilter.getClass().getSimpleName();

return S.toString(ServiceConfiguration.class, this, "svcCls", svcCls, "nodeFilterCls", nodeFilterCls);
return S.toString(ServiceConfiguration.class, this,
"svcCls", svcCls,
"nodeFilterCls", nodeFilterCls,
"localStartOrder", locStartOrder);
}
}
Loading
Loading