현재 meritmoot.com(개발 중이며 롤링 릴리스 방식으로 배포 중)와 관련된 플러그인을 개발하고 있습니다. 외부 데이터를 사용해야 하므로 긴 실행 시간의 Sidekiq 작업을 활용하고 있습니다. 그런데不知道为什么, 작업이 실패하지도 않고 에러 출력도 없이 내부 작업 코드를 계속 재시작하고 있습니다. Sidekiq에서 재시작을 트리거할 수 있는, 제가 놓치고 있는 부분이 있을까요?
마지막 작업(하루에 한 번만 실행되도록 설정된 Roll Calls 관련 작업)은 에러 없이 다음 간격으로 재시작되었습니다:
0시간 58분 42초 (첫 번째 반복 시작 시간)
1시간 30분 12초 (첫 번째 재시작)
2시간 1분 1초 (두 번째 재시작)
3시간 46분 49초
4시간 17분 11초
4시간 47분 33초
작업은 완료되지 않았으며, 모든 진행 상황이 처음부터 다시 시작되었습니다. 참고로 내부 코드의 stderr와 stdout을 리디렉션하는 자체 로깅 프로세스가 있지만, Sidekiq에 영향을 줄 것 같지는 않습니다. (확인해 보고 싶으시면 말씀해 주세요. 개발에 매우 유용합니다!)
코드 내에서 진행 상황을 저장하는 것은 가능하지만, 그렇게 하면 오버헤드가 발생하므로 더 간단한 프로세스를 선호합니다. Sidekiq에서 제 코드가 재시작을 트리거하도록 만드는, 제가 놓치고 있는 부분이 있을까요?
메모리가 데이터베이스/하드 드라이브 메모리를 의미하는 건가요? 저는 그걸 많이 사용하고 있습니다. 현재 @pfaffman님의 제안에서 약간 수정된 방안을 검토 중인데, 프로세스를 포크(fork)하여 메인 스레드가 종료되도록 하고, 동일한 컨텍스트를 가진 자식 프로세스를 생성하는 방식입니다. (실질적으로 sidekiq에 대한 외부 스크립트와 같은 개념입니다)
결국 프로세스 코드를 제거했는데, 효과는 없었습니다. 재시작 작업은 실제로 비효율적인 근본적인 문제를 가려만 주었을 뿐이었어요 .
대신 다음과 같은 방식을 적용했습니다:
순차 처리보다 훨씬 빠른 벌크 SQL 코드를 작성하고, 실제로 업데이트가 필요한 시점을 감지하며, 변경 사항이 없는 항목은 PostRevisor 클래스를 사용하여 다시 업데이트하는 과정을 건너뛸 수 있도록 했습니다.
HTTP를 통한 데이터 검색 효율성을 향상시키기 위해 영속적 연결(persistent connections)을 사용하고, 가능한 경우 압축된 항목을 포함하는 기타 데이터 포인트를 활용했습니다.
벌크 SQL 명령을 작성하는 것이 엄청난 속도 향상을 가져온다는 것을 발견했습니다. 제가 업데이트하는 대상은 다음과 같습니다:
post 테이블: cooked, last updated 컬럼
topic 테이블: title, last updated 컬럼
다음 아이디어는 PostRevisor를 완전히 건너뛰고, 다음과 같은 방식으로 처리하는 것입니다:
1 - 데이터를 임시 테이블(temptable)로 이동
2 - UPDATE topics FROM temp_table
SET topics.title = temp_table.title, topics.last_updated = temp_table.last_updated
WHERE topics.id = temp_table.id AND topics.title != temp_table.title
3 - UPDATE posts FROM temp_table
SET posts.raw = temp_table.raw, posts.last_updated = temp_table.last_updated
WHERE posts.id = temp_table.id AND posts.raw != temp_table.raw
4 - 제목과 콘텐츠가 변경되었으므로 검색 재인덱싱 작업을 트리거합니다.
무엇을 놓치고 있는 부분이 있을까요? Discourse는 복잡하고, PostRevisor를 건너뛰면 제가 경험이 없는 테이블들(post_stats, post_timings, post_uploads, quoted_posts 등 데이터베이스에서 확인되는 것들)에 영향을 미칠 수 있다고 느낍니다. 하지만 이 시스템은 신뢰할 수 있고 예측 가능한 소스에서 이러한 리비전을 가져오므로, PostRevisor가 제공하는 모든 검증이 필요하지도 않습니다. 꽤 운에 맡기는 솔루션처럼 느껴집니다.
업데이트 - 시간이 지남에 따라 이상할 정도로 많은 업데이트가 발생해서 코드 검사를 해보았는데, 원본 JSON 형식에서 실제로 변경되지 않은 데이터 항목에 대해 불필요한 업데이트가 일어나는 원인을 발견했습니다. 이 오류가 해결되면 위 내용은 아마도 필요하지 않을 겁니다. 테스트를 먼저 했어야 했는데… 그랬으면 훨씬 많은 시간을 절약할 수 있었을 텐데. 그래도 위 내용을 시도해볼 수도 있겠네요. 우선순위는 아니지만요. 데이터 표시 형식을 변경할 때 빠른 업데이트에 도움이 될 테니까요. 게다가 이미 작성은 되어 있으니, 테스트만 안 한 상태일 뿐입니다.
대량 업데이트 코드를 완성했습니다. 더 안정화된 후 특정 브랜치로 푸시하는 것에 관심이 있으신가요? 사용 사례가 다소 특이하지만, 해당 기능으로는 태그를 포함하여 수천 개의 레코드를 빠르게 업데이트할 수 있습니다. TopicsBulkAction을 확장하도록 구축되었습니다. 더 자세한 정보가 필요하시면 제가 작성한 readme를 확인해 보세요:
# 입력
# - cooked, post_id, topic_id, title, updated_at, tags (raw는 cooked를 가리킴)를 포함하는 해시 목록
# [{post_id: #, cooked: "", topic_id: #, title: "", updated_at: date_time, tags: [{tag: "", tagGroup: ""}, ... ] } , ... ]
# - category_name: 업데이트되는 카테고리의 이름. 이는 검색 인덱싱에 사용됩니다.
# 목록 항목에 포함할 선택적 해시 속성:
# - raw: 포함되지 않으면 cooked와 동일합니다.
# - fancy_title: 포함되지 않으면 title과 동일합니다.
# - slug: 포함되지 않으면 title에서 처리됩니다(이것은 URL과 관련이 있음)
# 사용 사례: 변경되는 비-discourse 백엔드 데이터 소지에서 주제를 정기적으로 효율적으로 업데이트하여
# 정보를 미러링하는 것. 이것은 일반적인 게시물 또는 주제 게시를 위한 것이 아니라, 주제의 제목과
# 주제의 메인 게시물을 업데이트하기 위한 것입니다. 일반적인 게시물 수정은 lib/post_revisor.rb의 PostRevisor를 사용하세요.
# - 사전 cooked, 사용자 정의 cooked, 또는 그대로 표시된다고 가정합니다. 데이터는 검증되지 않습니다.
# - raw == cooked인 경우 생성 시 (cook_methods: Post.cook_methods[:raw_html])를 설정해야 합니다.
# 게시물 안에 표시할 사용자 정의 HTML을 작성하는 경우 이렇게 해야 합니다.
# 그렇지 않으면 discourse가 나중에 다시 cooked 처리할 수 있으며 이는 좋지 않습니다. 정보 출처가
# 신뢰할 수 있는지, 그 내용이 이스케이프 처리되었는지 확인하세요.
# - 위의 조건이 이상적이지 않다면, raw를 포함하고 게시물 생성 시 올바른 cook 방법을 설정하세요
# (시스템이 다시 cooked 처리하는 경우를 대비하여). raw를 선택한 cook 방법으로 처리하고,
# 해시에 raw와 출력된 cooked를 포함하세요.
# - 게시물의 이전과 이후 단어 수 차이를 추적하여 주체에 전달함으로써 word_count를 추적합니다.
# - 태그 수를 유사한 방식으로 추적합니다