{"record":{"id":"3c06683166afe385","repo":"instructure/canvas-lms","slug":"max-number-of-clients-reached","errorCode":null,"errorMessage":"max number of clients reached","messagePattern":"max number of clients reached","errorType":"exception","errorClass":"MaxNumberOfClientsReachedError","httpStatus":null,"severity":"critical","filePath":"gems/canvas_cache/lib/redis_client/max_clients.rb","lineNumber":40,"sourceCode":"  MaxNumberOfClientsReachedError = Class.new(ConnectionError)\n\n  module MaxClients\n    def connect(*)\n      protect_from_max_clients { super }\n    end\n\n    def call(*)\n      protect_from_max_clients { super }\n    end\n\n    def call_pipelined(*)\n      protect_from_max_clients { super }\n    end\n\n    def protect_from_max_clients\n      yield\n    rescue CommandError => e\n      raise MaxNumberOfClientsReachedError, e.message if e.message.include?(\"max number of clients reached\")\n\n      raise\n    end\n  end\nend\n","sourceCodeStart":22,"sourceCodeEnd":46,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/canvas_cache/lib/redis_client/max_clients.rb#L22-L46","documentation":"canvas_cache wraps redis-client operations so that redis's 'max number of clients reached' CommandError is translated into a MaxNumberOfClientsReachedError with the original message. Redis itself emits the error when the server's maxclients limit is hit and new connections are refused. The wrapper only re-raises when the message contains the exact phrase.","triggerScenarios":"Any Redis command or connection attempt (connect, call, call_pipelined) while the Redis server has hit its maxclients limit; the underlying CommandError message includes 'max number of clients reached'.","commonSituations":"Connection leaks in an app (clients never returned to pool) pushing the server to maxclients; multiple app servers/shards sharing a small maxclients setting; stuck client connections from long-running jobs.","solutions":["Increase Redis server maxclients (redis.conf) and/or the OS file-descriptor limit (ulimit -n)","Fix connection leaks: ensure Redis clients/pools are reused and connections are released","Restart or CLIENT KILL stale connections to drop the current client count below maxclients","Scale horizontally with Redis Cluster or split workloads across instances; add retry/backoff on MaxNumberOfClientsReachedError"],"exampleFix":"// before\nredis.set(key, value) # raw, no retry\n// after\nbegin\n  redis.set(key, value)\nrescue CanvasCache::Redis::MaxNumberOfClientsReachedError\n  sleep_with_backoff && retry\nend","handlingStrategy":"retry","validationCode":"begin\n  info = redis.client.call('INFO', 'clients')\n  connected = info[/connected_clients:(\\d+)/, 1].to_i\n  at_capacity = connected >= max_clients_threshold\nrescue StandardError\n  at_capacity = false\nend","typeGuard":null,"tryCatchPattern":"begin\n  redis.call('GET', key)\nrescue CanvasCache::Redis::MaxNumberOfClientsReachedError\n  sleep(backoff *= 2)\n  retry\nend","preventionTips":["Use connection pools and always return clients","Monitor connected_clients vs maxclients","Set generous OS file-descriptor limits","Add retry with exponential backoff around Redis ops"],"tags":["redis","capacity","connection-limit"],"backgroundTag":"connection-refused","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}