GroupedUser counter_culture_fix_counts full table scan — missing scope
RCA: Api::V1::SignupsController#signup_on_team Latency (avg 1244ms)
Overview#
What Happened#
2026-05-26 05:15~06:10 UTC 사이에 cupixworks-api 서비스의 POST /api/v1/signups/team 엔드포인트가 평균 1244ms, 최대 1521ms의 응답 시간을 기록했다. 4개 리전(ca-central-1, ap-northeast-1, us-west-2, ap-southeast-1)에서 8건 발생. 모든 요청은 HTTP 200으로 정상 응답했으나 latency threshold(500ms)를 초과했다.
Quick Facts#
| Field | Value |
|---|---|
| resource_name | Api::V1::SignupsController#signup_on_team |
| top_frame | app/operations/signup_operation.rb:43 |
| avg_duration | 1244ms |
| max_duration | 1521ms |
| env | production (ca-central-1, ap-northeast-1, us-west-2, ap-southeast-1) |
Timeline#
- 2026-05-26T05:15:33Z — 최초 latency threshold 초과 감지
- 2026-05-26T06:10:54Z — 마지막 slow trace 기록 (8건 누적)
- 2026-05-26T10:24:02Z — APM 메트릭에서 평균 816ms로 확인 (개선 추세)
Error Log#
{
"resource_name": "Api::V1::SignupsController#signup_on_team",
"service": "cupixworks-api",
"occurrences": 8,
"avg_ms": 1244,
"max_ms": 1521,
"sample_trace_id": "3691244330617136317"
}
Impact#
- Service:
cupixworks-api - 발생 횟수: 8
- 최초 발생: 2026-05-26T05:15:33.012Z
- 최근 발생: 2026-05-26T06:10:54.831Z
- 사용자 영향: 팀 가입 시 1.2~1.5초 대기. 기능적 오류는 없으나 UX 저하.
Root Cause Summary#
signup_on_team 요청 처리 과정에서 3가지 성능 병목이 복합적으로 작용한다: (1) user.active_state! 상태 전환 시 after_transition 콜백에서 GroupedUser.counter_culture_fix_counts를 스코프 없이 호출하여 전체 grouped_users 테이블을 스캔하고, (2) Cupix::Aws::Cognito.change_password가 매 호출마다 STS assume_role + Cognito API를 순차적으로 실행하며 (credential 캐싱 없음), (3) Group 멤버십 삽입이 N+1 패턴으로 각 group.users << user마다 after_create 콜백(reindex_group, flush_cached_permitted_items)을 트리거한다.
Technical Analysis#
Code Path#
- Entry point:
app/controllers/api/v1/signups_controller.rb:24 SignupOperation.team_signup(params):app/operations/signup_operation.rb:43user.active_state!:app/operations/signup_operation.rb:81→ state machine transitionafter_transitioncallback:app/models/concerns/statable/user.rb:42-44→after_state_changedGroupedUser.counter_culture_fix_counts(unscoped):app/models/concerns/statable/user.rb:48- Group membership loop:
app/operations/signup_operation.rb:85-94 Cognito.change_password:app/operations/signup_operation.rb:96→lib/cupix/aws/cognito.rb:162Cognito.client(STS assume_role):lib/cupix/aws/cognito.rb:9-27UserOperation.get_token:app/controllers/api/v1/signups_controller.rb:27
병목 1: counter_culture_fix_counts (전체 테이블 스캔)
after_transition from: any, to: any do |user, transition|
user.after_state_changed(transition)
end
def after_state_changed(transition)
GroupedUser.counter_culture_fix_counts
end
counter_culture_fix_counts가 only: 또는 where: 파라미터 없이 호출된다. 이는 전체 grouped_users 테이블의 카운터를 재계산한다. 비교: 동일 코드베이스의 editing_factory.rb:68에서는 스코프를 지정한다:
::EditingEntity.counter_culture_fix_counts only: :editing, where: { editings: { id: editing_ids } }
병목 2: Cognito API 호출 (credential 미캐싱)
def client(region: nil)
region ||= Cupix::Tesla.region
sts_client = ::Aws::STS::Client.new(region: region)
assumed_role = sts_client.assume_role(
role_arn: $AWS.fetch(:cognito).fetch(:role),
role_session_name: 'CognitoSession'
)
credentials = assumed_role[:credentials]
::Aws::CognitoIdentityProvider::Client.new(
region: region,
credentials: ::Aws::Credentials.new(
credentials[:access_key_id],
credentials[:secret_access_key],
credentials[:session_token]
)
)
end
매 호출마다 STS assume_role → Cognito client 생성을 반복한다. change_password (cognito.rb:162)에서 client를 호출하므로 매 signup 요청마다 STS round-trip이 발생한다.
병목 3: N+1 Group 멤버십 삽입
Group.left_outer_joins(:email_domains).where(
team: team,
email_domains: { domain: ['*', user_email_domain] },
group_type_code: 'normal'
).or(Group.left_outer_joins(:email_domains).where(
team: team,
group_type_code: 'users'
)).distinct.each do |group|
group.users << user unless group.users.exists?(user.id)
end
각 group.users << user는 GroupedUser 생성을 트리거하고, after_create :reindex_group과 after_commit :flush_cached_permitted_items 콜백이 실행된다.
Log Evidence#
Datadog에서 해당 시간대의 요청을 확인. 모두 HTTP 200 정상 응답이나 latency threshold 초과:
service:cupixworks-api "SignupsController" "signup_on_team"
Time range: 2026-05-26T04:15:00Z to 2026-05-26T07:00:00Z
[200] POST /api/v1/signups/team (Api::V1::SignupsController#signup_on_team)
에러 로그 없음 (status:error 검색 결과 0건). 순수 latency 이슈.
APM 메트릭 (2026-05-26 17:24 KST 기준):
{
"query": "avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api::v1::signupscontroller_signup_on_team}",
"value": 0.816258,
"unit": "seconds"
}
평균 816ms로 여전히 높은 수준이나 인시던트 시점의 1244ms 대비 개선 추세.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | GroupedUser.counter_culture_fix_counts 전체 테이블 스캔이 주요 병목 |
스코프 없는 호출 확인 (statable/user.rb:48), 다른 코드에서는 only:/where: 사용 (editing_factory.rb:68) |
정확한 쿼리 시간 측정 불가 (APM span 미확인) | Confirmed |
| H2 | Cognito STS assume_role 매 호출 반복이 latency 증가 원인 | client 메서드가 캐싱 없이 매번 STS 호출 (cognito.rb:9-27), cross-region 호출 시 추가 latency |
Cognito 에러 로그 0건 (기능적 문제는 아님) | Confirmed |
| H3 | 외부 서비스 장애로 인한 일시적 latency spike | 4개 리전 동시 발생, 인시던트 시점에 집중 | 에러 로그 0건, 이후에도 816ms 유지, 코드 구조상 상시 발생 가능 | Rejected |
| H4 | DB 커넥션 풀 고갈/lock contention | 여러 리전 동시 발생 | 다른 엔드포인트 latency 이슈 없음, 발생 빈도 8건으로 burst 아님 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
app/models/concerns/statable/user.rb:48—GroupedUser.counter_culture_fix_counts를 해당 유저가 속한 그룹으로 스코프 제한. 현재 전체 테이블 스캔을 유저 관련 그룹으로 한정.
단기 개선 (1주 이내)#
lib/cupix/aws/cognito.rb:9-27—Cognito.client메서드에 credential 캐싱 추가. STS assume_role 결과를 TTL(예: 50분, 토큰 만료 1시간 기준)과 함께 캐싱하여 매 요청마다 STS round-trip을 제거.app/operations/signup_operation.rb:85-94— Group 멤버십 삽입을 bulk insert로 변경하거나, 콜백을 일시 suppress 후 일괄 처리하는 방식 검토.
장기 개선 (재발 방지)#
after_transition콜백에서 expensive operation(counter_culture_fix_counts)을 비동기 worker로 위임. 실시간 정확도가 불필요한 counter는 background job으로 처리.- Cognito client를 싱글톤 또는 connection pool 패턴으로 관리하여 credential 재사용 보장.
Monitoring#
avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api::v1::signupscontroller_signup_on_team}> 500ms 알림 설정GroupedUser.counter_culture_fix_counts실행 시간 계측 (custom instrumentation)- Cognito API 호출 횟수/latency 추적:
avg:trace.aws.request.duration{service:cupixworks-api,aws_service:cognitoidentityprovider}
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard —
counter_culture_fix_counts스코프 추가는 straightforward하나 counter 정확성 검증 필요. Cognito client 캐싱은 thread-safety 고려 필요.