Improves performance by caching the Lua bytecode.
Fixes: https://tracker.ceph.com/issues/74219
Signed-off-by: Nithya Balachandran <nithya.balachandran@ibm.com>
return -ENOENT;
}
+ std::tuple<rgw::lua::LuaCodeType, int> 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;
/** 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<rgw::lua::LuaCodeType, int> 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 */
return -ENOENT;
}
+std::tuple<rgw::lua::LuaCodeType, int> 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;
virtual ~POSIXLuaManager() = default;
virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override;
+ virtual std::tuple<rgw::lua::LuaCodeType, int> 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;
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)
{
return 0;
}
-int RadosLuaManager::put_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, const std::string& script)
+std::tuple<rgw::lua::LuaCodeType, int> 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<char> 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;
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;
}
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)
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<librados::notify_ack_t> acks;
+ std::vector<librados::notify_timeout_t> 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);
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();
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<std::string, uint64_t> script_watches;
+ std::map<uint64_t, std::string> 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<rgw::lua::LuaCodeType, int> 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;
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 {
lua_background->start();
env.lua.background = lua_background.get();
static_cast<rgw::sal::RadosLuaManager*>(env.lua.manager.get())->watch_reload(dpp);
+ env.lua.manager.get()->set_lua_background(lua_background.get());
}
#endif
} /* init_lua */
return manager ? manager->get_script(dpp, y, script_oid(ctx, tenant), script) : -ENOENT;
}
+std::tuple<LuaCodeType, int> 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;
#include <string>
#include <set>
#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"
// 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<LuaCodeType, int> 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);
return 0;
}
+static int bytecode_writer (lua_State *L, const void* p, size_t sz, void* ud) {
+ std::vector<char>* buffer = static_cast<std::vector<char>*>(ud);
+ const char* bytes = static_cast<const char*>(p);
+ buffer->insert(buffer->end(), bytes, bytes + sz);
+ return 0;
+}
+
+
Background::Background(
CephContext* _cct,
rgw::sal::LuaManager* _lua_manager,
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;});
}
lua_manager = _lua_manager;
}
+void Background::process_script_add(std::string script_oid) {
+ auto script_ptr = make_unique<std::string>(std::move(script_oid));
+ if (processing_q.push(script_ptr.get())) {
+ script_ptr.release();
+ }
+}
+
+void Background::process_scripts() {
+ std::set<std::string> removed;
+ std::set<std::string> updated_scripts;
+
+ const auto count = processing_q.consume_all([&](std::string* s) {
+ std::unique_ptr<std::string> 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<lua_state_guard> 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<std::shared_mutex> lock(updating_mutex);
+ lua_bytecode_cache.erase(key);
+ continue;
+ }
+ if (r == -ENOENT) {
+ removed.insert(key);
+ std::unique_lock<std::shared_mutex> 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<std::vector<char>>();
+ try {
+ if (luaL_loadstring(L, script.c_str()) == LUA_OK) {
+ lua_dump(L, bytecode_writer, buffer.get(), 0);
+ {
+ std::unique_lock<std::shared_mutex> 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<char>& lua_bytecode) {
+ lua_bytecode.clear();
+ std::shared_lock<std::shared_mutex> 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
#include "common/dout.h"
#include "rgw_common.h"
#include <string>
+#include <set>
#include <unordered_map>
#include <variant>
+#include <shared_mutex>
+#include <boost/lockfree/queue.hpp>
#include "rgw_lua_utils.h"
#include "rgw_realm_reloader.h"
std::mutex pause_mutex;
std::condition_variable cond;
+ std::map<std::string, std::unique_ptr<std::vector<char>>> lua_bytecode_cache; // script-name -> bytecode
+// bool updating = false;
+ std::shared_mutex updating_mutex;
+ boost::lockfree::queue<std::string*> processing_q{16};
+
void run();
std::string rgw_script;
int read_script();
std::unique_ptr<lua_state_guard> initialize_lguard_state();
+ void process_scripts();
+
public:
Background(CephContext* _cct,
rgw::sal::LuaManager* _lua_manager,
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<char>& lua_bytecode);
};
} //namespace rgw::lua
// 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) {
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;
public:
RGWGetObjFilter(req_state* s,
- const std::string& script,
+ const rgw::lua::LuaCodeType& script,
RGWGetObj_Filter* next) : RGWGetObj_Filter(next), filter(s, script)
{}
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)
{}
OpsLogSink* olog,
req_state* s,
RGWOp* op,
- const std::string& script)
+ const rgw::lua::LuaCodeType& code)
{
lua_state_guard lguard(s->cct->_conf->rgw_lua_max_memory_per_state,
s->cct->_conf->rgw_lua_max_runtime_per_state, s);
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;
- }
} catch (const std::runtime_error& e) {
ldpp_dout(s, 1) << "Lua ERROR: " << e.what() << dendl;
rc = -1;
OpsLogSink* olog,
req_state *s,
RGWOp* op,
- const std::string& script);
+ const rgw::lua::LuaCodeType& code);
} // namespace rgw::lua::request
--- /dev/null
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace rgw::lua {
+
+// Can hold Lua script or bytecode
+using LuaCodeType = std::variant<std::string, std::vector<char>>;
+}
#include <charconv> // for std::to_chars()
#include <string>
+#include <variant>
#include <lua.hpp>
#include "common/ceph_context.h"
#include "common/debug.h"
lua_sethook(state, runtime_hook, LUA_MASKLINE | LUA_MASKCOUNT, 1000);
}
+// helper type for the visitor
+template<class... Ts>
+struct overloads : Ts... { using Ts::operator()...; };
+template<class... Ts> overloads(Ts...) -> overloads<Ts...>;
+
+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<char>& 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
#include "include/common_fwd.h"
#include "rgw_perf_counters.h"
#include <common/ceph_time.h>
+#include "rgw_lua_types.h"
// a helper type traits structs for detecting std::variant
template<class>
}
};
+int lua_execute(lua_State* L, const DoutPrefixProvider* dpp, const rgw::lua::LuaCodeType& code);
+
} // namespace rgw::lua
}
int RGWGetObj::get_lua_filter(std::unique_ptr<RGWGetObj_Filter>* 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;
}
int RGWPutObj::get_lua_filter(std::unique_ptr<rgw::sal::DataProcessor>* 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;
{
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) {
"WARNING: failed to execute pre 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 pre request script. "
}
}
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) {
"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. "
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);
}
}
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;
/** 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<rgw::lua::LuaCodeType, int> 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 */
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 */
return -ENOENT;
}
+ std::tuple<rgw::lua::LuaCodeType, int> 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;
{
return -ENOENT;
}
-
+
} // namespace rgw::sal
extern "C" {
/** 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<rgw::lua::LuaCodeType, int> 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 */
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;
+
};
/*
return next->get_script(dpp, y, key, script);
}
+std::tuple<rgw::lua::LuaCodeType, int> 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)
{
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
virtual ~FilterLuaManager() = default;
virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override;
+ virtual std::tuple<rgw::lua::LuaCodeType, int> 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;
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
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;
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) {}
script = lua_script;
return 0;
}
+ std::tuple<rgw::lua::LuaCodeType, int> 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;
}