Add relay-wide event ingest limiter

This commit is contained in:
2026-03-18 13:42:56 +01:00
parent 57fdb4ed85
commit 1fef184f50
9 changed files with 181 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ defmodule Parrhesia.ApplicationTest do
assert is_pid(Process.whereis(Parrhesia.Supervisor))
assert is_pid(Process.whereis(Parrhesia.Telemetry))
assert is_pid(Process.whereis(Parrhesia.Config))
assert is_pid(Process.whereis(Parrhesia.Web.EventIngestLimiter))
assert is_pid(Process.whereis(Parrhesia.Storage.Supervisor))
assert is_pid(Process.whereis(Parrhesia.Subscriptions.Supervisor))
assert is_pid(Process.whereis(Parrhesia.Auth.Supervisor))

View File

@@ -8,6 +8,8 @@ defmodule Parrhesia.ConfigTest do
assert Parrhesia.Config.get([:limits, :max_event_ingest_per_window]) == 120
assert Parrhesia.Config.get([:limits, :max_tags_per_event]) == 256
assert Parrhesia.Config.get([:limits, :max_tag_values_per_filter]) == 128
assert Parrhesia.Config.get([:limits, :relay_max_event_ingest_per_window]) == 10_000
assert Parrhesia.Config.get([:limits, :relay_event_ingest_window_seconds]) == 1
assert Parrhesia.Config.get([:limits, :event_ingest_window_seconds]) == 1
assert Parrhesia.Config.get([:limits, :auth_max_age_seconds]) == 600
assert Parrhesia.Config.get([:limits, :max_outbound_queue]) == 256

View File

@@ -424,6 +424,42 @@ defmodule Parrhesia.Web.ConnectionTest do
]
end
test "EVENT ingest enforces relay-wide rate limits" do
limiter =
start_supervised!(
{Parrhesia.Web.EventIngestLimiter,
name: nil, max_events_per_window: 1, window_seconds: 60}
)
state =
connection_state(
event_ingest_limiter: limiter,
max_event_ingest_per_window: 10,
event_ingest_window_seconds: 60
)
first_event = valid_event(%{"content" => "first"})
second_event = valid_event(%{"content" => "second"})
assert {:push, {:text, first_response}, next_state} =
Connection.handle_in({JSON.encode!(["EVENT", first_event]), [opcode: :text]}, state)
assert JSON.decode!(first_response) == ["OK", first_event["id"], true, "ok: event stored"]
assert {:push, {:text, second_response}, ^next_state} =
Connection.handle_in(
{JSON.encode!(["EVENT", second_event]), [opcode: :text]},
next_state
)
assert JSON.decode!(second_response) == [
"OK",
second_event["id"],
false,
"rate-limited: relay-wide EVENT ingress exceeded"
]
end
test "EVENT ingest enforces max event bytes" do
state = connection_state(max_event_bytes: 128)

View File

@@ -0,0 +1,16 @@
defmodule Parrhesia.Web.EventIngestLimiterTest do
use ExUnit.Case, async: true
alias Parrhesia.Web.EventIngestLimiter
test "allows events up to the configured relay-wide window cap" do
limiter =
start_supervised!(
{EventIngestLimiter, name: nil, max_events_per_window: 2, window_seconds: 60}
)
assert :ok = EventIngestLimiter.allow(limiter)
assert :ok = EventIngestLimiter.allow(limiter)
assert {:error, :relay_event_rate_limited} = EventIngestLimiter.allow(limiter)
end
end