Skip to content

Commit 866b9af

Browse files
authored
chore: remove locks from acl validation in the hot path (#1765)
* remove locking the registry when Validating users * deep copies the acl categories in the connection context * streamline updates to the acl categories via the proactor pool
1 parent 7492550 commit 866b9af

File tree

11 files changed

+106
-23
lines changed

11 files changed

+106
-23
lines changed

src/server/acl/acl_family.cc

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "server/acl/acl_family.h"
55

6+
#include <glog/logging.h>
7+
68
#include <cctype>
79
#include <optional>
810
#include <variant>
@@ -12,6 +14,7 @@
1214
#include "absl/strings/match.h"
1315
#include "absl/strings/str_cat.h"
1416
#include "core/overloaded.h"
17+
#include "facade/dragonfly_connection.h"
1518
#include "facade/facade_types.h"
1619
#include "server/acl/acl_commands_def.h"
1720
#include "server/command_registry.h"
@@ -157,12 +160,31 @@ std::variant<User::UpdateRequest, ErrorReply> ParseAclSetUser(CmdArgList args) {
157160

158161
} // namespace
159162

163+
void AclFamily::StreamUpdatesToAllProactorConnections(std::string_view user, uint32_t update_cat) {
164+
auto update_cb = [user, update_cat]([[maybe_unused]] size_t id, util::Connection* conn) {
165+
DCHECK(conn);
166+
auto connection = static_cast<facade::Connection*>(conn);
167+
auto ctx = static_cast<ConnectionContext*>(connection->cntx());
168+
if (ctx && user == ctx->authed_username) {
169+
ctx->acl_categories = update_cat;
170+
}
171+
};
172+
173+
if (main_listener_) {
174+
main_listener_->TraverseConnections(update_cb);
175+
}
176+
}
177+
160178
void AclFamily::SetUser(CmdArgList args, ConnectionContext* cntx) {
161179
std::string_view username = facade::ToSV(args[0]);
162180
auto req = ParseAclSetUser(args.subspan(1));
163181
auto error_case = [cntx](ErrorReply&& error) { (*cntx)->SendError(error); };
164-
auto update_case = [username, cntx](User::UpdateRequest&& req) {
165-
ServerState::tlocal()->user_registry->MaybeAddAndUpdate(username, std::move(req));
182+
auto update_case = [username, cntx, this](User::UpdateRequest&& req) {
183+
auto& registry = ServerState::tlocal()->user_registry;
184+
auto user_with_lock = registry->MaybeAddAndUpdateWithLock(username, std::move(req));
185+
if (user_with_lock.exists) {
186+
StreamUpdatesToAllProactorConnections(username, user_with_lock.user.AclCategory());
187+
}
166188
(*cntx)->SendOk();
167189
};
168190

@@ -171,8 +193,15 @@ void AclFamily::SetUser(CmdArgList args, ConnectionContext* cntx) {
171193

172194
using CI = dfly::CommandId;
173195

174-
#define HFUNC(x) SetHandler(&AclFamily::x)
196+
using MemberFunc = void (AclFamily::*)(CmdArgList args, ConnectionContext* cntx);
197+
198+
inline CommandId::Handler HandlerFunc(AclFamily* acl, MemberFunc f) {
199+
return [=](CmdArgList args, ConnectionContext* cntx) { return (acl->*f)(args, cntx); };
200+
}
201+
202+
#define HFUNC(x) SetHandler(HandlerFunc(this, &AclFamily::x))
175203

204+
constexpr uint32_t kAcl = acl::CONNECTION;
176205
constexpr uint32_t kList = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
177206
constexpr uint32_t kSetUser = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
178207

@@ -184,7 +213,7 @@ constexpr uint32_t kSetUser = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
184213
// easy to handle that case explicitly in `DispatchCommand`.
185214

186215
void AclFamily::Register(dfly::CommandRegistry* registry) {
187-
*registry << CI{"ACL", CO::NOSCRIPT | CO::LOADING, 0, 0, 0, 0, acl::kList}.HFUNC(Acl);
216+
*registry << CI{"ACL", CO::NOSCRIPT | CO::LOADING, 0, 0, 0, 0, acl::kAcl}.HFUNC(Acl);
188217
*registry << CI{"ACL LIST", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, 1, 0, 0, 0, acl::kList}.HFUNC(
189218
List);
190219
*registry << CI{"ACL SETUSER", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kSetUser}
@@ -193,4 +222,8 @@ void AclFamily::Register(dfly::CommandRegistry* registry) {
193222

194223
#undef HFUNC
195224

225+
void AclFamily::Init(facade::Listener* main_listener) {
226+
main_listener_ = main_listener;
227+
}
228+
196229
} // namespace dfly::acl

src/server/acl/acl_family.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,38 @@
44

55
#pragma once
66

7+
#include <cstdint>
8+
#include <string_view>
9+
#include <vector>
10+
11+
#include "facade/dragonfly_listener.h"
12+
#include "helio/util/proactor_pool.h"
713
#include "server/common.h"
814

915
namespace dfly {
16+
1017
class ConnectionContext;
1118
class CommandRegistry;
1219

1320
namespace acl {
1421

15-
class AclFamily {
22+
class AclFamily final {
1623
public:
17-
static void Register(CommandRegistry* registry);
24+
AclFamily() = default;
25+
26+
void Register(CommandRegistry* registry);
27+
void Init(facade::Listener* listener);
1828

1929
private:
20-
static void Acl(CmdArgList args, ConnectionContext* cntx);
21-
static void List(CmdArgList args, ConnectionContext* cntx);
22-
static void SetUser(CmdArgList args, ConnectionContext* cntx);
30+
void Acl(CmdArgList args, ConnectionContext* cntx);
31+
void List(CmdArgList args, ConnectionContext* cntx);
32+
void SetUser(CmdArgList args, ConnectionContext* cntx);
33+
34+
// Helper function that updates all open connections and their
35+
// respective ACL fields on all the available proactor threads
36+
void StreamUpdatesToAllProactorConnections(std::string_view user, uint32_t update_cat);
37+
38+
facade::Listener* main_listener_{nullptr};
2339
};
2440

2541
} // namespace acl

src/server/acl/user_registry.cc

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,28 @@ bool UserRegistry::AuthUser(std::string_view username, std::string_view password
5555
return user->second.IsActive() && user->second.HasPassword(password);
5656
}
5757

58-
UserRegistry::RegistryViewWithLock::RegistryViewWithLock(std::shared_lock<util::SharedMutex> mu,
58+
UserRegistry::RegistryViewWithLock::RegistryViewWithLock(std::shared_lock<util::SharedMutex> lk,
5959
const RegistryType& registry)
60-
: registry(registry), registry_mu_(std::move(mu)) {
60+
: registry(registry), registry_lk_(std::move(lk)) {
6161
}
6262

6363
UserRegistry::RegistryViewWithLock UserRegistry::GetRegistryWithLock() const {
6464
std::shared_lock<util::SharedMutex> lock(mu_);
6565
return {std::move(lock), registry_};
6666
}
6767

68+
UserRegistry::UserViewWithLock::UserViewWithLock(std::shared_lock<util::SharedMutex> lk,
69+
const User& user, bool exists)
70+
: user(user), exists(exists), registry_lk_(std::move(lk)) {
71+
}
72+
73+
UserRegistry::UserViewWithLock UserRegistry::MaybeAddAndUpdateWithLock(std::string_view username,
74+
User::UpdateRequest req) {
75+
std::shared_lock<util::SharedMutex> lock(mu_);
76+
const bool exists = registry_.contains(username);
77+
auto& user = registry_[username];
78+
user.Update(std::move(req));
79+
return {std::move(lock), user, exists};
80+
}
81+
6882
} // namespace dfly::acl

src/server/acl/user_registry.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class UserRegistry {
3030
// If the user with name `username` does not exist, it's added in the store with
3131
// the exact fields found in req
3232
// If the user exists, the bitfields are updated with a `logical and` operation
33-
// TODO change return time to communicate back results to acl commands
3433
void MaybeAddAndUpdate(std::string_view username, User::UpdateRequest req);
3534

3635
// Acquires a write lock on mu_
@@ -55,16 +54,29 @@ class UserRegistry {
5554
// Helper class for accessing the registry with a ReadLock outside the scope of UserRegistry
5655
class RegistryViewWithLock {
5756
public:
58-
RegistryViewWithLock(std::shared_lock<util::SharedMutex> mu, const RegistryType& registry);
57+
RegistryViewWithLock(std::shared_lock<util::SharedMutex> lk, const RegistryType& registry);
5958
const RegistryType& registry;
6059

6160
private:
62-
std::shared_lock<util::SharedMutex> registry_mu_;
61+
std::shared_lock<util::SharedMutex> registry_lk_;
6362
};
6463

6564
// Helper function used for printing users via ACL LIST
6665
RegistryViewWithLock GetRegistryWithLock() const;
6766

67+
// Helper class for accessing a user with a ReadLock outside the scope of UserRegistry
68+
class UserViewWithLock {
69+
public:
70+
UserViewWithLock(std::shared_lock<util::SharedMutex> lk, const User& user, bool exists);
71+
const User& user;
72+
const bool exists;
73+
74+
private:
75+
std::shared_lock<util::SharedMutex> registry_lk_;
76+
};
77+
78+
UserViewWithLock MaybeAddAndUpdateWithLock(std::string_view username, User::UpdateRequest req);
79+
6880
private:
6981
RegistryType registry_;
7082
// TODO add abseil mutex attributes

src/server/acl/validator.cc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,12 @@
44

55
#include "server/acl/validator.h"
66

7-
#include "server/server_state.h"
8-
97
namespace dfly::acl {
108

119
[[nodiscard]] bool IsUserAllowedToInvokeCommand(const ConnectionContext& cntx,
1210
const facade::CommandId& id) {
13-
auto& registry = *ServerState::tlocal()->user_registry;
14-
auto credentials = registry.GetCredentials(cntx.authed_username);
1511
auto command_credentials = id.acl_categories();
16-
return (credentials.acl_categories & command_credentials) != 0;
12+
return (cntx.acl_categories & command_credentials) != 0;
1713
}
1814

1915
} // namespace dfly::acl

src/server/acl/validator.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
#pragma once
66

7-
#include <string_view>
8-
97
#include "facade/command_id.h"
108
#include "server/conn_context.h"
119

src/server/conn_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <absl/container/fixed_array.h>
88
#include <absl/container/flat_hash_set.h>
99

10+
#include "acl/acl_commands_def.h"
1011
#include "core/fibers.h"
1112
#include "facade/conn_context.h"
1213
#include "facade/reply_capture.h"
@@ -197,6 +198,7 @@ class ConnectionContext : public facade::ConnectionContext {
197198
FlowInfo* replication_flow;
198199

199200
std::string authed_username{"default"};
201+
uint32_t acl_categories{acl::ALL};
200202

201203
private:
202204
void EnableMonitoring(bool enable) {

src/server/dfly_main.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ bool RunEngine(ProactorPool* pool, AcceptServer* acceptor) {
330330
Listener* main_listener = nullptr;
331331

332332
std::vector<facade::Listener*> listeners;
333+
// If we ever add a new listener, plz don't change this,
334+
// we depend on tcp listener to be at the front since we later
335+
// need to pass it to the AclFamily::Init
333336
if (!tcp_disabled) {
334337
main_listener = new Listener{Protocol::REDIS, &service};
335338
listeners.push_back(main_listener);

src/server/main_service.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,12 @@ void Service::Init(util::AcceptServer* acceptor, std::vector<facade::Listener*>
664664
}
665665

666666
shard_set->Init(shard_num, !opts.disable_time_update);
667-
667+
const auto tcp_disabled = GetFlag(FLAGS_port) == 0u;
668+
// We assume that listeners.front() is the main_listener
669+
// see dfly_main RunEngine
670+
if (!tcp_disabled && !listeners.empty()) {
671+
acl_family_.Init(listeners.front());
672+
}
668673
request_latency_usec.Init(&pp_);
669674
StringFamily::Init(&pp_);
670675
GenericFamily::Init(&pp_);
@@ -2132,7 +2137,7 @@ void Service::RegisterCommands() {
21322137
BitOpsFamily::Register(&registry_);
21332138
HllFamily::Register(&registry_);
21342139
SearchFamily::Register(&registry_);
2135-
acl::AclFamily::Register(&registry_);
2140+
acl_family_.Register(&registry_);
21362141

21372142
server_family_.Register(&registry_);
21382143
cluster_family_.Register(&registry_);

src/server/main_service.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "base/varz_value.h"
1010
#include "core/interpreter.h"
1111
#include "facade/service_interface.h"
12+
#include "server/acl/acl_family.h"
1213
#include "server/acl/user_registry.h"
1314
#include "server/cluster/cluster_family.h"
1415
#include "server/command_registry.h"
@@ -165,6 +166,7 @@ class Service : public facade::ServiceInterface {
165166
util::ProactorPool& pp_;
166167

167168
acl::UserRegistry user_registry_;
169+
acl::AclFamily acl_family_;
168170
ServerFamily server_family_;
169171
ClusterFamily cluster_family_;
170172
CommandRegistry registry_;

0 commit comments

Comments
 (0)