Add support for configuring Envoys route idle_timeout (#14340)
* Add idleTimeout Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Dhia Ayachi <dhia@hashicorp.com>
This commit is contained in:
parent
ecd4307b7c
commit
71f7f2e3dc
|
@ -0,0 +1,4 @@
|
|||
```release-note:feature
|
||||
connect: Add local_idle_timeout_ms to allow configuring the Envoy route idle timeout on local_app
|
||||
connect: Add IdleTimeout to service-router to allow configuring the Envoy route idle timeout
|
||||
```
|
|
@ -4107,6 +4107,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
|
|||
"namespace" : "leek",
|
||||
"prefix_rewrite" : "/alternate",
|
||||
"request_timeout" : "99s",
|
||||
"idle_timeout" : "99s",
|
||||
"num_retries" : 12345,
|
||||
"retry_on_connect_failure": true,
|
||||
"retry_on_status_codes" : [401, 209]
|
||||
|
@ -4195,6 +4196,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
|
|||
namespace = "leek"
|
||||
prefix_rewrite = "/alternate"
|
||||
request_timeout = "99s"
|
||||
idle_timeout = "99s"
|
||||
num_retries = 12345
|
||||
retry_on_connect_failure = true
|
||||
retry_on_status_codes = [401, 209]
|
||||
|
@ -4284,6 +4286,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
|
|||
Partition: acl.DefaultPartitionName,
|
||||
PrefixRewrite: "/alternate",
|
||||
RequestTimeout: 99 * time.Second,
|
||||
IdleTimeout: 99 * time.Second,
|
||||
NumRetries: 12345,
|
||||
RetryOnConnectFailure: true,
|
||||
RetryOnStatusCodes: []uint32{401, 209},
|
||||
|
|
|
@ -682,6 +682,15 @@ func setupTestVariationDiscoveryChain(
|
|||
RequestTimeout: 33 * time.Second,
|
||||
},
|
||||
},
|
||||
{
|
||||
Match: httpMatch(&structs.ServiceRouteHTTPMatch{
|
||||
PathPrefix: "/idle-timeout",
|
||||
}),
|
||||
Destination: &structs.ServiceRouteDestination{
|
||||
Service: "idle-timeout",
|
||||
IdleTimeout: 33 * time.Second,
|
||||
},
|
||||
},
|
||||
{
|
||||
Match: httpMatch(&structs.ServiceRouteHTTPMatch{
|
||||
PathPrefix: "/retry-connect",
|
||||
|
|
|
@ -426,6 +426,10 @@ type ServiceRouteDestination struct {
|
|||
// downstream request (and retries) to be processed.
|
||||
RequestTimeout time.Duration `json:",omitempty" alias:"request_timeout"`
|
||||
|
||||
// IdleTimeout is The total amount of time permitted for the request stream
|
||||
// to be idle
|
||||
IdleTimeout time.Duration `json:",omitempty" alias:"idle_timeout"`
|
||||
|
||||
// NumRetries is the number of times to retry the request when a retryable
|
||||
// result occurs. This seems fairly proxy agnostic.
|
||||
NumRetries uint32 `json:",omitempty" alias:"num_retries"`
|
||||
|
@ -452,15 +456,21 @@ func (e *ServiceRouteDestination) MarshalJSON() ([]byte, error) {
|
|||
type Alias ServiceRouteDestination
|
||||
exported := &struct {
|
||||
RequestTimeout string `json:",omitempty"`
|
||||
IdleTimeout string `json:",omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
RequestTimeout: e.RequestTimeout.String(),
|
||||
IdleTimeout: e.IdleTimeout.String(),
|
||||
Alias: (*Alias)(e),
|
||||
}
|
||||
if e.RequestTimeout == 0 {
|
||||
exported.RequestTimeout = ""
|
||||
}
|
||||
|
||||
if e.IdleTimeout == 0 {
|
||||
exported.IdleTimeout = ""
|
||||
}
|
||||
|
||||
return json.Marshal(exported)
|
||||
}
|
||||
|
||||
|
@ -468,6 +478,7 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
|
|||
type Alias ServiceRouteDestination
|
||||
aux := &struct {
|
||||
RequestTimeout string
|
||||
IdleTimeout string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(e),
|
||||
|
@ -481,6 +492,11 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
if aux.IdleTimeout != "" {
|
||||
if e.IdleTimeout, err = time.ParseDuration(aux.IdleTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -603,6 +603,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
namespace = "leek"
|
||||
prefix_rewrite = "/alternate"
|
||||
request_timeout = "99s"
|
||||
idle_timeout = "99s"
|
||||
num_retries = 12345
|
||||
retry_on_connect_failure = true
|
||||
retry_on_status_codes = [401, 209]
|
||||
|
@ -704,6 +705,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
Namespace = "leek"
|
||||
PrefixRewrite = "/alternate"
|
||||
RequestTimeout = "99s"
|
||||
IdleTimeout = "99s"
|
||||
NumRetries = 12345
|
||||
RetryOnConnectFailure = true
|
||||
RetryOnStatusCodes = [401, 209]
|
||||
|
@ -805,6 +807,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
Namespace: "leek",
|
||||
PrefixRewrite: "/alternate",
|
||||
RequestTimeout: 99 * time.Second,
|
||||
IdleTimeout: 99 * time.Second,
|
||||
NumRetries: 12345,
|
||||
RetryOnConnectFailure: true,
|
||||
RetryOnStatusCodes: []uint32{401, 209},
|
||||
|
|
|
@ -49,6 +49,11 @@ type ProxyConfig struct {
|
|||
// respected (15s)
|
||||
LocalRequestTimeoutMs *int `mapstructure:"local_request_timeout_ms"`
|
||||
|
||||
// LocalIdleTimeoutMs is the number of milliseconds to timeout HTTP streams
|
||||
// to the local app instance. If not set, no value is set, Envoy defaults are
|
||||
// respected (300s)
|
||||
LocalIdleTimeoutMs *int `mapstructure:"local_idle_timeout_ms"`
|
||||
|
||||
// Protocol describes the service's protocol. Valid values are "tcp",
|
||||
// "http" and "grpc". Anything else is treated as tcp. This enables
|
||||
// protocol aware features like per-request metrics and connection
|
||||
|
|
|
@ -157,6 +157,39 @@ func TestParseProxyConfig(t *testing.T) {
|
|||
Protocol: "tcp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "local idle timeout override, float ",
|
||||
input: map[string]interface{}{
|
||||
"local_idle_timeout_ms": float64(1000.0),
|
||||
},
|
||||
want: ProxyConfig{
|
||||
LocalConnectTimeoutMs: 5000,
|
||||
LocalIdleTimeoutMs: intPointer(1000),
|
||||
Protocol: "tcp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "local idle timeout override, int ",
|
||||
input: map[string]interface{}{
|
||||
"local_idle_timeout_ms": 1000,
|
||||
},
|
||||
want: ProxyConfig{
|
||||
LocalConnectTimeoutMs: 5000,
|
||||
LocalIdleTimeoutMs: intPointer(1000),
|
||||
Protocol: "tcp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "local idle timeout override, string",
|
||||
input: map[string]interface{}{
|
||||
"local_idle_timeout_ms": "1000",
|
||||
},
|
||||
want: ProxyConfig{
|
||||
LocalConnectTimeoutMs: 5000,
|
||||
LocalIdleTimeoutMs: intPointer(1000),
|
||||
Protocol: "tcp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "balance inbound connections override, string",
|
||||
input: map[string]interface{}{
|
||||
|
|
|
@ -1275,6 +1275,7 @@ func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot
|
|||
routeName: name,
|
||||
cluster: LocalAppClusterName,
|
||||
requestTimeoutMs: cfg.LocalRequestTimeoutMs,
|
||||
idleTimeoutMs: cfg.LocalIdleTimeoutMs,
|
||||
tracing: tracing,
|
||||
}
|
||||
if useHTTPFilter {
|
||||
|
@ -2114,6 +2115,7 @@ type listenerFilterOpts struct {
|
|||
statPrefix string
|
||||
routePath string
|
||||
requestTimeoutMs *int
|
||||
idleTimeoutMs *int
|
||||
ingressGateway bool
|
||||
httpAuthzFilter *envoy_http_v3.HttpFilter
|
||||
forwardClientDetails bool
|
||||
|
@ -2260,6 +2262,11 @@ func makeHTTPFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error)
|
|||
r.Timeout = durationpb.New(time.Duration(*opts.requestTimeoutMs) * time.Millisecond)
|
||||
}
|
||||
|
||||
if opts.idleTimeoutMs != nil {
|
||||
r := route.GetRoute()
|
||||
r.IdleTimeout = durationpb.New(time.Duration(*opts.idleTimeoutMs) * time.Millisecond)
|
||||
}
|
||||
|
||||
// If a path is provided, do not match on a catch-all prefix
|
||||
if opts.routePath != "" {
|
||||
route.Match.PathSpecifier = &envoy_route_v3.RouteMatch_Path{Path: opts.routePath}
|
||||
|
|
|
@ -230,6 +230,7 @@ func TestListenersFromSnapshot(t *testing.T) {
|
|||
ns.Proxy.Config["protocol"] = "http"
|
||||
ns.Proxy.Config["local_connect_timeout_ms"] = 1234
|
||||
ns.Proxy.Config["local_request_timeout_ms"] = 2345
|
||||
ns.Proxy.Config["local_idle_timeout_ms"] = 3456
|
||||
}, nil)
|
||||
},
|
||||
},
|
||||
|
|
|
@ -577,6 +577,10 @@ func (s *ResourceGenerator) makeUpstreamRouteForDiscoveryChain(
|
|||
routeAction.Route.Timeout = durationpb.New(destination.RequestTimeout)
|
||||
}
|
||||
|
||||
if destination.IdleTimeout > 0 {
|
||||
routeAction.Route.IdleTimeout = durationpb.New(destination.IdleTimeout)
|
||||
}
|
||||
|
||||
if destination.HasRetryFeatures() {
|
||||
routeAction.Route.RetryPolicy = getRetryPolicyForDestination(destination)
|
||||
}
|
||||
|
|
|
@ -83,7 +83,8 @@
|
|||
},
|
||||
"route": {
|
||||
"cluster": "local_app",
|
||||
"timeout": "2.345s"
|
||||
"timeout": "2.345s",
|
||||
"idleTimeout": "3.456s"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -274,6 +274,15 @@
|
|||
"timeout": "33s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/idle-timeout"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "idle-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"idleTimeout": "33s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/retry-connect"
|
||||
|
|
|
@ -275,6 +275,15 @@
|
|||
"timeout": "33s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/idle-timeout"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "idle-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"idleTimeout": "33s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/retry-connect"
|
||||
|
|
|
@ -275,6 +275,15 @@
|
|||
"timeout": "33s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/idle-timeout"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "idle-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"idleTimeout": "33s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/retry-connect"
|
||||
|
|
|
@ -69,6 +69,7 @@ type ServiceRouteDestination struct {
|
|||
Partition string `json:",omitempty"`
|
||||
PrefixRewrite string `json:",omitempty" alias:"prefix_rewrite"`
|
||||
RequestTimeout time.Duration `json:",omitempty" alias:"request_timeout"`
|
||||
IdleTimeout time.Duration `json:",omitempty" alias:"idle_timeout"`
|
||||
NumRetries uint32 `json:",omitempty" alias:"num_retries"`
|
||||
RetryOnConnectFailure bool `json:",omitempty" alias:"retry_on_connect_failure"`
|
||||
RetryOnStatusCodes []uint32 `json:",omitempty" alias:"retry_on_status_codes"`
|
||||
|
@ -81,14 +82,19 @@ func (e *ServiceRouteDestination) MarshalJSON() ([]byte, error) {
|
|||
type Alias ServiceRouteDestination
|
||||
exported := &struct {
|
||||
RequestTimeout string `json:",omitempty"`
|
||||
IdleTimeout string `json:",omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
RequestTimeout: e.RequestTimeout.String(),
|
||||
IdleTimeout: e.IdleTimeout.String(),
|
||||
Alias: (*Alias)(e),
|
||||
}
|
||||
if e.RequestTimeout == 0 {
|
||||
exported.RequestTimeout = ""
|
||||
}
|
||||
if e.IdleTimeout == 0 {
|
||||
exported.IdleTimeout = ""
|
||||
}
|
||||
|
||||
return json.Marshal(exported)
|
||||
}
|
||||
|
@ -97,6 +103,7 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
|
|||
type Alias ServiceRouteDestination
|
||||
aux := &struct {
|
||||
RequestTimeout string
|
||||
IdleTimeout string
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(e),
|
||||
|
@ -110,6 +117,11 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
if aux.IdleTimeout != "" {
|
||||
if e.IdleTimeout, err = time.ParseDuration(aux.IdleTimeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -612,6 +612,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
"Namespace": "leek",
|
||||
"PrefixRewrite": "/alternate",
|
||||
"RequestTimeout": "99s",
|
||||
"IdleTimeout": "99s",
|
||||
"NumRetries": 12345,
|
||||
"RetryOnConnectFailure": true,
|
||||
"RetryOnStatusCodes": [401, 209]
|
||||
|
@ -696,6 +697,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
Namespace: "leek",
|
||||
PrefixRewrite: "/alternate",
|
||||
RequestTimeout: 99 * time.Second,
|
||||
IdleTimeout: 99 * time.Second,
|
||||
NumRetries: 12345,
|
||||
RetryOnConnectFailure: true,
|
||||
RetryOnStatusCodes: []uint32{401, 209},
|
||||
|
|
|
@ -821,6 +821,7 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
partition = "chard"
|
||||
prefix_rewrite = "/alternate"
|
||||
request_timeout = "99s"
|
||||
idle_timeout = "99s"
|
||||
num_retries = 12345
|
||||
retry_on_connect_failure = true
|
||||
retry_on_status_codes = [401, 209]
|
||||
|
@ -906,6 +907,7 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
Partition = "chard"
|
||||
PrefixRewrite = "/alternate"
|
||||
RequestTimeout = "99s"
|
||||
IdleTimeout = "99s"
|
||||
NumRetries = 12345
|
||||
RetryOnConnectFailure = true
|
||||
RetryOnStatusCodes = [401, 209]
|
||||
|
@ -992,6 +994,7 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
"partition": "chard",
|
||||
"prefix_rewrite": "/alternate",
|
||||
"request_timeout": "99s",
|
||||
"idle_timeout": "99s",
|
||||
"num_retries": 12345,
|
||||
"retry_on_connect_failure": true,
|
||||
"retry_on_status_codes": [
|
||||
|
@ -1085,6 +1088,7 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
"Partition": "chard",
|
||||
"PrefixRewrite": "/alternate",
|
||||
"RequestTimeout": "99s",
|
||||
"IdleTimeout": "99s",
|
||||
"NumRetries": 12345,
|
||||
"RetryOnConnectFailure": true,
|
||||
"RetryOnStatusCodes": [
|
||||
|
@ -1177,6 +1181,7 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
Partition: "chard",
|
||||
PrefixRewrite: "/alternate",
|
||||
RequestTimeout: 99 * time.Second,
|
||||
IdleTimeout: 99 * time.Second,
|
||||
NumRetries: 12345,
|
||||
RetryOnConnectFailure: true,
|
||||
RetryOnStatusCodes: []uint32{401, 209},
|
||||
|
|
|
@ -696,6 +696,12 @@ spec:
|
|||
description:
|
||||
'The total amount of time permitted for the entire downstream request (and retries) to be processed.',
|
||||
},
|
||||
{
|
||||
name: 'IdleTimeout',
|
||||
type: 'duration: 0',
|
||||
description:
|
||||
'The total amount of time permitted for the request stream to be idle',
|
||||
},
|
||||
{
|
||||
name: 'NumRetries',
|
||||
type: 'int: 0',
|
||||
|
|
|
@ -330,6 +330,11 @@ defaults that are inherited by all services.
|
|||
specified, inherits the Envoy default for route timeouts (15s). A value of 0 will
|
||||
disable request timeouts.
|
||||
|
||||
- `local_idle_timeout_ms` - In milliseconds, the idle timeout for HTTP requests
|
||||
to the local application instance. Applies to HTTP based protocols only. If not
|
||||
specified, inherits the Envoy default for route idle timeouts (15s). A value of 0
|
||||
disables request timeouts.
|
||||
|
||||
- `max_inbound_connections` - The maximum number of concurrent inbound connections
|
||||
to the local application instance. If not specified, inherits the Envoy default (1024).
|
||||
|
||||
|
|
Loading…
Reference in New Issue