{"id":"IL-0579","org":"Classi","date":"2026-11-11","docs":[],"title":{"en":"Portfolio search latency on Amazon OpenSearch 1.3 spiked to about 1 second after migrating from Elasticsearch 6.8","ko":"Elasticsearch 6.8 에서 OpenSearch 1.3 으로 옮긴 뒤 포트폴리오 검색의 응답 시간이 간헐적으로 1초 가까이 튀었다"},"domain":["database","aws"],"impact":{"scope":"Search for the Portfolio feature: maximum search latency intermittently about 1 second instead of about 100ms","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"The took value in OpenSearch's responses was already large, which could have narrowed the investigation to OpenSearch sooner. The slow log and the Profile API alone did not find the cause; asking when searches became slow, instead of where, led to the fix. The same setting can behave differently between versions, so defaults have to be checked as well.","ko":"OpenSearch 응답의 took 이 커져 있었으므로 더 일찍 OpenSearch 쪽으로 조사 범위를 좁힐 수 있었다고 팀은 돌아봤다. Slow Log 와 Profile API 만으로는 원인을 찾지 못했고, 어디가 느린지에서 언제 느려지는지로 관점을 바꾼 것이 개선의 계기였다. 버전에 따라 같은 설정 값이라도 동작이 다르므로 기본 동작까지 확인해야 한다고 정리했다."},"related":[],"sources":[{"url":"https://tech.classi.jp/entry/2026/11/11/100000","kind":"blog","archived":null}],"summary":{"en":"Classi's Portfolio feature stores records that students make in daily school life, and Amazon OpenSearch Service is its search backend. The team moved more than 30 million documents by taking a snapshot of the Elasticsearch domain and restoring it into a new OpenSearch domain, and the application could switch between the two engines during the migration to compare results. The indices had no explicit refresh_interval, and the team believed the default of 1s applied.\n\nIn Elasticsearch 6.x and earlier, an index refreshes every second by default even without refresh_interval. Since Search Idle arrived in Elasticsearch 7.0, a shard with no explicit refresh_interval that receives no search for a while goes idle and stops refreshing in the background. OpenSearch is based on Elasticsearch 7.10 and keeps this behaviour, and index.search.idle.after defaults to 30s. When a search reaches an idle shard that needs a refresh, that first search waits for the refresh to finish, which produced the spikes after updates.\n\nSetting refresh_interval explicitly to 3s cut the spikes sharply. Setting it explicitly back to 1s kept the improvement, which showed that what mattered was setting the value explicitly, which takes the shards out of Search Idle control, not the longer interval. The team set refresh_interval to 1s explicitly in production, and the intermittent latency spikes stopped.","ko":"Classi 는 포트폴리오 기능에 학생이 학교생활 중 기록한 데이터를 쌓고, 이를 검색하려고 Amazon OpenSearch Service 를 쓴다. 검색 엔진을 Elasticsearch 6.8 에서 OpenSearch 1.3 으로 옮기면서 Ruby 클라이언트를 elasticsearch-model 과 elasticsearch-rails 에서 opensearch-ruby 로, 인스턴스 타입을 m5.large.search 에서 AWS Graviton 기반의 m7g.large.search 로 함께 바꿨다. 문서 3,000만 건 이상은 기존 Elasticsearch 도메인의 스냅샷을 새 OpenSearch 도메인에 복원해 옮겼다.\n\n옮긴 뒤 검색 처리의 최대 응답 시간이 이전의 100ms 정도에서 1초 안팎까지 간헐적으로 튀었다. 팀은 Slow Log 와 Profile API 로 쿼리 처리를 보고, HTTP 클라이언트 설정과 GC 를 확인하고, curl 로 직접 검색해 봤지만 원인을 찾지 못했다. 언제 느려지는지를 살피다가 문서를 갱신한 뒤의 검색이 늦어진다는 가설을 세웠고, refresh_interval 을 명시해 3s 로 설정하자 지연이 크게 줄었다. 1s 로 명시해도 개선이 유지되어 운영 환경에도 1s 로 명시해 설정했고, 그 뒤 간헐적인 지연이 사라졌다.\n\n원인은 Search Idle 이었다. Elasticsearch 6.x 까지는 refresh_interval 을 정하지 않아도 1초마다 Refresh 가 돌았다. Elasticsearch 7.0 에서 들어온 Search Idle 은 refresh_interval 이 명시되지 않으면 일정 시간 검색되지 않은 샤드를 Idle 상태로 두고 백그라운드 Refresh 를 멈춘다. Elasticsearch 7.10 을 바탕으로 한 OpenSearch 도 이 동작을 물려받았고, index.search.idle.after 의 기본값은 30s 다. Idle 상태의 샤드에 검색이 들어왔을 때 Refresh 가 필요하면 첫 검색은 Refresh 가 끝날 때까지 기다린다. refresh_interval 을 명시하면 샤드가 이 제어에서 빠져 정한 간격으로 Refresh 된다."},"trigger":{"en":"The migration from Elasticsearch 6.8 to OpenSearch 1.3, with indices that had no explicit refresh_interval.","ko":"포트폴리오 기능의 검색 엔진을 Elasticsearch 6.8 에서 Amazon OpenSearch Service 의 OpenSearch 1.3 으로 옮겼다."},"severity":"unknown","symptoms":["high-latency"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"The team noticed latency spikes in metrics checked after the migration and compared the maximum search latency before and after the migration in Datadog APM.","ko":"OpenSearch 1.3 으로 옮긴 뒤 동작을 확인하던 중 팀이 검색 응답이 가끔 크게 늦어지는 것을 알아챘다. Datadog APM 에서 검색 처리의 최대 응답 시간을 옮기기 전후로 비교해 간헐적인 지연을 확인했다."},"components":["elasticsearch-opensearch"],"root_cause":"upgrade-breaking-change","remediation":[{"text":{"en":"Set refresh_interval explicitly to 3s, which sharply reduced the latency spikes","ko":"Refresh 빈도를 낮췄을 때의 변화를 보려고 refresh_interval 을 명시해 3s 로 설정했고, 검색 응답 시간이 튀는 일이 크게 줄었다."}},{"text":{"en":"Set refresh_interval explicitly to 1s in production","ko":"1s 로 명시해도 개선이 유지되는 것을 확인한 뒤 운영 환경에서도 refresh_interval 을 명시해 1s 로 설정했다."}}],"action_items":[],"contributing":["default-config-unchanged","assumption-unverified"],"recurrence_of":null,"symptoms_text":{"en":"After Classi moved the search engine behind its Portfolio feature from Elasticsearch 6.8 to Amazon OpenSearch Service 1.3, the team saw in post-migration metrics that search responses were sometimes much slower. Most searches returned normally, but latency jumped at a certain frequency.\n\nIn Datadog APM, the maximum search latency had stayed around 100ms on Elasticsearch 6.8, while on OpenSearch 1.3 it intermittently reached about 1 second; normal latency had not become worse across the board. The migration had also replaced the Ruby client, elasticsearch-model and elasticsearch-rails, with opensearch-ruby, and the instance type m5.large.search (x86_64) with m7g.large.search (AWS Graviton, Arm), so the move to OpenSearch itself was not the only suspect.\n\nThe team first suspected slow queries. With the slow log threshold set to 0ms, the logged query times showed no delay matching what the application saw. Search API profile=true on one slow request showed \"elapsed_ms\": 867.5 in the application and \"opensearch_took_ms\": 839, while every shard's query time was under 1ms. Changing Keep-Alive, idle_timeout and the Faraday HTTP adapter and checking OpenSearch GC showed no clear link to the latency. Running the same query repeatedly with curl sometimes made only the first run slow, but not reproducibly.\n\nThe team then asked when searches became slow instead of where the time went, and found that a search right after documents were added or updated could be slow.","ko":"OpenSearch 1.3 으로 옮긴 뒤 동작을 확인하던 중 팀은 검색 응답이 가끔 크게 늦어지는 것을 알아챘다. 평소에는 응답이 정상으로 돌아왔고, 일정한 빈도로 응답 시간이 크게 튀었다.\n\nDatadog APM 에서 검색 처리의 최대 응답 시간을 옮기기 전후로 비교했다. Elasticsearch 6.8 을 쓰던 기간에는 대체로 100ms 정도였고, OpenSearch 1.3 으로 옮긴 뒤에는 1초 안팎까지 올라가는 경우가 간헐적으로 생겼다. 평소의 응답 시간이 전체적으로 나빠진 것은 아니었다.\n\n쿼리 자체가 느린지 먼저 의심했다. Slow Log 의 기준값을 0ms 로 두고 모든 쿼리를 기록해 애플리케이션에서 늦어진 시각과 맞춰 봤지만, 그 지연에 해당하는 쿼리 처리 시간은 보이지 않았다.\n\nSearch API 의 profile=true 로 처리 내역을 봤다. 늦어진 한 요청에서 애플리케이션이 잰 처리 시간은 867.5ms, OpenSearch 응답의 took 은 839ms 였지만, 샤드별 쿼리 처리 시간은 모두 1ms 미만이었다.\n\n다음으로 애플리케이션과 OpenSearch 사이의 통신을 의심했다. Keep-Alive 와 idle_timeout 설정, Faraday 의 HTTP Adapter 를 바꿔 보고 OpenSearch 쪽 GC 도 확인했지만, 어느 것도 응답 시간과 뚜렷한 관련이 없었다. curl 로 Search API 를 직접 불렀을 때 같은 쿼리를 연달아 실행하면 첫 번째만 늦은 경우가 있었지만 재현되지 않았다.\n\n팀은 어디가 느린지가 아니라 언제 느려지는지를 보기로 하고 검색과 갱신을 되풀이했다. 문서를 추가하거나 갱신한 뒤에 검색하면 응답 시간이 커질 수 있다는 가설이 나왔다."},"updated_at":"2026-11-14T12:34:22.511077Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0579","org_slug":"classi"}