vagas-orientdb4r 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +26 -0
- data/.travis.yml +18 -0
- data/Gemfile +9 -0
- data/LICENSE +202 -0
- data/README.rdoc +124 -0
- data/Rakefile +43 -0
- data/changelog.txt +60 -0
- data/ci/initialize-ci.sh +36 -0
- data/fstudy/design_v1.dia +0 -0
- data/fstudy/design_v1.png +0 -0
- data/fstudy/domain_model.dia +0 -0
- data/fstudy/domain_model.png +0 -0
- data/fstudy/flat_class_perf.rb +56 -0
- data/fstudy/sample1_object_diagram.dia +0 -0
- data/fstudy/sample1_object_diagram.png +0 -0
- data/fstudy/study_case.rb +87 -0
- data/fstudy/technical_feasibility.rb +256 -0
- data/lib/orientdb4r.rb +115 -0
- data/lib/orientdb4r/bin/client.rb +86 -0
- data/lib/orientdb4r/bin/connection.rb +29 -0
- data/lib/orientdb4r/bin/constants.rb +20 -0
- data/lib/orientdb4r/bin/io.rb +38 -0
- data/lib/orientdb4r/bin/protocol28.rb +101 -0
- data/lib/orientdb4r/bin/protocol_factory.rb +25 -0
- data/lib/orientdb4r/chained_error.rb +37 -0
- data/lib/orientdb4r/client.rb +364 -0
- data/lib/orientdb4r/load_balancing.rb +113 -0
- data/lib/orientdb4r/node.rb +42 -0
- data/lib/orientdb4r/rest/client.rb +517 -0
- data/lib/orientdb4r/rest/excon_node.rb +115 -0
- data/lib/orientdb4r/rest/model.rb +159 -0
- data/lib/orientdb4r/rest/node.rb +43 -0
- data/lib/orientdb4r/rest/restclient_node.rb +77 -0
- data/lib/orientdb4r/rid.rb +54 -0
- data/lib/orientdb4r/utils.rb +203 -0
- data/lib/orientdb4r/version.rb +39 -0
- data/orientdb4r.gemspec +37 -0
- data/test/bin/test_client.rb +21 -0
- data/test/readme_sample.rb +38 -0
- data/test/test_client.rb +93 -0
- data/test/test_database.rb +261 -0
- data/test/test_ddo.rb +237 -0
- data/test/test_dmo.rb +115 -0
- data/test/test_document_crud.rb +184 -0
- data/test/test_gremlin.rb +52 -0
- data/test/test_helper.rb +10 -0
- data/test/test_loadbalancing.rb +81 -0
- data/test/test_utils.rb +67 -0
- metadata +136 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
3
|
+
module Orientdb4r
|
|
4
|
+
|
|
5
|
+
class Client
|
|
6
|
+
include Utils
|
|
7
|
+
|
|
8
|
+
# # Regexp to validate format of provided version.
|
|
9
|
+
SERVER_VERSION_PATTERN = /^\d+\.\d+\.\d+[-SNAPHOT]*$/
|
|
10
|
+
|
|
11
|
+
# connection parameters
|
|
12
|
+
attr_reader :user, :password, :database
|
|
13
|
+
# type of connection library [:restclient, :excon]
|
|
14
|
+
attr_reader :connection_library
|
|
15
|
+
# type of load balancing [:sequence, :round_robin]
|
|
16
|
+
attr_reader :load_balancing
|
|
17
|
+
# proxy for remote communication
|
|
18
|
+
attr_reader :proxy
|
|
19
|
+
|
|
20
|
+
# intern structures
|
|
21
|
+
|
|
22
|
+
# nodes responsible for communication with a server
|
|
23
|
+
attr_reader :nodes
|
|
24
|
+
# object implementing a LB strategy
|
|
25
|
+
attr_reader :lb_strategy
|
|
26
|
+
|
|
27
|
+
###
|
|
28
|
+
# Constructor.
|
|
29
|
+
def initialize
|
|
30
|
+
@nodes = []
|
|
31
|
+
@connected = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# --------------------------------------------------------------- CONNECTION
|
|
36
|
+
|
|
37
|
+
###
|
|
38
|
+
# Connects client to the server.
|
|
39
|
+
def connect options
|
|
40
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
###
|
|
45
|
+
# Disconnects client from the server.
|
|
46
|
+
def disconnect
|
|
47
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
###
|
|
52
|
+
# Gets flag whenever the client is connected or not.
|
|
53
|
+
def connected?
|
|
54
|
+
@connected
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
###
|
|
59
|
+
# Retrieve information about the connected OrientDB Server.
|
|
60
|
+
# Enables additional authentication to the server with an account
|
|
61
|
+
# that can access the 'server.info' resource.
|
|
62
|
+
def server(options={})
|
|
63
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ----------------------------------------------------------------- DATABASE
|
|
68
|
+
|
|
69
|
+
###
|
|
70
|
+
# Creates a new database.
|
|
71
|
+
# You can provide an additional authentication to the server with 'database.create' resource
|
|
72
|
+
# or the current one will be used.
|
|
73
|
+
# *options
|
|
74
|
+
# *storage - 'memory' (by default) or 'local'
|
|
75
|
+
# *type - 'document' (by default) or 'graph'
|
|
76
|
+
def create_database(options)
|
|
77
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
###
|
|
82
|
+
# Retrieves all the information about a database.
|
|
83
|
+
# Client has not to be connected to see databases suitable to connect.
|
|
84
|
+
def get_database(options)
|
|
85
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
###
|
|
90
|
+
# Checks existence of a given database.
|
|
91
|
+
# Client has not to be connected to see databases suitable to connect.
|
|
92
|
+
def database_exists?(options)
|
|
93
|
+
rslt = true
|
|
94
|
+
begin
|
|
95
|
+
get_database options
|
|
96
|
+
rescue OrientdbError
|
|
97
|
+
rslt = false
|
|
98
|
+
end
|
|
99
|
+
rslt
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
###
|
|
104
|
+
# Drops a database.
|
|
105
|
+
# Requires additional authentication to the server.
|
|
106
|
+
def delete_database(options)
|
|
107
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
###
|
|
112
|
+
# Retrieves the available databases.
|
|
113
|
+
# That is protected by the resource "server.listDatabases"
|
|
114
|
+
# that by default is assigned to the guest (anonymous) user in orientdb-server-config.xml.
|
|
115
|
+
def list_databases(options)
|
|
116
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
###
|
|
121
|
+
# Exports a gzip file that contains the database JSON export.
|
|
122
|
+
# Returns name of stored file.
|
|
123
|
+
def export(options)
|
|
124
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
###
|
|
129
|
+
# Imports a database from an uploaded JSON text file.
|
|
130
|
+
def import(options)
|
|
131
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ---------------------------------------------------------------------- SQL
|
|
135
|
+
|
|
136
|
+
###
|
|
137
|
+
# Executes a query against the database.
|
|
138
|
+
def query(sql, options)
|
|
139
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
###
|
|
144
|
+
# Executes a command against the database.
|
|
145
|
+
def command(sql)
|
|
146
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# -------------------------------------------------------------------- CLASS
|
|
151
|
+
|
|
152
|
+
###
|
|
153
|
+
# Creates a new class in the schema.
|
|
154
|
+
def create_class(name, options={})
|
|
155
|
+
raise ArgumentError, "class name is blank" if blank?(name)
|
|
156
|
+
opt_pattern = {
|
|
157
|
+
:extends => :optional, :cluster => :optional, :force => false, :abstract => false,
|
|
158
|
+
:properties => :optional
|
|
159
|
+
}
|
|
160
|
+
verify_options(options, opt_pattern)
|
|
161
|
+
|
|
162
|
+
sql = "CREATE CLASS #{name}"
|
|
163
|
+
sql << " EXTENDS #{options[:extends]}" if options.include? :extends
|
|
164
|
+
sql << " CLUSTER #{options[:cluster]}" if options.include? :cluster
|
|
165
|
+
sql << ' ABSTRACT' if options.include?(:abstract)
|
|
166
|
+
|
|
167
|
+
drop_class name if options[:force]
|
|
168
|
+
|
|
169
|
+
command sql
|
|
170
|
+
|
|
171
|
+
# properties given?
|
|
172
|
+
if options.include? :properties
|
|
173
|
+
props = options[:properties]
|
|
174
|
+
raise ArgumentError, 'properties have to be an array' unless props.is_a? Array
|
|
175
|
+
|
|
176
|
+
props.each do |prop|
|
|
177
|
+
raise ArgumentError, 'property definition has to be a hash' unless prop.is_a? Hash
|
|
178
|
+
prop_name = prop.delete :property
|
|
179
|
+
prop_type = prop.delete :type
|
|
180
|
+
create_property(name, prop_name, prop_type, prop)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
if block_given?
|
|
185
|
+
proxy = Orientdb4r::Utils::Proxy.new(self, name)
|
|
186
|
+
def proxy.property(property, type, options={})
|
|
187
|
+
self.target.send :create_property, self.context, property, type, options
|
|
188
|
+
end
|
|
189
|
+
def proxy.link(property, type, linked_class, options={})
|
|
190
|
+
raise ArgumentError, "type has to be a linked-type, given=#{type}" unless type.to_s.start_with? 'link'
|
|
191
|
+
options[:linked_class] = linked_class
|
|
192
|
+
self.target.send :create_property, self.context, property, type, options
|
|
193
|
+
end
|
|
194
|
+
yield proxy
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
###
|
|
200
|
+
# Gets informations about requested class.
|
|
201
|
+
def get_class(name)
|
|
202
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
###
|
|
207
|
+
# Checks existence of a given class.
|
|
208
|
+
def class_exists?(name)
|
|
209
|
+
rslt = true
|
|
210
|
+
begin
|
|
211
|
+
get_class name
|
|
212
|
+
rescue OrientdbError => e
|
|
213
|
+
raise e if e.is_a? ConnectionError and e.message == 'not connected' # workaround for AOP2 (unable to decorate already existing methods)
|
|
214
|
+
rslt = false
|
|
215
|
+
end
|
|
216
|
+
rslt
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
###
|
|
221
|
+
# Removes a class from the schema.
|
|
222
|
+
def drop_class(name, options={})
|
|
223
|
+
raise ArgumentError, 'class name is blank' if blank?(name)
|
|
224
|
+
|
|
225
|
+
# :mode=>:strict forbids to drop a class that is a super class for other one
|
|
226
|
+
opt_pattern = { :mode => :nil }
|
|
227
|
+
verify_options(options, opt_pattern)
|
|
228
|
+
if :strict == options[:mode]
|
|
229
|
+
response = get_database
|
|
230
|
+
children = response['classes'].select { |i| i['superClass'] == name }
|
|
231
|
+
unless children.empty?
|
|
232
|
+
raise OrientdbError, "class is super-class, cannot be deleted, name=#{name}"
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
command "DROP CLASS #{name}"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
###
|
|
241
|
+
# Creates a new property in the schema.
|
|
242
|
+
# You need to create the class before.
|
|
243
|
+
def create_property(clazz, property, type, options={})
|
|
244
|
+
raise ArgumentError, "class name is blank" if blank?(clazz)
|
|
245
|
+
raise ArgumentError, "property name is blank" if blank?(property)
|
|
246
|
+
opt_pattern = {
|
|
247
|
+
:mandatory => :optional , :notnull => :optional, :min => :optional, :max => :optional,
|
|
248
|
+
:readonly => :optional, :linked_class => :optional
|
|
249
|
+
}
|
|
250
|
+
verify_options(options, opt_pattern)
|
|
251
|
+
|
|
252
|
+
cmd = "CREATE PROPERTY #{clazz}.#{property} #{type.to_s}"
|
|
253
|
+
# link?
|
|
254
|
+
if [:link, :linklist, :linkset, :linkmap].include? type.to_s.downcase.to_sym
|
|
255
|
+
raise ArgumentError, "defined linked-type, but not linked-class" unless options.include? :linked_class
|
|
256
|
+
cmd << " #{options[:linked_class]}"
|
|
257
|
+
end
|
|
258
|
+
command cmd
|
|
259
|
+
|
|
260
|
+
# ALTER PROPERTY ...
|
|
261
|
+
options.delete :linked_class # it's not option for ALTER
|
|
262
|
+
unless options.empty?
|
|
263
|
+
options.each do |k,v|
|
|
264
|
+
command "ALTER PROPERTY #{clazz}.#{property} #{k.to_s.upcase} #{v}"
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# ----------------------------------------------------------------- DOCUMENT
|
|
271
|
+
|
|
272
|
+
###
|
|
273
|
+
# Create a new document.
|
|
274
|
+
# Returns the Record-id assigned for OrientDB version <= 1.3.x
|
|
275
|
+
# and the whole new document for version >= 1.4.x
|
|
276
|
+
# (see https://groups.google.com/forum/?fromgroups=#!topic/orient-database/UJGAXYpHDmo for more info).
|
|
277
|
+
def create_document(doc)
|
|
278
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
###
|
|
283
|
+
# Retrieves a document by given ID.
|
|
284
|
+
def get_document(rid)
|
|
285
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
###
|
|
290
|
+
# Updates an existing document.
|
|
291
|
+
def update_document(doc)
|
|
292
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
###
|
|
297
|
+
# Deletes an existing document.
|
|
298
|
+
def delete_document(rid)
|
|
299
|
+
raise NotImplementedError, 'this should be overridden by concrete client'
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
protected
|
|
304
|
+
|
|
305
|
+
###
|
|
306
|
+
# Calls the server with a specific task.
|
|
307
|
+
# Returns a response according to communication channel (e.g. HTTP response).
|
|
308
|
+
def call_server(options)
|
|
309
|
+
lb_all_bad_msg = 'all nodes failed to communicate with server!'
|
|
310
|
+
response = nil
|
|
311
|
+
|
|
312
|
+
# credentials if not defined explicitly
|
|
313
|
+
options[:user] = user unless options.include? :user
|
|
314
|
+
options[:password] = password unless options.include? :password
|
|
315
|
+
debug_string = options[:uri]
|
|
316
|
+
if debug_string
|
|
317
|
+
query_log("Orientdb4r::Client#call_server", URI.decode(debug_string))
|
|
318
|
+
end
|
|
319
|
+
idx = lb_strategy.node_index
|
|
320
|
+
raise OrientdbError, lb_all_bad_msg if idx.nil? # no good node found
|
|
321
|
+
|
|
322
|
+
begin
|
|
323
|
+
node = @nodes[idx]
|
|
324
|
+
begin
|
|
325
|
+
response = node.request options
|
|
326
|
+
lb_strategy.good_one idx
|
|
327
|
+
return response
|
|
328
|
+
|
|
329
|
+
rescue NodeError => e
|
|
330
|
+
Orientdb4r::logger.error "node error, index=#{idx}, msg=#{e.message}, #{node}"
|
|
331
|
+
node.cleanup
|
|
332
|
+
lb_strategy.bad_one idx
|
|
333
|
+
idx = lb_strategy.node_index
|
|
334
|
+
end
|
|
335
|
+
end until idx.nil? and response.nil? # both 'nil' <= we tried all nodes and all with problem
|
|
336
|
+
|
|
337
|
+
raise OrientdbError, lb_all_bad_msg
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
###
|
|
342
|
+
# Asserts if the client is connected and raises an error if not.
|
|
343
|
+
def assert_connected
|
|
344
|
+
raise ConnectionError, 'not connected' unless @connected
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
###
|
|
349
|
+
# Around advice to meassure and print the method time.
|
|
350
|
+
def time_around(&block)
|
|
351
|
+
start = Time.now
|
|
352
|
+
rslt = block.call
|
|
353
|
+
query_log("#{aop_context[:class].name}##{aop_context[:method]}", "elapsed time = #{Time.now - start} [s]")
|
|
354
|
+
rslt
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def query_log(context, message)
|
|
358
|
+
Orientdb4r::logger.debug \
|
|
359
|
+
" \033[01;33m#{context}:\033[0m #{message}"
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module Orientdb4r
|
|
2
|
+
|
|
3
|
+
###
|
|
4
|
+
# Base class for implementation of load balancing strategy.
|
|
5
|
+
class LBStrategy
|
|
6
|
+
|
|
7
|
+
# After what time [s] can be a failed node reused in load balancing.
|
|
8
|
+
DEFAULT_RECOVER_TIME = 30
|
|
9
|
+
|
|
10
|
+
attr_reader :nodes_count, :bad_nodes
|
|
11
|
+
attr_accessor :recover_time
|
|
12
|
+
|
|
13
|
+
###
|
|
14
|
+
# Constructor.
|
|
15
|
+
def initialize nodes_count
|
|
16
|
+
@nodes_count = nodes_count
|
|
17
|
+
@bad_nodes = {}
|
|
18
|
+
@recover_time = DEFAULT_RECOVER_TIME
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
###
|
|
22
|
+
# Gets index of node to be used for next request
|
|
23
|
+
# or 'nil' if there is no one next.
|
|
24
|
+
def node_index
|
|
25
|
+
raise NotImplementedError, 'this should be overridden in subclass'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
###
|
|
29
|
+
# Marks an index as good that means it can be used for next server calls.
|
|
30
|
+
def good_one(idx)
|
|
31
|
+
@bad_nodes.delete idx
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
###
|
|
35
|
+
# Marks an index as bad that means it will be not used until:
|
|
36
|
+
# * there is other 'good' node
|
|
37
|
+
# * timeout
|
|
38
|
+
def bad_one(idx)
|
|
39
|
+
@bad_nodes[idx] = Time.now
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
###
|
|
45
|
+
# Tries to find a new node if the given failed.
|
|
46
|
+
# Returns <i>nil</i> if no one found
|
|
47
|
+
def search_next_good(bad_idx)
|
|
48
|
+
Orientdb4r::logger.warn "identified bad node, idx=#{bad_idx}, age=#{Time.now - @bad_nodes[bad_idx]} [s]"
|
|
49
|
+
|
|
50
|
+
# alternative nodes of not found a good one
|
|
51
|
+
timeout_candidate = nil
|
|
52
|
+
|
|
53
|
+
# first round - try to find a first good one
|
|
54
|
+
1.upto(nodes_count) do |i|
|
|
55
|
+
candidate = (i + bad_idx) % nodes_count
|
|
56
|
+
|
|
57
|
+
if @bad_nodes.include? candidate
|
|
58
|
+
failure_time = @bad_nodes[candidate]
|
|
59
|
+
now = Time.now
|
|
60
|
+
# timeout candidate
|
|
61
|
+
if (now - failure_time) > recover_time
|
|
62
|
+
timeout_candidate = candidate
|
|
63
|
+
Orientdb4r::logger.debug "node timeout recovery, idx=#{candidate}"
|
|
64
|
+
good_one(candidate)
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
Orientdb4r::logger.debug "found good node, idx=#{candidate}"
|
|
68
|
+
return candidate
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# no good index found -> try timeouted one
|
|
73
|
+
unless timeout_candidate.nil?
|
|
74
|
+
Orientdb4r::logger.debug "good node not found, delivering timeouted one, idx=#{timeout_candidate}"
|
|
75
|
+
return timeout_candidate
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
Orientdb4r::logger.error 'no nodes more, all invalid'
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
###
|
|
85
|
+
# Implementation of Sequence strategy.
|
|
86
|
+
# Assigns work in the order of nodes defined by the client initialization.
|
|
87
|
+
class Sequence < LBStrategy
|
|
88
|
+
|
|
89
|
+
def node_index #:nodoc:
|
|
90
|
+
@last_index = 0 if @last_index.nil?
|
|
91
|
+
|
|
92
|
+
@last_index = search_next_good(@last_index) if @bad_nodes.include? @last_index
|
|
93
|
+
@last_index
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
###
|
|
99
|
+
# Implementation of Round Robin strategy.
|
|
100
|
+
# Assigns work in round-robin order per nodes defined by the client initialization.
|
|
101
|
+
class RoundRobin < LBStrategy
|
|
102
|
+
|
|
103
|
+
def node_index #:nodoc:
|
|
104
|
+
@last_index = -1 if @last_index.nil?
|
|
105
|
+
|
|
106
|
+
@last_index = (@last_index + 1) % nodes_count
|
|
107
|
+
@last_index = search_next_good(@last_index) if @bad_nodes.include? @last_index
|
|
108
|
+
@last_index
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|