fraggle-spanx 4.0.1.1

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,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