]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw/lua: cache lua bytecode
authorNithya Balachandran <nithya.balachandran@ibm.com>
Thu, 19 Feb 2026 15:38:32 +0000 (15:38 +0000)
committerNithya Balachandran <nithya.balachandran@ibm.com>
Tue, 24 Feb 2026 06:25:30 +0000 (06:25 +0000)
Improves performance by caching the Lua bytecode.

Fixes: https://tracker.ceph.com/issues/74219
Signed-off-by: Nithya Balachandran <nithya.balachandran@ibm.com>
28 files changed:
src/rgw/driver/motr/rgw_sal_motr.cc
src/rgw/driver/motr/rgw_sal_motr.h
src/rgw/driver/posix/rgw_sal_posix.cc
src/rgw/driver/posix/rgw_sal_posix.h
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/driver/rados/rgw_sal_rados.h
src/rgw/rgw_appmain.cc
src/rgw/rgw_lua.cc
src/rgw/rgw_lua.h
src/rgw/rgw_lua_background.cc
src/rgw/rgw_lua_background.h
src/rgw/rgw_lua_data_filter.cc
src/rgw/rgw_lua_data_filter.h
src/rgw/rgw_lua_request.cc
src/rgw/rgw_lua_request.h
src/rgw/rgw_lua_types.h [new file with mode: 0644]
src/rgw/rgw_lua_utils.cc
src/rgw/rgw_lua_utils.h
src/rgw/rgw_op.cc
src/rgw/rgw_process.cc
src/rgw/rgw_realm_reloader.cc
src/rgw/rgw_sal.h
src/rgw/rgw_sal_dbstore.cc
src/rgw/rgw_sal_dbstore.h
src/rgw/rgw_sal_filter.cc
src/rgw/rgw_sal_filter.h
src/rgw/rgw_sal_store.h
src/test/rgw/test_rgw_lua.cc

index f685bdf51f0902e9ad21bb2c8a429fcb2ddbdcc8..4cd90d0c7dccabd0fe98f929ba25ce8ea3ffbb89 100644 (file)
@@ -3879,6 +3879,12 @@ int MotrStore::init_metadata_cache(const DoutPrefixProvider *dpp,
     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;
index bce464faffa41ae2adb3a3eebe818a3a0341fe80..04a7f0d6cd6d9f44e42dfcdb85454cc6515cc831 100644 (file)
@@ -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<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 */
index 50df5fb2904efb78eb11e89940b685074b132857..29706ca8b1495364576242893be095a7afb81818 100644 (file)
@@ -1923,6 +1923,12 @@ int POSIXLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y,
   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;
index 488fbf6e01633a4d2ab7583e8e4f3695868ce664..4f1f66994095d4b8b4d00fe2003fb1de146c12f8 100644 (file)
@@ -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<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;
index 481822bb1cc2b42c979b86287b4f6ac8d5f5109d..39c46d6b98b64cdcf81e9fd24c4e272c21f53cce 100644 (file)
@@ -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<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;
@@ -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<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);
@@ -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();
index 152bb87413c7c0e027ae5d0025ee1fa5c36fb542..4d5ab11d4a2732b89279e1f356d459f14bb49ad2 100644 (file)
@@ -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<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;
@@ -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 {
index 2782da9f6ccfd638d458b1cc6c45d3f9d8cd1063..f1eb20d466572d9cfd09a694dc0e6550c7ac9f7e 100644 (file)
@@ -577,6 +577,7 @@ void rgw::AppMain::init_lua()
     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 */
index aacec7c7fe075a895c58e06d8202b0d9611fc42a..975ce19738b27e8b609dbcecdc4897f08abb400c 100644 (file)
@@ -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<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;
index 30b3272c4e48a2e113f5c08f39a35efbcc9f7940..a4ee24ff83188b921b5e202b59ab207593ecf46c 100644 (file)
@@ -3,6 +3,7 @@
 #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"
@@ -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<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);
 
index 46aae8dc28f0defa1f6ce72f61c16663a650cea2..0d0f34c91b6be985e08db68dacf5abb5c3a061f4 100644 (file)
@@ -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<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,
@@ -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::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
 
index 8745b7f39121a0311fbedf1b4fdc96b54726209a..421e64c2e437939d195474c2f57cf6f322c27460 100644 (file)
@@ -2,8 +2,11 @@
 #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"
 
@@ -151,12 +154,19 @@ private:
   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,
@@ -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<char>& lua_bytecode);
 };
 
 } //namespace rgw::lua
index 9a5398709bf4370fa804c132cad75ef2b4c32aaa..8efd81d138f13d3562392c295c4a361074bd38b9 100644 (file)
@@ -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) {
index 75596b64e6135de69a1757d41607ca08b3de6e7f..ec0944a205cba6c630175ba3c3e4f43af38cdf25 100644 (file)
@@ -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) 
   {}
 
index 908bcfc40024b892691984b6df59c56df4259a48..f77029b8763cf6bc52a50ec203b73153303214a8 100644 (file)
@@ -788,7 +788,7 @@ int execute(
     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);
@@ -820,13 +820,8 @@ 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;
-    }
   } catch (const std::runtime_error& e) {
     ldpp_dout(s, 1) << "Lua ERROR: " << e.what() << dendl;
     rc = -1;
index 911c660f2c6afd7141568313ec90bdc40e7cfcd9..4522d0390ab6948124c7070bfccbf2a037b8b468 100644 (file)
@@ -21,6 +21,6 @@ int execute(
     OpsLogSink* olog,
     req_state *s, 
     RGWOp* op,
-    const std::string& script);
+    const rgw::lua::LuaCodeType& 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 (file)
index 0000000..5008d70
--- /dev/null
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace rgw::lua {
+
+// Can hold Lua script or bytecode
+using LuaCodeType = std::variant<std::string, std::vector<char>>;
+}
index 8a9fe49df8fd86c9ba1353fb5b99c39aa4f12012..4abed330a9f1fea09730cf7c04279cbd1995e0ed 100644 (file)
@@ -1,5 +1,6 @@
 #include <charconv> // for std::to_chars()
 #include <string>
+#include <variant>
 #include <lua.hpp>
 #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<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
 
index bb2927c9d688086984c7b74267a2cc0839c60df1..2a8efe39e38f90bc0c5787cf010e4f43cc00a931 100644 (file)
@@ -14,6 +14,7 @@
 #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>
@@ -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
 
index b5fd4bb8ddf9711572a2e9691dc842cb6dd89318..6a5ca0bd42a9f4dc38ec6bac0eb92f6a00dea303 100644 (file)
@@ -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<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;
@@ -4415,8 +4415,8 @@ auto RGWPutObj::get_torrent_filter(rgw::sal::DataProcessor* cb)
 }
 
 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;
index bed3ff03d3ce41b1ec112c0c40e3fe354ca9aada..458a41d973f4e2c8a52a791a8bab9ccfd91a4130 100644 (file)
@@ -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) {
@@ -402,7 +401,7 @@ int process_request(const RGWProcessEnv& penv,
           "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. "
@@ -441,10 +440,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) {
@@ -452,7 +450,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. "
index d8c034fc571df3a13e6868e412c05e96a271ad6e..3a7482f531ad4a0f677b74166e212590dbc5ec29 100644 (file)
@@ -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);
     }
   }
 
index 1f6342a2570e0415a3fb309d4ee40c484936d037..420666fc18ce13498dbb321942d70ec7455e40e9 100644 (file)
@@ -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<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 */
@@ -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 */
index 6ab8e9711be2ea6113a645f9c3970d238b17d363..960531389a3417b03412eb262b3d6d3e1b851a87 100644 (file)
@@ -2134,6 +2134,12 @@ namespace rgw::sal {
     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;
@@ -2163,7 +2169,7 @@ namespace rgw::sal {
   {
     return -ENOENT;
   }
-  
+
 } // namespace rgw::sal
 
 extern "C" {
index 7db6a0424f493e4fbff97890ffde27bc03e13392..2e2776effcc3ee58bdadd31e2d95ec09dde893df 100644 (file)
@@ -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<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 */
@@ -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;
+
   };
 
   /*
index 5fe836a091be16697f4d04815305a9a941f0a9f0..f68b83e0c467174101db1522736b04fd025a6045 100644 (file)
@@ -1541,6 +1541,12 @@ int FilterLuaManager::get_script(const DoutPrefixProvider* dpp, optional_yield y
   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)
 {
@@ -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
 
index 0524c687d9697f9a2f1d0ff8aa0b339f101ef314..0774dfc2a08f7af92a6f030462e96e84e9416c9e 100644 (file)
@@ -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<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;
@@ -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
index 0f2f91f5fef76749fb392c8ce8279001070f127f..f37d2752f59273ffdd7f4fd8afed4a7d4c7d5f2e 100644 (file)
@@ -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) {}
index 625db9c8436c13a4d48322948d4f026f8e722e71..dcafe7bf6f32579fa4dd801b5c843fc12c764d19 100644 (file)
@@ -169,6 +169,10 @@ class TestLuaManager : public rgw::sal::StoreLuaManager {
       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;
     }