Java Code Examples for org.elasticsearch.cluster.routing.ShardRouting#relocatingNodeId()

The following examples show how to use org.elasticsearch.cluster.routing.ShardRouting#relocatingNodeId() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: TransportLeaderShardIngestAction.java    From elasticsearch-helper with Apache License 2.0 6 votes vote down vote up
private int findReplicaLevel(ShardIterator shardIt) {
    int replicaLevel = 0;
    shardIt.reset();
    ShardRouting shard;
    while ((shard = shardIt.nextOrNull()) != null) {
        if (shard.unassigned()) {
            continue;
        }
        boolean doOnlyOnRelocating = false;
        if (shard.primary()) {
            if (shard.relocating()) {
                doOnlyOnRelocating = true;
            } else {
                continue;
            }
        }
        String nodeId = !doOnlyOnRelocating ? shard.currentNodeId() : shard.relocating() ? shard.relocatingNodeId() : null;
        if (nodeId == null) {
            continue;
        }
        replicaLevel++;
    }
    return replicaLevel;
}
 
Example 2
Source File: InsertFromValues.java    From crate with Apache License 2.0 6 votes vote down vote up
private static ShardLocation getShardLocation(String indexName,
                                              String id,
                                              @Nullable String routing,
                                              ClusterService clusterService) {
    ShardIterator shardIterator = clusterService.operationRouting().indexShards(
        clusterService.state(),
        indexName,
        id,
        routing);

    final String nodeId;
    ShardRouting shardRouting = shardIterator.nextOrNull();
    if (shardRouting == null) {
        nodeId = null;
    } else if (shardRouting.active() == false) {
        nodeId = shardRouting.relocatingNodeId();
    } else {
        nodeId = shardRouting.currentNodeId();
    }
    return new ShardLocation(shardIterator.shardId(), nodeId);
}
 
Example 3
Source File: SysAllocations.java    From crate with Apache License 2.0 6 votes vote down vote up
private static ClusterAllocationExplanation explainShard(ShardRouting shardRouting,
                                                         RoutingAllocation allocation,
                                                         GatewayAllocator gatewayAllocator,
                                                         ShardsAllocator shardAllocator) {
    allocation.setDebugMode(RoutingAllocation.DebugMode.EXCLUDE_YES_DECISIONS);

    ShardAllocationDecision shardDecision;
    if (shardRouting.initializing() || shardRouting.relocating()) {
        shardDecision = ShardAllocationDecision.NOT_TAKEN;
    } else {
        AllocateUnassignedDecision allocateDecision = shardRouting.unassigned() ?
            gatewayAllocator.decideUnassignedShardAllocation(shardRouting, allocation) : AllocateUnassignedDecision.NOT_TAKEN;
        if (allocateDecision.isDecisionTaken() == false) {
            shardDecision = shardAllocator.decideShardAllocation(shardRouting, allocation);
        } else {
            shardDecision = new ShardAllocationDecision(allocateDecision, MoveDecision.NOT_TAKEN);
        }
    }
    return new ClusterAllocationExplanation(
        shardRouting,
        shardRouting.currentNodeId() != null ? allocation.nodes().get(shardRouting.currentNodeId()) : null,
        shardRouting.relocatingNodeId() != null ? allocation.nodes().get(shardRouting.relocatingNodeId()) : null,
        null,
        shardDecision
    );
}
 
Example 4
Source File: IndicesStore.java    From Elasticsearch with Apache License 2.0 5 votes vote down vote up
boolean shardCanBeDeleted(ClusterState state, IndexShardRoutingTable indexShardRoutingTable) {
    // a shard can be deleted if all its copies are active, and its not allocated on this node
    if (indexShardRoutingTable.size() == 0) {
        // should not really happen, there should always be at least 1 (primary) shard in a
        // shard replication group, in any case, protected from deleting something by mistake
        return false;
    }

    for (ShardRouting shardRouting : indexShardRoutingTable) {
        // be conservative here, check on started, not even active
        if (!shardRouting.started()) {
            return false;
        }

        // if the allocated or relocation node id doesn't exists in the cluster state  it may be a stale node,
        // make sure we don't do anything with this until the routing table has properly been rerouted to reflect
        // the fact that the node does not exists
        DiscoveryNode node = state.nodes().get(shardRouting.currentNodeId());
        if (node == null) {
            return false;
        }
        if (shardRouting.relocatingNodeId() != null) {
            node = state.nodes().get(shardRouting.relocatingNodeId());
            if (node == null) {
                return false;
            }
        }

        // check if shard is active on the current node or is getting relocated to the our node
        String localNodeId = state.getNodes().localNode().id();
        if (localNodeId.equals(shardRouting.currentNodeId()) || localNodeId.equals(shardRouting.relocatingNodeId())) {
            return false;
        }
    }

    return true;
}
 
Example 5
Source File: DiskThresholdDecider.java    From Elasticsearch with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the size of all shards that are currently being relocated to
 * the node, but may not be finished transfering yet.
 *
 * If subtractShardsMovingAway is set then the size of shards moving away is subtracted from the total size
 * of all shards
 */
public static long sizeOfRelocatingShards(RoutingNode node, ClusterInfo clusterInfo, boolean subtractShardsMovingAway, String dataPath) {
    long totalSize = 0;
    for (ShardRouting routing : node.shardsWithState(ShardRoutingState.RELOCATING, ShardRoutingState.INITIALIZING)) {
        String actualPath = clusterInfo.getDataPath(routing);
        if (dataPath.equals(actualPath)) {
            if (routing.initializing() && routing.relocatingNodeId() != null) {
                totalSize += getShardSize(routing, clusterInfo);
            } else if (subtractShardsMovingAway && routing.relocating()) {
                totalSize -= getShardSize(routing, clusterInfo);
            }
        }
    }
    return totalSize;
}
 
Example 6
Source File: GroupRowsByShard.java    From crate with Apache License 2.0 5 votes vote down vote up
@Nullable
private ShardLocation getShardLocation(String indexName, String id, @Nullable String routing) {
    try {
        ShardIterator shardIterator = clusterService.operationRouting().indexShards(
            clusterService.state(),
            indexName,
            id,
            routing
        );

        final String nodeId;
        ShardRouting shardRouting = shardIterator.nextOrNull();
        if (shardRouting == null) {
            nodeId = null;
        } else if (shardRouting.active() == false) {
            nodeId = shardRouting.relocatingNodeId();
        } else {
            nodeId = shardRouting.currentNodeId();
        }
        if (nodeId == null && LOGGER.isDebugEnabled()) {
            LOGGER.debug("Unable to get the node id for index {} and shard {}", indexName, id);
        }
        return new ShardLocation(shardIterator.shardId(), nodeId);
    } catch (IndexNotFoundException e) {
        if (!autoCreateIndices) {
            throw e;
        }
        return null;
    }
}
 
Example 7
Source File: AwarenessAllocationDecider.java    From Elasticsearch with Apache License 2.0 4 votes vote down vote up
private Decision underCapacity(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation, boolean moveToNode) {
    if (awarenessAttributes.length == 0) {
        return allocation.decision(Decision.YES, NAME, "no allocation awareness enabled");
    }

    IndexMetaData indexMetaData = allocation.metaData().index(shardRouting.index());
    int shardCount = indexMetaData.getNumberOfReplicas() + 1; // 1 for primary
    for (String awarenessAttribute : awarenessAttributes) {
        // the node the shard exists on must be associated with an awareness attribute
        if (!node.node().attributes().containsKey(awarenessAttribute)) {
            return allocation.decision(Decision.NO, NAME, "node does not contain awareness attribute: [%s]", awarenessAttribute);
        }

        // build attr_value -> nodes map
        ObjectIntHashMap<String> nodesPerAttribute = allocation.routingNodes().nodesPerAttributesCounts(awarenessAttribute);

        // build the count of shards per attribute value
        ObjectIntHashMap<String> shardPerAttribute = new ObjectIntHashMap<>();
        for (ShardRouting assignedShard : allocation.routingNodes().assignedShards(shardRouting)) {
            if (assignedShard.started() || assignedShard.initializing()) {
                // Note: this also counts relocation targets as that will be the new location of the shard.
                // Relocation sources should not be counted as the shard is moving away
                RoutingNode routingNode = allocation.routingNodes().node(assignedShard.currentNodeId());
                shardPerAttribute.addTo(routingNode.node().attributes().get(awarenessAttribute), 1);
            }
        }

        if (moveToNode) {
            if (shardRouting.assignedToNode()) {
                String nodeId = shardRouting.relocating() ? shardRouting.relocatingNodeId() : shardRouting.currentNodeId();
                if (!node.nodeId().equals(nodeId)) {
                    // we work on different nodes, move counts around
                    shardPerAttribute.putOrAdd(allocation.routingNodes().node(nodeId).node().attributes().get(awarenessAttribute), 0, -1);
                    shardPerAttribute.addTo(node.node().attributes().get(awarenessAttribute), 1);
                }
            } else {
                shardPerAttribute.addTo(node.node().attributes().get(awarenessAttribute), 1);
            }
        }

        int numberOfAttributes = nodesPerAttribute.size();
        String[] fullValues = forcedAwarenessAttributes.get(awarenessAttribute);
        if (fullValues != null) {
            for (String fullValue : fullValues) {
                if (!shardPerAttribute.containsKey(fullValue)) {
                    numberOfAttributes++;
                }
            }
        }
        // TODO should we remove ones that are not part of full list?

        int averagePerAttribute = shardCount / numberOfAttributes;
        int totalLeftover = shardCount % numberOfAttributes;
        int requiredCountPerAttribute;
        if (averagePerAttribute == 0) {
            // if we have more attributes values than shard count, no leftover
            totalLeftover = 0;
            requiredCountPerAttribute = 1;
        } else {
            requiredCountPerAttribute = averagePerAttribute;
        }
        int leftoverPerAttribute = totalLeftover == 0 ? 0 : 1;

        int currentNodeCount = shardPerAttribute.get(node.node().attributes().get(awarenessAttribute));
        // if we are above with leftover, then we know we are not good, even with mod
        if (currentNodeCount > (requiredCountPerAttribute + leftoverPerAttribute)) {
            return allocation.decision(Decision.NO, NAME,
                    "too many shards on node for attribute: [%s], required per attribute: [%d], node count: [%d], leftover: [%d]",
                    awarenessAttribute, requiredCountPerAttribute, currentNodeCount, leftoverPerAttribute);
        }
        // all is well, we are below or same as average
        if (currentNodeCount <= requiredCountPerAttribute) {
            continue;
        }
    }

    return allocation.decision(Decision.YES, NAME, "node meets awareness requirements");
}
 
Example 8
Source File: CancelAllocationCommand.java    From Elasticsearch with Apache License 2.0 4 votes vote down vote up
@Override
public RerouteExplanation execute(RoutingAllocation allocation, boolean explain) {
    DiscoveryNode discoNode = allocation.nodes().resolveNode(node);
    boolean found = false;
    for (RoutingNodes.RoutingNodeIterator it = allocation.routingNodes().routingNodeIter(discoNode.id()); it.hasNext(); ) {
        ShardRouting shardRouting = it.next();
        if (!shardRouting.shardId().equals(shardId)) {
            continue;
        }
        found = true;
        if (shardRouting.relocatingNodeId() != null) {
            if (shardRouting.initializing()) {
                // the shard is initializing and recovering from another node, simply cancel the recovery
                it.remove();
                // and cancel the relocating state from the shard its being relocated from
                RoutingNode relocatingFromNode = allocation.routingNodes().node(shardRouting.relocatingNodeId());
                if (relocatingFromNode != null) {
                    for (ShardRouting fromShardRouting : relocatingFromNode) {
                        if (fromShardRouting.isSameShard(shardRouting) && fromShardRouting.state() == RELOCATING) {
                            allocation.routingNodes().cancelRelocation(fromShardRouting);
                            break;
                        }
                    }
                }
            } else if (shardRouting.relocating()) {

                // the shard is relocating to another node, cancel the recovery on the other node, and deallocate this one
                if (!allowPrimary && shardRouting.primary()) {
                    // can't cancel a primary shard being initialized
                    if (explain) {
                        return new RerouteExplanation(this, allocation.decision(Decision.NO, "cancel_allocation_command",
                                "can't cancel " + shardId + " on node " + discoNode + ", shard is primary and initializing its state"));
                    }
                    throw new IllegalArgumentException("[cancel_allocation] can't cancel " + shardId + " on node " +
                            discoNode + ", shard is primary and initializing its state");
                }
                it.moveToUnassigned(new UnassignedInfo(UnassignedInfo.Reason.REROUTE_CANCELLED, null));
                // now, go and find the shard that is initializing on the target node, and cancel it as well...
                RoutingNodes.RoutingNodeIterator initializingNode = allocation.routingNodes().routingNodeIter(shardRouting.relocatingNodeId());
                if (initializingNode != null) {
                    while (initializingNode.hasNext()) {
                        ShardRouting initializingShardRouting = initializingNode.next();
                        if (initializingShardRouting.isRelocationTargetOf(shardRouting)) {
                            initializingNode.remove();
                        }
                    }
                }
            }
        } else {
            // the shard is not relocating, its either started, or initializing, just cancel it and move on...
            if (!allowPrimary && shardRouting.primary()) {
                // can't cancel a primary shard being initialized
                if (explain) {
                    return new RerouteExplanation(this, allocation.decision(Decision.NO, "cancel_allocation_command",
                            "can't cancel " + shardId + " on node " + discoNode + ", shard is primary and started"));
                }
                throw new IllegalArgumentException("[cancel_allocation] can't cancel " + shardId + " on node " +
                        discoNode + ", shard is primary and started");
            }
            it.moveToUnassigned(new UnassignedInfo(UnassignedInfo.Reason.REROUTE_CANCELLED, null));
        }
    }
    if (!found) {
        if (explain) {
            return new RerouteExplanation(this, allocation.decision(Decision.NO, "cancel_allocation_command",
                    "can't cancel " + shardId + ", failed to find it on node " + discoNode));
        }
        throw new IllegalArgumentException("[cancel_allocation] can't cancel " + shardId + ", failed to find it on node " + discoNode);
    }
    return new RerouteExplanation(this, allocation.decision(Decision.YES, "cancel_allocation_command",
            "shard " + shardId + " on node " + discoNode + " can be cancelled"));
}
 
Example 9
Source File: AwarenessAllocationDecider.java    From crate with Apache License 2.0 4 votes vote down vote up
private Decision underCapacity(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation, boolean moveToNode) {
    if (awarenessAttributes.isEmpty()) {
        return allocation.decision(Decision.YES, NAME,
            "allocation awareness is not enabled, set cluster setting [%s] to enable it",
            CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.getKey());
    }

    IndexMetaData indexMetaData = allocation.metaData().getIndexSafe(shardRouting.index());
    int shardCount = indexMetaData.getNumberOfReplicas() + 1; // 1 for primary
    for (String awarenessAttribute : awarenessAttributes) {
        // the node the shard exists on must be associated with an awareness attribute
        if (!node.node().getAttributes().containsKey(awarenessAttribute)) {
            return allocation.decision(Decision.NO, NAME,
                "node does not contain the awareness attribute [%s]; required attributes cluster setting [%s=%s]",
                awarenessAttribute, CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.getKey(),
                allocation.debugDecision() ? Strings.collectionToCommaDelimitedString(awarenessAttributes) : null);
        }

        // build attr_value -> nodes map
        ObjectIntHashMap<String> nodesPerAttribute = allocation.routingNodes().nodesPerAttributesCounts(awarenessAttribute);

        // build the count of shards per attribute value
        ObjectIntHashMap<String> shardPerAttribute = new ObjectIntHashMap<>();
        for (ShardRouting assignedShard : allocation.routingNodes().assignedShards(shardRouting.shardId())) {
            if (assignedShard.started() || assignedShard.initializing()) {
                // Note: this also counts relocation targets as that will be the new location of the shard.
                // Relocation sources should not be counted as the shard is moving away
                RoutingNode routingNode = allocation.routingNodes().node(assignedShard.currentNodeId());
                shardPerAttribute.addTo(routingNode.node().getAttributes().get(awarenessAttribute), 1);
            }
        }

        if (moveToNode) {
            if (shardRouting.assignedToNode()) {
                String nodeId = shardRouting.relocating() ? shardRouting.relocatingNodeId() : shardRouting.currentNodeId();
                if (!node.nodeId().equals(nodeId)) {
                    // we work on different nodes, move counts around
                    shardPerAttribute.putOrAdd(allocation.routingNodes().node(nodeId).node().getAttributes().get(awarenessAttribute),
                            0, -1);
                    shardPerAttribute.addTo(node.node().getAttributes().get(awarenessAttribute), 1);
                }
            } else {
                shardPerAttribute.addTo(node.node().getAttributes().get(awarenessAttribute), 1);
            }
        }

        int numberOfAttributes = nodesPerAttribute.size();
        List<String> fullValues = forcedAwarenessAttributes.get(awarenessAttribute);
        if (fullValues != null) {
            for (String fullValue : fullValues) {
                if (!shardPerAttribute.containsKey(fullValue)) {
                    numberOfAttributes++;
                }
            }
        }
        // TODO should we remove ones that are not part of full list?

        int averagePerAttribute = shardCount / numberOfAttributes;
        int totalLeftover = shardCount % numberOfAttributes;
        int requiredCountPerAttribute;
        if (averagePerAttribute == 0) {
            // if we have more attributes values than shard count, no leftover
            totalLeftover = 0;
            requiredCountPerAttribute = 1;
        } else {
            requiredCountPerAttribute = averagePerAttribute;
        }
        int leftoverPerAttribute = totalLeftover == 0 ? 0 : 1;

        int currentNodeCount = shardPerAttribute.get(node.node().getAttributes().get(awarenessAttribute));
        // if we are above with leftover, then we know we are not good, even with mod
        if (currentNodeCount > (requiredCountPerAttribute + leftoverPerAttribute)) {
            return allocation.decision(Decision.NO, NAME,
                    "there are too many copies of the shard allocated to nodes with attribute [%s], there are [%d] total configured " +
                    "shard copies for this shard id and [%d] total attribute values, expected the allocated shard count per " +
                    "attribute [%d] to be less than or equal to the upper bound of the required number of shards per attribute [%d]",
                    awarenessAttribute,
                    shardCount,
                    numberOfAttributes,
                    currentNodeCount,
                    requiredCountPerAttribute + leftoverPerAttribute);
        }
        // all is well, we are below or same as average
        if (currentNodeCount <= requiredCountPerAttribute) {
            continue;
        }
    }

    return allocation.decision(Decision.YES, NAME, "node meets all awareness attribute requirements");
}
 
Example 10
Source File: CancelAllocationCommand.java    From crate with Apache License 2.0 4 votes vote down vote up
@Override
public RerouteExplanation execute(RoutingAllocation allocation, boolean explain) {
    DiscoveryNode discoNode = allocation.nodes().resolveNode(node);
    ShardRouting shardRouting = null;
    RoutingNodes routingNodes = allocation.routingNodes();
    RoutingNode routingNode = routingNodes.node(discoNode.getId());
    IndexMetaData indexMetaData = null;
    if (routingNode != null) {
        indexMetaData = allocation.metaData().index(index());
        if (indexMetaData == null) {
            throw new IndexNotFoundException(index());
        }
        ShardId shardId = new ShardId(indexMetaData.getIndex(), shardId());
        shardRouting = routingNode.getByShardId(shardId);
    }
    if (shardRouting == null) {
        if (explain) {
            return new RerouteExplanation(this, allocation.decision(Decision.NO, "cancel_allocation_command",
                "can't cancel " + shardId + ", failed to find it on node " + discoNode));
        }
        throw new IllegalArgumentException("[cancel_allocation] can't cancel " + shardId + ", failed to find it on node " + discoNode);
    }
    if (shardRouting.primary() && allowPrimary == false) {
        if ((shardRouting.initializing() && shardRouting.relocatingNodeId() != null) == false) {
            // only allow cancelling initializing shard of primary relocation without allowPrimary flag
            if (explain) {
                return new RerouteExplanation(this, allocation.decision(Decision.NO, "cancel_allocation_command",
                    "can't cancel " + shardId + " on node " + discoNode + ", shard is primary and " +
                        shardRouting.state().name().toLowerCase(Locale.ROOT)));
            }
            throw new IllegalArgumentException("[cancel_allocation] can't cancel " + shardId + " on node " +
                discoNode + ", shard is primary and " + shardRouting.state().name().toLowerCase(Locale.ROOT));
        }
    }
    routingNodes.failShard(LogManager.getLogger(CancelAllocationCommand.class), shardRouting,
        new UnassignedInfo(UnassignedInfo.Reason.REROUTE_CANCELLED, null), indexMetaData, allocation.changes());
    // TODO: We don't have to remove a cancelled shard from in-sync set once we have a strict resync implementation.
    allocation.removeAllocationId(shardRouting);
    return new RerouteExplanation(this, allocation.decision(Decision.YES, "cancel_allocation_command",
            "shard " + shardId + " on node " + discoNode + " can be cancelled"));
}
 
Example 11
Source File: Get.java    From crate with Apache License 2.0 4 votes vote down vote up
@Override
public ExecutionPlan build(PlannerContext plannerContext,
                           ProjectionBuilder projectionBuilder,
                           int limitHint,
                           int offsetHint,
                           @Nullable OrderBy order,
                           @Nullable Integer pageSizeHint,
                           Row params,
                           SubQueryResults subQueryResults) {
    HashMap<String, Map<ShardId, List<PKAndVersion>>> idsByShardByNode = new HashMap<>();
    DocTableInfo docTableInfo = tableRelation.tableInfo();
    List<Symbol> boundOutputs = Lists2.map(
        outputs, s -> SubQueryAndParamBinder.convert(s, params, subQueryResults));
    for (DocKeys.DocKey docKey : docKeys) {
        String id = docKey.getId(plannerContext.transactionContext(), plannerContext.functions(), params, subQueryResults);
        if (id == null) {
            continue;
        }
        List<String> partitionValues = docKey.getPartitionValues(
            plannerContext.transactionContext(), plannerContext.functions(), params, subQueryResults);
        String indexName = indexName(docTableInfo, partitionValues);

        String routing = docKey.getRouting(
            plannerContext.transactionContext(), plannerContext.functions(), params, subQueryResults);
        ShardRouting shardRouting;
        try {
            shardRouting = plannerContext.resolveShard(indexName, id, routing);
        } catch (IndexNotFoundException e) {
            if (docTableInfo.isPartitioned()) {
                continue;
            }
            throw e;
        }
        String currentNodeId = shardRouting.currentNodeId();
        if (currentNodeId == null) {
            // If relocating is fast enough this will work, otherwise it will result in a shard failure which
            // will cause a statement retry
            currentNodeId = shardRouting.relocatingNodeId();
            if (currentNodeId == null) {
                throw new ShardNotFoundException(shardRouting.shardId());
            }
        }
        Map<ShardId, List<PKAndVersion>> idsByShard = idsByShardByNode.get(currentNodeId);
        if (idsByShard == null) {
            idsByShard = new HashMap<>();
            idsByShardByNode.put(currentNodeId, idsByShard);
        }
        List<PKAndVersion> pkAndVersions = idsByShard.get(shardRouting.shardId());
        if (pkAndVersions == null) {
            pkAndVersions = new ArrayList<>();
            idsByShard.put(shardRouting.shardId(), pkAndVersions);
        }
        long version = docKey
            .version(plannerContext.transactionContext(), plannerContext.functions(), params, subQueryResults)
            .orElse(Versions.MATCH_ANY);
        long sequenceNumber = docKey.sequenceNo(plannerContext.transactionContext(), plannerContext.functions(), params, subQueryResults)
            .orElse(SequenceNumbers.UNASSIGNED_SEQ_NO);
        long primaryTerm = docKey.primaryTerm(plannerContext.transactionContext(), plannerContext.functions(), params, subQueryResults)
            .orElse(SequenceNumbers.UNASSIGNED_PRIMARY_TERM);
        pkAndVersions.add(new PKAndVersion(id, version, sequenceNumber, primaryTerm));
    }
    return new Collect(
        new PKLookupPhase(
            plannerContext.jobId(),
            plannerContext.nextExecutionPhaseId(),
            docTableInfo.partitionedBy(),
            boundOutputs,
            idsByShardByNode
        ),
        TopN.NO_LIMIT,
        0,
        boundOutputs.size(),
        docKeys.size(),
        null
    );
}