Skip to content

Commit 5a4c9e2

Browse files
committed
Removed finalize from NetworkSession
It contained logic for closing opened underlying connection and logging info about the leak. Closing of resources in finalize is not reliable and overridden finalize can hurt when many sessions are garbage collected. Commit also introduces a system property `org.neo4j.driver.logSessionLeaks` to enable logging of unclosed/leaked sessions and log stacktrace of where they were instantiated. This is meant to help with session leak investigations. So finalization overhead will be present only when problem is present and is being investigated.
1 parent 45e3623 commit 5a4c9e2

14 files changed

+386
-31
lines changed

driver/src/main/java/org/neo4j/driver/internal/BaseDriver.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ abstract class BaseDriver implements Driver
3232
private final static String DRIVER_LOG_NAME = "Driver";
3333

3434
private final SecurityPlan securityPlan;
35+
protected final SessionFactory sessionFactory;
3536
protected final Logger log;
3637

3738
private AtomicBoolean closed = new AtomicBoolean( false );
3839

39-
BaseDriver( SecurityPlan securityPlan, Logging logging )
40+
BaseDriver( SecurityPlan securityPlan, SessionFactory sessionFactory, Logging logging )
4041
{
4142
this.securityPlan = securityPlan;
43+
this.sessionFactory = sessionFactory;
4244
this.log = logging.getLog( DRIVER_LOG_NAME );
4345
}
4446

driver/src/main/java/org/neo4j/driver/internal/DirectDriver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,18 @@ public DirectDriver(
3636
BoltServerAddress address,
3737
ConnectionPool connections,
3838
SecurityPlan securityPlan,
39+
SessionFactory sessionFactory,
3940
Logging logging )
4041
{
41-
super( securityPlan, logging );
42+
super( securityPlan, sessionFactory, logging );
4243
this.address = address;
4344
this.connections = connections;
4445
}
4546

4647
@Override
4748
protected Session newSessionWithMode( AccessMode mode )
4849
{
49-
return new NetworkSession( connections.acquire( address ) );
50+
return sessionFactory.newInstance( connections.acquire( address ) );
5051
}
5152

5253
@Override

driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,13 @@ public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings r
4949
BoltServerAddress address = BoltServerAddress.from( uri );
5050
SecurityPlan securityPlan = createSecurityPlan( address, config );
5151
ConnectionPool connectionPool = createConnectionPool( authToken, securityPlan, config );
52+
SessionFactory sessionFactory = createSessionFactory( config );
5253

5354
try
5455
{
55-
return createDriver( address, uri.getScheme(), connectionPool, config, routingSettings, securityPlan );
56+
return createDriver( address, uri.getScheme(), connectionPool, config, routingSettings, securityPlan,
57+
sessionFactory
58+
);
5659
}
5760
catch ( Throwable driverError )
5861
{
@@ -70,14 +73,16 @@ public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings r
7073
}
7174

7275
private Driver createDriver( BoltServerAddress address, String scheme, ConnectionPool connectionPool,
73-
Config config, RoutingSettings routingSettings, SecurityPlan securityPlan )
76+
Config config, RoutingSettings routingSettings, SecurityPlan securityPlan,
77+
SessionFactory sessionFactory )
7478
{
7579
switch ( scheme.toLowerCase() )
7680
{
7781
case "bolt":
78-
return createDirectDriver( address, connectionPool, config, securityPlan );
82+
return createDirectDriver( address, connectionPool, config, securityPlan, sessionFactory );
7983
case "bolt+routing":
80-
return createRoutingDriver( address, connectionPool, config, routingSettings, securityPlan );
84+
return createRoutingDriver( address, connectionPool, config, routingSettings, securityPlan,
85+
sessionFactory );
8186
default:
8287
throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) );
8388
}
@@ -89,21 +94,21 @@ private Driver createDriver( BoltServerAddress address, String scheme, Connectio
8994
* <b>This method is package-private only for testing</b>
9095
*/
9196
DirectDriver createDirectDriver( BoltServerAddress address, ConnectionPool connectionPool, Config config,
92-
SecurityPlan securityPlan )
97+
SecurityPlan securityPlan, SessionFactory sessionFactory )
9398
{
94-
return new DirectDriver( address, connectionPool, securityPlan, config.logging() );
99+
return new DirectDriver( address, connectionPool, securityPlan, sessionFactory, config.logging() );
95100
}
96101

97102
/**
98103
* Creates new {@link RoutingDriver}.
99104
* <p>
100105
* <b>This method is package-private only for testing</b>
101106
*/
102-
RoutingDriver createRoutingDriver( BoltServerAddress address, ConnectionPool connectionPool,
103-
Config config, RoutingSettings routingSettings, SecurityPlan securityPlan )
107+
RoutingDriver createRoutingDriver( BoltServerAddress address, ConnectionPool connectionPool, Config config,
108+
RoutingSettings routingSettings, SecurityPlan securityPlan, SessionFactory sessionFactory )
104109
{
105-
return new RoutingDriver( routingSettings, address, connectionPool, securityPlan, Clock.SYSTEM,
106-
config.logging() );
110+
return new RoutingDriver( routingSettings, address, connectionPool, securityPlan, sessionFactory,
111+
Clock.SYSTEM, config.logging() );
107112
}
108113

109114
/**
@@ -122,6 +127,15 @@ ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityP
122127
return new SocketConnectionPool( poolSettings, connector, Clock.SYSTEM, config.logging() );
123128
}
124129

130+
private static SessionFactory createSessionFactory( Config config )
131+
{
132+
if ( LeakLoggingNetworkSessionFactory.leakLoggingEnabled() )
133+
{
134+
return new LeakLoggingNetworkSessionFactory( config.logging() );
135+
}
136+
return new NetworkSessionFactory();
137+
}
138+
125139
private static SecurityPlan createSecurityPlan( BoltServerAddress address, Config config )
126140
{
127141
try
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2002-2016 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal;
20+
21+
import org.neo4j.driver.internal.spi.Connection;
22+
import org.neo4j.driver.v1.Logger;
23+
24+
import static java.lang.System.lineSeparator;
25+
26+
class LeakLoggingNetworkSession extends NetworkSession
27+
{
28+
private final Logger log;
29+
private final String stackTrace;
30+
31+
LeakLoggingNetworkSession( Connection connection, Logger log )
32+
{
33+
super( connection );
34+
this.log = log;
35+
this.stackTrace = captureStackTrace();
36+
}
37+
38+
@Override
39+
protected void finalize() throws Throwable
40+
{
41+
logLeakIfNeeded();
42+
super.finalize();
43+
}
44+
45+
private void logLeakIfNeeded()
46+
{
47+
if ( isOpen() )
48+
{
49+
log.error( "Neo4j Session object leaked, please ensure that your application" +
50+
"calls the `close` method on Sessions before disposing of the objects.\n" +
51+
"Session was create at:\n" + stackTrace, null );
52+
}
53+
}
54+
55+
private static String captureStackTrace()
56+
{
57+
StringBuilder result = new StringBuilder();
58+
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
59+
for ( StackTraceElement element : elements )
60+
{
61+
result.append( "\t" ).append( element ).append( lineSeparator() );
62+
}
63+
return result.toString();
64+
}
65+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2002-2016 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal;
20+
21+
import org.neo4j.driver.internal.spi.Connection;
22+
import org.neo4j.driver.v1.Logger;
23+
import org.neo4j.driver.v1.Logging;
24+
import org.neo4j.driver.v1.Session;
25+
26+
public class LeakLoggingNetworkSessionFactory implements SessionFactory
27+
{
28+
private static final String SYSTEM_PROPERTY_NAME = "org.neo4j.driver.logSessionLeaks";
29+
private static final String LOGGER_NAME = "sessionLeak";
30+
31+
private final Logger logger;
32+
33+
public LeakLoggingNetworkSessionFactory( Logging logging )
34+
{
35+
this.logger = logging.getLog( LOGGER_NAME );
36+
}
37+
38+
public static boolean leakLoggingEnabled()
39+
{
40+
String value = System.getProperty( SYSTEM_PROPERTY_NAME );
41+
return Boolean.parseBoolean( value );
42+
}
43+
44+
@Override
45+
public Session newInstance( Connection connection )
46+
{
47+
return new LeakLoggingNetworkSession( connection, logger );
48+
}
49+
}

driver/src/main/java/org/neo4j/driver/internal/NetworkSession.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void run()
6767
private ExplicitTransaction currentTransaction;
6868
private AtomicBoolean isOpen = new AtomicBoolean( true );
6969

70-
public NetworkSession( Connection connection )
70+
NetworkSession( Connection connection )
7171
{
7272
this.connection = connection;
7373

@@ -126,6 +126,7 @@ public static StatementResult run( Connection connection, Statement statement )
126126
return cursor;
127127
}
128128

129+
@Override
129130
public synchronized void reset()
130131
{
131132
ensureSessionIsOpen();
@@ -255,18 +256,6 @@ private void ensureConnectionIsValidBeforeOpeningTransaction()
255256
ensureConnectionIsOpen();
256257
}
257258

258-
@Override
259-
protected void finalize() throws Throwable
260-
{
261-
if ( isOpen.compareAndSet( true, false ) )
262-
{
263-
logger.error( "Neo4j Session object leaked, please ensure that your application calls the `close` " +
264-
"method on Sessions before disposing of the objects.", null );
265-
connection.close();
266-
}
267-
super.finalize();
268-
}
269-
270259
private void ensureNoUnrecoverableError()
271260
{
272261
if ( connection.hasUnrecoverableErrors() )
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2002-2016 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal;
20+
21+
import org.neo4j.driver.internal.spi.Connection;
22+
import org.neo4j.driver.v1.Session;
23+
24+
public class NetworkSessionFactory implements SessionFactory
25+
{
26+
@Override
27+
public Session newInstance( Connection connection )
28+
{
29+
return new NetworkSession( connection );
30+
}
31+
}

driver/src/main/java/org/neo4j/driver/internal/RoutingDriver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,19 @@ public RoutingDriver(
5151
BoltServerAddress seedAddress,
5252
ConnectionPool connections,
5353
SecurityPlan securityPlan,
54+
SessionFactory sessionFactory,
5455
Clock clock,
5556
Logging logging )
5657
{
57-
super( verifiedSecurityPlan( securityPlan ), logging );
58+
super( verifiedSecurityPlan( securityPlan ), sessionFactory, logging );
5859
this.loadBalancer = new LoadBalancer( settings, clock, log, connections, seedAddress );
5960
}
6061

6162
@Override
6263
protected Session newSessionWithMode( AccessMode mode )
6364
{
6465
Connection connection = acquireConnection( mode );
65-
NetworkSession networkSession = new NetworkSession( connection );
66+
Session networkSession = sessionFactory.newInstance( connection );
6667
return new RoutingNetworkSession( networkSession, mode, connection.boltServerAddress(), loadBalancer );
6768
}
6869

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2002-2016 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal;
20+
21+
import org.neo4j.driver.internal.spi.Connection;
22+
import org.neo4j.driver.v1.Session;
23+
24+
public interface SessionFactory
25+
{
26+
Session newInstance( Connection connection );
27+
}

driver/src/test/java/org/neo4j/driver/internal/DriverFactoryTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ private static class ThrowingDriverFactory extends DriverFactory
121121

122122
@Override
123123
DirectDriver createDirectDriver( BoltServerAddress address, ConnectionPool connectionPool, Config config,
124-
SecurityPlan securityPlan )
124+
SecurityPlan securityPlan, SessionFactory sessionFactory )
125125
{
126126
throw new UnsupportedOperationException( "Can't create direct driver" );
127127
}
128128

129129
@Override
130130
RoutingDriver createRoutingDriver( BoltServerAddress address, ConnectionPool connectionPool, Config config,
131-
RoutingSettings routingSettings, SecurityPlan securityPlan )
131+
RoutingSettings routingSettings, SecurityPlan securityPlan, SessionFactory sessionFactory )
132132
{
133133
throw new UnsupportedOperationException( "Can't create routing driver" );
134134
}

0 commit comments

Comments
 (0)