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

v2: Performance improvements to Replacer implementation (placeholders) #2674

Merged
merged 1 commit into from
Jul 16, 2019

Conversation

mholt
Copy link
Member

@mholt mholt commented Jul 16, 2019

1. What does this change do, exactly?

Improves performance of placeholders in Caddy 2. Placeholders are the equivalent of variables in other web servers (but better). They allow you to refer to dynamic values in static configuration.

I was lazy with the initial implementation because I wanted to get it done quickly.

The old implementation pre-generated all possible values and then lazily called strings.ReplaceAll() in a loop. Of course this produces LOTS of unnecessary allocations. Now, we simply use functions which return the requested value, or false if the value is not recognized by that function; and instead of a blanket strings.ReplaceAll() for every possible placeholder, we parse the input string and look for the specific placeholders we need. By using strings.Builder, the reconstruction of the string is very efficient.

Note that this optimization affects ALL usage of placeholders, but the tests I performed were using them in a response body.

Using this sample config:

{
	"apps": {
		"http": {
			"servers": {
				"myserver": {
					"listen": [":8080"],
					"routes": [
						{
							"handle": [{
								"handler": "rewrite",
								"method": "FOO",
								"uri": "/test/abc?foo=bar"
							},
							{
								"handler": "static_response",
								"body": "Method: {http.request.method} URI: '{http.request.uri}' Path: '{http.request.uri.path}' Query: '{http.request.uri.query}'"
							}]
						}
					]
				}
			}
		}
	}
}

Here are some high-level load tests on my 8-core Macbook Pro:

NO PLACEHOLDERS, BASELINE:

$ wrk -t12 -c400 -d10s http://127.0.0.1:8080
Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    10.25ms   49.25ms 992.92ms   98.28%
    Req/Sec     6.67k     1.49k   26.95k    85.37%
  798668 requests in 10.10s, 99.78MB read
  Socket errors: connect 0, read 248, write 0, timeout 0
Requests/sec:  79074.88
Transfer/sec:      9.88MB



PLACEHOLDERS, BEFORE OPTIMIZATION:

$ wrk -t12 -c400 -d10s http://127.0.0.1:8080
Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    18.18ms   63.94ms   1.02s    97.73%
    Req/Sec     3.11k   512.10    11.11k    92.02%
  372368 requests in 10.10s, 46.52MB read
  Socket errors: connect 0, read 286, write 0, timeout 0
Requests/sec:  36860.00
Transfer/sec:      4.60MB



PLACEHOLDERS, AFTER OPTIMIZATION:

$ wrk -t12 -c400 -d10s http://127.0.0.1:8080
Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     8.88ms   41.16ms 840.18ms   98.45%
    Req/Sec     6.89k     1.16k   11.70k    82.86%
  827893 requests in 10.10s, 103.43MB read
  Socket errors: connect 0, read 236, write 0, timeout 0
Requests/sec:  81964.30
Transfer/sec:     10.24MB



PLACEHOLDERS, AFTER OPTIMIZATION AND WITH LARGER BODY SIZE:

$ wrk -t12 -c400 -d10s http://127.0.0.1:8080
Running 10s test @ http://127.0.0.1:8080
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    11.28ms   54.51ms   1.02s    98.07%
    Req/Sec     6.64k     0.86k   11.81k    87.58%
  792326 requests in 10.00s, 780.56MB read
  Socket errors: connect 0, read 248, write 0, timeout 0
Requests/sec:  79211.38
Transfer/sec:     78.03MB

2. Please link to the relevant issues.

Closes #2673

3. Which documentation changes (if any) need to be made because of this PR?

None.

@mholt mholt added optimization 📉 Performance or cost improvements Caddy2 labels Jul 16, 2019
@mholt mholt added this to the 2.0 milestone Jul 16, 2019
@mholt mholt self-assigned this Jul 16, 2019
Copy link
Collaborator

@tobya tobya left a comment

Choose a reason for hiding this comment

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

LGTM

@mholt
Copy link
Member Author

mholt commented Jul 16, 2019

Thanks.

Going to merge this then, but more testing is needed. @aligator If you still want to help with placeholders, we could use some good unit tests (Test*() functions) around the replacer type, as well as benchmark tests (Bench*() functions).

If you want other ways to contribute, we also need help building out the reverse proxy. There's a lot of good stuff to do there (again, intermediate level -- some network/backend Go experience is definitely useful here). Just let me know.

@mholt mholt closed this Jul 16, 2019
@mholt mholt reopened this Jul 16, 2019
@mholt mholt merged commit b44a22a into v2 Jul 16, 2019
@mholt mholt deleted the v2-replacerperf branch July 16, 2019 18:27
@aligator
Copy link
Contributor

aligator commented Jul 16, 2019

@mholt Yes, I am definitely interested in helping you. But I am not very experienced with go yet so maybe some lighter things first.
Tests for the replacer are a great idea for my first steps with caddy.

Tell me If I can help you somewhere else. (I am also registered in the caddy forum as aligator)

@mholt
Copy link
Member Author

mholt commented Jul 16, 2019

Sounds good to me! Feel free to make a PR; incremental reviews are often easier than a big one at the end.

@aligator aligator mentioned this pull request Jul 16, 2019
3 tasks
@mholt mholt mentioned this pull request Aug 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
optimization 📉 Performance or cost improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants