Skip to content
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(server): Support limiting the number of open connections. #1670

Merged
merged 3 commits into from
Aug 27, 2023

Conversation

royjacobson
Copy link
Contributor

Close #1496.

Also, make CONFIG GET use config_registry so it's easier to add flags.

src/server/config_registry.cc Outdated Show resolved Hide resolved
src/facade/dragonfly_listener.cc Show resolved Hide resolved
if (!res)
return false;

for (auto* listener : listeners_) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should only apply to the main port listener. For sure not for admin

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed (and added a test accordingly).

I added IsAdmin/SetAdmin methods to Listener, but I don't think it's an overkill, we already wanted something similar when we spoke about hiding admin connections from CLIENT LIST.

* Don't limit admin connections (and add a test case)
@royjacobson royjacobson requested a review from romange August 9, 2023 20:04
Comment on lines +38 to +39
bool IsAdminInterface() const;
void SetAdminInterface(bool is_admin = true);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have Connection::IsAdmin(), could we maybe consolidate them to use the same logic? (either inspect the port or explicitly set like here)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooo, I did NOT expect the implementation of Connection::IsAdmin to be what it is 😆
Changed it to query the Listener.

chakaz
chakaz previously approved these changes Aug 10, 2023
Copy link
Contributor

@kostasrim kostasrim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧑‍🍳 🧑‍🍳 🧑‍🍳 nice work!

src/server/config_registry.cc Outdated Show resolved Hide resolved
src/server/config_registry.cc Outdated Show resolved Hide resolved
tests/dragonfly/config_test.py Show resolved Hide resolved
@@ -267,6 +275,10 @@ void Listener::OnConnectionClose(util::Connection* conn) {
}
}

void Listener::OnMaxConnectionsReached(util::FiberSocketBase* sock) {
sock->Write(io::Buffer("-ERR max number of clients reached\r\n"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will this be handled by the client?
I imagine that it will receive an error before even sending a command, so will it even know how to treat it?
Also, don't we want to close the connection? (or is it implicitly done by the framework?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clients I tested (python, redis-cli) seemed to handle it fine. redis does behave a bit differently (the socket doesn't close immediately and the message is delayed until the first command).

But then I tried to DDoS redis by opening a bunch of connections and leaving them open, and apparently it will start doing what we do.

and yeah, the framework is closing the connection :)

@@ -399,8 +400,7 @@ uint32_t Connection::GetClientId() const {
}

bool Connection::IsAdmin() const {
uint16_t admin_port = absl::GetFlag(FLAGS_admin_port);
return socket_->LocalEndpoint().port() == admin_port;
return static_cast<Listener*>(owner())->IsAdminInterface();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add IsAdminInterface() API to ListenerInterface?
I realize it's perhaps not clean, but having a downcast is messy and can lead to issues down the road

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsAdminInterface is dragonfly specific. On the other side, facade::Connection is always issued by facade::Listener.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to any suggestions of removing hardcoded downcasts :)
We could even template-ize Listener to have facade Listener return its own connection

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal opinion is that we should avoid casts if there is a reasonable design choice that allows it. And if not - we can have them and not feel bad about it. Specifically, any virtual inheritance implies the possibility of downcasts and it's fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the cast either, if we start doing it too much I think we should find out a better way, but for now this was the shortest way I found to do this :)

@royjacobson royjacobson merged commit ed845fe into main Aug 27, 2023
@royjacobson royjacobson deleted the max_clients branch August 27, 2023 08:30
kostasrim pushed a commit that referenced this pull request Aug 29, 2023
* feat(server): Support limiting the number of open connections.

* * Update helio after the small fix was merged to master
* Don't limit admin connections (and add a test case)

* Resolve CR comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

introduce maxclients flag
4 participants