Skip to content

Wip rgw/lua: performance improvements#66832

Merged
nbalacha merged 1 commit intoceph:mainfrom
nbalacha:wip-nbalacha-lua-74219
Mar 2, 2026
Merged

Wip rgw/lua: performance improvements#66832
nbalacha merged 1 commit intoceph:mainfrom
nbalacha:wip-nbalacha-lua-74219

Conversation

@nbalacha
Copy link
Copy Markdown
Contributor

@nbalacha nbalacha commented Jan 8, 2026

WIP
Improve performance by caching Lua bytecode.

Fixes: https://tracker.ceph.com/issues/74219

Contribution Guidelines

  • To sign and title your commits, please refer to Submitting Patches to Ceph.

  • If you are submitting a fix for a stable branch (e.g. "quincy"), please refer to Submitting Patches to Ceph - Backports for the proper workflow.

  • When filling out the below checklist, you may click boxes directly in the GitHub web UI. When entering or editing the entire PR message in the GitHub web UI editor, you may also select a checklist item by adding an x between the brackets: [x]. Spaces and capitalization matter when checking off items this way.

Checklist

  • Tracker (select at least one)
    • References tracker ticket
    • Very recent bug; references commit where it was introduced
    • New feature (ticket optional)
    • Doc update (no ticket needed)
    • Code cleanup (no ticket needed)
  • Component impact
    • Affects Dashboard, opened tracker ticket
    • Affects Orchestrator, opened tracker ticket
    • No impact that needs to be tracked
  • Documentation (select at least one)
    • Updates relevant documentation
    • No doc update is appropriate
  • Tests (select at least one)
Show available Jenkins commands

You must only issue one Jenkins command per-comment. Jenkins does not understand
comments with more than one command.

@nbalacha nbalacha requested review from a team as code owners January 8, 2026 06:15
@nbalacha nbalacha requested a review from yuvalif January 8, 2026 06:15
@nbalacha nbalacha marked this pull request as draft January 8, 2026 06:16
- The maximum number of entries in the table is 100,000. Each entry has a string key a value with a combined length of no more than 1KB.
A Lua script will abort with an error if the number of entries or entry size exceeds these limits.
- The ``RGW`` Lua table uses string indices and can store values of type: string, integer, double and boolean
- The ``RGW`` Lua table uses string indices and can store values of type string, integer, double and boolean
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest double backticks around the four type literals.

- if the value of ``key`` is not numeric, the execution of the script would fail
- if we try to increment or decrement by non-numeric values, the execution of the script would fail
- If the value of ``key`` is not numeric, the execution of the script would fail
- If we try to increment or decrement with non-numeric values, the execution of the script would fail
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S/would/will/

@@ -151,11 +154,19 @@ class Background : public RGWRealmReloader::Pauser {
std::mutex pause_mutex;
std::condition_variable cond;

std::set<std::string> updated_scripts;
Copy link
Copy Markdown
Contributor

@yuvalif yuvalif Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be a local variable of the processing function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}

int Background::bytecode_writer (lua_State *L, const void* p,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont think this should be a member function. just make it a standalone function here and remove from the class definition

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
ldpp_dout(&dp, 10) << "NITHYA: processing : " << key << dendl;
auto L = lguard->get();
// TODO: use a unique_ptr?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, please try to avoid new/delete as much as you can

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

std::map<std::string, uint64_t> script_watches;
std::map<uint64_t, std::string> reverse_script_watches;

std::mutex updates;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this being used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

}
}

uint64_t RadosLuaManager::get_watch_handle_for_script(std::string script_oid) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint64_t RadosLuaManager::get_watch_handle_for_script(std::string script_oid) {
uint64_t RadosLuaManager::get_watch_handle_for_script(const std::string& script_oid) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -23,6 +23,7 @@
#include <boost/process.hpp>

#include <fmt/core.h>
#include <lua.hpp>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you need this include for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

@@ -458,6 +458,8 @@ class POSIXLuaManager : public StoreLuaManager {
virtual ~POSIXLuaManager() = default;

virtual int get_script(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key, std::string& script) override;
virtual int get_script_or_bytecode(const DoutPrefixProvider* dpp, optional_yield y, const std::string& key,
std::string& script, std::vector<char>& lua_bytecode) override;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we replace these 2 parameters with std::variant holding an std::string or std::vector<char> ?
I think this would make the intent here more clear.

return r;
}

lua_background->mark_script_updated(key);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rename this function to "script_needs_processing"?
we dont know if the script was updated or not

@@ -151,11 +154,19 @@ class Background : public RGWRealmReloader::Pauser {
std::mutex pause_mutex;
std::condition_variable cond;

std::set<std::string> updated_scripts;
std::map<std::string, std::vector<char>*> lua_bytecode_cache; // script-name -> bytecode
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to use:

Suggested change
std::map<std::string, std::vector<char>*> lua_bytecode_cache; // script-name -> bytecode
std::map<std::string, std::unique_ptr<std::vector<char>>> lua_bytecode_cache; // script-name -> bytecode

@nbalacha nbalacha force-pushed the wip-nbalacha-lua-74219 branch 2 times, most recently from 7c9bddc to 56649c0 Compare January 14, 2026 05:37
@@ -32,6 +32,9 @@ enum class context {
// return "none" if not matched
context to_context(const std::string& s);

// Can hold the script or the bytecode
typedef std::variant<std::string, std::vector<char>> luaCodeType;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
typedef std::variant<std::string, std::vector<char>> luaCodeType;
using luaCodeType = std::variant<std::string, std::vector<char>> ;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -218,5 +219,98 @@ void Background::set_manager(rgw::sal::LuaManager* _lua_manager) {
lua_manager = _lua_manager;
}

void Background::process_script_add(std::string script_oid) {
std::unique_ptr<std::string> script_ptr = std::make_unique<std::string>(script_oid);
processing_q.push(script_ptr.release());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just use new (this is where it actually makes more sense)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
ldpp_dout(&dp, 10) << "NITHYA: processing : " << key << dendl;
auto L = lguard->get();
std::unique_ptr<std::vector<char>> buffer = std::make_unique<std::vector<char>>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (since it is clear what the type is from the make_unique call):

Suggested change
std::unique_ptr<std::vector<char>> buffer = std::make_unique<std::vector<char>>();
auto buffer = std::make_unique<std::vector<char>>();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

const std::string err(lua_tostring(L, -1));
ldpp_dout(s, 1) << "Lua ERROR: " << err << dendl;
// execute the lua script or bytecode
if (std::holds_alternative<std::vector<char>>(code)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:
instead of if/else maybe you can use std::visit with 2 lambdas, one that accept std::string and one that accepts std::vector<char> ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into this.

auto rc = rgw::lua::read_script(s, penv.lua.manager.get(),
s->bucket_tenant, s->yield,
rgw::lua::context::postRequest, script);
std::vector<char> lua_bytecode;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you using script and lua_bytecode ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

auto rc = rgw::lua::read_script(s, penv.lua.manager.get(),
s->bucket_tenant, s->yield,
rgw::lua::context::preRequest, script);
std::vector<char> lua_bytecode;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as line 446

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@nbalacha nbalacha force-pushed the wip-nbalacha-lua-74219 branch from 56649c0 to fdf517e Compare January 14, 2026 12:40
@nbalacha
Copy link
Copy Markdown
Contributor Author

nbalacha commented Feb 3, 2026

@nbalacha
Copy link
Copy Markdown
Contributor Author

nbalacha commented Feb 3, 2026

jenkins test make check

@yuvalif
Copy link
Copy Markdown
Contributor

yuvalif commented Feb 3, 2026

jenkins test api

@yuvalif
Copy link
Copy Markdown
Contributor

yuvalif commented Feb 3, 2026

@nbalacha i think we can merge once we have perf results

@github-actions
Copy link
Copy Markdown

This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved

@mkogan1 mkogan1 self-requested a review February 18, 2026 16:21
Copy link
Copy Markdown
Contributor

@mkogan1 mkogan1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, there is a performance improvement observed with this PR.

Test methodology:

cat > ./autotier.lua
-- Lua script to auto-tier S3 object PUT requests

-- exit script quickly if it is not a PUT request
if Request == nil or Request.RGWOp ~= "put_obj"
then
  return
end

-- apply StorageClass only if user hasn't already assigned a storage-class
if Request.HTTP.StorageClass == nil or Request.HTTP.StorageClass == '' then
  if Request.ContentLength < 16384 then
    Request.HTTP.StorageClass = "SMALL_OBJ"
  elseif Request.ContentLength < 1048576 then
    Request.HTTP.StorageClass = "MEDIUM_OBJ"
  else
    Request.HTTP.StorageClass = "STANDARD"
  end
end
^D

sudo ./bin/radosgw-admin zonegroup placement add --rgw-zone default --placement-id default-placement --storage-class SMALL_OBJ |jq -C
sudo ./bin/radosgw-admin zone placement add --rgw-zone default --placement-id default-placement --storage-class SMALL_OBJ --data-pool default.rgw.buckets.data.lukewarm |jq -C

sudo ./bin/radosgw-admin zonegroup placement add --rgw-zone default --placement-id default-placement --storage-class MEDIUM_OBJ |jq -C
sudo ./bin/radosgw-admin zone placement add --rgw-zone default --placement-id default-placement --storage-class MEDIUM_OBJ --data-pool default.rgw.buckets.data.frozen |jq -C



sudo ./bin/radosgw-admin script put --context=preRequest --infile=./autotier.lua --context=preRequest
#sudo ./bin/radosgw-admin script rm --context=preRequest



sudo truncate -s0 ./out/radosgw.8000.log ; sudo fuser -vk -9 ./bin/radosgw
sleep 1.25 ; sudo numactl -N 0 -m 0 --  ./bin/radosgw  --nolockdep -c ./ceph.conf --log-file=./out/radosgw.8000.log --admin-socket=./out/radosgw.8000.asok --pid-file=./out/radosgw.8000.pid -n client.rgw.8000 --rgw_frontends="beast port=8000 tcp_nodelay=1 request_timeout_ms=0 ssl_port=8443 ssl_certificate=./rgw.crt ssl_private_key=./rgw.key" --debug_ms=0 --debug_rgw=1 --debug_rgw_sync=0 --debug_rgw_notification=1 --debug_compressor=1 --debug_crypto=1  -f


sudo ./bin/radosgw-admin script put --context=preRequest --infile=./autotier.lua --context=preRequest


## WORKLOAD:
echo $RANDOM ; time (nice numactl -N 1 -m 1 -- env GOMAXPROCS=$(( $(numactl -N 0 -- nproc) / 2 )) ~/go/bin/hsbench -a b2345678901234567890 -s b234567890123456789012345678901234567890 -u http://127.0.0.1:8000 -z 4K -d -1 -t $(( $(numactl -N 0 -- nproc) / 2 )) -b $(( $(numactl -N 0 -- nproc) * 2 )) -n 100000 -m cxip -bp b01b${RANDOM}- -op 'folder01/stage01_' |& tee hsbench.log | stdbuf -o0 colrm $((${COLUMNS} - 1)))
  • Baseline performance: --rgw_lua_enable=false
Image
  • performance: --rgw_lua_enable=true & WITHOUT PR #66832
Image
  • performance: --rgw_lua_enable=true & WITH PR #66832
    (sample of two runs)
Image

On average, performance is better than without the PR.

@nbalacha nbalacha force-pushed the wip-nbalacha-lua-74219 branch from 0e26b0e to 345166c Compare February 19, 2026 15:42
@nbalacha
Copy link
Copy Markdown
Contributor Author

@anthonyeleven , I will move the doc changes to a different PR to make it easier to address comments.

@nbalacha
Copy link
Copy Markdown
Contributor Author

@anthonyeleven , I will move the doc changes to a different PR to make it easier to address comments.

@anthonyeleven , please see #67441

@github-actions
Copy link
Copy Markdown

This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved

@nbalacha nbalacha force-pushed the wip-nbalacha-lua-74219 branch 2 times, most recently from 3df4c7c to 5690d38 Compare February 25, 2026 09:24
if (s->penv.lua.background) {
s->penv.lua.background->create_background_metatable(L);
}
rc = rgw::lua::lua_execute(L, s, code);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you ignore rc?

i'm wondering if the origina lcode is correct?
if rc != LUA_OK should we truct the value of lua_tointeger(L, -1) or just reporn an error and dont set the return code value (similar to what happens in case of the exception)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry. not an issue. a script that has a runtime issue after the "return" call, will not fail:

return RGW_ABORT_REQUEST
RGWDebugLog("hello "..Request.Kaboom)

so this is not an issue

Improves performance by caching the Lua bytecode.

Fixes: https://tracker.ceph.com/issues/74219

Signed-off-by: Nithya Balachandran <nithya.balachandran@ibm.com>
@nbalacha nbalacha force-pushed the wip-nbalacha-lua-74219 branch from 5690d38 to b5fe6fa Compare February 25, 2026 10:29
@yuvalif
Copy link
Copy Markdown
Contributor

yuvalif commented Feb 26, 2026

jenkins test make check

@yuvalif
Copy link
Copy Markdown
Contributor

yuvalif commented Feb 26, 2026

jenkins test make check arm64

@nbalacha
Copy link
Copy Markdown
Contributor Author

nbalacha commented Mar 2, 2026

Latest teuthology run results:

https://pulpito.ceph.com/nithyab-2026-02-26_13:17:07-rgw-wip-nbalacha-lua-74219-distro-default-trial/

There are 22 failures, of which:

  • crypt and tempest failures (keystone related) : 4
  • multisite failures: 2
  • valgrind failures: 8
  • d4n failure: 1
  • singleton ('boto.vendored.six.moves') : 3
  • bucket notification : 1
  • multifs: 3

These are unrelated to this PR.

@yuvalif yuvalif dismissed mattbenjamin’s stale review March 2, 2026 10:55

issue was resolved

@nbalacha nbalacha merged commit a5263d8 into ceph:main Mar 2, 2026
13 checks passed
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 2, 2026

This is an automated message by src/script/redmine-upkeep.py.

I found one or more Fixes: tags in the commit messages in

git log a5263d8de61cc137043bac141b288fe65d3266cc^..a5263d8de61cc137043bac141b288fe65d3266cc

The referenced tickets are:

Those tickets do not reference this merged Pull Request. If this Pull Request merge resolves any of those tickets, please update the "Pull Request ID" field on each ticket. A future run of this script will appropriately update them.

Update Log: https://github.com/ceph/ceph/actions/runs/22572870530

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants