Skip to content

Commit 29990fd

Browse files
Add rate limiter to helm agent fetches
Pretty sure these can get overwhelmed at large scale, and a simple rate limit should improve the load shedding here.
1 parent 81db3cf commit 29990fd

12 files changed

Lines changed: 101 additions & 12 deletions

File tree

charts/console/templates/deployment.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ metadata:
88
{{ include "console.labels" . | indent 4 }}
99
spec:
1010
replicas: {{ .Values.replicaCount }}
11+
{{- if .Values.strategy }}
12+
strategy:
13+
{{- toYaml .Values.strategy | nindent 4 }}
14+
{{- end }}
1115
selector:
1216
matchLabels:
1317
app.kubernetes.io/name: console

charts/console/templates/migration.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ spec:
4343
name: console-migration-env
4444
env:
4545
{{ include "console.env" . | nindent 8 }}
46+
- name: DB_POOL_TARGET
47+
value: '1000'
4648
{{- if .Values.extraEnv }}
4749
{{- toYaml .Values.extraEnv | nindent 8 }}
4850
{{- end }}

charts/console/templates/secrets.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ data:
5858
{{ if .Values.console.config.tarballQps }}
5959
CONSOLE_TARBALL_QPS: {{ .Values.console.config.tarballQps | toString | b64enc | quote }}
6060
{{ end }}
61+
{{ if .Values.console.config.cacheAgentQps }}
62+
CONSOLE_CACHE_AGENT_QPS: {{ .Values.console.config.cacheAgentQps | toString | b64enc | quote }}
63+
{{ end }}
64+
{{ if .Values.console.config.cacheAgentQueueLimit }}
65+
CONSOLE_CACHE_AGENT_QUEUE_LIMIT: {{ .Values.console.config.cacheAgentQueueLimit | toString | b64enc | quote }}
66+
{{ end }}
6167
{{ if .Values.console.config.dbPoolSize }}
6268
DB_POOL_SIZE: {{ .Values.console.config.dbPoolSize | toString | b64enc | quote }}
6369
{{ end }}

charts/console/values.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ disableAdditionalVolumes: false
3232
# replicaCount is the replica count for console deployment
3333
replicaCount: 2
3434

35+
# strategy configures the main console Deployment strategy.
36+
# For rolling updates, set type to RollingUpdate and tune rollingUpdate.maxSurge/maxUnavailable.
37+
strategy: {}
38+
3539
# homeDir is the prefix for the mount path for console-conf volume in console container. The actual config will go in {homeDir}/.plural
3640
homeDir: /home/console
3741

@@ -72,6 +76,12 @@ console:
7276
# tarballQps configures the global QPS limit for digest and git tarball endpoints.
7377
tarballQps: 100
7478

79+
# cacheAgentQps configures the global QPS limit for pod-local cache agents.
80+
cacheAgentQps: ~
81+
82+
# cacheAgentQueueLimit configures the global queue limit for pod-local cache agents.
83+
cacheAgentQueueLimit: ~
84+
7585
# dbPoolSize configures the Ecto database connection pool size.
7686
dbPoolSize: 50
7787

config/config.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ config :console,
6565
kas_dns: "https://kas.example.com",
6666
qps: 1_000,
6767
tarball_qps: 100,
68+
cache_agent_qps: 50,
69+
cache_agent_queue_limit: 10,
70+
cache_agent_queue_shed: 5,
6871
nowatchers: false,
6972
default_project_name: "default",
7073
prom_plugins: [Console.Prom.Plugin],
@@ -122,7 +125,6 @@ config :console, Console.Guardian,
122125

123126
config :console, Console.Repo,
124127
types: Console.PostgrexTypes,
125-
queue_target: 1000,
126128
migration_timestamps: [type: :utc_datetime_usec]
127129

128130
config :tzdata, :autoupdate, :disabled

lib/console/deployments/git/agent.ex

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ defmodule Console.Deployments.Git.Agent do
1818

1919
@poll :timer.seconds(120)
2020
@timeout :timer.seconds(10)
21-
@limit 50
2221
@limit_interval :timer.seconds(1)
2322

2423
defmodule State, do: defstruct [:git, :cache, :last_pull, :url]
@@ -269,15 +268,40 @@ defmodule Console.Deployments.Git.Agent do
269268

270269
defp touch(pid, line), do: send(pid, {:touch, line})
271270

272-
defp rate_limited(key, fun) when is_function(fun, 0) do
273-
:erlang.term_to_binary(key)
274-
|> Hammer.check_rate(@limit_interval, @limit)
275-
|> case do
276-
{:allow, _} -> fun.()
277-
{:deny, _} -> {:error, :rate_limited}
271+
@doc false
272+
def rate_limited({_, pid} = key, fun) when is_function(fun, 0) and is_pid(pid) do
273+
hammer_key = :erlang.term_to_binary(key)
274+
with {:allow, _} <- Hammer.check_rate(hammer_key, @limit_interval, cache_agent_qps()),
275+
{:q, :ok} <- {:q, queue_limit(key, pid)} do
276+
fun.()
277+
else
278+
{:deny, _} ->
279+
Logger.warning "rate limiting git/helm agent fetch"
280+
{:error, :rate_limited}
281+
{:q, _} ->
282+
Logger.warning "rate limiting git/helm agent fetch due to message queue length"
283+
{:error, :rate_limited}
278284
end
279285
end
280286

287+
defp queue_limit(key, pid) when is_pid(pid) do
288+
lim = cache_agent_queue_limit()
289+
shed = cache_agent_queue_shed()
290+
case {:q, Process.info(pid, :message_queue_len)} do
291+
{:q, {_, ^lim}} ->
292+
case Hammer.check_rate(:erlang.term_to_binary({:shed, key}), @limit_interval, shed) do
293+
{:allow, _} -> :ok
294+
{:deny, _} -> :error
295+
end
296+
{:q, {_, len}} when len < lim -> :ok
297+
_ -> :error
298+
end
299+
end
300+
301+
defp cache_agent_queue_limit(), do: Console.conf(:cache_agent_queue_limit)
302+
defp cache_agent_queue_shed(), do: Console.conf(:cache_agent_queue_shed)
303+
defp cache_agent_qps(), do: Console.conf(:cache_agent_qps)
304+
281305
defp refresh(%GitRepository{health: :pullable} = git, cache), do: Cache.refresh(%{cache | git: git})
282306
defp refresh(_, cache), do: cache
283307

lib/console/deployments/helm/agent.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
defmodule Console.Deployments.Helm.Agent do
22
use GenServer, restart: :temporary
3+
import Console.Deployments.Git.Agent, only: [rate_limited: 2]
34
alias Console.Repo
45
alias Console.Deployments.Git
56
alias Console.Deployments.Helm.{AgentCache, Discovery, Supervisor}
@@ -21,7 +22,8 @@ defmodule Console.Deployments.Helm.Agent do
2122
_ <- touch(pid, line) do
2223
{:ok, opener(pid, f), d, i}
2324
else
24-
_ -> GenServer.call(pid, {:fetch, chart, vsn}, @timeout)
25+
_ ->
26+
rate_limited({:helm, pid}, fn -> GenServer.call(pid, {:fetch, chart, vsn}, @timeout) end)
2527
end
2628
end
2729

@@ -32,7 +34,8 @@ defmodule Console.Deployments.Helm.Agent do
3234
_ <- touch(pid, line) do
3335
{:ok, d}
3436
else
35-
_ -> GenServer.call(pid, {:digest, chart, vsn}, @timeout)
37+
_ ->
38+
rate_limited({:helm, pid}, fn -> GenServer.call(pid, {:digest, chart, vsn}, @timeout) end)
3639
end
3740
end
3841

lib/console/pipelines/ai/cluster/pipeline.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule Console.Pipelines.AI.Cluster.Pipeline do
2-
use Console.Pipelines.Consumer
2+
use Console.Pipelines.Consumer, demand: 10
33
import Console.Pipelines.AI.Base
44
alias Console.PubSub
55

lib/console/pipelines/ai/service/pipeline.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule Console.Pipelines.AI.Service.Pipeline do
2-
use Console.Pipelines.Consumer
2+
use Console.Pipelines.Consumer, demand: 20
33
import Console.Pipelines.AI.Base
44
alias Console.PubSub
55

lib/console_web/exceptions.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
defimpl Plug.Exception, for: DBConnection.ConnectionError do
2+
def status(%{reason: :queue_timeout}), do: 429
3+
def status(_), do: 500
4+
5+
def actions(_), do: []
6+
end

0 commit comments

Comments
 (0)