wordnik 4.08 → 4.09
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.
- data/Gemfile.lock +1 -1
- data/README.md +2 -2
- data/lib/wordnik/load_balancer.rb +17 -7
- data/lib/wordnik/version.rb +1 -1
- data/spec/load_balancer_spec.rb +96 -15
- metadata +4 -4
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -92,8 +92,8 @@ Releasing
|
|
92
92
|
---------
|
93
93
|
|
94
94
|
```bash
|
95
|
-
rake swagger
|
96
|
-
|
95
|
+
rake swagger # um, don't do this unless the API has changed
|
96
|
+
edit lib/wordnik/version.rb # bump the version number
|
97
97
|
rake spec # test
|
98
98
|
git commit -am "newness" # commit
|
99
99
|
git push origin master # push
|
@@ -19,8 +19,8 @@ module Wordnik
|
|
19
19
|
attr_accessor :current_host
|
20
20
|
|
21
21
|
def initialize(hosts)
|
22
|
-
@all_hosts = hosts
|
23
|
-
@hosts = @all_hosts
|
22
|
+
@all_hosts = hosts.clone
|
23
|
+
@hosts = @all_hosts.clone
|
24
24
|
@failed_hosts_table = {}
|
25
25
|
@current_host = nil
|
26
26
|
end
|
@@ -36,7 +36,7 @@ module Wordnik
|
|
36
36
|
#Wordnik.logger.debug "Informing failure about #{@current_host}. table: #{@failed_hosts_table.inspect}"
|
37
37
|
if @failed_hosts_table.include?(@current_host)
|
38
38
|
failures, failed_time = @failed_hosts_table[@current_host]
|
39
|
-
@failed_hosts_table[@current_host] = [failures+1,
|
39
|
+
@failed_hosts_table[@current_host] = [failures+1, Time.now.to_f]
|
40
40
|
else
|
41
41
|
@failed_hosts_table[@current_host] = [1, Time.now.to_f] # failure count, first failure time
|
42
42
|
end
|
@@ -57,15 +57,25 @@ module Wordnik
|
|
57
57
|
return if @failed_hosts_table.size == 0
|
58
58
|
@failed_hosts_table.each do |host, pair|
|
59
59
|
failures, failed_time = pair
|
60
|
-
|
61
|
-
|
60
|
+
n = Time.now.to_f
|
61
|
+
seconds_since_last_failure = (n - failed_time)
|
62
62
|
# exponential backoff, but try every hour...
|
63
|
-
if (
|
63
|
+
if (seconds_since_last_failure > [3600, 2**(failures-1)].min)
|
64
64
|
@hosts << host # give it a chance to succeed ...
|
65
|
-
|
65
|
+
update_failed_time(host, n)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
|
+
|
70
|
+
# mostly useful in mock testing...
|
71
|
+
def update_failed_time(host, time=Time.now)
|
72
|
+
if @failed_hosts_table.include? host
|
73
|
+
failures, _ = @failed_hosts_table[host]
|
74
|
+
@failed_hosts_table[host] = [failures, time.to_f]
|
75
|
+
else
|
76
|
+
@failed_hosts_table[host] = [1,time.to_f]
|
77
|
+
end
|
78
|
+
end
|
69
79
|
end
|
70
80
|
|
71
81
|
end
|
data/lib/wordnik/version.rb
CHANGED
data/spec/load_balancer_spec.rb
CHANGED
@@ -1,28 +1,109 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'objspace' # ruby 1.9, I think
|
2
3
|
|
3
4
|
describe Wordnik::LoadBalancer do
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
before(:each) do
|
6
|
+
@hosts = ["alpha","beta","gamma","delta"].map{|x| "#{x}.wordnik.com"}
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
+
describe "Load Balancer" do
|
9
10
|
it "allows creation with a list of hosts" do
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
12
|
+
h = lb.host
|
13
|
+
@hosts.should include(h)
|
13
14
|
end
|
14
15
|
|
15
16
|
it "returns different (random) hosts" do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
18
|
+
counts = Hash.new(0)
|
19
|
+
1.upto(1000) do
|
20
|
+
h = lb.host
|
21
|
+
counts[h] += 1
|
22
|
+
end
|
23
|
+
@hosts.each {|host| counts[host].should > 10}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not leak memory" do
|
27
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
28
|
+
free = ObjectSpace.count_objects[:TOTAL]
|
29
|
+
1000.times {h = lb.host}
|
30
|
+
free.should == ObjectSpace.count_objects[:TOTAL]
|
23
31
|
end
|
24
32
|
|
25
|
-
|
33
|
+
describe "Success and failure modes" do
|
34
|
+
it "allows for a failure" do
|
35
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
36
|
+
h = lb.host
|
37
|
+
lb.inform_failure
|
38
|
+
lb.failed_hosts_table.size.should == 1
|
39
|
+
lb.hosts.should_not include(h)
|
40
|
+
lb.hosts.size.should == lb.all_hosts.size - 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it "allows for two failures" do
|
44
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
45
|
+
h1 = lb.host
|
46
|
+
lb.inform_failure
|
47
|
+
h2 = lb.host
|
48
|
+
lb.inform_failure
|
49
|
+
lb.failed_hosts_table.size.should == 2
|
50
|
+
lb.hosts.should_not include(h1)
|
51
|
+
lb.hosts.should_not include(h2)
|
52
|
+
lb.hosts.size.should == lb.all_hosts.size - 2
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should never leave hosts empty" do
|
56
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
57
|
+
@hosts.size.times{h = lb.host; lb.inform_failure}
|
58
|
+
lb.hosts.size.should == 1
|
59
|
+
end
|
26
60
|
|
61
|
+
it "allows for a subsequent success" do
|
62
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
63
|
+
h = lb.host
|
64
|
+
lb.inform_failure
|
65
|
+
lb.inform_success
|
66
|
+
lb.failed_hosts_table.size.should == 0
|
67
|
+
lb.hosts.should include(h)
|
68
|
+
lb.hosts.size.should == lb.all_hosts.size
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does exponential back-off" do
|
72
|
+
lb = Wordnik::LoadBalancer.new(@hosts)
|
73
|
+
t = Time.now
|
74
|
+
h = lb.host
|
75
|
+
lb.inform_failure
|
76
|
+
lb.update_failed_time(h, t)
|
77
|
+
lb.update_failed_time(h, t-2) # pretend it happened two seconds ago
|
78
|
+
lb.restore_failed_hosts_maybe
|
79
|
+
lb.hosts.should include(h)
|
80
|
+
lb.hosts.size.should == lb.all_hosts.size
|
81
|
+
lb.inform_failure
|
82
|
+
lb.inform_failure
|
83
|
+
lb.update_failed_time(h, t-2) # pretend it happened two seconds ago
|
84
|
+
lb.restore_failed_hosts_maybe
|
85
|
+
lb.hosts.should_not include(h)
|
86
|
+
lb.hosts.size.should == lb.all_hosts.size - 1
|
87
|
+
lb.update_failed_time(h, t-4) # pretend it happened 4 seconds ago
|
88
|
+
lb.restore_failed_hosts_maybe
|
89
|
+
lb.hosts.should include(h)
|
90
|
+
lb.hosts.size.should == lb.all_hosts.size
|
91
|
+
# create a lot of failures
|
92
|
+
1000.times {lb.inform_failure}
|
93
|
+
lb.update_failed_time(h, t-((59*60)+59)) # pretend it happened 59:59 minutes ago
|
94
|
+
lb.restore_failed_hosts_maybe
|
95
|
+
lb.hosts.should_not include(h)
|
96
|
+
lb.hosts.size.should == lb.all_hosts.size - 1
|
97
|
+
lb.update_failed_time(h, t-(60*60)) # pretend it happened 1 hour ago
|
98
|
+
lb.restore_failed_hosts_maybe
|
99
|
+
lb.hosts.should include(h)
|
100
|
+
lb.hosts.size.should == lb.all_hosts.size
|
101
|
+
lb.inform_success
|
102
|
+
lb.failed_hosts_table.size.should == 0
|
103
|
+
end
|
104
|
+
end
|
27
105
|
end
|
106
|
+
|
107
|
+
|
108
|
+
|
28
109
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wordnik
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '4.
|
4
|
+
version: '4.09'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-10-
|
13
|
+
date: 2012-10-30 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: typhoeus
|
@@ -310,7 +310,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
310
310
|
version: '0'
|
311
311
|
segments:
|
312
312
|
- 0
|
313
|
-
hash:
|
313
|
+
hash: 1161348886692951169
|
314
314
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
315
315
|
none: false
|
316
316
|
requirements:
|
@@ -319,7 +319,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
319
319
|
version: '0'
|
320
320
|
segments:
|
321
321
|
- 0
|
322
|
-
hash:
|
322
|
+
hash: 1161348886692951169
|
323
323
|
requirements: []
|
324
324
|
rubyforge_project: wordnik
|
325
325
|
rubygems_version: 1.8.24
|