-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rgw/lua: support reloading lua packages on all RGWs #52326
Conversation
@@ -3482,6 +3477,101 @@ int RadosLuaManager::list_packages(const DoutPrefixProvider *dpp, optional_yield | |||
return 0; | |||
} | |||
|
|||
int RadosLuaManager::watch_reload(const DoutPrefixProvider *dpp, uint64_t* watch_handle, librados::WatchCtx2* watcher) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could not make that a sal API since the callbacks are expected to belong to a class deriving from WatchCtx2
which is RASDOS specific
src/rgw/rgw_lua_background.cc
Outdated
@@ -177,5 +189,46 @@ void Background::create_background_metatable(lua_State* L) { | |||
create_metatable<rgw::lua::RGWTable>(L, true, &rgw_map, &table_mutex); | |||
} | |||
|
|||
#ifdef WITH_RADOSGW_LUA_PACKAGES | |||
int Background::PackagesWatcher::start() { | |||
return static_cast<rgw::sal::RadosLuaManager*>(parent->lua_manager.get())->watch_reload(&parent->dp, &watch_handle, this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is ugly...
see comment on: https://github.com/ceph/ceph/pull/52326/files#diff-8d5ab77cf6b24e95c2c7b352c52d1e254781f6c9e7fb3fd148bddc8a3f4321e7R3480
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ceph code, like comedy, is not pretty ;)
This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved |
6e3f80e
to
6349290
Compare
6349290
to
3efd14c
Compare
3efd14c
to
bbe831c
Compare
doc/radosgw/lua-scripting.rst
Outdated
@@ -114,6 +115,13 @@ To print the list of packages in the allowlist: | |||
# radosgw-admin script-package list | |||
|
|||
|
|||
To apply all changes from the allowlist to all RGWs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove the first "all"
ceph::encode(reload_status, reply); | ||
auto pool = store->getRados()->get_lc_pool_ctx(); | ||
if (!pool->is_valid()) { | ||
ldpp_dout(_dpp, 1) << "ERROR: failed to ack reload of " << PACKAGE_LIST_OBJECT_NAME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: ACK
auto iter = ack.payload_bl.cbegin(); | ||
ceph::decode(r, iter); | ||
} catch (buffer::error& err) { | ||
ldpp_dout(_dpp, 1) << "ERROR: couldn't decode Lua packages reload status. error: " << |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: lua
here and below should be Lua
src/rgw/rgw_lua.cc
Outdated
auto r = install_packages(lua_manager->dpp(), lua_manager->driver(), | ||
y, lua_manager->luarocks_path(), failed_packages); | ||
if (r < 0) { | ||
ldpp_dout(lua_manager->dpp(), 1) << "WARNING: failed to install lua packages from allowlist. error: " << r |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING
and errror
seem at odds. Perhaps WARNING: failed to install Lua packages from allowlist: " << r << dendl;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it says "WARNING" because failing to install packages does not necesarily impact service
only if there is a script that want to use these packages the service may be impacted.
mayebe will change to "error code" istead of "error"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe "return code"?
src/rgw/rgw_lua_background.cc
Outdated
@@ -177,5 +189,46 @@ void Background::create_background_metatable(lua_State* L) { | |||
create_metatable<rgw::lua::RGWTable>(L, true, &rgw_map, &table_mutex); | |||
} | |||
|
|||
#ifdef WITH_RADOSGW_LUA_PACKAGES | |||
int Background::PackagesWatcher::start() { | |||
return static_cast<rgw::sal::RadosLuaManager*>(parent->lua_manager.get())->watch_reload(&parent->dp, &watch_handle, this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ceph code, like comedy, is not pretty ;)
jenkins test api |
jenkins test make check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm hoping we can clean up the sal interface
src/rgw/rgw_sal.h
Outdated
/** Add a callback to watch packages reload **/ | ||
virtual int watch_reload() = 0; | ||
/** Stop watching the packages reload **/ | ||
virtual int unwatch_reload() = 0; | ||
/** Send a reply to the reloader of the packages indicating if the reload was successfull **/ | ||
virtual void ack_reload(uint64_t notify_id, uint64_t cookie, int reload_status) = 0; | ||
/** Get the watch handle which should be shared between watch_reload() and unwatch_reload() **/ | ||
virtual uint64_t watch_handle() const = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are all internals of rados' watch/notify protocol that don't belong in the sal interface
can the RadosLuaManager
just initialize the watch on construction and drop it on shutdown/destruction to avoid exposing the watch_reload()
and unwatch_reload()
APIs? we shouldn't need an abstraction for ack_reload()
, either - the rados implementation would just provide a callback that calls install_packages()
from rgw_lua.cc
the only thing i think we actually need in the sal API is reload_packages()
to trigger the reload. for rados, that would just send a notify message and wait for acks. that would (indirectly) invoke our callback to do the actual reload
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
donne in fe641a9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbodley can you please re-review?
src/rgw/rgw_sal.h
Outdated
/** Get dout prefix provider **/ | ||
virtual const DoutPrefixProvider* dpp() const = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i see that RadosLuaManager needs a dpp for its watch/notify stuff, but that shouldn't need to be exposed in the interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
src/rgw/rgw_sal.h
Outdated
/** Get a Lua script manager for running lua scripts */ | ||
virtual std::unique_ptr<LuaManager> get_lua_manager() = 0; | ||
/** Get a Lua script manager for running lua scripts and reloading packages */ | ||
virtual std::unique_ptr<LuaManager> get_lua_manager(const DoutPrefixProvider* dpp = nullptr, const std::string& luarocks_path = "") = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please avoid using default arguments for virtual functions
7946bc5
to
fe641a9
Compare
This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved |
fe641a9
to
809cdc8
Compare
jenkins test api |
This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved |
and move the rest of the functionality to sal::Driver this is to prevent the cases where the lua manager was allocated only to invoke a single functions, and did not have any real lifetime. see discussion here: ceph#52326 (comment) Signed-off-by: Yuval Lifshitz <ylifshit@redhat.com>
see PR comments: ceph#52326 (comment) Signed-off-by: Yuval Lifshitz <ylifshit@redhat.com>
ca066e5
to
e018fc3
Compare
@dang could you please review the latest changes, as I moved most APIs from the Lua manager to the sal Driver? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a strong opinion about this either way. On the one hand, it's nice to break up the API into chunks. On the other, objects without well defined lifecycles can be problematic. My very slight preference is to have a LuaManager to separate out the API, but it's not a big deal to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will need to apply the sal changes to driver/posix also
lua_background->start(); | ||
env.lua.background = lua_background.get(); | ||
static_cast<rgw::sal::RadosLuaManager*>(env.lua.manager.get())->watch_reload(dpp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean? i don't think AppMain or the realm reloader should have to manage this watch. RadosLuaManager
can call watch/unwatch in its own constructor/destructor without needing anything else
env.lua.manager = env.driver->get_lua_manager(); | ||
env.lua.manager = env.driver->get_lua_manager(env.lua.manager->get_luarocks_path()); | ||
if (env.lua.background) { | ||
env.lua.background->set_manager(env.lua.manager.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even if this updates the LuaManager
pointer, it leaves Background::driver
as a dangling pointer to the Driver that we destroyed with DriverManager::close_storage()
above. the RadosLuaManager
would also hold a handling pointer to RadosStore* const store
we really should shut down this background thread before close_storage()
, and only restart it once we've recreated the new driver and lua manager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regarding the background thread. it is paused during reload (and the driver is set to null), and resumed only after the driver is updated to the new one. why do you think there is an issue?
regarding the lua package manager, it does not have any thread, just getting notifications from the rados thread (that should be paused during the reload). so, i'm not sure when the issue can happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you think there is an issue?
env.lua.manager
extends the lifetime of RadosLuaManager
past the point of its driver's deletion. this means that the ~RadosLuaManager()
dtor would be making calls on a librados::IoCtx
whose librados::Rados
client was already destroyed. we should free env.lua.manager
before calling close_storage()
the posix driver derives from |
and move the rest of the functionality to sal::Driver this is to prevent the cases where the lua manager was allocated only to invoke a single functions, and did not have any real lifetime. see discussion here: ceph#52326 (comment) Signed-off-by: Yuval Lifshitz <ylifshit@redhat.com>
see PR comments: ceph#52326 (comment) Signed-off-by: Yuval Lifshitz <ylifshit@redhat.com>
e018fc3
to
889290a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the Background pause/resume logic does look like it would avoid dangling pointer issues, just a question about locking there
src/rgw/rgw_lua_background.cc
Outdated
std::unique_lock cond_lock(pause_mutex); | ||
if (!paused) { | ||
set_package_path(L, lua_manager->get_luarocks_path()); | ||
std::string no_tenant; | ||
const auto rc = rgw::lua::read_script(&dp, driver, no_tenant, | ||
null_yield, rgw::lua::context::background, script); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is it necessary to hold this lock over the call to read_script()
?
looking at the lock above:
if (paused) {
ldpp_dout(dpp, 10) << "Lua background thread paused" << dendl;
std::unique_lock cond_lock(cond_mutex);
cond.wait(cond_lock, [this]{return !paused || stopped;});
paused
isn't atomic - shouldn't we acquire the lock before checking it? if we take the lock there, we should be able to call set_package_path()
under that same lock instead of reacquiring it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is it necessary to hold this lock over the call to
read_script()
?
what if I'm reading the script (= RADOS object), and then real reload is triggered?
I want the function that tries to call Background::pause()
to be blocked until the read ends
looking at the lock above:
if (paused) { ldpp_dout(dpp, 10) << "Lua background thread paused" << dendl; std::unique_lock cond_lock(cond_mutex); cond.wait(cond_lock, [this]{return !paused || stopped;});
paused
isn't atomic - shouldn't we acquire the lock before checking it? if we take the lock there, we should be able to callset_package_path()
under that same lock instead of reacquiring it here
paused
is just a boolean, it is being set atomically (although there could be a race condition when one thread sets it and another one reads it but still doe not see the new value).
this means that:
puase()
was called, but we still see "pause=false", but in this case, we would see the right value in line 157, won't read the script and won't run it. and just sleep until the next iterationresume()
was called, but we still see "pause=true", in this case we would get into the "if" statement in line 142, and wait. agree there is an issue here, we may get stuck onwait
:-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
paused
is just a boolean, it is being set atomically (although there could be a race condition when one thread sets it and another one reads it but still doe not see the new value).
there's really no need to be clever about this, just to avoid a lock every 5 seconds
and i still think this would be simpler if we took out this pause/resume logic and just stop/restart the background thread during realm reload
what if I'm reading the script (= RADOS object), and then real reload is triggered?
this wouldn't be a problem if we shutdown/join the thread before destroying the rados store
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. agree. its is going to be simpler with paus/resume == shutdown/start
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed here: 0beafd6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbodley now background is explicitly shutdown+start in the realm reloader.
this also makes the lua background independent of RADOS
env.lua.manager = env.driver->get_lua_manager(); | ||
env.lua.manager = env.driver->get_lua_manager(env.lua.manager->get_luarocks_path()); | ||
if (env.lua.background) { | ||
env.lua.background->set_manager(env.lua.manager.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you think there is an issue?
env.lua.manager
extends the lifetime of RadosLuaManager
past the point of its driver's deletion. this means that the ~RadosLuaManager()
dtor would be making calls on a librados::IoCtx
whose librados::Rados
client was already destroyed. we should free env.lua.manager
before calling close_storage()
0beafd6
to
9bb458e
Compare
This pull request can no longer be automatically merged: a rebase is needed and changes have to be manually resolved |
Signed-off-by: Yuval Lifshitz <ylifshit@ibm.com>
and switch to it only once installation is done. this is needed for cases where installation can happen while RGW is running Signed-off-by: Yuval Lifshitz <ylifshit@ibm.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks great! we'll eventually want some reload coverage in teuthology
without requiring a restart of the RGWs test instructions: https://gist.github.com/yuvalif/95b8ed9ea73ab4591c59644a050e01e2 also use capitalized "Lua" in logs/doc Signed-off-by: Yuval Lifshitz <ylifshit@ibm.com>
9bb458e
to
7a11f1d
Compare
thanks! |
teuthology results: http://pulpito.ceph.com/yuvalif-2023-10-08_12:46:18-rgw-wip-yuval-lua-reload-distro-default-smithi/
|
same teuthology results as "main": |
test instructions:
https://gist.github.com/yuvalif/95b8ed9ea73ab4591c59644a050e01e2
Checklist
Show available Jenkins commands
jenkins retest this please
jenkins test classic perf
jenkins test crimson perf
jenkins test signed
jenkins test make check
jenkins test make check arm64
jenkins test submodules
jenkins test dashboard
jenkins test dashboard cephadm
jenkins test api
jenkins test docs
jenkins render docs
jenkins test ceph-volume all
jenkins test ceph-volume tox
jenkins test windows