1. X 4세대+10 드랍 — MultiRisk 광고 블렌더 · VF 안전라벨 워머 · Phoenix streamed restore + gRPC 트레일러 픽스 + copy_port 셔플 · 잡다한 하드닝 다수
자료 xai-org/x-algorithm@24c6094 — 2026-08-28T01:00:41Z 커밋(파일 41, +2071/−265). 커밋 메시지는 세대 시리즈 관행대로 "Open-source X Recommendation Algorithm" 한 줄이라 실제 내용은 파일 diff로만 판독. 규모로는 4세대+7(+1,286) 위, +6(+3,105) 아래. 서브시스템 폭은 넓다 — home-mixer 광고·필터·util, visibility-filtering 안전 라벨 조회 인프라, Phoenix 학습·서빙, grox reply_spam, abuse-enforcement, simclusters 배치 잡까지 7곳.
배경 X For You 타임라인의 4세대 오픈소스 릴리스는 2026-08-13 헤드라인 드랍(47c1bcd, +363,246/−11,640) 이후 격일~매일 후속 커밋이 붙는 상태고, 이 문서 기준 오늘이 열한 번째 후속(4세대+10)이다. 4세대+9(2026-08-26, 45b48ba)가 Phoenix 체크포인트 스택 저장·복원 정리·trainer_recsys valid_step lock-step 통일·grox reply_spam LM 프롬프트 확장·VF media fallback cache 제거를 나란히 얹은 넓은 인프라 드랍이었다면, 이번 4세대+10은 (a) 어제 4세대+9의 Phoenix streamed restore·grox monotone-only가 소비자 배선까지 완결되고 (b) 광고 안전 스택의 마지막 조각인 tier×tier 블렌더 옵션이 신설되고 (c) VF의 요청 경로 P99 지연 축인 twemcache miss rate을 낮추는 워머 인프라가 붙는 성격이다. 이틀 전(4세대+9)의 대칭 하드닝이 이번 커밋에서 실제 소비 경로로 이어지는 흐름이 반복되고 있다.
이 절은 여섯 갈래로 나눠 설명한다.
1-A. MultiRiskAdsBlender — BSR-high 광고 tier × medium-risk 포스트 폴백 매트릭스
home-mixer/ads/multi_risk_blender.rs(332줄 신규) + home-mixer/ads/tests/multi_risk_blender_tests.rs(215줄 신규) + home-mixer/selectors/blender_selector.rs(+8/−4)에서 네 번째 광고 블렌더 옵션이 배선됐다 [확인] — home-mixer/ads/multi_risk_blender.rs, home-mixer/selectors/blender_selector.rs.
기존 세 블렌더 safe_gap·partition_organic·time_gap이 "모든 광고 → Safe 포스트 옆에만 배치"라는 대칭 정책을 강제했다면, multi_risk는 포스트를 3단(Safe·MediumRisk·HighRisk), 광고를 2단(BsrNormal·BsrHigh)으로 분해해 인접성 매트릭스로 슬롯을 결정한다.
포스트의 3단 분류는 4세대+7 (2026-08-24)에 신설된 BrandSafetyVerdict::HighRisk tier에 의존한다. HighRisk가 도입되면서 이전에 medium에 있던 NSFW_HIGH_PRECISION·NSFW_HIGH_RECALL·GORE_AND_VIOLENCE_HIGH_PRECISION·PDNA·EGREGIOUS_NSFW 5종과 신규 SOFT_NSFW가 승격됐다. 광고의 2단 분류는 AdIndexInfo.ad_adjacency_control.brand_safety_risk 필드의 BsrNormal|BsrHigh — 광고주가 자기 광고에 대해 medium-risk 컨텍스트 노출을 감수하는 opt-in 신호 [추정](코드에는 필드 정의만, 광고주 UI 흐름은 미공개).
슬롯 배치 규칙:
| 광고 tier | Safe 옆 | MediumRisk 옆 | HighRisk 옆 |
|---|---|---|---|
BsrNormal (기본) |
배치 | 안 함 | 안 함 |
BsrHigh (광고주 opt-in) |
배치 | 배치 | 안 함 |
즉 BsrHigh 광고는 safe 슬롯이 소진되면 medium-medium 슬롯을 폴백으로 얻는다 — 대신 광고주 handle이 이웃 포스트에 언급됐거나 광고 키워드가 이웃 텍스트와 겹치면 여전히 드롭. HighRisk 포스트는 어떤 광고와도 인접 불가.
슬롯 예산 산정 — safe_budget = min(num_ads, spacing_cap, safe_count / 2). 세 상한 중 어느 것이 binding이었는지가 MultiRisk.serving_limitation{factor=ads_supply|spacing|safe_posts} 카운터로 관측됨. 예: 광고 공급이 충분한데 slot이 안 채워지면 safe_posts가 factor로 뜸 → 뷰어의 피드에 안전 포스트가 부족했다는 신호.
슬롯 rejection 세분화 — 각 슬롯이 결국 광고 하나로 채워졌는지, 어느 안전 필터에 걸려 비었는지가 MultiRisk.slot_outcome{outcome=filled|unfilled_bsr_low|unfilled_handle|unfilled_keyword|unfilled_no_ads}로 슬라이스 관측. SlotRejections::stuck_outcome()이 세 rejection reason(bsr_low·handle·keyword) 중 최다 발생을 slot의 대표 원인으로 뽑는다. 즉 A/B 대시보드에서 "slot 하나가 비었을 때 가장 자주 걸린 안전 필터"를 직접 관측 가능.
같은 커밋에서 "partition_organic_low_risk" 앨리어스도 AdsBlenderType param에 추가돼 총 5개 옵션(safe_gap / partition_organic / partition_organic_low_risk / time_gap / multi_risk). 앨리어스는 기존 PartitionOrganicAdsBlender로 라우팅.
"if any_bsr_high && max_medium_slots > 0 && spacing_cap > 0 { ... } else if safe_budget == 0 { return posts_to_feed_items(scored_posts); }" —
home-mixer/ads/multi_risk_blender.rs, 2026-08-28
정본 반영 ../services/x.md 광고 절에 "4세대+10 (2026-08-28, 24c6094) — MultiRiskAdsBlender 신설" subsection 추가. 인접성 매트릭스 표·슬롯 예산 산정식·3종 metric 정의를 담음.
의미 4세대+7 HighRisk tier 신설 → 4세대+9 광고 픽셀·conv_asset_ids 배선 → 4세대+10 tier×tier 매트릭스 블렌더의 5일 흐름이 완결. HighRisk가 tier로 분리됐고 광고 픽셀 컨버전 헤드가 붙었으니, 이제 광고주가 자기 노출을 tier 축에서 opt-in할 수 있는 blender 옵션이 신설된 것. 광고주 관점에서 BSR-high opt-in의 인센티브 구조가 명시적으로 배선됐다는 신호 — medium-risk 컨텍스트를 감수하면 폴백 슬롯을 얻어 노출 총량이 늘어남. 다만 AdsBlenderType은 아직 default가 아니라 experiment 게이트 뒤에 있고, 다음 관측 포인트는 이 param의 기본값이 언제 multi_risk로 승격되는지.
1-B. VF 안전-라벨 twemcache 미스에 대한 sampled async 워머
visibility-filtering/safety_label_source/에 새 파일 warmer.rs(203줄)가 생기고, 그 옆의 lookup.rs가 twemcache miss ID를 warmer에 fire-and-forget으로 흘리도록 배선됐다 [확인] — visibility-filtering/safety_label_source/warmer.rs, visibility-filtering/safety_label_source/lookup.rs.
배경 정리 — VF는 매 요청마다 후보 수십~수백 tweet의 안전 라벨(visibility/baseTweetSafetyLabelMap Strato 컬럼)을 조회해서 3-tier 정책(base_home_rules() 28개 + OON 추가 26개 = 54 규칙)으로 Allow / Interstitial / Drop을 판정한다. twemcache가 look-aside cache로 앞에 걸려 있고, miss 시 Manhattan 폴백. 이 조회의 hit rate가 VF P99 지연의 핵심 변수.
동작 흐름:
RemoteSource::get(ids)가 twemcache 결과를 네 종류로 분기 —Hit·NotFound(정의적 미존재)·Miss(cache가 이 키를 모름)·FallThrough(reason)(cache 에러/타임아웃).Miss인 경우에만 별도 벡터warm_ids로 수집.NotFound는 정의적으로 없는 트윗이라 워머 대상 아님(어차피 라벨 없음);FallThrough는 캐시 인프라 이슈라 워머로 해소할 성격 아님. 이 3분법이 워머 도입의 원칙 — 워머는 "캐시가 아직 안 채워진 상태"만 해소.- Manhattan 폴백 요청이 나가기 전에
warmer.warm(warm_ids)를 호출(fire-and-forget). 요청 응답 지연에는 영향 없음. SampledWarmer가VF_CACHE_WARM_SAMPLE_PCT환경변수(0–100, panic on out-of-range)로 확률 샘플링 → 통과한 ID만 tokiompsc::channelbounded queue(용량 1024)에try_send. 실패(채널 full)하면WarmKeyResult::DroppedChannelFull카운터만 올리고 요청 처리를 진행. 워머 채널 포화가 요청 경로에 절대 backpressure를 걸지 않도록 설계.- 백그라운드 drain 태스크가 큐에서 벡터 pop → 500ms linger 동안 뒤에 오는 벡터를 합쳐 최대 50 ID까지 배치 →
StratoWarmFetcher::fetch(chunk)로 Strato에batch_call(WARM_COLUMN_PATH = "visibility/baseTweetSafetyLabelMap"). Strato가 write-back한 결과가 twemcache에 채워짐(별도 read-through 캐시 계층이 있는 것으로 추정[추정], warmer 코드는 fetch 응답을 직접 twemcache에 쓰지는 않음).
관측 카운터 record_cache_warm_keys(result, count) — 6종 outcome: EligibleMiss (miss로 워머 대상이 된 총량, 샘플링 전) · SampledOut · Enqueued · DroppedChannelFull · FetchIssued · FetchFailed. 이 6종으로 워머의 각 단계 통과율·병목을 관측. 예: FetchFailed / FetchIssued가 높으면 Strato 인프라 이슈; DroppedChannelFull이 지속적으로 발생하면 채널 용량(1024) 부족.
같은 파일에서 config 정리 3건 — VF_GIZMODUCK_CLIENT_ID·VF_TWEMCACHE_CLIENT_NAME·APP_ENV 3개 env 신설. gizmoduck_client_id()가 configured 값이 없으면 visibility-filtering-service.{APP_ENV|prod} 템플릿으로 자동 조립. VF가 dark_traffic으로 staging에서 돌 때 staging용 gizmoduck 쿼터/레이트리밋을 프로덕션과 분리하는 목적으로 추정 [추정].
정본 반영 ../services/x.md 안전성/visibility-filtering 절에 4세대+10 subsection 추가. 워머 흐름·bounded 채널 원칙·6종 metric·config 정리를 담음.
의미 twemcache miss는 (a) 트윗이 아직 어떤 라벨링도 안 받은 신선한 상태(cache는 물론 라벨도 없음) 또는 (b) TTL 만료·evict 두 경우인데, 이 워머는 특히 (b) — Manhattan에 라벨은 있는데 캐시가 놓쳐서 매 요청 Manhattan을 때리는 낭비를 해소. Sample-pct 게이트로 트래픽이 급증해도 워머 자체가 Strato 폭주를 유발하지 않게 안전 설계. VF_CACHE_WARM_SAMPLE_PCT를 낮게 시작해 점진적으로 올리는 dial-up 롤아웃이 가능 — 4세대의 다른 dial-up 패턴(Grok 4.6 10%→30%→100%, 4세대+4→+5→+6)과 유사한 관행. VF의 요청 경로 층이 새로 열린 게 아니라 하부 인프라 최적화 — 규칙 판정(3-tier / 54 규칙)은 그대로.
1-C. Phoenix trainer가 restore_streamed를 실제로 소비
4세대+9(2026-08-26)에 도입된 load_checkpoint_streamed가 스캐폴딩만 배선된 상태였는데, 이 커밋에서 Trainer.maybe_load_checkpoint가 실제 소비자로 붙었다 [확인] — phoenix/xrex/train/trainer.py:1177-1330, phoenix/python/training/xai-checkpointing/xai_checkpointing/load.py.
활성 조건은 세 개 모두 참일 때: restore_streamed=True AND format == "orbax" AND save_method != "tensorstore". 하나라도 안 맞으면 로그 남기고 기존 load_checkpoint(전량 pinned-host 스테이지)로 폴백. 즉 config 오설정이나 orbax가 아닌 checkpoint(tensorstore 등)면 안전한 기본 경로로 fall-through — 하위 호환.
use_streamed_restore=True 경로가 그 자체로 checkpointing_load.copy_aliased_arrays(self.state)를 호출해 aliased state leaves를 사전에 분리. 새 함수 _graft_replaced(pairs)가 loaded tensor를 원래 state tree의 자리에 in-place로 grafting하는데 aliased leaf가 두 자리에 있으면 어느 슬롯에 넣을지 애매해지므로 조기 assertion (restore_streamed does not support aliased state leaves: multiple loaded tensors share one device array).
load_checkpoint_streamed가 소비하는 새 인자 두 개도 이 커밋에서 라우팅됨: window_gb (해당 배치의 pinned-host 예산, None이면 auto-window) + on_replaced 콜백 (완료 배치의 old/new device array pair 리스트를 받아 호출자가 state tree에 grafting). window_cap_gb는 window_gb가 명시 안 될 때만 restore_concurrent_gb로 세팅되는 안전벨트 — 이전 API의 concurrent_gb를 streamed 경로에서 window cap 상한으로 재활용.
이 흐름의 핵심: pinned-host에 optimizer state 사본을 만들지 않고 tensor 하나씩 device로 이동한 뒤 원 참조를 그래프트. warm_start 케이스(pretrained params 로드, opt state는 fresh)에서 특히 이득 — 기존 do_not_load_opt_state=True 경로가 warm_purge(self.state)로 opt state 필드를 미리 잘라내고, streamed면 host_state를 None으로 두고 loaded params만 grafting해서 opt state pinned-host 사본이 아예 생성 안 됨. 4세대+6·+7의 Muon+GB300 flagship이 pretrained + fresh optimizer로 시작하는 표준 흐름을 checkpoint stack이 원자적으로 지원하게 됐다.
정본 반영 ../services/x.md Phoenix 학습·인덱스 절에 "4세대+10 (2026-08-28, 24c6094)" subsection의 첫 갈래로 추가.
의미 4세대+9 → +10의 이틀 흐름이 "스캐폴딩 → 소비자 배선"으로 완결. restore_streamed가 현재 default인지 experiment flag 뒤에 있는지는 코드에서 명확하지 않으나, config에 라인 1줄만 추가된 것으로 봐서 아직 default false로 추정 [추정]. 다음 관측 포인트는 flagship config에서 이 플래그가 True로 승격되는 커밋.
1-D. Phoenix gRPC compression 트레일러 유실 버그픽스 + zstd encode 시간 히스토그램
GrpcCompressionService가 응답 본문을 zstd로 압축할 때 이전 로직은 collected.to_bytes()로 body만 뽑아 새 axum::body::Body::from(data)로 다시 감쌌다. 이 과정에서 gRPC trailers(특히 grpc-status·grpc-message)가 통째 유실됐다 [확인] — phoenix/crates/serving/xai-recsys-engine/src/grpc_compression.rs.
gRPC 프레임은 body 뒤에 trailer로 상태 코드를 실어 보내는 구조인데 그 부분이 버려진 것. 클라이언트 측에서는 응답이 정상 오면서도 status trailer가 없어 UNKNOWN/EOF로 처리됐을 가능성 — 재시도 로직·에러 로깅·서킷브레이커가 모두 오작동. 새 코드는 collected.trailers().cloned()로 trailer를 뽑아 두고, rebuild_response(parts, data, trailers) 헬퍼가 Full::new(data).with_trailers(async move { Some(Ok(tr)) })로 트레일러를 다시 얹은 뒤 응답 본문 구성. 압축 성공/실패 둘 다에서 트레일러가 유지되도록 통일.
회귀 테스트 두 개(compressed_response_forwards_trailers, non_compressed_response_forwards_trailers)로 명시. 이 두 테스트가 있는 한 미래에 유사 리팩터로 trailer가 다시 새는 걸 CI가 잡음.
같은 파일에서 새 히스토그램 recsys_engine_response_compress_ms{method} 배선 — tokio::task::spawn_blocking(compress_grpc_frame)에 들어가기 직전에 Instant::now(), 완료 후 elapsed().as_secs_f64() * 1000.0 관측. 즉 zstd 인코딩 큐잉 + CPU 시간을 method별 슬라이스로 볼 수 있음. buckets는 request latency 버킷과 공유 (crate::request_metrics::latency_buckets_ms()).
정본 반영 ../services/x.md Phoenix 절 두 번째 갈래로.
의미 trailer 유실은 서빙 계약을 조용히 깨는 심각한 버그. 이 커밋으로 회귀 방지 테스트까지 붙어 리그레션 확률이 크게 낮아짐. _compress_ms 히스토그램은 GB300에서 큰 응답의 zstd 인코딩이 응답 지연에 얼마나 기여하는지 슬라이스 가능해서 압축 레벨 튜닝의 근거가 됨. 미공개: 이 버그가 프로덕션에서 언제부터 있었는지, 관측된 증거(고객 티켓? 재시도 폭주?).
1-E. copy_port shard-schedule 셔플로 peer-load 균등화
shuffle_sharded_schedule(n, tensor_name)이 신설됐다 [확인] — phoenix/crates/serving/xai-recsys-engine/src/copy_port_client.rs, phoenix/crates/serving/xai-recsys-engine/src/emb_table.rs.
각 pod가 텐서의 여러 shard를 병렬 다운로드할 때 이전엔 모든 pod가 index 0부터 순서대로 요청 → 첫 shard의 owner peer가 순간적으로 과부하. 새 로직은 POD_NAME(또는 HOSTNAME)과 tensor name을 hash로 섞어 StdRng::seed_from_u64(hash)로 초기화된 shuffle을 적용 → 각 pod가 다른 순서로 shard를 요청. 결정론적 seed라 같은 pod가 restart돼도 같은 순서를 유지(로그 재현 가능성). 그 대신 완료된 결과를 원래 순서로 복원하기 위해 restore_piece_order(shuffled, schedule) -> Option<Vec<T>> 헬퍼.
emb_table.rs와 copy_port_client.rs 두 소비 지점 모두 새 헬퍼로 통일. sharded ownership에만 셔플 적용, replicated는 in-order 유지(모든 pod가 같은 데이터를 받으므로 셔플 이득 없음).
첫 8개 index만 로그(copy_port: shard schedule shuffled name={tensor} n={total} first={:?})로 남겨 셔플이 실제로 걸렸는지 진단 가능. TRANSFER_FAILED_SENTINEL을 만나면 restore 전에 에러로 조기 반환.
정본 반영 ../services/x.md Phoenix 절 세 번째 갈래로.
의미 대형 임베딩 테이블·dense weight을 다수 pod가 restore할 때 shard 0을 담당하는 peer에 traffic spike가 몰리는 문제는 4세대+7의 노드-serialize + concurrent_gb로 완화됐지만, peer-side 스파이크는 여전히 남아 있었다. shuffle은 그 마지막 병목을 pod 수에 반비례하게 나눔. 결정론적 셔플이 중요한 이유: (a) 셔플 자체를 로그로 재현 가능하고, (b) 같은 pod가 같은 tensor를 여러 번 로드해도 같은 순서라 캐시 hit rate 관측이 안정.
1-F. 잡다한 하드닝 5건
home-mixer polling 요청 관측성 [확인] — home-mixer/util/strato_context.rs (75줄 신규), home-mixer/side_effects/response_stats_side_effect.rs (+34/−0). 새 유틸이 tonic gRPC의 MetadataMap에서 stratocontext 또는 stratocontext-bin 키를 뽑아 base64 디코드 후 proto StratoContext { is_polling: bool }로 파싱. response_stats_side_effect.rs의 새 함수 stat_request_context(feed, query)가 각 요청에 대해 세 카운터를 발화 — <feed>.request.polling{polling=true|false, client}, <feed>.request.request_context{request_context, client}, <feed>.request.client{client, client_version}. 즉 서빙 지표에서 정기 polling refresh vs 유저 초기 요청을 슬라이스 가능해짐. polling 요청은 시퀀스가 짧고 has_cached_posts 확률이 다르므로 이 축의 분리 관측이 랭킹 실험 해석에 중요.
grox reply_spam Kafka-first + >= idempotency + fetch fail-loud [확인] — grox/flows/reply_spam/task_write.py (+15/−21), grox/flows/reply_spam/classifier_coordinated_spam.py (+5/−1). _publish_to_reply_ranking_manhattan이 4세대+9 monotone-only 위에 세 갈래로 조여졌다:
- 비교 연산자
>→>=— 이전엔 새 스코어가 기존과 같으면 그대로 씀(중복 write). 이번엔score >= existing.score이면 skip. 카운터 이름도higher_than_existing→not_lower_than_existing으로 개명해 시맨틱 명시. - Kafka 이벤트를 Manhattan write 앞으로 이동 — 이전 순서는
save_reply_ranking_score(Manhattan)→save_reply_ranking_kafka_v2(Kafka). 새 순서는 반대. Kafka는 append-only라 실패 시 재시도 가능하지만 Manhattan은 monotone-only 로직에 걸려 재시도 자체가 skip될 수 있고 upsert side-effect가 있어 무겁다. Kafka-first가 시스템 전체의 관측 가능성 정합성을 보장하는 durability 순서 — Kafka publish 성공 후 Manhattan write 실패는 오프라인 재처리 파이프라인이 복구 가능하지만 반대는 downstream(서빙 캐시 warm-back, 다른 소비자)이 Manhattan에 이미 반영된 새 스코어를 못 보게 됨. fetch_reply_ranking_score의 try/except 제거 — 이전엔 fetch 실패 시 warning 로그 남기고existing = None으로 진행 → 항상 write. 이번엔 예외를 그대로 위로 던져 flow task가 실패로 마크되고 재시도. defensive 예외 처리를 걷어내고 로직 정확성 우선.
같은 커밋에서 classifier_coordinated_spam.py의 _to_convo가 PostRenderer.render(p, max_media=..., include_follower_count=True)로 호출되도록 인자 추가. 4세대+9에 옵션으로 도입된 include_follower_count가 여기서 상시 활성으로 채택 — coordinated spam 판정이 스레드 각 참여 계정의 팔로워 카운트를 프롬프트로 수신.
abuse-enforcement AIS thrift 스키마 conformance 테스트 [확인] — abuse-enforcement-service/service-lib/src/service.rs (+58/−0). 새 테스트 every_action_shape_encodes_against_the_ais_schema가 8종 EnforcementAction 형태(suspend_user·add_user_label·add_post_label·bounce_arkose·bounce_captcha·spam_liveness_check)를 실제 AIS thrift 스키마(ActionIntakeService.intakeAction)로 인코드 테스트 → 회귀 방지. Transcoder::from_schema_bytes(SCHEMA_AIS)로 스키마 로드, lookup_method로 메서드 찾고, 각 action에 대해 build_intake_request + encode_call 수행. AES의 payload 스키마가 다운스트림 thrift 서비스와 drift하지 않도록 CI로 보장. 4세대+8에서 도입된 generic_actions 패스스루로 스코어러가 임의 action을 얹어 보낼 수 있게 된 흐름의 안전벨트.
home-mixer CJK muted keyword whole-token 회귀 테스트 [확인] — home-mixer/filters/viewer_muted_keyword_filter.rs (+18/−0). 새 테스트 test_cjk_keyword_whole_token_only가 명시: 京都 뮤트가 I visited 京都 last week(공백 경계 안에 京都 온전히 등장)는 걸러내지만 東京都に行くのが楽しみ(京都가 東京都의 부분 문자열)는 걸러내지 않아야 함. 필터 본체 diff는 없음 — 즉 필터는 이미 CJK word boundary를 다루고 있고 이번은 그 시맨틱을 회귀 테스트로 잠근 것. 뮤트 키워드가 부분 문자열 매칭으로 확장돼 유저가 예상 밖의 포스트를 못 보게 되는 사고를 방지.
Brazil2026ElectionFilter 계정 리스트 주석 재정리 [확인] — home-mixer/filters/brazil_2026_election_filter.rs (+63/−77). 이전에 665개 계정을 no live account was found / deleted his account 등 단일 반복 주석으로 나열하던 것을, 10개 계정에 대해 "We believe the account @X reported by the candidate is not the candidate's actual account, so we are not currently filtering it" 판정과 no live account found 다수로 재정리. 필터 시맨틱은 그대로(팔로우 예외 유지). 4세대+8의 리스트 갱신 관행과 별개로 이번은 문서·주석 톤 정리.
simclusters SimClustersInterestedIn2020 스냅샷 폴백 인라이닝 [확인] — simclusters/simclusters_v2/scalding/embedding/EntityToSimClustersEmbeddingsJob.scala (+17/−2). InterestedInSources.simClustersInterestedIn2020Source(dateRange, timeZone) 헬퍼를 직접 DAL.readMostRecentSnapshot(interestedIn2020WithFallback, dateRange.prepend(Days(28)(timeZone))) + .withRemoteReadPolicy(ExplicitLocation(ProcAtla)) + fallbackPath = Some("viewfs://.../simclusters_v2_interested_in_20M_145K_2020/_TMP_RECOVERY/{timestamp}")로 인라이닝. 즉 28일 룩백 + 특정 데이터센터(ProcAtla) pin + 임시 복구 경로 폴백. 배치 잡이 스냅샷 결측(dataset partition 유실·업스트림 지연 등)에 더 관대해짐. SimClusters 임베딩 잡의 데이터 신뢰도 하드닝.
정본 반영 — 이 다섯 잡다한 하드닝은 x.md의 4세대+10 드랍으로 해소된 것 / 신규 버킷에 각 한 줄씩 체크마크로 남김. 각 항목이 서로 독립적이라 별도 절로 뽑기보다 버킷에서 인덱싱하는 게 검색 편의.
2. 레이더 — arXiv triage에서 통과 1건
30건 훑어서 수용 기준(플랫폼 기업 저자 / 프로덕션·A/B / 메커니즘 신규성 / 스케일)을 넘긴 것은 하나. 나머지 29건은 학술·도메인 한정·프로덕션 진술 없음 등으로 탈락.
자료 Scaling Graph Neural Networks for Friend Recommendation: Multi-Hash User Embeddings and Temporal Neighbor Sampling — arXiv 2608.27413, 2026-08-27 v1, CIKM 2026 accepted. 저자 3명 (Maksim Utushkin·Andrei Ovsiannikov·Alexander D'yakonov) — arXiv abstract 페이지에 소속 미표기지만 본문에서 참조하는 그래프 규모와 러시아어 저자명 조합으로 VK(러시아 대형 소셜 네트워크)로 추정 [추정].
배경 친구 추천(social platform의 People You May Know / Suggested Friends)은 유저 속성(관심사·나이·위치)보다 다중 홉 소셜 컨텍스트(친구의 친구, 공통 친구 수)가 관련성의 주 신호라서 GNN(Graph Neural Network — 노드가 자기 이웃의 임베딩을 aggregate해서 자기 임베딩을 갱신하는 message-passing 모델)이 자연스러운 프레임워크다. 다만 프로덕션 소셜 그래프는 수억 유저·수백억 엣지 규모라 (a) 유저 ID 임베딩 테이블이 폭발하고 (b) 슈퍼노드(팔로우 수가 극단적으로 많은 계정)의 이웃 순회가 O(deg) 비용으로 병목이 된다. 이 논문은 이 두 시스템 문제를 정면으로 푼다.
새로 알게 된 것
- Multi-hash user embeddings로 ID 임베딩 테이블을 98% 이상 축소
[확인]— 각 유저 ID를 여러 해시 함수의 임베딩 lookup으로 조합해 표현. hash collision을 감수하되 ranking quality는 유지. 원문 - Temporal neighbor sampling — timestamp-sorted CSR(Compressed Sparse Row — 그래프 저장의 표준 자료구조, 인접 리스트를 하나의 큰 array + offset array로 압축) 저장 + binary search로 이웃 샘플링을
O(deg(v) + k)에서O(log(deg(v)) + k)로 단축[확인]. 즉 팔로우 수가 큰 노드도 이웃 순회 비용이 로그 시간. - 프로덕션 소셜 그래프 규모: 194M users · 28B edges
[확인]. - Strong production baseline 대비 온라인 지표: friend additions from recommendations +16%, unique friend adders +11.5%
[확인].
"On a graph with 194M users and 28B edges, our system increases friend additions from recommendations by 16 percent and unique friend adders by 11.5 percent over a strong production baseline." — arXiv 2608.27413 abstract, 2026-08-27
레이더 반영 radar.md "이번 달 신규"에 새 항목으로 추가. LLM-native·스케일링 법칙 스레드에는 안 걸리지만 명시적 그래프 도메인에서 GNN이 여전히 살아남는 사례로서 Meta ConnectionMind(그래프 위 LLM 경로 추론기)의 대조점 역할.
의미 strong prod baseline 대비 두 자릿수 uplift(+16%)는 GNN 논문에서 드물다. GNN이 실무에서 결국 살아남는 도메인은 완전 명시적 그래프(친구 그래프, follow 그래프)라는 관찰이 강화됨. Meta ConnectionMind가 LLM으로 그래프 경로 추론을 시도하는 것과 다른 방향 — VK는 순수 GNN을 소셜 그래프의 스케일 한계까지 밀어붙였다. Multi-hash 임베딩 -98% 축소는 embedding table 크기가 서빙 인프라의 핵심 제약이라는 X SimClusters의 관측과도 결이 통함. 서빙 지연 없이 얹은 것으로 abstract에 명시 없으나 두 최적화가 정확히 그 목적(테이블 축소·이웃 순회 로그화). 다음 관찰 포인트는 CIKM 2026(10월) 발표에서 소속·인프라 세부가 추가로 공개되는지, 그리고 LinkedIn People You May Know · Facebook People You May Know의 GNN 갱신 진술이 나오는지.
다음에 볼 것
AdsBlenderTypeparam의 기본값이multi_risk로 승격되는 커밋 — 그 시점이 실 노출 광고에 이 매트릭스가 적용되는 순간VF_CACHE_WARM_SAMPLE_PCT의 초기 프로덕션 값과 dial-up 커밋 — Grok 4.6 shadow와 같은 dial-up 관행이 여기서도 나올지- Phoenix
restore_streamedconfig의 default 승격 — flagship에서 True로 바뀌는 커밋 = warm_start 흐름이 프로덕션 학습에 붙는 신호 recsys_engine_response_compress_ms히스토그램 도입 후 압축 레벨 튜닝 — GB300 응답에서 zstd가 P99에 기여하는 비율이 눈에 보이면 압축 파라미터 후속 조정- grox reply_spam Manhattan write skip율의 시간축 변화 — monotone-only +
>=이후 실제 몇 %가 skip되는지 (not_lower_than_existing카운터 관측 대상) - AIS conformance 테스트가 실제로 회귀를 캐치하는 첫 사례 — 이 테스트가 붙은 이유가 있을 것 (과거 스코어러 개발 사이클에서 실제 drift가 있었을 가능성)