Wip rgw/lua: performance improvements#66832
Conversation
doc/radosgw/lua-scripting.rst
Outdated
| - 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 |
There was a problem hiding this comment.
Suggest double backticks around the four type literals.
doc/radosgw/lua-scripting.rst
Outdated
| - 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 |
src/rgw/rgw_lua_background.h
Outdated
| @@ -151,11 +154,19 @@ class Background : public RGWRealmReloader::Pauser { | |||
| std::mutex pause_mutex; | |||
| std::condition_variable cond; | |||
|
|
|||
| std::set<std::string> updated_scripts; | |||
There was a problem hiding this comment.
this could be a local variable of the processing function
src/rgw/rgw_lua_background.cc
Outdated
| } | ||
| } | ||
|
|
||
| int Background::bytecode_writer (lua_State *L, const void* p, |
There was a problem hiding this comment.
dont think this should be a member function. just make it a standalone function here and remove from the class definition
src/rgw/rgw_lua_background.cc
Outdated
| } | ||
| ldpp_dout(&dp, 10) << "NITHYA: processing : " << key << dendl; | ||
| auto L = lguard->get(); | ||
| // TODO: use a unique_ptr? |
There was a problem hiding this comment.
yes, please try to avoid new/delete as much as you can
src/rgw/driver/rados/rgw_sal_rados.h
Outdated
| std::map<std::string, uint64_t> script_watches; | ||
| std::map<uint64_t, std::string> reverse_script_watches; | ||
|
|
||
| std::mutex updates; |
| } | ||
| } | ||
|
|
||
| uint64_t RadosLuaManager::get_watch_handle_for_script(std::string script_oid) { |
There was a problem hiding this comment.
| 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) { |
| @@ -23,6 +23,7 @@ | |||
| #include <boost/process.hpp> | |||
|
|
|||
| #include <fmt/core.h> | |||
| #include <lua.hpp> | |||
There was a problem hiding this comment.
what do you need this include for?
src/rgw/driver/posix/rgw_sal_posix.h
Outdated
| @@ -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; | |||
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
maybe rename this function to "script_needs_processing"?
we dont know if the script was updated or not
src/rgw/rgw_lua_background.h
Outdated
| @@ -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 | |||
There was a problem hiding this comment.
is it possible to use:
| 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 |
7c9bddc to
56649c0
Compare
src/rgw/rgw_lua.h
Outdated
| @@ -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; | |||
There was a problem hiding this comment.
nit:
| typedef std::variant<std::string, std::vector<char>> luaCodeType; | |
| using luaCodeType = std::variant<std::string, std::vector<char>> ; |
src/rgw/rgw_lua_background.cc
Outdated
| @@ -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()); | |||
There was a problem hiding this comment.
just use new (this is where it actually makes more sense)
src/rgw/rgw_lua_background.cc
Outdated
| } | ||
| 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>>(); |
There was a problem hiding this comment.
nit (since it is clear what the type is from the make_unique call):
| std::unique_ptr<std::vector<char>> buffer = std::make_unique<std::vector<char>>(); | |
| auto buffer = std::make_unique<std::vector<char>>(); |
src/rgw/rgw_lua_request.cc
Outdated
| 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)) { |
There was a problem hiding this comment.
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> ?
There was a problem hiding this comment.
I'll look into this.
src/rgw/rgw_process.cc
Outdated
| 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; |
There was a problem hiding this comment.
are you using script and lua_bytecode ?
src/rgw/rgw_process.cc
Outdated
| 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; |
56649c0 to
fdf517e
Compare
|
The PR has passed the lua suite in teuthology |
|
jenkins test make check |
|
jenkins test api |
|
@nbalacha i think we can merge once we have perf results |
|
This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved |
There was a problem hiding this comment.
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
- performance: --rgw_lua_enable=true & WITHOUT PR #66832
- performance: --rgw_lua_enable=true & WITH PR #66832
(sample of two runs)
On average, performance is better than without the PR.
0e26b0e to
345166c
Compare
|
@anthonyeleven , I will move the doc changes to a different PR to make it easier to address comments. |
345166c to
d7bcb16
Compare
@anthonyeleven , please see #67441 |
|
This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved |
3df4c7c to
5690d38
Compare
| if (s->penv.lua.background) { | ||
| s->penv.lua.background->create_background_metatable(L); | ||
| } | ||
| rc = rgw::lua::lua_execute(L, s, code); |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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>
5690d38 to
b5fe6fa
Compare
|
jenkins test make check |
|
jenkins test make check arm64 |
|
Latest teuthology run results: There are 22 failures, of which:
These are unrelated to this PR. |
|
This is an automated message by src/script/redmine-upkeep.py. I found one or more
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 |
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
xbetween the brackets:[x]. Spaces and capitalization matter when checking off items this way.Checklist
Show available Jenkins commands
jenkins test classic perfJenkins Job | Jenkins Job Definitionjenkins test crimson perfJenkins Job | Jenkins Job Definitionjenkins test signedJenkins Job | Jenkins Job Definitionjenkins test make checkJenkins Job | Jenkins Job Definitionjenkins test make check arm64Jenkins Job | Jenkins Job Definitionjenkins test submodulesJenkins Job | Jenkins Job Definitionjenkins test dashboardJenkins Job | Jenkins Job Definitionjenkins test dashboard cephadmJenkins Job | Jenkins Job Definitionjenkins test apiJenkins Job | Jenkins Job Definitionjenkins test docsReadTheDocs | Github Workflow Definitionjenkins test ceph-volume allJenkins Jobs | Jenkins Jobs Definitionjenkins test windowsJenkins Job | Jenkins Job Definitionjenkins test rook e2eJenkins Job | Jenkins Job DefinitionYou must only issue one Jenkins command per-comment. Jenkins does not understand
comments with more than one command.