
The Life of a Typeahead Query
Visit the post for more.

개요
2009년 가을에 페이스북의 몇몇은 좀더 상호작용적인, 고품질의 결과가 유저가 타이핑을 하면서 같이 나타나는 검색 경험을 상상했다.
프론트엔드는 브라우저에서 작동하는 코드가 결과를 받아와서 렌더링하는 속도가 유저가 타이핑할때 불편하지 않을만큼 충분히 빨라야 한다.
새로운 typeahead 쿼리가 기존에 존재하는 것보다 느리면 안됐기에 매 milisec 가 중요했음
이러한 성능적인 제약사항에도 불구하고 UI는 minimalist이면 안되고, 검색박스에서 엔터를 누르면, 나온 결과 중 첫번째 결과를 자동 선택하는 기능도 있어야 했기에 찾고자 하는 결과와 높은 연관성을 가진 결과가 추출되어야 했었음
이러한 모든 요구사항을 만족시키기 위한 백엔드 서비스를 한번 살펴보자

1. Bootstrapping Connections
유저가 텍스트박스에 포커싱하면, 브라우저에서 유저의 friends, pages, groups, applications, upcoming events 를 조회하기 위해 요청을 보내고 이 결과들을 브라우저 캐시에 저장함 → 또다른 요청을 보내지 않고 해당 결과들을 빨리 빨리 찾을 수 있게 (기존의 typeahead 시스템도 이것은 했지만 여기까지만 했었음)
2. AJAX Request
브라우저 캐시에 충분한 결과가 있지 않다면, 브라우저는 AJAX 요청을 현재 검색 문자열과, 브라우저 캐시에 있는 연관 검색 결과를 같이 보냄. 로드 밸런서를 통해 요청을 적절한 웹서버에 전달
3. Aggregator Service
이 서비스는 여러 검색 서비스의 루트에 있는 서비스임. Aggregator 자체는 무상태이고, index(검색 결과에대한) 또한도 갖고 있지않음
단지, 전달받은 쿼리를 다수의 저수준 서비스로 전달하고 그들의 결과를 수집하는 역할을 함
4. Leaf Services
각각의 백엔드 검색서비스는 병렬적으로 검색창의 내용과 prefix-match 되는 결과들을 index로 찾아낸다.
각각의 Leaf 서비스는 결과들을 조회하고 rank를 매길 수 있게 디자인 되었다.
global
서비스는 사이트에서 모든 pages와 applications에 대한 인덱스를 유지할 수 있었음. 그 이유는 페이스북의 대부분의 다른 기능들과 달리 이 결과들은 개인화가 전혀 필요 없었기 때문이러한 서비스의 record들이 모든 유저에 대해 동일하다는 것은 많은 수의 사용자들이 상호작용한 application과 방문한 페이지들에 대해 rank를 매겨보았을때 알수 있었다.
이러한 서비스들의 결과가 누가 쿼리하는지와는 전혀 의존성이 없었기에 최신 결과를 memcache 기반의 query cache에 저장함으로써 지연시간을 줄일 수 있었다.
graph
서비스는 사용자의 이웃을 검색하고 결과를 반환해준다. 예를 들어 어떠한 쿼리에 대해서는 사용자와 그의 친구 사이의 graph connection이 결과에 대한 선호도에 강력한 신호로 나타났고, 그래서 연관성 또한 있었다.근데 그래프는 수학적으로 매우 계산하기 어려운 객체임( 매우 크고, computationally 어려운 문제임). 우리 그래프는 4억명의 활성화 유저를 갖고 있고 수십억의 사람 사이 관계가 있고 그리고 유저와 pages, application, events 등의 오브젝트에 대한 관계 또한도 포함하고 있다.
5. Merging Results
Aggregator가 각각의 Leaf 서비스의 결과와 feature를 병합하고 우리의 모델이 결과에 대한 순위를 매김
상위의 결과들이 web 계층으로 보내짐
6. Fetching Data and Validating Results
aggregator에 의해 반환되는 결과값은 단순히 id의 리스트임
Web 계층은 결과를 렌더링하고 이름, profile, picture, link, shared networks, mutual friends 같은 정보를 보여주기 위해 필요한 데이터를 memcache나 Mysql에서 조회해와야 했음
또한 웹 계층에서는 검색하는 사용자가 그 결과를 볼 권한이 있는지에 따라 privacy check도 필요했다.
7. Displaying the Results
연관성있는 데이터와, 결과값이 브라우저에 보내짐
이러한 결과들은 브라우저 캐시에 bootstrapped connection과 같이 다시 추가되고 비슷한 추가적인 검색에 대해 백엔드로 다시 요청할 필요가 없다.
Putting it all Together
기본적인 구성이 자리 잡히고 나서도, 모든것들이 운영 준비 상태가 되기 까지는 많은 시간이 걸렸다.
새로운 typeahead 시스템이 도입되고 나서 사용자 패턴이 많이 바뀌는것도 알게되었다. 그러나 이것은 scalability 이슈를 발견하기 위한 좋은 출발점이었음
테스트의 결과로 네트워크 한계를 발견하였고 이는 아키텍처의 topology를 조절하게 되었다.
또한 많은 사용자 인터페이스의 변화를 A/B 테스트를 통해 실험하였고 좀더 정성적인 사용성 스터디는 어려운 결정을 돕게 해주었다.
- Number of results: 우리는 충분한 엔트리를 보여주기를 원했고 그래서 웃긴 결과들을 타이핑하면서 사용자가 자주 우연히 결과들을 마주할 수 있기를 바랐다. 그러나 적은 결과를 보여주는게 더 빠르고 덜 혼란스러웠기에 최종 디자인은 브라우저 창의 높이에 따라 보여지는 개수가 달라지도록 변경함
- Searching: 우리가 엔터키를 눌렀을때 첫번째 결과를 자동으로 선택하도록 만들었지만, 그래도 검색을 추가로 더 하고자 하는 사용자가 있다면 그렇게 할 수 있도록 해주고 싶었다. See More Results 링크의 외양, 위치, 워딩을 많이 실험해보았다.
- Mouse vs. keyboard: 페이스북의 근로자들이 키보드를 많이 사용하는 유저인데 반해 많은 사용자들은 마우스를 이용해 검색결과를 선택하는 것을 선호한다는 것을 발견했다. 이로부터 마우스의 유즈케이스에 더 초점을 맞추게 되었다.
다양한 이미지 사이즈, 다양한 결과 타입(application vs people) 사이의 구분을 하는 방법, 결과에서 query string을 하이라이트 하는 것, 서로 친구관계인 사람들에 대한 표시해주는 방법 등에 대해 다양한 실험을 추가로 해보았다.
이러한 모든 흥미로운 엔지니어링 프로젝트와 함께, trade-off는 우리가 대부분의 challenge와 놀람과 재미를 가졌던 모든곳에서 있었다. 검색에 대한 많은 기술적 결정은 성능(performance)과 재현(recall)과 연관성(relevance) 사이의 trillema 로 귀결되었다. 우리의 typeahead 는 아마도 성능에 높은 우선순위를 둔 우리에게는 비정상적이긴 했었다. 결과를 조회하는데 100msec 이상 걸리면서 typeahead가 멈칫거리게 하였고 유저경험도 좋지 않았다. 우리는 많은 수의 미세조정을 진행했다. 그러나 항상 long tail 쿼리가 있을것이고 이는 우리 typeahead 시스템에는 꽤 무거울 것이다. …
Long-tail data is the collection of all data about items that serve a specific niche and have a low demand but exist in greater varieties and larger quantities.