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

rcmgr: Connections and Streams should also reserve memory #2010

Open
MarcoPolo opened this issue Jan 24, 2023 · 6 comments
Open

rcmgr: Connections and Streams should also reserve memory #2010

MarcoPolo opened this issue Jan 24, 2023 · 6 comments

Comments

@MarcoPolo
Copy link
Collaborator

What

Reserving a connection/stream should reserve a roughly accurate amount of memory.

Why

This lets connections/streams still be limited by the memory limits. This enables a use case where a user may only want to think in terms of FDs and memory. They can set connections/streams to unlimited and only limit FDs and memory.

Why in go-libp2p

... as opposed to folks wrapping the resource manager.

go-libp2p has the most context to give a good approximation for the resource cost of a connection for each transport. e.g. a QUIC connection won't consume a FD but a TCP connection will.

Kubo folks (especially @ajnavarro) brought this up as helpful simplification of the knobs in the resource manager.

@marten-seemann
Copy link
Contributor

We kind of do this already, since the muxer applied on top of the connection will reserve memory. And in the case of QUIC, the QUIC transport takes care of this.

I'm not sure how useful memory tracking actually is though, except for muxers, so #1708 might be an alternative.

@MarcoPolo
Copy link
Collaborator Author

We kind of do this already, since the muxer applied on top of the connection will reserve memory.

Yeah. I think reserving some memory for the connection itself also makes sense. This way a connection (even if it doesn't have any streams) still has a weight to it.

I'm not sure how useful memory tracking actually is though

It's probably the most useful thing to track. The thing that will kill our process is running out of memory. I'd argue that limiting connections and streams is just a proxy for limiting memory (and CPU time).

It also simpler to think about just memory (and FDs) than to think about connection counts and streams at the various levels.

@BigLep
Copy link
Contributor

BigLep commented Jan 24, 2023

@BigLep has been in Kubo standup and go-libp2p triage. Here's the Kubo perspective that I want to address ASAP:

Protect against a script-kiddie being able to easily take down a Kubo node by simply opening many connections or stream.

The way I'd like to accomplish this is with default configuration in ipfs/kubo#9587 . We can discuss specifics there.

In order for this work, established (non-transient) connections will need register memory usage with the resource manager/accountant, ideally across all transports and muxers. If an established libp2p connection doesn't increase memory accounted for by the resource manager/accountant, then I believe the work in this issue will be needed. I'm trying to avoid needing to go-libp2p code changes.

@marten-seemann
Copy link
Contributor

In order for this work, established (non-transient) connections will need register memory usage with the resource manager/accountant, ideally across all transports and muxers. If an established libp2p connection doesn't increase memory accounted for by the resource manager/accountant, then I believe the work in this issue will be needed. I'm trying to avoid needing to go-libp2p code changes.

It makes sense to reserve some amount of memory (1 MB maybe?) per connection, once a connection has been accepted by the swarm. This would be justifiable since a connection does impose a certain overhead (storing it in the swarm, tracking addresses and protocols in the peer store etc.). This amount of memory would be independent of (and in addition to) the memory reserved by the stream multiplexer.
We might also want to consider doing the same for every stream, with a much smaller amount (1 kB?).

Memory reservations should use different priorities for incoming and outgoing connections / streams, otherwise an attacker will be able to saturate all available memory for incoming connections, preventing the node from dialing new outgoing connections.

@MarcoPolo
Copy link
Collaborator Author

Thanks Marten. I agree with all those points.

To answer Steve's question of "Can I use this config today safely?". We take into account the memory usage of streams already.

I believe all but tcp+yamux connections reserve memory already as a byproduct of the above. TCP+Yamux connections will reserve memory once streams are opened. If there are no streams, these connections are still bounded by file descriptor limits.

Therefore, I think that config is safe to use. However I'm still for making this change. I just don't think it's a blocker.

@BigLep
Copy link
Contributor

BigLep commented Jan 26, 2023

Cool. I think we can do this in Kubo for now and let it roll out in go-libp2p naturally. I updated ipfs/kubo#9587 with the intent of setting System.ConnsInbound to equal 1 inbound connection per MB so we're protected from the TCP+Yamux connection case. That can be removed when go-libp2p does more memory accounting.

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

No branches or pull requests

3 participants