-
Notifications
You must be signed in to change notification settings - Fork 1
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
ipfs blockservice is incompatible with sessions #34
Comments
What if it returns a blocking Exchange and blocking Blockstore when those methods are called ? |
This would be incompatible with this new boxo patch: ipfs/boxo@22fa8b1#diff-c3167a605aeb6a2f9430f5730ee6508f88e15022a746998ff6ec8ba013d0d6dc The problem is that in the wrapping code I use the incoming blockservice as context key: So
block, err := blockstore.Get(ctx, c)
switch {
case err == nil:
return block, nil
case ipld.IsNotFound(err):
break
default:
return nil, err
} Which would allows us to do filtering only in the blockstore and have it also filter the exchange thx to: default:
return nil, err I'll fix that in Other solutions exists but I think it's the least bad one. |
That sounds like relying on an implementation detail that causes exchange-blocking by chance (only because blockstore is checked first?) so I'm not very sold... better to wrap both. I think conceptually BlockService is the right layer... it feels weird that we have to work around the fact that implementation for a BlockService session bypasses the layer and uses underlying exchange/blockstore directly. Can't we wrap sessions with blocking code directly by making |
Fair.
That would work but that require breaking the interface and I don't know if anyone else has custom blockservice implementation. Else we could have something like: func WithContentFilter(b func(cid.Cid) error) Option {/* ... */} In This also means nops only needs to provide 1 small function instead of many plumbing methods. |
The goal is to help with ipfs-shipyard/nopfs#34.
The goal is to help with ipfs-shipyard/nopfs#34.
What about a new |
I think #35 is cleaner. func ToBlockServiceBlocker(blocker *nopfs.Blocker) blockservice.Blocker {
return func(c cid.Cid) error {
err := blocker.IsCidBlocked(c).ToError()
if err != nil {
logger.Warnf("blocked blocks for blockservice: (%s) %s", c, err)
}
return err
}
} While not adding as much complexity in
I don't get how this would work. |
It is, but relies on the fact that a specific blockservice implementation exists that provides content-blocking capabilities.
Session should be an interface (or a blockservice itself) and we wrap it... similar to Sorry for resisting here a bit, just aiming to keep the conceptual layer model based on interfaces so that nopfs stays as purely plug-in (I think is nice that things can be like this). If we have a content-blocking BlockService, we should also think of doing the same in Namesys and Resolver and removing wrapping for general consistency. |
@Jorropo how much work would it be to make session an interface? I did look at (ipfs/boxo#556) and it seems to do what it says it does, but I'd like us to have a consensus here on what to do next.
Maybe breaking the interface isn't the worst idea if the outcome is better and cleaner. We can always provide with a changelog that explains clearly what needs to be updated in someone else's blockservices. What do you think @Jorropo @hsanjuan? |
The current situation is pretty bad because it's neither a struct pointer nor an interface, it's both. Imo we should remove the blockservice interface and put all these kinds of ad-hoc things in I'm also fine making newsession a method of blockservice and make session an interface. We just need to decide which one of the two way forward. |
I need a more concrete example to understand what you mean by that 😅 |
I'll submit both PR then we can choose, aint much work. |
While implementing the no interface solution I found yet an other bug. |
…r` option The idea is to have a THE blockservice object, this means we wont ever have an issue where multiple competitive features play poorly with each other due to how nested blockservices could do. Let's say we add multi-exchange based on content routing, do we want to have to handle the mess this could create with nested blockservices ? It implements features for ipfs-shipyard/nopfs#34.
I posted both options there:
They are both significantly bigger than the original workaround I sent in ipfs/boxo#556 |
The goal is to help with ipfs-shipyard/nopfs#34.
…r` option The idea is to have a THE blockservice object, this means we wont ever have an issue where multiple competitive features play poorly with each other due to how nested blockservices could do. Let's say we add multi-exchange based on content routing, do we want to have to handle the mess this could create with nested blockservices ? It implements features for ipfs-shipyard/nopfs#34.
Thanks! please give me a couple of days to look into the proposals. |
In principle I'm ok with #563 in that it is aligned with how we normally do things... (granted this might not be the best way of doing things all the time, but seems straightforward at least). |
It's how we usually do things indeed, however I think it might be too complex for it's own good. The tricky thing is that |
What is the motivation for embedding sessions in the context? I may have missed it but I didn't see what prompted that change in the first place. |
@hsanjuan this is a thing a couple of peoples including me had in mind. ipfs/boxo#94 is the oldest written trace I know about. We are quite bad at passing around sessions everywhere. This constant session reset prevent us to do other optimizations (like setting the initial provider delay on bitswap to 0s) because this generate DHT load at each reset. The main reason we are bad at passing sessions outside of context is that people usually have a blockgetter argument in their However to do that properly you need to do initialization per request, not per application instance, which is fine, it's just way more work to update that in all the data stack of kubo and rainbow. That also mean we can't use Passing the session in the context is cheap, it works, and I don't think it makes the code worst (incentivize to pass everything in the context at the cost of compiletime safety) since the unwrapping only happen on existing blockservice object, so you still need to pass the blockservice which let you analyze and follow the code statically. |
…r` option The idea is to have a THE blockservice object, this means we wont ever have an issue where multiple competitive features play poorly with each other due to how nested blockservices could do. Let's say we add multi-exchange based on content routing, do we want to have to handle the mess this could create with nested blockservices ? It implements features for ipfs-shipyard/nopfs#34.
I understand. Honestly, now that mostly everything is in boxo, it might be worth to streamline session handling one day. Passing things in context is a hack so that we don't pass them as explicit arguments after all... But as you say, it is a cheap approach and solves the issue for a low price, compared to the non-trivial re-wiring otherwise. |
nopfs/ipfs/blockservice.go
Lines 62 to 70 in 5edc09a
See how the code does not apply any blocking on sessions.
Theses are used by
boxo/blockservice.NewSession
when a session is used.This also cause issues with new
ContextWithSession
feature because it's not possible to unwrap a nopfs blockservice and access the real one underneath (so the context value key becomes the nopfs blockservice which isn't what we want because nopfs doesn't do session redirection based on context).We should do blocking on the
blockstore.Blockstore
andexchange.Exchange
API arguments before they are passed to the blockservice.The text was updated successfully, but these errors were encountered: