-
Notifications
You must be signed in to change notification settings - Fork 1k
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(kad): Implement ability to acknowledge AddProvider
before reading further messages from substream
#3468
feat(kad): Implement ability to acknowledge AddProvider
before reading further messages from substream
#3468
Conversation
… messages from substream
AddProvider
before reading further messages from substreamAddProvider
before reading further messages from substream
Thank you for submitting this. I feel uneasy about introducing yet another pattern. The solution is novel but I don't think we have an RAII guards so far? @mxinden What are your thoughts on this vs a The latter would also have the benefit of greatly simplifying It comes at the expense of removing @melekes How do you use @divagant-martian Do you use Further reading: #3411 |
I believe all of the streams have RAII guards as discussed on Rust maintainers call recently, so it is not entirely new to the codebase. Though I can certainly refactor it to use something else if that is desirable.
I'm not sure how it'll help, we still want to be able to read and write multiple things to/from record store, being behind mutex means it'll not allow for concurrency. In this case it should be just Though this is a departure from current design where boxed values are rare. Also while at it I think Alternatively I recall there was a thought somewhere to remove |
I am not sure if the comparison is 100% on point. Here the "guard" is a token that is passed along but otherwise not in use whereas a stream will free resources in
If your DB is faster than the network, the mutex doesn't harm. If the DB is slower than the network, we will backpressure to the remote. Isn't that what we want?
Minor correct: Everything regarding streams uses boxing and
That is another idea yes. That is what I am currently prototyping in mxinden/asynchronous-codec#5. I hadn't considered exposing the |
e9340b9
to
1f73380
Compare
Found some issues with older revision, replaced with a bit better one.
The issue here is that all accesses to record store will be sequential. While SSDs are optimized to do parallelized work. Not to say that "just DB" is an oversimplification in our protocol, we are also doing other things that can take seconds and we certainly don't want to block any access to record store for the duration of it. SSDs are optimized for concurrent access, it would be a shame to not take advantage of it. |
Are you saying that some Wouldn't it be better to limit the scope of |
Absolutely! RocksDB, ParityDB and probably most other databases use I could spawn tasks, but the API would have to be async anyway because creating unbounded number of tasks is not an option. But either way single read or write to the database is a no-go for our use case. |
I am not suggesting you should do all your networking etc in the Can you expand why that is not possible in your situation? Without knowing all requirements of your usecase, my first idea for a system that needs to be optimised for throughput would be to have mutations of the Other work like network requests can be asynchronously propagated from this event-log. |
we're just reading / writing records
probably not so much |
Record store in my opinion must be generic and multi-threaded. It can be in-memory, can be disk-based database, can be remote database with considerable latency, some RPC API, anything. Serializing access to record store is going to make any small delay multiplied by a big factor and can eventually result in DoS. It will work great for a narrow use-case of in-memory Even if there is mutex, it needs to be an implementation detail of a specific record store, not a wrapper on top of record store. In our protocol we're talking about thousands requests per minute, in some cases tens of thousands of requests for records/providers. There is no way we can handle them in sequential order and no way they fit into memory either. |
Right, if the records don't fit in memory, it is indeed hard to design an API that is directly called from within the I am currently travelling so don't have much capacity for coding. What do you think of mxinden/asynchronous-codec#5? Would you be interested in prototyping its usage in |
Fixed compatibility with #3474, works for me now. I'll take a look at mxinden/asynchronous-codec#5 likely some time tomorrow, thanks! |
This pull request has merge conflicts. Could you please resolve them @nazar-pc? 🙏 |
Closed because stale. |
Description
The goal is to make it possible for
AddProvider
to be processed asynchronously. It is a special case in Kademlia because there is no response expected.While it works fine with in-memory storage, disk-based and other kinds of storage present challenges here and it might be desirable to do it asynchronously, but this is a challenge because Kademlia implementation before this PR will simply move on and will accept ever growing announcements, which may result in DoS.
The approach here is to process
AddProvider
with events (KademliaStoreInserts::FilterBoth
) and keep introduced event guard alive for corresponding event as long as necessary. As long as guard is not dropped, corresponding incoming stream will be in pending state and will not receive new incoming messages from sender. Once guard is dropped incoming stream will continue working normally.Notes
This could have been implemented with an additional message passing, but I find it more cumbersome to implement and to use, thus prefer RAII whenever possible for these things.
Opening as draft for now since due to lack of stream reuse this resurrects following error in my environment:
It was partially mitigated in #3287, but never resolved fully and we acknowledged back then that there is a small chance it might still happen. Here chance is 100% under right conditions because incoming streams will be in pending state indefinitely, while sender will assume stream is closed already because they did it already.
This is fixed by stream reuse implemented in #3474.
Note that we don't HAVE TO expose
Arc<InboundStreamEventGuard>
publicly, we could make it anonymous withArc<dyn Any + Send + Sync + 'static>
, but it looked not as nice to me, so I decided to leave it exposed.Links to any relevant issues
Primarily #3411
Open Questions
Change checklist