브론즈 배지 5개마다 은색 배지 1개씩

다른 사용자를 돕고자 하는 사람들을 위해 청동 배지인 Users Helping Users를 만들었습니다. 이 배지는 수동으로 부여됩니다(도움을 주려는 시도와 단순한 "나도"라는 반응의 차이를 구분하기 위함입니다).

각 청동 배지 5개마다 조각, Invaluable 배지를 수여하고자 합니다.

제가 고안한 SQL은 올바른 두 사용자를 찾지만, 각 사용자당 단 한 번만 찾습니다:

SELECT user_id, current_timestamp AS granted_at 
FROM user_badges
WHERE badge_id = 110 -- Users Helping Users
    AND (:backfill OR user_id IN (:user_ids))
GROUP BY user_id
HAVING COUNT(*) >= 5

제 SQL에 어떤 :magic_wand: 마법이 빠져 있는 건가요, 아니면 배수 처리가 SQL 외부에서 이루어지는 건가요?

또한, 두 사용자가 새로운 청동 배지를 5개 더 받지 않은 경우에도 매일 새로운 배지를 받지 않도록 수치가 어떻게 계산되는지 제게는 정말로 명확하지 않습니다(이것은 야간 트리거로 실행될 예정입니다).

제 검색으로 찾지 못한 이에 대한 가이드가 있을까요?

더 깊이 파고들다 보니 이 가이드로 이어지는 단서를 찾게 되었습니다.

백필(backfill) 부분은 여전히 조금 불분명하지만, 일단 (당연하죠!) 테스트 인스턴스에서 시도해 보기로 했습니다. 백필은 밤새 실행되는 것 같으니, 내일 더 자세한 내용을 알게 될 것 같습니다.

'여러 번 수여 가능’을 체크하지 않는 한, 자격을 두 번째(또는 그 이상)로 충족하더라도 한 번 이상 수여되지 않아야 합니다. :+1:


좋아요, 게시물을 더 자세히 다시 읽어보았고 무엇을 목표로 하는지 더 명확하게 파악했습니다.

현재 SQL을 기준으로, 여러 번 수여를 허용하더라도 해당 사용자는 실버 배지를 단 한 번만 수여받게 됩니다:

  • 테스트 사이트에서 관련 배지를 생성했습니다(여러 번 수여 허용 포함)
  • 브론즈 배지 5개를 수여하고 배지 수여를 위한 백그라운드 작업을 실행했습니다(실버 배지가 성공적으로 수여되었습니다 :partying_face:)
  • 그 후 BadgeGrant 작업을 다시 실행했는데, 실버 배지가 두 번째로 수여되지 않았습니다
  • 다음으로 브론즈 배지 수를 11개로 늘리고 배지 수여 작업을 다시 실행했습니다
  • 두 번째 실버 배지는 수여되지 않았습니다

여러 번 확인해 보았어요.

하지만 두 번 지급되어야 하는데, 한 번만 지급되었어요.

그러면… 12개의 청동 배지를 가진 내 사용자에게 마땅히 받아야 할 2개의 은 배지를 받으려면 무엇을 해야 하나요?

바로 답을 알지 못하는 좋은 질문이네요. :slight_smile:

RANK를 사용해서 뭔가 해볼까 하고 막연히 생각 중인데, 막연한 추측일 수도 있겠네요… :thinking:


대략적인 프로토타입...
WITH badge_count AS (

    SELECT 
        user_id,
        granted_at,
        RANK() OVER (PARTITION BY user_id ORDER BY granted_at ASC) AS rank
    FROM user_badges
    WHERE badge_id = 110

)

SELECT user_id, granted_at
FROM badge_count
WHERE rank IN (5,10,15,20,25,30,35,40,45,50)

자, 이번에는 ROW_NUMBER를 대신 사용해 두 번째 시도를 해보겠습니다:

WITH badge_count AS (

    SELECT 
        user_id,
        granted_at,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY granted_at ASC) AS row
    FROM user_badges
    WHERE badge_id = 110

)

SELECT user_id, granted_at
FROM badge_count
WHERE row % 5 = 0

다만, 더 테스트를 해보니 미리보기에서는 올바르게 작동하지만 실제 badge grant 작업이 실행될 때 여러 번 부여되지 않는 문제가 있습니다. 그 이유는 잘 모르겠네요. :thinking:

제 스스로를 혼란스럽게 만들었네요. 차 한 잔 마시고 마음을 가다듬을게요. :slight_smile:

음… 어제 내 유저에게 동메달 4개를 추가로 부여해서, 밤새 실행을 하면 방금 "획득"한 새 배지가 최소한은 추가될지 확인해 보았습니다. (나머지는 수동으로 채워야 한다면 할 수 있다고 생각했거든요.) 하지만 그것도 작동하지 않았습니다.

쿼리에 대해 도움을 드릴 수 없지만, Discourse AI 봇에게 도움을 요청해 보세요. 상단의 봇 아이콘을 선택하세요
image → GPT-4 → SQL Helper.

내가 필요한 것을 단순히 설명하기만 해도 데이터 탐색기 쿼리를 만드는 데 매우 뛰어나다고 느꼈고, 배지 쿼리에도 도움이 될 것 같습니다.

토니, 그런 건 다들 있는 건 아니잖아 :shushing_face: 부러워할 사람이 많을 거야. :slight_smile: (다만 @ganncamp에게는 있으니 선택지는 되는군)

하지만… 내 쿼리는 정확하다고 꽤 확신하고 있어. 미리보기에서는 올바른 대상이 잘 골라지는데, ‘일일 업데이트’ 트리거를 사용하면 두 개 이상은 절대 부여되지 않아.

‘특정 주제에서 5개 게시물마다’ 배지 기준으로 ‘사용자가 게시물을 생성하거나 수정할 때’ 트리거를 사용해 거의 동일한 구성으로 테스트용 배지를 하나 더 만들어 봤는데, 그쪽은 완벽하게 작동해. 무엇이 다른지 알아보고 있는 중인데…

비교를 위해 그 테스트 배지의 SQL을 여기에 올릴게. 뭔가 보이는 사람이 있으면 알려줘:

WITH post_count AS (

    SELECT 
        user_id,
        id,
        created_at,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at ASC) AS row
    FROM posts
    WHERE topic_id = 864

)

SELECT user_id, created_at granted_at, id post_id
FROM post_count
WHERE row % 5 = 0
  AND (:backfill OR id IN (:post_ids))

@tpetrov 님, 접근 권한은 있는데 제가 사용하려고 시도해 본 결과는… 별로 좋지 않았어요. 아니면 제가 어려운 질문을 잘하는 걸까요? :laughing:

조금 탐색하고 상담해본 결과, 자동 배지 부여자는 배지가 특정 게시물에 기반할 때만 여러 배지를 부여하는 것 같습니다. 따라서 이 유형의 배지는 항상 첫 번째 배지만 부여됩니다(미리보기가 오해를 불러일으키네요 :frowning:).

비슷한 경우 ‘단계적’ 배지가 잘 작동할 수 있다고 생각합니다(예: ‘Solved’ 배지처럼). 예를 들어, 30회 달성 시 은메달, 100회 달성 시 금메달처럼 말이죠. 이것이 실행 가능한 대안이 될 수 있을까요?

그럼… 청동 배지가 게시물에 기반하여 부여되긴 하지만… 그건 해당이 안 되는 거죠?

잘 모르겠어요. 질문의 의도를 이해하지 못하는 것 같습니다. :laughing:

제안이 5개의 배지를 찾는 것이 아니라, 배지가 부여된 5개의 게시물을 찾는 것인 것 같습니다. 그건 할 수 있어요. “배지 부여를 위해 새 게시물을 찾기” 보고서에서 거의 이미 그렇게 했습니다.

음. :thinking: 어떤 의도인지 조금씩 감이 오네요. 한 번 더 시도해볼게요…


@ganncamp - 성공할 수 있을 것 같습니다… :slight_smile:

브론즈 배지 A가 포스트 렌치(post wrench)를 통해, 또는 /admin/users/{user_id}/{username}/badges 페이지에서 사유를 입력하는 방식으로 부여된다고 가정하면:

이 경우 실제로 가능할 것 같습니다. :partying_face:

WITH badge_count AS (

    SELECT 
        user_id,
        granted_at,
        post_id,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY granted_at DESC) AS row
    FROM user_badges
    WHERE badge_id = 110
      AND post_id IS NOT NULL
)

SELECT user_id, granted_at, post_id
FROM badge_count
WHERE row % 5 = 0
  AND (:backfill OR post_id IN (:post_ids))

(AND post_id IS NOT NULL 조건을 추가하면 사유 없이 배지가 부여된 경우를 방지할 수 있으며, 그렇지 않으면 오류가 발생합니다)

방금 직접 테스트해보고 GrantBadge 백그라운드 작업을 트리거하여 시간을 앞당겼더니, 테스트 사용자가 마침내 마땅히 받아야 할 전체 크레딧을 받았습니다. :slight_smile:

그 후 5개의 다른 포스트에 대해 배지 A를 5개 더 부여하고 다시 실행해봤습니다: :tada:

@JammyDodger 감사합니다! :tada: :tada: :tada:

테스트 인스턴스에 설정해 보았어요 (신뢰하지 않는다는 뜻은 절대 아니에요 :joy:) 이번 주에 프로덕션에 적용할 계획입니다! :star_struck:

그리고… 이 기회에 이 튜토리얼에 이번 작업에서 배운 내용들을 업데이트해 주실 수 있을까요? :smiley:

그렇죠, 당연히요. 믿되 검증하라는 말은 분명 현명한 선택입니다. :slight_smile:

조금 더 추가할 수 있는 부분이 있는지 알아볼게요. :slight_smile: :+1:

음… 작업이 밤새 실행되었는데,
동메달 6개를 가진 내 사용자는 은메달 1개를 받았습니다 :white_check_mark:
동메달 13개를 가진 내 사용자는… 은메달 1개를 받았습니다 :slightly_frowning_face:

튜토리얼의 이 부분 때문에 별도의 명시적인 백필(backfill) 작업은 필요 없다고 생각했습니다

일일 전체 백필이 실행되므로 이를 고려하여 :backfill 파라미터 처리를 포함해야 합니다.

이미 은메달 1개 이상을 획득한 사용자에게는 어떻게 메달을 지급해야 하나요? 수동으로 처리해야 하나요?

백필(backfill)은 일일 작업입니다. ‘일일 업데이트’ 트리거는 기본적으로 바로 그 역할을 수행하는 반면, 다른 트리거들은 훨씬 더 '실시간’에 가깝습니다(예: 배지가 '사용자가 게시물을 생성하거나 편집할 때’라는 조건을 사용한다면, 상금이 지급되도록 하루 밤을 기다릴 필요가 없습니다).

무엇이 다른지 확인할 수 있도록 배지의 스크린샷을 올려 주시겠습니까?

여기 있습니다:

참고로, 이 중 몇 가지는 제가 직접 확인해 봤는데… 제가 뭘 하고 있는지 모르거든요 :joy:

13개의 'Badge A’를 가진 사용자의 /admin/users/{user_id}/{username}/badges 페이지를 보면 사유가 입력되어 있나요?

핵심 부분은 동일합니다. :slight_smile: 나머지 항목은 여전히 유효하지만 선택 사항입니다.

이것을 스테이징 사이트에서 실행 중이신가요, 아니면 자체 호스팅 테스트 사이트에서 실행 중이신가요?

와! 의문이 풀렸네요!

저는 그 작업이 (제) 새벽에 실행된다고 가정하고 있었습니다.

사실, 저는 원래 이유 없이 이 사용자에게 배지를 부여했었기 때문에 어제 모두 취소하고, 그의 게시물을 찾아 다시 부여했습니다. 작업은 제가 부여하는 도중에 실행되었습니다.

참고로 이것은 제 스테이징 사이트입니다.

2번째 배지는 몇 시간 후에 부여될 것 같지만, 저는 여러 배지가 한꺼번에 부여되는 것을 보고 싶습니다. 실버 배지를 취소하면 그런 일이 일어날까요? 그는 … 2시간 후에 2개의 새로운 실버 배지를 부여받을 수 있을까요?