All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.internal.kernel.api.helpers.TransactionDependenciesResolver Maven / Gradle / Ivy

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.internal.kernel.api.helpers;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static org.neo4j.lock.LockType.EXCLUSIVE;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.kernel.api.KernelTransactionHandle;
import org.neo4j.kernel.api.query.QuerySnapshot;
import org.neo4j.lock.ActiveLock;
import org.neo4j.lock.ResourceType;

public class TransactionDependenciesResolver {
    private final Map> handleSnapshotsMap;
    private final Map> directDependencies;

    public TransactionDependenciesResolver(Map> handleSnapshotsMap) {
        this.handleSnapshotsMap = handleSnapshotsMap;
        this.directDependencies = initDirectDependencies();
    }

    public boolean isBlocked(KernelTransactionHandle handle) {
        return directDependencies.get(handle) != null;
    }

    public String describeBlockingTransactions(KernelTransactionHandle handle) {
        Set allBlockers =
                new TreeSet<>(Comparator.comparingLong(KernelTransactionHandle::getTransactionSequenceNumber));
        Set handles = directDependencies.get(handle);
        if (handles != null) {
            Deque blockerQueue = new ArrayDeque<>(handles);
            while (!blockerQueue.isEmpty()) {
                KernelTransactionHandle transactionHandle = blockerQueue.pop();
                if (allBlockers.add(transactionHandle)) {
                    Set transactionHandleSet = directDependencies.get(transactionHandle);
                    if (transactionHandleSet != null) {
                        blockerQueue.addAll(transactionHandleSet);
                    }
                }
            }
        }
        return describe(allBlockers);
    }

    private Map> initDirectDependencies() {
        Map> directDependencies = new HashMap<>();

        Map> transactionLocksMap =
                handleSnapshotsMap.keySet().stream().collect(toMap(identity(), getTransactionLocks()));

        for (Map.Entry> entry : handleSnapshotsMap.entrySet()) {
            Optional snapshot = entry.getValue();
            if (snapshot.isPresent()) {
                KernelTransactionHandle txHandle = entry.getKey();
                evaluateDirectDependencies(directDependencies, transactionLocksMap, txHandle, snapshot.get());
            }
        }
        return directDependencies;
    }

    private static Function> getTransactionLocks() {
        return KernelTransactionHandle::activeLocks;
    }

    private static void evaluateDirectDependencies(
            Map> directDependencies,
            Map> handleLocksMap,
            KernelTransactionHandle txHandle,
            QuerySnapshot querySnapshot) {
        List waitingOnLocks = querySnapshot.waitingLocks();
        for (ActiveLock activeLock : waitingOnLocks) {
            for (Map.Entry> handleListEntry :
                    handleLocksMap.entrySet()) {
                KernelTransactionHandle kernelTransactionHandle = handleListEntry.getKey();
                if (!kernelTransactionHandle.equals(txHandle)) {
                    if (isBlocked(activeLock, handleListEntry.getValue())) {
                        Set kernelTransactionHandles =
                                directDependencies.computeIfAbsent(txHandle, handle -> new HashSet<>());
                        kernelTransactionHandles.add(kernelTransactionHandle);
                    }
                }
            }
        }
    }

    private static boolean isBlocked(ActiveLock activeLock, Collection activeLocks) {
        return EXCLUSIVE == activeLock.lockType()
                ? haveAnyLocking(activeLocks, activeLock.resourceType(), activeLock.resourceId())
                : haveExclusiveLocking(activeLocks, activeLock.resourceType(), activeLock.resourceId());
    }

    private static boolean haveAnyLocking(Collection locks, ResourceType resourceType, long resourceId) {
        return locks.stream().anyMatch(lock -> lock.resourceId() == resourceId && lock.resourceType() == resourceType);
    }

    private static boolean haveExclusiveLocking(
            Collection locks, ResourceType resourceType, long resourceId) {
        return locks.stream()
                .anyMatch(lock -> EXCLUSIVE == lock.lockType()
                        && lock.resourceId() == resourceId
                        && lock.resourceType() == resourceType);
    }

    private static String describe(Set allBlockers) {
        if (allBlockers.isEmpty()) {
            return StringUtils.EMPTY;
        }
        StringJoiner stringJoiner = new StringJoiner(", ", "[", "]");
        for (KernelTransactionHandle blocker : allBlockers) {
            stringJoiner.add(blocker.getUserTransactionName());
        }
        return stringJoiner.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy