fraggle 4.0.0.pre.1 → 4.0.0.pre.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.
- 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"
|