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
44
41
public class ClusterDriver extends BaseDriver
45
42
{
46
43
private static final String DISCOVER_MEMBERS = "dbms.cluster.discoverMembers" ;
47
44
private static final String ACQUIRE_ENDPOINTS = "dbms.cluster.acquireEndpoints" ;
48
- private static final int MINIMUM_NUMBER_OF_SERVERS = 3 ;
49
45
50
- private final ConnectionPool connections ;
46
+ private final Endpoints endpoints = new Endpoints ();
47
+ private final ClusterSettings clusterSettings ;
48
+ private boolean discoverable = true ;
51
49
52
- public ClusterDriver ( BoltServerAddress seedAddress , ConnectionSettings connectionSettings , SecurityPlan securityPlan ,
53
- PoolSettings poolSettings , Logging logging )
50
+ public ClusterDriver ( BoltServerAddress seedAddress , ConnectionSettings connectionSettings ,
51
+ ClusterSettings clusterSettings ,
52
+ SecurityPlan securityPlan ,
53
+ PoolSettings poolSettings , Logging logging )
54
54
{
55
- super ( seedAddress , securityPlan , logging );
56
- this .connections = new SocketConnectionPool ( connectionSettings , securityPlan , poolSettings , logging ) ;
55
+ super ( new SocketConnectionPool ( connectionSettings , securityPlan , poolSettings , logging ), seedAddress , securityPlan , logging );
56
+ this .clusterSettings = clusterSettings ;
57
57
discover ();
58
58
}
59
59
60
- void discover ()
60
+ synchronized void discover ()
61
61
{
62
- final List <BoltServerAddress > newServers = new LinkedList <>( );
62
+ if (!discoverable )
63
+ {
64
+ return ;
65
+ }
66
+
63
67
try
64
68
{
65
69
boolean success = false ;
66
- while ( !servers .isEmpty () && !success )
70
+ while ( !connections .isEmpty () && !success )
67
71
{
68
72
success = call ( DISCOVER_MEMBERS , new Consumer <Record >()
69
73
{
70
74
@ Override
71
75
public void accept ( Record record )
72
76
{
73
- newServers .add ( new BoltServerAddress ( record .get ( "address" ).asString () ) );
77
+ connections .add (new BoltServerAddress ( record .get ( "address" ).asString () ));
74
78
}
75
79
} );
76
-
77
80
}
78
- if ( success )
81
+ if ( ! success )
79
82
{
80
- this .servers .clear ();
81
- this .servers .addAll ( newServers );
82
- log .debug ( "~~ [MEMBERS] -> %s" , newServers );
83
- }
84
- else
85
- {
86
- throw new ClusterUnavailableException ( "Run out of servers" );
83
+ throw new ServiceUnavailableException ( "Run out of servers" );
87
84
}
88
85
}
89
86
catch ( ClientException ex )
90
87
{
91
88
if ( ex .code ().equals ( "Neo.ClientError.Procedure.ProcedureNotFound" ) )
92
89
{
93
- throw new ClientException ( "Discovery failed: could not find procedure %s" , DISCOVER_MEMBERS );
90
+ //no procedure there, not much to do, stick with what we've got
91
+ //this may happen because server is running in standalone mode
92
+ log .warn ( "Could not find procedure %s" , DISCOVER_MEMBERS );
93
+ discoverable = false ;
94
94
}
95
95
else
96
96
{
@@ -99,13 +99,15 @@ public void accept( Record record )
99
99
}
100
100
}
101
101
102
+ //must be called from a synchronized method
102
103
private boolean call ( String procedureName , Consumer <Record > recorder )
103
104
{
105
+ Connection acquire = null ;
106
+ Session session = null ;
107
+ try {
108
+ acquire = connections .acquire ();
109
+ session = new NetworkSession ( acquire , log );
104
110
105
- BoltServerAddress address = randomServer ();
106
- Connection acquire = connections .acquire ( address );
107
- try ( Session session = new NetworkSession ( acquire , log ) )
108
- {
109
111
StatementResult records = session .run ( format ( "CALL %s" , procedureName ) );
110
112
while ( records .hasNext () )
111
113
{
@@ -114,65 +116,162 @@ private boolean call( String procedureName, Consumer<Record> recorder )
114
116
}
115
117
catch ( ConnectionFailureException e )
116
118
{
117
- forget (address );
119
+ if (acquire != null )
120
+ {
121
+ forget ( acquire .address () );
122
+ }
118
123
return false ;
119
124
}
125
+ finally
126
+ {
127
+ if (acquire != null )
128
+ {
129
+ acquire .close ();
130
+ }
131
+ if (session != null )
132
+ {
133
+ session .close ();
134
+ }
135
+ }
120
136
return true ;
121
137
}
122
138
123
- private void forget (BoltServerAddress address )
139
+ //must be called from a synchronized method
140
+ private void callWithRetry (String procedureName , Consumer <Record > recorder )
124
141
{
125
- servers .remove ( address );
126
- connections .purge (address );
142
+ while ( !connections .isEmpty () )
143
+ {
144
+ Connection acquire = null ;
145
+ Session session = null ;
146
+ try {
147
+ acquire = connections .acquire ();
148
+ session = new NetworkSession ( acquire , log );
149
+ List <Record > list = session .run ( format ( "CALL %s" , procedureName ) ).list ();
150
+ for ( Record record : list )
151
+ {
152
+ recorder .accept ( record );
153
+ }
154
+ //we found results give up
155
+ return ;
156
+ }
157
+ catch ( ConnectionFailureException e )
158
+ {
159
+ if (acquire != null )
160
+ {
161
+ forget ( acquire .address () );
162
+ }
163
+ }
164
+ finally
165
+ {
166
+ if (acquire != null )
167
+ {
168
+ acquire .close ();
169
+ }
170
+ if (session != null )
171
+ {
172
+ session .close ();
173
+ }
174
+ }
175
+ }
176
+
177
+ throw new ServiceUnavailableException ( "Failed to communicate with any of the cluster members" );
178
+ }
179
+
180
+ private synchronized void forget ( BoltServerAddress address )
181
+ {
182
+ connections .purge ( address );
127
183
}
128
184
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
185
@ Override
132
186
public Session session ()
133
187
{
134
- throw new UnsupportedOperationException ( );
188
+ return session ( SessionMode . WRITE );
135
189
}
136
190
137
191
@ Override
138
192
public Session session ( final SessionMode mode )
139
193
{
140
- return new ClusteredSession ( new Supplier < Connection >( )
194
+ switch ( mode )
141
195
{
142
- @ Override
143
- public Connection get ()
196
+ case READ :
197
+ return new ReadNetworkSession ( new Supplier < Connection > ()
144
198
{
145
- return acquireConnection ( mode );
146
- }
147
- }, log );
199
+ @ Override
200
+ public Connection get ()
201
+ {
202
+ return acquireConnection ( mode );
203
+ }
204
+ }, new Consumer <Connection >()
205
+ {
206
+ @ Override
207
+ public void accept ( Connection connection )
208
+ {
209
+ forget ( connection .address () );
210
+ }
211
+ }, clusterSettings , log );
212
+ case WRITE :
213
+ throw new UnsupportedOperationException ();
214
+ default :
215
+ throw new UnsupportedOperationException ();
216
+ }
148
217
}
149
218
150
- private Connection acquireConnection ( SessionMode mode )
219
+ private synchronized Connection acquireConnection ( SessionMode mode )
151
220
{
221
+ if (!discoverable )
222
+ {
223
+ return connections .acquire ();
224
+ }
225
+
152
226
//if we are short on servers, find new ones
153
- if ( servers . size () < MINIMUM_NUMBER_OF_SERVERS )
227
+ if ( connections . addressCount () < clusterSettings . minimumNumberOfServers () )
154
228
{
155
229
discover ();
156
230
}
157
231
158
- final BoltServerAddress [] addresses = new BoltServerAddress [ 2 ] ;
159
- call ( ACQUIRE_ENDPOINTS , new Consumer < Record >()
232
+ endpoints . clear () ;
233
+ try
160
234
{
161
- @ Override
162
- public void accept ( Record record )
235
+ callWithRetry ( ACQUIRE_ENDPOINTS , new Consumer <Record >()
163
236
{
164
- addresses [0 ] = new BoltServerAddress ( record .get ( "READ" ).asString () );
165
- addresses [1 ] = new BoltServerAddress ( record .get ( "WRITE" ).asString () );
237
+ @ Override
238
+ public void accept ( Record record )
239
+ {
240
+ String serverMode = record .get ( "mode" ).asString ();
241
+ if ( serverMode .equals ( "READ" ) )
242
+ {
243
+ endpoints .readServer = new BoltServerAddress ( record .get ( "address" ).asString () );
244
+ }
245
+ else if ( serverMode .equals ( "WRITE" ) )
246
+ {
247
+ endpoints .writeServer = new BoltServerAddress ( record .get ( "address" ).asString () );
248
+ }
249
+ }
250
+ } );
251
+ }
252
+ catch (ClientException e )
253
+ {
254
+ if ( e .code ().equals ( "Neo.ClientError.Procedure.ProcedureNotFound" ) )
255
+ {
256
+ log .warn ( "Could not find procedure %s" , ACQUIRE_ENDPOINTS );
257
+ discoverable = false ;
258
+ return connections .acquire ();
166
259
}
167
- } );
260
+ throw e ;
261
+ }
262
+
263
+ if ( !endpoints .valid () )
264
+ {
265
+ throw new ServiceUnavailableException ("Could not establish any endpoints for the call" );
266
+ }
168
267
169
268
170
269
switch ( mode )
171
270
{
172
271
case READ :
173
- return connections .acquire ( addresses [ 0 ] );
272
+ return connections .acquire ( endpoints . readServer );
174
273
case WRITE :
175
- return connections .acquire ( addresses [ 0 ] );
274
+ return connections .acquire ( endpoints . writeServer );
176
275
default :
177
276
throw new ClientException ( mode + " is not supported for creating new sessions" );
178
277
}
@@ -191,4 +290,21 @@ public void close()
191
290
}
192
291
}
193
292
293
+ private static class Endpoints
294
+ {
295
+ BoltServerAddress readServer ;
296
+ BoltServerAddress writeServer ;
297
+
298
+ public boolean valid ()
299
+ {
300
+ return readServer != null && writeServer != null ;
301
+ }
302
+
303
+ public void clear ()
304
+ {
305
+ readServer = null ;
306
+ writeServer = null ;
307
+ }
308
+ }
309
+
194
310
}
0 commit comments