From: Nithya Balachandran Date: Thu, 19 Feb 2026 15:38:32 +0000 (+0000) Subject: rgw/lua: cache lua bytecode X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fheads%2Fwip-nbalacha-lua-74219;p=ceph-ci.git rgw/lua: cache lua bytecode Improves performance by caching the Lua bytecode. Fixes: https://tracker.ceph.com/issues/74219 Signed-off-by: Nithya Balachandran --- diff --git a/src/rgw/driver/motr/rgw_sal_motr.cc b/src/rgw/driver/motr/rgw_sal_motr.cc index f685bdf51f0..4cd90d0c7dc 100644 --- a/src/rgw/driver/motr/rgw_sal_motr.cc +++ b/src/rgw/driver/motr/rgw_sal_motr.cc @@ -3879,6 +3879,12 @@ int MotrStore::init_metadata_cache(const DoutPrefixProvider *dpp, return -ENOENT; } + std::tuple MotrLuaManager::get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) + { + return std::make_tuple("", -ENOENT); + } + int MotrLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) { return -ENOENT; diff --git a/src/rgw/driver/motr/rgw_sal_motr.h b/src/rgw/driver/motr/rgw_sal_motr.h index bce464faffa..04a7f0d6cd6 100644 --- a/src/rgw/driver/motr/rgw_sal_motr.h +++ b/src/rgw/driver/motr/rgw_sal_motr.h @@ -548,6 +548,9 @@ class MotrLuaManager : public StoreLuaManager { /** Get a script named with the given key from the backing store */ virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override; + /** Get the Lua bytecode if it exists, else script named with the given key from the backing store */ + virtual std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) override; /** Put a script named with the given key to the backing store */ virtual int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override; /** Delete a script named with the given key from the backing store */ diff --git a/src/rgw/driver/posix/rgw_sal_posix.cc b/src/rgw/driver/posix/rgw_sal_posix.cc index 50df5fb2904..29706ca8b14 100644 --- a/src/rgw/driver/posix/rgw_sal_posix.cc +++ b/src/rgw/driver/posix/rgw_sal_posix.cc @@ -1923,6 +1923,12 @@ int POSIXLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y, return -ENOENT; } +std::tuple POSIXLuaManager::get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) +{ + return std::make_tuple("", -ENOENT); +} + int POSIXLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) { return -ENOENT; diff --git a/src/rgw/driver/posix/rgw_sal_posix.h b/src/rgw/driver/posix/rgw_sal_posix.h index 488fbf6e016..4f1f6699409 100644 --- a/src/rgw/driver/posix/rgw_sal_posix.h +++ b/src/rgw/driver/posix/rgw_sal_posix.h @@ -458,6 +458,8 @@ public: virtual ~POSIXLuaManager() = default; virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override; + virtual std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) override; virtual int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override; virtual int del_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) override; virtual int add_package(const DoutPrefixProvider* dpp, optional_yield y, const std::string& package_name) override; diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc index 481822bb1cc..39c46d6b98b 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.cc +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -5335,8 +5335,81 @@ RadosLuaManager::RadosLuaManager(RadosStore* _s, const std::string& _luarocks_pa store(_s), pool((store->svc() && store->svc()->zone) ? store->svc()->zone->get_zone_params().log_pool : rgw_pool()), ioctx(*store->getRados()->get_lc_pool_ctx()), - packages_watcher(this) -{ } + packages_watcher(this), + scripts_watcher(this) +{ + if (!pool.empty()) { + //TODO: error check + // The packages and script rados objects are in different namespaces + rgw_init_ioctx(&scripts_watcher, store->getRados()->get_rados_handle(), pool, ioctx_scripts); + } +} + +uint64_t RadosLuaManager::get_watch_handle_for_script(const std::string& script_oid) { + auto search = script_watches.find(script_oid); + if (search != script_watches.end()) { + return search->second; + } + return 0; +} + +std::string RadosLuaManager::get_script_for_watch_handle(uint64_t handle) { + auto search = reverse_script_watches.find(handle); + if (search != reverse_script_watches.end()) { + return search->second; + } + return {}; +} + +int RadosLuaManager::watch_script(const DoutPrefixProvider* dpp, const std::string& script_oid) { + + if (get_watch_handle_for_script(script_oid) == 0) { + uint64_t w_handle; + auto r = ioctx_scripts.watch2(script_oid, &w_handle, &scripts_watcher); + if (r < 0) { + ldpp_dout(dpp, 1) << "ERROR: failed to watch " << script_oid + << ". error: " << cpp_strerror(r) << dendl; + if (r == -ENOENT) { + // Let the background thread know to update the script cache + lua_background->process_script_add(script_oid); + } + // Return error? + return r; + } + ldpp_dout(dpp, 20) << "INFO: inited watch on " << script_oid << " with handle " << w_handle << dendl; + script_watches.emplace(script_oid, w_handle); + reverse_script_watches.emplace(w_handle, script_oid); + } + return 0; +} + +int RadosLuaManager::unwatch_script(const DoutPrefixProvider* dpp, const std::string& script_oid) { + + if (!ioctx_scripts.is_valid()) { + ldpp_dout(dpp, 1) << "ERROR: invalid pool when unwatch Lua script " << script_oid << dendl; + return 0; + } + + auto w_handle = get_watch_handle_for_script(script_oid); + if (w_handle == 0) { + return 0; + } + + const auto r = ioctx_scripts.unwatch2(w_handle); + if (r < 0 && r != -ENOENT) { + ldpp_dout(dpp, 1) << "ERROR: failed to unwatch Lua script " << script_oid + << ". error: " << cpp_strerror(r) << dendl; + return r; + } + + script_watches.erase(script_oid); + reverse_script_watches.erase(w_handle); + + ldpp_dout(dpp, 20) << "Stopped watching for updates of Lua script " << script_oid + << " with handle: " << w_handle << dendl; + + return 0; +} int RadosLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) { @@ -5361,7 +5434,51 @@ int RadosLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y, return 0; } -int RadosLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) +std::tuple RadosLuaManager::get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) +{ + if (pool.empty()) { + ldpp_dout(dpp, 10) << "WARNING: missing pool when reading Lua script " << dendl; + return std::make_tuple("", 0); + } + std::vector lua_bytecode; + lua_bytecode.clear(); + // First try to get the bytecode + int r = lua_background->get_script_bytecode(key, lua_bytecode); + if (r == 0) { + return std::make_tuple(lua_bytecode, 0); + } + + std::string script; + bufferlist bl; + r = rgw_get_system_obj(store->svc()->sysobj, pool, key, bl, nullptr, nullptr, y, dpp); + if (r < 0) { + return std::make_tuple("", r); + } + + // The bytecode has not been cached yet. Let the lua background know. + lua_background->process_script_add(key); + + auto iter = bl.cbegin(); + try { + ceph::decode(script, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 1) << "ERROR : failed to decode Lua script " << key + << ", error = " << err.what() << dendl; + return std::make_tuple("", -EIO); + } + + r = watch_script(dpp, key); + if (r < 0) { + ldpp_dout(dpp, 10) << "WARNING: failed to watch Lua script: " << key << ", err:" + << r << dendl; + } + + return std::make_tuple(script, 0); +} + +int RadosLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key, const std::string& script) { if (pool.empty()) { ldpp_dout(dpp, 10) << "WARNING: missing pool when writing Lua script " << dendl; @@ -5375,6 +5492,11 @@ int RadosLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, return r; } + r = notify_script_update(dpp, key, y); + if (r < 0) { + ldpp_dout(dpp, 10) << "WARNING: failed to send Lua script update notification :" << key << ", err:" << r << dendl; + return r; + } return 0; } @@ -5392,6 +5514,40 @@ int RadosLuaManager::del_script(const DoutPrefixProvider* dpp, optional_yield y, return 0; } +void RadosLuaManager::ack_script_update(const DoutPrefixProvider* dpp, + uint64_t notify_id, uint64_t cookie, + int update_status) +{ + if (!ioctx_scripts.is_valid()) { + ldpp_dout(dpp, 10) << "WARNING: missing pool when acking Lua script update" << dendl; + return; + } + + std::string script_oid = get_script_for_watch_handle(cookie); + if (script_oid.empty()) { + ldpp_dout(dpp, 10) << "WARNING: failed to find script when acking Lua script update" << dendl; + return; + } + + bufferlist reply; + ceph::encode(update_status, reply); + ioctx_scripts.notify_ack(script_oid, notify_id, cookie, reply); +} + +void RadosLuaManager::handle_script_update_notify(const DoutPrefixProvider* dpp, + optional_yield y, uint64_t notify_id, + uint64_t cookie) +{ + std::string key = get_script_for_watch_handle(cookie); + if (key.empty()) { + ldpp_dout(dpp, 10) << "WARNING: missing Lua script for watch handle: " << cookie << dendl; + return; + } + // Let the background thread know to remove the bytecode from the cache + lua_background->process_script_add(key); + ack_script_update(dpp, notify_id, cookie, 0); +} + const std::string PACKAGE_LIST_OBJECT_NAME = "lua_package_allowlist"; int RadosLuaManager::add_package(const DoutPrefixProvider *dpp, optional_yield y, const std::string& package_name) @@ -5605,6 +5761,49 @@ int RadosLuaManager::reload_packages(const DoutPrefixProvider *dpp, optional_yie return 0; } +int RadosLuaManager::notify_script_update(const DoutPrefixProvider *dpp, const std::string& script_oid, optional_yield y) +{ + if (!ioctx_scripts.is_valid()) { + ldpp_dout(dpp, 10) << "WARNING: missing pool when attempting Lua script update notification" << dendl; + return -ENOENT; + } + bufferlist empty_bl; + bufferlist reply_bl; + const uint64_t timeout_ms = 0; + auto r = rgw_rados_notify(dpp, + ioctx_scripts, + script_oid, + empty_bl, timeout_ms, &reply_bl, y); + if (r < 0) { + ldpp_dout(dpp, 1) << "ERROR: failed to notify update on " << script_oid + << ". error: " << cpp_strerror(r) << dendl; + return r; + } + + std::vector acks; + std::vector timeouts; + ioctx_scripts.decode_notify_response(reply_bl, &acks, &timeouts); + if (timeouts.size() > 0) { + ldpp_dout(dpp, 1) << "ERROR: failed to notify update on " << script_oid + << ". error: timeout" << dendl; + return -EAGAIN; + } + for (auto& ack : acks) { + try { + auto iter = ack.payload_bl.cbegin(); + ceph::decode(r, iter); + } catch (buffer::error& err) { + ldpp_dout(dpp, 1) << "ERROR: couldn't decode Lua script update status for " + << script_oid << ", error: " << err.what() << dendl; + return -EINVAL; + } + if (r < 0) { + return r; + } + } + return 0; +} + void RadosLuaManager::PackagesWatcher::handle_notify(uint64_t notify_id, uint64_t cookie, uint64_t notifier_id, bufferlist &bl) { parent->handle_reload_notify(this, null_yield, notify_id, cookie); @@ -5633,6 +5832,38 @@ std::ostream& RadosLuaManager::PackagesWatcher::gen_prefix(std::ostream& out) co return out << "rgw lua package reloader: "; } +CephContext* RadosLuaManager::ScriptsWatcher::get_cct() const { + return parent->store->ctx(); +} + +unsigned RadosLuaManager::ScriptsWatcher::get_subsys() const { + return dout_subsys; +} + +std::ostream& RadosLuaManager::ScriptsWatcher::gen_prefix(std::ostream& out) const { + return out << "rgw lua scripts watcher: "; +} + +void RadosLuaManager::ScriptsWatcher::handle_notify(uint64_t notify_id, uint64_t cookie, uint64_t notifier_id, bufferlist &bl) +{ + parent->handle_script_update_notify(this, null_yield, notify_id, cookie); +} + +void RadosLuaManager::ScriptsWatcher::handle_error(uint64_t cookie, int err) +{ + std::string script_oid = parent->get_script_for_watch_handle(cookie); + if (script_oid.empty()) { + ldpp_dout(this, 5) << "ERROR: failed to find the script for watch handle: " + << cookie << dendl; + return; + } + ldpp_dout(this, 5) << "WARNING: restarting watch handler for script: " + << script_oid << ", err:" << err << dendl; + + parent->unwatch_script(this, script_oid); + parent->watch_script(this, script_oid); +} + int RadosRole::store_info(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y) { librados::Rados& rados = *store->getRados()->get_rados_handle(); diff --git a/src/rgw/driver/rados/rgw_sal_rados.h b/src/rgw/driver/rados/rgw_sal_rados.h index 152bb87413c..4d5ab11d4a2 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.h +++ b/src/rgw/driver/rados/rgw_sal_rados.h @@ -1224,19 +1224,48 @@ class RadosLuaManager : public StoreLuaManager { std::ostream& gen_prefix(std::ostream& out) const override; }; + class ScriptsWatcher : public librados::WatchCtx2, public DoutPrefixProvider { + RadosLuaManager* const parent; + public: + ScriptsWatcher(RadosLuaManager* _parent) : + parent(_parent) {} + ~ScriptsWatcher() override = default; + void handle_notify(uint64_t notify_id, uint64_t cookie, + uint64_t notifier_id, bufferlist& bl) override; + void handle_error(uint64_t cookie, int err) override; + + // DoutPrefixProvider iterface + CephContext* get_cct() const override; + unsigned get_subsys() const override; + std::ostream& gen_prefix(std::ostream& out) const override; + }; + RadosStore* const store; rgw_pool pool; librados::IoCtx& ioctx; + librados::IoCtx ioctx_scripts; PackagesWatcher packages_watcher; + ScriptsWatcher scripts_watcher; void ack_reload(const DoutPrefixProvider* dpp, uint64_t notify_id, uint64_t cookie, int reload_status); void handle_reload_notify(const DoutPrefixProvider* dpp, optional_yield y, uint64_t notify_id, uint64_t cookie); + void ack_script_update(const DoutPrefixProvider* dpp, uint64_t notify_id, uint64_t cookie, int reload_status); + void handle_script_update_notify(const DoutPrefixProvider* dpp, optional_yield y, uint64_t notify_id, uint64_t cookie); + int notify_script_update(const DoutPrefixProvider *dpp, const std::string& script_oid, optional_yield y); + uint64_t get_watch_handle_for_script(const std::string& script_oid); + std::string get_script_for_watch_handle(uint64_t handle); + uint64_t watch_handle = 0; + std::map script_watches; + std::map reverse_script_watches; public: RadosLuaManager(RadosStore* _s, const std::string& _luarocks_path); ~RadosLuaManager() override = default; + // To be used by the radosgw-admin process int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override; + // To be used by the radosgw process + std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) override; int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override; int del_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) override; int add_package(const DoutPrefixProvider* dpp, optional_yield y, const std::string& package_name) override; @@ -1245,6 +1274,9 @@ public: int reload_packages(const DoutPrefixProvider* dpp, optional_yield y) override; int watch_reload(const DoutPrefixProvider* dpp); int unwatch_reload(const DoutPrefixProvider* dpp); + int watch_script(const DoutPrefixProvider* dpp, const std::string& script_oid); + int unwatch_script(const DoutPrefixProvider* dpp, const std::string& script_oid); + }; class RadosRole : public RGWRole { diff --git a/src/rgw/rgw_appmain.cc b/src/rgw/rgw_appmain.cc index 2782da9f6cc..f1eb20d4665 100644 --- a/src/rgw/rgw_appmain.cc +++ b/src/rgw/rgw_appmain.cc @@ -577,6 +577,7 @@ void rgw::AppMain::init_lua() lua_background->start(); env.lua.background = lua_background.get(); static_cast(env.lua.manager.get())->watch_reload(dpp); + env.lua.manager.get()->set_lua_background(lua_background.get()); } #endif } /* init_lua */ diff --git a/src/rgw/rgw_lua.cc b/src/rgw/rgw_lua.cc index aacec7c7fe0..975ce19738b 100644 --- a/src/rgw/rgw_lua.cc +++ b/src/rgw/rgw_lua.cc @@ -90,6 +90,12 @@ int read_script(const DoutPrefixProvider *dpp, sal::LuaManager* manager, const s return manager ? manager->get_script(dpp, y, script_oid(ctx, tenant), script) : -ENOENT; } +std::tuple read_script_or_bytecode(const DoutPrefixProvider *dpp, sal::LuaManager* manager, + const std::string& tenant, optional_yield y, context ctx) +{ + return manager ? manager->get_script_or_bytecode(dpp, y, script_oid(ctx, tenant)) : std::make_tuple("", -ENOENT); +} + int write_script(const DoutPrefixProvider *dpp, sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx, const std::string& script) { return manager ? manager->put_script(dpp, y, script_oid(ctx, tenant), script) : -ENOENT; diff --git a/src/rgw/rgw_lua.h b/src/rgw/rgw_lua.h index 30b3272c4e4..a4ee24ff831 100644 --- a/src/rgw/rgw_lua.h +++ b/src/rgw/rgw_lua.h @@ -3,6 +3,7 @@ #include #include #include "rgw_lua_version.h" +#include "rgw_lua_types.h" #include "common/async/yield_context.h" #include "common/dout.h" #include "rgw_sal_fwd.h" @@ -41,6 +42,8 @@ int write_script(const DoutPrefixProvider *dpp, rgw::sal::LuaManager* manager, c // read the stored lua script from a context int read_script(const DoutPrefixProvider *dpp, rgw::sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx, std::string& script); +std::tuple read_script_or_bytecode(const DoutPrefixProvider *dpp, rgw::sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx); + // delete the stored lua script from a context int delete_script(const DoutPrefixProvider *dpp, rgw::sal::LuaManager* manager, const std::string& tenant, optional_yield y, context ctx); diff --git a/src/rgw/rgw_lua_background.cc b/src/rgw/rgw_lua_background.cc index 46aae8dc28f..0d0f34c91b6 100644 --- a/src/rgw/rgw_lua_background.cc +++ b/src/rgw/rgw_lua_background.cc @@ -56,6 +56,14 @@ int RGWTable::increment_by(lua_State* L) { return 0; } +static int bytecode_writer (lua_State *L, const void* p, size_t sz, void* ud) { + std::vector* buffer = static_cast*>(ud); + const char* bytes = static_cast(p); + buffer->insert(buffer->end(), bytes, bytes + sz); + return 0; +} + + Background::Background( CephContext* _cct, rgw::sal::LuaManager* _lua_manager, @@ -201,6 +209,7 @@ void Background::run() { perfcounter->inc((failed ? l_rgw_lua_script_fail : l_rgw_lua_script_ok), 1); } } + process_scripts(); std::unique_lock cond_lock(cond_mutex); cond.wait_for(cond_lock, std::chrono::seconds(execute_interval), [this]{return stopped;}); } @@ -218,5 +227,90 @@ void Background::set_manager(rgw::sal::LuaManager* _lua_manager) { lua_manager = _lua_manager; } +void Background::process_script_add(std::string script_oid) { + auto script_ptr = make_unique(std::move(script_oid)); + if (processing_q.push(script_ptr.get())) { + script_ptr.release(); + } +} + +void Background::process_scripts() { + std::set removed; + std::set updated_scripts; + + const auto count = processing_q.consume_all([&](std::string* s) { + std::unique_ptr sptr(s); + updated_scripts.insert(*sptr); + }); + + if (updated_scripts.empty()) { + return; + } + ldpp_dout(&dp, 20) << "INFO: Num scripts to process: " << count << dendl; + //updating = true; + std::unique_ptr lguard = initialize_lguard_state(); + if (!lguard) { + return; + } + std::string script; + for (const auto& key: updated_scripts) { + int r = lua_manager->get_script(&dp, null_yield, key, script); + if (r < 0 && r != -ENOENT) { + ldpp_dout(&dp, 10) << "ERROR: Failed to get script : " << key + << ". r = " << r << dendl; + // Clear the cache + removed.insert(key); + std::unique_lock lock(updating_mutex); + lua_bytecode_cache.erase(key); + continue; + } + if (r == -ENOENT) { + removed.insert(key); + std::unique_lock lock(updating_mutex); + lua_bytecode_cache.erase(key); + continue; + } + ldpp_dout(&dp, 20) << "INFO: processing script: " << key << dendl; + auto L = lguard->get(); + auto buffer = std::make_unique>(); + try { + if (luaL_loadstring(L, script.c_str()) == LUA_OK) { + lua_dump(L, bytecode_writer, buffer.get(), 0); + { + std::unique_lock lock(updating_mutex); + lua_bytecode_cache.insert_or_assign(key, std::move(buffer)); + } + lua_pop(L, 1); + removed.insert(key); + } else { + const std::string err(lua_tostring(L, -1)); + ldpp_dout(&dp, 1) << "Lua ERROR: failed to compile script : " << key + << ", error : " << err << dendl; + } + } catch (const std::runtime_error& e) { + ldpp_dout(&dp, 1) << "Lua ERROR: failed to compile script : " << key + << ", error : " << e.what() << dendl; + } + } + + //updating = false; + for (const auto& key: removed) { + updated_scripts.erase(key); + } +} + +int Background::get_script_bytecode(std::string script, std::vector& lua_bytecode) { + lua_bytecode.clear(); + std::shared_lock lock(updating_mutex); + auto itr = lua_bytecode_cache.find(script); + if(itr != lua_bytecode_cache.end()) { + lua_bytecode = *((itr->second).get()); + return 0; + } + + ldpp_dout(&dp, 20) << "INFO: lua script bytecode not found : " << script << dendl; + return -ENOENT; +} + } //namespace rgw::lua diff --git a/src/rgw/rgw_lua_background.h b/src/rgw/rgw_lua_background.h index 8745b7f3912..421e64c2e43 100644 --- a/src/rgw/rgw_lua_background.h +++ b/src/rgw/rgw_lua_background.h @@ -2,8 +2,11 @@ #include "common/dout.h" #include "rgw_common.h" #include +#include #include #include +#include +#include #include "rgw_lua_utils.h" #include "rgw_realm_reloader.h" @@ -151,12 +154,19 @@ private: std::mutex pause_mutex; std::condition_variable cond; + std::map>> lua_bytecode_cache; // script-name -> bytecode +// bool updating = false; + std::shared_mutex updating_mutex; + boost::lockfree::queue processing_q{16}; + void run(); std::string rgw_script; int read_script(); std::unique_ptr initialize_lguard_state(); + void process_scripts(); + public: Background(CephContext* _cct, rgw::sal::LuaManager* _lua_manager, @@ -178,6 +188,10 @@ private: void pause() override; // Does not actually use `Driver` argument. void resume(rgw::sal::Driver*) override; + + // for lua bytecode caching + void process_script_add(std::string script); + int get_script_bytecode(std::string key, std::vector& lua_bytecode); }; } //namespace rgw::lua diff --git a/src/rgw/rgw_lua_data_filter.cc b/src/rgw/rgw_lua_data_filter.cc index 9a5398709bf..8efd81d138f 100644 --- a/src/rgw/rgw_lua_data_filter.cc +++ b/src/rgw/rgw_lua_data_filter.cc @@ -103,11 +103,8 @@ int RGWObjFilter::execute(bufferlist& bl, off_t offset, const char* op_name) con // create the "RGW" table s->penv.lua.background->create_background_metatable(L); } - - // execute the lua script - if (luaL_dostring(L, script.c_str()) != LUA_OK) { - const std::string err(lua_tostring(L, -1)); - ldpp_dout(s, 1) << "Lua ERROR: " << err << dendl; + auto rc = rgw::lua::lua_execute(L, s, script); + if (rc) { return -EINVAL; } } catch (const std::runtime_error& e) { diff --git a/src/rgw/rgw_lua_data_filter.h b/src/rgw/rgw_lua_data_filter.h index 75596b64e61..ec0944a205c 100644 --- a/src/rgw/rgw_lua_data_filter.h +++ b/src/rgw/rgw_lua_data_filter.h @@ -8,11 +8,11 @@ namespace rgw::lua { class RGWObjFilter { req_state* const s; - const std::string script; + const rgw::lua::LuaCodeType script; public: RGWObjFilter(req_state* s, - const std::string& script) : + const rgw::lua::LuaCodeType& script) : s(s), script(script) {} int execute(bufferlist& bl, off_t offset, const char* op_name) const; @@ -23,7 +23,7 @@ class RGWGetObjFilter : public RGWGetObj_Filter { public: RGWGetObjFilter(req_state* s, - const std::string& script, + const rgw::lua::LuaCodeType& script, RGWGetObj_Filter* next) : RGWGetObj_Filter(next), filter(s, script) {} @@ -40,7 +40,7 @@ class RGWPutObjFilter : public rgw::putobj::Pipe { public: RGWPutObjFilter(req_state* s, - const std::string& script, + const rgw::lua::LuaCodeType& script, rgw::sal::DataProcessor* next) : rgw::putobj::Pipe(next), filter(s, script) {} diff --git a/src/rgw/rgw_lua_request.cc b/src/rgw/rgw_lua_request.cc index 6f65a4c7ff0..7e8ef34529e 100644 --- a/src/rgw/rgw_lua_request.cc +++ b/src/rgw/rgw_lua_request.cc @@ -788,8 +788,8 @@ int execute( OpsLogSink* olog, req_state* s, RGWOp* op, - const std::string& script, - int& script_return_code ) + const rgw::lua::LuaCodeType& code, + int& script_return_code) { lua_state_guard lguard(s->cct->_conf->rgw_lua_max_memory_per_state, s->cct->_conf->rgw_lua_max_runtime_per_state, s); @@ -824,18 +824,15 @@ int execute( if (s->penv.lua.background) { s->penv.lua.background->create_background_metatable(L); } + rc = rgw::lua::lua_execute(L, s, code); - // execute the lua script - if (luaL_dostring(L, script.c_str()) != LUA_OK) { - const std::string err(lua_tostring(L, -1)); - ldpp_dout(s, 1) << "Lua ERROR: " << err << dendl; - rc = -1; - } - if (lua_isinteger(L, -1)) { - script_return_code = static_cast(lua_tointeger(L, -1)); - ldpp_dout(s, 20) << "Lua script executed successfully and returned code: " << script_return_code << dendl; - } else { - ldpp_dout(s, 20) << "Lua script executed, but did not return an integer. Ignoring return code." << dendl; + if (!rc) { + if (lua_isinteger(L, -1)) { + script_return_code = static_cast(lua_tointeger(L, -1)); + ldpp_dout(s, 20) << "Lua script executed successfully and returned code: " << script_return_code << dendl; + } else { + ldpp_dout(s, 20) << "Lua script executed, but did not return an integer. Ignoring return code." << dendl; + } } } catch (const std::runtime_error& e) { ldpp_dout(s, 1) << "Lua ERROR: " << e.what() << dendl; @@ -853,11 +850,10 @@ int execute( OpsLogSink* olog, req_state* s, RGWOp* op, - const std::string& script -) + const rgw::lua::LuaCodeType& code) { int dummy_script_return_code = 0; - return execute(rest, olog, s, op, script, dummy_script_return_code); + return execute(rest, olog, s, op, code, dummy_script_return_code); } } // namespace rgw::lua::request diff --git a/src/rgw/rgw_lua_request.h b/src/rgw/rgw_lua_request.h index d1491ba92b9..ab1012e66c3 100644 --- a/src/rgw/rgw_lua_request.h +++ b/src/rgw/rgw_lua_request.h @@ -21,14 +21,14 @@ int execute( OpsLogSink* olog, req_state *s, RGWOp* op, - const std::string& script); + const rgw::lua::LuaCodeType& code); int execute( RGWREST* rest, OpsLogSink* olog, req_state *s, RGWOp* op, - const std::string& script, - int& script_return_code ); + const rgw::lua::LuaCodeType& code, + int& script_return_code); } // namespace rgw::lua::request diff --git a/src/rgw/rgw_lua_types.h b/src/rgw/rgw_lua_types.h new file mode 100644 index 00000000000..5008d70f2e9 --- /dev/null +++ b/src/rgw/rgw_lua_types.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace rgw::lua { + +// Can hold Lua script or bytecode +using LuaCodeType = std::variant>; +} diff --git a/src/rgw/rgw_lua_utils.cc b/src/rgw/rgw_lua_utils.cc index 8a9fe49df8f..4abed330a9f 100644 --- a/src/rgw/rgw_lua_utils.cc +++ b/src/rgw/rgw_lua_utils.cc @@ -1,5 +1,6 @@ #include // for std::to_chars() #include +#include #include #include "common/ceph_context.h" #include "common/debug.h" @@ -267,5 +268,40 @@ void lua_state_guard::set_runtime_hook() { lua_sethook(state, runtime_hook, LUA_MASKLINE | LUA_MASKCOUNT, 1000); } +// helper type for the visitor +template +struct overloads : Ts... { using Ts::operator()...; }; +template overloads(Ts...) -> overloads; + +int lua_execute(lua_State* L, const DoutPrefixProvider* dpp, const LuaCodeType& code) { + // execute the lua script or bytecode + return std::visit(overloads { + [L, dpp](const std::string& script) -> int { + if (luaL_dostring(L, script.c_str()) != LUA_OK) { + const std::string err(lua_tostring(L, -1)); + ldpp_dout(dpp, 1) << "Lua ERROR: failed to execute script : " << err << dendl; + lua_pop(L, 1); + return -1; + } + return 0; + }, + [L, dpp](const std::vector& bytecode) -> int { + if (luaL_loadbuffer(L, bytecode.data(), bytecode.size(), "bytecode_chunk") != LUA_OK) { + const std::string err(lua_tostring(L, -1)); + ldpp_dout(dpp, 1) << "Lua ERROR: failed to load buffer for bytecode : " << err << dendl; + lua_pop(L, 1); + return -1; + } + if (lua_pcall(L, 0, 0, 0) != LUA_OK) { + const std::string err(lua_tostring(L, -1)); + ldpp_dout(dpp, 1) << "Lua ERROR: failed to execute bytecode : " << err << dendl; + lua_pop(L, 1); + return -1; + } + return 0; + } + }, code); +} + } // namespace rgw::lua diff --git a/src/rgw/rgw_lua_utils.h b/src/rgw/rgw_lua_utils.h index bb2927c9d68..2a8efe39e38 100644 --- a/src/rgw/rgw_lua_utils.h +++ b/src/rgw/rgw_lua_utils.h @@ -14,6 +14,7 @@ #include "include/common_fwd.h" #include "rgw_perf_counters.h" #include +#include "rgw_lua_types.h" // a helper type traits structs for detecting std::variant template @@ -535,5 +536,7 @@ struct StringMapMetaTable : public EmptyMetaTable { } }; +int lua_execute(lua_State* L, const DoutPrefixProvider* dpp, const rgw::lua::LuaCodeType& code); + } // namespace rgw::lua diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index b5fd4bb8ddf..6a5ca0bd42a 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -2403,8 +2403,8 @@ int RGWGetObj::get_data_cb(bufferlist& bl, off_t bl_ofs, off_t bl_len) } int RGWGetObj::get_lua_filter(std::unique_ptr* filter, RGWGetObj_Filter* cb) { - std::string script; - const auto rc = rgw::lua::read_script(s, s->penv.lua.manager.get(), s->bucket_tenant, s->yield, rgw::lua::context::getData, script); + const auto [script, rc] = rgw::lua::read_script_or_bytecode(s, s->penv.lua.manager.get(), + s->bucket_tenant, s->yield, rgw::lua::context::getData); if (rc == -ENOENT) { // no script, nothing to do return 0; @@ -4415,8 +4415,8 @@ auto RGWPutObj::get_torrent_filter(rgw::sal::DataProcessor* cb) } int RGWPutObj::get_lua_filter(std::unique_ptr* filter, rgw::sal::DataProcessor* cb) { - std::string script; - const auto rc = rgw::lua::read_script(s, s->penv.lua.manager.get(), s->bucket_tenant, s->yield, rgw::lua::context::putData, script); + const auto [script, rc] = rgw::lua::read_script_or_bytecode(s, s->penv.lua.manager.get(), + s->bucket_tenant, s->yield, rgw::lua::context::putData); if (rc == -ENOENT) { // no script, nothing to do return 0; diff --git a/src/rgw/rgw_process.cc b/src/rgw/rgw_process.cc index d560446b135..507d4e3ff7b 100644 --- a/src/rgw/rgw_process.cc +++ b/src/rgw/rgw_process.cc @@ -391,10 +391,9 @@ int process_request(const RGWProcessEnv& penv, { s->trace_enabled = tracing::rgw::tracer.is_enabled(); if (!is_health_request) { - std::string script; - auto rc = rgw::lua::read_script(s, penv.lua.manager.get(), - s->bucket_tenant, s->yield, - rgw::lua::context::preRequest, script); + auto [lua_script, rc] = rgw::lua::read_script_or_bytecode(s, penv.lua.manager.get(), + s->bucket_tenant, s->yield, + rgw::lua::context::preRequest); if (rc == -ENOENT) { // no script, nothing to do } else if (rc < 0) { @@ -403,7 +402,8 @@ int process_request(const RGWProcessEnv& penv, "error: " << rc << dendl; } else { int script_return_code = 0; - rc = rgw::lua::request::execute(rest, penv.olog.get(), s, op, script, script_return_code); + rc = rgw::lua::request::execute(rest, penv.olog.get(), s, op, lua_script, script_return_code); + if (rc < 0) { ldpp_dout(op, 5) << "WARNING: failed to execute pre request script. " @@ -446,10 +446,9 @@ done: } } if (!is_health_request) { - std::string script; - auto rc = rgw::lua::read_script(s, penv.lua.manager.get(), - s->bucket_tenant, s->yield, - rgw::lua::context::postRequest, script); + auto [lua_script, rc] = rgw::lua::read_script_or_bytecode(s, penv.lua.manager.get(), + s->bucket_tenant, s->yield, + rgw::lua::context::postRequest); if (rc == -ENOENT) { // no script, nothing to do } else if (rc < 0) { @@ -457,7 +456,7 @@ done: "WARNING: failed to read post request script. " "error: " << rc << dendl; } else { - rc = rgw::lua::request::execute(rest, penv.olog.get(), s, op, script); + rc = rgw::lua::request::execute(rest, penv.olog.get(), s, op, lua_script); if (rc < 0) { ldpp_dout(op, 5) << "WARNING: failed to execute post request script. " diff --git a/src/rgw/rgw_realm_reloader.cc b/src/rgw/rgw_realm_reloader.cc index d8c034fc571..3a7482f531a 100644 --- a/src/rgw/rgw_realm_reloader.cc +++ b/src/rgw/rgw_realm_reloader.cc @@ -195,6 +195,7 @@ void RGWRealmReloader::reload() env.lua.manager->luarocks_path()); if (env.lua.background) { env.lua.background->set_manager(env.lua.manager.get()); + env.lua.manager.get()->set_lua_background(env.lua.background); } } diff --git a/src/rgw/rgw_sal.h b/src/rgw/rgw_sal.h index 1f6342a2570..420666fc18c 100644 --- a/src/rgw/rgw_sal.h +++ b/src/rgw/rgw_sal.h @@ -66,6 +66,10 @@ namespace rgw::restore { struct RestoreEntry; } +namespace rgw::lua { + class Background; +} + class RGWGetDataCB { public: virtual int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) = 0; @@ -1922,6 +1926,8 @@ public: /** Get a script named with the given key from the backing store */ virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) = 0; + /** Get a copy of the lua bytecode if it exists, else the script named with the given key from the backing store */ + virtual std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) = 0; /** Put a script named with the given key to the backing store */ virtual int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) = 0; /** Delete a script named with the given key from the backing store */ @@ -1938,6 +1944,8 @@ public: virtual const std::string& luarocks_path() const = 0; /** Set the path to the loarocks install location **/ virtual void set_luarocks_path(const std::string& path) = 0; + + virtual void set_lua_background(rgw::lua::Background* background) = 0; }; /** @} namespace rgw::sal in group RGWSAL */ diff --git a/src/rgw/rgw_sal_dbstore.cc b/src/rgw/rgw_sal_dbstore.cc index 6ab8e9711be..960531389a3 100644 --- a/src/rgw/rgw_sal_dbstore.cc +++ b/src/rgw/rgw_sal_dbstore.cc @@ -2134,6 +2134,12 @@ namespace rgw::sal { return -ENOENT; } + std::tuple DBLuaManager::get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) + { + return std::make_tuple("", -ENOENT); + } + int DBLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) { return -ENOENT; @@ -2163,7 +2169,7 @@ namespace rgw::sal { { return -ENOENT; } - + } // namespace rgw::sal extern "C" { diff --git a/src/rgw/rgw_sal_dbstore.h b/src/rgw/rgw_sal_dbstore.h index 7db6a0424f4..2e2776effcc 100644 --- a/src/rgw/rgw_sal_dbstore.h +++ b/src/rgw/rgw_sal_dbstore.h @@ -330,6 +330,9 @@ protected: /** Get a script named with the given key from the backing store */ virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override; + /** Get a ref to the Lua bytecode if it exists, else the script named with the given key from the backing store */ + virtual std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) override; /** Put a script named with the given key to the backing store */ virtual int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override; /** Delete a script named with the given key from the backing store */ @@ -342,6 +345,7 @@ protected: virtual int list_packages(const DoutPrefixProvider* dpp, optional_yield y, rgw::lua::packages_t& packages) override; /** Reload lua packages */ virtual int reload_packages(const DoutPrefixProvider* dpp, optional_yield y) override; + }; /* diff --git a/src/rgw/rgw_sal_filter.cc b/src/rgw/rgw_sal_filter.cc index 5fe836a091b..f68b83e0c46 100644 --- a/src/rgw/rgw_sal_filter.cc +++ b/src/rgw/rgw_sal_filter.cc @@ -1541,6 +1541,12 @@ int FilterLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y return next->get_script(dpp, y, key, script); } +std::tuple FilterLuaManager::get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) +{ + return next->get_script_or_bytecode(dpp, y, key); +} + int FilterLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) { @@ -1583,6 +1589,9 @@ const std::string& FilterLuaManager::luarocks_path() const { void FilterLuaManager::set_luarocks_path(const std::string& path) { next->set_luarocks_path(path); } +void FilterLuaManager::set_lua_background(rgw::lua::Background* background) { + next->set_lua_background(background); +} } } // namespace rgw::sal diff --git a/src/rgw/rgw_sal_filter.h b/src/rgw/rgw_sal_filter.h index 0524c687d96..0774dfc2a08 100644 --- a/src/rgw/rgw_sal_filter.h +++ b/src/rgw/rgw_sal_filter.h @@ -1156,6 +1156,8 @@ public: virtual ~FilterLuaManager() = default; virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override; + virtual std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, + const std::string& key) override; virtual int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override; virtual int del_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) override; virtual int add_package(const DoutPrefixProvider* dpp, optional_yield y, const std::string& package_name) override; @@ -1165,6 +1167,7 @@ public: const std::string& luarocks_path() const override; void set_luarocks_path(const std::string& path) override; + void set_lua_background(rgw::lua::Background* background) override; }; } } // namespace rgw::sal diff --git a/src/rgw/rgw_sal_store.h b/src/rgw/rgw_sal_store.h index 0f2f91f5fef..f37d2752f59 100644 --- a/src/rgw/rgw_sal_store.h +++ b/src/rgw/rgw_sal_store.h @@ -519,6 +519,7 @@ class StoreZone : public Zone { class StoreLuaManager : public LuaManager { protected: std::string _luarocks_path; + rgw::lua::Background* lua_background; public: const std::string& luarocks_path() const override { return _luarocks_path; @@ -526,6 +527,11 @@ public: void set_luarocks_path(const std::string& path) override { _luarocks_path = path; } + + void set_lua_background(rgw::lua::Background* background) override { + lua_background = background; + } + StoreLuaManager() = default; StoreLuaManager(const std::string& __luarocks_path) : _luarocks_path(__luarocks_path) {} diff --git a/src/test/rgw/test_rgw_lua.cc b/src/test/rgw/test_rgw_lua.cc index 7509b82d4f8..5160acc786c 100644 --- a/src/test/rgw/test_rgw_lua.cc +++ b/src/test/rgw/test_rgw_lua.cc @@ -169,6 +169,10 @@ class TestLuaManager : public rgw::sal::StoreLuaManager { script = lua_script; return 0; } + std::tuple get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key) override { + std::this_thread::sleep_for(std::chrono::seconds(read_time)); + return std::make_tuple(lua_script, 0); + } int put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script) override { return 0; }