Average Latency Feedback
Overview
The response times of a service start to deteriorate when the service's underlying concurrency limit is surpassed. Consequently, a degradation in response latency can serve as a reliable signal for identifying service overload. This policy is designed to detect overload situations based on latency deterioration. During overload, the request rate is throttled so that latency gets restored back to an acceptable range.
Configuration
This policy is based on the
Load Scheduling with Average Latency Feedback
blueprint. The latency of requests processed by the
cart-service.prod.svc.cluster.local
service is monitored. By analyzing the
variance between current latency and a historical latency baseline, the policy
facilitates the identification of potential service overload. A deviation of
1.1
from the baseline is considered as a signal of service overload.
To mitigate service overload, the requests to the
cart-service.prod.svc.cluster.local
service are passed through a load
scheduler. The load scheduler reduces the request rate in overload scenarios,
temporarily placing excess requests in a queue.
As service latency improves, indicating a return to normal operational state, the request rate is incrementally increased until it matches the incoming request rate. This responsive mechanism helps ensure that service performance is optimized while mitigating the risk of service disruptions due to overload.
The below values.yaml
file can be generated by following the steps in the
Installation section.
- aperturectl values.yaml
# yaml-language-server: $schema=../../../../../../blueprints/load-scheduling/average-latency/gen/definitions.json
# Generated values file for load-scheduling/average-latency blueprint
# Documentation/Reference for objects and parameters can be found at:
# https://docs.fluxninja.com/reference/blueprints/load-scheduling/average-latency
policy:
# Name of the policy.
# Type: string
# Required: True
policy_name: basic-service-protection
service_protection_core:
adaptive_load_scheduler:
load_scheduler:
# The selectors determine the flows that are protected by this policy.
# Type: []aperture.spec.v1.Selector
# Required: True
selectors:
- control_point: ingress
service: cart-service.prod.svc.cluster.local
latency_baseliner:
# Tolerance factor beyond which the service is considered to be in overloaded state. E.g. if the long-term average of latency is L and if the tolerance is T, then the service is considered to be in an overloaded state if the short-term average of latency is more than L*T.
# Type: float64
latency_tolerance_multiplier: 1.1
# Flux Meter defines the scope of latency measurements.
# Type: aperture.spec.v1.FluxMeter
# Required: True
flux_meter:
selectors:
- control_point: ingress
service: cart-service.prod.svc.cluster.local
Generated Policy
apiVersion: fluxninja.com/v1alpha1
kind: Policy
metadata:
labels:
fluxninja.com/validate: "true"
name: basic-service-protection
spec:
circuit:
components:
- flow_control:
adaptive_load_scheduler:
dry_run: false
dry_run_config_key: dry_run
in_ports:
overload_confirmation:
constant_signal:
value: 1
setpoint:
signal_name: SETPOINT
signal:
signal_name: SIGNAL
out_ports:
desired_load_multiplier:
signal_name: DESIRED_LOAD_MULTIPLIER
observed_load_multiplier:
signal_name: OBSERVED_LOAD_MULTIPLIER
parameters:
alerter:
alert_name: Load Throttling Event
gradient:
max_gradient: 1
min_gradient: 0.1
slope: -1
load_multiplier_linear_increment: 0.025
load_scheduler:
selectors:
- control_point: ingress
service: cart-service.prod.svc.cluster.local
max_load_multiplier: 2
- query:
promql:
evaluation_interval: 10s
out_ports:
output:
signal_name: SIGNAL
query_string: sum(increase(flux_meter_sum{flow_status="OK", flux_meter_name="basic-service-protection",
policy_name="basic-service-protection"}[30s]))/sum(increase(flux_meter_count{flow_status="OK",
flux_meter_name="basic-service-protection", policy_name="basic-service-protection"}[30s]))
- query:
promql:
evaluation_interval: 30s
out_ports:
output:
signal_name: SIGNAL_LONG_TERM
query_string: sum(increase(flux_meter_sum{flow_status="OK", flux_meter_name="basic-service-protection",
policy_name="basic-service-protection"}[1800s]))/sum(increase(flux_meter_count{flow_status="OK",
flux_meter_name="basic-service-protection", policy_name="basic-service-protection"}[1800s]))
- arithmetic_combinator:
in_ports:
lhs:
signal_name: SIGNAL_LONG_TERM
rhs:
constant_signal:
value: 1.1
operator: mul
out_ports:
output:
signal_name: SETPOINT
evaluation_interval: 10s
resources:
flow_control:
classifiers: []
flux_meters:
basic-service-protection:
selectors:
- control_point: ingress
service: cart-service.prod.svc.cluster.local
infra_meters: {}
Circuit Diagram for this policy.
Installation
Generate a values file specific to the policy. This can be achieved using the command provided below.
aperturectl blueprints values --name=load-scheduling/average-latency --version=main --output-file=values.yaml
Adjust the values to match the application requirements. Use the following command to generate the policy.
aperturectl blueprints generate --name=load-scheduling/average-latency --values-file=values.yaml --output-dir=policy-gen --version=main
Apply the policy using the aperturectl
CLI or kubectl
.
- aperturectl
- kubectl
Pass the --kube
flag with aperturectl
to directly apply the generated policy
on a Kubernetes cluster in the namespace where the Aperture Controller is
installed.
aperturectl apply policy --file=policy-gen/policies/load-scheduling.yaml --kube
Apply the policy YAML generated (Kubernetes Custom Resource) using the above
example with kubectl
.
kubectl apply -f policy-gen/configuration/load-scheduling-cr.yaml -n aperture-controller
Policy in Action
To see the policy in action, the traffic is generated such that it starts within
the service's capacity and then goes beyond the capacity after some time. Such a
traffic pattern is repeated periodically. The below dashboard demonstrates that
when latency spikes due to high traffic at
cart-service.prod.svc.cluster.local
, the controller throttles the rate of
requests admitted into the service. This approach helps protect the service from
becoming unresponsive and maintains the current latency within the tolerance
limit (1.1
) of historical latency.
Dry Run Mode
You can run this policy in the Dry Run
mode by setting the
policy.service_protection_core.dry_run
parameter to true
. In the Dry Run
mode, the policy does not throttle the request rate while still evaluating the
decisions it would take in each cycle. This is useful for evaluating the policy
without impacting the service.
The Dry Run
mode can also be toggled dynamically at runtime, without reloading
the policy.
Demo Video
The below demo video shows the basic service protection and workload prioritization policy in action within Aperture Playground.