diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index b2976287c3f95fce3aba5d3efda37fb7bc79bd96..40143a9ee9fa2ef8bf8c772898d71385b8c856fc 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -2149,6 +2149,16 @@ event. is not connected.</simpara></listitem> </varlistentry> + <varlistentry> + <term> + <emphasis role="lua">box.session.storage</emphasis> + </term> + <listitem><simpara>A virtual table that is local for each session. + It can be used to store session-specific values. The lifetime is the + same as the session's (until disconnect). + </simpara></listitem> + </varlistentry> + </variablelist> <para> This module also makes it possible to define triggers on connect diff --git a/include/session.h b/include/session.h index 60719cbc17848cefa169c1857a3918c7c567019e..3695eb049220eb2f0ba0c5a00a04a2fe39243389 100644 --- a/include/session.h +++ b/include/session.h @@ -93,3 +93,6 @@ session_init(); void session_free(); + +void +session_storage_cleanup(int sid); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cbbb0b56d62f89bcd0cb5cd05df32d9b5f6e20a..a98372c62609aa17dd2cb12d603a139f4d42d929 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ endif() file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/lua) set(lua_sources) lua_source(lua_sources lua/uuid.lua) +lua_source(lua_sources lua/session.lua) set(bin_sources) bin_source(bin_sources bootstrap.snap bootstrap.h) diff --git a/src/lua/init.cc b/src/lua/init.cc index 66906f35dc6c3c0428b291fe8f25ae5c7405e23b..3fda445bc7d82b614f675cd786869ddeacfbd558 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -80,7 +80,8 @@ struct lua_State *tarantool_L; /* contents of src/lua/ files */ extern char uuid_lua[]; -static const char *lua_sources[] = { uuid_lua, NULL }; +extern char session_lua[]; +static const char *lua_sources[] = { uuid_lua, session_lua, NULL }; /* * {{{ box Lua library: common functions diff --git a/src/lua/session.cc b/src/lua/session.cc index 1036ce04a87be08dfeeee3197b936db249953ef6..57c5dfacf5c0eb4547526d93ad8b117c3c6bf538 100644 --- a/src/lua/session.cc +++ b/src/lua/session.cc @@ -117,6 +117,28 @@ lbox_session_on_disconnect(struct lua_State *L) lbox_session_run_trigger); } +void +session_storage_cleanup(int sid) +{ + static int ref = LUA_REFNIL; + struct lua_State *L = tarantool_L; + + int top = lua_gettop(L); + + if (ref == LUA_REFNIL) { + lua_getfield(L, LUA_GLOBALSINDEX, "box"); + lua_getfield(L, -1, "session"); + lua_getmetatable(L, -1); + lua_getfield(L, -1, "aggregate_storage"); + ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + + lua_pushnil(L); + lua_rawseti(L, -2, sid); + lua_settop(L, top); +} + void tarantool_lua_session_init(struct lua_State *L) { diff --git a/src/lua/session.lua b/src/lua/session.lua new file mode 100644 index 0000000000000000000000000000000000000000..e3e4bd7cd7c41d04180607764ed84ec94479528e --- /dev/null +++ b/src/lua/session.lua @@ -0,0 +1,21 @@ +-- box.session.lua + +setmetatable(box.session, { + __index = function(tbl, idx) + + if idx ~= 'storage' then + return + end + + local sid = box.session.id() + + local mt = getmetatable(tbl) + + if mt.aggregate_storage[ sid ] == nil then + mt.aggregate_storage[ sid ] = {} + end + return mt.aggregate_storage[ sid ] + end, + + aggregate_storage = {} +}) diff --git a/src/session.cc b/src/session.cc index e320563a521d48f338572d5f4c5609656ca9863d..6973db23132cc9f1a5acffe7bb0ec141b21cd453 100644 --- a/src/session.cc +++ b/src/session.cc @@ -87,6 +87,7 @@ session_destroy(uint32_t sid) } catch (...) { /* catch all. */ } + session_storage_cleanup(sid); struct mh_i32ptr_node_t node = { sid, NULL }; mh_i32ptr_remove(session_registry, &node, NULL); } diff --git a/test/box/session.storage.result b/test/box/session.storage.result new file mode 100644 index 0000000000000000000000000000000000000000..1e36006a2c1d471f33ccf9449b88392c414f007f --- /dev/null +++ b/test/box/session.storage.result @@ -0,0 +1,81 @@ +lua dump = function(data) return "'" .. box.cjson.encode(data) .. "'" end +--- +... +lua type(box.session.id()) +--- + - number +... +lua box.session.unknown_field +--- + - nil +... +lua type(box.session.storage) +--- + - table +... +lua box.session.storage.abc = 'cde' +--- +... +lua box.session.storage.abc +--- + - cde +... +lua all = getmetatable(box.session).aggregate_storage +--- +... +lua type(box.session.storage) +--- + - table +... +lua type(box.session.storage.abc) +--- + - nil +... +lua box.session.storage.abc = 'def' +--- +... +lua box.session.storage.abc +--- + - def +... +lua box.session.storage.abc +--- + - cde +... +lua dump(all[box.session.id()]) +--- + - '{"abc":"def"}' +... +lua dump(all[box.session.id()]) +--- + - '{"abc":"cde"}' +... +lua tres1 = {} +--- +... +lua tres2 = {} +--- +... +lua for k,v in pairs(all) do table.insert(tres1, v.abc) end +--- +... +lua box.fiber.sleep(.01) +--- +... +lua for k,v in pairs(all) do table.insert(tres2, v.abc) end +--- +... +lua table.sort(tres1) +--- +... +lua table.sort(tres2) +--- +... +lua dump(tres1) +--- + - '["cde","def"]' +... +lua dump(tres2) +--- + - '["cde"]' +... diff --git a/test/box/session.storage.test b/test/box/session.storage.test new file mode 100644 index 0000000000000000000000000000000000000000..da6c785084516b7d835af3a268c089a45d40ad6c --- /dev/null +++ b/test/box/session.storage.test @@ -0,0 +1,35 @@ +# encoding: tarantool + +from lib.admin_connection import AdminConnection +from lib.box_connection import BoxConnection + +exec admin "lua dump = function(data) return \"'\" .. box.cjson.encode(data) .. \"'\" end" + +exec admin "lua type(box.session.id())" +exec admin "lua box.session.unknown_field" +exec admin "lua type(box.session.storage)" +exec admin "lua box.session.storage.abc = 'cde'" +exec admin "lua box.session.storage.abc" + +exec admin "lua all = getmetatable(box.session).aggregate_storage" + +con1 = AdminConnection('localhost', server.admin_port) +exec con1 "lua type(box.session.storage)" +exec con1 "lua type(box.session.storage.abc)" +exec con1 "lua box.session.storage.abc = 'def'" +exec con1 "lua box.session.storage.abc" +exec admin "lua box.session.storage.abc" +exec con1 "lua dump(all[box.session.id()])" +exec admin "lua dump(all[box.session.id()])" +exec admin "lua tres1 = {}" +exec admin "lua tres2 = {}" +exec admin "lua for k,v in pairs(all) do table.insert(tres1, v.abc) end" +con1.disconnect() +# to call session cleanup +exec admin "lua box.fiber.sleep(.01)" +exec admin "lua for k,v in pairs(all) do table.insert(tres2, v.abc) end" + +exec admin "lua table.sort(tres1)" +exec admin "lua table.sort(tres2)" +exec admin "lua dump(tres1)" +exec admin "lua dump(tres2)"