Skip to content
This repository has been archived by the owner on Sep 26, 2018. It is now read-only.

Add Sidecar MaxConn support without other functionality changes #8

Merged
merged 13 commits into from
May 16, 2017

Conversation

mihaitodor
Copy link

@mihaitodor mihaitodor commented Apr 27, 2017

  • Fix resource leak (missing resp.Body.Close() in Sidecar.fetchState())

  • Add maxconn functionality

    • allow the user to configure MaxConns via the TOML settings file
    • extend TestSidecarMakeBackends to test this new functionality
  • Restructure tests and remove useless repetition (couldn't help myself, sorry)

    • use custom http.Client instances which we can reset to
      http.DefaultClient in the unit tests, because httpmock can't hook
      into (multiple) non-default HTTP clients and we need to have two
      of them (one for the watcher endpoint and one for fetching
      state.json)
    • adjust TestSidecarWatcher to actually test that
      loadSidecarConfig is being invoked when adding new service entries
      to the mock Sidecar watcher response
  • Fix linter suggestions

Update 09.05.2017:

  • Used a pool to keep track of all goroutines that are spawned by the Sidecar provider. The functionality from safe/routine.go allows us to keep track and stop all child goroutines.
  • Remove the "Frontend" field from the Sidecar provider and use the default "Filename" field from the embedded BaseProvider struct.
  • Added MaxConn as an optional field to the frontends defined in the configuration TOML.

TODO in separate PRs:

(cc @bparli, @relistan)

configuration.go Outdated
@@ -334,6 +334,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
defaultSidecar.Endpoint = "http://127.0.0.1:7777"
defaultSidecar.Frontend = "sidecar.toml"
defaultSidecar.RefreshConn = flaeg.Duration(60 * time.Second)
defaultSidecar.MaxConns = 300

Choose a reason for hiding this comment

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

This is a super low default. HAproxy defaults to 2000 for example:

Also, when is set to large values, it is possible that the servers
are not sized to accept such loads, and for this reason it is generally wise
to assign them some reasonable connection limits.

By default, this value is set to 2000.

}
return nil

return provider.loadSidecarConfig(states.ByService())

Choose a reason for hiding this comment

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

As mentioned in #7, I'd just pass states here and loop over them with states.EachService() later. Saves allocation for a biggish map and lots less CPU time.

Copy link
Author

Choose a reason for hiding this comment

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

This might not work out easily, because the Sidecar watch endpoint returns state.ByService(), which is a map[string][]*service.Service.

Given this, you mentioned submitting a PR to Sidecar to add a parameter to this endpoint that allows the consumer to specify whether they want to get the state "ByService" or not. I'll proceed to make the required changes in Sidecar for this to work.

Copy link
Author

Choose a reason for hiding this comment

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

@relistan Changes pushed to address this... Hopefully, it will fit nicely with the Sidecar watcher additions :)

safe.Go(func() { catalog.DecodeStream(resp.Body, provider.callbackLoader) })

//wait on refresh connection timer. If this expires we haven't seen an update in a
//while and should cancel the request, reset the time, and reconnect just in case
<-provider.connTimer.C
provider.connTimer.Reset(time.Duration(provider.RefreshConn))
tr.CancelRequest(req)

//TODO: Deprecated method. Refactor this to use a context.

Choose a reason for hiding this comment

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

👍

}

func TestSidecarFetchState(t *testing.T) {
setup()

Choose a reason for hiding this comment

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

This is working against the grain for goconvey. Have a read here: https://github.com/smartystreets/goconvey/wiki/Execution-order

configMsg, _ := <-configurationChan

// We have no way to shut down the provider when it's running in watch mode,
// so just let the unit test close it at the end

Choose a reason for hiding this comment

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

That's a smell. There are solutions. This is one: https://github.com/relistan/go-director

Copy link
Author

Choose a reason for hiding this comment

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

@relistan I have pushed 4635181 to address this comment. Please let me know if that change fully addresses this code smell.

method = "wrr"
weight = 0
sticky = false
maxConnExtractorFunc = "request.host"

Choose a reason for hiding this comment

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

I'm struggling to see where this is used other than in New and tests

Copy link
Author

Choose a reason for hiding this comment

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

That's just there until we finally agree to have this setting configurable via TOML along with the actual limit. In our case "request.host" makes sense, but other people might want to change it. It's used here and it can be set to "client.ip" or "request.host" or "request.header.<header_name>". I think it's best if we allow users to change it via the config file.

Copy link

Choose a reason for hiding this comment

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

If you guys really think we need this level of control with the max connections per backend (as opposed to one global setting), then I agree with Mihai's implementation. Simplest would be to expose it in the toml config file.

@relistan
Copy link

Some comments inline above. But generally 👍


sidecarHTTPClient = &http.Client{
Timeout: 5 * time.Second,
}
Copy link

Choose a reason for hiding this comment

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

👍 this is much cleaner

@bparli
Copy link

bparli commented Apr 28, 2017

Just one comment from me re: the max connections setting, but I'll defer to you guys on that point. Thanks for doing this @mihaitodor 👍

@mihaitodor mihaitodor force-pushed the add-sidecar-maxconn-support-no-extras branch from 2b7a4cc to 138c9ff Compare May 9, 2017 11:35
@mihaitodor
Copy link
Author

@relistan @bparli I have managed to add the MaxConn field as optional on the frontends defined in the TOML via commit c06c529. Please note that it will default to Amount: 2000 / ExtractorFunc: "request.host" if no MaxConn field is defined for a specific frontend in the TOML file.

This approach was discussed here and I assumed silent approval :)

- restructure tests and remove useless repetition
- use custom http.Client instances which we can reset to
http.DefaultClient in the unit tests, because httpmock can't hook
into (multiple) non-default HTTP clients and we need to have two
of them (one for the watcher endpoint and one for fetching
state.json)
- adjust TestSidecarWatcher to actually test that
loadSidecarConfig is being invoked when adding new service entries
to the mock Sidecar watcher response
- Allow the user to configure MaxConns via the TOML settings file
- Extend TestSidecarMakeBackends to test this new functionality
I decided to not add a dependency on go-director, since Traefik
already has similar functionality builtin (safe/routine.go).

Discussion:
#7 (comment)
#8 (comment)
Remove the "Frontend" field from the Sidecar provider and use the
default "Filename" field from the embedded BaseProvider struct.

Note: this will require updating the configuration files in erector.

Discussion: https://github.com/Nitro/traefik/pull/7/files#r113908566
decodeStream should terminate when the input stream is closed.

Discussion: #8 (comment)
- This commit enables adding a MaxConn field on the frontends defined
for Sidecar via the TOML specified in the Filename field.

- Design discussed here: #7 (comment)
and here: #8 (comment)
@mihaitodor mihaitodor force-pushed the add-sidecar-maxconn-support-no-extras branch from c06c529 to 726472c Compare May 9, 2017 15:11
[frontends.web.routes.test_1]
rule = "Host: some-aws-host"
rule = "Host: some-aws-host"
[frontends.web.maxconn]
Copy link

Choose a reason for hiding this comment

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

Should there be another test without the frontends.web.maxconn to verify the default Maxconn setting?

Copy link
Author

Choose a reason for hiding this comment

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

It's tested for the other backend defined in this file. See here.

I was considering to create a separate TOML for that specific case, but I think it wouldn't add much value.

@mihaitodor mihaitodor merged commit 9280d68 into master May 16, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants