diff --git a/src/lua/swim.lua b/src/lua/swim.lua index 365d6ec6354c7e34567c6d2522a5991e18e3bb26..dfd11c7bdc6b379a23a45a228e9c597f85b73359 100644 --- a/src/lua/swim.lua +++ b/src/lua/swim.lua @@ -422,10 +422,19 @@ local swim_member_mt = { -- table-wrapper stores not only a pointer, but also cached -- decoded payload. -- -local function swim_member_wrap(ptr) - capi.swim_member_ref(ptr) - ffi.gc(ptr, capi.swim_member_unref) - return setmetatable({ptr = ptr}, swim_member_mt) +local function swim_wrap_member(s, ptr) + -- Lua tables can't normally work with cdata keys. Even when + -- cdata is a simple number, table can't do search by it. + local key = tonumber(ffi.cast('unsigned long', ptr)) + local cache = s.cache_table + local wrapped = cache[key] + if wrapped == nil then + capi.swim_member_ref(ptr) + ffi.gc(ptr, capi.swim_member_unref) + wrapped = setmetatable({ptr = ptr}, swim_member_mt) + cache[key] = wrapped + end + return wrapped end -- @@ -560,7 +569,8 @@ end -- into the member table. -- local function swim_self(s) - return swim_member_wrap(capi.swim_self(swim_check_instance(s, 'swim:self'))) + local ptr = swim_check_instance(s, 'swim:self') + return swim_wrap_member(s, capi.swim_self(ptr)) end -- @@ -574,7 +584,7 @@ local function swim_member_by_uuid(s, uuid) if m == nil then return nil end - return swim_member_wrap(m) + return swim_wrap_member(s, m) end -- @@ -618,13 +628,14 @@ end -- member object as a value. -- local function swim_pairs_next(ctx) - if ctx.swim.ptr == nil then + local s = ctx.swim + if s.ptr == nil then return swim_error_deleted() end local iterator = ctx.iterator local m = capi.swim_iterator_next(iterator) if m ~= nil then - m = swim_member_wrap(m) + m = swim_wrap_member(s, m) return m:uuid(), m end capi.swim_iterator_close(ffi.gc(iterator, nil)) @@ -760,6 +771,10 @@ local swim_not_configured_mt = { local swim_cfg_not_configured_mt = table.deepcopy(swim_cfg_mt) swim_cfg_not_configured_mt.__call = swim_cfg_first_call +-- Member cache stores week references so as to do not care about +-- removed members erasure - GC drops them automatically. +local cache_table_mt = { __mode = 'v' } + -- -- Create a new SWIM instance, and configure if @a cfg is -- provided. @@ -772,7 +787,8 @@ local function swim_new(cfg) ffi.gc(ptr, capi.swim_delete) local s = setmetatable({ ptr = ptr, - cfg = setmetatable({index = {}}, swim_cfg_not_configured_mt) + cfg = setmetatable({index = {}}, swim_cfg_not_configured_mt), + cache_table = setmetatable({}, cache_table_mt) }, swim_not_configured_mt) if cfg then local ok, err = s:cfg(cfg) diff --git a/test/swim/swim.result b/test/swim/swim.result index 24d25873f83d8196b33a8e3ebd15b1a6bb36c2cc..762c56cb672fc5aa07245d05978921d703369426 100644 --- a/test/swim/swim.result +++ b/test/swim/swim.result @@ -949,6 +949,77 @@ s1:delete() s2:delete() --- ... +-- +-- Member table cache in Lua. +-- +s = swim.new({uuid = uuid(1), uri = uri()}) +--- +... +self = s:self() +--- +... +s:self() == self +--- +- true +... +s:add_member({uuid = uuid(2), uri = 1}) +--- +- true +... +s2 = s:member_by_uuid(uuid(2)) +--- +... +s2 +--- +- uri: 127.0.0.1:<port> + status: alive + incarnation: 0 + uuid: 00000000-0000-1000-8000-000000000002 + payload_size: 0 +... +-- Next lookups return the same member table. +s2_old_uri = s2:uri() +--- +... +-- Check, that it is impossible to take removed member from the +-- cached table. +s:remove_member(uuid(2)) +--- +- true +... +s:member_by_uuid(uuid(2)) +--- +- null +... +-- GC automatically removes members from the member table. +self = nil +--- +... +s2 = nil +--- +... +collectgarbage('collect') +--- +- 0 +... +s.cache_table +--- +- [] +... +s:add_member({uuid = uuid(2), uri = 2}) +--- +- true +... +s2 = s:member_by_uuid(uuid(2)) +--- +... +s2:uri() ~= s2_old_uri +--- +- true +... +s:delete() +--- +... test_run:cmd("clear filter") --- - true diff --git a/test/swim/swim.test.lua b/test/swim/swim.test.lua index 78d465df49f0c2957617962ee6d3b6bcdbe19bb4..91894c16f647fed0a0a4728b1c1cc247239bbe24 100644 --- a/test/swim/swim.test.lua +++ b/test/swim/swim.test.lua @@ -313,4 +313,33 @@ s1_view:incarnation() s1:delete() s2:delete() +-- +-- Member table cache in Lua. +-- +s = swim.new({uuid = uuid(1), uri = uri()}) +self = s:self() +s:self() == self + +s:add_member({uuid = uuid(2), uri = 1}) +s2 = s:member_by_uuid(uuid(2)) +s2 +-- Next lookups return the same member table. +s2_old_uri = s2:uri() + +-- Check, that it is impossible to take removed member from the +-- cached table. +s:remove_member(uuid(2)) +s:member_by_uuid(uuid(2)) + +-- GC automatically removes members from the member table. +self = nil +s2 = nil +collectgarbage('collect') +s.cache_table +s:add_member({uuid = uuid(2), uri = 2}) +s2 = s:member_by_uuid(uuid(2)) +s2:uri() ~= s2_old_uri + +s:delete() + test_run:cmd("clear filter")