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

Fix memory bloat for unresponded to packets #33

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

TheRushingWookie
Copy link
Contributor

@TheRushingWookie TheRushingWookie commented Apr 12, 2023

Description

Separates out packet ID tracking from the main Ping object into IDGenerator. This allows IDGenerator to keep an LRU based set of buckets of UUIDs and evict old buckets of UUIDs if needed.

It allows users to fix a memory bloat issue where pinging an address which never responds will cause IDs used inflight to be accumulate forever.

When using the LRU deletion policy, if we hit the max number of used IDs then the last bucket of IDs is deleted. This does cause us to not be able to track dropped buckets of IDs for deduplication or per packet timeouts. I think this is a worthwhile tradeoff for users that need this.

Tests

Added unit test for a server which never responds to pings
Modified existing tests to use IDGenerator correctly

Compatibility

This introduces no new APIs or dependencies and does not change overall behavior by default. If a deletion policy is used, it may prevent the tracking of duplicate packets.

@TheRushingWookie TheRushingWookie force-pushed the remove_bloat branch 4 times, most recently from 75a70f4 to 098c83a Compare April 12, 2023 22:22
@TheRushingWookie TheRushingWookie marked this pull request as ready for review April 12, 2023 22:23
@SuperQ
Copy link
Contributor

SuperQ commented Apr 13, 2023

This is similar to the work I was doing in #9.

One thing tho, you're not doing any locking, so this is not going to be thread safe as-is.

@TheRushingWookie
Copy link
Contributor Author

TheRushingWookie commented Apr 13, 2023

@SuperQ Yup this is sort of similar #9 , I took a look at that before starting this one.
I think these two PRs are trying to solve different things but there are common parts.
#9 is trying to separate out id generation/tracking so that timeouts + deleting can work. I think this is great. Per packet timeouts will be very useful.

This PR is trying to solve id generation/tracking to fix only memory bloat. It doesn't really care about timeouts but would allow other code to retire packets in the future. You could imagine per packet timeouts being added on top of this by attaching a wrapper around the returned ID or having a separate structure track per packet timeouts per ID.

@TheRushingWookie
Copy link
Contributor Author

Reading some more of the code, it looks like we don't need locks for IDGenerator as its only accessed from the runLoop goroutine. Is there a different reason to add locks?

@SuperQ
Copy link
Contributor

SuperQ commented Apr 13, 2023

There's no way to predict how this might be used later. Any time you make code that updates a shards struct like this, better safe than sorry.

@TheRushingWookie
Copy link
Contributor Author

We know how its being used currently and control how it will be used later. I don't think its best to try to predict all futures for a library and think that the library should be stricter within itself than how its used publicly

Within the package I think its better not to have locks between each subcomponent as locks are not free and we know exactly how the subcomponents work with each other. Adding locks is also not a great thing in golang as the default mutexs are not reetrant safe and can easily cause deadlocks if the components do not know the threading.

Currently, the threading is nice and simple with all logic contained within one thread (runloop) and the icmprecving thread passing it to through a channel to runloop. I'm actually really happy that the icmprecving thread passed it through a channel to runloop instead of trying to grab locks and update the structures itself.

Locking at the public API level is different and great as it means APIs are now threadsafe and no one has to tack locks on when using the API in a way the maintainers can't predict

@SuperQ
Copy link
Contributor

SuperQ commented Apr 14, 2023

Yes, exactly, Next(), Retire(), and RemoveAfterMaxItems() are public API functions. They need locking protection if they mutate the state.

@TheRushingWookie
Copy link
Contributor Author

Oh wow, yeah I forgot to make these private to within the library. They should not be called by external users!

Does this seem like the right direction for a PR? If not, let me know and I'll wait for the refactor. I won't have as much time in the next few weeks to contribute to pro-bing so I'm trying to see if I can get this in before I have to stop working with pro-bing

@TheRushingWookie
Copy link
Contributor Author

I've updated the PR to make all the APIs private

@TheRushingWookie
Copy link
Contributor Author

@SuperQ Can I get a review of this? I will probably not be able to work more on pro-bing starting next wednesday

@TheRushingWookie
Copy link
Contributor Author

Still looking for a review of this

@SuperQ
Copy link
Contributor

SuperQ commented May 1, 2023

Sorry, I've been traveling and have a bit of a review backlog.

@TheRushingWookie
Copy link
Contributor Author

Hi, could I get a look at this when you have some time?

Copy link
Contributor

@SuperQ SuperQ left a comment

Choose a reason for hiding this comment

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

Sorry for the review delay. A couple of minor issues, otherwise I think this is good to go.

The IDGenerator.go filename should be all lowercase.

}
}

func RemoveAfterMaxItems(maxItems int64) func(*idGenerator) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Public functions should have a doc comment.

Suggested change
func RemoveAfterMaxItems(maxItems int64) func(*idGenerator) {
// RemoveAfterMaxItems does some thing, I don't know what.
func RemoveAfterMaxItems(maxItems int64) func(*idGenerator) {

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.

2 participants