-
Notifications
You must be signed in to change notification settings - Fork 871
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(AclFamily): add acl log #1865
Conversation
auto end = res.find("irq"); | ||
res.erase(start, end - start); | ||
|
||
// The following are dummy fields and users should not rely on those unless |
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, apparently I am running an "old" version of the client (mine is 4.5
).
I think this is fixed for 5.0
. We will need to update the version for pytests + test on 5.0
. Will ping about this on Monday.
I had to slightly diverge from the original implementation https://redis.io/commands/acl-log/ In particular:
|
@dranikpg I need to fix the tests, it's a small but + investigate for the pyclient version 5. Do not review :) |
src/server/acl/acl_family.cc
Outdated
} | ||
|
||
std::vector<AclLog::LogType> logs(pool_->size()); | ||
pool_->AwaitFiberOnAll( |
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 nicer to pass max_output
into GetLog()
so you don't have to copy redundant data in the first place.
src/server/acl/acl_family.cc
Outdated
(*cntx)->StartArray(12); | ||
(*cntx)->SendSimpleString("reason"); | ||
using Reason = AclLog::Reason; | ||
std::string reason = entry.reason == Reason::COMMAND ? "COMMAND" : "AUTH"; |
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.
std::string reason = entry.reason == Reason::COMMAND ? "COMMAND" : "AUTH"; | |
std::string_view reason = entry.reason == Reason::COMMAND ? "COMMAND" : "AUTH"; |
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 will make the change but this doesn't offer anything. SSO will kick in anyway for the 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.
fair :)
src/server/acl/acl_family.cc
Outdated
} | ||
|
||
(*cntx)->StartArray(total_entries); | ||
for (auto& log : logs) { |
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.
We should merge + sort by timestamp otherwise the first shard can shadow everything from the other shards.
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.
💎 💯
|
||
private: | ||
LogType log_; | ||
const size_t total_entries_allowed_; |
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.
We should make this configurable in runtime as well
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.
Yep, just like acllog. This will be on a different PR
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'll drop my comments as well if Roy did 😆 (I've seen you asked not to review)
await async_client.execute_command("AUTH elon wrong") | ||
|
||
res = await async_client.execute_command("ACL LOG") | ||
assert 1 == len(res) |
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'd test at least once the array has meaningful contents 🙂
@@ -23,7 +23,7 @@ namespace acl { | |||
|
|||
class AclFamily final { | |||
public: | |||
explicit AclFamily(UserRegistry* registry); | |||
explicit AclFamily(UserRegistry* registry, util::ProactorPool* pool); |
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.
global shard_set->pool()
is our default proactor pool
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.
which is the same:
shard_set = new EngineShardSet(pp);
and above that:
629 Service::Service(ProactorPool* pp)
630 : pp_(*pp),
631 acl_family_(&user_registry_, pp),
632 server_family_(this),
633 cluster_family_(&server_family_) {
so shard_set->pool()
returns pp
😛
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.
Yes, so I meant that you don't need to pass it around (almost none of our code does) 😝
src/server/server_state.h
Outdated
acl::UserRegistry* user_registry; | ||
|
||
acl::AclLog log; |
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'd prefix acl stuff with acl_
using clock = std::chrono::system_clock; | ||
LogEntry entry = {std::move(username), std::move(client_info), std::move(object), reason, | ||
clock::now()}; | ||
log_.push_front(std::move(entry)); |
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.
emplace_back 👀
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's sorted by time, that's why I am pushing at the front... Then we can perform an m-way
merge of the sorted logs to finally print the output
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.
Ah, I missed the "front". And you use a deque, right. We had a ring buffer in helio but it doesn't matter in this context
src/facade/dragonfly_connection.cc
Outdated
auto start = res.find("tid"); | ||
auto end = res.find("irq"); | ||
res.erase(start, end - 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.
This looks fragile 🙂
src/facade/dragonfly_connection.cc
Outdated
// we decide to implement them. | ||
// This is only done because the redis pyclient parser for the field "client-info" | ||
// for the command ACL LOG | ||
// is completely *BROKEN* and it literally hardcodes the expected values. | ||
// What is more preposterous is that that it actually doesn't conform to the | ||
// actual expected values, since it's missing half of them. That is, even for | ||
// redis-server, issuing an ACL LOG command via redis-cli and the pyclient | ||
// will return different results! An example of ACL LOG ran with redis-cli is: | ||
// | ||
// "client-info" | ||
// 14) "id=3 addr=127.0.0.1:57275 laddr=127.0.0.1:6379 fd=8 name= age=16 | ||
// idle=0 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 qbuf=48 qbuf-free=16842 | ||
// argv-mem=25 multi-mem=0 rbs=1024 rbp=0 obl=0 oll=0 omem=0 | ||
// tot-mem=18737 events=r cmd=auth user=default redir=-1 resp=2" | ||
// | ||
// Meanwhile the parser in the pyclient: | ||
// |
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.
You forgot to add what the pyclient returns. It might very well be that it expects hardcoded values, but as long as it worked for hundreds of thousands of people I assume its not seriously broken. I'm also not sure we need this long comment 😆, you can just add a comment above strappends below that this if for compatibility with redis-py 4.X
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 was completely broken. It basically filtered most of the output fields.
worked for hundreds of thousands of people I assume its not seriously broken
It worked so well that they refactored all of its code on the next version 😛
you can just add a comment above strappends below that this if for compatibility with redis-py 4.X
Yes I want to first check if all works with 5.0. But it was a rollercoaster, because my tests would fail because the pyclient could not parse the correct by the protocol output
src/server/acl/acl_log.cc
Outdated
ABSL_FLAG(size_t, acllog_max_len, 32, | ||
"Specify the number of log entries. Logs are kept locally for each proactor " | ||
"and therefore the total number of entries are acllog_max_len * proactors"); |
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 might be confusing to people who don't know what a "proactor" is, lets call it thread. Also I assume that those events (unsuccessful login & unauthorized execution) are not that common to not use a centralized counter for this
@@ -23,7 +23,7 @@ namespace acl { | |||
|
|||
class AclFamily final { | |||
public: | |||
explicit AclFamily(UserRegistry* registry); | |||
explicit AclFamily(UserRegistry* registry, util::ProactorPool* pool); |
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.
Yes, so I meant that you don't need to pass it around (almost none of our code does) 😝
Ohhh, I will fix this on the next PR <3 |
Resolves #1808