├── README.md ├── lib └── resty │ └── bloomd.lua └── t └── test.lua /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | lua-resty-bloomd - Is a client library based on ngx_lua to interface with bloomd servers(https://github.com/armon/bloomd)
5 | 6 | Bloomd is a high-performance C server which is used to expose bloom filters and operations over them to networked clients. 7 | 8 | Table of Contents 9 | ================= 10 | 11 | * [Name](#name) 12 | * [Status](#status) 13 | * [Synopsis](#synopsis) 14 | * [Methods](#methods) 15 | * [new](#new) 16 | * [create](#create) 17 | * [list](#list) 18 | * [drop](#drop) 19 | * [close](#close) 20 | * [clear](#clear) 21 | * [check](#check) 22 | * [checks](#checks) 23 | * [set](#set) 24 | * [sets](#sets) 25 | * [info](#info) 26 | * [flush](#flush) 27 | * [Installation](#installation) 28 | * [Authors](#authors) 29 | * [Copyright and License](#copyright-and-license) 30 | 31 | Status 32 | ====== 33 | 34 | This library is production ready. 35 | 36 | Synopsis 37 | ======== 38 | ```lua 39 | lua_package_path "/path/to/lua-resty-bloomd/lib/?.lua;;"; 40 | 41 | server { 42 | location /test { 43 | content_by_lua ' 44 | local bloomd = require("resty.bloomd") 45 | 46 | local function debug(name, ok, err) 47 | if type(err) == 'table' then 48 | local t = {} 49 | for k, v in pairs(err) do 50 | table.insert(t, k .. ":" .. tostring(v)) 51 | end 52 | err = table.concat(t, ",") 53 | end 54 | ngx.say(string.format("%15s -- ok: %5s, err: %s", name, tostring(ok), tostring(err))) 55 | end 56 | -- create a new instance and connect to the bloomd(127.0.0.1:8673) 57 | local filter_obj = bloomd:new("127.0.0.1", 8673, 2000) 58 | local function test_main() 59 | local filter_name = "my_filter" 60 | local capacity = 100001 61 | local probability = 0.001 62 | -- create a filter named filter_name 63 | local ok, err = filter_obj:create(filter_name, capacity, probability) 64 | debug("create-new", ok, err) 65 | assert(ok == true) 66 | -- assert(err == "Done", "err ~= 'Done'") 67 | 68 | -- create a filter, the name is exist 69 | local ok, err = filter_obj:create(filter_name, capacity, probability) 70 | debug("create-exist", ok, err) 71 | assert(ok == true) 72 | assert(err == "Exists") 73 | 74 | -- set a key, New 75 | local ok, err = filter_obj:set(filter_name, 'my_key') 76 | debug("set-new", ok, err) 77 | assert(ok==true) 78 | assert(err == "Yes") 79 | 80 | -- set a key, Exist 81 | local ok, err = filter_obj:set(filter_name, 'my_key') 82 | debug("set-exist", ok, err) 83 | assert(ok==true) 84 | assert(err == "No") 85 | 86 | -- check a key, Exist 87 | local ok, err = filter_obj:check(filter_name, 'my_key') 88 | debug("check-exist", ok, err) 89 | assert(ok==true) 90 | assert(err == "Yes") 91 | 92 | -- check a key, Not Exist 93 | local ok, err = filter_obj:check(filter_name, 'this_key_not_exist') 94 | debug("check-not-exist", ok, err) 95 | assert(ok==true) 96 | assert(err == "No") 97 | 98 | -- flush a filter 99 | local ok, err = filter_obj:flush(filter_name) 100 | debug("flush", ok, err) 101 | assert(ok==true) 102 | assert(err == "Done") 103 | 104 | -- close a bloom filter 105 | local ok, err = filter_obj:close(filter_name) 106 | debug("close", ok, err) 107 | assert(ok==true) 108 | assert(err == "Done") 109 | 110 | -- check a key, Exist 111 | local ok, err = filter_obj:check(filter_name, 'my_key') 112 | debug("check-exist", ok, err) 113 | assert(ok==true) 114 | assert(err == "Yes") 115 | 116 | 117 | filter_obj:create("my_filter3", capacity, 0.001) 118 | -- list all filter 119 | local ok, filters = filter_obj:list(filter_name) 120 | debug("list", ok, filters) 121 | assert(ok==true) 122 | assert(type(filters)=='table' and #filters==2) 123 | for _,filter in ipairs(filters) do 124 | if filter.name == filter_name then 125 | assert(filter.size == 1) 126 | end 127 | end 128 | filter_obj:drop('my_filter3') 129 | 130 | -- Set many items in a filter at once(bulk command) 131 | local ok, status = filter_obj:sets(filter_name, {"a", "b", "c"}) 132 | assert(ok) 133 | assert(type(status)=='table') 134 | err = table.concat(status, ' ') 135 | debug("sets", ok, err) 136 | assert(err == "Yes Yes Yes") 137 | 138 | local ok, status = filter_obj:sets(filter_name, {"a", "b", "d"}) 139 | assert(ok) 140 | assert(type(status)=='table') 141 | err = table.concat(status, ' ') 142 | debug("sets", ok, err) 143 | assert(err == "No No Yes") 144 | 145 | -- Checks if a list of keys are in a filter 146 | local ok, status = filter_obj:checks(filter_name, {"a", "x", "c", "d", "e"}) 147 | assert(ok) 148 | assert(type(status)=='table') 149 | err = table.concat(status, ' ') 150 | debug("checks", ok, err) 151 | assert(err == "Yes No Yes Yes No") 152 | 153 | 154 | -- Gets info about a filter 155 | local ok, info = filter_obj:info(filter_name) 156 | debug("info", ok, info) 157 | assert(ok) 158 | assert(type(info)=='table') 159 | assert(info.capacity == capacity) 160 | assert(info.probability == probability) 161 | assert(info.size == 5) 162 | 163 | -- drop a filter 164 | local ok, err = filter_obj:drop(filter_name) 165 | debug("drop", ok, err) 166 | assert(ok==true) 167 | assert(err == "Done") 168 | 169 | 170 | -- Test filter not exist 171 | local ok, err = filter_obj:drop(filter_name) 172 | debug("drop-not-exist", ok, err) 173 | assert(ok==false) 174 | assert(err == "Filter does not exist") 175 | 176 | 177 | -- create, close and clear a bloom filter, my_filter2 is still in disk. 178 | local ok, err = filter_obj:create("my_filter2", 10000*20, 0.001) 179 | debug("create-new", ok, err) 180 | assert(ok == true) 181 | assert(err == "Done", "err ~= 'Done'") 182 | local ok, err = filter_obj:close("my_filter2") 183 | debug("close", ok, err) 184 | assert(ok==true) 185 | assert(err == "Done") 186 | local ok, err = filter_obj:clear("my_filter2") 187 | debug("clear", ok, err) 188 | assert(ok==true) 189 | assert(err == "Done") 190 | 191 | ngx.say("--------- all test ok --------------") 192 | end 193 | local ok, err = pcall(test_main) 194 | if not ok then 195 | filter_obj:close("my_filter") 196 | filter_obj:close("my_filter2") 197 | filter_obj:close("my_filter3") 198 | assert(ok, err) 199 | end 200 | '; 201 | } 202 | } 203 | ``` 204 | 205 | Methods 206 | ======= 207 | 208 | [Back to TOC](#table-of-contents) 209 | new 210 | --- 211 | `syntax: filter_obj = bloomd:new(host, port, timeout)` 212 | 213 | Create a new bloom filter object. 214 | * IN: the `host` is the bloomd's host, default is '127.0.0.1'. 215 | * IN: the `port` is the bloomd's port, default is 8673. 216 | * IN: the `timeout` is the timeout time in ms, default is 5000ms. 217 | 218 | create 219 | --- 220 | `syntax: ok, err = filter_obj:create(filter_name, capacity, prob, in_memory)` 221 | 222 | Create a new filter 223 | * IN: the `filter_name` is the name of the filter, and can contain the characters a-z, A-Z, 0-9, ., _. 224 | * IN: the `capacity` is provided the filter will be created to store at least that many items in the initial filter. default is 0.001. 225 | * IN: the `prob` is maximum false positive probability provided. 226 | * IN: the 'in_memory' is to force the filter not to be persisted to disk. 227 | 228 | list 229 | --- 230 | `syntax: ok, filters = filter_obj:list(filter_prefix)` 231 | 232 | List all filters or those matching a prefix.
233 | * OUT: the `ok` is list status. 234 | * OUT: the `filters` is a Lua table holding all the matched filter. 235 | 236 | ```lua 237 | for _,filter in ipairs(filters) do 238 | ngx.say(filter.name, ",", filter.probability, ",", 239 | filter.storage, ",", filter.capacity, ",",filter.size) 240 | end 241 | ``` 242 | 243 | drop 244 | --- 245 | `syntax: ok, err = filter_obj:drop(filter_name)` 246 | 247 | Drop a filter (Deletes from disk). On Success Returns ok:true, err:'Done' 248 | 249 | close 250 | --- 251 | `syntax: ok, err = filter_obj:close(filter_name)` 252 | 253 | Close a filter (Unmaps from memory, but still accessible). On Success Returns ok:true, err:'Done' 254 | 255 | 256 | clear 257 | --- 258 | `syntax: ok, err = filter_obj:clear(filter_name)` 259 | 260 | Clear a filter from the lists (Removes memory, left on disk) 261 | 262 | 263 | check 264 | --- 265 | `syntax: ok, status = filter_obj:check(filter_name, key)` 266 | 267 | Check if a key is in a filter. 268 | * IN: the `status` is 'Yes'(`key` exists in filter) or 'No'(if `key` does not exists in filter). 269 | 270 | checks 271 | --- 272 | `syntax: ok, status = filter_obj:checks(filter_name, keys)` 273 | 274 | Check if a list of keys are in a filter 275 | * IN: the `keys` is a table that contains some keys. 276 | * OUT: the `status` is a table that contains each key's status('Yes' or 'No'). 277 | 278 | set 279 | --- 280 | `syntax: ok, status = filter_obj:set(filter_name, key)` 281 | 282 | Set an item in a filter 283 | * OUT: the `status` is 'Yes'(`key` is successfully set to the filter) or 'No'(`key` exists in the filter). 284 | 285 | 286 | sets 287 | --- 288 | `syntax: ok, status = filter_obj:sets(filter_name, keys)` 289 | 290 | Set many items in a filter at once 291 | * IN: the `keys` is a table that contains some keys. 292 | * OUT: the `status` is a table that contains each key's set status('Yes' or 'No'). 293 | 294 | info 295 | --- 296 | `syntax: ok, info = filter_obj:info(filter_name)` 297 | 298 | Get info about a filter 299 | * OUT: the `info` is a table like `{in_memory:1,set_misses:3,checks:8,capacity:100001, probability:0.001,page_outs:1,size:5,check_hits:5, 300 | storage:240141,page_ins:1,set_hits:5,check_misses:3,sets:8}`. 301 | 302 | flush 303 | --- 304 | `syntax: ok, err = filter_obj:flush(filter_name)` 305 | 306 | Flush a specified filter. 307 | 308 | [Back to TOC](#table-of-contents) 309 | 310 | Installation 311 | ============ 312 | 313 | You need to compile [ngx_lua](https://github.com/chaoslawful/lua-nginx-module/tags) with your Nginx. 314 | 315 | You need to configure 316 | the [lua_package_path](https://github.com/chaoslawful/lua-nginx-module#lua_package_path) directive to 317 | add the path of your `lua-resty-bloomd` source tree to ngx_lua's Lua module search path, as in 318 | 319 | # nginx.conf 320 | http { 321 | lua_package_path "/path/to/lua-resty-bloomd/lib/?.lua;;"; 322 | ... 323 | } 324 | 325 | and then load the library in Lua: 326 | 327 | bloomd = require "resty.bloomd" 328 | 329 | [Back to TOC](#table-of-contents) 330 | 331 | Authors 332 | ======= 333 | 334 | Xiaojie Liu 。 335 | 336 | [Back to TOC](#table-of-contents) 337 | 338 | Copyright and License 339 | ===================== 340 | 341 | This module is licensed under the BSD license. 342 | 343 | Copyright (C) 2015, by Xiaojie Liu 344 | 345 | All rights reserved. 346 | 347 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 348 | 349 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 350 | 351 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 352 | 353 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 354 | 355 | [Back to TOC](#table-of-contents) 356 | 357 | -------------------------------------------------------------------------------- /lib/resty/bloomd.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2015 Xiaojie Liu (jie123108@163.com). 2 | -- lua-resty-bloomd - Is a ngx_lua client library to interface with bloomd servers 3 | -- https://github.com/armon/bloomd 4 | 5 | local ok, new_tab = pcall(require, "table.new") 6 | if not ok then 7 | new_tab = function (narr, nrec) return {} end 8 | end 9 | 10 | local ok, clear_tab = pcall(require, "table.clear") 11 | if not ok then 12 | clear_tab = function(tab) for k, _ in pairs(tab) do tab[k] = nil end end 13 | end 14 | 15 | local _M = new_tab(0, 2) 16 | 17 | _M._VERSION = '0.01' 18 | 19 | local function split_by_space(str) 20 | local result = {} 21 | for match in string.gmatch(str, "[^ ]+") do 22 | table.insert(result, match); 23 | end 24 | return result 25 | end 26 | 27 | local mt = { __index = _M } 28 | 29 | function _M:new(host, port, timeout) 30 | host = host or "127.0.0.1" 31 | port = port or 8673 32 | timeout = timeout or 1000*5 33 | return setmetatable({host=host, port=port, timeout=timeout}, mt) 34 | end 35 | 36 | function _M:_connect() 37 | local sock = ngx.socket.tcp() 38 | if self.timeout then 39 | sock:settimeout(self.timeout) 40 | end 41 | local ok, err = sock:connect(self.host, self.port) 42 | if not ok then 43 | sock:close() 44 | return nil, err 45 | end 46 | 47 | return sock 48 | end 49 | 50 | function _M:_setkeepalive(sock) 51 | if sock then 52 | sock:setkeepalive() 53 | sock = nil 54 | end 55 | end 56 | 57 | function _M:_request(sock, command, endval) 58 | assert(sock ~= nil, "socket not inited") 59 | assert(command ~= nil, "cmd is nil") 60 | local n, err = sock:send(command .. "\n") 61 | if err then 62 | return nil, err 63 | end 64 | if endval == nil then 65 | local line, err, partial = sock:receive('*l') 66 | return line, err, partial 67 | else 68 | local lines = {} 69 | for i=1, 10240 do 70 | local line, err, partial = sock:receive('*l') 71 | if err then 72 | table.insert(lines, partial) 73 | return nil, err, table.concat(lines, '\n') 74 | end 75 | table.insert(lines, line) 76 | if line == endval then 77 | break 78 | end 79 | end 80 | return lines, err 81 | end 82 | end 83 | 84 | function _M:_request_once(cmd, endval) 85 | local sock, err = self:_connect() 86 | if not sock then 87 | return nil, err 88 | end 89 | local line, err, partial = self:_request(sock, cmd, endval) 90 | self:_setkeepalive(sock) 91 | return line, err 92 | end 93 | 94 | function _M:create(filter_name, capacity, prob, in_memory) 95 | assert(filter_name ~= nil, "filter_name is nil") 96 | 97 | local cmds = {"create " .. filter_name} 98 | table.insert(cmds, cmd) 99 | 100 | if capacity and type(capacity) == 'number' then 101 | table.insert(cmds, "capacity=" .. tostring(capacity)) 102 | end 103 | if prob and type(prob) == 'number' then 104 | table.insert(cmds, "prob=" .. tostring(prob)) 105 | end 106 | if in_memory == 0 then 107 | in_memory = false 108 | end 109 | if in_memory then 110 | table.insert(cmds, "in_memory=1") 111 | end 112 | 113 | local cmd = table.concat(cmds, " ") 114 | 115 | local line, err = self:_request_once(cmd) 116 | 117 | if line == nil then 118 | return false, err 119 | end 120 | if line == "Done" or line == "Exists" then 121 | return true, line 122 | else 123 | return false, line 124 | end 125 | end 126 | 127 | function _M:_do_command1(cmd) 128 | assert(cmd ~= nil, "cmd is nil") 129 | local line, err = self:_request_once(cmd) 130 | if line == nil then 131 | return false, err 132 | end 133 | if line == "Done" then 134 | return true, line 135 | else 136 | return false, line 137 | end 138 | end 139 | 140 | function _M:drop(filter_name) 141 | assert(filter_name ~= nil, "filter_name is nil") 142 | local cmd = "drop " .. filter_name 143 | return self:_do_command1(cmd) 144 | end 145 | 146 | function _M:close(filter_name) 147 | assert(filter_name ~= nil, "filter_name is nil") 148 | local cmd = "close " .. filter_name 149 | return self:_do_command1(cmd) 150 | end 151 | 152 | function _M:clear(filter_name) 153 | assert(filter_name ~= nil, "filter_name is nil") 154 | local cmd = "clear " .. filter_name 155 | return self:_do_command1(cmd) 156 | end 157 | 158 | function _M:flush(filter_name) 159 | assert(filter_name ~= nil, "filter_name is nil") 160 | local cmd = "flush " .. filter_name 161 | return self:_do_command1(cmd) 162 | end 163 | 164 | 165 | function _M:_do_command2(cmd) 166 | assert(cmd ~= nil, "cmd is nil") 167 | local line, err = self:_request_once(cmd) 168 | if line == nil then 169 | return false, err 170 | end 171 | if line == "Yes" or line == "No" then 172 | return true, line 173 | else 174 | return false, line 175 | end 176 | end 177 | 178 | function _M:check(filter_name, key) 179 | assert(filter_name ~= nil, "filter_name is nil") 180 | assert(key ~= nil, "key is nil") 181 | local cmd = "c " .. filter_name .. " " .. tostring(key) 182 | return self:_do_command2(cmd) 183 | end 184 | 185 | function _M:set(filter_name, key) 186 | assert(filter_name ~= nil, "filter_name is nil") 187 | assert(key ~= nil, "key is nil") 188 | local cmd = "s " .. filter_name .. " " .. tostring(key) 189 | return self:_do_command2(cmd) 190 | end 191 | 192 | function _M:_do_command_multi(cmd) 193 | assert(cmd ~= nil, "cmd is nil") 194 | local line, err = self:_request_once(cmd) 195 | if line == nil then 196 | return false, err 197 | end 198 | if line == "Filter does not exist" then 199 | return false, line 200 | else 201 | local result = split_by_space(line) 202 | return true, result 203 | end 204 | end 205 | 206 | function _M:checks(filter_name, keys) 207 | assert(filter_name ~= nil, "filter_name is nil") 208 | assert(type(keys) == 'table', "keys must be a 'table'") 209 | local cmd = "m " .. filter_name .. " " .. table.concat(keys, " ") 210 | return self:_do_command_multi(cmd) 211 | end 212 | 213 | function _M:sets(filter_name, keys) 214 | assert(filter_name ~= nil, "filter_name is nil") 215 | assert(type(keys) == 'table', "keys must be a 'table'") 216 | local cmd = "b " .. filter_name .. " " .. table.concat(keys, " ") 217 | return self:_do_command_multi(cmd) 218 | end 219 | 220 | function _M:_do_command_list(cmd) 221 | assert(cmd ~= nil, "cmd is nil") 222 | local lines, err = self:_request_once(cmd, "END") 223 | if lines == nil then 224 | return false, err 225 | end 226 | assert(type(lines) == 'table') 227 | local list = {} 228 | for _,line in ipairs(lines) do 229 | if line ~= 'START' and line ~= 'END' then 230 | table.insert(list, line) 231 | end 232 | end 233 | return true, list 234 | end 235 | 236 | function _M:info(filter_name) 237 | assert(filter_name ~= nil, "filter_name is nil") 238 | local cmd = "info " .. filter_name 239 | local ok, list = self:_do_command_list(cmd) 240 | if not ok then 241 | return ok, list 242 | end 243 | local infos = {} 244 | for _, line in ipairs(list) do 245 | local item = split_by_space(line) 246 | if #item == 2 then 247 | infos[item[1]] = tonumber(item[2]) 248 | else 249 | ngx.log(ngx.ERR, "invalid line [", line, "]") 250 | end 251 | end 252 | return true, infos 253 | end 254 | 255 | function _M:list(prefix) 256 | local cmd = "list" 257 | if prefix then 258 | cmd = cmd .. " " .. prefix 259 | end 260 | local ok, list = self:_do_command_list(cmd) 261 | if not ok then 262 | return ok, list 263 | end 264 | local filters = {} 265 | for _, line in ipairs(list) do 266 | local item = split_by_space(line) 267 | if #item == 5 then 268 | -- filter_name probability storage capacity size 269 | local filter_info = {} 270 | filter_info['name'] = item[1] 271 | filter_info['probability'] = tonumber(item[2]) 272 | filter_info['storage'] = tonumber(item[3]) 273 | filter_info['capacity'] = tonumber(item[4]) 274 | filter_info['size'] = tonumber(item[5]) 275 | 276 | table.insert(filters, filter_info) 277 | else 278 | ngx.log(ngx.ERR, "invalid line [", line, "]") 279 | end 280 | end 281 | return true, filters 282 | end 283 | 284 | return _M 285 | -------------------------------------------------------------------------------- /t/test.lua: -------------------------------------------------------------------------------- 1 | local bloomd = require("resty.bloomd") 2 | 3 | local function debug(name, ok, err) 4 | if type(err) == 'table' then 5 | local t = {} 6 | for k, v in pairs(err) do 7 | table.insert(t, k .. ":" .. tostring(v)) 8 | end 9 | err = table.concat(t, ",") 10 | end 11 | ngx.say(string.format("%15s -- ok: %5s, err: %s", name, tostring(ok), tostring(err))) 12 | end 13 | -- create a new instance and connect to the bloomd(127.0.0.1:8673) 14 | local filter_obj = bloomd:new("127.0.0.1", 8673, 2000) 15 | local function test_main() 16 | local filter_name = "my_filter" 17 | local capacity = 100001 18 | local probability = 0.001 19 | -- create a filter named filter_name 20 | local ok, err = filter_obj:create(filter_name, capacity, probability) 21 | debug("create-new", ok, err) 22 | assert(ok == true) 23 | -- assert(err == "Done", "err ~= 'Done'") 24 | 25 | -- create a filter, the name is exist 26 | local ok, err = filter_obj:create(filter_name, capacity, probability) 27 | debug("create-exist", ok, err) 28 | assert(ok == true) 29 | assert(err == "Exists") 30 | 31 | -- set a key, New 32 | local ok, err = filter_obj:set(filter_name, 'my_key') 33 | debug("set-new", ok, err) 34 | assert(ok==true) 35 | assert(err == "Yes") 36 | 37 | -- set a key, Exist 38 | local ok, err = filter_obj:set(filter_name, 'my_key') 39 | debug("set-exist", ok, err) 40 | assert(ok==true) 41 | assert(err == "No") 42 | 43 | -- check a key, Exist 44 | local ok, err = filter_obj:check(filter_name, 'my_key') 45 | debug("check-exist", ok, err) 46 | assert(ok==true) 47 | assert(err == "Yes") 48 | 49 | -- check a key, Not Exist 50 | local ok, err = filter_obj:check(filter_name, 'this_key_not_exist') 51 | debug("check-not-exist", ok, err) 52 | assert(ok==true) 53 | assert(err == "No") 54 | 55 | -- flush a filter 56 | local ok, err = filter_obj:flush(filter_name) 57 | debug("flush", ok, err) 58 | assert(ok==true) 59 | assert(err == "Done") 60 | 61 | -- close a bloom filter 62 | local ok, err = filter_obj:close(filter_name) 63 | debug("close", ok, err) 64 | assert(ok==true) 65 | assert(err == "Done") 66 | 67 | -- check a key, Exist 68 | local ok, err = filter_obj:check(filter_name, 'my_key') 69 | debug("check-exist", ok, err) 70 | assert(ok==true) 71 | assert(err == "Yes") 72 | 73 | 74 | filter_obj:create("my_filter3", capacity, 0.001) 75 | -- list all filter 76 | local ok, filters = filter_obj:list(filter_name) 77 | debug("list", ok, filters) 78 | assert(ok==true) 79 | assert(type(filters)=='table' and #filters==2) 80 | for _,filter in ipairs(filters) do 81 | if filter.name == filter_name then 82 | assert(filter.size == 1) 83 | end 84 | end 85 | filter_obj:drop('my_filter3') 86 | 87 | -- Set many items in a filter at once(bulk command) 88 | local ok, status = filter_obj:sets(filter_name, {"a", "b", "c"}) 89 | assert(ok) 90 | assert(type(status)=='table') 91 | err = table.concat(status, ' ') 92 | debug("sets", ok, err) 93 | assert(err == "Yes Yes Yes") 94 | 95 | local ok, status = filter_obj:sets(filter_name, {"a", "b", "d"}) 96 | assert(ok) 97 | assert(type(status)=='table') 98 | err = table.concat(status, ' ') 99 | debug("sets", ok, err) 100 | assert(err == "No No Yes") 101 | 102 | -- Checks if a list of keys are in a filter 103 | local ok, status = filter_obj:checks(filter_name, {"a", "x", "c", "d", "e"}) 104 | assert(ok) 105 | assert(type(status)=='table') 106 | err = table.concat(status, ' ') 107 | debug("checks", ok, err) 108 | assert(err == "Yes No Yes Yes No") 109 | 110 | 111 | -- Gets info about a filter 112 | local ok, info = filter_obj:info(filter_name) 113 | debug("info", ok, info) 114 | assert(ok) 115 | assert(type(info)=='table') 116 | assert(info.capacity == capacity) 117 | assert(info.probability == probability) 118 | assert(info.size == 5) 119 | 120 | -- drop a filter 121 | local ok, err = filter_obj:drop(filter_name) 122 | debug("drop", ok, err) 123 | assert(ok==true) 124 | assert(err == "Done") 125 | 126 | 127 | -- Test filter not exist 128 | local ok, err = filter_obj:drop(filter_name) 129 | debug("drop-not-exist", ok, err) 130 | assert(ok==false) 131 | assert(err == "Filter does not exist") 132 | 133 | 134 | -- create, close and clear a bloom filter, my_filter2 is still in disk. 135 | local ok, err = filter_obj:create("my_filter2", 10000*20, 0.001) 136 | debug("create-new", ok, err) 137 | assert(ok == true) 138 | assert(err == "Done", "err ~= 'Done'") 139 | local ok, err = filter_obj:close("my_filter2") 140 | debug("close", ok, err) 141 | assert(ok==true) 142 | assert(err == "Done") 143 | local ok, err = filter_obj:clear("my_filter2") 144 | debug("clear", ok, err) 145 | assert(ok==true) 146 | assert(err == "Done") 147 | 148 | ngx.say("--------- all test ok --------------") 149 | end 150 | local ok, err = pcall(test_main) 151 | if not ok then 152 | filter_obj:close("my_filter") 153 | filter_obj:close("my_filter2") 154 | filter_obj:close("my_filter3") 155 | assert(ok, err) 156 | end 157 | --------------------------------------------------------------------------------