Skip to content
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

Add option to use packed encoding in rpc.StreamTransport. #161

Merged
merged 4 commits into from
Mar 3, 2021

Conversation

lthibault
Copy link
Collaborator

This PR allows users to specify the encoding (packed vs unpacked) used by rpc.StreamTransport.

It achieves this by adding the rpc.StreamTransportOption type to parametrize rpc.StreamTransport, and the rpc.WithCodec option. Codec is a factory type that can encode/decode messages to/from bytes.

In the future, we should perhaps look into reusing capnp.Encoder and capnp.Decoder, for example with a sync.Pool manipulated by codec types.

I have not tested this beyond ensuring the current unit tests continue to pass.

⏱️ Estimated review time: 10 mins.

@lthibault lthibault self-assigned this Feb 13, 2021
Copy link
Contributor

@zombiezen zombiezen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the short, focused PR. This helps a lot. 👍

Sending a packed stream over the network does make sense to me as a feature, but can you help me understand why we want to give the user a general mechanism for overriding how messages get read and written? Particularly, could we give another constructor that has packing behavior instead?

My experience from working on RPC packages is that the more callback hooks you give the user, the trickier it is to reason about the locking, because you have to be extraordinarily careful to release the lock while the user code is running or document precisely what the user is allowed to do in the callback. Otherwise, the user can have hard-to-reason-about deadlocks or inconsistent behavior.

rpc/rpc.go Outdated Show resolved Hide resolved
rpc/transport.go Outdated Show resolved Hide resolved
@lthibault
Copy link
Collaborator Author

lthibault commented Feb 13, 2021

Thanks for the short, focused PR. This helps a lot. 👍

My pleasure -- happy it helps!

Sending a packed stream over the network does make sense to me as a feature, but can you help me understand why we want to give the user a general mechanism for overriding how messages get read and written? Particularly, could we give another constructor that has packing behavior instead?

I have no argument against providing NewPackedStreamTransport. The decisions here were mostly made for the sake of expediency and fast feedback.

In the back of my mind, I was thinking about a future PR in which we could pool encoders and decoders, and my thinking was that the get/put logic could be contained in Codec.

general mechanism for overriding how messages get read and written

Just to make sure we're on the same page, is this referring to the use of options, or the use of a Codec interface? I'm not sure I understand how this qualifies as a general mechanism since option types work by manipulating private fields (i.e. no surface for uninformed users to misconfigure) and Codec doesn't provide access to anything that needs synchronization.

Again, I'm totally happy to just provide a new constructor -- I just want to sync our mental models in order to make better-informed design decisions in the future 🙂

My experience from working on RPC packages is that the more callback hooks you give the user, the trickier it is to reason about the locking, because you have to be extraordinarily careful to release the lock while the user code is running or document precisely what the user is allowed to do in the callback. Otherwise, the user can have hard-to-reason-about deadlocks or inconsistent behavior.

This makes sense in general, but as mentioned above, it seems like the present changes don't touch anything that needs locking? I'm guessing you're concerned about what kinds of future options might get added?

In any case, thanks for the quick feedback. I'm going to make the requested changes and push again in a few minutes.

Copy link
Contributor

@zenhack zenhack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a specific comment re: the Codec interface in-line. I might suggest instead doing something like:

interface Codec {
    Encode(m *capnp.Message) error
    Decode() (*capnp.Message, error)
    Close() error
}

type codec struct {
    *Encoder
    *Decoder
    io.Closer
}

func NewCodecTransport(codec Codec) Transport { /* ... */ }

func NewPackedStreamTransport(rwc io.ReadWriteCloser) Transport {
    return NewCodecTransport(codec{
        Encoder: NewPackedEncoder(rwc),
        Decoder: NewPackedDecoder(rwc),
        Closer: rwc,
    })
}

func NewStreamTransport(rwc io.ReadWriteCloser) Transport {
    // Similar to NewPackedStreamTransport
}

(with possible name bikeshedding)

rpc/codec.go Outdated Show resolved Hide resolved
@lthibault lthibault changed the title Add option to used packed encoding in rpc.StreamTransport. Add option to use packed encoding in rpc.StreamTransport. Feb 13, 2021
@zenhack
Copy link
Contributor

zenhack commented Feb 15, 2021 via email

@zombiezen
Copy link
Contributor

Replying to @lthibault:

Again, I'm totally happy to just provide a new constructor -- I just want to sync our mental models in order to make better-informed design decisions in the future 🙂

I'm 100% on the same page: I'm intentionally being a little chattier than I normally am on code review so that I can share mindset.

This makes sense in general, but as mentioned above, it seems like the present changes don't touch anything that needs locking? I'm guessing you're concerned about what kinds of future options might get added?

Ah right, I guess we already release the lock before I/O operations for exactly this reason.


BTW, we might want to switch the CI from Travis to GitHub Actions. The Bazel builds should probably be dropped, and the Go version needs to be updated.

@lthibault
Copy link
Collaborator Author

BTW, we might want to switch the CI from Travis to GitHub Actions. The Bazel builds should probably be dropped, and the Go version needs to be updated.

Glad to hear you say it -- I was wondering how to diplomatically bring this up! 😆

lthibault added a commit that referenced this pull request Feb 21, 2021
Eschew functional options in favor of new constructor functions.

See:  #161
@lthibault lthibault requested a review from zombiezen February 21, 2021 23:35
@lthibault
Copy link
Collaborator Author

@zombiezen @zenhack

I think I've found an approach that satisfies Ian's remarks.

Test for the rpc package pass. I've added a new test for the packed encoding.

lthibault added a commit that referenced this pull request Feb 21, 2021
Eschew functional options in favor of new constructor functions.

See:  #161
Eschew functional options in favor of new constructor functions.

See:  #161
Improves legibility by removing ambiguity about what the mutex is
protecting, and rendering explicit the error lookup/setting semantics.
@lthibault
Copy link
Collaborator Author

@zenhack @zombiezen You guys good to merge this?

@zenhack
Copy link
Contributor

zenhack commented Mar 3, 2021

Works for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants