diff --git a/internal/infrastructure/db/migrations/000041_rate_limits_100rps.down.sql b/internal/infrastructure/db/migrations/000041_rate_limits_100rps.down.sql new file mode 100644 index 00000000..7ae28cb2 --- /dev/null +++ b/internal/infrastructure/db/migrations/000041_rate_limits_100rps.down.sql @@ -0,0 +1,16 @@ +ALTER TABLE user_rate_limits ALTER COLUMN limit_read_pm SET DEFAULT 300; +ALTER TABLE user_rate_limits ALTER COLUMN limit_write_pm SET DEFAULT 60; +ALTER TABLE user_rate_limits ALTER COLUMN limit_bulk_pm SET DEFAULT 10; +ALTER TABLE user_rate_limits ALTER COLUMN limit_unibox_pm SET DEFAULT 120; +ALTER TABLE user_rate_limits ALTER COLUMN limit_analytics_pm SET DEFAULT 60; +ALTER TABLE user_rate_limits ALTER COLUMN limit_api_calls_daily SET DEFAULT 50000; +ALTER TABLE user_rate_limits ALTER COLUMN burst_multiplier SET DEFAULT 1.5; + +ALTER TABLE plan_rate_limits ALTER COLUMN limit_read_pm SET DEFAULT 300; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_write_pm SET DEFAULT 60; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_bulk_pm SET DEFAULT 10; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_unibox_pm SET DEFAULT 120; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_analytics_pm SET DEFAULT 60; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_analytics_pm SET DEFAULT 60; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_api_calls_daily SET DEFAULT 50000; +ALTER TABLE plan_rate_limits ALTER COLUMN burst_multiplier SET DEFAULT 1.5; diff --git a/internal/infrastructure/db/migrations/000041_rate_limits_100rps.up.sql b/internal/infrastructure/db/migrations/000041_rate_limits_100rps.up.sql new file mode 100644 index 00000000..3c6d47bb --- /dev/null +++ b/internal/infrastructure/db/migrations/000041_rate_limits_100rps.up.sql @@ -0,0 +1,56 @@ +-- Raise per-user and per-plan API rate limits so paid customers can sustain +-- ~100 req/s without bumping into category caps. 100 req/s = 6000 req/min. +-- Existing rows are bumped only when they still match the original defaults +-- so any admin-customized values stay intact. + +ALTER TABLE user_rate_limits ALTER COLUMN limit_read_pm SET DEFAULT 6000; +ALTER TABLE user_rate_limits ALTER COLUMN limit_write_pm SET DEFAULT 6000; +ALTER TABLE user_rate_limits ALTER COLUMN limit_bulk_pm SET DEFAULT 600; +ALTER TABLE user_rate_limits ALTER COLUMN limit_unibox_pm SET DEFAULT 1200; +ALTER TABLE user_rate_limits ALTER COLUMN limit_analytics_pm SET DEFAULT 600; +ALTER TABLE user_rate_limits ALTER COLUMN limit_api_calls_daily SET DEFAULT 500000; +ALTER TABLE user_rate_limits ALTER COLUMN burst_multiplier SET DEFAULT 2.0; + +ALTER TABLE plan_rate_limits ALTER COLUMN limit_read_pm SET DEFAULT 6000; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_write_pm SET DEFAULT 6000; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_bulk_pm SET DEFAULT 600; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_unibox_pm SET DEFAULT 1200; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_analytics_pm SET DEFAULT 600; +ALTER TABLE plan_rate_limits ALTER COLUMN limit_api_calls_daily SET DEFAULT 500000; +ALTER TABLE plan_rate_limits ALTER COLUMN burst_multiplier SET DEFAULT 2.0; + +-- Bump rows that still hold the pre-existing defaults. Anything custom is +-- left alone so admin-tuned accounts keep their overrides. +UPDATE user_rate_limits +SET limit_read_pm = 6000, + limit_write_pm = 6000, + limit_bulk_pm = 600, + limit_unibox_pm = 1200, + limit_analytics_pm = 600, + limit_api_calls_daily = 500000, + burst_multiplier = 2.0, + updated_at = NOW() +WHERE limit_read_pm = 300 + AND limit_write_pm = 60 + AND limit_bulk_pm = 10 + AND limit_unibox_pm = 120 + AND limit_analytics_pm = 60 + AND limit_api_calls_daily = 50000 + AND burst_multiplier = 1.5; + +UPDATE plan_rate_limits +SET limit_read_pm = 6000, + limit_write_pm = 6000, + limit_bulk_pm = 600, + limit_unibox_pm = 1200, + limit_analytics_pm = 600, + limit_api_calls_daily = 500000, + burst_multiplier = 2.0, + updated_at = NOW() +WHERE limit_read_pm = 300 + AND limit_write_pm = 60 + AND limit_bulk_pm = 10 + AND limit_unibox_pm = 120 + AND limit_analytics_pm = 60 + AND limit_api_calls_daily = 50000 + AND burst_multiplier = 1.5; diff --git a/internal/models/rate_limit.go b/internal/models/rate_limit.go index 625414aa..b2b0ada5 100644 --- a/internal/models/rate_limit.go +++ b/internal/models/rate_limit.go @@ -89,21 +89,25 @@ type RateLimitStatus struct { RetryAfterMs *int64 `json:"retry_after_ms,omitempty"` } -// DefaultRateLimits returns the default rate limits +// DefaultRateLimits returns the default rate limits. Sized to allow paid +// customers to sustain ~100 req/s on read and write categories, matching +// the API throughput competitors (Instantly) advertise as of mid-2026. +// 100 req/s = 6000 req/min. BurstMultiplier=2.0 lets brief spikes hit +// 200 req/s before throttling kicks in. func DefaultRateLimits() *UserRateLimits { return &UserRateLimits{ - LimitReadPM: 300, - LimitWritePM: 60, - LimitBulkPM: 10, - LimitUniboxPM: 120, - LimitAnalyticsPM: 60, - LimitAPICallsDaily: 50000, - LimitBulkOpsDaily: 100, + LimitReadPM: 6000, + LimitWritePM: 6000, + LimitBulkPM: 600, + LimitUniboxPM: 1200, + LimitAnalyticsPM: 600, + LimitAPICallsDaily: 500000, + LimitBulkOpsDaily: 1000, LimitWSMessagePM: 120, LimitWSJoinPM: 30, LimitWSEventPM: 60, MaxConnections: 10, - BurstMultiplier: 1.5, + BurstMultiplier: 2.0, } }