Class: MemCache

Inherits:
Object
  • Object
show all
Defined in:
lib/memcache.rb

Overview

A Ruby client library for memcached.

This is intended to provide access to basic memcached functionality. It does not attempt to be complete implementation of the entire API.

In particular, the methods of this class are not thread safe. The calling application is responsible for implementing any necessary locking if a cache object will be called from multiple threads.

Defined Under Namespace

Classes: ClientError, InternalError, MemCacheError, Server, ServerError

Constant Summary collapse

GENERAL_ERROR =

Patterns for matching against server error replies.

/^ERROR\r\n/
CLIENT_ERROR =
/^CLIENT_ERROR/
SERVER_ERROR =
/^SERVER_ERROR/
DEFAULT_OPTIONS =

Default options for the cache object.

{
    :namespace => nil,
    :readonly  => false
}
DEFAULT_PORT =

Default memcached port.

11211
DEFAULT_WEIGHT =

Default memcached server weight.

1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ MemCache

Valid options are:

:namespace
    If specified, all keys will have the given value prepended
    before accessing the cache.  Defaults to nil.

:readonly
    If this is set, any attempt to write to the cache will generate
    an exception.  Defaults to false.


45
46
47
48
49
50
51
52
# File 'lib/memcache.rb', line 45

def initialize(opts = {})
    opts = DEFAULT_OPTIONS.merge(opts)   
    @namespace = opts[:namespace]
    @readonly  = opts[:readonly]
    @mutex     = Mutex.new
    @servers   = []
    @buckets   = []
end

Instance Attribute Details

#request_timeoutObject

The amount of time to wait for a response from a memcached server. If a response is not completed within this time, the connection to the server will be closed and an error will be raised.



33
34
35
# File 'lib/memcache.rb', line 33

def request_timeout
  @request_timeout
end

Instance Method Details

#[](key) ⇒ Object

Shortcut to get a value from the cache.



190
191
192
# File 'lib/memcache.rb', line 190

def [](key)
    self.get(key)
end

#[]=(key, value) ⇒ Object

Shortcut to save a value in the cache. This method does not set an expiration on the entry. Use set to specify an explicit expiry.



196
197
198
# File 'lib/memcache.rb', line 196

def []=(key, value)
    self.set(key, value)
end

#active?Boolean

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

Returns:

  • (Boolean)


61
62
63
# File 'lib/memcache.rb', line 61

def active?
    not @servers.empty?
end

#delete(key, expiry = 0) ⇒ Object

Remove an entry from the cache.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/memcache.rb', line 159

def delete(key, expiry = 0)
    @mutex.synchronize do
        raise MemCacheError, "No active servers" unless self.active?
        cache_key = make_cache_key(key)
        server = get_server_for_key(cache_key)

        sock = server.socket
        if sock.nil?
            raise MemCacheError, "No connection to server"
        end

        begin
            sock.write "delete #{cache_key} #{expiry}\r\n"
            sock.gets
        rescue SystemCallError, IOError => err
            server.close
            raise MemCacheError, err.message
        end
    end
end

#get(key) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/memcache.rb', line 97

def get(key)
    @mutex.synchronize do
        raise MemCacheError, "No active servers" unless self.active?
        cache_key = make_cache_key(key)
        server = get_server_for_key(cache_key)

        sock = server.socket
        if sock.nil?
            raise MemCacheError, "No connection to server"
        end

        value = nil
        begin
            sock.write "get #{cache_key}\r\n"
            text = sock.gets # "VALUE <key> <flags> <bytes>\r\n"
            return nil if text =~ /^END/ # HACK: no regex

            v, cache_key, flags, bytes = text.split(/ /)
            value = sock.read(bytes.to_i)
            sock.read(2) # "\r\n"
            sock.gets    # "END\r\n"
        rescue SystemCallError, IOError => err
            server.close
            raise MemCacheError, err.message
        end

        # Return the unmarshaled value.
        begin
            return Marshal.load(value)
        rescue ArgumentError, TypeError => err
            server.close
            raise MemCacheError, err.message
        end
    end
end

#inspectObject

Return a string representation of the cache object.



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

def inspect
    sprintf("<MemCache: %s servers, %s buckets, ns: %p, ro: %p>",
            @servers.nitems, @buckets.nitems, @namespace, @readonly)
end

#readonly?Boolean

Returns whether the cache was created read only.

Returns:

  • (Boolean)


66
67
68
# File 'lib/memcache.rb', line 66

def readonly?
    @readonly
end

#resetObject

Reset the connection to all memcache servers. This should be called if there is a problem with a cache lookup that might have left the connection in a corrupted state.



183
184
185
186
187
# File 'lib/memcache.rb', line 183

def reset
    @mutex.synchronize do
        @servers.each { |server| server.close }
    end
end

#servers=(servers) ⇒ Object

Set the servers that the requests will be distributed between. Entries can be either strings of the form “hostname:port” or “hostname:port:weight” or MemCache::Server objects.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/memcache.rb', line 73

def servers=(servers)
    # Create the server objects.
    @servers = servers.collect do |server|
        case server
        when String
            host, port, weight = server.split(/:/, 3)
            port ||= DEFAULT_PORT
            weight ||= DEFAULT_WEIGHT
            Server::new(host, port, weight)
        when Server
            server
        else
            raise TypeError, "Cannot convert %s to MemCache::Server" %
                svr.class.name
        end
    end

    # Create an array of server buckets for weight selection of servers.
    @buckets = []
    @servers.each do |server|
        server.weight.times { @buckets.push(server) }
    end
end

#set(key, value, expiry = 0) ⇒ Object

Add an entry to the cache.



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

def set(key, value, expiry = 0)
    @mutex.synchronize do
        raise MemCacheError, "No active servers" unless self.active?
        raise MemCacheError, "Update of readonly cache" if @readonly
        cache_key = make_cache_key(key)
        server = get_server_for_key(cache_key)

        sock = server.socket
        if sock.nil?
            raise MemCacheError, "No connection to server"
        end

        marshaled_value = Marshal.dump(value)
        command = "set #{cache_key} 0 #{expiry} #{marshaled_value.size}\r\n" + marshaled_value + "\r\n"
        begin
            sock.write command
            sock.gets
        rescue SystemCallError, IOError => err
            server.close
            raise MemCacheError, err.message
        end
    end
end