{"id":"IL-0353","org":"Fly.io","date":"2026-05-28","docs":[],"title":{"en":"Fly.io edge proxies in US west coast regions were overloaded and flapping, with lazy loader delays of seconds","ko":"미국 서부 에지의 fly-proxy 가 과부하에 빠져 요청이 수백 밀리초에서 몇 초씩 밀렸다"},"domain":["networking"],"impact":{"scope":"Requests through fly-proxy on US west coast edges were delayed while the edges flapped; edges in other regions were briefly affected when traffic was moved out of the west coast","users":"unknown","region":"US west coast (SJC, LAX)"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":null,"related":[],"sources":[{"url":"https://fly.io/infra-log/edge-proxies-overload/","kind":"blog","archived":null}],"summary":{"en":"Fly.io's edges run fly-proxy, which since an earlier change loads only the Corrosion entries that active requests need through a lazy loader, and Airtime, a per-process bandwidth limit tuned to trigger near the saturation point of an edge server. On 28 May 2026 edges in the US west coast, first SJC and later LAX, were overloaded: lazy loader queries took from 500 ms to seconds, and moving Anycast traffic from SJC to LAX, and at times off the west coast, only moved the problem. Lowering Airtime's limit reduced softirq CPU and load, and bouncing the proxy, with a hard kill of all proxy processes pending shutdown, cleared the lazy loader backlog and ended the first acute phase.\n\nThe overload returned because a bug in how the proxy deployment script works with systemd had left several proxy instances running on some nodes: systemctl kill does not move a unit to a stopped state, and with Restart=always the process simply restarts. Each instance enforced Airtime alone, so the effective limit could double. Fly.io names four causes together: edge capacity in some regions had not kept up with user growth, Airtime's tuning no longer matched real traffic or other proxy scaling limits, a bug let multiple active proxy instances coexist without code for shared state, and the lazy loader queued without bound under high load. Fly.io also observed high queuing delays in inserting data into the proxy's in-memory service catalog behind a single coarse-grained lock, which it believes no longer scales with growth.\n\nFly.io fixed the bug that let proxy instances coexist, made Airtime apply a much stricter limit when it is not the expected active instance, and fixed load shedding in the lazy loader to bound request latency. It plans much more edge capacity, a sharded service catalog with finer-grained locking tested with Antithesis, an Airtime that reacts to overall system load, and monitoring for proxies not in their expected configuration.","ko":"fly-proxy 는 Fly.io 가 Rust 로 만든 사용자 공간 L4/L7 로드 밸런서다. Corrosion 은 서비스 디스커버리와 상태 전파를 맡는 Fly.io 의 분산 시스템이다. fly-proxy 는 예전에는 Corrosion 의 데이터를 거의 전부 프로세스 메모리에 올렸지만, 확장이 잘 되지 않아 처리 중인 요청에 필요한 항목만 읽어 오는 lazy loader 방식으로 바꿨다. Airtime 은 앱 하나가 호스트의 대역폭을 독차지하지 못하게 fly-proxy 에 넣은 트래픽 급증 방어 장치다. Fly.io 는 그해 초 Airtime 이 에지 서버 한 대의 대역폭 포화 지점 근처에서 작동하도록 값을 조정했다.\n\n5월 28일 미국 서부 리전에서, 처음에는 주로 SJC 에서 에지가 흔들렸고 lazy loader 지연이 500 ms 에서 몇 초까지 올라갔다. Fly.io 는 Corrosion 에 상태가 아주 큰 앱을 원인으로 보고 임시 완화책과 함께 SJC 에 프록시를 배포했지만, 새 프로세스가 워밍업을 마치자 지연이 다시 올랐다. 서버 몇 대가 빠져 용량이 모자란 SJC 의 Anycast 트래픽을 LAX 로 옮겼지만 LAX 도 곧 버거워했다. Airtime 대역폭 한도를 낮추자 softirq CPU 사용률과 load average 가 내려갔고, 프록시를 재시작하자 lazy loader 문제도 풀려 첫 고비가 끝났다.\n\n그 뒤에도 장애는 되풀이됐다. Airtime 은 프로세스 하나 안에서만 동작하고 자기가 아는 것을 밖으로 전하지 않는다. 그런데 프록시 배포 스크립트가 systemd 와 맞물리는 방식의 버그로 일부 노드에 프록시 인스턴스가 여럿 계속 떠 있었다. systemctl kill 은 유닛을 멈춘 상태로 바꾸지 않고, Restart=always 와 겹치면 프로세스를 다시 띄울 뿐이었다. 그래서 무거운 연결이 다른 프록시 인스턴스로 가면 Airtime 한도가 사실상 두 배가 되어 같은 문제가 다시 났다. Airtime 을 조정한 뒤 프록시를 재시작해야 했던 것도 다른 문제를 드러냈다. 높은 부하에서 softirq 가 사용자 공간과 CPU 를 다투면 lazy loader 가 느려졌다. 이렇게 느려지면 sqlite 연결을 기다리는 대기열이 사실상 끝없이 길어졌고, 프로세스를 재시작해야만 풀렸다.\n\nFly.io 는 원인을 넷으로 정리했다. 일부 리전의 에지 용량이 최근의 사용자 증가를 따라가지 못했다. Airtime 의 조정 값이 트래픽 패턴의 변화나 대역폭이 아닌 프록시의 다른 확장 문제로 현실과 맞지 않게 됐다. 버그로 함께 떠 있는 프록시 인스턴스들에는 공유 상태를 다루는 코드가 없었다. lazy loader 는 높은 부하에서 대기열이 걷잡을 수 없이 늘었다. Fly.io 는 프록시가 전보다 일찍 lazy loader 성능 문제에 부딪히는 이유도 찾았다. 프록시 메모리 안 서비스 카탈로그에 걸린 단위가 큰 락 하나가 성장을 따라가지 못했다. 큰 대기 지연은 sqlite 질의가 아니라 이 카탈로그에 데이터를 넣는 데서 보였다."},"trigger":null,"severity":"unknown","symptoms":["high-latency"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"Fly.io noticed flapping in its US west coast regions, and proxy logs and metrics showed high lazy loader latency.","ko":"Fly.io 는 미국 서부 리전에서, 처음에는 주로 SJC 에서 에지가 흔들리는 것을 알아챘다. 로그와 지표에서 lazy loader 지연이 500 ms 에서 몇 초까지 높게 나왔다."},"components":["cdn","service-discovery"],"root_cause":"capacity","remediation":[{"text":{"en":"Deployed the proxy in SJC with a temporary mitigation for one app with very large state in Corrosion, which helped only briefly","ko":"Corrosion 에 상태가 아주 큰 앱을 원인으로 보고 임시 완화책을 넣은 뒤 SJC 에 프록시를 배포했다. 잠시 나아졌지만 새 프로세스가 워밍업을 마치자 지연이 다시 치솟았다."}},{"text":{"en":"Shifted Anycast traffic from SJC to LAX","ko":"서버 몇 대가 빠져 용량이 모자란 SJC 에서 Anycast 트래픽을 LAX 로 옮겼다. 처음에는 나아졌지만 얼마 뒤 LAX 도 버거워했다."}},{"text":{"en":"Lowered Airtime's bandwidth limit, which reduced softirq CPU usage and host load average","ko":"Airtime 의 대역폭 한도를 낮췄다. softirq CPU 사용률과 호스트 load average 는 내려갔지만 lazy loader 질의는 계속 느렸다."}},{"text":{"en":"Bounced the proxy and hard-killed all proxy processes pending shutdown, which cleared the lazy loader problem","ko":"프록시를 재시작하면서 종료를 기다리던 프록시 프로세스를 모두 강제로 죽여 lazy loader 문제를 풀었다. 이것으로 첫 고비가 끝났다."}}],"action_items":[{"text":{"en":"Provision significantly more edge capacity","ko":"앞으로 몇 주에서 몇 달 사이에 에지 용량을 크게 늘린다."}},{"text":{"en":"Fixed the bug that let multiple proxy instances coexist","ko":"프록시 인스턴스가 여럿 함께 떠 있게 만든 버그를 고쳤다."}},{"text":{"en":"Changed Airtime so that, for now, it applies a much stricter limit when it is not the expected active proxy instance","ko":"Airtime 이 돌고 있는 프록시가 기대한 활성 인스턴스가 아닐 때는 당분간 훨씬 엄격한 한도를 쓰도록 바꿨다."}},{"text":{"en":"Fixed load shedding in the lazy loader so request latency has a reasonable upper bound","ko":"lazy loader 의 로드 셰딩 동작을 고쳐 요청 처리 지연의 최대값에 합리적인 상한이 생기게 했다."}},{"text":{"en":"Shard the in-memory service catalog and move to finer-grained locking, tested with Antithesis","ko":"프록시 메모리 안 서비스 카탈로그를 샤드로 나누고 더 잘게 나눈 락으로 바꾼다. 이 전환이 장애를 더 내지 않도록 Antithesis 같은 테스트를 함께 쓴다."}},{"text":{"en":"Rework Airtime to react to overall system load instead of only the proxy","ko":"Airtime 이 프록시만이 아니라 시스템 전체 부하에 맞춰 반응하도록 다시 만든다. 프록시 프로세스가 여럿 떠 있거나 같은 호스트의 다른 프로세스가 대역폭 여유를 쓸 때도 안전장치가 되게 한다."}},{"text":{"en":"Look into better monitoring for when the proxy is not in its expected configuration","ko":"프록시가 기대한 구성으로 돌지 않을 때를 더 잘 감시하는 방법을 찾는다."}}],"contributing":["no-capacity-planning","limit-unknown","overlapping-failures"],"recurrence_of":null,"symptoms_text":{"en":"On 28 May 2026, Fly.io's edges in US west coast regions began flapping, first mainly in SJC. Logs and metrics of fly-proxy, the userspace L4 and L7 load balancer on the edges, showed lazy loader latency of 500 ms to several seconds, so many new requests waited that long or longer, while proxy CPU usage and the inbound connection rate were not especially high.\n\nEngineers first blamed one app with very large state in Corrosion, the service discovery and state propagation system the lazy loader reads from, and deployed the proxy in SJC with a temporary mitigation for it. Latency improved for a moment and then climbed sharply again once the new proxy processes warmed up. Inefficient sqlite queries were ruled out, and lock contention from higher connection rates after recent growth was considered. Airtime, the proxy's built-in defence against traffic spikes, reported higher bandwidth in SJC, but below what had been taken as the ceiling for one edge server.\n\nBecause SJC was short of edge capacity with a couple of servers out of production, Anycast traffic was shifted to LAX, which helped at first until LAX began to struggle too. At some points traffic was moved off the west coast entirely, which briefly affected edges in other regions. Lowering Airtime's bandwidth limit brought softirq CPU usage and host load average down, but lazy loader queries stayed slow until the proxy was bounced.\n\nThe problem then came back. Some affected nodes had several fly-proxy instances running at once, each with its own Airtime state that it did not share, so any Airtime limit could in effect double when heavy connections landed on another instance. The lazy loader could also build effectively endless queues waiting on sqlite connections when it was slow, and those queues cleared only when the process was bounced.","ko":"미국 서부 리전에서, 처음에는 주로 SJC 에서 에지가 반복해서 흔들렸다. 로그와 지표에서는 fly-proxy 가 처리 중인 요청에 필요한 항목만 읽어 오는 lazy loader 의 지연이 500 ms 에서 몇 초까지 높게 나왔다. 새 요청도 그만큼 더 기다린 뒤에야 처리됐다.\n\n한편 fly-proxy 의 CPU 사용률은 그리 높지 않았고, 들어오는 연결 속도도 높지 않았다. Fly.io 는 이런 모습을 전에도 봤는데, 보통은 비효율적인 sqlite 질의, 서비스 디스커버리 시스템인 Corrosion 에 지나치게 큰 상태를 둔 앱, 호스트 전반의 성능 문제 가운데 하나였다.\n\nFly.io 는 Corrosion 에 상태가 아주 큰 앱 하나를 찾아 원인으로 보고 임시 완화책을 넣은 뒤 SJC 에 프록시를 배포했다. 잠시 나아지는 듯했지만 새 프록시 프로세스가 워밍업을 마치자 지연이 다시 치솟았다. sqlite 질의 문제는 배제했고, 최근 성장으로 연결이 늘어 락 경합이 생긴 것인지 의심했다.\n\n이때 fly-proxy 의 트래픽 급증 방어 장치인 Airtime 이 SJC 의 대역폭이 늘었다고 보고했지만, 에지 서버 한 대가 감당할 수 있다고 앞서 정한 한도보다는 낮았다. 서버 몇 대가 운영에서 빠져 SJC 의 에지 용량이 모자랐기 때문에 Anycast 트래픽을 LAX 로 옮겼다. 처음에는 나아졌지만 얼마 뒤 LAX 도 버거워했다. 트래픽을 서부 밖으로 아예 옮긴 때도 있어 다른 리전의 에지도 잠깐 영향을 받았다.\n\nAirtime 의 대역폭 한도를 낮추자 softirq CPU 사용률과 호스트 load average 는 내려갔지만 lazy loader 질의는 여전히 느렸다. 프록시를 재시작하자 lazy loader 문제도 풀렸다. 그 뒤에도 같은 문제가 되풀이됐다."},"updated_at":"2026-11-14T12:34:00.765632Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0353","org_slug":"fly-io"}