-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Use block decoding framework #4040
Conversation
Change IPFS to use the new pluggable Block to IPLD decoding framework. License: MIT Signed-off-by: Steven Allen <[email protected]>
Yeah, I'm actually in favor of having to call a sort of 'Register' function on the import to do the setup. That makes things more explicit, and potentially less error prone (I'm leaning towards this for the plugin stuff in #4033 ) |
@Stebalien could you make codeclimate happy? |
License: MIT Signed-off-by: Steven Allen <[email protected]>
Lovely... rust has the exact opposite convention: don't include the function's name in the documentation because it's obvious... |
Yeah, its done that way to force you to phrase your documentation consistently. Ends up looking and reading nicely in godoc: https://golang.org/pkg/net/http/#Serve |
Consistency is good, I'm just lamenting at the 14+1 conflicting standards.
As pointed out in that PR, you still need a global. This means you either need to synchronize access to the global or be very careful to call register on startup only. The nice thing about the current implicit system is that all registrations are made in Possible solutions:
|
I think as long as we are clear that the user must do all registration in the global defaultblockdecoder i think its fine to keep it as is. All the other solutions to 'protect the user' feel pretty messy |
There is also use case of plugins: #4033 Currently they are just hacking their way into the With Go 1.9 we will get concurrent maps which will replace the RWMutex in this place. |
I would also be for not making that global functions but a structure with functions. So that during testing or in future we can use it as non-global. |
So, protect the user or not? You and @whyrusleeping appear to be in disagreement.
Hm. The problem there is that you'd need to feed this structure to everything that needs to be able to decode blocks. The current way to test this is to edit the global My current use-case for this is floodsub; I need to be able to decode IPLD nodes. Requiring an IPLD block decoder in the constructor is a bit messy. It also means that different parts of the codebase can end up with different block decoders and blocks understood by some parts may not be understood by all parts. If we do decide to "protect the user", I plan on changing the type BlockDecoder interface {
Register(codec uint64, decoder DecodeBlockFunc)
Decode(blocks.Block) (Node, error)
}
type safeBlockDecoder struct {
lock sync.RWMutex // Can be replaced with atomic pointer magic as an optimization later.
decoders map[uint64]DecodeBlockFunc
}
func (d *safeBlockDecoder) Register(codec uint64, decoder DecodeBlockFunc) {
defer lock.Lock().Unlock()
d.decoders[codec] = decoder
}
func (d *safeBlockDecoder) Decode(block blocks.Block) (nodes.Node, error) {
// Short-circuit by cast if we already have a Node.
if node, ok := block.(Node); ok {
return node, nil
}
ty := block.Cid().Type()
locker := d.lock.RLock()
decoder, ok := b[ty];
locker.Unlock()
if ok {
return decoder(block)
} else {
// TODO: get the *long* name for this format
return nil, fmt.Errorf("unrecognized object type: %d", ty)
}
}
var DefaultBlockDecoder BlockDecoder = &safeBlockDecoder{ decoders: make(map[uint64]DecodeBlockFunc }
// + global functions that call into DefaultBlockDecoder. |
Plugins break the premise that everything is initialized in
For now I would be OK with everything using it as global, there are just places where having an instance makes more sense. |
So I'll go with the code I posted above for now.
For our use-case, I think an RCU using an atomic pointer would be faster than anything in the standard library given our use-case (unless go's concurrent maps do some really fancy static-analysis). The downside is that doing fancy things with atomic pointers can lead to fancy bugs. |
It is always that trade off. (I have version of bblom code that uses atomic ops and CAS, it is faster but current overhead is not high enough to risk possible bugs in it). |
I'm really leaning towards not protecting it, even with plugins things should register in serial and all up front. If you feel strongly that we should protect it @Kubuxu then lets do it, just make it clean. |
There is a risk in not protecting it (single miss behaving piece of code can cause application panic), so I would say, let's protect it. If the cost becomes to high, we can decide to change it to RCU (which should have very low cost). |
Lets just make sure to implement it in a way that doesnt block us from doing block decodes in parallel |
Superseded by #4060 |
Change IPFS to use the new pluggable Block to IPLD decoding framework.
Later, I'll pull the other node formats out of IPFS (at least the raw one).
Concern: As-is, it's non-obvious that importing go-ipld-cbor automagically makes
node.Decode
decode CBOR nodes. Worse, it's non-obvious where we should beimporting this package (it really only needs to be imported once).