twirl 0.1.0

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.
@@ -0,0 +1,3 @@
1
+ module Twirl
2
+ VERSION = "0.1.0"
3
+ end
data/script/bootstrap ADDED
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ #/ Usage: bootstrap [bundle options]
3
+ #/
4
+ #/ Bundle install the dependencies.
5
+ #/
6
+ #/ Examples:
7
+ #/
8
+ #/ bootstrap
9
+ #/ bootstrap --local
10
+ #/
11
+
12
+ set -e
13
+ cd $(dirname "$0")/..
14
+
15
+ [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
16
+ grep '^#/' <"$0"| cut -c4-
17
+ exit 0
18
+ }
19
+
20
+ rm -rf .bundle/{binstubs,config}
21
+ bundle install --binstubs .bundle/binstubs --path .bundle "$@"
data/script/kestrel ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+ require "pathname"
6
+ root = Pathname(__FILE__).join("..", "..").expand_path
7
+
8
+ $LOAD_PATH.unshift root.join("lib")
9
+ require "twirl/server"
10
+
11
+ {
12
+ "kestrel1" => {
13
+ memcache_port: 9444,
14
+ thrift_port: 9445,
15
+ text_port: 9446,
16
+ admin_port: 9447,
17
+ },
18
+ "kestrel2" => {
19
+ memcache_port: 9544,
20
+ thrift_port: 9545,
21
+ text_port: 9546,
22
+ admin_port: 9547,
23
+ },
24
+ }.each do |dir, options|
25
+ server = Twirl::Server.new(root.join("tmp", dir), options)
26
+ puts "\n\nMaking sure kestrel is running on the following ports: #{options.inspect}"
27
+ if ARGV[0] == "stop"
28
+ server.stop
29
+ else
30
+ server.start
31
+ end
32
+ end
33
+
34
+ puts "\n\nYou can now run the tests."
data/script/release ADDED
@@ -0,0 +1,42 @@
1
+ #!/bin/sh
2
+ #/ Usage: release
3
+ #/
4
+ #/ Tag the version in the repo and push the gem.
5
+ #/
6
+
7
+ set -e
8
+ cd $(dirname "$0")/..
9
+
10
+ [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
11
+ grep '^#/' <"$0"| cut -c4-
12
+ exit 0
13
+ }
14
+
15
+ gem_name=twirl
16
+
17
+ # Build a new gem archive.
18
+ rm -rf $gem_name-*.gem
19
+ gem build -q $gem_name.gemspec
20
+
21
+ # Make sure we're on the master branch.
22
+ (git branch | grep -q '* master') || {
23
+ echo "Only release from the master branch."
24
+ exit 1
25
+ }
26
+
27
+ # Figure out what version we're releasing.
28
+ tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"`
29
+
30
+ echo "Releasing $tag"
31
+
32
+ # Make sure we haven't released this version before.
33
+ git fetch -t origin
34
+
35
+ (git tag -l | grep -q "$tag") && {
36
+ echo "Whoops, there's already a '${tag}' tag."
37
+ exit 1
38
+ }
39
+
40
+ # Tag it and bag it.
41
+ gem push $gem_name-*.gem && git tag "$tag" &&
42
+ git push origin master && git push origin "$tag"
data/script/test ADDED
@@ -0,0 +1,25 @@
1
+ #!/bin/sh
2
+ #/ Usage: test [individual test file]
3
+ #/
4
+ #/ Bootstrap and run all tests or an individual test.
5
+ #/
6
+ #/ Examples:
7
+ #/
8
+ #/ # run all tests
9
+ #/ test
10
+ #/
11
+ #/ # run individual test
12
+ #/ test test/twirl_test.rb
13
+ #/
14
+
15
+ set -e
16
+ cd $(dirname "$0")/..
17
+
18
+ [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
19
+ grep '^#/' <"$0"| cut -c4-
20
+ exit 0
21
+ }
22
+
23
+ script/bootstrap && ruby -I lib -I test -r rubygems \
24
+ -e 'require "bundler/setup"' \
25
+ -e '(ARGV.empty? ? Dir["test/**/*_test.rb"] : ARGV).each { |f| load f }' -- "$@"
data/script/watch ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ #/ Usage: watch
3
+ #/
4
+ #/ Run the tests whenever any relevant files change.
5
+ #/
6
+
7
+ require "pathname"
8
+ require "rubygems"
9
+ require "bundler/setup"
10
+
11
+ # Put us where we belong, in the root dir of the project.
12
+ Dir.chdir Pathname.new(__FILE__).realpath + "../.."
13
+
14
+ # Run the tests to start.
15
+ system "clear; script/test"
16
+
17
+ require "rb-fsevent"
18
+
19
+ IgnoreRegex = /\/fixtures/
20
+
21
+ fs = FSEvent.new
22
+ fs.watch ["app", "lib", "test"], latency: 1 do |args|
23
+ unless args.first =~ IgnoreRegex
24
+ system "clear"
25
+ puts "#{args.first} changed..."
26
+ system "script/test"
27
+ end
28
+ end
29
+ fs.run
@@ -0,0 +1,469 @@
1
+ require "helper"
2
+ require "twirl/cluster"
3
+
4
+ class ClusterTest < Minitest::Test
5
+ def test_initialize
6
+ clients = [
7
+ KJess::Client.new(host: "localhost", port: 1),
8
+ KJess::Client.new(host: "localhost", port: 2),
9
+ ]
10
+ cluster = Twirl::Cluster.new(clients)
11
+ assert_equal 0, cluster.command_count
12
+ assert_equal 0, cluster.client_index
13
+ assert_equal 100, cluster.commands_per_client
14
+ assert_equal 5, cluster.retries
15
+ assert_equal [KJess::NetworkError, KJess::ServerError],
16
+ cluster.retryable_errors
17
+ assert_equal Twirl::Instrumenters::Noop, cluster.instrumenter
18
+ end
19
+
20
+ def test_initialize_shuffles_clients
21
+ clients = Minitest::Mock.new
22
+ clients.expect :shuffle, :shuffle_result
23
+ cluster = Twirl::Cluster.new(clients)
24
+ assert_equal :shuffle_result, cluster.instance_variable_get("@clients")
25
+ clients.verify
26
+ end
27
+
28
+ def test_overriding_instrumenter
29
+ instrumenter = Object.new
30
+ cluster = Twirl::Cluster.new([], instrumenter: instrumenter)
31
+ assert_equal instrumenter, cluster.instrumenter
32
+ end
33
+
34
+ def test_overriding_retryable_errors
35
+ retryable_errors = StandardError
36
+ cluster = Twirl::Cluster.new([], retryable_errors: retryable_errors)
37
+ assert_equal retryable_errors, cluster.retryable_errors
38
+ end
39
+
40
+ def test_overriding_retries
41
+ cluster = Twirl::Cluster.new([], retries: 1)
42
+ assert_equal 1, cluster.retries
43
+ end
44
+
45
+ def test_enumeration
46
+ cluster = build(:mock_cluster)
47
+ clients = []
48
+ cluster.each_with_index do |client, index|
49
+ client.expect :port, index
50
+ clients << client
51
+ end
52
+ assert_equal clients, cluster.to_a
53
+ assert_equal [0, 1, 2], cluster.map(&:port)
54
+ end
55
+
56
+ def test_size
57
+ cluster = build(:mock_cluster)
58
+ assert_equal 3, cluster.size
59
+ assert_equal 3, cluster.length
60
+ assert_equal 3, cluster.count
61
+ end
62
+
63
+ def test_bracket_access
64
+ clients = [
65
+ KJess::Client.new(host: "localhost", port: 1),
66
+ KJess::Client.new(host: "localhost", port: 2),
67
+ KJess::Client.new(host: "localhost", port: 3),
68
+ ]
69
+ cluster = Twirl::Cluster.new(clients)
70
+ shuffled_clients = cluster.instance_variable_get("@clients")
71
+ assert_equal shuffled_clients[0], cluster[0]
72
+ assert_equal shuffled_clients[1], cluster[1]
73
+ assert_equal shuffled_clients[2], cluster[2]
74
+ end
75
+
76
+ def test_set_rotates_based_on_commands_per_client
77
+ args = ["testing", "data", 0]
78
+ cluster = build(:mock_cluster, commands_per_client: 1)
79
+
80
+ cluster.each do |client|
81
+ client.expect :set, true, args
82
+ end
83
+
84
+ assert_equal true, cluster.set(*args)
85
+ assert_equal true, cluster.set(*args)
86
+ assert_equal true, cluster.set(*args)
87
+
88
+ cluster.each(&:verify)
89
+ end
90
+
91
+ def test_get_returns_item_if_client_returns_item
92
+ args = ["testing", {}]
93
+ cluster = build(:mock_cluster)
94
+ cluster[0].expect :get, "data", args
95
+
96
+ item = cluster.get(*args)
97
+ assert_instance_of Twirl::Item, item
98
+ assert_equal "testing", item.key
99
+ assert_equal "data", item.value
100
+
101
+ cluster.each(&:verify)
102
+ end
103
+
104
+ def test_get_returns_nil_if_client_returns_nil
105
+ args = ["testing", {}]
106
+ cluster = build(:mock_cluster)
107
+ cluster[0].expect :get, nil, args
108
+
109
+ assert_nil cluster.get(*args)
110
+
111
+ cluster.each(&:verify)
112
+ end
113
+
114
+ def test_get_passes_options_to_client
115
+ args = ["testing", {open: true}]
116
+ cluster = build(:mock_cluster)
117
+ cluster[0].expect :get, nil, args
118
+ cluster.get(*args)
119
+ cluster.each(&:verify)
120
+ end
121
+
122
+ def test_get_rotates_based_on_commands_per_client
123
+ args = ["testing", {}]
124
+ cluster = build(:mock_cluster, commands_per_client: 1)
125
+
126
+ cluster[0].expect :get, "result-1", args
127
+ cluster[1].expect :get, "result-2", args
128
+ cluster[2].expect :get, "result-3", args
129
+
130
+ assert_equal "result-1", cluster.get(*args).value
131
+ assert_equal "result-2", cluster.get(*args).value
132
+ assert_equal "result-3", cluster.get(*args).value
133
+
134
+ cluster.each(&:verify)
135
+ end
136
+
137
+ def test_get_rotates_client_if_nil
138
+ args = ["testing", {}]
139
+ cluster = build(:mock_cluster, commands_per_client: 1_000)
140
+
141
+ cluster[0].expect :get, nil, args
142
+ cluster[1].expect :get, nil, args
143
+ cluster[2].expect :get, "data", args
144
+
145
+ assert_nil cluster.get(*args)
146
+ assert_nil cluster.get(*args)
147
+ assert_equal "data", cluster.get(*args).value
148
+
149
+ cluster.each(&:verify)
150
+ end
151
+
152
+ def test_reserve
153
+ cluster = build(:mock_cluster)
154
+ cluster[0].expect :reserve, "data", ["testing", {}]
155
+ assert_equal "data", cluster.reserve("testing").value
156
+ cluster.each(&:verify)
157
+ end
158
+
159
+ def test_reserve_returns_nil_if_client_returns_nil
160
+ cluster = build(:mock_cluster)
161
+ cluster[0].expect :reserve, nil, ["testing", {}]
162
+ assert_nil cluster.reserve("testing")
163
+ cluster.each(&:verify)
164
+ end
165
+
166
+ def test_reserve_rotates_client_if_nil
167
+ args = ["testing", {}]
168
+ cluster = build(:mock_cluster, commands_per_client: 1_000)
169
+
170
+ cluster[0].expect :reserve, nil, args
171
+ cluster[1].expect :reserve, nil, args
172
+ cluster[2].expect :reserve, "data", args
173
+
174
+ assert_nil cluster.reserve(*args)
175
+ assert_nil cluster.reserve(*args)
176
+ assert_equal "data", cluster.reserve(*args).value
177
+
178
+ cluster.each(&:verify)
179
+ end
180
+
181
+ def test_peek
182
+ cluster = build(:mock_cluster)
183
+ cluster[0].expect :peek, "data", ["testing"]
184
+ assert_equal "data", cluster.peek("testing").value
185
+ cluster.each(&:verify)
186
+ end
187
+
188
+ def test_peek_returns_nil_if_client_returns_nil
189
+ cluster = build(:mock_cluster)
190
+ cluster[0].expect :peek, nil, ["testing"]
191
+ assert_nil cluster.peek("testing")
192
+ cluster.each(&:verify)
193
+ end
194
+
195
+ def test_peek_rotates_client_if_nil
196
+ args = ["testing"]
197
+ cluster = build(:mock_cluster, commands_per_client: 1_000)
198
+
199
+ cluster[0].expect :peek, nil, args
200
+ cluster[1].expect :peek, nil, args
201
+ cluster[2].expect :peek, "data", args
202
+
203
+ assert_nil cluster.peek(*args)
204
+ assert_nil cluster.peek(*args)
205
+ assert_equal "data", cluster.peek(*args).value
206
+
207
+ cluster.each(&:verify)
208
+ end
209
+
210
+ def test_flush
211
+ cluster = build(:mock_cluster)
212
+ cluster.size.times do |index|
213
+ cluster[index].expect :host, "localhost"
214
+ cluster[index].expect :port, (index + 1).to_s
215
+ cluster[index].expect :flush, index % 2 == 0, ["foo"]
216
+ end
217
+ expected = {
218
+ "localhost:1" => true,
219
+ "localhost:2" => false,
220
+ "localhost:3" => true,
221
+ }
222
+ assert_equal expected, cluster.flush("foo")
223
+ cluster.each(&:verify)
224
+ end
225
+
226
+ def test_flush_all
227
+ cluster = build(:mock_cluster)
228
+ cluster.size.times do |index|
229
+ cluster[index].expect :host, "localhost"
230
+ cluster[index].expect :port, (index + 1).to_s
231
+ cluster[index].expect :flush_all, index % 2 == 0
232
+ end
233
+ expected = {
234
+ "localhost:1" => true,
235
+ "localhost:2" => false,
236
+ "localhost:3" => true,
237
+ }
238
+ assert_equal expected, cluster.flush_all
239
+ cluster.each(&:verify)
240
+ end
241
+
242
+ def test_disconnect
243
+ cluster = build(:mock_cluster)
244
+ cluster.size.times do |index|
245
+ cluster[index].expect :disconnect, nil
246
+ end
247
+ cluster.disconnect
248
+ cluster.each(&:verify)
249
+ end
250
+
251
+ def test_version
252
+ cluster = build(:mock_cluster)
253
+ cluster.size.times do |index|
254
+ cluster[index].expect :host, "localhost"
255
+ cluster[index].expect :port, (index + 1).to_s
256
+ cluster[index].expect :version, "2.4.1"
257
+ end
258
+ expected = {
259
+ "localhost:1" => "2.4.1",
260
+ "localhost:2" => "2.4.1",
261
+ "localhost:3" => "2.4.1",
262
+ }
263
+ assert_equal expected, cluster.version
264
+ cluster.each(&:verify)
265
+ end
266
+
267
+ def test_version_with_one_raising_exception
268
+ cluster = build(:mock_cluster)
269
+ cluster.size.times do |index|
270
+ cluster[index].expect :host, "localhost"
271
+ cluster[index].expect :port, (index + 1).to_s
272
+ end
273
+
274
+ # force client1 to raise an error and other clients to return as normal
275
+ client1 = cluster[0]
276
+ def client1.version
277
+ raise KJess::ProtocolError
278
+ end
279
+ cluster[1].expect :version, "2.4.1"
280
+ cluster[2].expect :version, "2.4.1"
281
+
282
+ expected = {
283
+ "localhost:1" => "unavailable",
284
+ "localhost:2" => "2.4.1",
285
+ "localhost:3" => "2.4.1",
286
+ }
287
+ assert_equal expected, cluster.version
288
+ cluster.each(&:verify)
289
+ end
290
+
291
+ def test_delete
292
+ cluster = build(:mock_cluster)
293
+ cluster.size.times do |index|
294
+ cluster[index].expect :host, "localhost"
295
+ cluster[index].expect :port, (index + 1).to_s
296
+ cluster[index].expect :delete, index % 2 == 0, ["foo"]
297
+ end
298
+ expected = {
299
+ "localhost:1" => true,
300
+ "localhost:2" => false,
301
+ "localhost:3" => true,
302
+ }
303
+ assert_equal expected, cluster.delete("foo")
304
+ cluster.each(&:verify)
305
+ end
306
+
307
+ def test_ping
308
+ cluster = build(:mock_cluster)
309
+ cluster.size.times do |index|
310
+ cluster[index].expect :host, "localhost"
311
+ cluster[index].expect :port, (index + 1).to_s
312
+ cluster[index].expect :ping, index % 2 == 0
313
+ end
314
+ expected = {
315
+ "localhost:1" => true,
316
+ "localhost:2" => false,
317
+ "localhost:3" => true,
318
+ }
319
+ assert_equal expected, cluster.ping
320
+ cluster.each(&:verify)
321
+ end
322
+
323
+ def test_shutdown
324
+ cluster = build(:mock_cluster)
325
+ cluster.size.times do |index|
326
+ cluster[index].expect :shutdown, nil
327
+ end
328
+ cluster.shutdown
329
+ cluster.each(&:verify)
330
+ end
331
+
332
+ def test_reload
333
+ cluster = build(:mock_cluster)
334
+ cluster.size.times do |index|
335
+ cluster[index].expect :host, "localhost"
336
+ cluster[index].expect :port, (index + 1).to_s
337
+ cluster[index].expect :reload, index % 2 == 0
338
+ end
339
+ expected = {
340
+ "localhost:1" => true,
341
+ "localhost:2" => false,
342
+ "localhost:3" => true,
343
+ }
344
+ assert_equal expected, cluster.reload
345
+ cluster.each(&:verify)
346
+ end
347
+
348
+ def test_quit
349
+ cluster = build(:mock_cluster)
350
+ cluster.size.times do |index|
351
+ cluster[index].expect :host, "localhost"
352
+ cluster[index].expect :port, (index + 1).to_s
353
+ cluster[index].expect :quit, index % 2 == 0
354
+ end
355
+ expected = {
356
+ "localhost:1" => true,
357
+ "localhost:2" => false,
358
+ "localhost:3" => true,
359
+ }
360
+ assert_equal expected, cluster.quit
361
+ cluster.each(&:verify)
362
+ end
363
+
364
+ def test_stats
365
+ cluster = build(:mock_cluster)
366
+ cluster.size.times do |index|
367
+ cluster[index].expect :host, "localhost"
368
+ cluster[index].expect :port, (index + 1).to_s
369
+ cluster[index].expect :stats, {items: index + 1}
370
+ end
371
+ expected = {
372
+ "localhost:1" => {items: 1},
373
+ "localhost:2" => {items: 2},
374
+ "localhost:3" => {items: 3},
375
+ }
376
+ assert_equal expected, cluster.stats
377
+ cluster.each(&:verify)
378
+ end
379
+
380
+ def test_rotation
381
+ cluster = build(:mock_cluster, commands_per_client: 3)
382
+ assert_equal 0, cluster.command_count
383
+
384
+ counts = 7.times.map {
385
+ cluster.client
386
+ cluster.command_count
387
+ }
388
+
389
+ assert_equal [1, 2, 3, 1, 2, 3, 1], counts
390
+ end
391
+
392
+ RetriableMethods = {
393
+ get: [["testing"], "data"],
394
+ reserve: [["testing"], "data"],
395
+ peek: [["testing"], "data"],
396
+ set: [["testing", "data"], true],
397
+ }
398
+
399
+ RecoverableExceptions = [
400
+ KJess::NetworkError,
401
+ KJess::ServerError,
402
+ ]
403
+
404
+ def test_get_retries_network_errors
405
+ cluster = build(:cluster)
406
+ client = cluster[0]
407
+
408
+ RetriableMethods.each do |op, info|
409
+ args, result = info
410
+ RecoverableExceptions.each do |exception|
411
+ client.instance_eval <<-EOC
412
+ def client.#{op}(*args)
413
+ @counter ||= 0
414
+ if @counter < 4
415
+ @counter += 1
416
+ raise #{exception}
417
+ else
418
+ #{result.inspect}
419
+ end
420
+ end
421
+
422
+ def client.reset
423
+ @counter = 0
424
+ end
425
+ EOC
426
+
427
+ begin
428
+ op_result = cluster.send(op, *args)
429
+ ensure
430
+ client.reset
431
+ end
432
+ end
433
+ end
434
+ end
435
+
436
+ def test_too_many_retries_raises
437
+ cluster = build(:cluster)
438
+ client = cluster[0]
439
+
440
+ RetriableMethods.each do |op, info|
441
+ args, result = info
442
+ RecoverableExceptions.each do |exception|
443
+ client.instance_eval <<-EOC
444
+ def client.#{op}(*args)
445
+ @counter ||= 0
446
+ if @counter < 5
447
+ @counter += 1
448
+ raise #{exception}
449
+ else
450
+ #{result.inspect}
451
+ end
452
+ end
453
+
454
+ def client.reset
455
+ @counter = 0
456
+ end
457
+ EOC
458
+
459
+ assert_raises exception, "#{op} raising #{exception}" do
460
+ begin
461
+ cluster.send(op, *args)
462
+ ensure
463
+ client.reset
464
+ end
465
+ end
466
+ end
467
+ end
468
+ end
469
+ end