-
Notifications
You must be signed in to change notification settings - Fork 283
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
Move service handler creation after FX creates dispatcher #312
Conversation
@alsamylkin, thanks for your PR! By analyzing the history of the files in this pull request, we identified @shawnburke, @sectioneight and @dmcaulay to be potential reviewers. |
This approach appears to be too limiting.
|
What exactly does CreateThriftServiceFunc provide? Why not just allow the user to construct their own handler in main using dig? |
This approach doesn't limit the existing functionality, it moves the dispatcher from DIG to the handler constructor, so users don't have to resolve it. Before we asked a user to create transport.Procedures and give them to us to attach the dispatcher,
|
Another thing: right now handler constructor(CreateThriftServiceFunc) is called before dispatcher is even created, which makes common client initialization impossible. E.g. this is what users need to do now: func NewYarpcThriftHandler(_ service.Host) error {
var d *yarpc.Dispatcher
if err := dig.Resolve(&d); err != nil {
return err
}
handler := &YarpcHandler{
items: map[string]string{},
client : keyvalueclient.New(d),
}
return kvs.New(handler)
} with this change, it will be something like: func NewYarpcThriftHandler(_ service.Host, d *yarpc.Dispatcher) error {
handler := &YarpcHandler{
items: map[string]string{},
client : keyvalueclient.New(d),
}
dispatcher.Register(kvs.New(handler))
return nil
} |
Actually it can be little better: func NewYarpcThriftHandler(_ service.Host, d *yarpc.Dispatcher) ([]transport.Procedure, error) {
handler := &YarpcHandler{
items: map[string]string{},
client: keyvalueclient.New(d),
}
return kvs.New(handler), nil
} |
So how would I share the client between 2 handlers? What if I have a separate shape in my application, not a handler, that also needs the same client? It seems to me like there needs to be a way to configure and register clients in DIG, so that all shapes, including handlers, can simply declare their dependencies on said client. |
I am not sure, if they should be easily shared, but may be it can be autogenerated and cached? func Get() keyvalueclient.Interface {
if cached {
return client
}
var d *yarpc.Dispatcher
if err := dig.Resolve(&d); err != nil {
return nil
}
cached = true
client = keyvalueclient.New(d)
return client
} Although, some people would argue, that this is an extra function call and DIG doesn't yet support multiple objects of the same type.. |
The problem we are talking about is the problem dig is supposed to solve. Users need to be able to register shapes with dig, either programmatically or using config, and then trust dig to build the graph correctly. I don't think this part should behave differently - register client into graph, register handlers into graph, every object declares its deps which dig resolves. Using dig will provide a single way of solving this for all usecases, not just this client+handler one. |
If possible, I'd like to get @glibsm in here for a proposal: The proposal is that all deps/shapes in a service (the application graph), including clients and handlers, get registered into a dig so that the full graph can be resolved. This has the effect of cleaning a few things up:
I worry that by allowing the graph or dispatcher to be used, users will actually be shooting themselves in the foot. We should be making it easy to do the right thing, instead of handing them a loaded gun (excuse the analogy). Crazy? |
I do like where we are going with it and I think it is time to set up a meeting in person and discuss everything offline. |
I think I like this... Are you proposing that we keep the graph private and only register/resolve internally? I'm thinking about doing this in Glue because I don't want it to be too easy to breakdown the layers of abstraction that we're trying to build. I was even thinking about having different graphs for each layer. Still brainstorming though... |
All layers would still need access to the fx graph though. Not quite sure how that would work. |
DIG and Glue are solving this problem very differently. Problem- Too much boilerplate needed to wire up your service. Glue- If you follow these patterns and each layer only depends on x, y, z, then we'll generate some simple code to wire it up for you. DIG- Register all the things and depend on whatever you want (no cycles) and we'll wire it up for you. This is really cool, but it makes me nervous. I'm obviously biased 😃 |
1 similar comment
Ok, I wont block this PR anymore. After talking with the UberFX team, @glibsm and I are going to design an alternate API that takes a DI-first approach. That design and prototype will inform what changes will have to be made to the existing experience. For now, I count this as an improvement to the existing experience, in that now clients can be instantiated slightly before the hot path. It doesn't solve the DI problem in a generic way, but that is ok and will be solved by the plan @glibsm and I coming up with. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm once Travis is happy.
002778a
to
9adaa9e
Compare
Because we create service before the yarpc.Dispatcher it is hard for clients to get it in the constructor, they have to get it during request serving to e.g. create client.
With this change services will be able to resolve Dispatcher at construction time.