새로운 멤버들에게 discobot 튜토리얼을 완료하도록 독려해 왔는데, 상당수가 이전(스테이징) Discourse 인스턴스에서 이미 경험했던 문제를 겪고 있습니다. 바로 마지막 게시글의 인증서가 생성되고 실제로 표시되기까지 긴 시간이 소요되며, 로딩 또는 생성 중이라는 어떠한 표시도 없다는 점입니다. 여기서 긴 시간이란 10~20초를 의미하며, 이는 누군가 포기하기에 충분한 시간입니다. meta에서 테스트해 보니 여기서는 즉각적으로 표시됩니다.
인증서는 /discobot/certificate.svg?date=Oct+28+2020&user_id=123 URL을 사용하여 온디맨드(on-demand)로 생성될 수 있음을 발견했고, 이에 몇 가지 테스트를 수행했습니다. 처음에는 즉각적으로 생성되었습니다. 그러나 최근에 튜토리얼을 완료하지 않은 사용자 ID를 사용하자마자 10~20초의 대기 시간이 발생했습니다(처음 생성된 후에는 빠르므로 어딘가에 캐시가 있는 것으로 추정됩니다). 또한 대기 기간 동안 몇 가지 500 에러가 발생했는데, 이는 다음 새로고침 시 정상 작동했으므로某种 종류의 타임아웃으로 추정됩니다.
/logs에서 관련이 있는지 알 수 없는 경고 메시지를 확인했습니다:
Failed to process hijacked response correctly : Net::ReadTimeout
traceback
/usr/local/lib/ruby/2.6.0/net/protocol.rb:217:in `rbuf_fill'
/usr/local/lib/ruby/2.6.0/net/protocol.rb:191:in `readuntil'
/usr/local/lib/ruby/2.6.0/net/protocol.rb:201:in `readline'
/usr/local/lib/ruby/2.6.0/net/http/response.rb:40:in `read_status_line'
/usr/local/lib/ruby/2.6.0/net/http/response.rb:29:in `read_new'
/usr/local/lib/ruby/2.6.0/net/http.rb:1509:in `block in transport_request'
/usr/local/lib/ruby/2.6.0/net/http.rb:1506:in `catch'
/usr/local/lib/ruby/2.6.0/net/http.rb:1506:in `transport_request'
/usr/local/lib/ruby/2.6.0/net/http.rb:1479:in `request'
rack-mini-profiler-2.0.2/lib/patches/net_patches.rb:9:in `block in request_with_mini_profiler'
rack-mini-profiler-2.0.2/lib/mini_profiler/profiling_methods.rb:39:in `step'
rack-mini-profiler-2.0.2/lib/patches/net_patches.rb:8:in `request_with_mini_profiler'
/var/www/discourse/lib/final_destination.rb:370:in `block in safe_get'
/var/www/discourse/lib/final_destination.rb:414:in `block in safe_session'
/usr/local/lib/ruby/2.6.0/net/http.rb:920:in `start'
/usr/local/lib/ruby/2.6.0/net/http.rb:605:in `start'
/var/www/discourse/lib/final_destination.rb:411:in `safe_session'
/var/www/discourse/lib/final_destination.rb:362:in `safe_get'
/var/www/discourse/lib/final_destination.rb:131:in `get'
/var/www/discourse/lib/final_destination.rb:152:in `get'
/var/www/discourse/lib/file_helper.rb:55:in `download'
/var/www/discourse/plugins/discourse-narrative-bot/plugin.rb:113:in `fetch_avatar'
/var/www/discourse/plugins/discourse-narrative-bot/plugin.rb:98:in `block in generate'
/var/www/discourse/lib/hijack.rb:56:in `instance_eval'
/var/www/discourse/lib/hijack.rb:56:in `block in hijack'
/var/www/discourse/lib/scheduler/defer.rb:94:in `block in do_work'
rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/lib/scheduler/defer.rb:89:in `do_work'
/var/www/discourse/lib/scheduler/defer.rb:79:in `block (2 levels) in start_thread'
인증서는 외부 네트워크 요청 없이 로컬에서 생성되는 것으로 이해하고 있습니다.
서버는 t3a.medium(메모리 4GB)이며 포럼은 특별히 바쁘지 않습니다(로드 평균 약 0.4, 여전히 충분한 여유 메모리 존재). 인증서 생성을 기다리는 동안 CPU 사용률이 전혀 변하지 않았으므로, 이것이 병목 현상의 원인으로 보이지 않습니다.
이 문제를 겪는 곳은 우리만 아닙니다(여기 참고, 이것이 관련 있는지는 확실하지 않음), 하지만 거기서 도움이 될 만한 내용을 찾지 못했습니다.
확신은 없는데, 인증서 URL을 몇 번 새로고침하면 결국 속도를 줄이라는 오류 메시지가 표시됩니다. 많은 사람이 동일한 URL 경로에서 인증서를 생성할 때 간섭을 일으킬 수 있는 URL 스로틀링(요청 제한) 메커니즘이 있는 걸까요? 아마 아닐 수도 있지만, 왜 이런 일이 발생하는지 막막합니다. 어떤 힌트든 환영합니다.
말씀드렸듯이, 예를 들어 /discobot/certificate.svg?date=Oct+28+2020&user_id=123 요청이 완료될 때까지 기다리는 동안 CPU 사용량이 평소보다 더 높지 않기 때문에(평소에도 매우 바쁘지 않음) CPU 병목인 것 같지는 않습니다.
이 문제는 네트워크 요청 타임아웃과 관련이 있는 것으로 보입니다:
인증서의 아바타는 data:image/png;base64 블롭인 것 같습니다. 제가 볼 때, 타임아웃이 발생하는 아바타 다운로드 시도가 있는 것 같습니다. 이것이 여기서 처리를 늦추는 원인이 아닐까요?
참고로, 이 포럼은 SSO를 사용 중이며 아바타는 S3에 저장된 https URL입니다. 도커 컨테이너 내에서 일부 아바타를 가져오는 것을 시도해 보았는데 작동하며 거의 즉시 완료됩니다 — 방화벽 문제 등은 없습니다. 인증서 생성 시에는 오랜 시간이 걸리지만, 포럼의 모든 다른 기능은 빠르게 작동합니다.
인증서 생성을 늦추고 있는 타임아웃 오류는 Discourse의 속도 제한과 관련이 있을까요? Discourse 인스턴스 _자체_에서 아바타를 너무 빠르게 요청하거나, 여러 사용자가 봇 튜토리얼을 수행하는 동안 Amazon S3에 너무 빠르게 접근하려고 해서 그런 것일까요?
흥미롭네요. 여기 base64 이미지를 인라인으로 넣고 있네요. image svg 태그에서 외부 리소스에 링크를 걸지 않은 이유가 궁금하네요
이 플러그인의 히스토리를 처음 이 기능이 추가된 곳까지 파고들어 봤는데:
아무래도 처음부터 base64 인라인 아바타를 사용해 온 것 같습니다.
수정: 조사해 보니 인증서가 img HTML 태그를 통해 로드되기 때문에 base64로 인라인 처리하고 있었습니다. 더 가벼운 아바타를 사용하려면 CSP에서 기본적으로 차단되어 있는 object 태그로 전환하거나, SVG 전체를 게시글 콘텐츠에 인라인으로 넣어야 합니다. 아마 iframe이 최선의 방법이 될 것 같습니다.
base64 인코딩된 아바타를 SVG에 임베드한 것이 로고를 “그렇게 처리한” 이유와 동일한 목적, 즉 인증서가 특정 시점의 스냅샷 역할을 하도록 하기 위함이라고 가정했습니다. 수상자가 외부 의존성 없이 인증서를 저장해 두면 나중에에도 동일한 방식으로 렌더링되도록 하려는 것이죠.
iframe 방식은 게시물에서 인증서를 쉽게 공유하거나 자랑하기 어렵게 만듭니다. 다른 포럼에서는 얼마나 흔한지 모르겠지만, 우리 사용자 중 일부는 이미지의 URL을 그대로 복사해서 공유하는 것을 본 적이 있습니다.
외부 리소스에 링크를 사용하는 방향으로 간다면, <image> 태그를 사용하면 여기서 작동하고 allowed_iframes를 변경할 필요가 없게 되지 않을까요?
저희는 stable 버전을 사용 중이며, 현재 이 기능을 테스트할 좋은 방법이 없습니다. 하지만 fetch_avatar가 더 이상 사용되지 않는다면, 이 변경 사항으로 인해 저희의 Net::ReadTimeout 오류가 사라질 것으로 기대합니다. 이 이슈를 해결로 표시하고, 업데이트 후 같은 문제가 다시 발생하면 다시 열겠습니다. 감사합니다!