Background Context
--------------------
The ``background`` context may be used for purposes that include analytics, monitoring, caching data for other context executions.
+- Background script execution default interval is 5 seconds.
+Global ``RGW`` Table
+--------------------
The ``RGW`` Lua table is accessible from all contexts and saves data written to it
during execution so that it may be read and used later during other executions, from the same context of a different one.
-
-- Background script execution default interval is 5 seconds.
-
- Each RGW instance has its own private and ephemeral ``RGW`` Lua table that is lost when the daemon restarts. Note that ``background`` context scripts will run on every instance.
+- The maximum number of entries in the table is 100,000. Each entry has a string key a value with a combined length of no more than 1KB.
+A Lua script will abort with an error if the number of entries or entry size exceeds these limits.
+- The ``RGW`` Lua table uses string indeces and can store values of type: string, integer, double and boolean
+
+Increment/Decrement Functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Since entries in the ``RGW`` table could be accessed from multiple places at the same time we need a way
+to atomically increment and decrement numeric values in it. For that the following functions should be used:
+- ``RGW.increment(<key>, [value])`` would increment the value of ``key`` by ``value`` if value is provided or by 1 if not
+- ``RGW.decrement(<key>, [value])`` would decrement the value of ``key`` by ``value`` if value is provided or by 1 if not
+- if the value of ``key`` is not numeric, the execution of the script would fail
+- if we try to increment or decrement by non-numeric values, the execution of the script would fail
-- The maximum number of entries in the table is 100,000. Each entry has a key and string value with a combined length of no more than 1KB.
- A Lua script will abort with an error if the number of entries or entry size exceeds these limits.
Lua Code Samples
----------------
namespace rgw::lua {
+const char* RGWTable::INCREMENT = "increment";
+const char* RGWTable::DECREMENT = "decrement";
+
+int RGWTable::increment_by(lua_State* L) {
+ const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(1)));
+ auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(2)));
+ auto decrement = lua_toboolean(L, lua_upvalueindex(3));
+
+ const auto args = lua_gettop(L);
+ const auto index = luaL_checkstring(L, 1);
+
+ // by default we increment by 1/-1
+ const auto default_inc = (decrement ? -1 : 1);
+ BackgroundMapValue inc_by = default_inc;
+ if (args == 2) {
+ if (lua_isinteger(L, 2)) {
+ inc_by = lua_tointeger(L, 2)*default_inc;
+ } else if (lua_isnumber(L, 2)){
+ inc_by = lua_tonumber(L, 2)*static_cast<double>(default_inc);
+ } else {
+ return luaL_error(L, "can increment only by numeric values");
+ }
+ }
+
+ std::unique_lock l(mtx);
+
+ const auto it = map->find(std::string(index));
+ if (it != map->end()) {
+ auto& value = it->second;
+ if (std::holds_alternative<double>(value) && std::holds_alternative<double>(inc_by)) {
+ value = std::get<double>(value) + std::get<double>(inc_by);
+ } else if (std::holds_alternative<int64_t>(value) && std::holds_alternative<int64_t>(inc_by)) {
+ value = std::get<int64_t>(value) + std::get<int64_t>(inc_by);
+ } else if (std::holds_alternative<double>(value) && std::holds_alternative<int64_t>(inc_by)) {
+ value = std::get<double>(value) + static_cast<double>(std::get<int64_t>(inc_by));
+ } else if (std::holds_alternative<int64_t>(value) && std::holds_alternative<double>(inc_by)) {
+ value = static_cast<double>(std::get<int64_t>(value)) + std::get<double>(inc_by);
+ } else {
+ mtx.unlock();
+ return luaL_error(L, "can increment only numeric values");
+ }
+ }
+
+ return 0;
+}
+
Background::Background(rgw::sal::Store* store,
CephContext* cct,
const std::string& luarocks_path,
lua_pushboolean(L, value);
}
+
struct RGWTable : EmptyMetaTable {
+
+ static const char* INCREMENT;
+ static const char* DECREMENT;
+
static std::string TableName() {return "RGW";}
static std::string Name() {return TableName() + "Meta";}
+ static int increment_by(lua_State* L);
+
static int IndexClosure(lua_State* L) {
- auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(2)));
const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(1)));
+ auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(2)));
const char* index = luaL_checkstring(L, 2);
+ if (strcasecmp(index, INCREMENT) == 0) {
+ lua_pushlightuserdata(L, map);
+ lua_pushlightuserdata(L, &mtx);
+ lua_pushboolean(L, false /*increment*/);
+ lua_pushcclosure(L, increment_by, THREE_UPVALS);
+ return ONE_RETURNVAL;
+ }
+ if (strcasecmp(index, DECREMENT) == 0) {
+ lua_pushlightuserdata(L, map);
+ lua_pushlightuserdata(L, &mtx);
+ lua_pushboolean(L, true /*decrement*/);
+ lua_pushcclosure(L, increment_by, THREE_UPVALS);
+ return ONE_RETURNVAL;
+ }
+
std::lock_guard l(mtx);
const auto it = map->find(std::string(index));
auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(2)));
const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(1)));
const auto index = luaL_checkstring(L, 2);
+
+ if (strcasecmp(index, INCREMENT) == 0 || strcasecmp(index, DECREMENT) == 0) {
+ return luaL_error(L, "increment/decrement are reserved function names for RGW");
+ }
std::unique_lock l(mtx);
EXPECT_EQ(get_table_value<int64_t>(lua_background, "size"), 5);
}
+TEST(TestRGWLuaBackground, TableIncrement)
+{
+ TestBackground lua_background("");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.increment("key1")
+ assert(RGW["key1"] == 43)
+ RGW.increment("key2")
+ assert(RGW["key2"] == 43.2)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementBy)
+{
+ TestBackground lua_background("");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.increment("key1", 10)
+ assert(RGW["key1"] == 52)
+ RGW.increment("key2", 10)
+ assert(RGW["key2"] == 52.2)
+ RGW.increment("key1", 0.2)
+ assert(RGW["key1"] == 52.2)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableDecrement)
+{
+ TestBackground lua_background("");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.decrement("key1")
+ assert(RGW["key1"] == 41)
+ RGW.decrement("key2")
+ assert(RGW["key2"] == 41.2)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableDecrementBy)
+{
+ TestBackground lua_background("");
+
+ const std::string request_script = R"(
+ RGW["key1"] = 42
+ RGW["key2"] = 42.2
+ RGW.decrement("key1", 10)
+ assert(RGW["key1"] == 32)
+ RGW.decrement("key2", 10)
+ assert(RGW["key2"] == 32.2)
+ RGW.decrement("key1", 0.8)
+ assert(RGW["key1"] == 31.2)
+ )";
+
+ DEFINE_REQ_STATE;
+
+ const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementValueError)
+{
+ TestBackground lua_background("");
+
+ std::string request_script = R"(
+ -- cannot increment string values
+ RGW["key1"] = "hello"
+ RGW.increment("key1")
+ )";
+
+ DEFINE_REQ_STATE;
+
+ auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_NE(rc, 0);
+
+ request_script = R"(
+ -- cannot increment bool values
+ RGW["key1"] = true
+ RGW.increment("key1")
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_NE(rc, 0);
+
+ request_script = R"(
+ -- cannot increment by string values
+ RGW["key1"] = 99
+ RGW.increment("key1", "kaboom")
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_NE(rc, 0);
+}
+
+TEST(TestRGWLuaBackground, TableIncrementError)
+{
+ TestBackground lua_background("");
+
+ std::string request_script = R"(
+ -- missing argument
+ RGW["key1"] = 11
+ RGW.increment()
+ )";
+
+ DEFINE_REQ_STATE;
+
+ auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_NE(rc, 0);
+
+ request_script = R"(
+ -- used as settable field
+ RGW.increment = 11
+ )";
+
+ rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", request_script, &lua_background);
+ ASSERT_NE(rc, 0);
+}
+