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

org.neo4j.driver.internal.pool.InternalConnectionPool Maven / Gradle / Ivy

/**
 * Copyright (c) 2002-2016 "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.pool;

import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.neo4j.driver.internal.connector.socket.SocketConnector;
import org.neo4j.driver.internal.spi.Connection;
import org.neo4j.driver.internal.spi.ConnectionPool;
import org.neo4j.driver.internal.spi.Connector;
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.internal.util.Consumer;
import org.neo4j.driver.v1.Config;
import org.neo4j.driver.v1.exceptions.ClientException;

/**
 * A basic connection pool that optimizes for threads being long-lived, acquiring/releasing many connections.
 * It uses a global queue as a fallback pool, but tries to avoid coordination by storing connections in a ThreadLocal.
 * 

* Safety is achieved by tracking thread locals getting garbage collected, returning connections to the global pool * when this happens. *

* If threads are long-lived, this pool will achieve linearly scalable performance with overhead equivalent to a * hash-map lookup per acquire. *

* If threads are short-lived, this pool is not ideal. */ public class InternalConnectionPool implements ConnectionPool { /** * Map of scheme -> connector, this is what we use to establish new connections. */ private final ConcurrentHashMap connectors = new ConcurrentHashMap<>(); /** * Pools, organized by URL. */ private final ConcurrentHashMap> pools = new ConcurrentHashMap<>(); /** * Connections that fail this criteria will be disposed of. */ private final ValidationStrategy connectionValidation; /** * Timeout in milliseconds if there are no available sessions. */ private final long acquireSessionTimeout; private final Clock clock; private final Config config; public InternalConnectionPool( Config config ) { this( loadConnectors(), Clock.SYSTEM, config, Long.getLong( "neo4j.driver.acquireSessionTimeout", 30_000 ) ); } public InternalConnectionPool( Collection conns, Clock clock, Config config, long acquireTimeout ) { this.acquireSessionTimeout = acquireTimeout; this.config = config; this.clock = clock; this.connectionValidation = new PooledConnectionValidator( config.idleTimeBeforeConnectionTest() ); for ( Connector connector : conns ) { for ( String s : connector.supportedSchemes() ) { this.connectors.put( s, connector ); } } } @Override public Connection acquire( URI sessionURI ) { try { Connection conn = pool( sessionURI ).acquire( acquireSessionTimeout, TimeUnit.MILLISECONDS ); if( conn == null ) { throw new ClientException( "Failed to acquire a session with Neo4j " + "as all the connections in the connection pool are already occupied by other sessions. "+ "Please close unused session and retry. " + "Current Pool size: " + config.connectionPoolSize() + ". If your application requires running more sessions concurrently than the current pool " + "size, you should create a driver with a larger connection pool size." ); } return conn; } catch ( InterruptedException e ) { throw new ClientException( "Interrupted while waiting for a connection to Neo4j." ); } } private ThreadCachingPool pool( URI sessionURI ) { ThreadCachingPool pool = pools.get( sessionURI ); if ( pool == null ) { pool = newPool( sessionURI ); if ( pools.putIfAbsent( sessionURI, pool ) != null ) { // We lost a race to create the pool, dispose of the one we created, and recurse pool.close(); return pool( sessionURI ); } } return pool; } private static Collection loadConnectors() { List connectors = new LinkedList<>(); // Hard code socket connector Connector conn = new SocketConnector(); connectors.add( conn ); // Load custom loadConnectors via JSL ServiceLoader load = ServiceLoader.load( Connector.class ); for ( Connector connector : load ) { connectors.add( connector ); } return connectors; } @Override public void close() throws Exception { for ( ThreadCachingPool pool : pools.values() ) { pool.close(); } pools.clear(); } private String connectorSchemes() { return Arrays.toString( connectors.keySet().toArray( new String[connectors.keySet().size()] ) ); } private ThreadCachingPool newPool( final URI uri ) { return new ThreadCachingPool<>( config.connectionPoolSize(), new Allocator() { @Override public PooledConnection allocate( Consumer release ) { Connector connector = connectors.get( uri.getScheme() ); if ( connector == null ) { throw new ClientException( "'" + uri.getScheme() + "' is not a supported transport (in '" + uri + "', available transports are: " + connectorSchemes() + "." ); } Connection conn = connector.connect( uri, config ); return new PooledConnection( conn, release ); } @Override public void onDispose( PooledConnection pooledConnection ) { pooledConnection.dispose(); } @Override public void onAcquire( PooledConnection pooledConnection ) { } }, connectionValidation, clock ); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy