Class: MogileFS::Backend
- Inherits:
-
Object
- Object
- MogileFS::Backend
- Defined in:
- lib/mogilefs/backend.rb
Overview
MogileFS::Backend communicates with the MogileFS trackers.
Constant Summary collapse
- BACKEND_ERRORS =
{}
Instance Attribute Summary collapse
-
#lasterr ⇒ Object
readonly
The last error.
-
#lasterrstr ⇒ Object
readonly
The string attached to the last error.
Class Method Summary collapse
-
.add_command(*names) ⇒ Object
Adds MogileFS commands
names
. -
.add_error(err_snake) ⇒ Object
this converts an error code from a mogilefsd tracker to an exception:.
Instance Method Summary collapse
-
#do_request(cmd, args) ⇒ Object
Performs the
cmd
request withargs
. -
#error(err_snake) ⇒ Object
this converts an error code from a mogilefsd tracker to an exception Most of these exceptions should already be defined, but since the MogileFS server code is liable to change and we may not always be able to keep up with the changes.
-
#initialize(args) ⇒ Backend
constructor
Creates a new MogileFS::Backend.
-
#make_request(cmd, args) ⇒ Object
Makes a new request string for
cmd
andargs
. -
#parse_response(line) ⇒ Object
Turns the
line
response from the server into a Hash of options, an error, or raises, as appropriate. -
#readable? ⇒ Boolean
Raises if the socket does not become readable in @timeout seconds.
-
#shutdown ⇒ Object
Closes this backend’s socket.
-
#socket ⇒ Object
Returns a socket connected to a MogileFS tracker.
-
#url_decode(str) ⇒ Object
Turns a url params string into a Hash.
-
#url_encode(params) ⇒ Object
Turns a Hash (or Array of pairs) into a url params string.
-
#url_escape(str) ⇒ Object
Ruby 1.8.
-
#url_unescape(str) ⇒ Object
Unescapes naughty URL characters.
Constructor Details
#initialize(args) ⇒ Backend
Creates a new MogileFS::Backend.
:hosts is a required argument and must be an Array containing one or more ‘hostname:port’ pairs as Strings.
:timeout adjusts the request timeout before an error is returned.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/mogilefs/backend.rb', line 55 def initialize(args) @hosts = args[:hosts] raise ArgumentError, "must specify at least one host" unless @hosts raise ArgumentError, "must specify at least one host" if @hosts.empty? unless @hosts == @hosts.select { |h| h =~ /:\d+$/ } then raise ArgumentError, ":hosts must be in 'host:port' form" end @mutex = Mutex.new @timeout = args[:timeout] || 3 @socket = nil @lasterr = nil @lasterrstr = nil @dead = {} end |
Instance Attribute Details
#lasterr ⇒ Object (readonly)
The last error
40 41 42 |
# File 'lib/mogilefs/backend.rb', line 40 def lasterr @lasterr end |
#lasterrstr ⇒ Object (readonly)
The string attached to the last error
45 46 47 |
# File 'lib/mogilefs/backend.rb', line 45 def lasterrstr @lasterrstr end |
Class Method Details
.add_command(*names) ⇒ Object
Adds MogileFS commands names
.
13 14 15 16 17 18 19 |
# File 'lib/mogilefs/backend.rb', line 13 def self.add_command(*names) names.each do |name| define_method name do |*args| do_request name, args.first || {} end end end |
.add_error(err_snake) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/mogilefs/backend.rb', line 29 def self.add_error(err_snake) err_camel = err_snake.gsub(/(?:^|_)([a-z])/) { $1.upcase } << 'Error' unless self.const_defined?(err_camel) self.class_eval("class #{err_camel} < MogileFS::Error; end") end BACKEND_ERRORS[err_snake] = self.const_get(err_camel) end |
Instance Method Details
#do_request(cmd, args) ⇒ Object
Performs the cmd
request with args
.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/mogilefs/backend.rb', line 151 def do_request(cmd, args) @mutex.synchronize do request = make_request cmd, args begin bytes_sent = socket.send request, 0 rescue SystemCallError shutdown raise MogileFS::UnreachableBackendError end unless bytes_sent == request.length then raise MogileFS::RequestTruncatedError, "request truncated (sent #{bytes_sent} expected #{request.length})" end readable? parse_response(socket.gets) end end |
#error(err_snake) ⇒ Object
this converts an error code from a mogilefsd tracker to an exception Most of these exceptions should already be defined, but since the MogileFS server code is liable to change and we may not always be able to keep up with the changes
184 185 186 |
# File 'lib/mogilefs/backend.rb', line 184 def error(err_snake) BACKEND_ERRORS[err_snake] || self.class.add_error(err_snake) end |
#make_request(cmd, args) ⇒ Object
Makes a new request string for cmd
and args
.
176 177 178 |
# File 'lib/mogilefs/backend.rb', line 176 def make_request(cmd, args) "#{cmd} #{url_encode args}\r\n" end |
#parse_response(line) ⇒ Object
Turns the line
response from the server into a Hash of options, an error, or raises, as appropriate.
192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/mogilefs/backend.rb', line 192 def parse_response(line) if line =~ /^ERR\s+(\w+)\s*(.*)/ then @lasterr = $1 @lasterrstr = $2 ? url_unescape($2) : nil raise error(@lasterr) return nil end return url_decode($1) if line =~ /^OK\s+\d*\s*(\S*)/ raise MogileFS::InvalidResponseError, "Invalid response from server: #{line.inspect}" end |
#readable? ⇒ Boolean
Raises if the socket does not become readable in @timeout seconds.
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/mogilefs/backend.rb', line 209 def readable? timeleft = @timeout peer = nil loop do t0 = Time.now found = IO.select([socket], nil, nil, timeleft) return true if found && found[0] timeleft -= (Time.now - t0) if timeleft < 0 peer = @socket ? "#{@socket.mogilefs_peername} " : nil # we DO NOT want the response we timed out waiting for, to crop up later # on, on the same socket, intersperesed with a subsequent request! so, # we close the socket if it times out like this shutdown raise MogileFS::UnreadableSocketError, "#{peer}never became readable" break end shutdown end false end |
#shutdown ⇒ Object
Closes this backend’s socket.
75 76 77 78 79 80 |
# File 'lib/mogilefs/backend.rb', line 75 def shutdown if @socket @socket.close rescue nil # ignore errors @socket = nil end end |
#socket ⇒ Object
Returns a socket connected to a MogileFS tracker.
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/mogilefs/backend.rb', line 236 def socket return @socket if @socket and not @socket.closed? now = Time.now @hosts.sort_by { rand(3) - 1 }.each do |host| next if @dead.include? host and @dead[host] > now - 5 begin @socket = Socket.mogilefs_new(*(host.split(/:/) << @timeout)) rescue SystemCallError, MogileFS::Timeout @dead[host] = now next end return @socket end raise MogileFS::UnreachableBackendError end |
#url_decode(str) ⇒ Object
Turns a url params string into a Hash.
260 261 262 263 264 |
# File 'lib/mogilefs/backend.rb', line 260 def url_decode(str) Hash[*(str.split(/&/).map { |pair| pair.split(/=/, 2).map { |x| url_unescape(x) } } ).flatten] end |
#url_encode(params) ⇒ Object
Turns a Hash (or Array of pairs) into a url params string.
269 270 271 272 273 |
# File 'lib/mogilefs/backend.rb', line 269 def url_encode(params) params.map do |k,v| "#{url_escape k.to_s}=#{url_escape v.to_s}" end.join("&") end |
#url_escape(str) ⇒ Object
Ruby 1.8
282 283 284 |
# File 'lib/mogilefs/backend.rb', line 282 def url_escape(str) str.gsub(/([^\w\,\-.\/\\\: ])/) { "%%%02x" % $1.ord }.tr(' ', '+') end |
#url_unescape(str) ⇒ Object
Unescapes naughty URL characters.
290 291 292 |
# File 'lib/mogilefs/backend.rb', line 290 def url_unescape(str) str.gsub(/%([a-f0-9][a-f0-9])/i) { [$1.to_i(16)].pack 'C' }.tr('+', ' ') end |