16
16
* See the License for the specific language governing permissions and
17
17
* limitations under the License.
18
18
*/
19
-
20
19
package org .neo4j .driver .internal ;
21
20
22
- import java .util .LinkedList ;
23
21
import java .util .List ;
24
22
25
23
import org .neo4j .driver .internal .net .BoltServerAddress ;
26
24
import org .neo4j .driver .internal .net .pooling .PoolSettings ;
27
25
import org .neo4j .driver .internal .net .pooling .SocketConnectionPool ;
28
26
import org .neo4j .driver .internal .security .SecurityPlan ;
29
27
import org .neo4j .driver .internal .spi .Connection ;
30
- import org .neo4j .driver .internal .spi .ConnectionPool ;
31
28
import org .neo4j .driver .internal .util .Consumer ;
32
29
import org .neo4j .driver .internal .util .Supplier ;
33
30
import org .neo4j .driver .v1 .Logging ;
36
33
import org .neo4j .driver .v1 .SessionMode ;
37
34
import org .neo4j .driver .v1 .StatementResult ;
38
35
import org .neo4j .driver .v1 .exceptions .ClientException ;
39
- import org .neo4j .driver .v1 .exceptions .ClusterUnavailableException ;
40
36
import org .neo4j .driver .v1 .exceptions .ConnectionFailureException ;
37
+ import org .neo4j .driver .v1 .exceptions .ServiceUnavailableException ;
41
38
42
39
import static java .lang .String .format ;
43
40
@@ -47,50 +44,44 @@ public class ClusterDriver extends BaseDriver
47
44
private static final String ACQUIRE_ENDPOINTS = "dbms.cluster.acquireEndpoints" ;
48
45
private static final int MINIMUM_NUMBER_OF_SERVERS = 3 ;
49
46
50
- private final ConnectionPool connections ;
47
+ private final Endpoints endpoints = new Endpoints () ;
51
48
52
- public ClusterDriver ( BoltServerAddress seedAddress , ConnectionSettings connectionSettings , SecurityPlan securityPlan ,
53
- PoolSettings poolSettings , Logging logging )
49
+ public ClusterDriver ( BoltServerAddress seedAddress , ConnectionSettings connectionSettings ,
50
+ SecurityPlan securityPlan ,
51
+ PoolSettings poolSettings , Logging logging )
54
52
{
55
- super ( seedAddress , securityPlan , logging );
56
- this .connections = new SocketConnectionPool ( connectionSettings , securityPlan , poolSettings , logging );
53
+ super ( new SocketConnectionPool ( connectionSettings , securityPlan , poolSettings , logging ),seedAddress , securityPlan , logging );
57
54
discover ();
58
55
}
59
56
60
- void discover ()
57
+ synchronized void discover ()
61
58
{
62
- final List <BoltServerAddress > newServers = new LinkedList <>( );
63
59
try
64
60
{
65
61
boolean success = false ;
66
- while ( !servers .isEmpty () && !success )
62
+ while ( !connections .isEmpty () && !success )
67
63
{
68
64
success = call ( DISCOVER_MEMBERS , new Consumer <Record >()
69
65
{
70
66
@ Override
71
67
public void accept ( Record record )
72
68
{
73
- newServers .add ( new BoltServerAddress ( record .get ( "address" ).asString () ) );
69
+ connections .add (new BoltServerAddress ( record .get ( "address" ).asString () ));
74
70
}
75
71
} );
76
-
77
- }
78
- if ( success )
79
- {
80
- this .servers .clear ();
81
- this .servers .addAll ( newServers );
82
- log .debug ( "~~ [MEMBERS] -> %s" , newServers );
83
72
}
84
- else
73
+ if ( ! success )
85
74
{
86
- throw new ClusterUnavailableException ( "Run out of servers" );
75
+ throw new ServiceUnavailableException ( "Run out of servers" );
87
76
}
88
77
}
89
78
catch ( ClientException ex )
90
79
{
91
80
if ( ex .code ().equals ( "Neo.ClientError.Procedure.ProcedureNotFound" ) )
92
81
{
93
- throw new ClientException ( "Discovery failed: could not find procedure %s" , DISCOVER_MEMBERS );
82
+ //no procedure there, not much to do, stick with what we've got
83
+ //this may happen because server is running in standalone mode
84
+ log .warn ( "Could not find procedure %s" , DISCOVER_MEMBERS );
94
85
}
95
86
else
96
87
{
@@ -99,13 +90,15 @@ public void accept( Record record )
99
90
}
100
91
}
101
92
93
+ //must be called from a synchronized method
102
94
private boolean call ( String procedureName , Consumer <Record > recorder )
103
95
{
96
+ Connection acquire = null ;
97
+ Session session = null ;
98
+ try {
99
+ acquire = connections .acquire ();
100
+ session = new NetworkSession ( acquire , log );
104
101
105
- BoltServerAddress address = randomServer ();
106
- Connection acquire = connections .acquire ( address );
107
- try ( Session session = new NetworkSession ( acquire , log ) )
108
- {
109
102
StatementResult records = session .run ( format ( "CALL %s" , procedureName ) );
110
103
while ( records .hasNext () )
111
104
{
@@ -114,65 +107,157 @@ private boolean call( String procedureName, Consumer<Record> recorder )
114
107
}
115
108
catch ( ConnectionFailureException e )
116
109
{
117
- forget (address );
110
+ if (acquire != null )
111
+ {
112
+ forget ( acquire .address () );
113
+ }
118
114
return false ;
119
115
}
116
+ finally
117
+ {
118
+ if (acquire != null )
119
+ {
120
+ acquire .close ();
121
+ }
122
+ if (session != null )
123
+ {
124
+ session .close ();
125
+ }
126
+ }
120
127
return true ;
121
128
}
122
129
123
- private void forget (BoltServerAddress address )
130
+ //must be called from a synchronized method
131
+ private void callWithRetry (String procedureName , Consumer <Record > recorder )
132
+ {
133
+ while ( !connections .isEmpty () )
134
+ {
135
+ Connection acquire = null ;
136
+ Session session = null ;
137
+ try {
138
+ acquire = connections .acquire ();
139
+ session = new NetworkSession ( acquire , log );
140
+ List <Record > list = session .run ( format ( "CALL %s" , procedureName ) ).list ();
141
+ for ( Record record : list )
142
+ {
143
+ recorder .accept ( record );
144
+ }
145
+ //we found results give up
146
+ return ;
147
+ }
148
+ catch ( ConnectionFailureException e )
149
+ {
150
+ if (acquire != null )
151
+ {
152
+ forget ( acquire .address () );
153
+ }
154
+ }
155
+ finally
156
+ {
157
+ if (acquire != null )
158
+ {
159
+ acquire .close ();
160
+ }
161
+ if (session != null )
162
+ {
163
+ session .close ();
164
+ }
165
+ }
166
+ }
167
+
168
+ throw new ServiceUnavailableException ( "Failed to communicate with any of the cluster members" );
169
+ }
170
+
171
+ private synchronized void forget ( BoltServerAddress address )
124
172
{
125
- servers . remove ( address );
126
- connections .purge (address );
173
+ address . markInvalid ( );
174
+ connections .purge ( address );
127
175
}
128
176
129
- //TODO this could return a WRITE session but that may lead to users using the LEADER too much
130
- //a `ClientException` may be what we want
131
177
@ Override
132
178
public Session session ()
133
179
{
134
- throw new UnsupportedOperationException ( );
180
+ return session ( SessionMode . WRITE );
135
181
}
136
182
137
183
@ Override
138
184
public Session session ( final SessionMode mode )
139
185
{
140
- return new ClusteredSession ( new Supplier < Connection >( )
186
+ switch ( mode )
141
187
{
142
- @ Override
143
- public Connection get ()
188
+ case READ :
189
+ return new ReadNetworkSession ( new Supplier < Connection > ()
144
190
{
145
- return acquireConnection ( mode );
146
- }
147
- }, log );
191
+ @ Override
192
+ public Connection get ()
193
+ {
194
+ return acquireConnection ( mode );
195
+ }
196
+ }, new Consumer <Connection >()
197
+ {
198
+ @ Override
199
+ public void accept ( Connection connection )
200
+ {
201
+ forget ( connection .address () );
202
+ }
203
+ }, log );
204
+ case WRITE :
205
+ throw new UnsupportedOperationException ();
206
+ default :
207
+ throw new UnsupportedOperationException ();
208
+ }
148
209
}
149
210
150
- private Connection acquireConnection ( SessionMode mode )
211
+ private synchronized Connection acquireConnection ( SessionMode mode )
151
212
{
152
213
//if we are short on servers, find new ones
153
- if ( servers .size () < MINIMUM_NUMBER_OF_SERVERS )
214
+ if ( connections .size () < MINIMUM_NUMBER_OF_SERVERS )
154
215
{
155
216
discover ();
156
217
}
157
218
158
- final BoltServerAddress [] addresses = new BoltServerAddress [2 ];
159
- call ( ACQUIRE_ENDPOINTS , new Consumer <Record >()
219
+ endpoints .clear ();
220
+ try
221
+ {
222
+ callWithRetry ( ACQUIRE_ENDPOINTS , new Consumer <Record >()
223
+ {
224
+ @ Override
225
+ public void accept ( Record record )
226
+ {
227
+ String serverMode = record .get ( "mode" ).asString ();
228
+ if ( serverMode .equals ( "READ" ) )
229
+ {
230
+ endpoints .readServer = new BoltServerAddress ( record .get ( "address" ).asString () );
231
+ }
232
+ else if ( serverMode .equals ( "WRITE" ) )
233
+ {
234
+ endpoints .writeServer = new BoltServerAddress ( record .get ( "address" ).asString () );
235
+ }
236
+ }
237
+ } );
238
+ }
239
+ catch (ClientException e )
160
240
{
161
- @ Override
162
- public void accept ( Record record )
241
+ if ( e .code ().equals ( "Neo.ClientError.Procedure.ProcedureNotFound" ) )
163
242
{
164
- addresses [ 0 ] = new BoltServerAddress ( record . get ( "READ" ). asString () );
165
- addresses [ 1 ] = new BoltServerAddress ( record . get ( "WRITE" ). asString () );
243
+ log . warn ( "Could not find procedure %s" , ACQUIRE_ENDPOINTS );
244
+ return connections . acquire ( );
166
245
}
167
- } );
246
+ throw e ;
247
+ }
248
+
249
+ if ( !endpoints .valid () )
250
+ {
251
+ throw new ServiceUnavailableException ("Could not establish any endpoints for the call" );
252
+ }
168
253
169
254
170
255
switch ( mode )
171
256
{
172
257
case READ :
173
- return connections .acquire ( addresses [ 0 ] );
258
+ return connections .acquire ( endpoints . readServer );
174
259
case WRITE :
175
- return connections .acquire ( addresses [ 0 ] );
260
+ return connections .acquire ( endpoints . writeServer );
176
261
default :
177
262
throw new ClientException ( mode + " is not supported for creating new sessions" );
178
263
}
@@ -191,4 +276,21 @@ public void close()
191
276
}
192
277
}
193
278
279
+ private static class Endpoints
280
+ {
281
+ BoltServerAddress readServer ;
282
+ BoltServerAddress writeServer ;
283
+
284
+ public boolean valid ()
285
+ {
286
+ return readServer != null && writeServer != null ;
287
+ }
288
+
289
+ public void clear ()
290
+ {
291
+ readServer = null ;
292
+ writeServer = null ;
293
+ }
294
+ }
295
+
194
296
}
0 commit comments