Skip to content

[FM-283] 상점 검색기(부엉이) 검색 응답 콜백이 ch.Call 없이 인벤토리에서 검색기를 빼서, 응답 대기 중 맵을 옮기면 채널이 크래시할 수 있음 #369

Closed

Description

@boyism80

문제

Scan은 요청을 받은 맵 actor 컨텍스트(actx)로 SearchShopsAsync를 부르고, 응답 콜백을 owner.Call 없이 바로 실행한다. async.Promise는 ReenterAfter로 요청했던 actor에서 콜백을 돌리므로(core/async/promise.go driveFrom), 검색 응답을 기다리는 사이 캐릭터가 포탈로 다른 맵(다른 logic actor)으로 옮기면 콜백은 옛 actor에서 돈다.

콜백은 이때 다음을 직접 쓴다.

  • r.owner.Inventory.RemoveItem(invType, slot, 1) (78행): 소비 탭 맵에서 검색기 아이템을 줄이거나 지움
  • r.scanning, r.found, r.retryAt (68·70·75·83행)
  • OnUpdateStats / OnShopScannerResult 패킷 전송

같은 순간 새 actor가 그 캐릭터의 패킷(아이템 사용·이동·줍기)이나 주기 저장 BuildRPC로 같은 인벤토리 맵을 읽고 쓰면 Go 런타임 concurrent map read and map write fatal로 채널 프로세스 전체가 내려갈 수 있다.

영향

  • 채널 크래시(그 채널의 모든 접속 끊김). 자유시장에서 검색 직후 포탈을 타는 흔한 동작으로 창이 열린다.
  • 크래시가 안 나도 옛 actor와 새 actor가 scanning/found를 따로 써서, 검색 결과 목록이 섞이거나 scanning이 잠시 잘못 남을 수 있다(부수 영향).

수정 방향

#368에서 상점 열기 콜백을 고친 것처럼 콜백 본문을 소유 actor로 넘긴다.

// before
SearchShopsAsync(actx, r.owner, searchID, highFirst).Do(func(v *internal.SearchShopsReply) error {
	r.scanning = false
	...
	r.owner.Inventory.RemoveItem(invType, slot, 1)
	...
})

// after
SearchShopsAsync(actx, r.owner, searchID, highFirst).Do(func(v *internal.SearchShopsReply) error {
	r.owner.Call(func(actor.Context) {
		r.scanning = false
		...
		r.owner.Inventory.RemoveItem(invType, slot, 1)
		...
	})
	return nil
}).OnError(func(err error) {
	r.owner.Call(func(actor.Context) {
		r.scanning = false
		r.owner.Listener.OnUpdateStats(r.owner, nil, true)
	})
	log.Printf(...)
})

FindPopularSearches(32-37행)는 패킷 전송만 해서 크래시 경로는 아니지만, 같은 파일에서 함께 Call로 맞추면 규칙이 한 가지가 된다. 회귀 확인: SearchShops RPC에 지연을 넣고 검색 직후 포탈 이동 + -race.

(FM 리뷰어 정기 검수 2026-10-15 22시)

Activity

  1. boyism80 commented on Oct 16, 2026

    @boyism80
    OwnerAuthor

    [FM 리뷰어] ec802304에서 해결 확인. RemoteShops.Scan(remote_shops.go:67)·FindPopularSearches(:32)·Manage(:123) 모두 .Bind(r.owner)로 응답과 OnError가 Character.Post 경유 소유 actor에서 돈다(core/async/promise.go continueFrom·invokeOnErrors). 검색기 차감·r.scanning 쓰기가 옛 맵 actor에서 일어나는 경로가 없어졌다. close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions