| 1 | |
|---|
| 2 | --Copyright (c) 2006 Neil Richardson (nrich@iinet.net.au) |
|---|
| 3 | -- |
|---|
| 4 | --Permission is hereby granted, free of charge, to any person obtaining a copy |
|---|
| 5 | --of this software and associated documentation files (the "Software"), to deal |
|---|
| 6 | --in the Software without restriction, including without limitation the rights |
|---|
| 7 | --to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|---|
| 8 | --copies of the Software, and to permit persons to whom the Software is |
|---|
| 9 | --furnished to do so, subject to the following conditions: |
|---|
| 10 | -- |
|---|
| 11 | --The above copyright notice and this permission notice shall be included in all |
|---|
| 12 | --copies or substantial portions of the Software. |
|---|
| 13 | -- |
|---|
| 14 | --THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 15 | --IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 16 | --FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|---|
| 17 | --AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|---|
| 18 | --LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|---|
| 19 | --OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|---|
| 20 | --IN THE SOFTWARE. |
|---|
| 21 | |
|---|
| 22 | require("socket") |
|---|
| 23 | |
|---|
| 24 | Memcached = { |
|---|
| 25 | |
|---|
| 26 | set = function(cache, key, value) |
|---|
| 27 | return _store(cache, 'set', key, value) |
|---|
| 28 | end, |
|---|
| 29 | |
|---|
| 30 | add = function(cache, key, value) |
|---|
| 31 | return _store(cache, 'add', key, value) |
|---|
| 32 | end, |
|---|
| 33 | |
|---|
| 34 | replace = function(cache, key, value) |
|---|
| 35 | return _store(cache, 'replace', key, value) |
|---|
| 36 | end, |
|---|
| 37 | |
|---|
| 38 | get = function(cache, key) |
|---|
| 39 | return _retrieve(cache, 'get ' .. key) |
|---|
| 40 | end, |
|---|
| 41 | |
|---|
| 42 | delete = function(cache, key) |
|---|
| 43 | local res = _send(cache, 'delete ' .. key) |
|---|
| 44 | |
|---|
| 45 | if res == 'NOT_FOUND' then |
|---|
| 46 | return nil |
|---|
| 47 | end |
|---|
| 48 | |
|---|
| 49 | if res ~= 'DELETED' then |
|---|
| 50 | error("Error deleting '" .. key .. "': " .. res) |
|---|
| 51 | return nil |
|---|
| 52 | end |
|---|
| 53 | |
|---|
| 54 | return true |
|---|
| 55 | end, |
|---|
| 56 | |
|---|
| 57 | incr = function(cache, key, val) |
|---|
| 58 | if val == nil then |
|---|
| 59 | val = 1 |
|---|
| 60 | end |
|---|
| 61 | |
|---|
| 62 | local res = _send(cache, 'incr ' .. key .. ' ' .. val) |
|---|
| 63 | |
|---|
| 64 | if res == 'ERROR' or res == 'CLIENT_ERROR' then |
|---|
| 65 | error("Error incrementing '" .. key .. "': " .. res) |
|---|
| 66 | end |
|---|
| 67 | |
|---|
| 68 | return res |
|---|
| 69 | end, |
|---|
| 70 | |
|---|
| 71 | decr = function(cache, key, val) |
|---|
| 72 | if val == nil then |
|---|
| 73 | val = 1 |
|---|
| 74 | end |
|---|
| 75 | |
|---|
| 76 | local res = _send(cache, 'decr ' .. key .. ' ' .. val) |
|---|
| 77 | |
|---|
| 78 | if res == 'ERROR' or res == 'CLIENT_ERROR' then |
|---|
| 79 | error("Error incrementing '" .. key .. "': " .. res) |
|---|
| 80 | end |
|---|
| 81 | |
|---|
| 82 | return res |
|---|
| 83 | end, |
|---|
| 84 | |
|---|
| 85 | Connect = function(address, port) |
|---|
| 86 | if address == nil then |
|---|
| 87 | error('No host address defined') |
|---|
| 88 | end |
|---|
| 89 | |
|---|
| 90 | if port == nil then |
|---|
| 91 | port = 11211 |
|---|
| 92 | end |
|---|
| 93 | |
|---|
| 94 | local client = socket.connect(address, port) |
|---|
| 95 | |
|---|
| 96 | if not client then |
|---|
| 97 | error('Could not connect to ' .. address .. ':' .. port) |
|---|
| 98 | end |
|---|
| 99 | |
|---|
| 100 | function _send(cache, str) |
|---|
| 101 | local socket = cache.socket |
|---|
| 102 | |
|---|
| 103 | socket:send(str .. "\r\n") |
|---|
| 104 | local line, err = socket:receive() |
|---|
| 105 | |
|---|
| 106 | if not err then return line end |
|---|
| 107 | end |
|---|
| 108 | |
|---|
| 109 | function _store(cache, op, key, value) |
|---|
| 110 | local len = string.len(value) |
|---|
| 111 | local cmd = op .. ' ' .. key .. ' 0 0 ' .. len .. '\n' .. value |
|---|
| 112 | |
|---|
| 113 | local res = _send(cache, cmd) |
|---|
| 114 | |
|---|
| 115 | if res ~= 'STORED' then |
|---|
| 116 | error("Error storing '" .. key .. "': " .. res) |
|---|
| 117 | return nil |
|---|
| 118 | end |
|---|
| 119 | |
|---|
| 120 | return true |
|---|
| 121 | end |
|---|
| 122 | |
|---|
| 123 | function _retrieve(cache, str) |
|---|
| 124 | local socket = cache.socket |
|---|
| 125 | |
|---|
| 126 | socket:send(str .. '\n') |
|---|
| 127 | |
|---|
| 128 | local data = {} |
|---|
| 129 | while true do |
|---|
| 130 | local line, err = socket:receive() |
|---|
| 131 | |
|---|
| 132 | if line == 'END' then |
|---|
| 133 | break |
|---|
| 134 | elseif string.sub(line, 1, 5) == 'VALUE' then |
|---|
| 135 | else |
|---|
| 136 | table.insert(data, line) |
|---|
| 137 | end |
|---|
| 138 | end |
|---|
| 139 | |
|---|
| 140 | if table.getn(data) == 0 then |
|---|
| 141 | return nil |
|---|
| 142 | end |
|---|
| 143 | |
|---|
| 144 | local datastring = table.concat(data, '\n') |
|---|
| 145 | |
|---|
| 146 | return datastring |
|---|
| 147 | |
|---|
| 148 | end |
|---|
| 149 | |
|---|
| 150 | local cache = { |
|---|
| 151 | socket = client, |
|---|
| 152 | |
|---|
| 153 | set = Memcached.set, |
|---|
| 154 | add = Memcached.add, |
|---|
| 155 | replace = Memcached.replace, |
|---|
| 156 | get = Memcached.get, |
|---|
| 157 | delete = Memcached.delete, |
|---|
| 158 | incr = Memcached.incr, |
|---|
| 159 | decr = Memcached.decr, |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | return cache |
|---|
| 163 | end |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | -- |
|---|
| 167 | -- Memcached.lua |
|---|
| 168 | -- |
|---|
| 169 | -- A pure lua implementation of a simple memcached client. Only 1 memcached server is currently supported. Requires the luasocket library. |
|---|
| 170 | -- See http://www.danga.com/memcached/ for more information about memcached. |
|---|
| 171 | -- |
|---|
| 172 | -- |
|---|
| 173 | -- |
|---|
| 174 | -- Synopsis |
|---|
| 175 | -- |
|---|
| 176 | -- require('Memcached') |
|---|
| 177 | -- |
|---|
| 178 | -- memcache = Memcached.Connect('some.host.com', 11000) |
|---|
| 179 | -- |
|---|
| 180 | -- memcache:set('some_key', 1234) |
|---|
| 181 | -- memcache:add('new_key', 'add new value') |
|---|
| 182 | -- memcache:replace('existing_key', 'replace old value') |
|---|
| 183 | -- |
|---|
| 184 | -- cached_data = memcache:get('some_key') |
|---|
| 185 | -- |
|---|
| 186 | -- memcache:delete('old_key') |
|---|
| 187 | -- |
|---|
| 188 | -- |
|---|
| 189 | -- |
|---|
| 190 | -- Methods: |
|---|
| 191 | -- |
|---|
| 192 | -- memcache = Memcached.Connect(host[, port]) |
|---|
| 193 | -- Connect to memcached server at 'host' on port number 'port'. If port is not provider, port 11211 is used. |
|---|
| 194 | -- |
|---|
| 195 | -- memcache:set(key, value) |
|---|
| 196 | -- Unconditionally sets a key to a given value in the memcache. |
|---|
| 197 | -- |
|---|
| 198 | -- memcache:add(key, value) |
|---|
| 199 | -- Like set, but only stores in memcache if the key doesn't already exist. |
|---|
| 200 | -- |
|---|
| 201 | -- memcache:replace(key, value) |
|---|
| 202 | -- Like set, but only stores in memcache if the key already exists. The opposite of add. |
|---|
| 203 | -- |
|---|
| 204 | -- value = memcache:get(key) |
|---|
| 205 | -- Retrieves a key from the memcache. Returns the value or nil |
|---|
| 206 | -- |
|---|
| 207 | -- memcache:delete(key) |
|---|
| 208 | -- Deletes a key. Returns true on deletion, nil if the key was not found. |
|---|
| 209 | -- |
|---|
| 210 | -- value = memcache:incr(key[, value]) |
|---|
| 211 | -- Sends a command to the server to atomically increment the value for key by value, or by 1 if value is nil. |
|---|
| 212 | -- Returns nil if key doesn't exist on server, otherwise it returns the new value after incrementing. Value should be zero or greater. |
|---|
| 213 | -- |
|---|
| 214 | -- value = memcache:decr(key[, value]) |
|---|
| 215 | -- Like incr, but decrements. Unlike incr, underflow is checked and new values are capped at 0. If server value is 1, a decrement of 2 returns 0, not -1. |
|---|
| 216 | -- |
|---|