Skip to content

Commit

Permalink
memcached: add a new module function with slab info
Browse files Browse the repository at this point in the history
Before switching to our own slab arena user can control usage and
information about slab arena using Tarantool Lua API (specifically
box.slab.{check,info,stat}() functions), see [1]. Example of output
box.slab.info() is below:

tarantool> box.slab.info()
---
- items_size: 146896
  items_used_ratio: 26.05%
  quota_size: 268435456
  quota_used_ratio: 12.50%
  arena_used_ratio: 3.7%
  items_used: 38264
  quota_used: 33554432
  arena_size: 33554432
  arena_used: 1234296
...

With our own slab arena we need a way to obtain information about arena.
It's a reason why new module function memcached.slab.info() was
introduced. Output of memcached.slab.info() is similar to output
produced by `box.slab.info()`, but without items_size, items_used,
items_used_ratio and arena_used.

tarantool> mc = require('memcached')
creating arena with 4294967295 bytes...
allocate slab cache with slab size 4194304
---
...

tarantool> mc.slab.info()
---
- quota_used: 0
  quota_size: 4294967296
  quota_used_ratio: 0%
...

1. https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_slab/slab_info/

Follows up #96
  • Loading branch information
ligurio committed Apr 6, 2022
1 parent d7d0384 commit 0338d03
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 1 deletion.
1 change: 1 addition & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
std = "luajit"
globals = {
"box",
"tonumber64",
}
ignore = {
-- Accessing an undefined field of a global variable <package>.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ END
## API

* `local memcached = require('memcached')` - acquire a library handle
* `local slab = memcached.slab.info()` - show information about slab arena and quota.
* `local instance = memcached.create(<name>, <uri>, <opts>)` - create a new instance, register it and run
- `name` - a string with instance name
- `uri` - a string with uri to bind to, for example: `0.0.0.0:11211` (only TCP is supported now)
Expand Down
29 changes: 29 additions & 0 deletions memcached/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ struct memcached_stat {
uint64_t auth_errors;
};

struct slab_arena_info {
size_t quota_size;
size_t quota_used;
double quota_used_ratio;
};

void
memcached_set_opt (struct memcached_service *srv, int opt, ...);

Expand Down Expand Up @@ -86,6 +92,9 @@ void memcached_slab_arena_create();

void memcached_slab_cache_create();

struct slab_arena_info *
memcached_slab_arena_info();

int memcached_start (struct memcached_service *);
void memcached_stop (struct memcached_service *);
void memcached_free (struct memcached_service *);
Expand Down Expand Up @@ -237,6 +246,12 @@ local RUNNING = 'r'
local STOPPED = 's'
local ERRORED = 'e'

local slab_arena_info_table = {
'quota_size',
'quota_used',
'quota_used_ratio',
}

local stat_table = {
'total_items', 'curr_items',
'curr_conns', 'total_conns',
Expand Down Expand Up @@ -390,6 +405,17 @@ local function memcached_get(name)
return memcached_services[name]
end

local function memcached_slab_info()
local slab_arena_info = memcached_internal.memcached_slab_arena_info()
local slab_info = {}
for _, v in pairs(slab_arena_info_table) do
slab_info[v] = tonumber64(slab_arena_info[0][v])
end
slab_info['quota_used_ratio'] = string.format('%0.2f%%', slab_info['quota_used_ratio'])

return slab_info
end

local function memcached_init()
memcached_internal.memcached_slab_arena_create()
memcached_internal.memcached_slab_cache_create()
Expand All @@ -399,6 +425,9 @@ memcached_init()

return {
create = memcached_create_instance,
slab = {
info = memcached_slab_info,
},
get = memcached_get,
server = setmetatable({}, {
__index = memcached_services
Expand Down
29 changes: 29 additions & 0 deletions memcached/internal/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,35 @@ static struct slab_arena arena;
static struct slab_cache slabc;
static struct quota quota;

struct slab_arena_info {
uint64_t quota_size;
uint64_t quota_used;
double quota_used_ratio;
};

struct slab_arena_info slab_info;

/*
* Function produce an output with information of total quota size, used quota
* size and quota used ratio. Note that function relies on a fact that
* memcached uses a single slab cache for all allocations it made.
*/
struct slab_arena_info *
memcached_slab_arena_info()
{
/*
* How much quota has been booked - reflects the total
* size of slabs in various slab caches.
*/
slab_info.quota_used = (uint64_t)quota_used(arena.quota);

slab_info.quota_size = (uint64_t)quota_total(arena.quota);
slab_info.quota_used_ratio = 100 * ((double) slab_info.quota_used /
((double) slab_info.quota_size + 0.0001));

return &slab_info;
}

void
memcached_slab_arena_create()
{
Expand Down
3 changes: 3 additions & 0 deletions memcached/internal/alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ memcached_slab_cache_destroy();
struct slab_cache *
memcached_slab_cache();

struct slab_arena_info *
memcached_slab_arena_info();

#endif /* ALLOC_H_INCLUDED */
39 changes: 38 additions & 1 deletion test/common/module_api.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

local socket = require('socket')
local tap = require('tap')
local ffi = require('ffi')
package.cpath = './?.so;' .. package.cpath
local memcached = require('memcached')
local test = tap.test('memcached module api')
local abs = require('math').abs

-- 1. Open connection to memcached instance.
-- 2. Set a key 'key' to value 5.
Expand Down Expand Up @@ -46,13 +48,26 @@ if type(box.cfg) == 'function' then
box.schema.user.grant('guest', 'read,write,execute', 'universe')
end

test:plan(15)
test:plan(31)

-- memcached.server: module is initialized, no instances

test:istable(memcached.server, 'type of memcached.server is a table')
test:is(#memcached.server, 0, 'memcached.server is empty')

-- memcached.slab(): memcached module is initialized, no instances

test:istable(memcached.slab, 'memcached.slab (no instances) returns a table')
test:isfunction(memcached.slab.info, 'memcached.slab.info() (no instances) returns a function')
local slab_info = memcached.slab.info()
test:iscdata(slab_info.quota_size, ffi.typeof('uint64_t'),
'memcached.slab.info().quota_size (no instances) returns a cdata')
test:is(slab_info.quota_size, 4398046510080, 'memcached.slab.info().quota_size (no instances) is correct')
test:iscdata(slab_info.quota_used, ffi.typeof('uint64_t'),
'memcached.slab.info().quota_used (no instances) returns a cdata')
test:is(slab_info.quota_used, 0, 'memcached.slab.info().quota_used (no instances) is correct')
test:isstring(slab_info.quota_used_ratio, 'memcached.slab.info().quota_used_ratio (no instances) returns a string')

-- memcached.create(): instance 1

local mc_1_port = 11211
Expand Down Expand Up @@ -108,4 +123,26 @@ test:is(instance_2_info.name, mc_2.name, 'memcached.get(): name of instance 2 is
test:is(instance_2_info.space_name, mc_2.space_name, 'memcached.get(): space name of instance 2 is correct')
test:is(instance_2_info.space.id, mc_2.space.id, 'memcached.get(): space id of instance 2 is correct')

-- memcached.slab(): check numbers with created memcached instances

mc_1:start()
mc_2:start()

test:istable(memcached.slab, 'memcached.slab returns a table')
test:isfunction(memcached.slab.info, 'memcached.slab.info() returns a function')
local slab_info = memcached.slab.info()
test:istable(slab_info, 'memcached.slab.info() returns a table')
test:iscdata(slab_info.quota_size, ffi.typeof('uint64_t'), 'memcached.slab.info().quota_size returns a cdata')
test:is(slab_info.quota_size, 4398046510080, 'memcached.slab.info().quota_size is correct')
test:iscdata(slab_info.quota_used, ffi.typeof('uint64_t'), 'memcached.slab.info().quota_used returns a cdata')
test:is(slab_info.quota_used, 4194304, 'memcached.slab.info().quota_used is correct')
test:isstring(slab_info.quota_used_ratio, 'memcached.slab.info().quota_used_ratio returns a string')
local expected_ratio = tonumber(100 * slab_info.quota_used / slab_info.quota_size)
local actual_ratio = tonumber(slab_info.quota_used_ratio:match('^(%d.%d+)%%'))
local eps_is_correct = abs(actual_ratio - expected_ratio) < 1
test:is(eps_is_correct, true, 'memcached.slab.info().quota_used_ratio is correct')

mc_1:stop()
mc_2:stop()

os.exit(test:check() and 0 or 1)

0 comments on commit 0338d03

Please sign in to comment.