-
Notifications
You must be signed in to change notification settings - Fork 885
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
feat(namespaces): Initial support for multi-tenant #3260
base: main
Are you sure you want to change the base?
Conversation
This PR introduces a way to create multiple, separate and isolated namespaces in Dragonfly. Each user can be associated with a single namespace, and will not be able to interact with other namespaces. This is still experimental, and lacks some important features, such as: * Replication and RDB saving completely ignores non-default namespaces * Defrag and statistics either use the default namespace or all namespaces without separation To associate a user with a namespace, use the `ACL` command with the `TENANT:<namespace>` flag: ``` ACL SETUSER user TENANT:namespace1 ON >user_pass +@ALL ~* ``` For more examples and up to date info check `tests/dragonfly/acl_family_test.py` - specifically the `test_namespaces` function.
Is it the whole PR or the first one in the series? |
src/server/acl/user.h
Outdated
// TODO allow reset all | ||
// bool reset_all{false}; | ||
|
||
std::optional<std::string> ns{}; |
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.
subjective nit (related to the previous comment) - can be just a string with empty indicating it's not set.
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.
SG. + @kostasrim to make sure this UpdateRequest
class is always expected to contain all information (i.e. that there can't be only deltas in it)
|
||
namespace dfly { | ||
|
||
class Namespace { |
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 add class description, including a threading/ownership model?
can also be a reference to .md file if you write it there.
src/server/namespaces.h
Outdated
|
||
class Namespace { | ||
public: | ||
explicit Namespace(); |
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.
explicit not needed?
src/server/bitops_family.cc
Outdated
@@ -310,15 +310,15 @@ class ElementAccess { | |||
}; | |||
|
|||
std::optional<bool> ElementAccess::Exists(EngineShard* shard) { | |||
auto res = shard->db_slice().FindReadOnly(context_, key_, OBJ_STRING); | |||
auto res = context_.ns->GetCurrentDbSlice().FindReadOnly(context_, key_, OBJ_STRING); |
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.
Our call chain is already designed to pass shard by argument, therefore it's already in a register. I suggest that you actually use here GetDbSlice(ShardId)
and avoid doing it implicitly by calling GetCurrentDbSlice.
Now, it complicates the whole expression in so many places, so I also suggest to add a convenience wrapper ConnectionContext::FindReadOnly
.
why? because we use context_
twice here, so instead we could shorten this
in some many places to just context_.FindReadOnly(key_, OBJ_STRING)
Of course it also applies to other frequent operations like FindMutable
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.
There is a small challenge with this proposal: transaction that's part of the connection context can sometimes be null. If it's null, we can't tell which tx time to pass to FindReadOnly()
(which requires a DbContext
)
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.
Wait, sorry, my bad. I thought that your proposal was to make this change in ConnectionContext
. I now understand you meant in DbContext
..
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 looks like the only place which uses DbContext, but not using OpArgs, is bitops family, so I didn't do that.
But I used shard->shard_id()
as you proposed.
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.
Yeah, I confused both types.
src/server/bitops_family.cc
Outdated
@@ -355,7 +355,7 @@ OpResult<bool> BitNewValue(const OpArgs& args, std::string_view key, uint32_t of | |||
bool bit_value) { | |||
EngineShard* shard = args.shard; | |||
ElementAccess element_access{key, args}; | |||
auto& db_slice = shard->db_slice(); | |||
auto& db_slice = args.db_cntx.ns->GetCurrentDbSlice(); |
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.
similarly, please add a convenience accessor GetDbSlice
in OpArgs that uses the shard variable inside OpArgs
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.
Adding it in OpArgs is a great idea, it saves a lot of boilerplate, thanks!
This is the whole PR.
Indeed, I forgot about the .md hld. I'll add that very soon. |
src/server/acl/user.cc
Outdated
namespace_ = ns; | ||
} | ||
|
||
std::string User::Namespace() const { |
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.
const ref
Transaction*, std::string_view key) -> bool { | ||
return owner->db_slice().FindReadOnly(context, key, req_obj_type).ok(); | ||
auto ns = &trans->GetNamespace(); | ||
const auto key_checker = [req_obj_type, ns](EngineShard* owner, const DbContext& context, |
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.
seems you do not use ns
?
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.
See line 343 below: ns->GetDbSlice()
...
I added the high level design .md doc. Please let me know what else you would like me to include in it. I captured everything I could think of, but I probably have missed some details... |
This PR introduces a way to create multiple, separate and isolated namespaces in Dragonfly. Each user can be associated with a single namespace, and will not be able to interact with other namespaces.
This is still experimental, and lacks some important features, such as:
To associate a user with a namespace, use the
ACL
command with theNAMESPACE:<namespace>
flag:For more examples and up to date info check
tests/dragonfly/acl_family_test.py
- specifically thetest_namespaces
function.