[tarantool-patches] [PATCH 10/10] swim: cache members in Lua member table
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Wed May 15 22:36:38 MSK 2019
Each time a member was returned from a SWIM instance object, it
was wrapped by a table with a special metatable, cached payload.
But next the same lookup returned a new table. It
- created garbage as a new member wrapper;
- lost cached decoded payload.
This commit caches in a private table all wrapped members and
returns an existing wrapper on a next lookup. A microbenchmark
showed, that cached result retrieval is 10 times faster, than
each time create a new table.
Cache table keeps week references - it means, that when a member
object looses all its references in a user's application, it is
automatically dropped from the table.
Part of #3234
---
src/lua/swim.lua | 34 ++++++++++++++-----
test/swim/swim.result | 75 +++++++++++++++++++++++++++++++++++++++++
test/swim/swim.test.lua | 30 +++++++++++++++++
3 files changed, 130 insertions(+), 9 deletions(-)
diff --git a/src/lua/swim.lua b/src/lua/swim.lua
index 852192479..35ceea669 100644
--- a/src/lua/swim.lua
+++ b/src/lua/swim.lua
@@ -378,10 +378,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
--
@@ -516,7 +525,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
--
@@ -530,7 +540,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
--
@@ -590,13 +600,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))
@@ -732,6 +743,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.
@@ -744,7 +759,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 ef7203a37..531903e7f 100644
--- a/test/swim/swim.result
+++ b/test/swim/swim.result
@@ -930,6 +930,81 @@ 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 == s:member_by_uuid(uuid(2))
+---
+- true
+...
+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 c3387cf0a..3b0807f1e 100644
--- a/test/swim/swim.test.lua
+++ b/test/swim/swim.test.lua
@@ -307,4 +307,34 @@ 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 == s:member_by_uuid(uuid(2))
+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")
--
2.20.1 (Apple Git-117)
More information about the Tarantool-patches
mailing list