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:
rampup:
type: string
pattern: '^\d+(\.\d+)?(ms|[smhdwy])$'
shard_param:
type: string
tls:
type: object
properties:
......
......@@ -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)
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
[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)
......@@ -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
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:
```
......@@ -277,6 +287,7 @@ spec:
type: shard
warmup: 50
rampup: 5m
shard_param: param
```
For the Ingress implementation, a director is always configured,
......
......@@ -82,5 +82,6 @@ spec:
# would be sharded if the "first" Endpoint were to be removed.
# - Requests for newly added Endpoints ramp up over five minutes.
type: shard
shard_param: caffeine
warmup: 50
rampup: 5m
......@@ -28,6 +28,7 @@ apps:
# would be sharded if the "first" Endpoint were to be removed.
# - Requests for newly added Endpoints ramp up over five minutes.
type: shard
shard_param: caffeine
warmup: 50
rampup: 5m
......
......@@ -502,9 +502,10 @@ const (
// DirectorSpec corresponds to spec.director in a BackendConfig, and
// allows for a choice of directors, and some parameters.
type DirectorSpec struct {
Type DirectorType `json:"type,omitempty"`
Warmup *int32 `json:"warmup,omitempty"`
Rampup string `json:"rampup,omitempty"`
Type DirectorType `json:"type,omitempty"`
Warmup *int32 `json:"warmup,omitempty"`
Rampup string `json:"rampup,omitempty"`
ShardParam string `json:"shard_param,omitempty"`
}
// TLSSpec corresponds to spec.tls in a BackendConfig, to configure
......
......@@ -132,9 +132,10 @@ func GetDirectorType(dirStr string) DirectorType {
// Director is derived from spec.director in a BackendConfig, and allows
// for some choice of the director, and sets some parameters.
type Director struct {
Rampup string
Warmup float64
Type DirectorType
Rampup string
Warmup float64
Type DirectorType
ShardParam string
}
func (dir Director) hash(hash hash.Hash) {
......
......@@ -108,6 +108,7 @@ backend vk8s_tea-svc_192_0_2_3_80 {
sub vcl_init {
new caffeine = directors.shard_param();
new vk8s_coffee-svc_director = directors.random();
vk8s_coffee-svc_director.add_backend(vk8s_coffee-svc_192_0_2_4_80
, 1.0
......@@ -131,6 +132,7 @@ sub vcl_init {
);
vk8s_tea-svc_director.set_warmup(0.5);
vk8s_tea-svc_director.set_rampup(5m);
vk8s_tea-svc_director.associate(caffeine.use());
vk8s_tea-svc_director.reconfigure();
new vk8s_cafe_example_com_exactMatcher = selector.set();
......
......@@ -54,6 +54,7 @@ backend vk8s_tea-svc_192_0_2_3_80 {
sub vcl_init {
new caffeine = directors.shard_param();
new vk8s_tea-svc_director = directors.shard();
vk8s_tea-svc_director.add_backend(vk8s_tea-svc_192_0_2_1_80
);
......@@ -63,6 +64,7 @@ sub vcl_init {
);
vk8s_tea-svc_director.set_warmup(0.5);
vk8s_tea-svc_director.set_rampup(5m);
vk8s_tea-svc_director.associate(caffeine.use());
vk8s_tea-svc_director.reconfigure();
}
......
......@@ -131,6 +131,7 @@ backend vk8s_tea-svc_192_0_2_3_80 {
sub vcl_init {
new caffeine = directors.shard_param();
new vk8s_coffee-svc_director = directors.random();
vk8s_coffee-svc_director.add_backend(vk8s_coffee-svc_192_0_2_4_80
, 1.0
......@@ -154,6 +155,7 @@ sub vcl_init {
);
vk8s_tea-svc_director.set_warmup(0.5);
vk8s_tea-svc_director.set_rampup(5m);
vk8s_tea-svc_director.associate(caffeine.use());
vk8s_tea-svc_director.reconfigure();
new vk8s_metaxa-svc_resolver = dynamic.resolver();
......
......@@ -526,9 +526,10 @@ var teaSvcProbeDir = Service{
Threshold: "3",
},
Director: &Director{
Type: Shard,
Rampup: "5m",
Warmup: 0.5,
Type: Shard,
Rampup: "5m",
Warmup: 0.5,
ShardParam: "caffeine",
},
}
......
......@@ -126,7 +126,9 @@ backend {{backendName $svc $addr}} {
{{end}}
sub vcl_init {
{{- range $param := shardParams .IntSvcs}}
new {{$param}} = directors.shard_param();
{{- end}}
{{- range $name, $svc := .IntSvcs}}
{{- $dirType := dirType $svc}}
new {{dirName $svc}} = directors.{{$dirType}}();
......@@ -144,6 +146,10 @@ sub vcl_init {
{{- if $svc.Director.Rampup}}
{{dirName $svc}}.set_rampup({{$svc.Director.Rampup}});
{{- end}}
{{- $param := $svc.Director.ShardParam}}
{{- if $param }}
{{dirName $svc}}.associate({{$param}}.use());
{{- end }}
{{dirName $svc}}.reconfigure();
{{- end}}
{{end}}
......@@ -422,6 +428,22 @@ func needsVia(intSvcs, extSvcs map[string]Service) bool {
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{
"plusOne": func(i int) int { return i + 1 },
"dirType": func(svc Service) string { return dirType(svc) },
......@@ -429,6 +451,7 @@ var vclFuncs = template.FuncMap{
"backendName": backendName,
"hostMangle": hostMangle,
"needsVia": needsVia,
"shardParams": shardParams,
"pfxPattern": pfxPattern,
"pfxSorted": pfxSorted,
"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