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

Wrong calculation for ServerNameHashMaxSize? #564

Closed
ankon opened this issue Apr 6, 2017 · 7 comments · Fixed by #607
Closed

Wrong calculation for ServerNameHashMaxSize? #564

ankon opened this issue Apr 6, 2017 · 7 comments · Fixed by #607

Comments

@ankon
Copy link
Contributor

ankon commented Apr 6, 2017

https://github.com/kubernetes/ingress/blob/master/controllers/nginx/pkg/cmd/controller/nginx.go#L328 tries to auto-size the hash for the server names, and it uses the total number of bytes for all server names to determine ServerNameHashMaxSize.

But, according to http://nginx.org/en/docs/http/server_names.html#optimization that seems wrong, and instead the number of server names should be considered:

If a large number of server names are defined [...] try to set server_names_hash_max_size to a number close to the number of server names.

Am I missing something here?

ankon added a commit to Collaborne/ingress that referenced this issue Apr 6, 2017
@aledbf
Copy link
Member

aledbf commented Apr 6, 2017

@ankon this is the length of the key in the hash tables not the length of the table.
The calculation is here

@ankon
Copy link
Contributor Author

ankon commented Apr 6, 2017

	var serverNames int
	for _, srv := range ingressCfg.Servers {
		serverNames += len([]byte(srv.Hostname))
		if longestName < len(srv.Hostname) {
			longestName = len(srv.Hostname)
		}
	}

At the end we have serverNames as sum of the (byte) lengths of the Hostnames, right?

	serverNameHashMaxSize := nextPowerOf2(serverNames)
	if serverNameHashMaxSize > cfg.ServerNameHashMaxSize {
		glog.V(3).Infof("adjusting ServerNameHashMaxSize variable from %v to %v",
			cfg.ServerNameHashMaxSize, serverNameHashMaxSize)
		cfg.ServerNameHashMaxSize = serverNameHashMaxSize
	}

So here we end up with serverNameHashMaxSize being the first power of 2 > serverNames -- which was a sum, not a number/count. The documentation I quoted seems to indicate that server_names_hash_max_size should be calculated by counting the server names, and server_names_hash_bucket_size should be calculated as "longer than the length of the longest name" (which is what the calculation for serverNameHashBucketSize does based on longestName).

So this calculation seems to "overestimate" the needed space in the table.

@aledbf
Copy link
Member

aledbf commented Apr 6, 2017

So this calculation seems to "overestimate" the needed space in the table.

Right. What alternative you propose?

If we leave this as a parameter (until 0.8.3) every time you add a new ingress you could end with a non working nginx configuration. Honestly I prefer overestimate the size of the table.

@ankon
Copy link
Contributor Author

ankon commented Apr 6, 2017

Being able to configure things is definitely helpful ... in fact the reason I started to look at this code in the first place is that I hit the other limit and didn't know there is a way to configure it :)

Rough idea would be this though:

diff --git a/controllers/nginx/pkg/cmd/controller/nginx.go b/controllers/nginx/pkg/cmd/controller/nginx.go
index a57df8d..be30b67 100644
--- a/controllers/nginx/pkg/cmd/controller/nginx.go
+++ b/controllers/nginx/pkg/cmd/controller/nginx.go
@@ -327,9 +327,7 @@ func (n *NGINXController) SetListers(lister ingress.StoreLister) {
 // if an error is returned means requeue the update
 func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) ([]byte, error) {
 	var longestName int
-	var serverNames int
 	for _, srv := range ingressCfg.Servers {
-		serverNames += len([]byte(srv.Hostname))
 		if longestName < len(srv.Hostname) {
 			longestName = len(srv.Hostname)
 		}
@@ -358,7 +356,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) ([]byte, er
 			cfg.ServerNameHashBucketSize, nameHashBucketSize)
 		cfg.ServerNameHashBucketSize = nameHashBucketSize
 	}
-	serverNameHashMaxSize := nextPowerOf2(serverNames)
+	serverNameHashMaxSize := nextPowerOf2(len(ingressCfg.Servers))
 	if serverNameHashMaxSize > cfg.ServerNameHashMaxSize {
 		glog.V(3).Infof("adjusting ServerNameHashMaxSize variable from %v to %v",
 			cfg.ServerNameHashMaxSize, serverNameHashMaxSize)

ankon added a commit to Collaborne/ingress that referenced this issue Apr 6, 2017
@aledbf
Copy link
Member

aledbf commented Apr 6, 2017

@ankon I think we can change the default ServerNameHashMaxSize to -1 and use the calculated serverNameHashMaxSize value or use the value you configured in the configmap

if cfg.ServerNameHashMaxSize == -1 {
  // use the serverNameHashMaxSize value
}

@aledbf
Copy link
Member

aledbf commented Apr 13, 2017

@ankon friendly ping

@kylebyerly-hp
Copy link

With 0.9-beta.4 and 8 ingresses each doing 4 urls (of various length 26-47 chars) I start getting this error.
I agree with @aledbf on the over-estimation.

-------------------------------------------------------------------------------
Error: exit status 1
2017/04/14 19:07:51 [emerg] 43#43: could not build server_names_hash, you should increase server_names_hash_bucket_size: 64
nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 64
nginx: configuration file /tmp/nginx-cfg017454066 test failed

-------------------------------------------------------------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants