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

org.neo4j.fabric.transaction.TransactionManager Maven / Gradle / Ivy

There is a newer version: 5.25.1
Show newest version
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://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.fabric.transaction;

import java.time.Clock;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.neo4j.configuration.Config;
import org.neo4j.configuration.GraphDatabaseSettings;
import org.neo4j.fabric.bookmark.TransactionBookmarkManager;
import org.neo4j.fabric.config.FabricConfig;
import org.neo4j.fabric.eval.CatalogManager;
import org.neo4j.fabric.executor.FabricLocalExecutor;
import org.neo4j.fabric.executor.FabricRemoteExecutor;
import org.neo4j.graphdb.DatabaseShutdownException;
import org.neo4j.internal.kernel.api.security.AbstractSecurityLog;
import org.neo4j.internal.kernel.api.security.LoginContext;
import org.neo4j.kernel.api.exceptions.Status;
import org.neo4j.kernel.availability.AvailabilityGuard;
import org.neo4j.kernel.database.DatabaseReference;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.lifecycle.LifecycleAdapter;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.locks.LockSupport.parkNanos;

public class TransactionManager extends LifecycleAdapter
{
    private final FabricRemoteExecutor remoteExecutor;
    private final FabricLocalExecutor localExecutor;
    private final ErrorReporter errorReporter;
    private final FabricConfig fabricConfig;
    private final FabricTransactionMonitor transactionMonitor;
    private final AbstractSecurityLog securityLog;
    private final Clock clock;

    private final Set openTransactions = ConcurrentHashMap.newKeySet();
    private final long awaitActiveTransactionDeadlineMillis;
    private final AvailabilityGuard availabilityGuard;
    private final CatalogManager catalogManager;

    public TransactionManager( FabricRemoteExecutor remoteExecutor,
            FabricLocalExecutor localExecutor,
            CatalogManager CatalogManager, FabricConfig fabricConfig, FabricTransactionMonitor transactionMonitor, AbstractSecurityLog securityLog,
            Clock clock, Config config, AvailabilityGuard availabilityGuard, ErrorReporter errorReporter )
    {
        this.remoteExecutor = remoteExecutor;
        this.localExecutor = localExecutor;
        this.catalogManager = CatalogManager;
        this.errorReporter = errorReporter;
        this.fabricConfig = fabricConfig;
        this.transactionMonitor = transactionMonitor;
        this.securityLog = securityLog;
        this.clock = clock;
        this.awaitActiveTransactionDeadlineMillis = config.get( GraphDatabaseSettings.shutdown_transaction_end_timeout ).toMillis();
        this.availabilityGuard = availabilityGuard;
    }

    public FabricTransaction begin( FabricTransactionInfo transactionInfo, TransactionBookmarkManager transactionBookmarkManager )
    {
        if ( availabilityGuard.isShutdown() )
        {
            throw new DatabaseShutdownException();
        }

        var sessionDb = transactionInfo.getSessionDatabaseReference();
        var databaseNameToAuthorizeFor = sessionDb instanceof DatabaseReference.Internal ?
                                         ((DatabaseReference.Internal) sessionDb).databaseId().name() :
                                         sessionDb.alias().name();

        transactionInfo.getLoginContext()
                       .authorize( LoginContext.IdLookup.EMPTY, databaseNameToAuthorizeFor, securityLog );

        FabricTransactionImpl fabricTransaction = new FabricTransactionImpl( transactionInfo,
                                                                             transactionBookmarkManager,
                                                                             remoteExecutor,
                                                                             localExecutor,
                                                                             errorReporter,
                                                                             this,
                                                                             fabricConfig,
                                                                             catalogManager.currentCatalog() );

        openTransactions.add( fabricTransaction );
        transactionMonitor.startMonitoringTransaction( fabricTransaction, transactionInfo );
        return fabricTransaction;
    }

    @Override
    public void stop()
    {
        // On a fabric level we will deal with transactions that a cross DBMS.
        // Any db specific transaction will be handled on a database level with own set of rules, checks etc
        var nonLocalTransaction = collectNonLocalTransactions();
        if ( nonLocalTransaction.isEmpty() )
        {
            return;
        }
        awaitTransactionsClosedWithinTimeout( nonLocalTransaction );
        nonLocalTransaction.forEach( tx -> tx.markForTermination( Status.Transaction.Terminated ) );
    }

    private Collection collectNonLocalTransactions()
    {
        return openTransactions.stream().filter( tx -> !tx.isLocal() ).collect( Collectors.toList() );
    }

    private void awaitTransactionsClosedWithinTimeout( Collection nonLocalTransaction )
    {
        long deadline = clock.millis() + awaitActiveTransactionDeadlineMillis;
        while ( hasOpenTransactions( nonLocalTransaction ) && clock.millis() < deadline )
        {
            parkNanos( MILLISECONDS.toNanos( 10 ) );
        }
    }

    private static boolean hasOpenTransactions( Collection nonLocalTransaction )
    {
        for ( FabricTransactionImpl fabricTransaction : nonLocalTransaction )
        {
            if ( fabricTransaction.isOpen() )
            {
                return true;
            }
        }
        return false;
    }

    void removeTransaction( FabricTransactionImpl transaction )
    {
        openTransactions.remove( transaction );
        transactionMonitor.stopMonitoringTransaction( transaction );
    }

    public Set getOpenTransactions()
    {
        return Collections.unmodifiableSet( openTransactions );
    }

    public Optional findTransactionContaining( InternalTransaction transaction )
    {
        return openTransactions.stream()
                               .filter( tx -> tx.getInternalTransactions().stream()
                                                .anyMatch( itx -> itx.kernelTransaction() == transaction.kernelTransaction() ) )
                               .map( FabricTransaction.class::cast )
                               .findFirst();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy