diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index ab8d735ca210026600449a6fe5a1b9cf4bca9c49..cbc988fa95b285778b9b6700c8c018194f914c8c 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 37c5799c410a77342979cd48cb205868ab0baba8..a8e94cd35b23e3bb79243c6d4a555d99d28945d4 100644 --- a/include/session.h +++ b/include/session.h @@ -104,3 +104,6 @@ session_init(); void session_free(); + +void +session_storage_cleanup(int sid); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b1c98c9cd2e480709343bbc4fe244819329c964..f96545ee8cce465beb663cf1f17288b43ea7331a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,6 +62,7 @@ set_property(DIRECTORY PROPERTY CLEAN_NO_CUSTOM true) 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) add_custom_target(generate_lua_sources WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/box diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc index e047033b5fcd184c3e43c59c2f76739fb839e113..509f44091f6e9a0252d3277f0ea8e282e7e2ebe5 100644 --- a/src/box/box_lua.cc +++ b/src/box/box_lua.cc @@ -62,7 +62,7 @@ static const char *lua_sources[] = { box_lua, box_net_lua, misc_lua, sql_lua, NU * Lua coroutines (lua_newthread()) to have multiple * procedures running at the same time. */ -static lua_State *root_L; +lua_State *root_L; /* * Functions, exported in box_lua.h should have prefix diff --git a/src/lua/init.cc b/src/lua/init.cc index 8fa810a7618afc8a908ca3878298ba12127ca957..ed78c07fcf4a5c6b718a1e72b0ebff26070a68bd 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -78,7 +78,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 }; /** * Remember the output of the administrative console in the diff --git a/src/lua/session.cc b/src/lua/session.cc index 508c34247cbd689a78bc0d9fd9113d94090e57fe..05a21ce4baa85314de40c59b3cafe0b3ca6c79c8 100644 --- a/src/lua/session.cc +++ b/src/lua/session.cc @@ -40,6 +40,7 @@ extern "C" { #include "sio.h" static const char *sessionlib_name = "box.session"; +extern lua_State *root_L; /** * Return a unique monotonic session @@ -175,10 +176,26 @@ lbox_session_on_disconnect(struct lua_State *L) return lbox_session_set_trigger(L, &on_disconnect); } -static const struct luaL_reg lbox_session_meta [] = { - {"id", lbox_session_id}, - {NULL, NULL} -}; +void +session_storage_cleanup(int sid) +{ + static int ref = LUA_REFNIL; + + int top = lua_gettop(root_L); + + if (ref == LUA_REFNIL) { + lua_getfield(root_L, LUA_GLOBALSINDEX, "box"); + lua_getfield(root_L, -1, "session"); + lua_getmetatable(root_L, -1); + lua_getfield(root_L, -1, "aggregate_storage"); + ref = luaL_ref(root_L, LUA_REGISTRYINDEX); + } + lua_rawgeti(root_L, LUA_REGISTRYINDEX, ref); + + lua_pushnil(root_L); + lua_rawseti(root_L, -2, sid); + lua_settop(root_L, top); +} static const struct luaL_reg sessionlib[] = { {"id", lbox_session_id}, 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 eeb9c524eab9798e985352bf4eba031f537c0305..dcddeb38221a03dd81ef3ab02ca5ca46de63fc55 100644 --- a/src/session.cc +++ b/src/session.cc @@ -93,6 +93,7 @@ session_destroy(uint32_t sid) /* 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)"