Browse Source

Add server-side query result limit to prevent memory exhaustion (v0.52.11)

- Add ORLY_QUERY_RESULT_LIMIT config (default 256)
- Enforce limit on each REQ filter before database query
- Caps client limit if it exceeds server limit
- Applies server limit if client sends no limit

This prevents unbounded queries from loading millions of events
into memory and causing OOM kills.

Files modified:
- app/config/config.go: Add QueryResultLimit config
- app/handle-req.go: Enforce limit on each filter
- pkg/version/version: Bump to v0.52.11

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
main v0.52.11
woikos 4 months ago
parent
commit
779f341dda
No known key found for this signature in database
  1. 3
      app/config/config.go
  2. 19
      app/handle-req.go
  3. 2
      pkg/version/version

3
app/config/config.go

@ -148,6 +148,9 @@ type C struct {
MaxHandlersPerConnection int `env:"ORLY_MAX_HANDLERS_PER_CONN" default:"100" usage:"max concurrent message handlers per WebSocket connection (limits goroutine growth under load)"` MaxHandlersPerConnection int `env:"ORLY_MAX_HANDLERS_PER_CONN" default:"100" usage:"max concurrent message handlers per WebSocket connection (limits goroutine growth under load)"`
MaxConnectionsPerIP int `env:"ORLY_MAX_CONN_PER_IP" default:"25" usage:"max WebSocket connections per IP address (prevents resource exhaustion, hard limit 40)"` MaxConnectionsPerIP int `env:"ORLY_MAX_CONN_PER_IP" default:"25" usage:"max WebSocket connections per IP address (prevents resource exhaustion, hard limit 40)"`
// Query result limits (prevents memory exhaustion from unbounded queries)
QueryResultLimit int `env:"ORLY_QUERY_RESULT_LIMIT" default:"256" usage:"max events returned per REQ filter (prevents unbounded memory usage, 0=unlimited)"`
// Adaptive rate limiting (PID-controlled) // Adaptive rate limiting (PID-controlled)
RateLimitEnabled bool `env:"ORLY_RATE_LIMIT_ENABLED" default:"true" usage:"enable adaptive PID-controlled rate limiting for database operations"` RateLimitEnabled bool `env:"ORLY_RATE_LIMIT_ENABLED" default:"true" usage:"enable adaptive PID-controlled rate limiting for database operations"`
RateLimitTargetMB int `env:"ORLY_RATE_LIMIT_TARGET_MB" default:"0" usage:"target memory limit in MB (0=auto-detect: 66% of available, min 500MB)"` RateLimitTargetMB int `env:"ORLY_RATE_LIMIT_TARGET_MB" default:"0" usage:"target memory limit in MB (0=auto-detect: 66% of available, min 500MB)"`

19
app/handle-req.go

@ -316,8 +316,27 @@ func (l *Listener) HandleReq(msg []byte) (err error) {
// Collect all events from all filters // Collect all events from all filters
var allEvents event.S var allEvents event.S
// Server-side query result limit to prevent memory exhaustion
serverLimit := l.Config.QueryResultLimit
if serverLimit <= 0 {
serverLimit = 256 // Default if not configured
}
for _, f := range *env.Filters { for _, f := range *env.Filters {
if f != nil { if f != nil {
// Enforce server-side limit on each filter
if serverLimit > 0 {
if f.Limit == nil {
// No client limit - apply server limit
limitVal := uint(serverLimit)
f.Limit = &limitVal
} else if int(*f.Limit) > serverLimit {
// Client limit exceeds server limit - cap it
limitVal := uint(serverLimit)
f.Limit = &limitVal
}
}
// Summarize filter details for diagnostics (avoid internal fields) // Summarize filter details for diagnostics (avoid internal fields)
var kindsLen int var kindsLen int
if f.Kinds != nil { if f.Kinds != nil {

2
pkg/version/version

@ -1 +1 @@
v0.52.10 v0.52.11

Loading…
Cancel
Save