-
Notifications
You must be signed in to change notification settings - Fork 233
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
Limit the size of provider sets #316
Comments
@Kubuxu do you have time to land a fix? |
If the storage space isn't an issue, perhaps we need some smarter provider set management so that it is not a performance issue any more. |
@Stebalien can you share the performance trace? |
We probably want to optimize the garbage collector (and possibly the data structure), since that's what seems to be eating the cpu in the linked issue. |
This is a fundamental flaw in our design and we can’t solve it nicely without introducing some form of scoring. |
Involving @jhiesey and @anacrolix |
Alternatively adopting sloppy hashing would alleviate the burden on strictly near peer ID. |
So one thing we could do to immediately reduce the stress, is to bin the provider record by (next) hour of expiry. edit: referring to gc efficiency, which seems to be the limiting factor as it is a linear check of all provider records. |
Nodes should throttle provider records, and providers should expand their radius upon getting throttled. That way, content is sloppily radiated across the network the more popular it gets, and it’s also findable more rapidly as you don’t need a perfect match any longer. |
This approach is similar to how topics will be QoSsed in discv5 in Ethereum. See this proposal: libp2p/specs#145 |
Actually with binning we wouldn't even have to iterate on expiry, we could simply throw away the next bin on every tick (which is hourly). |
@vyzo for GC, have a look at the 2-cycle algorithm we implemented in the datastore-backed peerstore. It has lookahead and prune cycles, and it operates similarly to what you propose. How do you deal with record renewals with binning, where the record hasn’t yet been pruned yet and still lives in a bin? Looking it up becomes O(n) again? |
Would bins contain references or the physical record? I guess the former, in which case the above could be a problem. Can’t be the latter because access would be O(n). |
I think it is fine if we have a provider appear in multiple bins, but we'll have to deduplicate on the response. |
nevermind, I think the deduplication alone would turn the response into O(n) which is unacceptable. |
So if the provider record size is the issue, I am not sure that dropping new providers is the right solution. |
The provider mechanism in our DHT is inspired by Coral, but it’s a half-baked, naïve implementation. Let’s implement it all the way with sloppy hashing (backwards compatible) and record rejection and backtracking. Although backtracking is not Byzantine resistant IIRC, we can make it so (keep going forward a few steps, disjoint paths, etc.) |
Opened a discussion in libp2p/specs to get this implemented: libp2p/specs#163 |
When I say "space isn't an issue" I mean that that's not what's crashing the node. It's still an issue and there's no way we can meaningfully use more than say 100 provider records. I can't share the dumps but I can share the results. CPU Profile:
CPU Profile Code Listing:
You'll notice the 70% CPU time... |
So basically, we need a quick fix, then the right fix. That's why I suggested just dropping provider records as they aren't useful in bulk anyways. |
Just to confirm the issue, I understand from reading the code that the hourly cleanup deserializes every provider record entirely into memory from the provider data store, checks all the timeouts and inserts any changes back into the store? It does seem strange that timeouts feature so prominently everywhere. Ultimately with enough traffic/churn, something has to give. I think timeouts help remove old/quiet data, but aren't sufficient. The mapping looks like @Stebalien do you have numbers for this provider set? How many entries for example? |
Unfortunately, not. I just know that we're spending a lot of time iterating over that map. In terms of sqlite, we've considered this in go-ipfs as well because using a kvstore for everything is kind of painful. Unfortunately, sqlite itself requires an external library (can we statically link) and CFFI calls (slow in go). However, we should still look into this and, maybe, alternative databases.
A proper kv iterator would actually be pretty easy. I believe we're doing it this way due to how we're caching provider records in memory. |
I don't know how big the impact is but it looks like the cleanup code is only deleting expired provider records from the datastore when all providers are removed for a key. If only some of the providers are removed the in-memory/cached provider set is updated but the expired entries aren't removed from the database. It seems like this could at least be exacerbating the current issue. |
Ah. So if the cache is full, we'll repeatedly restore this set from disk. That's bad. |
part of fixing: libp2p/go-libp2p-kad-dht#316 (comment)
Hm. So... it looks like our current GC algorithm is n^2. Let's fix that. |
complains
|
@whyrusleeping which version of what? Assuming go-ipfs 0.4.20, try running |
We still need to limit the size of this set, but this issue was really about a bug where we weren't garbage collecting it properly. |
Currently, provider sets can grow quite large. Unfortunately, if a peer ID happens to lie close to some very popular content, that node will end up storing a ton of provider records for that content. This usually isn't much of an issue in terms of storage space but is definitely an issue in terms of performance.
See ipfs/kubo#5613 (comment).
cc @markg85
The text was updated successfully, but these errors were encountered: