이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Feed Timeline에서 fanout on write와 fanout on read는 어떤 조건에서 선택하는가?
- 유명인 계정과 follower graph skew는 성능, 정합성, 장애 격리를 어떻게 흔드는가?
- Feed 설계는 freshness, materialization, rebuild 비용을 어떻게 운영 기준으로 바꾸는가?
개요
Feed Timeline은 사용자가 팔로우한 사람들의 게시글을 시간순 또는 랭킹순으로 보여주는 시스템이다. 단순 목록 조회처럼 보이지만 핵심은 follower graph와 fanout 비용이다. 사용자가 글을 쓸 때 팔로워 timeline에 미리 넣을 수도 있고, 사용자가 feed를 열 때 팔로우 목록을 읽어 그때 합칠 수도 있다. 전자를 fanout on write, 후자를 fanout on read라고 부른다. 대부분의 실제 시스템은 둘을 섞는다. 일반 사용자는 미리 materialize하고, 유명인 계정은 읽을 때 합치거나 별도 path로 처리한다.
이 문서가 바꾸는 설계 축
| 축 | 바뀌는 내용 |
|---|---|
| 성능 | write fanout과 read merge 중 어느 시점에 비용을 낼지 정한다. |
| 정합성 | timeline freshness, 중복 제거, 삭제/차단 반영 지연을 계약한다. |
| 장애 격리 | 유명인 hot key와 fanout queue 적체가 전체 feed를 막지 않게 한다. |
| 운영 복잡도 | materialized timeline, rebuild, ranking feature, graph 변경을 운영한다. |
요구사항 분리
| 요구사항 | 설계 질문 |
|---|---|
| 최신순 feed | 몇 초 지연을 허용하는가? |
| 랭킹 feed | ranking feature와 재계산 주기는 무엇인가? |
| 팔로우/언팔로우 | 과거 게시글을 즉시 반영해야 하는가? |
| 삭제/차단 | 이미 materialized된 항목을 어떻게 제거하는가? |
| 유명인 게시글 | 수백만 follower fanout을 어떻게 제한하는가? |
| 광고/추천 | source feed와 섞는 기준은 무엇인가? |
| Feed는 한 SQL로 끝나는 기능이 아니라 여러 source를 사용자 경험에 맞게 합성하는 기능이다. |
대표 시나리오
다음 traffic을 가정한다.
daily_active_users = 5,000,000
avg_following = 300
feed_open_per_user_per_day = 8
post_per_day = 2,000,000
celebrity_followers = 8,000,000
freshness_target = 30s모든 게시글을 모든 팔로워 timeline에 즉시 넣으면 유명인 글 하나가 800만 write가 된다. 반대로 feed를 열 때마다 300명의 최신 글을 merge하면 read latency가 커진다. Follower 분포가 설계의 핵심 입력이다.
Fanout On Write
사용자가 글을 쓰면 팔로워 timeline에 item을 미리 넣는다.
post_created -> fanout queue -> timeline:{followerId} append장점은 feed 읽기가 빠르다는 점이다. 단점은 write가 팔로워 수만큼 증폭된다는 점이다. 팔로워 200명인 일반 사용자는 괜찮지만 팔로워 800만 계정은 queue를 태운다. Fanout on write는 read-heavy이고 follower 수가 적당할 때 강하다.
Fanout On Read
사용자가 feed를 열 때 팔로우한 author의 최신 글을 읽고 merge한다.
following authors -> fetch recent posts -> merge/sort/rank -> return장점은 write 비용이 작고 유명인 글도 write 폭발이 없다. 단점은 feed read latency가 커지고 cache 전략이 어려워진다. 팔로우 수가 많거나 ranking feature가 복잡하면 read path가 무거워진다. Fanout on read는 write-heavy 계정이나 유명인 처리에 유리하다.
Hybrid 전략
대부분은 hybrid가 현실적이다.
| author 유형 | 전략 | 이유 |
|---|---|---|
| 일반 사용자 | fanout on write | follower 수가 작아 read가 빠름 |
| 유명인 | fanout on read 또는 lazy inject | write 폭발 방지 |
| 광고/추천 | read time inject | 정책과 실험이 자주 바뀜 |
| 차단/신고 콘텐츠 | read time filter | 즉시 제거 필요 |
| Hybrid는 성능을 얻지만 운영 복잡도가 늘어난다. | ||
| 사용자는 같은 feed에서 materialized item과 live item이 섞인 결과를 보게 된다. |
Timeline 저장
Materialized timeline은 보통 user별 sorted set이나 table로 둔다.
timeline:{userId} -> [(score=createdAt, postId), ...]저장 기간을 무한으로 두면 비용이 커진다. 최근 1,000개만 유지하고 오래된 항목은 source post에서 조회하게 할 수 있다. 삭제와 차단은 timeline item을 즉시 제거하거나 read time filter로 숨긴다. 어느 쪽이든 “삭제된 게시글이 몇 초 동안 보일 수 있는가”를 정해야 한다.
Freshness와 지연
Feed는 보통 strong consistency가 필요하지 않다. 하지만 지연이 너무 길면 사용자는 글이 사라졌다고 느낀다.
fanout_queue_oldest_age = 45s
freshness_target = 30s
status = degradedFreshness는 queue lag와 사용자 경험을 연결하는 지표다. 사용자에게 “최신 글을 불러오는 중” 같은 상태를 보여줄 수도 있다. 운영자는 feed p95만이 아니라 fanout lag와 stale item 비율을 봐야 한다.
중복과 정렬
Hybrid feed는 중복 제거가 필요하다. 같은 post가 materialized timeline과 celebrity read path에서 동시에 들어올 수 있다.
record FeedItemKey(String sourceType, String postId) {}응답 생성 시 postId 기준으로 dedupe하고 score를 다시 계산한다.
Pagination은 cursor 기반으로 설계한다.
Offset pagination은 새 글 삽입과 삭제 때 중복 또는 누락을 만들기 쉽다.
Rebuild와 Backfill
Timeline 저장소가 깨지거나 ranking 정책이 바뀌면 rebuild가 필요하다. Rebuild는 운영 작업이다.
- rebuild 대상 user segment를 고른다.
- source posts와 follow graph snapshot을 고정한다.
- batch로 timeline을 재생성한다.
- old/new timeline 결과를 sample 비교한다.
- 점진적으로 read traffic을 전환한다. 이 절차가 없으면 materialized feed는 장애 후 복구하기 어렵다.
장애 중 판단
| 장애 | 대응 |
|---|---|
| fanout queue lag 증가 | 유명인 fanout 중단, 일반 사용자 우선순위 |
| timeline store 장애 | source post fallback 또는 stale feed |
| ranking service 장애 | 최신순 fallback |
| follow graph 지연 | 이전 graph snapshot 사용 |
| delete propagation 지연 | read time block filter 우선 |
| Feed는 degrade 선택지가 많다. | |
| 최신순, stale feed, 일부 추천 제거 중 무엇을 선택할지 미리 정해야 한다. |
개인 프로젝트 기준
- fanout on write/read를 follower 수와 read latency 기준으로 비교한다.
- 유명인 계정 예외 path를 반드시 설명한다.
- timeline freshness target과 queue lag 지표를 둔다.
- cursor pagination과 dedupe 기준을 설명한다.
- 삭제/차단이 materialized timeline에 어떻게 반영되는지 적는다.
기업 운영 기준
- follower 분포, fanout lag, timeline freshness, stale item 비율을 dashboard로 본다.
- celebrity account 정책과 emergency throttle을 둔다.
- ranking fallback과 feature flag를 운영한다.
- timeline rebuild runbook과 검증 쿼리를 준비한다.
- privacy, block, delete 요청은 read time filter와 backfill 정리를 함께 둔다.
위험 신호
- 모든 author에게 fanout on write를 적용하고 follower skew를 계산하지 않는다.
- feed read p95만 보고 fanout queue lag를 보지 않는다.
- 삭제/차단된 글이 materialized timeline에 남는 문제를 다루지 않는다.
- offset pagination으로 최신 feed를 만든다.
- ranking service 장애 때 fallback이 없다.
- timeline rebuild 절차 없이 cache처럼 취급한다.
확인 질문
확인 질문
- 확인 질문: Feed에서 fanout on write의 가장 큰 장점과 비용은 무엇인가?
- 답변: 읽기는 빠르지만 게시글 작성 시 팔로워 수만큼 write가 증폭된다.
- 확인 질문: 유명인 계정은 왜 별도 path가 필요한가?
- 답변: follower 수가 매우 커 fanout on write가 hot key와 queue 폭증을 만들기 때문이다.
- 확인 질문: Feed freshness는 어떤 지표로 운영하는가?
- 답변: fanout queue oldest age, stale item 비율, timeline read p95, fallback rate로 운영한다.