diff --git a/src/box/lua/slab.cc b/src/box/lua/slab.cc index 131cd2344364ec3015639530627d7a12e1b7c07a..de50dba4fc7054e22b4dcd69d524687dc5a64557 100644 --- a/src/box/lua/slab.cc +++ b/src/box/lua/slab.cc @@ -36,6 +36,7 @@ extern "C" { } /* extern "C" */ #include "box/tuple.h" +#include "box/memtx_engine.h" #include "small/small.h" #include "small/quota.h" #include "memory.h" @@ -43,6 +44,12 @@ extern "C" { /** A callback passed into salloc_stat() and invoked for every slab class. */ extern "C" { +static int +small_stats_noop_cb(const struct mempool_stats * /* stats */, void * /* cb_ctx */) +{ + return 0; +} + static int small_stats_lua_cb(const struct mempool_stats *stats, void *cb_ctx) { @@ -95,40 +102,96 @@ small_stats_lua_cb(const struct mempool_stats *stats, void *cb_ctx) } /* extern "C" */ +static int +lbox_slab_stats(struct lua_State *L) +{ + struct small_stats totals; + lua_newtable(L); + /* + * List all slabs used for tuples and slabs used for + * indexes, with their stats. + */ + small_stats(&memtx_alloc, &totals, small_stats_lua_cb, L); + struct mempool_stats index_stats; + mempool_stats(&memtx_index_extent_pool, &index_stats); + small_stats_lua_cb(&index_stats, L); + + return 1; +} + + static int lbox_slab_info(struct lua_State *L) { struct small_stats totals; + /* + * List all slabs used for tuples and slabs used for + * indexes, with their stats. + */ lua_newtable(L); - lua_pushstring(L, "slabs"); - lua_newtable(L); - small_stats(&memtx_alloc, &totals, small_stats_lua_cb, L); + small_stats(&memtx_alloc, &totals, small_stats_noop_cb, L); + struct mempool_stats index_stats; + mempool_stats(&memtx_index_extent_pool, &index_stats); + + struct slab_arena *tuple_arena = memtx_alloc.cache->arena; + struct quota *memtx_quota = tuple_arena->quota; + double ratio; + char ratio_buf[32]; + + ratio = 100 * ((double) totals.used + / ((double) totals.total + 0.0001)); + snprintf(ratio_buf, sizeof(ratio_buf), "%0.1lf%%", ratio); + + /* + * Fragmentation factor for tuples. Don't account indexes, + * even if they are fragmented, there is nothing people + * can do about it. + */ + lua_pushstring(L, "items_used_ratio"); + lua_pushstring(L, ratio_buf); lua_settable(L, -3); + /** How much address space has been already touched */ + lua_pushstring(L, "arena_size"); + luaL_pushuint64(L, totals.total + index_stats.totals.total); + lua_settable(L, -3); + /** + * How much of this formatted address space is used for + * actual data. + */ lua_pushstring(L, "arena_used"); - luaL_pushuint64(L, totals.used); + luaL_pushuint64(L, totals.used + index_stats.totals.used); lua_settable(L, -3); - lua_pushstring(L, "arena_size"); - luaL_pushuint64(L, totals.total); + /* + * This is pretty much the same as + * box.cfg.slab_alloc_arena, but in bytes + */ + lua_pushstring(L, "quota_size"); + luaL_pushuint64(L, quota_total(memtx_quota)); lua_settable(L, -3); - char value[32]; - double items_used_ratio = 100 - * ((double)totals.used) - / ((double)memtx_alloc.cache->arena->prealloc + 0.0001); - snprintf(value, sizeof(value), "%0.1lf%%", items_used_ratio); - lua_pushstring(L, "items_used_ratio"); - lua_pushstring(L, value); + /* + * How much quota has been booked - reflects the total + * size of slabs in various slab caches. + */ + lua_pushstring(L, "quota_used"); + luaL_pushuint64(L, quota_used(memtx_quota)); lua_settable(L, -3); - double arena_used_ratio = 100 - * ((double)memtx_alloc.cache->arena->used) - / ((double)memtx_alloc.cache->arena->prealloc + 0.0001); - snprintf(value, sizeof(value), "%0.1lf%%", arena_used_ratio); + /** + * This should be the same as arena_size/arena_used, however, + * don't trust totals in the most important monitoring + * factor, it's the quota that give you OOM error in the + * end of the day. + */ + ratio = 100 * ((double) quota_used(memtx_quota) / + ((double) quota_total(memtx_quota) + 0.0001)); + snprintf(ratio_buf, sizeof(ratio_buf), "%0.1lf%%", ratio); + lua_pushstring(L, "arena_used_ratio"); - lua_pushstring(L, value); + lua_pushstring(L, ratio_buf); lua_settable(L, -3); return 1; @@ -144,7 +207,7 @@ lbox_runtime_info(struct lua_State *L) lua_settable(L, -3); lua_pushstring(L, "maxalloc"); - luaL_pushuint64(L, quota_get(runtime.quota)); + luaL_pushuint64(L, quota_total(runtime.quota)); lua_settable(L, -3); return 1; @@ -169,6 +232,10 @@ box_lua_slab_init(struct lua_State *L) lua_pushcfunction(L, lbox_slab_info); lua_settable(L, -3); + lua_pushstring(L, "stats"); + lua_pushcfunction(L, lbox_slab_stats); + lua_settable(L, -3); + lua_pushstring(L, "check"); lua_pushcfunction(L, lbox_slab_check); lua_settable(L, -3); diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc index b7605f6d4bd5c851999e5839ecd442492401a95a..81fc282818dcf33bc4f294b0d55490cd13b84c8b 100644 --- a/src/box/memtx_engine.cc +++ b/src/box/memtx_engine.cc @@ -52,12 +52,17 @@ #include "errinj.h" #include "scoped_guard.h" -/** For all memory used by all indexes. */ +/** For all memory used by all indexes. + * If you decide to use memtx_index_arena or + * memtx_index_slab_cache for anything other than + * memtx_index_extent_pool, make sure this is reflected in + * box.slab.info(), @sa lua/slab.cc + */ extern struct quota memtx_quota; static bool memtx_index_arena_initialized = false; static struct slab_arena memtx_index_arena; -static struct slab_cache memtx_index_arena_slab_cache; -static struct mempool memtx_index_extent_pool; +static struct slab_cache memtx_index_slab_cache; +struct mempool memtx_index_extent_pool; /** * To ensure proper statement-level rollback in case * of out of memory conditions, we maintain a number @@ -848,10 +853,10 @@ memtx_index_arena_init() panic_syserror("failed to initialize index arena"); } /* Creating slab cache */ - slab_cache_create(&memtx_index_arena_slab_cache, &memtx_index_arena); + slab_cache_create(&memtx_index_slab_cache, &memtx_index_arena); /* Creating mempool */ mempool_create(&memtx_index_extent_pool, - &memtx_index_arena_slab_cache, + &memtx_index_slab_cache, MEMTX_EXTENT_SIZE); /* Empty reserved list */ memtx_index_num_reserved_extents = 0; diff --git a/src/box/memtx_engine.h b/src/box/memtx_engine.h index ed0561f1c846c3c33511013cb2e1443f18119709..1484eadcf60971db8edd8fd576e2c435edc6ff74 100644 --- a/src/box/memtx_engine.h +++ b/src/box/memtx_engine.h @@ -37,6 +37,9 @@ enum memtx_recovery_state { MEMTX_OK, }; +/** Memtx extents pool, available to statistics. */ +extern struct mempool memtx_index_extent_pool; + struct MemtxEngine: public Engine { MemtxEngine(); virtual Handler *open(); diff --git a/src/box/xlog.cc b/src/box/xlog.cc index 2b804114267371b48184f0eb7aaf7379d4dfced4..d9062acbded5f055d6a76e3d23d433ba2c02eee6 100644 --- a/src/box/xlog.cc +++ b/src/box/xlog.cc @@ -646,8 +646,6 @@ xlog_atfork(struct xlog **lptr) * fclose(). */ close(fileno(l->f)); - fclose(l->f); - free(l); *lptr = NULL; } } diff --git a/src/lib/small/quota.h b/src/lib/small/quota.h index 70ceb8407f90f6c7710002de7406d3910e1d459b..da5f23fc51d3270d9538e00a040a22a10feb690d 100644 --- a/src/lib/small/quota.h +++ b/src/lib/small/quota.h @@ -77,7 +77,7 @@ quota_init(struct quota *quota, size_t total) * Get current quota limit */ static inline size_t -quota_get(const struct quota *quota) +quota_total(const struct quota *quota) { return (quota->value >> 32) * QUOTA_UNIT_SIZE; } diff --git a/src/lib/small/slab_arena.c b/src/lib/small/slab_arena.c index a233e538fc0bde3f6a7605c710b1f483c6c15874..59ca29846b6180752c6dc584d14288eecc4229e1 100644 --- a/src/lib/small/slab_arena.c +++ b/src/lib/small/slab_arena.c @@ -132,7 +132,7 @@ slab_arena_create(struct slab_arena *arena, struct quota *quota, arena->quota = quota; /** Prealloc can not be greater than the quota */ - prealloc = MIN(prealloc, quota_get(quota)); + prealloc = MIN(prealloc, quota_total(quota)); /** Extremely large sizes can not be aligned properly */ prealloc = MIN(prealloc, SIZE_MAX - arena->slab_size); /* Align prealloc around a fixed number of slabs. */ diff --git a/test/app/snapshot.test.lua b/test/app/snapshot.test.lua index c98cbf9a16b381852d202c34039bb2e8359bb3c0..a2d78590a083244187636bb4f0e8ad7c00fe6580 100755 --- a/test/app/snapshot.test.lua +++ b/test/app/snapshot.test.lua @@ -5,9 +5,11 @@ fiber = require('fiber') -- -- Check that Tarantool creates ADMIN session for #! script -- +continue_snapshoting = true + function noise() fiber.name('noise-'..fiber.id()) - while true do + while continue_snapshoting do if box.space.test:len() < 300000 then local value = string.rep('a', math.random(255)+1) box.space.test:auto_increment{fiber.time64(), value} @@ -18,7 +20,7 @@ end function purge() fiber.name('purge-'..fiber.id()) - while true do + while continue_snapshoting do local min = box.space.test.index.primary:min() if min ~= nil then box.space.test:delete{min[1]} @@ -27,18 +29,13 @@ function purge() end end -continue_snapshoting = true - function snapshot(lsn) fiber.name('snapshot') - while true do + while continue_snapshoting do local new_lsn = box.info.server.lsn if new_lsn ~= lsn then lsn = new_lsn; - box.snapshot() - end - if not continue_snapshoting then - break + pcall(box.snapshot) end fiber.sleep(0.001) end diff --git a/test/box/admin.result b/test/box/admin.result index 126e00164fc920df5be100ae72c93989569280a5..925cc525d6d994fd4082e9dfe754983445dfa38d 100644 --- a/test/box/admin.result +++ b/test/box/admin.result @@ -118,24 +118,25 @@ end; ... function test_box_slab_info() local tmp = box.slab.info() + local tmp_slabs = box.slab.stats() local cdata = {'arena_size', 'arena_used'} local failed = {} - if type(tmp.slabs) == 'table' then - for name, tbl in ipairs(tmp.slabs) do + if type(tmp_slabs) == 'table' then + for name, tbl in ipairs(tmp_slabs) do local bl, fld = test_slab(tbl) if bl == true then tmp[name] = nil else for k, v in ipairs(fld) do - table.append(failed, v) + table.insert(failed, v) end end end else - table.append(failed, 'box.slab.info().slabs is not ok') + table.insert(failed, 'box.slab.info().slabs is not ok') end - if #tmp.slabs == 0 then - tmp.slabs = nil + if #tmp_slabs == 0 then + tmp_slabs = nil end for k, v in ipairs(cdata) do if check_type(tmp[v], 'number') == false then @@ -164,7 +165,7 @@ function test_fiber(tbl) if type(tbl.backtrace) == 'table' and #tbl.backtrace > 0 then tbl.backtrace = nil else - table.append(failed, 'backtrace') + table.insert(failed, 'backtrace') end if #tbl > 0 or #failed > 0 then return false, failed @@ -183,7 +184,7 @@ function test_box_fiber_info() tmp[name] = nil else for k, v in ipairs(fld) do - table.append(failed, v) + table.insert(failed, v) end end end diff --git a/test/box/admin.test.lua b/test/box/admin.test.lua index 5744c28ade11321b5a83077e5cb899b5511d9c1e..0a7e6440c8c0244562159c0b2a275752db78a8ab 100644 --- a/test/box/admin.test.lua +++ b/test/box/admin.test.lua @@ -72,24 +72,25 @@ end; function test_box_slab_info() local tmp = box.slab.info() + local tmp_slabs = box.slab.stats() local cdata = {'arena_size', 'arena_used'} local failed = {} - if type(tmp.slabs) == 'table' then - for name, tbl in ipairs(tmp.slabs) do + if type(tmp_slabs) == 'table' then + for name, tbl in ipairs(tmp_slabs) do local bl, fld = test_slab(tbl) if bl == true then tmp[name] = nil else for k, v in ipairs(fld) do - table.append(failed, v) + table.insert(failed, v) end end end else - table.append(failed, 'box.slab.info().slabs is not ok') + table.insert(failed, 'box.slab.info().slabs is not ok') end - if #tmp.slabs == 0 then - tmp.slabs = nil + if #tmp_slabs == 0 then + tmp_slabs = nil end for k, v in ipairs(cdata) do if check_type(tmp[v], 'number') == false then @@ -117,7 +118,7 @@ function test_fiber(tbl) if type(tbl.backtrace) == 'table' and #tbl.backtrace > 0 then tbl.backtrace = nil else - table.append(failed, 'backtrace') + table.insert(failed, 'backtrace') end if #tbl > 0 or #failed > 0 then return false, failed @@ -135,7 +136,7 @@ function test_box_fiber_info() tmp[name] = nil else for k, v in ipairs(fld) do - table.append(failed, v) + table.insert(failed, v) end end end diff --git a/test/box/misc.result b/test/box/misc.result index 2dd1329d52d3bdf025a153863ce47d15b26837d7..c119f075b5dba51433ef2a6721a3600f64532bcd 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -187,7 +187,7 @@ box.slab.info().arena_size > 0; --- - true ... -string.match(tostring(box.slab.info().slabs), '^table:') ~= nil; +string.match(tostring(box.slab.stats()), '^table:') ~= nil; --- - true ... @@ -201,11 +201,12 @@ end; ... t; --- -- - arena_used_ratio +- - quota_used + - arena_used_ratio - items_used_ratio - - arena_used - arena_size - - slabs + - quota_size + - arena_used ... box.runtime.info().used > 0; --- diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua index 97602d056833a21f096b8379e8e86d96aff96bcc..daaf1f71774a7fcce067c455a0b1d50a97c1540d 100644 --- a/test/box/misc.test.lua +++ b/test/box/misc.test.lua @@ -71,7 +71,7 @@ t; string.match(tostring(box.slab.info()), '^table:') ~= nil; box.slab.info().arena_used >= 0; box.slab.info().arena_size > 0; -string.match(tostring(box.slab.info().slabs), '^table:') ~= nil; +string.match(tostring(box.slab.stats()), '^table:') ~= nil; t = {}; for k, v in pairs(box.slab.info()) do table.insert(t, k) diff --git a/test/unit/quota.cc b/test/unit/quota.cc index f1cf7ae3de4b074bd95e468f8b27af03a7bd5293..31c6d0a2de15ef3ec20b0ed098b30d2b2b72eb84 100644 --- a/test/unit/quota.cc +++ b/test/unit/quota.cc @@ -83,7 +83,7 @@ main(int n, char **a) long set_success_count = 0; long use_success_count = 0; for (size_t i = 0; i < THREAD_CNT; i++) { - if (datum[i].last_lim_set == quota_get("a)) + if (datum[i].last_lim_set == quota_total("a)) one_set_successed = true; total_alloc += datum[i].use_change; use_success_count += datum[i].use_change_success; diff --git a/test/unit/slab_arena.c b/test/unit/slab_arena.c index fe71bc713454bd8cd3041f79c2633d04dbb7b919..2f7a5b48d0f37a8f7b555d90049209caf010743e 100644 --- a/test/unit/slab_arena.c +++ b/test/unit/slab_arena.c @@ -11,7 +11,7 @@ slab_arena_print(struct slab_arena *arena) { printf("arena->prealloc = %zu\narena->maxalloc = %zu\n" "arena->used = %zu\narena->slab_size = %u\n", - arena->prealloc, quota_get(arena->quota), + arena->prealloc, quota_total(arena->quota), arena->used, arena->slab_size); }