From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
To: tarantool-patches@freelists.org
Cc: kostja@tarantool.org
Subject: [tarantool-patches] [PATCH 09/10] swim: cache decoded payload in the Lua module
Date: Wed, 15 May 2019 22:36:46 +0300 [thread overview]
Message-ID: <ccdf4f8d5ee33b3507e8a3b39e8b481ac077fcfb.1557948687.git.v.shpilevoy@tarantool.org> (raw)
In-Reply-To: <cover.1557948686.git.v.shpilevoy@tarantool.org>
Users of Lua SWIM module likely will use Lua objects as a
payload. Lua objects are serialized into MessagePack
automatically, and deserialized back on other instances. But
deserialization of 1.2Kb payload on each member:payload()
invocation is quite heavy operation. This commit caches decoded
payloads to return them again until change.
A microbenchmark showed, that cached payload is returned ~100
times faster, than it is decoded each time. Even though a tested
payload was quite small and simple:
s:set_payload({a = 100, b = 200})
Even this payload is returned 100 times faster, and does not
affect GC.
Part of #3234
---
| 1 +
src/lib/swim/swim.c | 6 +++
src/lib/swim/swim.h | 12 +++++
src/lua/swim.lua | 39 +++++++++++---
test/swim/swim.result | 113 ++++++++++++++++++++++++++++++++++++++++
test/swim/swim.test.lua | 45 ++++++++++++++++
6 files changed, 208 insertions(+), 8 deletions(-)
--git a/extra/exports b/extra/exports
index b0e20e11f..8a38e9ef2 100644
--- a/extra/exports
+++ b/extra/exports
@@ -111,6 +111,7 @@ swim_member_payload
swim_member_ref
swim_member_unref
swim_member_is_dropped
+swim_member_is_payload_up_to_date
# Module API
diff --git a/src/lib/swim/swim.c b/src/lib/swim/swim.c
index fb2b1490b..9d67c4cb8 100644
--- a/src/lib/swim/swim.c
+++ b/src/lib/swim/swim.c
@@ -2080,3 +2080,9 @@ swim_member_payload(const struct swim_member *member, int *size)
*size = member->payload_size;
return member->payload;
}
+
+bool
+swim_member_is_payload_up_to_date(const struct swim_member *member)
+{
+ return member->is_payload_up_to_date;
+}
diff --git a/src/lib/swim/swim.h b/src/lib/swim/swim.h
index 8c4f8c3cf..5f3134cc4 100644
--- a/src/lib/swim/swim.h
+++ b/src/lib/swim/swim.h
@@ -220,6 +220,18 @@ swim_member_incarnation(const struct swim_member *member);
const char *
swim_member_payload(const struct swim_member *member, int *size);
+/**
+ * Check if member's payload is up to its incarnation. Sometimes
+ * it happens, that a member has changed payload, but other
+ * members learned only a new incarnation without the new payload.
+ * Then the payload is considered outdated, and is updated
+ * eventually later. The method is rather internal, and should not
+ * be used by any public API. It is exposed to implement decoded
+ * payload cache in the SWIM Lua module.
+ */
+bool
+swim_member_is_payload_up_to_date(const struct swim_member *member);
+
/**
* Reference a member. The member memory will be valid until unref
* is called.
diff --git a/src/lua/swim.lua b/src/lua/swim.lua
index 80c7375ab..852192479 100644
--- a/src/lua/swim.lua
+++ b/src/lua/swim.lua
@@ -96,6 +96,9 @@ ffi.cdef[[
bool
swim_member_is_dropped(const struct swim_member *member);
+
+ bool
+ swim_member_is_payload_up_to_date(const struct swim_member *member);
]]
-- Shortcut to avoid unnecessary lookups in 'ffi' table.
@@ -297,17 +300,36 @@ end
-- This member method tries to interpret payload as MessagePack,
-- and if fails, returns the payload as a string.
--
+-- This function caches its result. It means, that only first call
+-- actually decodes cdata payload. All the next calls return
+-- pointer to the same result, until payload is changed with a new
+-- incarnation.
+--
local function swim_member_payload(m)
local ptr = swim_check_member(m, 'member:payload()')
+ -- Two keys are needed. Incarnation is not enough, because a
+ -- new incarnation can be disseminated earlier than a new
+ -- payload. For example, via ACK messages.
+ local key1 = capi.swim_member_incarnation(ptr)
+ local key2 = capi.swim_member_is_payload_up_to_date(ptr)
+ if key1 == m.p_key1 and key2 == m.p_key2 then
+ return m.p
+ end
local cdata, size = swim_member_payload_raw(ptr)
+ local ok, result
if size == 0 then
- return ''
- end
- local ok, res = pcall(msgpack.decode, cdata, size)
- if not ok then
- return ffi.string(cdata, size)
+ result = ''
+ else
+ ok, result = pcall(msgpack.decode, cdata, size)
+ if not ok then
+ result = ffi.string(cdata, size)
+ end
end
- return res
+ -- Member bans new indexes. Only rawset() can be used.
+ rawset(m, 'p', result)
+ rawset(m, 'p_key1', key1)
+ rawset(m, 'p_key2', key2)
+ return result
end
--
@@ -352,8 +374,9 @@ local swim_member_mt = {
}
--
--- Wrap a SWIM member into a table with proper metamethods. Also
--- it is going to be used to cache a decoded payload.
+-- Wrap a SWIM member into a table with proper metamethods. The
+-- table-wrapper stores not only a pointer, but also cached
+-- decoded payload.
--
local function swim_member_wrap(ptr)
capi.swim_member_ref(ptr)
diff --git a/test/swim/swim.result b/test/swim/swim.result
index 91a8ab6e9..ef7203a37 100644
--- a/test/swim/swim.result
+++ b/test/swim/swim.result
@@ -817,6 +817,119 @@ iterate()
s:delete()
---
...
+--
+-- Payload caching.
+--
+s1 = swim.new({uuid = uuid(1), uri = uri(listen_port), heartbeat_rate = 0.01})
+---
+...
+s2 = swim.new({uuid = uuid(2), uri = uri(), heartbeat_rate = 0.01})
+---
+...
+s1_self = s1:self()
+---
+...
+s1:add_member({uuid = s2:self():uuid(), uri = s2:self():uri()})
+---
+- true
+...
+s2:add_member({uuid = s1_self:uuid(), uri = s1_self:uri()})
+---
+- true
+...
+s1:size()
+---
+- 2
+...
+s2:size()
+---
+- 2
+...
+s1_view = s2:member_by_uuid(s1_self:uuid())
+---
+...
+s1:set_payload({a = 100})
+---
+- true
+...
+p = s1_self:payload()
+---
+...
+s1_self:payload() == p
+---
+- true
+...
+while s1_view:payload() == '' do fiber.sleep(0.01) end
+---
+...
+p = s1_view:payload()
+---
+...
+s1_view:payload() == p
+---
+- true
+...
+-- Now a complex case. It is possible, that a new member's
+-- incarnation is learned, but new payload is not. Payload cache
+-- should correctly process that.
+s1:cfg({heartbeat_rate = 1000})
+---
+- true
+...
+s2:cfg({heartbeat_rate = 1000})
+---
+- true
+...
+s1:set_payload({a = 200})
+---
+- true
+...
+-- Via probe() S2 learns new incarnation of S1, but without new
+-- payload.
+s2:probe_member(s1_self:uri())
+---
+- true
+...
+s1_view:payload()
+---
+- {'a': 100}
+...
+s1_view:incarnation()
+---
+- 2
+...
+s1:cfg({heartbeat_rate = 0.01})
+---
+- true
+...
+s2:cfg({heartbeat_rate = 0.01})
+---
+- true
+...
+while s1_view:payload().a ~= 200 do fiber.sleep(0.01) end
+---
+...
+p = s1_view:payload()
+---
+...
+s1_view:payload() == p
+---
+- true
+...
+p
+---
+- {'a': 200}
+...
+s1_view:incarnation()
+---
+- 2
+...
+s1:delete()
+---
+...
+s2:delete()
+---
+...
test_run:cmd("clear filter")
---
- true
diff --git a/test/swim/swim.test.lua b/test/swim/swim.test.lua
index a744b2a29..c3387cf0a 100644
--- a/test/swim/swim.test.lua
+++ b/test/swim/swim.test.lua
@@ -262,4 +262,49 @@ s:add_member({uuid = uuid(3), uri = uri()})
iterate()
s:delete()
+--
+-- Payload caching.
+--
+s1 = swim.new({uuid = uuid(1), uri = uri(listen_port), heartbeat_rate = 0.01})
+s2 = swim.new({uuid = uuid(2), uri = uri(), heartbeat_rate = 0.01})
+s1_self = s1:self()
+s1:add_member({uuid = s2:self():uuid(), uri = s2:self():uri()})
+s2:add_member({uuid = s1_self:uuid(), uri = s1_self:uri()})
+s1:size()
+s2:size()
+s1_view = s2:member_by_uuid(s1_self:uuid())
+
+s1:set_payload({a = 100})
+p = s1_self:payload()
+s1_self:payload() == p
+
+while s1_view:payload() == '' do fiber.sleep(0.01) end
+p = s1_view:payload()
+s1_view:payload() == p
+
+-- Now a complex case. It is possible, that a new member's
+-- incarnation is learned, but new payload is not. Payload cache
+-- should correctly process that.
+
+s1:cfg({heartbeat_rate = 1000})
+s2:cfg({heartbeat_rate = 1000})
+
+s1:set_payload({a = 200})
+-- Via probe() S2 learns new incarnation of S1, but without new
+-- payload.
+s2:probe_member(s1_self:uri())
+s1_view:payload()
+s1_view:incarnation()
+
+s1:cfg({heartbeat_rate = 0.01})
+s2:cfg({heartbeat_rate = 0.01})
+while s1_view:payload().a ~= 200 do fiber.sleep(0.01) end
+p = s1_view:payload()
+s1_view:payload() == p
+p
+s1_view:incarnation()
+
+s1:delete()
+s2:delete()
+
test_run:cmd("clear filter")
--
2.20.1 (Apple Git-117)
next prev parent reply other threads:[~2019-05-15 19:36 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-15 19:36 [tarantool-patches] [PATCH 00/10] swim Lua API Vladislav Shpilevoy
2019-05-15 19:36 ` [tarantool-patches] [PATCH 01/10] swim: fix an assertion on attempt to chage timeouts Vladislav Shpilevoy
2019-05-16 7:28 ` [tarantool-patches] " Konstantin Osipov
2019-05-15 19:36 ` [tarantool-patches] [PATCH 10/10] swim: cache members in Lua member table Vladislav Shpilevoy
2019-05-16 7:31 ` [tarantool-patches] " Konstantin Osipov
2019-05-15 19:36 ` [tarantool-patches] [PATCH 02/10] swim: make swim_new_round() void Vladislav Shpilevoy
2019-05-16 7:31 ` [tarantool-patches] " Konstantin Osipov
2019-05-15 19:36 ` [tarantool-patches] [PATCH 03/10] swim: validate URI in swim_probe_member() Vladislav Shpilevoy
2019-05-16 7:31 ` [tarantool-patches] " Konstantin Osipov
2019-05-15 19:36 ` [tarantool-patches] [PATCH 04/10] swim: introduce Lua interface Vladislav Shpilevoy
2019-05-15 19:36 ` [tarantool-patches] [PATCH 05/10] swim: Lua bindings to manipulate member table Vladislav Shpilevoy
2019-05-16 7:32 ` [tarantool-patches] " Konstantin Osipov
2019-05-15 19:36 ` [tarantool-patches] [PATCH 06/10] swim: Lua bindings to access individual members Vladislav Shpilevoy
2019-05-15 19:36 ` [tarantool-patches] [PATCH 07/10] swim: pairs() function to iterate over member table Vladislav Shpilevoy
2019-05-15 19:36 ` [tarantool-patches] [PATCH 08/10] swim: allow to use cdata struct tt_uuid in Lua API Vladislav Shpilevoy
2019-05-15 19:36 ` Vladislav Shpilevoy [this message]
2019-05-16 7:36 ` [tarantool-patches] Re: [PATCH 09/10] swim: cache decoded payload in the Lua module Konstantin Osipov
2019-05-16 11:58 ` Vladislav Shpilevoy
2019-05-16 22:46 ` Vladislav Shpilevoy
2019-05-21 16:57 ` [tarantool-patches] Re: [PATCH 00/10] swim Lua API Vladislav Shpilevoy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ccdf4f8d5ee33b3507e8a3b39e8b481ac077fcfb.1557948687.git.v.shpilevoy@tarantool.org \
--to=v.shpilevoy@tarantool.org \
--cc=kostja@tarantool.org \
--cc=tarantool-patches@freelists.org \
--subject='Re: [tarantool-patches] [PATCH 09/10] swim: cache decoded payload in the Lua module' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox