Skip to content

Commit 45e3623

Browse files
author
Zhen Li
authored
Merge pull request #289 from lutovich/1.1-causal-cluster-it
Basic infrastructure for causal cluster integration tests
2 parents e4435e7 + a234ca0 commit 45e3623

File tree

12 files changed

+1228
-8
lines changed

12 files changed

+1228
-8
lines changed

driver/src/main/java/org/neo4j/driver/internal/util/Iterables.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,21 @@ public static <T> List<T> asList( Iterable<T> it )
5252
return list;
5353
}
5454

55+
public static <T> T single( Iterable<T> it )
56+
{
57+
Iterator<T> iterator = it.iterator();
58+
if ( !iterator.hasNext() )
59+
{
60+
throw new IllegalArgumentException( "Given iterable is empty" );
61+
}
62+
T result = iterator.next();
63+
if ( iterator.hasNext() )
64+
{
65+
throw new IllegalArgumentException( "Given iterable contains more than one element: " + it );
66+
}
67+
return result;
68+
}
69+
5570
public static Map<String, String> map( String ... alternatingKeyValue )
5671
{
5772
Map<String, String> out = new HashMap<>();
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
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.v1.integration;
20+
21+
import org.junit.Rule;
22+
import org.junit.Test;
23+
24+
import java.net.URI;
25+
import java.util.concurrent.TimeoutException;
26+
27+
import org.neo4j.driver.internal.logging.DevNullLogger;
28+
import org.neo4j.driver.v1.AccessMode;
29+
import org.neo4j.driver.v1.Config;
30+
import org.neo4j.driver.v1.Driver;
31+
import org.neo4j.driver.v1.GraphDatabase;
32+
import org.neo4j.driver.v1.Logger;
33+
import org.neo4j.driver.v1.Logging;
34+
import org.neo4j.driver.v1.Record;
35+
import org.neo4j.driver.v1.Session;
36+
import org.neo4j.driver.v1.Transaction;
37+
import org.neo4j.driver.v1.Values;
38+
import org.neo4j.driver.v1.exceptions.ServiceUnavailableException;
39+
import org.neo4j.driver.v1.exceptions.SessionExpiredException;
40+
import org.neo4j.driver.v1.util.Function;
41+
import org.neo4j.driver.v1.util.cc.Cluster;
42+
import org.neo4j.driver.v1.util.cc.ClusterMember;
43+
import org.neo4j.driver.v1.util.cc.ClusterRule;
44+
45+
import static org.junit.Assert.assertEquals;
46+
import static org.junit.Assert.assertNotNull;
47+
import static org.junit.Assert.fail;
48+
49+
public class CausalClusteringIT
50+
{
51+
private static final long DEFAULT_TIMEOUT_MS = 15_000;
52+
53+
@Rule
54+
public final ClusterRule clusterRule = new ClusterRule();
55+
56+
@Test
57+
public void shouldExecuteReadAndWritesWhenDriverSuppliedWithAddressOfLeader() throws Exception
58+
{
59+
Cluster cluster = clusterRule.getCluster();
60+
61+
int count = executeWriteAndReadThroughBolt( cluster.leader() );
62+
63+
assertEquals( 1, count );
64+
}
65+
66+
@Test
67+
public void shouldExecuteReadAndWritesWhenDriverSuppliedWithAddressOfFollower() throws Exception
68+
{
69+
Cluster cluster = clusterRule.getCluster();
70+
71+
int count = executeWriteAndReadThroughBolt( cluster.anyFollower() );
72+
73+
assertEquals( 1, count );
74+
}
75+
76+
@Test
77+
public void sessionCreationShouldFailIfCallingDiscoveryProcedureOnEdgeServer() throws Exception
78+
{
79+
Cluster cluster = clusterRule.getCluster();
80+
81+
ClusterMember readReplica = cluster.anyReadReplica();
82+
try
83+
{
84+
createDriver( readReplica.getRoutingUri() );
85+
fail( "Should have thrown an exception using a read replica address for routing" );
86+
}
87+
catch ( ServiceUnavailableException ex )
88+
{
89+
assertEquals( "Could not perform discovery. No routing servers available.", ex.getMessage() );
90+
}
91+
}
92+
93+
// Ensure that Bookmarks work with single instances using a driver created using a bolt[not+routing] URI.
94+
@Test
95+
public void bookmarksShouldWorkWithDriverPinnedToSingleServer() throws Exception
96+
{
97+
Cluster cluster = clusterRule.getCluster();
98+
ClusterMember leader = cluster.leader();
99+
100+
try ( Driver driver = createDriver( leader.getBoltUri() ) )
101+
{
102+
String bookmark = inExpirableSession( driver, createSession(), new Function<Session,String>()
103+
{
104+
@Override
105+
public String apply( Session session )
106+
{
107+
try ( Transaction tx = session.beginTransaction() )
108+
{
109+
tx.run( "CREATE (p:Person {name: {name} })", Values.parameters( "name", "Alistair" ) );
110+
tx.success();
111+
}
112+
113+
return session.lastBookmark();
114+
}
115+
} );
116+
117+
assertNotNull( bookmark );
118+
119+
try ( Session session = driver.session();
120+
Transaction tx = session.beginTransaction( bookmark ) )
121+
{
122+
Record record = tx.run( "MATCH (n:Person) RETURN COUNT(*) AS count" ).next();
123+
assertEquals( 1, record.get( "count" ).asInt() );
124+
tx.success();
125+
}
126+
}
127+
}
128+
129+
@Test
130+
public void shouldUseBookmarkFromAReadSessionInAWriteSession() throws Exception
131+
{
132+
Cluster cluster = clusterRule.getCluster();
133+
ClusterMember leader = cluster.leader();
134+
135+
try ( Driver driver = createDriver( leader.getBoltUri() ) )
136+
{
137+
inExpirableSession( driver, createWritableSession(), new Function<Session,Void>()
138+
{
139+
@Override
140+
public Void apply( Session session )
141+
{
142+
session.run( "CREATE (p:Person {name: {name} })", Values.parameters( "name", "Jim" ) );
143+
return null;
144+
}
145+
} );
146+
147+
final String bookmark;
148+
try ( Session session = driver.session( AccessMode.READ ) )
149+
{
150+
try ( Transaction tx = session.beginTransaction() )
151+
{
152+
tx.run( "MATCH (n:Person) RETURN COUNT(*) AS count" ).next();
153+
tx.success();
154+
}
155+
156+
bookmark = session.lastBookmark();
157+
}
158+
159+
assertNotNull( bookmark );
160+
161+
inExpirableSession( driver, createWritableSession(), new Function<Session,Void>()
162+
{
163+
@Override
164+
public Void apply( Session session )
165+
{
166+
try ( Transaction tx = session.beginTransaction( bookmark ) )
167+
{
168+
tx.run( "CREATE (p:Person {name: {name} })", Values.parameters( "name", "Alistair" ) );
169+
tx.success();
170+
}
171+
172+
return null;
173+
}
174+
} );
175+
176+
try ( Session session = driver.session() )
177+
{
178+
Record record = session.run( "MATCH (n:Person) RETURN COUNT(*) AS count" ).next();
179+
assertEquals( 2, record.get( "count" ).asInt() );
180+
}
181+
}
182+
}
183+
184+
private int executeWriteAndReadThroughBolt( ClusterMember member ) throws TimeoutException, InterruptedException
185+
{
186+
try ( Driver driver = createDriver( member.getRoutingUri() ) )
187+
{
188+
return inExpirableSession( driver, createWritableSession(), executeWriteAndRead() );
189+
}
190+
}
191+
192+
private Function<Driver,Session> createSession()
193+
{
194+
return new Function<Driver,Session>()
195+
{
196+
@Override
197+
public Session apply( Driver driver )
198+
{
199+
return driver.session();
200+
}
201+
};
202+
}
203+
204+
private Function<Driver,Session> createWritableSession()
205+
{
206+
return new Function<Driver,Session>()
207+
{
208+
@Override
209+
public Session apply( Driver driver )
210+
{
211+
return driver.session( AccessMode.WRITE );
212+
}
213+
};
214+
}
215+
216+
private Function<Session,Integer> executeWriteAndRead()
217+
{
218+
return new Function<Session,Integer>()
219+
{
220+
@Override
221+
public Integer apply( Session session )
222+
{
223+
session.run( "MERGE (n:Person {name: 'Jim'})" ).consume();
224+
Record record = session.run( "MATCH (n:Person) RETURN COUNT(*) AS count" ).next();
225+
return record.get( "count" ).asInt();
226+
}
227+
};
228+
}
229+
230+
private <T> T inExpirableSession( Driver driver, Function<Driver,Session> acquirer, Function<Session,T> op )
231+
throws TimeoutException, InterruptedException
232+
{
233+
long endTime = System.currentTimeMillis() + DEFAULT_TIMEOUT_MS;
234+
235+
do
236+
{
237+
try ( Session session = acquirer.apply( driver ) )
238+
{
239+
return op.apply( session );
240+
}
241+
catch ( SessionExpiredException e )
242+
{
243+
// role might have changed; try again;
244+
}
245+
}
246+
while ( System.currentTimeMillis() < endTime );
247+
248+
throw new TimeoutException( "Transaction did not succeed in time" );
249+
}
250+
251+
private Driver createDriver( URI boltUri )
252+
{
253+
Logging devNullLogging = new Logging()
254+
{
255+
@Override
256+
public Logger getLog( String name )
257+
{
258+
return DevNullLogger.DEV_NULL_LOGGER;
259+
}
260+
};
261+
262+
Config config = Config.build()
263+
.withLogging( devNullLogging )
264+
.toConfig();
265+
266+
return GraphDatabase.driver( boltUri, clusterRule.getDefaultAuthToken(), config );
267+
}
268+
}

driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,7 @@ public void restartNeo4j(Neo4jSettings neo4jSettings) throws IOException
159159
private int runCommand( String... cmd ) throws IOException
160160
{
161161
ProcessBuilder pb = new ProcessBuilder().inheritIO();
162-
Map<String,String> env = System.getenv();
163-
pb.environment().put( "JAVA_HOME",
164-
// This driver is built to work with multiple java versions.
165-
// Neo4j, however, works with a specific version of Java. This allows
166-
// specifying which Java version to use for Neo4j separately from which
167-
// version to use for the driver tests.
168-
env.containsKey( "NEO4J_JAVA" ) ? env.get( "NEO4J_JAVA" ) :
169-
System.getProperties().getProperty( "java.home" ) );
162+
ProcessEnvConfigurator.configure( pb );
170163
if( NEORUN_START_ARGS != null )
171164
{
172165
// overwrite the env var in the sub process if the system property is specified
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.v1.util;
20+
21+
import java.util.Map;
22+
23+
public final class ProcessEnvConfigurator
24+
{
25+
/**
26+
* Name of environment variable used by the Neo4j database.
27+
*/
28+
private static final String JAVA_HOME = "JAVA_HOME";
29+
/**
30+
* Name of environment variable to be used for the Neo4j database, defined by the build system.
31+
*/
32+
private static final String NEO4J_JAVA = "NEO4J_JAVA";
33+
34+
private ProcessEnvConfigurator()
35+
{
36+
}
37+
38+
public static void configure( ProcessBuilder processBuilder )
39+
{
40+
processBuilder.environment().put( JAVA_HOME, determineJavaHome() );
41+
}
42+
43+
/**
44+
* This driver is built to work with multiple java versions. Neo4j, however, works with a specific version of
45+
* Java. This allows specifying which Java version to use for Neo4j separately from which version to use for
46+
* the driver tests.
47+
* <p>
48+
* This method determines which java home to use based on present environment variables.
49+
*
50+
* @return path to the java home.
51+
*/
52+
private static String determineJavaHome()
53+
{
54+
Map<String,String> environment = System.getenv();
55+
56+
String definedJava = environment.get( NEO4J_JAVA );
57+
if ( definedJava != null )
58+
{
59+
return definedJava;
60+
}
61+
62+
return System.getProperties().getProperty( "java.home" );
63+
}
64+
}

0 commit comments

Comments
 (0)