{"id":"IL-0547","org":"Buildkite","date":"2025-11-10","docs":[],"title":{"en":"Buildkite annotation queries timed out and drove database CPU up across shards after a migration removed an index","ko":"Buildkite 의 Agent API, REST API, GraphQL 이 annotations 테이블 쿼리 타임아웃으로 4시간 가까이 저하됐다"},"domain":["database"],"impact":{"scope":"Agent API severely degraded for most customers from 04:48 to 05:30 UTC and for some until 08:43 UTC; REST API, GraphQL and Web UI degraded from 04:48 to 08:43 UTC; some agents and jobs left inconsistent until 11:56 UTC","users":"Most Buildkite customers","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Buildkite said the incident was mainly caused by a database migration that exposed gaps in its review process.","ko":"Buildkite 는 이 장애가 주로 데이터베이스 마이그레이션 하나에서 비롯됐고, 그 마이그레이션이 검토 과정의 빈틈을 드러냈다고 밝혔다."},"related":[],"sources":[{"url":"https://www.buildkitestatus.com/incidents/sv6phcn6xwwg","kind":"official-status","archived":"https://web.archive.org/web/20260418095948/https://www.buildkitestatus.com/incidents/sv6phcn6xwwg"}],"summary":{"en":"At 04:48 UTC on 10 November 2025 Buildkite applied a database migration that removed an index on the annotations table of its Pipelines product. Another index was already in place to replace it, but a small number of high-traffic queries did not use the new index as expected. From 04:53 UTC those queries timed out, the agent command that creates annotations was behind many of them, and the CPU load they put on the affected PostgreSQL databases degraded every Pipelines feature. Migrations at Buildkite apply to all shards at the same time.\n\nThe team began recreating the removed index at once and finished it on about half of the shards by 05:30 UTC, which partly restored service. On the remaining shards the query load prevented efficient index creation. The team split up to try several ways of reducing that load: disabling annotations in the product, sending new annotations to a separate table, and blocking the table through table-level permissions. Temporarily revoking the application user's read and write permissions on the annotations table, together with raising max_parallel_maintenance_workers in Postgres, made index creation much faster, and between 07:50 and 08:43 UTC the index was rebuilt on the remaining shards, after which latency and error rates were back to normal.\n\nSome agents and jobs were left in an inconsistent state. Where the system could not heal itself, engineers freed the agents and cleaned up job states by hand, which was finished by 11:56 UTC. Buildkite said the migration had exposed gaps in its review process.","ko":"2025년 11월 10일 04:48 UTC 에 Buildkite 는 Pipelines 제품의 annotations 를 저장하는 테이블에서 인덱스 하나를 지우는 데이터베이스 마이그레이션을 적용했다. 지운 인덱스를 대신할 인덱스가 이미 있었지만, 트래픽이 많은 소수의 쿼리가 예상과 달리 새 인덱스를 타지 않았다. 데이터베이스 마이그레이션은 모든 샤드에 동시에 적용된다.\n\n04:53 UTC 에 annotations 테이블 쿼리의 타임아웃이 감지됐다. 타임아웃 난 쿼리 다수는 에이전트가 새 annotation 을 만드는 명령에서 나왔다. 이 쿼리들 때문에 영향을 받은 데이터베이스의 CPU 부하가 크게 올랐고 Pipelines 의 모든 기능이 느려졌다. 팀은 바로 지운 인덱스를 다시 만들기 시작해 05:30 UTC 까지 데이터베이스 샤드의 절반쯤에서 끝냈고, 서비스 일부가 돌아왔다. 나머지 샤드에서는 annotations 테이블로 계속 들어오는 쿼리 부하 때문에 인덱스를 효율적으로 다시 만들 수 없었다.\n\n팀은 여럿으로 나뉘어 부하를 줄일 방법을 찾았다. 제품에서 annotation 기능을 끄는 방법, 새 annotation 을 다른 테이블로 보내는 방법, 테이블 수준 권한으로 annotations 테이블을 막는 방법을 살폈다. 가장 깔끔하고 효율적인 방법은 애플리케이션 사용자의 annotations 테이블 읽기·쓰기 권한을 잠시 빼는 것이었다. 여기에 Postgres 의 max_parallel_maintenance_workers 를 늘려 인덱스 생성 속도를 크게 올렸다. 07:50 UTC 부터 08:43 UTC 까지 읽기·쓰기 권한을 뺀 상태에서 나머지 샤드의 인덱스를 다시 만들었고, Agent API, REST API, GraphQL 의 지연과 오류율이 평소 수준으로 돌아왔다. 장애로 일관되지 않은 상태가 된 일부 에이전트와 잡은 이어진 4시간 동안 풀어 주고 정리했으며, 손으로 해야 했던 작업은 11:56 UTC 에 끝났다."},"trigger":{"en":"At 04:48 UTC a database migration removed an index on the annotations table of the Pipelines product.","ko":"2025년 11월 10일 04:48 UTC 에 Pipelines 제품의 annotations 를 저장하는 테이블에서 인덱스 하나를 지우는 데이터베이스 마이그레이션이 적용됐다. 대신할 인덱스가 이미 있었지만 트래픽이 많은 일부 쿼리가 새 인덱스를 타지 않았다."},"severity":"unknown","symptoms":["intermittent-timeouts","database-overloaded","high-error-rate","high-latency"],"timeline":{"detected_after":"5m","resolved_after":"3h 55m"},"date_kind":"incident","detection":{"en":"At 04:53 UTC the impact of the removed index was detected as queries timing out on the annotations table.","ko":"04:53 UTC 에 annotations 테이블 쿼리의 타임아웃으로 인덱스 삭제의 영향이 감지됐다."},"components":["database"],"root_cause":"deploy-rollout","remediation":[{"text":{"en":"Recreated the removed index, finishing on about half of the database shards by 05:30 UTC","ko":"바로 지운 인덱스를 다시 만들기 시작해 05:30 UTC 까지 데이터베이스 샤드의 절반쯤에서 끝냈고, 서비스 일부가 돌아왔다."}},{"text":{"en":"Temporarily revoked the application user's permission to read from and write to the annotations table","ko":"나머지 샤드의 부하를 줄이려고 애플리케이션 사용자의 annotations 테이블 읽기·쓰기 권한을 잠시 뺐다."}},{"text":{"en":"Increased max_parallel_maintenance_workers in Postgres to speed up index creation","ko":"Postgres 의 max_parallel_maintenance_workers 를 늘려 인덱스 생성 속도를 크게 올렸다."}},{"text":{"en":"Finished recreating the index on the remaining shards between 07:50 and 08:43 UTC","ko":"07:50 UTC 부터 08:43 UTC 까지 읽기·쓰기 권한을 뺀 상태에서 나머지 샤드의 인덱스를 다시 만들었고, Agent API, REST API, GraphQL 의 지연과 오류율이 평소 수준으로 돌아왔다."}},{"text":{"en":"Freed the agents and cleaned up the job states left inconsistent, handling by hand the cases the system could not self-heal, by 11:56 UTC","ko":"일관되지 않은 상태로 남은 에이전트를 풀어 주고 잡 상태를 정리했다. 스스로 회복하지 못한 경우는 손으로 처리했고 11:56 UTC 에 끝났다."}}],"action_items":[{"text":{"en":"Extend the internal tooling that flags risky migrations in code review to cover index removals and ask engineers to verify an index is no longer needed","ko":"코드 리뷰 때 위험한 마이그레이션을 짚어 주는 내부 도구가 인덱스 삭제도 다루도록 넓혔다. 이 도구는 인덱스를 지우는 마이그레이션을 배포하기 전에 그 인덱스가 더는 필요 없는지 엔지니어가 확인하게 한다."}},{"text":{"en":"Roll out high-risk migrations to shards in phases instead of to all shards at once","ko":"지금은 데이터베이스 마이그레이션이 모든 샤드에 동시에 적용된다. 위험도가 높은 마이그레이션은 단계적으로 적용해 영향 범위를 좁히고, 문제가 생기면 더 빨리 복구할 수 있게 한다."}},{"text":{"en":"Evaluate tools such as HypoPG to analyse index creation and removal","ko":"인덱스를 만들거나 지울 때의 분석을 개선하려고 HypoPG 같은 도구를 검토한다."}},{"text":{"en":"Show index usage data from pganalyze more prominently in the development workflow","ko":"pganalyze 의 인덱스 사용 데이터를 개발 흐름에서 더 잘 보이게 해, 엔지니어가 그 데이터를 보고 인덱스 변경을 결정하게 한다."}},{"text":{"en":"Automate the manual cleanup of inconsistent agent states so the system can heal itself","ko":"일부 에이전트의 일관되지 않은 상태를 풀 때 쓴 수작업 정리를 자동화해, 비슷한 문제에서 시스템이 스스로 회복하게 한다."}}],"contributing":["review-missed","no-staged-rollout"],"recurrence_of":null,"symptoms_text":{"en":"From 04:48 UTC on 10 November 2025, queries on the table that stores annotations in Buildkite Pipelines timed out, CPU load on the affected databases rose sharply, and the Agent API, the REST API, GraphQL and the Web UI slowed down and returned more errors.\n\nMany of the timed-out queries came from the agent command that creates a new annotation. The status page first reported problems displaying build annotations, then increased error rates and slow performance on the REST and GraphQL APIs and the Web UI. The Agent API was severely degraded for most customers until 05:30 UTC and for some remaining customers until 08:43 UTC, and the REST API and GraphQL were degraded from 04:48 until 08:43 UTC.\n\nRecreating the removed index restored about half of the database shards by 05:30 UTC. On the other shards the ongoing load from annotation queries kept the index from being rebuilt efficiently, so recovery there stalled until that load was cut off.","ko":"2025년 11월 10일 04:48 UTC 부터 Buildkite 의 Agent API 가 대부분의 고객에게 심하게 저하됐고, REST API, GraphQL, Web UI 도 오류율이 오르고 느려졌다.\n\n04:53 UTC 에 annotations 테이블의 쿼리가 타임아웃 나는 것이 보였다. 타임아웃 난 쿼리 가운데 다수는 에이전트가 새 annotation 을 만드는 명령에서 나왔다. 이 쿼리들 때문에 영향을 받은 데이터베이스의 CPU 부하가 크게 올라 Pipelines 의 모든 기능이 느려졌다.\n\n상태 페이지에는 05:22 UTC 에 빌드 annotation 표시에 문제가 있어 웹 UI 에서 빌드를 보는 데 영향이 있을 수 있다는 글이 먼저 올라왔다. 05:30 UTC 에는 REST API, GraphQL API, Web UI 의 오류율 증가와 느린 성능을 알렸다.\n\n05:30 UTC 까지 데이터베이스 샤드의 절반쯤에서 인덱스를 다시 만들어 서비스 일부가 돌아왔다. 나머지 샤드에서는 annotations 테이블로 계속 들어오는 쿼리 부하 때문에 인덱스를 효율적으로 다시 만들 수 없었고, 일부 고객의 Agent API 저하와 REST API, GraphQL 저하는 08:43 UTC 까지 이어졌다.\n\n장애 뒤 일부 에이전트와 잡이 일관되지 않은 상태로 남았다. 스스로 회복하지 못한 경우는 손으로 처리했고 11:56 UTC 에 끝났다."},"updated_at":"2026-11-14T12:34:19.156666Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0547","org_slug":"buildkite"}