'bundle exec rake db:migrate'가 캘린더 마이그레이션으로 인해 오래 걸림

여러분, 정말 감사드립니다. @pfaffman 님, 제 일 때문에 늦게까지 일하지 마세요. 저녁에 로그아웃하셔야 한다면 포럼 사용자들은 그냥 기다려야 할 겁니다.

discourse=# SELECT pid, age(clock_timestamp(), query_start), usename, query 
FROM pg_stat_activity 
WHERE query != '<IDLE>' AND query NOT ILIKE '%pg_stat_activity%' 
ORDER BY query_start desc;
 pid  |       age       |  usename  |                                                    query                                                     
------+-----------------+-----------+--------------------------------------------------------------------------------------------------------------
   47 |                 |           | 
   50 |                 | postgres  | 
   48 |                 |           | 
   46 |                 |           | 
   45 |                 |           | 
 3231 | 00:07:57.332835 | discourse | DELETE                                                                                                      +
      |                 |           |   FROM calendar_events ce                                                                                   +
      |                 |           | WHERE                                                                                                       +
      |                 |           |   ce.id IN (SELECT DISTINCT(ce3.id) FROM calendar_events ce2                                                +
      |                 |           |             LEFT JOIN calendar_events ce3 ON ce3.user_id = ce2.user_id AND ce3.description = ce2.description+
      |                 |           |             WHERE ce2.start_date >= (ce3.start_date - INTERVAL '1 days')                                    +
      |                 |           |               AND ce2.start_date <= (ce3.start_date + INTERVAL '1 days')                                    +
      |                 |           |               AND ce2.timezone IS NOT NULL                                                                  +
      |                 |           |               AND ce3.timezone IS NULL                                                                      +
      |                 |           |               AND ce3.id != ce2.id                                                                          +
      |                 |           |               AND ce2.post_id IS NULL                                                                       +
      |                 |           |               AND ce3.post_id IS NULL                                                                       +
      |                 |           |   )                                                                                                         +
      |                 |           | 
 3232 | 00:07:57.347747 | discourse | SELECT pg_try_advisory_lock(2859260972035668690)
(7 rows)

오 마이 갓

discourse=# select count(*) from calendar_events;
  count   
----------
 69724384
(1 row)

와, 이게 뭐야?

그건 캘린더 플러그인입니다

7시간 동안 실행된 건가요, 아니면 7분 동안 실행된 건가요?

마지막에는 7분보다 훨씬 오래 걸렸지만, 적어도 나한테는 7시간은 아니었다.

캘린더 플러그인은 거의 사용되지 않습니다. 도움이 된다면 제거할 수 있습니다.

좋네요.

Falco–그냥 플러그인을 삭제하면 어떨까요?

그 다음 플러그인 테이블도 삭제해야 하나요?

수정: 캘린더를 제거한 후 데이터베이스 마이그레이션이 완료되었고 에셋이 사전 컴파일 중입니다. 사이트가 몇 분 안에 정상적으로 작동할 것입니다.

캘린더에 도대체 무슨 일이 벌어져서 그런 문제가 생겼는지 정말 궁금해요?!

살아 있다. 진짜 전설들이다.

내일 아침에 정리하는 법에 대해 연락할게.

여러분을 위해 더 많은 노력을 기울여 주셔서 감사합니다 <3

      DELETE
        FROM calendar_events ce
      WHERE
        ce.id IN (SELECT DISTINCT(ce3.id) FROM calendar_events ce2
                  LEFT JOIN calendar_events ce3 ON ce3.user_id = ce2.user_id AND ce3.description = ce2.description
                  WHERE ce2.start_date >= (ce3.start_date - INTERVAL '1 days')
                    AND ce2.start_date <= (ce3.start_date + INTERVAL '1 days')
                    AND ce2.timezone IS NOT NULL
                    AND ce3.timezone IS NULL
                    AND ce3.id != ce2.id
                    AND ce2.post_id IS NULL
                    AND ce3.post_id IS NULL
        )
    SQL

그러니까 이건 최소한 O(n^2) 연산인 것 같은데, 맞나요? 해당 테이블에는 69,724,384개의 행이 있으므로, 좋은 아이디어라고 보기 어렵습니다.

@Sikamikanico. 캘린더 플러그인을 다시 원하신다면, 모든 캘린더 이벤트를 삭제하고 처음부터 다시 시작하는 것이 좋을 것 같습니다. 다른 방법으로는 활성 서버에서 해당 쿼리를 실행하여 완료되는지 확인해 볼 수 있습니다.

근본 원인은 시간대가 변경될 때 이벤트를 중복 생성하는 플러그인의 버그라고 생각합니다.

calendar_events 테이블은 11GB입니다:

discourse=# SELECT pg_size_pretty( pg_total_relation_size('calendar_events') );
 pg_size_pretty 
----------------
 11 GB
(1 row)

가장 큰 테이블들은 다음과 같습니다:

       relation       | total_size 
----------------------+------------
 calendar_events      | 11 GB
 post_timings         | 6884 MB
 posts                | 2292 MB
 user_auth_token_logs | 1240 MB
 user_actions         | 1055 MB
(5 rows)

calendar_events만 확실히 비정상적입니다.

이것이 플러그인의 현재 버그인지, 아니면 과거에 일어난 일회성 문제인지 이해해야 합니다.

중복된 항목의 대부분이 과거에 생성된 것인지, 아니면 최근 created_at 값이 있는 행이 여전히 너무 많은지 확인해 주실 수 있을까요?

감사합니다!

discourse=# select count(*) from calendar_events where created_at > NOW()-INTERVAL '200 days';                                                                         
  count                                                                                                                                                                
----------                                                                                                                                                             
 25970368                                                                                                                                                              
(1 row)                                                                                                                                                                
                                                                                                                                                                       
discourse=# select count(*) from calendar_events where created_at > NOW()-INTERVAL '100 days';                                                                         
  count                                                                                                                                                                
----------                                                                                                                                                             
 14377700                                                                                                                                                              
(1 row)                                                                                                                                                                
                                                                                                                                                                       
discourse=# select count(*) from calendar_events where created_at > NOW()-INTERVAL '50 days';                                                                          
  count                                                                                                                                                                
---------                                                                                                                                                              
 7207939                                                                                                                                                               
(1 row)                                                                                                                                                                
                                                                                                                                                                       
discourse=# select count(*) from calendar_events where created_at > NOW()-INTERVAL '5 days';                                                                           
 count                                                                                                                                                                 
--------                                                                                                                                                               
 589938                                  
(1 row)                                                                                                 
iscourse=# select count(*) from calendar_events where created_at < NOW()-INTERVAL '200 days';
  count   
----------
 43754016
(1 row)