-
Notifications
You must be signed in to change notification settings - Fork 36
Delete race in ARC cache #64
Comments
Note: this is not an issue for the bloom cache because we only add "might haves" to the cache (never remove from it unless we rebuild). |
Notes:
Unfortunately locking the entire blockstore while we put/get will significantly impact performance. We have two options:
I'd implement both options and benchmark them:
(recording allocation information at the same time https://golang.org/pkg/testing/#B.ReportAllocs) |
I've filed a PR to add benchmarking to the arc_cache here: #65. It serves as the base for the following drafts:
I'll share benchmarks in the description of each PR |
@warpfork could you pick this up? Compare the benchmarks, review, etc. |
I have low context on this, so I mostly just come up with questions. I'll share those questions, but if folks with higher context think these are silly, feel free to ignore.
|
I'm happy to answer questions, but it's a potential dataloss issue in our happy path so whether or not we fix it is a forgone conclusion.
We don't know. But it's a dataloss issue and we do ocasonally get complaints about dataloss. On the other hand, most users don't use GC.
The benchmarks are trying to emulate fetching multiple blocks in parallel, which we do. Unfortunately, our parallelism varies depending on the workload. Most users will have 10-32 parallelism, but a gateway will fetch 1000s of nodes at a time.
Our system is pretty safe in that regard. Unfortunately, this bug also affects concurrent reads and GC. For example, a network peer might read a block that's about to be deleted which will force our cache into an incorrect state.
It has been an issue for a while, but:
Yes. The invariant being enforced here is simply: the cache remains consistent with the underlying blockstore. That's it. The wider question of the pinset being valid, etc, is enforced at a higher layer. This higher layer is responsible for calling delete and ensures that no pin operation overlaps with delete operations. |
I wrote a new benchmark in #70, following what has previously been said on this thread. It's on top of master, as I understand that's the latest v1.0 development. While I wait for reviews there, the plan was to get benchmark numbers comparing master to the four proposed fixes. However, I'm struggling because each of those four fix PRs are built on v1.0.0, which a year behind master at this point, which contains v1.0.3. In particular, I keep running into conflicts when I try rebasing the four PRs on master. @frrist is there a particular reason you built your fixes on an older v1.0 version? I'll try to backport the benchmark to v1.0.0 and use that as a baseline for benchmark comparison, but I assume that's not helpful. We want the benchmark numbers for master, since presumably that would become another release like v1.0.4. |
Here are the benchmark results. This is all on v1.0.0, and with the benchmark from #70 running with 4k blocks and 1, 64, and 1028 concurrent goroutines.
|
As a summary: #66 is a hit of between 10% and 30% in perf, especially with more goroutines. I think it should be discarded, as it's the one that performs the worst. #67 is fairly low overhead, with a modest hit of only 12-14% with many goroutines. #68 is fairly similar to #66, being better in some cases and worse in others. Overall the performance isn't great either. #69 has decent performance, but still a bit worse than #67, topping at 18-20% overhead with many goroutines. So I think #67, striped locking, is the winner in terms of overhead, especially with lots of concurrency. This aligns with what @Stebalien said earlier:
|
@mvdan I based my branches on
I'm sorry that I misunderstood this (I assumed 1.0 meant v1.0.0). I can rebase the branches onto |
I think that would be helpful :) If you can rebase on master without including your benchmark that would be best, because then it's trivial to rebase that on top of either benchmark to get numbers. I still think the numbers above are useful, though. If we think they might drastically change on master, I can get new numbers after the rebases. |
All branches have been rebased onto master. |
Thanks! Sorry, by "1.0" I meant "v1" versus "v0" (we maintain two versions). |
Note: We haven't backported to the v0 branch, but this shouldn't be necessary anymore as we've picked up the blockstore migration project again. |
I believe ipfs/kubo#6815 captures the "blockstore migration project". |
Steps:
After step 1, the block may or may not be in the blockstore depending on how the operations serialize. However, after step 2, the block must be in the datastore.
Unfortunately, there's a race:
There's also a similar race between get & delete and get & put.
Fix: add striped (by CID) locking.
Impact:
The text was updated successfully, but these errors were encountered: