Skip to content

Commit bcd8bab

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 e6c9976 commit bcd8bab

13 files changed

+377
-34
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,14 +32,16 @@ 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 final ReentrantReadWriteLock closedLock = new ReentrantReadWriteLock();
3839
private boolean closed;
3940

40-
BaseDriver( SecurityPlan securityPlan, Logging logging )
41+
BaseDriver( SecurityPlan securityPlan, SessionFactory sessionFactory, Logging logging )
4142
{
4243
this.securityPlan = securityPlan;
44+
this.sessionFactory = sessionFactory;
4345
this.log = logging.getLog( DRIVER_LOG_NAME );
4446
}
4547

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
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/main/java/org/neo4j/driver/v1/GraphDatabase.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@
2424

2525
import org.neo4j.driver.internal.ConnectionSettings;
2626
import org.neo4j.driver.internal.DirectDriver;
27-
import org.neo4j.driver.internal.NetworkSession;
27+
import org.neo4j.driver.internal.LeakLoggingNetworkSessionFactory;
28+
import org.neo4j.driver.internal.NetworkSessionFactory;
2829
import org.neo4j.driver.internal.RoutingDriver;
30+
import org.neo4j.driver.internal.SessionFactory;
2931
import org.neo4j.driver.internal.net.BoltServerAddress;
3032
import org.neo4j.driver.internal.net.pooling.PoolSettings;
3133
import org.neo4j.driver.internal.net.pooling.SocketConnectionPool;
3234
import org.neo4j.driver.internal.security.SecurityPlan;
33-
import org.neo4j.driver.internal.spi.Connection;
3435
import org.neo4j.driver.internal.spi.ConnectionPool;
3536
import org.neo4j.driver.internal.util.Clock;
3637
import org.neo4j.driver.v1.exceptions.ClientException;
37-
import org.neo4j.driver.v1.util.Function;
3838

3939
import static java.lang.String.format;
4040
import static org.neo4j.driver.internal.security.SecurityPlan.insecure;
@@ -47,17 +47,6 @@
4747
*/
4848
public class GraphDatabase
4949
{
50-
51-
private static final Function<Connection,Session>
52-
SESSION_PROVIDER = new Function<Connection,Session>()
53-
{
54-
@Override
55-
public Session apply( Connection connection )
56-
{
57-
return new NetworkSession( connection );
58-
}
59-
};
60-
6150
/**
6251
* Return a driver for a Neo4j instance with the default configuration settings
6352
*
@@ -182,16 +171,20 @@ public static Driver driver( URI uri, AuthToken authToken, Config config )
182171
// And finally, construct the driver proper
183172
ConnectionPool connectionPool =
184173
new SocketConnectionPool( connectionSettings, securityPlan, poolSettings, config.logging() );
174+
175+
SessionFactory sessionFactory = createSessionFactory( config );
176+
185177
switch ( scheme.toLowerCase() )
186178
{
187179
case "bolt":
188-
return new DirectDriver( address, connectionPool, securityPlan, config.logging() );
180+
return new DirectDriver( address, connectionPool, securityPlan, sessionFactory, config.logging() );
189181
case "bolt+routing":
190182
return new RoutingDriver(
191183
config.routingSettings(),
192184
address,
193185
connectionPool,
194186
securityPlan,
187+
sessionFactory,
195188
Clock.SYSTEM,
196189
config.logging() );
197190
default:
@@ -244,4 +237,13 @@ private static SecurityPlan createSecurityPlan( BoltServerAddress address, Confi
244237
return insecure();
245238
}
246239
}
240+
241+
private static SessionFactory createSessionFactory( Config config )
242+
{
243+
if ( LeakLoggingNetworkSessionFactory.leakLoggingEnabled() )
244+
{
245+
return new LeakLoggingNetworkSessionFactory( config.logging() );
246+
}
247+
return new NetworkSessionFactory();
248+
}
247249
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.junit.Test;
22+
23+
import org.neo4j.driver.internal.spi.Connection;
24+
import org.neo4j.driver.v1.Logging;
25+
import org.neo4j.driver.v1.Session;
26+
27+
import static org.hamcrest.Matchers.instanceOf;
28+
import static org.junit.Assert.assertThat;
29+
import static org.mockito.Mockito.mock;
30+
31+
public class LeakLoggingNetworkSessionFactoryTest
32+
{
33+
@Test
34+
public void createsLeakLoggingNetworkSessions()
35+
{
36+
SessionFactory factory = new LeakLoggingNetworkSessionFactory( mock( Logging.class ) );
37+
38+
Session session = factory.newInstance( mock( Connection.class ) );
39+
40+
assertThat( session, instanceOf( LeakLoggingNetworkSession.class ) );
41+
}
42+
}

0 commit comments

Comments
 (0)