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

Performance enhancements for the rules matchers. #3563

Merged
merged 5 commits into from
Jul 9, 2018

Conversation

ShaneSaww
Copy link
Contributor

@ShaneSaww ShaneSaww commented Jul 5, 2018

This commit works towards calling types.CannonicalDomain less and reducing the amount of calls that are in the hotpath for a request.

The big change here is moving the cannon'd request Host header into a middleware so it only has be cannonized once per request.

What does this PR do?

This PR creates a middleware that gets hit early on in the request lifecycle and puts then CanonicalDomain request host into a context to be read during the route Matcher funcs.

Along with that this re-works the matcherFuncs to call CanonicalDomain less by using the context, and calling CanonicalDomain for the hosts at the initialization of the matcherFunc not inside the matcher func where it's expensive.

Motivation

Performance tuning in our production environment showed that CanonicalDomain was using a good chunk of CPU time.

See the include flameGraph photos of the matcherFunc section.

Before:
matcherfuncbefore

After:
matcherfuncafter

More

  • Added/updated tests
  • Added/updated documentation

Additional Notes

Copy link
Member

@mmatur mmatur 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 your contribution @ShaneSaww.
First review


var reqHostKey struct{}

// ReqHostMiddleware is the struct for the middleware that adds the CanonicalDomain of the requst Host into a context for later use.
Copy link
Member

Choose a reason for hiding this comment

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

Could you please replace requst by request

// ReqHostMiddleware is the struct for the middleware that adds the CanonicalDomain of the requst Host into a context for later use.
type ReqHostMiddleware struct{}

// NewReqHostMiddleware is a middleware that adds the CanonicalDomain of the requst Host into a context for later use.
Copy link
Member

Choose a reason for hiding this comment

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

Could you please replace requst by request

}
}

// GetCannonHost plucks the cannonized host key from the requst of a context that was put through the middleware
Copy link
Member

Choose a reason for hiding this comment

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

Could you please replace requst by request and cannonized by canonized

}

// GetCannonHost plucks the cannonized host key from the requst of a context that was put through the middleware
func GetCannonHost(ctx context.Context) (string, bool) {
Copy link
Member

Choose a reason for hiding this comment

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

Should maybe rename into GetCanonHost

var reqHostKey struct{}

// ReqHostMiddleware is the struct for the middleware that adds the CanonicalDomain of the requst Host into a context for later use.
type ReqHostMiddleware struct{}
Copy link
Member

Choose a reason for hiding this comment

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

Could you rename ReqHostMiddleware by ReqHost please

type ReqHostMiddleware struct{}

// NewReqHostMiddleware is a middleware that adds the CanonicalDomain of the requst Host into a context for later use.
func NewReqHostMiddleware() *ReqHostMiddleware {
Copy link
Member

Choose a reason for hiding this comment

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

This function is not necessary, you could directly use &ReqHostMiddleware{} instead of creating a function for that. Like it is done for Compress or AddPrefix middlewares

return &ReqHostMiddleware{}
}

func (rhm *ReqHostMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
Copy link
Member

Choose a reason for hiding this comment

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

You could maybe rename rhm with rh

}

func (rhm *ReqHostMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
reqHost := r.Host
Copy link
Member

Choose a reason for hiding this comment

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

You could maybe simplify like that

func (rh *ReqHostMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	reqHost := types.CanonicalDomain(parseHost(r.Host))
	if next != nil {
		next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), reqHostKey, reqHost)))
	}
}

func parseHost(addr string) string {
	if !strings.Contains(addr, ":") {
		return addr
	}

	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return addr
	}
	return host
}

WDYT?

And you should be able to test parseHost function

@mmatur mmatur changed the title performance enhancements for the rules matchers. Performance enhancements for the rules matchers. Jul 6, 2018
@mmatur
Copy link
Member

mmatur commented Jul 6, 2018

The code freeze happen today, so I will do the change to be able to merge your PR today before the code freeze

Copy link
Member

@mmatur mmatur left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@juliens juliens left a comment

Choose a reason for hiding this comment

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

LGTM

@ldez ldez force-pushed the upsmaster branch 2 times, most recently from 2a2c370 to 3b19e92 Compare July 6, 2018 11:53
Copy link
Contributor

@ldez ldez left a comment

Choose a reason for hiding this comment

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

I think there is another way to optimize.
I will do tests

Copy link
Contributor

@ldez ldez left a comment

Choose a reason for hiding this comment

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

I'm not really happy to add a middleware that call CanonicalDomain for all requests.

But for now LGTM

ssmith-sahnow added 2 commits July 9, 2018 12:50
This commit works towards calling types.CannonicalDomain less and reducing the amount of calls that are in the hotpath for a request.
The big change here is moving the cannon'd request Host header into a middleware so it only has be cannonized once per request.
@traefiker traefiker merged commit bf73127 into traefik:master Jul 9, 2018
@ShaneSaww ShaneSaww deleted the upsmaster branch July 10, 2018 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants