fraggle-spanx 4.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.prof*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fraggle.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Blake Mizerany, Keith Rarick, Chris Moos
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,223 @@
1
+ # Fraggle
2
+ ## The current gem is v4.0.0.pre.1 is compatible with Doozer 0.6
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.
5
+
6
+ **An EventMachine based Doozer client**
7
+
8
+ ## Install
9
+
10
+ (For pre-releases, use `--pre`)
11
+
12
+ $ gem install fraggle
13
+
14
+ ## Use
15
+
16
+ *Connecting to a cluster*
17
+
18
+ Use `Fraggle.connect`. It takes an optional [doozer uri][] (String). If no
19
+ parameters are given, it will use the DOOZER_URI` environment variable if
20
+ present, otherwise it will default to the uri containing the default doozer
21
+ addresses with IP 127.0.0.1 and ports 8046, 8041, 8042, 8043.
22
+
23
+ *simple example*
24
+
25
+ require 'rubygems'
26
+ require 'eventmachine'
27
+ require 'fraggle'
28
+
29
+ EM.run do
30
+ # In the event of a lost connection, fraggle will attempt
31
+ # other doozers until one accepts or it runs out of options; A NoAddrs
32
+ # exception will be raised if that later happens.
33
+
34
+ Fraggle.connect do |c, err|
35
+ if err
36
+ raise err.message
37
+ end
38
+
39
+ c.rev do |v|
40
+ c.get(v, "/foo") do |e, err|
41
+ if err
42
+ err.code # => nil
43
+ err.detail # => nil
44
+ else
45
+ e.value # => nil
46
+ e.rev # => 0
47
+ e.missing? # => true
48
+ end
49
+
50
+ p [:get, e, err]
51
+ end
52
+
53
+ ## Obtain the current revision the store is at and watch from then on for
54
+ ## any SET or DEL to /foo.
55
+ c.wait(v, "/foo") do |e, err|
56
+ # The event has:
57
+ # ------------------------
58
+ if err
59
+ err.code # => nil
60
+ err.detail # => nil
61
+ else
62
+ e.path # => "/foo"
63
+ e.value # => "zomg!"
64
+ e.rev # => 123
65
+ e.set? # => true
66
+ e.del? # => false
67
+ end
68
+
69
+ p [:wait, e, err]
70
+ end
71
+ end
72
+
73
+ ## Setting a key (this will trigger the watch above)
74
+ f = Proc.new do |e, err|
75
+ p [:e, e, err]
76
+
77
+ # This case statement is undesirable. We're working
78
+ # on better error handling.
79
+ case (err && err.code rescue err)
80
+ when Fraggle::Connection::DisconnectedError
81
+ # Fraggle (for now) does not attempt a non-idempotent request. This means
82
+ # Fraggle will hand off the error to the user if there is a SET or DEL
83
+ # with rev 0 (missing) and delete it during the time we may be
84
+ # disconnected.
85
+ #
86
+ # In this scenario, there are no other clients that can exist that will
87
+ # attempt to set this "lock" if it's missing then delete it. It is safe
88
+ # for us to resend the request if we were disconnected from the previous
89
+ # server before a response.
90
+ #
91
+ # See High-Availability in the README for more information about this.
92
+ #
93
+ c.set(0, "/foo", "zomg!", &f)
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: " + err.inspect
101
+ end
102
+ end
103
+
104
+ c.set(0, "/foo", "zomg!", &f)
105
+ end
106
+ end
107
+
108
+ ## Consistency
109
+
110
+ Fraggle read commands take a `rev`. If no rev is given, Doozer will reply with
111
+ the most up-to-date data. If you need to do multiple reads at certain
112
+ point in time for consistency, use the `rev` command.
113
+
114
+ c.rev do |v|
115
+ c.get(v, "/a") { ... }
116
+ c.get(v, "/b") { ... }
117
+ c.get(v, "/c") { ... }
118
+ end
119
+
120
+ This also means you can go back in time or into the future!
121
+
122
+ # This will not yield until the data store is at revision 100,000
123
+ c.get(100_000, "/a") { ... }
124
+
125
+ NOTE: Doozer's data store is a [persistent data structure][pd]. You can reference the
126
+ stores history as far back as it is configured to hold it. The default is
127
+ 360,000 revisions. See [data model][] for more information.
128
+
129
+ ## High Availability
130
+
131
+ Fraggle has mechanisms to gracefully deal with connection loss. They are:
132
+
133
+ *Resend / Connection loss*
134
+
135
+ When a connection is lost and Fraggle successfully reconnects to another
136
+ Doozer node, Fraggle will resend most pending requests to the new connection.
137
+ This means you will not miss events; Even events that happened while you were
138
+ disconnected! All read commands will pick up where they left off. This is
139
+ valuable to understand because it means you don't need to code for failure on
140
+ reads; Fraggle gracefully handles it for you.
141
+
142
+ Write commands will be resent if their `rev` is greater than 0. These are
143
+ idempotent requests. A rev of 0 will cause that request's error
144
+ callback to be invoked with a Fraggle::Connection::Disconnected response.
145
+ You will have to handle these yourself because Fraggle cannot know whether or
146
+ not it's safe to retry on your behalf.
147
+
148
+ **attempt**
149
+
150
+ Before fraggle will attempt a new address after connection loss, it calls the
151
+ block given to `Fraggle::Client#attempt`. If the block returns `false`,
152
+ Fraggle will not attempt that address or anymore. The block is called with on
153
+ parameter `addr`, which is the address being attempted.
154
+
155
+ Example:
156
+
157
+ c = Fraggle.connect
158
+
159
+ c.attempt do |addr|
160
+ addr =~ /^127\.*$/ # don't connect to localhost doozers
161
+ end
162
+
163
+ The default `attempt` is `Proc.new {|_| true }`
164
+
165
+ ## Commands
166
+
167
+ Each command below behaves according to the [proto spec][], respectively.
168
+ Their `blk`s are called with two parameters, a `Fraggle::Response` as the first
169
+ or a `Fraggle::Connection::ResponseError` as the second if a response is
170
+ returned from the server.
171
+
172
+ `set(rev, path, value, &blk)`
173
+
174
+ `del(rev, path, &blk)`
175
+
176
+ `get(rev, path, &blk)`
177
+
178
+ `wait(rev, path, &blk)`
179
+
180
+ `rev(&blk)`
181
+
182
+ `stat(rev, path, &blk)`
183
+
184
+ ## Sugar commands
185
+
186
+ `watch(rev, path, &blk)`
187
+
188
+ Watches `path` (a glob pattern) for changes, from `rev` in history on. Its
189
+ `blk` is called with a `Fraggle::Response` for each event.
190
+
191
+ `getdir(rev, path, off=0, lim=MaxInt64, ents=[], &blk)`
192
+
193
+ Behaves like `getdir` but collects `ents`, starting at `off` until all or `lim`
194
+ entries are read. When done, `blk` is called with the result (an `Array`) as the
195
+ first parameter or a `Fraggle::Connection::Response` as the second. Depending
196
+ on the response, one or the other will be set and the other with be `nil`.
197
+
198
+ `walk(rev, path, off=0, lim=MaxInt64, ents=[], &blk)`
199
+
200
+ Like `getdir`, but but path is a glob pattern and each result contains a `path`,
201
+ `value`, and `rev`.
202
+
203
+ ## Dev
204
+
205
+ **Clone**
206
+
207
+ $ git clone http://github.com/ha/fraggle.git
208
+
209
+ **Test**
210
+
211
+ $ gem install turn
212
+
213
+ $ turn
214
+
215
+ **Mailing List**
216
+
217
+ Please join the Doozer mailing list for help:
218
+ http://groups.google.com/forum/#!forum/doozer
219
+
220
+ [data model]: https://github.com/ha/doozerd/blob/master/doc/data-model.md
221
+ [doozer uri]: https://github.com/ha/doozerd/blob/master/doc/uri.md
222
+ [proto spec]: https://github.com/ha/doozerd/blob/master/doc/proto.md
223
+ [pd]: http://en.wikipedia.org/wiki/Persistent_data_structure
@@ -0,0 +1,17 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ HOME = ENV["HOME"]
5
+ PBDIR = HOME+"/src/doozerd/src/pkg/server"
6
+
7
+ namespace :proto do
8
+ task :update do
9
+ ENV["BEEFCAKE_NAMESPACE"] = "Fraggle"
10
+ sh(
11
+ "protoc",
12
+ "--beefcake_out", "lib/fraggle",
13
+ "-I", PBDIR,
14
+ PBDIR+"/msg.proto"
15
+ )
16
+ end
17
+ end
@@ -0,0 +1,87 @@
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("gs <[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
+ Fraggle.connect do |c, err|
23
+ if err
24
+ raise err.message
25
+ end
26
+
27
+ sent = 0
28
+ received = 0
29
+ start = Time.now
30
+
31
+ f = Proc.new do |r, err|
32
+ if err
33
+ p [:err, err]
34
+ next
35
+ end
36
+
37
+ received_at = Time.now
38
+ received +=1
39
+ latency = received_at - sent_at
40
+
41
+ latencies << latency
42
+ if verbose
43
+ $stdout.puts("received=#{received} ok=#{r.ok?} rev=#{r.rev} latency=#{latency}")
44
+ elsif (received % 10 == 0)
45
+ $stdout.print(".")
46
+ end
47
+ if (received == total)
48
+ EM.stop
49
+ elapsed = Time.now - start
50
+ vector = latencies.to_scale
51
+ $stdout.puts
52
+ $stdout.puts("total=#{total}")
53
+ $stdout.puts("elapsed=#{elapsed}")
54
+ $stdout.puts("rate=#{total / elapsed}")
55
+ $stdout.puts("mean=#{vector.mean}")
56
+ $stdout.puts("sd=#{vector.sd}")
57
+ $stdout.puts("perc90=#{vector.percentil(90)}")
58
+ $stdout.puts("perc99=#{vector.percentil(99)}")
59
+ $stdout.puts("max=#{vector.max}")
60
+ end
61
+ end
62
+
63
+ tick = Proc.new do
64
+ if (sent == total)
65
+ # done sending
66
+ elsif ((sent - received) < width)
67
+ # pipe open
68
+ sent_at = Time.now
69
+ sent += 1
70
+ if verbose
71
+ $stdout.puts("sent=#{sent}")
72
+ end
73
+
74
+ case op
75
+ when :get
76
+ c.get(nil, "/processes/#{sent}", &f)
77
+ when :set
78
+ c.set(Fraggle::Clobber, "/processes/#{sent}", "1", &f)
79
+ end
80
+ else
81
+ # pipe closed
82
+ end
83
+ EM.next_tick(&tick)
84
+ end
85
+ tick.call
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'fraggle'
3
+
4
+ EM.run do
5
+ c = Fraggle.connect do |c, err|
6
+ c.rev do |v|
7
+ # Valid
8
+ req = c.getdir(v, "/ctl/node") do |ents, err|
9
+ if err
10
+ p [:err, err]
11
+ else
12
+ ents.each do |e|
13
+ puts File.join(req.path, e.path)
14
+ end
15
+ end
16
+ end
17
+
18
+ # Limit 0 return nothing
19
+ c.getdir(v, "/ctl/node", 0, 0) do |ents, err|
20
+ p [:ret, ents, err]
21
+ end
22
+
23
+ # Error
24
+ c.getdir(v, "/nothere") do |ents, err|
25
+ p [:ret, ents, err]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,82 @@
1
+ require 'rubygems'
2
+ require 'eventmachine'
3
+ require 'fraggle'
4
+
5
+ EM.run do
6
+ # In the event of a lost connection, fraggle will attempt
7
+ # other doozers until one accepts or it runs out of options; A NoAddrs
8
+ # exception will be raised if that later happens.
9
+
10
+ Fraggle.connect do |c, err|
11
+ if err
12
+ raise err.message
13
+ end
14
+
15
+ c.rev do |v|
16
+ c.get(v, "/foo") do |e, err|
17
+ if err
18
+ err.code # => nil
19
+ err.detail # => nil
20
+ else
21
+ e.value # => nil
22
+ e.rev # => 0
23
+ e.missing? # => true
24
+ end
25
+
26
+ p [:get, e, err]
27
+ end
28
+
29
+ ## Obtain the current revision the store is at and watch from then on for
30
+ ## any SET or DEL to /foo.
31
+ c.wait(v, "/foo") do |e, err|
32
+ # The event has:
33
+ # ------------------------
34
+ if err
35
+ err.code # => nil
36
+ err.detail # => nil
37
+ else
38
+ e.path # => "/foo"
39
+ e.value # => "zomg!"
40
+ e.rev # => 123
41
+ e.set? # => true
42
+ e.del? # => false
43
+ end
44
+
45
+ p [:wait, e, err]
46
+ end
47
+ end
48
+
49
+ ## Setting a key (this will trigger the watch above)
50
+ f = Proc.new do |e, err|
51
+ p [:e, e, err]
52
+
53
+ # This case statement is undesirable. We're working
54
+ # on better error handling.
55
+ case (err && err.code rescue err)
56
+ when Fraggle::Connection::DisconnectedError
57
+ # Fraggle (for now) does not attempt a non-idempotent request. This means
58
+ # Fraggle will hand off the error to the user if there is a SET or DEL
59
+ # with rev 0 (missing) and delete it during the time we may be
60
+ # disconnected.
61
+ #
62
+ # In this scenario, there are no other clients that can exist that will
63
+ # attempt to set this "lock" if it's missing then delete it. It is safe
64
+ # for us to resend the request if we were disconnected from the previous
65
+ # server before a response.
66
+ #
67
+ # See High-Availability in the README for more information about this.
68
+ #
69
+ c.set(0, "/foo", "zomg!", &f)
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: " + err.inspect
77
+ end
78
+ end
79
+
80
+ c.set(0, "/foo", "zomg!", &f)
81
+ end
82
+ end