fraggle 4.0.0.pre.1 → 4.0.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +69 -44
- data/bench/gs.rb +77 -0
- data/example/{getdir_all.rb → getdir.rb} +3 -3
- data/example/readme.rb +51 -25
- data/example/{walk_all.rb → walk.rb} +5 -5
- data/example/watch.rb +2 -2
- data/fraggle.gemspec +1 -0
- data/lib/fraggle/client.rb +52 -48
- data/lib/fraggle/connection.rb +34 -16
- data/lib/fraggle/msg.pb.rb +1 -0
- data/lib/fraggle/response.rb +0 -6
- data/lib/fraggle/version.rb +1 -1
- data/test/fraggle_client_test.rb +19 -10
- data/test/fraggle_transaction_test.rb +13 -16
- data/test/helper.rb +4 -3
- metadata +21 -7
- data/bench/gets.rb +0 -32
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
# Fraggle
|
1
|
+
# Fraggle
|
2
|
+
## The current gem is v4.0.0.pre.1 is compatible with Doozer 0.6
|
2
3
|
|
3
|
-
|
4
|
+
Please see the [4.0.0.pre.1 README](https://github.com/ha/fraggle/tree/v4.0.0.pre.1) for instructions on use.
|
4
5
|
|
5
6
|
**An EventMachine based Doozer client**
|
6
7
|
|
7
8
|
## Install
|
8
9
|
|
10
|
+
(For pre-releases, use `--pre`)
|
11
|
+
|
9
12
|
$ gem install fraggle
|
10
13
|
|
11
14
|
## Use
|
@@ -31,48 +34,75 @@ addresses with IP 127.0.0.1 and ports 8046, 8041, 8042, 8043.
|
|
31
34
|
c = Fraggle.connect
|
32
35
|
|
33
36
|
c.rev do |v|
|
34
|
-
c.get(v
|
35
|
-
|
36
|
-
|
37
|
+
c.get(v, "/foo") do |e, err|
|
38
|
+
if err
|
39
|
+
err.code # => nil
|
40
|
+
err.detail # => nil
|
41
|
+
else
|
37
42
|
e.value # => nil
|
38
43
|
e.rev # => 0
|
39
44
|
e.missing? # => true
|
40
|
-
else
|
41
|
-
e.err_code # => nil
|
42
|
-
e.err_detail # => nil
|
43
45
|
end
|
46
|
+
|
47
|
+
p [:get, e, err]
|
44
48
|
end
|
45
49
|
|
46
50
|
## Obtain the current revision the store is at and watch from then on for
|
47
51
|
## any SET or DEL to /foo.
|
48
|
-
c.wait(v
|
52
|
+
c.wait(v, "/foo") do |e, err|
|
49
53
|
# The event has:
|
50
54
|
# ------------------------
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
if err
|
56
|
+
err.code # => nil
|
57
|
+
err.detail # => nil
|
58
|
+
else
|
59
|
+
e.path # => "/foo"
|
60
|
+
e.value # => "zomg!"
|
61
|
+
e.rev # => 123
|
62
|
+
e.set? # => true
|
63
|
+
e.del? # => false
|
64
|
+
end
|
65
|
+
|
66
|
+
p [:wait, e, err]
|
60
67
|
end
|
61
68
|
end
|
62
69
|
|
63
70
|
## Setting a key (this will trigger the watch above)
|
64
|
-
|
71
|
+
f = Proc.new do |e, err|
|
72
|
+
p [:e, e, err]
|
73
|
+
|
74
|
+
if err && err.disconnected?
|
75
|
+
# Fraggle (for now) does not attempt a non-idempotent request. This means
|
76
|
+
# Fraggle will hand off the error to the user if there is a SET or DEL
|
77
|
+
# with rev 0 (missing) and delete it during the time we may be
|
78
|
+
# disconnected.
|
79
|
+
#
|
80
|
+
# In this scenario, there are no other clients that can exist that will
|
81
|
+
# attempt to set this "lock" if it's missing then delete it. It is safe
|
82
|
+
# for us to resend the request if we were disconnected from the previous
|
83
|
+
# server before a response.
|
84
|
+
#
|
85
|
+
# See High-Availability in the README for more information about this.
|
86
|
+
#
|
87
|
+
c.set(0, "/foo", "zomg!", &f)
|
88
|
+
next
|
89
|
+
end
|
90
|
+
|
65
91
|
# Success!
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
92
|
+
if err
|
93
|
+
case err.code
|
94
|
+
when Fraggle::REV_MISMATCH
|
95
|
+
p :not_it
|
96
|
+
when nil
|
97
|
+
# Success!
|
98
|
+
p [:it, e]
|
99
|
+
else
|
100
|
+
fail "something bad happened: " + e.inspect
|
101
|
+
end
|
73
102
|
end
|
74
103
|
end
|
75
104
|
|
105
|
+
c.set(0, "/foo", "zomg!", &f)
|
76
106
|
end
|
77
107
|
|
78
108
|
## Consistency
|
@@ -82,9 +112,9 @@ the most up-to-date data. If you need to do multiple reads at certain
|
|
82
112
|
point in time for consistency, use the `rev` command.
|
83
113
|
|
84
114
|
c.rev do |v|
|
85
|
-
c.get(v
|
86
|
-
c.get(v
|
87
|
-
c.get(v
|
115
|
+
c.get(v, "/a") { ... }
|
116
|
+
c.get(v, "/b") { ... }
|
117
|
+
c.get(v, "/c") { ... }
|
88
118
|
end
|
89
119
|
|
90
120
|
This also means you can go back in time or into the future!
|
@@ -117,8 +147,9 @@ stores history as far back as it is configured to hold it. The default is
|
|
117
147
|
|
118
148
|
## Commands
|
119
149
|
|
120
|
-
Each command behaves according to the [proto spec][], respectively.
|
121
|
-
Their `blk`s are called with
|
150
|
+
Each command below behaves according to the [proto spec][], respectively.
|
151
|
+
Their `blk`s are called with two parameters, a `Fraggle::Response` as the first
|
152
|
+
or a `Fraggle::Connection::ResponseError` as the second if a response is
|
122
153
|
returned from the server.
|
123
154
|
|
124
155
|
`set(rev, path, value, &blk)`
|
@@ -127,10 +158,6 @@ returned from the server.
|
|
127
158
|
|
128
159
|
`get(rev, path, &blk)`
|
129
160
|
|
130
|
-
`getdir(rev, path, offset, &blk)`
|
131
|
-
|
132
|
-
`walk(rev, path, offset, &blk)`
|
133
|
-
|
134
161
|
`wait(rev, path, &blk)`
|
135
162
|
|
136
163
|
`rev(&blk)`
|
@@ -144,19 +171,17 @@ returned from the server.
|
|
144
171
|
Watches `path` (a glob pattern) for changes, from `rev` in history on. Its
|
145
172
|
`blk` is called with a `Fraggle::Response` for each event.
|
146
173
|
|
147
|
-
`
|
174
|
+
`getdir(rev, path, off=0, lim=MaxInt64, ents=[], &blk)`
|
148
175
|
|
149
176
|
Behaves like `getdir` but collects `ents`, starting at `off` until all or `lim`
|
150
|
-
entries are read. When done `blk` is called with the result as the
|
151
|
-
parameter or
|
152
|
-
other will be set and the other with be `nil`.
|
177
|
+
entries are read. When done, `blk` is called with the result (an `Array`) as the
|
178
|
+
first parameter or a `Fraggle::Connection::Response` as the second. Depending
|
179
|
+
on the response, one or the other will be set and the other with be `nil`.
|
153
180
|
|
154
|
-
`
|
181
|
+
`walk(rev, path, off=0, lim=MaxInt64, ents=[], &blk)`
|
155
182
|
|
156
|
-
|
157
|
-
|
158
|
-
parameter or an error as the second. Depending on the response, one or the
|
159
|
-
other will be set and the other with be `nil`.
|
183
|
+
Like `getdir`, but but path is a glob pattern and each result contains a `path`,
|
184
|
+
`value`, and `rev`.
|
160
185
|
|
161
186
|
## Dev
|
162
187
|
|
data/bench/gs.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# By Mark McGranaghan
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require "bundler"
|
7
|
+
Bundler.setup
|
8
|
+
require "fraggle"
|
9
|
+
require "statsample"
|
10
|
+
|
11
|
+
$stdout.sync = true
|
12
|
+
|
13
|
+
abort("read <[get|set]> <total> <width> [verbose]") if (ARGV.size < 2)
|
14
|
+
op = ARGV.shift.to_sym
|
15
|
+
total = ARGV.shift.to_i
|
16
|
+
width = ARGV.shift.to_i
|
17
|
+
verbose = !!ARGV.shift
|
18
|
+
latencies = []
|
19
|
+
sent_at = nil
|
20
|
+
|
21
|
+
EM.run do
|
22
|
+
client = Fraggle.connect
|
23
|
+
sent = 0
|
24
|
+
received = 0
|
25
|
+
start = Time.now
|
26
|
+
|
27
|
+
f = Proc.new do |r|
|
28
|
+
received_at = Time.now
|
29
|
+
received +=1
|
30
|
+
latency = received_at - sent_at
|
31
|
+
|
32
|
+
latencies << latency
|
33
|
+
if verbose
|
34
|
+
$stdout.puts("received=#{received} ok=#{r.ok?} rev=#{r.rev} latency=#{latency}")
|
35
|
+
elsif (received % 10 == 0)
|
36
|
+
$stdout.print(".")
|
37
|
+
end
|
38
|
+
if (received == total)
|
39
|
+
EM.stop
|
40
|
+
elapsed = Time.now - start
|
41
|
+
vector = latencies.to_scale
|
42
|
+
$stdout.puts
|
43
|
+
$stdout.puts("total=#{total}")
|
44
|
+
$stdout.puts("elapsed=#{elapsed}")
|
45
|
+
$stdout.puts("rate=#{total / elapsed}")
|
46
|
+
$stdout.puts("mean=#{vector.mean}")
|
47
|
+
$stdout.puts("sd=#{vector.sd}")
|
48
|
+
$stdout.puts("perc90=#{vector.percentil(90)}")
|
49
|
+
$stdout.puts("perc99=#{vector.percentil(99)}")
|
50
|
+
$stdout.puts("max=#{vector.max}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
tick = Proc.new do
|
55
|
+
if (sent == total)
|
56
|
+
# done sending
|
57
|
+
elsif ((sent - received) < width)
|
58
|
+
# pipe open
|
59
|
+
sent_at = Time.now
|
60
|
+
sent += 1
|
61
|
+
if verbose
|
62
|
+
$stdout.puts("sent=#{sent}")
|
63
|
+
end
|
64
|
+
|
65
|
+
case op
|
66
|
+
when :get
|
67
|
+
client.get(nil, "/processes/#{sent}", &f)
|
68
|
+
when :set
|
69
|
+
client.set(Fraggle::Clobber, "/processes/#{sent}", "1", &f)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
# pipe closed
|
73
|
+
end
|
74
|
+
EM.next_tick(&tick)
|
75
|
+
end
|
76
|
+
tick.call
|
77
|
+
end
|
@@ -6,7 +6,7 @@ EM.run do
|
|
6
6
|
|
7
7
|
c.rev do |v|
|
8
8
|
# Valid
|
9
|
-
req = c.
|
9
|
+
req = c.getdir(v, "/ctl/node") do |ents, err|
|
10
10
|
if err
|
11
11
|
p [:err, err]
|
12
12
|
else
|
@@ -17,12 +17,12 @@ EM.run do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Limit 0 return nothing
|
20
|
-
c.
|
20
|
+
c.getdir(v, "/ctl/node", 0, 0) do |ents, err|
|
21
21
|
p [:ret, ents, err]
|
22
22
|
end
|
23
23
|
|
24
24
|
# Error
|
25
|
-
c.
|
25
|
+
c.getdir(v, "/nothere") do |ents, err|
|
26
26
|
p [:ret, ents, err]
|
27
27
|
end
|
28
28
|
end
|
data/example/readme.rb
CHANGED
@@ -10,47 +10,73 @@ EM.run do
|
|
10
10
|
c = Fraggle.connect
|
11
11
|
|
12
12
|
c.rev do |v|
|
13
|
-
c.get(v
|
14
|
-
|
15
|
-
|
13
|
+
c.get(v, "/foo") do |e, err|
|
14
|
+
if err
|
15
|
+
err.code # => nil
|
16
|
+
err.detail # => nil
|
17
|
+
else
|
16
18
|
e.value # => nil
|
17
19
|
e.rev # => 0
|
18
20
|
e.missing? # => true
|
19
|
-
else
|
20
|
-
e.err_code # => nil
|
21
|
-
e.err_detail # => nil
|
22
21
|
end
|
22
|
+
|
23
|
+
p [:get, e, err]
|
23
24
|
end
|
24
25
|
|
25
26
|
## Obtain the current revision the store is at and watch from then on for
|
26
27
|
## any SET or DEL to /foo.
|
27
|
-
c.wait(v
|
28
|
+
c.wait(v, "/foo") do |e, err|
|
28
29
|
# The event has:
|
29
30
|
# ------------------------
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
if err
|
32
|
+
err.code # => nil
|
33
|
+
err.detail # => nil
|
34
|
+
else
|
35
|
+
e.path # => "/foo"
|
36
|
+
e.value # => "zomg!"
|
37
|
+
e.rev # => 123
|
38
|
+
e.set? # => true
|
39
|
+
e.del? # => false
|
40
|
+
end
|
41
|
+
|
42
|
+
p [:wait, e, err]
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
46
|
## Setting a key (this will trigger the watch above)
|
43
|
-
|
47
|
+
f = Proc.new do |e, err|
|
48
|
+
p [:e, e, err]
|
49
|
+
|
50
|
+
if err && err.disconnected?
|
51
|
+
# Fraggle (for now) does not attempt a non-idempotent request. This means
|
52
|
+
# Fraggle will hand off the error to the user if there is a SET or DEL
|
53
|
+
# with rev 0 (missing) and delete it during the time we may be
|
54
|
+
# disconnected.
|
55
|
+
#
|
56
|
+
# In this scenario, there are no other clients that can exist that will
|
57
|
+
# attempt to set this "lock" if it's missing then delete it. It is safe
|
58
|
+
# for us to resend the request if we were disconnected from the previous
|
59
|
+
# server before a response.
|
60
|
+
#
|
61
|
+
# See High-Availability in the README for more information about this.
|
62
|
+
#
|
63
|
+
c.set(0, "/foo", "zomg!", &f)
|
64
|
+
next
|
65
|
+
end
|
66
|
+
|
44
67
|
# Success!
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
68
|
+
if err
|
69
|
+
case err.code
|
70
|
+
when Fraggle::REV_MISMATCH
|
71
|
+
p :not_it
|
72
|
+
when nil
|
73
|
+
# Success!
|
74
|
+
p [:it, e]
|
75
|
+
else
|
76
|
+
fail "something bad happened: " + e.inspect
|
77
|
+
end
|
53
78
|
end
|
54
79
|
end
|
55
80
|
|
81
|
+
c.set(0, "/foo", "zomg!", &f)
|
56
82
|
end
|
@@ -6,7 +6,7 @@ EM.run do
|
|
6
6
|
|
7
7
|
c.rev do |v|
|
8
8
|
# Valid
|
9
|
-
req = c.
|
9
|
+
req = c.walk(v, "/ctl/node/**") do |ents, err|
|
10
10
|
if err
|
11
11
|
p [:err, err]
|
12
12
|
else
|
@@ -17,13 +17,13 @@ EM.run do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Limit 0 return nothing
|
20
|
-
c.
|
21
|
-
p [:
|
20
|
+
c.walk(v, "/ctl/node/**", 0, 0) do |ents, err|
|
21
|
+
p [:nothing, ents, err]
|
22
22
|
end
|
23
23
|
|
24
24
|
# Error
|
25
|
-
c.
|
26
|
-
p [:
|
25
|
+
c.walk(v, "/nothere") do |ents, err|
|
26
|
+
p [:nothere, ents, err]
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
data/example/watch.rb
CHANGED
data/fraggle.gemspec
CHANGED
data/lib/fraggle/client.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
require 'fraggle/connection'
|
3
|
-
require 'logger'
|
4
3
|
|
5
4
|
module Fraggle
|
6
5
|
class Client
|
@@ -11,13 +10,10 @@ module Fraggle
|
|
11
10
|
class NoMoreAddrs < StandardError
|
12
11
|
end
|
13
12
|
|
14
|
-
|
15
|
-
DefaultLog.level = Logger::UNKNOWN
|
13
|
+
attr_reader :cn, :addrs
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
def initialize(cn, addrs, log=DefaultLog)
|
20
|
-
@cn, @addrs, @log = cn, addrs, log
|
15
|
+
def initialize(cn, addrs)
|
16
|
+
@cn, @addrs = cn, addrs
|
21
17
|
end
|
22
18
|
|
23
19
|
def addr
|
@@ -52,7 +48,7 @@ module Fraggle
|
|
52
48
|
idemp(req, &blk)
|
53
49
|
end
|
54
50
|
|
55
|
-
def
|
51
|
+
def _getdir(rev, path, offset, &blk)
|
56
52
|
req = Request.new
|
57
53
|
req.verb = GETDIR
|
58
54
|
req.rev = rev
|
@@ -62,7 +58,7 @@ module Fraggle
|
|
62
58
|
resend(req, &blk)
|
63
59
|
end
|
64
60
|
|
65
|
-
def
|
61
|
+
def _walk(rev, path, offset, &blk)
|
66
62
|
req = Request.new
|
67
63
|
req.verb = WALK
|
68
64
|
req.rev = rev
|
@@ -85,7 +81,9 @@ module Fraggle
|
|
85
81
|
req = Request.new
|
86
82
|
req.verb = REV
|
87
83
|
|
88
|
-
resend(req,
|
84
|
+
resend(req) do |v, _|
|
85
|
+
blk.call(v.rev)
|
86
|
+
end
|
89
87
|
end
|
90
88
|
|
91
89
|
def stat(rev, path, &blk)
|
@@ -97,21 +95,29 @@ module Fraggle
|
|
97
95
|
resend(req, &blk)
|
98
96
|
end
|
99
97
|
|
98
|
+
def access(secret, &blk)
|
99
|
+
req = Request.new
|
100
|
+
req.verb = ACCESS
|
101
|
+
req.value = secret
|
102
|
+
|
103
|
+
resend(req, &blk)
|
104
|
+
end
|
105
|
+
|
100
106
|
def watch(rev, path, &blk)
|
101
|
-
wait(rev, path
|
102
|
-
blk.call(e)
|
103
|
-
if
|
104
|
-
watch(
|
107
|
+
wait(rev, path) do |e, err|
|
108
|
+
blk.call(e, err)
|
109
|
+
if ! err
|
110
|
+
watch(e.rev+1, path, &blk)
|
105
111
|
end
|
106
112
|
end
|
107
113
|
end
|
108
114
|
|
109
|
-
def
|
110
|
-
all(:
|
115
|
+
def getdir(rev, path, off=0, lim=MaxInt64, ents=[], &blk)
|
116
|
+
all(:_getdir, rev, path, off, lim, ents, &blk)
|
111
117
|
end
|
112
118
|
|
113
|
-
def
|
114
|
-
all(:
|
119
|
+
def walk(rev, path, off=0, lim=MaxInt64, ents=[], &blk)
|
120
|
+
all(:_walk, rev, path, off, lim, ents, &blk)
|
115
121
|
end
|
116
122
|
|
117
123
|
def all(m, rev, path, off, lim, ents=[], &blk)
|
@@ -120,11 +126,10 @@ module Fraggle
|
|
120
126
|
return
|
121
127
|
end
|
122
128
|
|
123
|
-
__send__(m, rev, path, off) do |e|
|
124
|
-
case
|
129
|
+
__send__(m, rev, path, off) do |e, err|
|
130
|
+
case err && err.code
|
125
131
|
when nil
|
126
|
-
ents << e
|
127
|
-
all(m, rev, path, off+1, lim-1, ents, &blk)
|
132
|
+
all(m, rev, path, off+1, lim-1, ents << e, &blk)
|
128
133
|
when Fraggle::Response::Err::RANGE
|
129
134
|
blk.call(ents, nil)
|
130
135
|
else
|
@@ -136,56 +141,56 @@ module Fraggle
|
|
136
141
|
# Sends a request to the server. Returns the request with a new tag
|
137
142
|
# assigned.
|
138
143
|
def send(req, &blk)
|
139
|
-
cb = Proc.new do |e|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
144
|
+
cb = Proc.new do |e, err|
|
145
|
+
case err
|
146
|
+
when Connection::DisconnectedError
|
147
|
+
if cn.err?
|
148
|
+
reconnect!
|
149
|
+
end
|
145
150
|
end
|
146
|
-
|
147
|
-
blk.call(e)
|
151
|
+
blk.call(e, err)
|
148
152
|
end
|
149
153
|
|
150
|
-
log.debug("sending: #{req.inspect}")
|
151
154
|
cn.send_request(req, cb)
|
152
155
|
end
|
153
156
|
|
154
157
|
def resend(req, &blk)
|
155
|
-
|
156
|
-
|
158
|
+
send(req) do |e, err|
|
159
|
+
case err
|
160
|
+
when nil
|
161
|
+
blk.call(e, err)
|
162
|
+
when Connection::DisconnectedError
|
157
163
|
req.tag = nil
|
158
|
-
log.debug("resending: #{req.inspect}")
|
159
164
|
resend(req, &blk)
|
160
165
|
else
|
161
|
-
blk.call(e)
|
166
|
+
blk.call(e, err)
|
162
167
|
end
|
163
168
|
end
|
164
|
-
|
165
|
-
send(req, &cb)
|
166
169
|
end
|
167
170
|
|
168
171
|
def idemp(req, &blk)
|
169
|
-
|
170
|
-
|
172
|
+
send(req) do |e, err|
|
173
|
+
case err
|
174
|
+
when Connection::DisconnectedError
|
171
175
|
# If we're trying to update a value that isn't missing or that we're
|
172
176
|
# not trying to clobber, it's safe to retry. We can't idempotently
|
173
177
|
# update missing values because there may be a race with another
|
174
178
|
# client that sets and/or deletes the key during the time between your
|
175
179
|
# read and write.
|
176
|
-
req.
|
177
|
-
|
178
|
-
|
180
|
+
if (req.rev || 0) > 0
|
181
|
+
req.tag = nil
|
182
|
+
idemp(req, &blk)
|
183
|
+
else
|
184
|
+
blk.call(e, err)
|
185
|
+
end
|
186
|
+
when nil
|
187
|
+
blk.call(e, err)
|
179
188
|
end
|
180
|
-
|
181
|
-
blk.call(e)
|
182
189
|
end
|
183
|
-
|
184
|
-
send(req, &cb)
|
185
190
|
end
|
186
191
|
|
187
192
|
def reconnect!
|
188
|
-
if addr = @addrs.
|
193
|
+
if addr = @addrs.slice!(rand(@addrs.length))
|
189
194
|
reconnect(addr)
|
190
195
|
else
|
191
196
|
raise NoMoreAddrs
|
@@ -193,7 +198,6 @@ module Fraggle
|
|
193
198
|
end
|
194
199
|
|
195
200
|
def reconnect(addr)
|
196
|
-
log.warn("reconnecting to #{addr}")
|
197
201
|
host, port = addr.split(":")
|
198
202
|
@cn = EM.connect(host, port, Fraggle::Connection, addr)
|
199
203
|
end
|
data/lib/fraggle/connection.rb
CHANGED
@@ -4,21 +4,35 @@ module Fraggle
|
|
4
4
|
|
5
5
|
module Connection
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# Base class for all Connection errors
|
11
|
-
class Error < StandardError
|
12
|
-
attr_accessor :req
|
13
|
-
|
7
|
+
# Raised when a request is invalid
|
8
|
+
class SendError < StandardError
|
14
9
|
def initialize(req, msg=nil)
|
15
10
|
@req = req
|
16
11
|
super(msg)
|
17
12
|
end
|
18
13
|
end
|
19
14
|
|
20
|
-
|
21
|
-
|
15
|
+
class DisconnectedError < StandardError
|
16
|
+
def ==(o)
|
17
|
+
return false if ! o.kind_of?(self.class)
|
18
|
+
message == o.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ResponseError < StandardError
|
23
|
+
attr_reader :code
|
24
|
+
|
25
|
+
alias :detail :message
|
26
|
+
|
27
|
+
def initialize(res)
|
28
|
+
@code = res.err_code
|
29
|
+
super("#{res.name_for(Response::Err, code)}: #{res.err_detail}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(o)
|
33
|
+
return false if ! o.kind_of?(self.class)
|
34
|
+
code == o.code && message == o.message
|
35
|
+
end
|
22
36
|
end
|
23
37
|
|
24
38
|
|
@@ -33,10 +47,10 @@ module Fraggle
|
|
33
47
|
|
34
48
|
while @buf.length > 0
|
35
49
|
if @len && @buf.length >= @len
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
50
|
+
bytes = @buf.slice!(0, @len)
|
51
|
+
@len = nil
|
52
|
+
res = Response.decode(bytes)
|
53
|
+
receive_response(res)
|
40
54
|
elsif @buf.length >= 4
|
41
55
|
bytes = @buf.slice!(0, 4)
|
42
56
|
@len = bytes.unpack("N")[0]
|
@@ -51,7 +65,11 @@ module Fraggle
|
|
51
65
|
return if err?
|
52
66
|
req, blk = @cb.delete(res.tag)
|
53
67
|
return if ! blk
|
54
|
-
|
68
|
+
if res.err_code
|
69
|
+
blk.call(nil, ResponseError.new(res))
|
70
|
+
else
|
71
|
+
blk.call(res, nil)
|
72
|
+
end
|
55
73
|
end
|
56
74
|
|
57
75
|
def send_request(req, blk)
|
@@ -60,7 +78,7 @@ module Fraggle
|
|
60
78
|
end
|
61
79
|
|
62
80
|
if err?
|
63
|
-
next_tick { blk.call(
|
81
|
+
next_tick { blk.call(nil, DisconnectedError.new(self.addr)) }
|
64
82
|
return req
|
65
83
|
end
|
66
84
|
|
@@ -85,7 +103,7 @@ module Fraggle
|
|
85
103
|
def unbind
|
86
104
|
@err = true
|
87
105
|
@cb.values.each do |_, blk|
|
88
|
-
blk.call(
|
106
|
+
blk.call(nil, DisconnectedError.new(self.addr))
|
89
107
|
end
|
90
108
|
end
|
91
109
|
|
data/lib/fraggle/msg.pb.rb
CHANGED
data/lib/fraggle/response.rb
CHANGED
@@ -6,8 +6,6 @@ module Fraggle
|
|
6
6
|
SET = 4
|
7
7
|
DEL = 8
|
8
8
|
|
9
|
-
attr_accessor :disconnected
|
10
|
-
|
11
9
|
def set?
|
12
10
|
return false if !flags
|
13
11
|
(flags & SET) > 0
|
@@ -26,9 +24,5 @@ module Fraggle
|
|
26
24
|
err_code.nil?
|
27
25
|
end
|
28
26
|
|
29
|
-
def disconnected?
|
30
|
-
!!@disconnected
|
31
|
-
end
|
32
|
-
|
33
27
|
end
|
34
28
|
end
|
data/lib/fraggle/version.rb
CHANGED
data/test/fraggle_client_test.rb
CHANGED
@@ -23,7 +23,7 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
23
23
|
@c.__send__(:initialize, cn, @addrs)
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
26
|
+
def test_response_error
|
27
27
|
req, log = request(V::REV)
|
28
28
|
|
29
29
|
c.send(req, &log)
|
@@ -31,7 +31,7 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
31
31
|
res = reply(req.tag, :err_code => E::OTHER)
|
32
32
|
c.cn.receive_response(res)
|
33
33
|
|
34
|
-
assert_equal [res], log.valid
|
34
|
+
assert_equal [[nil, C::ResponseError.new(res)]], log.valid
|
35
35
|
end
|
36
36
|
|
37
37
|
def test_reconnect_without_pending_requests
|
@@ -50,7 +50,7 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
50
50
|
assert exp.include?(c.cn.addr), "#{c.cn.addr.inspect} not in #{exp.inspect}"
|
51
51
|
|
52
52
|
# If the client can handle an error, it should not mention it to the user.
|
53
|
-
assert_equal [
|
53
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], log.valid
|
54
54
|
end
|
55
55
|
|
56
56
|
def test_reconnect_with_pending_request
|
@@ -68,7 +68,7 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
68
68
|
|
69
69
|
assert exp.include?(c.cn.addr), "#{c.cn.addr.inspect} not in #{exp.inspect}"
|
70
70
|
|
71
|
-
assert_equal [
|
71
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], log.valid
|
72
72
|
end
|
73
73
|
|
74
74
|
def test_reconnect_with_multiple_pending_requests
|
@@ -93,8 +93,8 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
93
93
|
assert_equal exp.length - 1, c.addrs.length
|
94
94
|
|
95
95
|
# If the client can handle an error, it should not mention it to the user.
|
96
|
-
assert_equal [
|
97
|
-
assert_equal [
|
96
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], loga.valid
|
97
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], logb.valid
|
98
98
|
end
|
99
99
|
|
100
100
|
def test_resend_pending_requests
|
@@ -120,8 +120,8 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
120
120
|
|
121
121
|
assert_equal [one], c.cn.sent
|
122
122
|
|
123
|
-
assert_equal [
|
124
|
-
assert_equal [
|
123
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], zlog.valid
|
124
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], nlog.valid
|
125
125
|
end
|
126
126
|
|
127
127
|
###
|
@@ -181,7 +181,7 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
181
181
|
:offset => 0
|
182
182
|
}
|
183
183
|
|
184
|
-
assert_verb exp, :
|
184
|
+
assert_verb exp, :_getdir, 0, "/foo", 0
|
185
185
|
end
|
186
186
|
|
187
187
|
def test_rev
|
@@ -210,7 +210,7 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
210
210
|
:offset => 0
|
211
211
|
}
|
212
212
|
|
213
|
-
assert_verb exp, :
|
213
|
+
assert_verb exp, :_walk, 0, "/foo/*", 0
|
214
214
|
end
|
215
215
|
|
216
216
|
def test_wait
|
@@ -223,4 +223,13 @@ class FraggleClientTest < Test::Unit::TestCase
|
|
223
223
|
assert_verb exp, :wait, 0, "/foo/*"
|
224
224
|
end
|
225
225
|
|
226
|
+
def test_wait
|
227
|
+
exp = {
|
228
|
+
:verb => V::ACCESS,
|
229
|
+
:value => "abc"
|
230
|
+
}
|
231
|
+
|
232
|
+
assert_verb exp, :access, "abc"
|
233
|
+
end
|
234
|
+
|
226
235
|
end
|
@@ -5,20 +5,16 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
attr_reader :cn
|
7
7
|
|
8
|
-
def nop(attrs={})
|
9
|
-
request(V::NOP, attrs)
|
10
|
-
end
|
11
|
-
|
12
8
|
def setup
|
13
9
|
@cn = TestConn.new("127.0.0.1:0")
|
14
10
|
end
|
15
11
|
|
16
12
|
def test_tagging
|
17
|
-
req, _ =
|
13
|
+
req, _ = request(V::REV)
|
18
14
|
assert_equal 0, cn.send_request(req, _).tag
|
19
|
-
req, _ =
|
15
|
+
req, _ = request(V::REV)
|
20
16
|
assert_equal 1, cn.send_request(req, _).tag
|
21
|
-
req, _ =
|
17
|
+
req, _ = request(V::REV)
|
22
18
|
assert_equal 2, cn.send_request(req, _).tag
|
23
19
|
end
|
24
20
|
|
@@ -30,7 +26,7 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
30
26
|
res = reply(req.tag)
|
31
27
|
cn.receive_response(res)
|
32
28
|
|
33
|
-
assert_equal [res], log.valid
|
29
|
+
assert_equal [[res, nil]], log.valid
|
34
30
|
end
|
35
31
|
|
36
32
|
def test_error
|
@@ -41,7 +37,8 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
41
37
|
res = reply(req.tag, :err_code => E::OTHER)
|
42
38
|
cn.receive_response(res)
|
43
39
|
|
44
|
-
|
40
|
+
err = C::ResponseError.new(res)
|
41
|
+
assert_equal [[nil, err]], log.valid
|
45
42
|
end
|
46
43
|
|
47
44
|
def test_invalid_tag
|
@@ -63,7 +60,7 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
63
60
|
# This should be ignored
|
64
61
|
cn.receive_response(res)
|
65
62
|
|
66
|
-
assert_equal [res], log.valid
|
63
|
+
assert_equal [[res, nil]], log.valid
|
67
64
|
end
|
68
65
|
|
69
66
|
def test_error_deletes_callback
|
@@ -77,14 +74,14 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
77
74
|
# This should be ignored
|
78
75
|
cn.receive_response(res)
|
79
76
|
|
80
|
-
assert_equal [res], log.valid
|
77
|
+
assert_equal [[nil, C::ResponseError.new(res)]], log.valid
|
81
78
|
end
|
82
79
|
|
83
80
|
def test_cannot_reuse_sent_request
|
84
81
|
req, _ = request(V::REV)
|
85
82
|
cn.send_request(req, _)
|
86
83
|
|
87
|
-
assert_raises
|
84
|
+
assert_raises C::SendError do
|
88
85
|
cn.send_request(req, _)
|
89
86
|
end
|
90
87
|
end
|
@@ -99,9 +96,9 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
99
96
|
|
100
97
|
cn.unbind
|
101
98
|
|
102
|
-
assert_equal [
|
103
|
-
assert_equal [
|
104
|
-
assert_equal [
|
99
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], al.valid
|
100
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], bl.valid
|
101
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], cl.valid
|
105
102
|
end
|
106
103
|
|
107
104
|
def test_send_when_disconnected
|
@@ -114,6 +111,6 @@ class FraggleTransactionTest < Test::Unit::TestCase
|
|
114
111
|
|
115
112
|
cn.tick!
|
116
113
|
|
117
|
-
assert_equal [
|
114
|
+
assert_equal [[nil, C::DisconnectedError.new("127.0.0.1:0")]], log.valid
|
118
115
|
end
|
119
116
|
end
|
data/test/helper.rb
CHANGED
@@ -46,6 +46,7 @@ end
|
|
46
46
|
|
47
47
|
class Test::Unit::TestCase
|
48
48
|
|
49
|
+
C = Fraggle::Connection
|
49
50
|
V = Fraggle::Request::Verb
|
50
51
|
F = Fraggle::Response
|
51
52
|
E = Fraggle::Response::Err
|
@@ -57,13 +58,13 @@ class Test::Unit::TestCase
|
|
57
58
|
@valid = []
|
58
59
|
end
|
59
60
|
|
60
|
-
def call(e)
|
61
|
-
@valid << e
|
61
|
+
def call(e, err)
|
62
|
+
@valid << [e, err]
|
62
63
|
end
|
63
64
|
|
64
65
|
def to_proc
|
65
66
|
me = self
|
66
|
-
Proc.new {|e| me.call(e) }
|
67
|
+
Proc.new {|e, err| me.call(e, err) }
|
67
68
|
end
|
68
69
|
end
|
69
70
|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fraggle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1923831853
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 4
|
8
8
|
- 0
|
9
9
|
- 0
|
10
10
|
- pre
|
11
|
-
-
|
12
|
-
version: 4.0.0.pre.
|
11
|
+
- 2
|
12
|
+
version: 4.0.0.pre.2
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Blake Mizerany
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2011-05-
|
20
|
+
date: 2011-05-05 00:00:00 -07:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -63,6 +63,20 @@ dependencies:
|
|
63
63
|
version: "0"
|
64
64
|
type: :development
|
65
65
|
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: statsample
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
66
80
|
description: An EventMachine Client for Doozer
|
67
81
|
email:
|
68
82
|
- blake.mizerany@gmail.com
|
@@ -78,10 +92,10 @@ files:
|
|
78
92
|
- LICENSE
|
79
93
|
- README.md
|
80
94
|
- Rakefile
|
81
|
-
- bench/
|
82
|
-
- example/
|
95
|
+
- bench/gs.rb
|
96
|
+
- example/getdir.rb
|
83
97
|
- example/readme.rb
|
84
|
-
- example/
|
98
|
+
- example/walk.rb
|
85
99
|
- example/watch.rb
|
86
100
|
- fraggle.gemspec
|
87
101
|
- lib/fraggle.rb
|
data/bench/gets.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'eventmachine'
|
3
|
-
require 'fraggle'
|
4
|
-
|
5
|
-
reqs = 0
|
6
|
-
|
7
|
-
def rget(c, path, rev, &blk)
|
8
|
-
c.get(path, rev) do |e|
|
9
|
-
blk.call
|
10
|
-
rget(c, path, rev, &blk)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
EM.run do
|
15
|
-
c = Fraggle.connect
|
16
|
-
|
17
|
-
EM.add_timer(1) do
|
18
|
-
# The primer is done. Reset `reqs` and do it for real.
|
19
|
-
reqs = 0
|
20
|
-
EM.add_timer(1) do
|
21
|
-
EM.stop_event_loop
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
c.rev do |v|
|
26
|
-
rget(c, "/ctl/cal/0", v.rev) do
|
27
|
-
reqs += 1
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
puts "Result (GET): #{reqs}/sec"
|