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

Move service handler creation after FX creates dispatcher #312

Merged
6 commits merged into from
Feb 24, 2017
Merged

Conversation

ghost
Copy link

@ghost ghost commented Feb 23, 2017

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.

@mention-bot
Copy link

@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.

@ghost ghost requested review from HelloGrayson and dmcaulay February 23, 2017 02:50
@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 92.199% when pulling 3cab60b on exposeDispatcher into 8622d3c on master.

@HelloGrayson
Copy link
Contributor

This approach appears to be too limiting.

  • Users need to be able to configure clients, and then inject them across multiple handlers. Injecting the dispatcher at this way will lead to instantiating the same clients over again for many handlers.
  • Clients should ideally all be configured in the same place
  • Beyond clients, there needs to be a way to inject other shared deps into handlers

@HelloGrayson
Copy link
Contributor

What exactly does CreateThriftServiceFunc provide? Why not just allow the user to construct their own handler in main using dig?

@ghost
Copy link
Author

ghost commented Feb 23, 2017

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,
but

  1. They may need a dispatcher to create clients
  2. We don't do anything special about it now, just call dispatcher.Register()

@ghost
Copy link
Author

ghost commented Feb 23, 2017

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
}

@ghost
Copy link
Author

ghost commented Feb 23, 2017

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
}

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 92.209% when pulling c6202a4 on exposeDispatcher into 8622d3c on master.

@HelloGrayson
Copy link
Contributor

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.

@ghost
Copy link
Author

ghost commented Feb 23, 2017

I am not sure, if they should be easily shared, but may be it can be autogenerated and cached?
Something like:

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..

@HelloGrayson
Copy link
Contributor

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.

@HelloGrayson
Copy link
Contributor

HelloGrayson commented Feb 23, 2017

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:

  • No need to inject dig's graph anywhere (this is really important)
  • No need to inject the dispatcher anywhere (equally important)
  • Users simply register shapes and declare deps, letting dig resolve

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?

@ghost
Copy link
Author

ghost commented Feb 23, 2017

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.

@dmcaulay
Copy link
Contributor

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...

@dmcaulay
Copy link
Contributor

All layers would still need access to the fx graph though. Not quite sure how that would work.

@dmcaulay
Copy link
Contributor

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 😃

@ghost ghost changed the title Expose dispatcher in CreateThriftServiceFunc Move service handler creation after FX creates dispatcher Feb 23, 2017
@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 92.146% when pulling 002778a on exposeDispatcher into 8622d3c on master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 92.146% when pulling 002778a on exposeDispatcher into 8622d3c on master.

@HelloGrayson
Copy link
Contributor

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.

Copy link
Contributor

@HelloGrayson HelloGrayson left a 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.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 92.296% when pulling 9adaa9e on exposeDispatcher into 62a9f89 on master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 92.296% when pulling 9adaa9e on exposeDispatcher into 62a9f89 on master.

@ghost ghost merged commit bebde66 into master Feb 24, 2017
@ghost ghost deleted the exposeDispatcher branch February 24, 2017 00:41
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants