23
23
import java .util .Collections ;
24
24
import java .util .HashSet ;
25
25
import java .util .List ;
26
- import java .util .Objects ;
27
26
import java .util .Set ;
28
27
import java .util .concurrent .ThreadLocalRandom ;
29
28
import java .util .concurrent .TimeUnit ;
38
37
import org .neo4j .driver .v1 .Session ;
39
38
import org .neo4j .driver .v1 .StatementResult ;
40
39
40
+ import static org .neo4j .driver .internal .util .Iterables .single ;
41
41
import static org .neo4j .driver .v1 .Config .TrustStrategy .trustAllCertificates ;
42
42
43
43
public class Cluster
44
44
{
45
45
private static final String ADMIN_USER = "neo4j" ;
46
+ private static final int STARTUP_TIMEOUT_SECONDS = 60 ;
46
47
47
48
private final Path path ;
48
49
private final String password ;
@@ -53,15 +54,16 @@ public Cluster( Path path, String password )
53
54
this ( path , password , Collections .<ClusterMember >emptySet () );
54
55
}
55
56
56
- public Cluster ( Path path , String password , Set <ClusterMember > members )
57
+ private Cluster ( Path path , String password , Set <ClusterMember > members )
57
58
{
58
- this .path = Objects . requireNonNull ( path ) ;
59
+ this .path = path ;
59
60
this .password = password ;
60
- this .members = waitForMembers ( password , members ) ;
61
+ this .members = members ;
61
62
}
62
63
63
- Cluster withMembers ( Set <ClusterMember > newMembers )
64
+ Cluster withMembers ( Set <ClusterMember > newMembers ) throws ClusterUnavailableException
64
65
{
66
+ waitForMembers ( newMembers , password );
65
67
return new Cluster ( path , password , newMembers );
66
68
}
67
69
@@ -84,7 +86,6 @@ public void accept( Session session )
84
86
85
87
public ClusterMember leaderTx ( Consumer <Session > tx )
86
88
{
87
- // todo: handle leader switches
88
89
ClusterMember leader = leader ();
89
90
try ( Driver driver = createDriver ( leader .getBoltUri (), password );
90
91
Session session = driver .session () )
@@ -125,18 +126,6 @@ public Set<ClusterMember> readReplicas()
125
126
return membersWithRole ( ClusterMemberRole .READ_REPLICA );
126
127
}
127
128
128
- private static Driver createDriver ( String password , Set <ClusterMember > members )
129
- {
130
- if ( members .isEmpty () )
131
- {
132
- throw new IllegalArgumentException ( "No members, can't create driver" );
133
- }
134
-
135
- ClusterMember firstMember = members .iterator ().next ();
136
- URI boltUri = firstMember .getBoltUri ();
137
- return createDriver ( boltUri , password );
138
- }
139
-
140
129
@ Override
141
130
public String toString ()
142
131
{
@@ -150,7 +139,7 @@ private Set<ClusterMember> membersWithRole( ClusterMemberRole role )
150
139
{
151
140
Set <ClusterMember > membersWithRole = new HashSet <>();
152
141
153
- try ( Driver driver = createDriver ( password , members );
142
+ try ( Driver driver = createDriver ( members , password );
154
143
Session session = driver .session ( AccessMode .READ ) )
155
144
{
156
145
StatementResult result = session .run ( "call dbms.cluster.overview()" );
@@ -177,20 +166,23 @@ private Set<ClusterMember> membersWithRole( ClusterMemberRole role )
177
166
return membersWithRole ;
178
167
}
179
168
180
- private static Set <ClusterMember > waitForMembers ( String password , Set <ClusterMember > members )
169
+ private static Set <ClusterMember > waitForMembers ( Set <ClusterMember > members , String password )
170
+ throws ClusterUnavailableException
181
171
{
182
172
if ( members .isEmpty () )
183
173
{
184
- return members ;
174
+ throw new IllegalArgumentException ( "No members to wait for" ) ;
185
175
}
186
176
187
177
Set <ClusterMember > offlineMembers = new HashSet <>( members );
178
+ long deadline = System .currentTimeMillis () + TimeUnit .SECONDS .toMillis ( STARTUP_TIMEOUT_SECONDS );
188
179
189
- try ( Driver driver = createDriver ( password , members ) )
180
+ try ( Driver driver = createDriver ( members , password ) )
190
181
{
191
- // todo: add some timeout
192
182
while ( !offlineMembers .isEmpty () )
193
183
{
184
+ assertDeadlineNotReached ( deadline );
185
+
194
186
try ( Session session = driver .session ( AccessMode .READ ) )
195
187
{
196
188
StatementResult result = session .run ( "call dbms.cluster.overview()" );
@@ -211,11 +203,55 @@ private static Set<ClusterMember> waitForMembers( String password, Set<ClusterMe
211
203
return members ;
212
204
}
213
205
206
+ private static Driver createDriver ( Set <ClusterMember > members , String password )
207
+ {
208
+ if ( members .isEmpty () )
209
+ {
210
+ throw new IllegalArgumentException ( "No members, can't create driver" );
211
+ }
212
+
213
+ for ( ClusterMember member : members )
214
+ {
215
+ Driver driver = createDriver ( member .getBoltUri (), password );
216
+ try ( Session session = driver .session ( AccessMode .READ ) )
217
+ {
218
+ if ( isCoreMember ( session ) )
219
+ {
220
+ return driver ;
221
+ }
222
+ }
223
+ catch ( Exception e )
224
+ {
225
+ driver .close ();
226
+ throw e ;
227
+ }
228
+ }
229
+
230
+ throw new IllegalStateException ( "No core members found among: " + members );
231
+ }
232
+
214
233
private static Driver createDriver ( URI boltUri , String password )
215
234
{
216
235
return GraphDatabase .driver ( boltUri , AuthTokens .basic ( ADMIN_USER , password ), driverConfig () );
217
236
}
218
237
238
+ private static boolean isCoreMember ( Session session )
239
+ {
240
+ Record record = single ( session .run ( "call dbms.cluster.role" ).list () );
241
+ String roleName = record .get ( "role" ).asString ();
242
+ ClusterMemberRole role = ClusterMemberRole .valueOf ( roleName .toUpperCase () );
243
+ return role != ClusterMemberRole .READ_REPLICA ;
244
+ }
245
+
246
+ private static void assertDeadlineNotReached ( long deadline ) throws ClusterUnavailableException
247
+ {
248
+ if ( System .currentTimeMillis () > deadline )
249
+ {
250
+ throw new ClusterUnavailableException (
251
+ "Cluster did not become available in " + STARTUP_TIMEOUT_SECONDS + " seconds" );
252
+ }
253
+ }
254
+
219
255
private static URI extractBoltUri ( Record record )
220
256
{
221
257
List <Object > addresses = record .get ( "addresses" ).asList ();
0 commit comments