Optionally support a parameter object to control internal service shards

The shard director supports many interesting use cases, for example

- retrying backend requests on different backends

- sharding by custom keys, for example for "client stickyness"
  or cache optimization

This patch adds the option to create parameter objects for backends
using the shard director.

Each named parameter object will be created only once and associated
with all directors for which the backend configuration names them.
parent 20f31575
...@@ -134,6 +134,8 @@ spec: ...@@ -134,6 +134,8 @@ spec:
rampup: rampup:
type: string type: string
pattern: '^\d+(\.\d+)?(ms|[smhdwy])$' pattern: '^\d+(\.\d+)?(ms|[smhdwy])$'
shard_param:
type: string
tls: tls:
type: object type: object
properties: properties:
......
...@@ -237,6 +237,10 @@ All of the properties of ``spec.director`` are optional: ...@@ -237,6 +237,10 @@ All of the properties of ``spec.director`` are optional:
[``rampup`` parameter](https://varnish-cache.org/docs/6.3/reference/vmod_directors.generated.html#void-xshard-set-rampup-duration-duration-0) [``rampup`` parameter](https://varnish-cache.org/docs/6.3/reference/vmod_directors.generated.html#void-xshard-set-rampup-duration-duration-0)
of the ``shard`` director. Ignored for the other directors. of the ``shard`` director. Ignored for the other directors.
* ``shard_param`` (string): Name of a
[shard_param](https://varnish-cache.org/docs/7.3/reference/vmod_directors.html#directors-shard-param)
object to control the shard director. Ignored for other directors.
With ``type`` you can choose the With ``type`` you can choose the
[round-robin](https://varnish-cache.org/docs/6.3/reference/vmod_directors.generated.html#obj-round-robin), [round-robin](https://varnish-cache.org/docs/6.3/reference/vmod_directors.generated.html#obj-round-robin),
[random](https://varnish-cache.org/docs/6.3/reference/vmod_directors.generated.html#obj-random) [random](https://varnish-cache.org/docs/6.3/reference/vmod_directors.generated.html#obj-random)
...@@ -267,6 +271,12 @@ rather than the new Endpoint for a request, with a probability that is ...@@ -267,6 +271,12 @@ rather than the new Endpoint for a request, with a probability that is
end of the rampup interval. This mitigates the "thundering herd" of end of the rampup interval. This mitigates the "thundering herd" of
requests for a newly added Endpoint. requests for a newly added Endpoint.
With ``shard_param`` set, the ingress controller creates a vcl object
by the given name and associates it with the director. It can then be
used from custom VCL to control the behaviour of the
director. Multiple directors can share the same ``shard_param``
object.
For example: For example:
``` ```
...@@ -277,6 +287,7 @@ spec: ...@@ -277,6 +287,7 @@ spec:
type: shard type: shard
warmup: 50 warmup: 50
rampup: 5m rampup: 5m
shard_param: param
``` ```
For the Ingress implementation, a director is always configured, For the Ingress implementation, a director is always configured,
......
...@@ -82,5 +82,6 @@ spec: ...@@ -82,5 +82,6 @@ spec:
# would be sharded if the "first" Endpoint were to be removed. # would be sharded if the "first" Endpoint were to be removed.
# - Requests for newly added Endpoints ramp up over five minutes. # - Requests for newly added Endpoints ramp up over five minutes.
type: shard type: shard
shard_param: caffeine
warmup: 50 warmup: 50
rampup: 5m rampup: 5m
...@@ -28,6 +28,7 @@ apps: ...@@ -28,6 +28,7 @@ apps:
# would be sharded if the "first" Endpoint were to be removed. # would be sharded if the "first" Endpoint were to be removed.
# - Requests for newly added Endpoints ramp up over five minutes. # - Requests for newly added Endpoints ramp up over five minutes.
type: shard type: shard
shard_param: caffeine
warmup: 50 warmup: 50
rampup: 5m rampup: 5m
......
...@@ -502,9 +502,10 @@ const ( ...@@ -502,9 +502,10 @@ const (
// DirectorSpec corresponds to spec.director in a BackendConfig, and // DirectorSpec corresponds to spec.director in a BackendConfig, and
// allows for a choice of directors, and some parameters. // allows for a choice of directors, and some parameters.
type DirectorSpec struct { type DirectorSpec struct {
Type DirectorType `json:"type,omitempty"` Type DirectorType `json:"type,omitempty"`
Warmup *int32 `json:"warmup,omitempty"` Warmup *int32 `json:"warmup,omitempty"`
Rampup string `json:"rampup,omitempty"` Rampup string `json:"rampup,omitempty"`
ShardParam string `json:"shard_param,omitempty"`
} }
// TLSSpec corresponds to spec.tls in a BackendConfig, to configure // TLSSpec corresponds to spec.tls in a BackendConfig, to configure
......
...@@ -132,9 +132,10 @@ func GetDirectorType(dirStr string) DirectorType { ...@@ -132,9 +132,10 @@ func GetDirectorType(dirStr string) DirectorType {
// Director is derived from spec.director in a BackendConfig, and allows // Director is derived from spec.director in a BackendConfig, and allows
// for some choice of the director, and sets some parameters. // for some choice of the director, and sets some parameters.
type Director struct { type Director struct {
Rampup string Rampup string
Warmup float64 Warmup float64
Type DirectorType Type DirectorType
ShardParam string
} }
func (dir Director) hash(hash hash.Hash) { func (dir Director) hash(hash hash.Hash) {
......
...@@ -108,6 +108,7 @@ backend vk8s_tea-svc_192_0_2_3_80 { ...@@ -108,6 +108,7 @@ backend vk8s_tea-svc_192_0_2_3_80 {
sub vcl_init { sub vcl_init {
new caffeine = directors.shard_param();
new vk8s_coffee-svc_director = directors.random(); new vk8s_coffee-svc_director = directors.random();
vk8s_coffee-svc_director.add_backend(vk8s_coffee-svc_192_0_2_4_80 vk8s_coffee-svc_director.add_backend(vk8s_coffee-svc_192_0_2_4_80
, 1.0 , 1.0
...@@ -131,6 +132,7 @@ sub vcl_init { ...@@ -131,6 +132,7 @@ sub vcl_init {
); );
vk8s_tea-svc_director.set_warmup(0.5); vk8s_tea-svc_director.set_warmup(0.5);
vk8s_tea-svc_director.set_rampup(5m); vk8s_tea-svc_director.set_rampup(5m);
vk8s_tea-svc_director.associate(caffeine.use());
vk8s_tea-svc_director.reconfigure(); vk8s_tea-svc_director.reconfigure();
new vk8s_cafe_example_com_exactMatcher = selector.set(); new vk8s_cafe_example_com_exactMatcher = selector.set();
......
...@@ -54,6 +54,7 @@ backend vk8s_tea-svc_192_0_2_3_80 { ...@@ -54,6 +54,7 @@ backend vk8s_tea-svc_192_0_2_3_80 {
sub vcl_init { sub vcl_init {
new caffeine = directors.shard_param();
new vk8s_tea-svc_director = directors.shard(); new vk8s_tea-svc_director = directors.shard();
vk8s_tea-svc_director.add_backend(vk8s_tea-svc_192_0_2_1_80 vk8s_tea-svc_director.add_backend(vk8s_tea-svc_192_0_2_1_80
); );
...@@ -63,6 +64,7 @@ sub vcl_init { ...@@ -63,6 +64,7 @@ sub vcl_init {
); );
vk8s_tea-svc_director.set_warmup(0.5); vk8s_tea-svc_director.set_warmup(0.5);
vk8s_tea-svc_director.set_rampup(5m); vk8s_tea-svc_director.set_rampup(5m);
vk8s_tea-svc_director.associate(caffeine.use());
vk8s_tea-svc_director.reconfigure(); vk8s_tea-svc_director.reconfigure();
} }
......
...@@ -131,6 +131,7 @@ backend vk8s_tea-svc_192_0_2_3_80 { ...@@ -131,6 +131,7 @@ backend vk8s_tea-svc_192_0_2_3_80 {
sub vcl_init { sub vcl_init {
new caffeine = directors.shard_param();
new vk8s_coffee-svc_director = directors.random(); new vk8s_coffee-svc_director = directors.random();
vk8s_coffee-svc_director.add_backend(vk8s_coffee-svc_192_0_2_4_80 vk8s_coffee-svc_director.add_backend(vk8s_coffee-svc_192_0_2_4_80
, 1.0 , 1.0
...@@ -154,6 +155,7 @@ sub vcl_init { ...@@ -154,6 +155,7 @@ sub vcl_init {
); );
vk8s_tea-svc_director.set_warmup(0.5); vk8s_tea-svc_director.set_warmup(0.5);
vk8s_tea-svc_director.set_rampup(5m); vk8s_tea-svc_director.set_rampup(5m);
vk8s_tea-svc_director.associate(caffeine.use());
vk8s_tea-svc_director.reconfigure(); vk8s_tea-svc_director.reconfigure();
new vk8s_metaxa-svc_resolver = dynamic.resolver(); new vk8s_metaxa-svc_resolver = dynamic.resolver();
......
...@@ -526,9 +526,10 @@ var teaSvcProbeDir = Service{ ...@@ -526,9 +526,10 @@ var teaSvcProbeDir = Service{
Threshold: "3", Threshold: "3",
}, },
Director: &Director{ Director: &Director{
Type: Shard, Type: Shard,
Rampup: "5m", Rampup: "5m",
Warmup: 0.5, Warmup: 0.5,
ShardParam: "caffeine",
}, },
} }
......
...@@ -126,7 +126,9 @@ backend {{backendName $svc $addr}} { ...@@ -126,7 +126,9 @@ backend {{backendName $svc $addr}} {
{{end}} {{end}}
sub vcl_init { sub vcl_init {
{{- range $param := shardParams .IntSvcs}}
new {{$param}} = directors.shard_param();
{{- end}}
{{- range $name, $svc := .IntSvcs}} {{- range $name, $svc := .IntSvcs}}
{{- $dirType := dirType $svc}} {{- $dirType := dirType $svc}}
new {{dirName $svc}} = directors.{{$dirType}}(); new {{dirName $svc}} = directors.{{$dirType}}();
...@@ -144,6 +146,10 @@ sub vcl_init { ...@@ -144,6 +146,10 @@ sub vcl_init {
{{- if $svc.Director.Rampup}} {{- if $svc.Director.Rampup}}
{{dirName $svc}}.set_rampup({{$svc.Director.Rampup}}); {{dirName $svc}}.set_rampup({{$svc.Director.Rampup}});
{{- end}} {{- end}}
{{- $param := $svc.Director.ShardParam}}
{{- if $param }}
{{dirName $svc}}.associate({{$param}}.use());
{{- end }}
{{dirName $svc}}.reconfigure(); {{dirName $svc}}.reconfigure();
{{- end}} {{- end}}
{{end}} {{end}}
...@@ -422,6 +428,22 @@ func needsVia(intSvcs, extSvcs map[string]Service) bool { ...@@ -422,6 +428,22 @@ func needsVia(intSvcs, extSvcs map[string]Service) bool {
return false return false
} }
func shardParams(intSvcs map[string]Service) []string {
params := []string{}
seen := make(map[string]bool)
for _, svc := range intSvcs {
if svc.Director == nil ||
svc.Director.ShardParam == "" ||
seen[svc.Director.ShardParam] {
continue
}
seen[svc.Director.ShardParam] = true
params = append(params, svc.Director.ShardParam)
}
return params
}
var vclFuncs = template.FuncMap{ var vclFuncs = template.FuncMap{
"plusOne": func(i int) int { return i + 1 }, "plusOne": func(i int) int { return i + 1 },
"dirType": func(svc Service) string { return dirType(svc) }, "dirType": func(svc Service) string { return dirType(svc) },
...@@ -429,6 +451,7 @@ var vclFuncs = template.FuncMap{ ...@@ -429,6 +451,7 @@ var vclFuncs = template.FuncMap{
"backendName": backendName, "backendName": backendName,
"hostMangle": hostMangle, "hostMangle": hostMangle,
"needsVia": needsVia, "needsVia": needsVia,
"shardParams": shardParams,
"pfxPattern": pfxPattern, "pfxPattern": pfxPattern,
"pfxSorted": pfxSorted, "pfxSorted": pfxSorted,
"pfxSvc": pfxSvc, "pfxSvc": pfxSvc,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment