Class: Errdb

Inherits:
Object
  • Object
show all
Defined in:
lib/errdb.rb,
lib/errdb/version.rb

Overview

A Ruby client library for errdb.

Defined Under Namespace

Classes: BufferedIO, ErrdbError, Server

Constant Summary collapse

DEFAULT_OPTIONS =

Default options for the cache object.

{
  :readonly     => true,
  :timeout      => 5,
  :logger       => nil,
  :no_reply     => false,
  :check_size   => true,
}
DEFAULT_PORT =

Default errdb port.

7272
VERSION =
"3.0"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Errdb

Accepts a list of servers and a list of opts. servers may be omitted. See servers= for acceptable server list arguments.

Valid options for opts are:

[:namespace]    Prepends this value to all keys added or retrieved.
[:readonly]     Raises an exception on cache writes when true.
[:timeout]      Time to use as the socket read timeout.  Defaults to 0.5 sec,
                set to nil to disable timeouts.
[:logger]       Logger to use for info/debug output, defaults to nil
[:no_reply]     Don't bother looking for a reply for write operations (i.e. they
                become 'fire and forget'), errdb 1.2.5 and later only, speeds up
                set/add/delete/incr/decr significantly.
[:check_size]   Raises a ErrdbError if the value to be set is greater than 1 MB, which
                is the maximum key size for the standard errdb server.  Defaults to true.
[:autofix_keys] If a key is longer than 250 characters or contains spaces,
                use an SHA1 hash instead, to prevent collisions on truncated keys.

Other options are ignored.



91
92
93
94
95
96
97
98
99
# File 'lib/errdb.rb', line 91

def initialize(options = {})
  @host = options[:host] || "127.0.0.1"
  @port = (options[:port] || DEFAULT_PORT).to_i
  @readonly     = options[:readonly] || true
  @timeout = (options[:timeout] || 5).to_f
  @logger       = options[:logger]
  @server = Server.new(self, @host, @port)
  logger.info { "errdb-client #{VERSION} #{server}" } if logger
end

Instance Attribute Details

#hostObject (readonly)

The server this client talks to. Play at your own peril.



52
53
54
# File 'lib/errdb.rb', line 52

def host
  @host
end

#loggerObject (readonly)

Log debug/info/warn/error to the given Logger, defaults to nil.



69
70
71
# File 'lib/errdb.rb', line 69

def logger
  @logger
end

#portObject (readonly)

Returns the value of attribute port.



54
55
56
# File 'lib/errdb.rb', line 54

def port
  @port
end

#serverObject (readonly)

Returns the value of attribute server.



56
57
58
# File 'lib/errdb.rb', line 56

def server
  @server
end

#timeoutObject (readonly)

Socket timeout limit with this client, defaults to 0.5 sec. Set to nil to disable timeouts.



62
63
64
# File 'lib/errdb.rb', line 62

def timeout
  @timeout
end

Instance Method Details

#active?Boolean

Returns whether there is at least one active server for the object.

Returns:

  • (Boolean)


112
113
114
# File 'lib/errdb.rb', line 112

def active?
  @server.active?
end

#fetch(key, fields, start_time, end_time) ⇒ Object

ā€œ19199191:a,b,cā€ =~ /A(d+):(.*)/



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/errdb.rb', line 138

def fetch(key, fields, start_time, end_time)
  rows = []
  with_socket(server) do |socket|
    socket.write "fetch #{key} #{fields} #{start_time} #{end_time}\r\n"
    while line = socket.gets do
      raise_on_error_response! line
      break if line == "END\r\n"
      if line =~ /\ATIME:(.*)/ then
        names = $1.strip.split(",")
      elsif line =~ /\A(\d+):(.*)/ then
        timestamp = $1.to_i
        values = $2.strip.split(",").map {|v| v.to_f}
        rows << {timestamp => values}
      else
        logger.warn { "unexpected line: #{line}" } if logger
      end
    end
  end
  rows
end

#handle_error(server, error) ⇒ Object

Handles error from server.



200
201
202
203
204
205
206
# File 'lib/errdb.rb', line 200

def handle_error(server, error)
  raise error if error.is_a?(ErrdbError)
  server.close if server && server.status == "CONNECTED"
  new_error = ErrdbError.new error.message
  new_error.set_backtrace error.backtrace
  raise new_error
end

#insert(key, time, metrics) ⇒ Object



123
124
125
126
127
128
# File 'lib/errdb.rb', line 123

def insert(key, time, metrics)
	data = (metrics.map do |k, v| "#{k}=#{v}" end).join(",")
  with_socket(server) do |socket|
    socket.write "insert #{key} #{time} #{data}\r\n"
  end
end

#inspectObject

Returns a string representation of the cache object.



104
105
106
107
# File 'lib/errdb.rb', line 104

def inspect
  "<Errdb: %s , ro: %p>" %
    [@server.inspect, @readonly]
end

#raise_on_error_response!(response) ⇒ Object



208
209
210
211
212
# File 'lib/errdb.rb', line 208

def raise_on_error_response!(response)
  if response =~ /\A(?:CLIENT_|SERVER_)?ERROR(.*)/
    raise ErrdbError, $1.strip
  end
end

#readonly?Boolean

Returns whether or not the cache object was created read only.

Returns:

  • (Boolean)


119
120
121
# File 'lib/errdb.rb', line 119

def readonly?
  @readonly
end

#with_socket(server, &block) ⇒ Object

Gets or creates a socket connected to the given server, and yields it to the block

If a socket error (SocketError, SystemCallError, IOError) or protocol error (ErrdbError) is raised by the block, closes the socket, attempts to connect again, and retries the block (once). If an error is again raised, reraises it as ErrdbError.

If unable to connect to the server (or if in the reconnect wait period), raises ErrdbError. Note that the socket connect code marks a server dead for a timeout period, so retrying does not apply to connection attempt failures (but does still apply to unexpectedly lost connections etc.).



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/errdb.rb', line 173

def with_socket(server, &block)

  begin
    socket = server.socket

    # Raise an IndexError to show this server is out of whack. If were inside
    # a with_server block, we'll catch it and attempt to restart the operation.

    raise IndexError, "No connection to server (#{server.status})" if socket.nil?

    block.call(socket)

  rescue SocketError, Errno::EAGAIN, Timeout::Error => err
    logger.warn { "Socket failure: #{err.message}" } if logger
    server.mark_dead(err)
    handle_error(server, err)

  rescue ErrdbError, SystemCallError, IOError => err
    logger.warn { "Generic failure: #{err.class.name}: #{err.message}" } if logger
    handle_error(server, err) if socket.nil?
  end

end