Skip to content

Commit a9e2049

Browse files
authored
Merge pull request #631 from zhenlineo/4.0-upgrade-netty
Update netty version to the latest.
2 parents 4856b72 + e5a6f48 commit a9e2049

16 files changed

+399
-355
lines changed

driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package org.neo4j.driver.internal.async;
2020

2121
import io.netty.channel.Channel;
22-
import io.netty.channel.pool.ChannelPool;
2322

2423
import java.util.concurrent.CompletableFuture;
2524
import java.util.concurrent.CompletionStage;
@@ -28,6 +27,7 @@
2827
import org.neo4j.driver.internal.BoltServerAddress;
2928
import org.neo4j.driver.internal.async.connection.ChannelAttributes;
3029
import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher;
30+
import org.neo4j.driver.internal.async.pool.ExtendedChannelPool;
3131
import org.neo4j.driver.internal.handlers.ChannelReleasingResetResponseHandler;
3232
import org.neo4j.driver.internal.handlers.ResetResponseHandler;
3333
import org.neo4j.driver.internal.messaging.BoltProtocol;
@@ -57,15 +57,15 @@ public class NetworkConnection implements Connection
5757
private final BoltServerAddress serverAddress;
5858
private final ServerVersion serverVersion;
5959
private final BoltProtocol protocol;
60-
private final ChannelPool channelPool;
60+
private final ExtendedChannelPool channelPool;
6161
private final CompletableFuture<Void> releaseFuture;
6262
private final Clock clock;
6363

6464
private final AtomicReference<Status> status = new AtomicReference<>( Status.OPEN );
6565
private final MetricsListener metricsListener;
6666
private final ListenerEvent inUseEvent;
6767

68-
public NetworkConnection( Channel channel, ChannelPool channelPool, Clock clock, MetricsListener metricsListener )
68+
public NetworkConnection( Channel channel, ExtendedChannelPool channelPool, Clock clock, MetricsListener metricsListener )
6969
{
7070
this.channel = channel;
7171
this.messageDispatcher = ChannelAttributes.messageDispatcher( channel );

driver/src/main/java/org/neo4j/driver/internal/async/pool/ConnectionPoolImpl.java

Lines changed: 91 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@
2121
import io.netty.bootstrap.Bootstrap;
2222
import io.netty.channel.Channel;
2323
import io.netty.channel.EventLoopGroup;
24-
import io.netty.channel.pool.ChannelPool;
25-
import io.netty.util.concurrent.Future;
2624

27-
import java.util.Map;
2825
import java.util.Set;
26+
import java.util.concurrent.CompletableFuture;
2927
import java.util.concurrent.CompletionException;
3028
import java.util.concurrent.CompletionStage;
3129
import java.util.concurrent.ConcurrentHashMap;
@@ -48,6 +46,8 @@
4846
import org.neo4j.driver.internal.util.Futures;
4947

5048
import static java.lang.String.format;
49+
import static org.neo4j.driver.internal.util.Futures.combineErrors;
50+
import static org.neo4j.driver.internal.util.Futures.completeWithNullIfNoError;
5151

5252
public class ConnectionPoolImpl implements ConnectionPool
5353
{
@@ -62,6 +62,7 @@ public class ConnectionPoolImpl implements ConnectionPool
6262

6363
private final ConcurrentMap<BoltServerAddress,ExtendedChannelPool> pools = new ConcurrentHashMap<>();
6464
private final AtomicBoolean closed = new AtomicBoolean();
65+
private final CompletableFuture<Void> closeFuture = new CompletableFuture<>();
6566
private final ConnectionFactory connectionFactory;
6667

6768
public ConnectionPoolImpl( ChannelConnector connector, Bootstrap bootstrap, PoolSettings settings, MetricsListener metricsListener, Logging logging,
@@ -95,9 +96,9 @@ public CompletionStage<Connection> acquire( BoltServerAddress address )
9596

9697
ListenerEvent acquireEvent = metricsListener.createListenerEvent();
9798
metricsListener.beforeAcquiringOrCreating( pool.id(), acquireEvent );
98-
Future<Channel> connectionFuture = pool.acquire();
99+
CompletionStage<Channel> channelFuture = pool.acquire();
99100

100-
return Futures.asCompletionStage( connectionFuture ).handle( ( channel, error ) ->
101+
return channelFuture.handle( ( channel, error ) ->
101102
{
102103
try
103104
{
@@ -131,8 +132,8 @@ public void retainAll( Set<BoltServerAddress> addressesToRetain )
131132
if ( pool != null )
132133
{
133134
log.info( "Closing connection pool towards %s, it has no active connections " +
134-
"and is not in the routing table", address );
135-
closePool( pool );
135+
"and is not in the routing table registry.", address );
136+
closePoolInBackground( address, pool );
136137
}
137138
}
138139
}
@@ -156,37 +157,24 @@ public CompletionStage<Void> close()
156157
{
157158
if ( closed.compareAndSet( false, true ) )
158159
{
159-
try
160-
{
161-
nettyChannelTracker.prepareToCloseChannels();
162-
for ( Map.Entry<BoltServerAddress,ExtendedChannelPool> entry : pools.entrySet() )
163-
{
164-
BoltServerAddress address = entry.getKey();
165-
ExtendedChannelPool pool = entry.getValue();
166-
log.info( "Closing connection pool towards %s", address );
167-
closePool( pool );
168-
}
160+
nettyChannelTracker.prepareToCloseChannels();
161+
CompletableFuture<Void> allPoolClosedFuture = closeAllPools();
169162

163+
// We can only shutdown event loop group when all netty pools are fully closed,
164+
// otherwise the netty pools might missing threads (from event loop group) to execute clean ups.
165+
allPoolClosedFuture.whenComplete( ( ignored, pollCloseError ) -> {
170166
pools.clear();
171-
}
172-
finally
173-
{
174-
175-
if (ownsEventLoopGroup) {
176-
// This is an attempt to speed up the shut down procedure of the driver
177-
// Feel free return this back to shutdownGracefully() method with default values
178-
// if this proves troublesome!!!
179-
eventLoopGroup().shutdownGracefully(200, 15_000, TimeUnit.MILLISECONDS);
167+
if ( !ownsEventLoopGroup )
168+
{
169+
completeWithNullIfNoError( closeFuture, pollCloseError );
180170
}
181-
}
182-
}
183-
if (!ownsEventLoopGroup)
184-
{
185-
return Futures.completedWithNull();
171+
else
172+
{
173+
shutdownEventLoopGroup( pollCloseError );
174+
}
175+
} );
186176
}
187-
188-
return Futures.asCompletionStage( eventLoopGroup().terminationFuture() )
189-
.thenApply( ignore -> null );
177+
return closeFuture;
190178
}
191179

192180
@Override
@@ -195,31 +183,10 @@ public boolean isOpen( BoltServerAddress address )
195183
return pools.containsKey( address );
196184
}
197185

198-
private ExtendedChannelPool getOrCreatePool( BoltServerAddress address )
199-
{
200-
return pools.computeIfAbsent( address, this::newPool );
201-
}
202-
203-
private void closePool( ExtendedChannelPool pool )
204-
{
205-
pool.close();
206-
// after the connection pool is removed/close, I can remove its metrics.
207-
metricsListener.removePoolMetrics( pool.id() );
208-
}
209-
210-
ExtendedChannelPool newPool( BoltServerAddress address )
211-
{
212-
NettyChannelPool pool =
213-
new NettyChannelPool( address, connector, bootstrap, nettyChannelTracker, channelHealthChecker, settings.connectionAcquisitionTimeout(),
214-
settings.maxConnectionPoolSize() );
215-
// before the connection pool is added I can add the metrics for the pool.
216-
metricsListener.putPoolMetrics( pool.id(), address, this );
217-
return pool;
218-
}
219-
220-
private EventLoopGroup eventLoopGroup()
186+
@Override
187+
public String toString()
221188
{
222-
return bootstrap.config().group();
189+
return "ConnectionPoolImpl{" + "pools=" + pools + '}';
223190
}
224191

225192
private void processAcquisitionError( ExtendedChannelPool pool, BoltServerAddress serverAddress, Throwable error )
@@ -259,26 +226,84 @@ private void assertNotClosed()
259226
}
260227
}
261228

262-
private void assertNotClosed( BoltServerAddress address, Channel channel, ChannelPool pool )
229+
private void assertNotClosed( BoltServerAddress address, Channel channel, ExtendedChannelPool pool )
263230
{
264231
if ( closed.get() )
265232
{
266233
pool.release( channel );
267-
pool.close();
234+
closePoolInBackground( address, pool );
268235
pools.remove( address );
269236
assertNotClosed();
270237
}
271238
}
272239

273-
@Override
274-
public String toString()
275-
{
276-
return "ConnectionPoolImpl{" + "pools=" + pools + '}';
277-
}
278-
279240
// for testing only
280241
ExtendedChannelPool getPool( BoltServerAddress address )
281242
{
282243
return pools.get( address );
283244
}
245+
246+
ExtendedChannelPool newPool( BoltServerAddress address )
247+
{
248+
return new NettyChannelPool( address, connector, bootstrap, nettyChannelTracker, channelHealthChecker, settings.connectionAcquisitionTimeout(),
249+
settings.maxConnectionPoolSize() );
250+
}
251+
252+
private ExtendedChannelPool getOrCreatePool( BoltServerAddress address )
253+
{
254+
return pools.computeIfAbsent( address, ignored -> {
255+
ExtendedChannelPool pool = newPool( address );
256+
// before the connection pool is added I can add the metrics for the pool.
257+
metricsListener.putPoolMetrics( pool.id(), address, this );
258+
return pool;
259+
} );
260+
}
261+
262+
private CompletionStage<Void> closePool( ExtendedChannelPool pool )
263+
{
264+
return pool.close().whenComplete( ( ignored, error ) ->
265+
// after the connection pool is removed/close, I can remove its metrics.
266+
metricsListener.removePoolMetrics( pool.id() ) );
267+
}
268+
269+
private void closePoolInBackground( BoltServerAddress address, ExtendedChannelPool pool )
270+
{
271+
// Close in the background
272+
closePool( pool ).whenComplete( ( ignored, error ) -> {
273+
if ( error != null )
274+
{
275+
log.warn( format( "An error occurred while closing connection pool towards %s.", address ), error );
276+
}
277+
} );
278+
}
279+
280+
private EventLoopGroup eventLoopGroup()
281+
{
282+
return bootstrap.config().group();
283+
}
284+
285+
private void shutdownEventLoopGroup( Throwable pollCloseError )
286+
{
287+
// This is an attempt to speed up the shut down procedure of the driver
288+
// This timeout is needed for `closePoolInBackground` to finish background job, especially for races between `acquire` and `close`.
289+
eventLoopGroup().shutdownGracefully( 200, 15_000, TimeUnit.MILLISECONDS );
290+
291+
Futures.asCompletionStage( eventLoopGroup().terminationFuture() )
292+
.whenComplete( ( ignore, eventLoopGroupTerminationError ) -> {
293+
CompletionException combinedErrors = combineErrors( pollCloseError, eventLoopGroupTerminationError );
294+
completeWithNullIfNoError( closeFuture, combinedErrors );
295+
} );
296+
}
297+
298+
private CompletableFuture<Void> closeAllPools()
299+
{
300+
return CompletableFuture.allOf(
301+
pools.entrySet().stream().map( entry -> {
302+
BoltServerAddress address = entry.getKey();
303+
ExtendedChannelPool pool = entry.getValue();
304+
log.info( "Closing connection pool towards %s", address );
305+
// Wait for all pools to be closed.
306+
return closePool( pool ).toCompletableFuture();
307+
} ).toArray( CompletableFuture[]::new ) );
308+
}
284309
}

driver/src/main/java/org/neo4j/driver/internal/async/pool/ExtendedChannelPool.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@
1818
*/
1919
package org.neo4j.driver.internal.async.pool;
2020

21-
import io.netty.channel.pool.ChannelPool;
21+
import io.netty.channel.Channel;
2222

23-
public interface ExtendedChannelPool extends ChannelPool
23+
import java.util.concurrent.CompletionStage;
24+
25+
public interface ExtendedChannelPool
2426
{
27+
CompletionStage<Channel> acquire();
28+
29+
CompletionStage<Void> release( Channel channel );
30+
2531
boolean isClosed();
2632

2733
String id();
34+
35+
CompletionStage<Void> close();
2836
}

0 commit comments

Comments
 (0)