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

org.neo4j.driver.internal.net.pooling.PooledSocketConnection Maven / Gradle / Ivy

There is a newer version: 5.27.0
Show newest version
/*
 * Copyright (c) 2002-2017 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed 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.neo4j.driver.internal.net.pooling;

import java.util.Map;

import org.neo4j.driver.internal.SessionResourcesHandler;
import org.neo4j.driver.internal.net.BoltServerAddress;
import org.neo4j.driver.internal.spi.Collector;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.spi.PooledConnection;
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.internal.util.Consumer;
import org.neo4j.driver.v1.Value;
import org.neo4j.driver.v1.exceptions.Neo4jException;
import org.neo4j.driver.v1.summary.ServerInfo;

/**
 * The state of a pooledConnection from a pool point of view could be one of the following:
 * Created,
 * Available,
 * Claimed,
 * Closed,
 * Disposed.
 *
 * The state machine looks like:
 *
 *                      session.finalize
 *                       session.close     failed return to pool
 * Created -------> Claimed  ----------> Closed ---------> Disposed
 *                    ^                    |                    ^
 *      pool.acquire  |                    |returned to pool    |
 *                    |                    |                    |
 *                    ---- Available <-----                     |
 *                              |           pool.close          |
 *                              ---------------------------------
 */
public class PooledSocketConnection implements PooledConnection
{
    /** The real connection who will do all the real jobs */
    private final Connection delegate;
    private final Consumer release;

    private boolean unrecoverableErrorsOccurred = false;
    private SessionResourcesHandler resourcesHandler;
    private final Clock clock;
    private long lastUsedTimestamp;

    public PooledSocketConnection( Connection delegate, Consumer release, Clock clock )
    {
        this.delegate = delegate;
        this.release = release;
        this.clock = clock;
        updateLastUsedTimestamp();
    }

    @Override
    public void init( String clientName, Map authToken )
    {
        try
        {
            delegate.init( clientName, authToken );
        }
        catch( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void run( String statement, Map parameters,
            Collector collector )
    {
        try
        {
            delegate.run( statement, parameters, collector );
        }
        catch(RuntimeException e)
        {
            onDelegateException( e );
        }
    }

    @Override
    public void discardAll( Collector collector )
    {
        try
        {
            delegate.discardAll( collector );
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void pullAll( Collector collector )
    {
        try
        {
            delegate.pullAll( collector );
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void reset()
    {
        try
        {
            delegate.reset();
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void ackFailure()
    {
        try
        {
            delegate.ackFailure();
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void sync()
    {
        try
        {
            delegate.sync();
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void flush()
    {
        try
        {
            delegate.flush();
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public void receiveOne()
    {
        try
        {
            delegate.receiveOne();
        }
        catch ( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    /**
     * Make sure only close the connection once on each session to avoid releasing the connection twice, a.k.a.
     * adding back the connection twice into the pool.
     */
    @Override
    public void close()
    {
        updateLastUsedTimestamp();
        resourcesHandler = null;
        release.accept( this );
        // put the full logic of deciding whether to dispose the connection or to put it back to
        // the pool into the release object
    }

    @Override
    public boolean isOpen()
    {
        return delegate.isOpen();
    }

    @Override
    public boolean hasUnrecoverableErrors()
    {
        return unrecoverableErrorsOccurred;
    }

    @Override
    public void resetAsync()
    {
        try
        {
            delegate.resetAsync();
        }
        catch( RuntimeException e )
        {
            onDelegateException( e );
        }
    }

    @Override
    public boolean isAckFailureMuted()
    {
        return delegate.isAckFailureMuted();
    }

    @Override
    public ServerInfo server()
    {
        return delegate.server();
    }

    @Override
    public BoltServerAddress boltServerAddress()
    {
        return delegate.boltServerAddress();
    }

    @Override
    public void dispose()
    {
        delegate.close();
    }

    /**
     * If something goes wrong with the delegate, we want to figure out if this "wrong" is something that means
     * the connection cannot be reused (and thus should be evicted from the pool), or if it's something that we can
     * safely recover from.
     * @param e the exception the delegate threw
     */
    private void onDelegateException( RuntimeException e )
    {
        if ( !isClientOrTransientError( e ) || isProtocolViolationError( e ) )
        {
            unrecoverableErrorsOccurred = true;
        }
        else if ( !isAckFailureMuted() )
        {
            ackFailure();
        }
        if ( resourcesHandler != null )
        {
            resourcesHandler.onConnectionError( !unrecoverableErrorsOccurred );
        }
        throw e;
    }

    @Override
    public void setResourcesHandler( SessionResourcesHandler resourcesHandler )
    {
        this.resourcesHandler = resourcesHandler;
    }

    @Override
    public long lastUsedTimestamp()
    {
        return lastUsedTimestamp;
    }

    private boolean isProtocolViolationError( RuntimeException e )
    {
        if ( e instanceof Neo4jException )
        {
            String errorCode = ((Neo4jException) e).code();
            if ( errorCode != null )
            {
                return errorCode.startsWith( "Neo.ClientError.Request" );
            }
        }
        return false;
    }

    private boolean isClientOrTransientError( RuntimeException e )
    {
        // Eg: DatabaseErrors and unknown (no status code or not neo4j exception) cause session to be discarded
        if ( e instanceof Neo4jException )
        {
            String errorCode = ((Neo4jException) e).code();
            if ( errorCode != null )
            {
                return errorCode.contains( "ClientError" ) || errorCode.contains( "TransientError" );
            }
        }
        return false;
    }

    private void updateLastUsedTimestamp()
    {
        this.lastUsedTimestamp = clock.millis();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy