By Bret Taylor · February 27, 2009

Background

We use MySQL for storing all of the data in FriendFeed. Our database has grown a lot as our user base has grown. We now store over 250 million entries and a bunch of other data, from comments and "likes" to friend lists.

As our database has grown, we have tried to iteratively deal with the scaling issues that come with rapid growth. We did the typical things, like using read slaves and memcache to increase read throughput and sharding our database to improve write throughput. However, as we grew, scaling our existing features to accomodate more traffic turned out to be much less of an issue than adding new features.

In particular, making schema changes or adding indexes to a database with more than 10 - 20 million rows completely locks the database for hours at a time. Removing old indexes takes just as much time, and not removing them hurts performance because the database will continue to read and write to those unused blocks on every INSERT, pushing important blocks out of memory. There are complex operational procedures you can do to circumvent these problems (like setting up the new index on a slave, and then swapping the slave and the master), but those procedures are so error prone and heavyweight, they implicitly discouraged our adding features that would require schema/index changes. Since our databases are all heavily sharded, the relational features of MySQL like JOIN have never been useful to us, so we decided to look outside of the realm of RDBMS.

Lots of projects exist designed to tackle the problem storing data with flexible schemas and building new indexes on the fly (e.g., CouchDB). However, none of them seemed widely-used enough by large sites to inspire confidence. In the tests we read about and ran ourselves, none of the projects were stable or battle-tested enough for our needs (see this somewhat outdated article on CouchDB, for example). MySQL works. It doesn't corrupt data. Replication works. We understand its limitations already. We like MySQL for storage, just not RDBMS usage patterns.

After some deliberation, we decided to implement a "schema-less" storage system on top of MySQL rather than use a completely new storage system. This post attempts to describe the high-level details of the system. We are curious how other large sites have tackled these problems, and we thought some of the design work we have done might be useful to other developers.

Overview

Our datastore stores schema-less bags of properties (e.g., JSON objects or Python dictionaries). The only required property of stored entities is id, a 16-byte UUID. The rest of the entity is opaque as far as the datastore is concerned. We can change the "schema" simply by storing new properties.

We index data in these entities by storing indexes in separate MySQL tables. If we want to index three properties in each entity, we will have three MySQL tables - one for each index. If we want to stop using an index, we stop writing to that table from our code and, optionally, drop the table from MySQL. If we want a new index, we make a new MySQL table for that index and run a process to asynchronously populate the index without disrupting our live service.

As a result, we end up having more tables than we had before, but adding and removing indexes is easy. We have heavily optimized the process that populates new indexes (which we call "The Cleaner") so that it fills new indexes rapidly without disrupting the site. We can store new properties and index them in a day's time rather than a week's time, and we don't need to swap MySQL masters and slaves or do any other scary operational work to make it happen.

Details

In MySQL, our entities are stored in a table that looks like this:

CREATE TABLE entities (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    id BINARY(16) NOT NULL,
    updated TIMESTAMP NOT NULL,
    body MEDIUMBLOB,
    UNIQUE KEY (id),
    KEY (updated)
) ENGINE=InnoDB;

The added_id column is present because InnoDB stores data rows physically in primary key order. TheAUTO_INCREMENT primary key ensures new entities are written sequentially on disk after old entities, which helps for both read and write locality (new entities tend to be read more frequently than old entities since FriendFeed pages are ordered reverse-chronologically). Entity bodies are stored as zlib-compressed, pickledPython dictionaries.

Indexes are stored in separate tables. To create a new index, we create a new table storing the attributes we want to index on all of our database shards. For example, a typical entity in FriendFeed might look like this:

{
    "id": "71f0c4d2291844cca2df6f486e96e37c",
    "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "title": "We just launched a new backend system for FriendFeed!",
    "link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}

We want to index the user_id attribute of these entities so we can render a page of all the entities a given user has posted. Our index table looks like this:

CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;

Our datastore automatically maintains indexes on your behalf, so to start an instance of our datastore that stores entities like the structure above with the given indexes, you would write (in Python):

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index])

new_entity = {
    "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"),
    "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "title": u"We just launched a new backend system for FriendFeed!",
    "link": u"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}
datastore.put(new_entity)
entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"))
entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"))

The Index class above looks for the user_id property in all entities and automatically maintains the index in the index_user_id table. Since our database is sharded, the shard_on argument is used to determine which shard the index gets stored on (in this case, entity["user_id"] % num_shards).

You can query an index using the index instance (see user_id_index.get_all above). The datastore code does the "join" between the index_user_id table and the entities table in Python, by first querying theindex_user_id tables on all database shards to get a list of entity IDs and then fetching those entity IDs from the entities table.

To add a new index, e.g., on the link property, we would create a new table:

CREATE TABLE index_link (
    link VARCHAR(735) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (link, entity_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

We would change our datastore initialization code to include this new index:

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
link_index = friendfeed.datastore.Index(
    table="index_link", properties=["link"], shard_on="link")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index, link_index])

And we could populate the index asynchronously (even while serving live traffic) with:

./rundatastorecleaner.py --index=index_link

Consistency and Atomicity

Since our database is sharded, and indexes for an entity can be stored on different shards than the entities themselves, consistency is an issue. What if the process crashes before it has written to all the index tables?

Building a transaction protocol was appealing to the most ambitious of FriendFeed engineers, but we wanted to keep the system as simple as possible. We decided to loosen constraints such that:

  • The property bag stored in the main entities table is canonical
  • Indexes may not reflect the actual entity values

Consequently, we write a new entity to the database with the following steps:

  1. Write the entity to the entities table, using the ACID properties of InnoDB
  2. Write the indexes to all of the index tables on all of the shards

When we read from the index tables, we know they may not be accurate (i.e., they may reflect old property values if writing has not finished step 2). To ensure we don't return invalid entities based on the constraints above, we use the index tables to determine which entities to read, but we re-apply the query filters on the entities themselves rather than trusting the integrity of the indexes:

  1. Read the entity_id from all of the index tables based on the query
  2. Read the entities from the entities table from the given entity IDs
  3. Filter (in Python) all of the entities that do not match the query conditions based on the actual property values

To ensure that indexes are not missing perpetually and inconsistencies are eventually fixed, the "Cleaner" process I mentioned above runs continously over the entities table, writing missing indexes and cleaning up old and invalid indexes. It cleans recently updated entities first, so inconsistencies in the indexes get fixed fairly quickly (within a couple of seconds) in practice.

Performance

We have optimized our primary indexes quite a bit in this new system, and we are quite pleased with the results. Here is a graph of FriendFeed page view latency for the past month (we launched the new backend a couple of days ago, as you can tell by the dramatic drop):

In particular, the latency of our system is now remarkably stable, even during peak mid-day hours. Here is a graph of FriendFeed page view latency for the past 24 hours:

Compare this to one week ago:

The system has been really easy to work with so far. We have already changed the indexes a couple of times since we deployed the system, and we have started converting some of our biggest MySQL tables to use this new scheme so we can change their structure more liberally going forward.

Posted by 솔라리스™
:

  1. Twitter와 FriendFeed 이야기 (2008.8.27)
  2. 스코블은 블로그로 돌아올 것인가? (2008.12.25)
  3. Facebook과 소셜 검색(2009.8.11)

국내에서는 잘 모르시겠지만 해외에서는 Twitter라는 ‘소셜 메시징’ 서비스가 매우 인기입니다. 우리나라에서는 ‘마이크로 블로그’라고 알려진 Playtalk이나 Me2day의 원조 서비스로 잘알려져 있습니다만 제가 ‘소셜 메시징’이라고 한데는 다른 이유가 있습니다.

저는 웹 2.0을 초고속 인터넷망으로 인해 사람들이 인터넷에 직접 참여하면서 나온 문화적 결과라고 해석한 바 있습니다. 따라서, 우리 나라에서 나타난 (인터넷) 문화 현상이 해외에서도 공통적으로 나타나고 있죠. 마이스페이스나 페이스북은 아이러브스쿨이나 싸이월드와 같은 동인(動因)으로 생각하고 있구요. 블로그 확산 현상도 오마이뉴스 같은 개인 참여 미디어의 결과입니다.

그럼 Twitter는 무엇일까요? 블로그의 아류는 아니고 이게 어떻게 진화할지 꽤 궁금했는데요. 결과적으로 실시간 메시징 플랫폼으로 활용하고 있다고 봅니다. 솔직히 우리 나라도 SMS와 메신저 사용량이 증가하면서 이메일 사용량이 급격하게 낮아졌고, 이제 커뮤니케이션 가속 시대에 접어드는 문화적 변화가 있었습니다. 메신저나 SMS에 답이 안오면 “씹는다”라는 말이 생겼으니 삶이 더 각박해졌죠.

소셜 메시징 플랫폼의 한계
사람들은 이메일이 아니라 Twitter를 열어 놓고 서로 리플놀이를 하면서 연락을 주고 받고 있습니다. 물론 메신저를 사용하지 않는 건 아닙니다만, 웹 접속 접근성이 높아졌기 때문에 솔직히 프로그램을 설치해야 하는 번거러움이 있는 폐쇄된 버디 네트웍을 쓰는 것보다 웹 기반 서비스를 더 선호하고 있습니다.

근데 Twitter를 메시징 플랫폼으로 쓰기 시작하면서 어려움을 겪게 됩니다. 즉, 전 세계 사람들이 함께 토론하면 어떻게 될까? 라는 딜레마에 빠진 것이죠. 웹 서비스에 메시징이 결합하면 그 트래픽은 상상도 못할 정도의 데이터 처리 능력이 필요합니다. 지금까지 전혀 겪어 보지 못한 문제입니다.

올해 3월에 Techcrunch가 잠정 집계한 통계에 따르면 가입자는 수백 만명이고, 매주 20만명 이상이 한 개이상 메시지를 올리고 있으며, 하루에 300만개의 메시지가 왔다갔다 한다고 합니다.

솔직히 이런 폭주 현상은 스타트업 서비스로서 깜짝 인기를 얻은 Twitter가 감내하기 어려운 구조라 볼 수 있습니다. 몇 개월전만 해도 Twitter의 DB 구조가 취약해서 누군가 항상 기계 앞에 있다가 마스터 DB가 장애가 나면 수동으로 슬레이브 DB를 작동시켜야 한다는 루머(?)가 나돌기도 했습니다. 실제로 Twitter의 장애는 아주 빈번해서 사람들을 정말 열받게 할 정도였습니다.

대안은 오픈화? 하지만…
Twitter는 작년 말 부터 올해 상반기까지 수 없는 장애와 기능 중단과 재가동이 계속 겹치면서 기술 아키텍터인 Blaine Cook이 회사를 떠나게 되고 현재 문제를 해결할려고 많은 노력을 하고 있습니다. 하지만, 사람들은 이미 마음을 돌리고 있는 형편입니다.

기술 조언가들은 Twitter가 집중되지 않는 분산형 Instant Messaging 서비스 체제로 바뀌어야 한다고 조언하고 있고, 극단적으로 도메인 네임 서비스 같은 공공재로 전환해야 한다고 주장하는 사람도 있습니다.

일반적으로 Twitter같은 서비스는 사용자 수가 어느 정도 임계치에 다다르면 네트워크 효과가 생겨 새로 등록하는 사람 때문에 장애가 일어날 수 밖에 없습니다. DataPortability의 Faraday Media와 Chris Saad는 해결 방법으로 Twitter 트래픽의 많은 부분을 차지 하는 외부 서드파티 애플리케이션(Twitterific, AlertThingy, Twhirl등)과 메신저, SMS 등을 분산화 하라고 조언하기도 했습니다.

게다가 identi.ca라는 서비스는 아예 Twitter 같은 서비스를 laconi.ca라는 오픈 소스 소프트웨어로 만들어서 공개해 버렸습니다. 이 프로그램을 설치하면 Twitter 같은 소셜 메시징 사이트를 손 쉽게 만들 수 있습니다. 현재 크리에이티브 커먼즈 라이센스를 이용하고 있기 때문에 여기에 관여하는 유명한 분들이 많이 이용하고 있습니다.

틈새를 파고든 FriendFeed
이럴 때 사람들은 대안을 찾게 마련인데요. 남의 불행은 나의 기회라고 여기에 FriendFeed가 끼어듭니다. 이 서비스는 전직 구글 개발자들이 나와서 만든 일종의 소셜 서비스 신디케이션인데 Plxaso 같은데서 이미 있던 아이디어를 구현한 것입니다.

블로그, Twitter, Flickr, 유튜브 등 자기가 활동하는 웹 서비스의 데이터를 한꺼번에 나열해서 서로 뭐하고 놀고 있는지 친구들과 공유하는 것이죠. 솔직히 이게 처음 나왔을 때는 사람들에게 거의 관심을 못받았습니다.

그런데, FriendFeed에서 외부에서 받은 각 항목에다 직접 댓글을 다는 기능을 추가하여 신디케이션에서 메시징 기능을 추가하면서 논란이 벌어졌습니다. 내가 다는 댓글이 FriendFeed인지 원래 서비스에 가야할 댓글인지 논란이 된것이죠.

솔직히 신디케이션만 해야지 거기서 커뮤니케이션을 하게 한다는 건 올블로그에서 직접 댓글 서비스를 하는 것과 같은 거니까 문제가 있는 것이죠. 사실 직접적인 영향을 받은 게 Twitter인데 FriendFeed의 40%는 Twitter의 메시지를 받아오고 있었으니까요.

실제로 Twitter가 빈번히 장애가 나고 FriendFeed가 리플 기능을 제공하기 시작한 올해 상반기 부터 FriendFeed 이용률이 급속히 증가하고 있습니다. 주목할 점은 사용자의 체류 시간이 증가하고 있는데 이들 대부분은 Twitter에서 온 액티브 사용자라는 점은 이견이 없습니다.

아직은 FriendFeed가 완벽히 Twitter를 대체하고 있다던지 Twitter 사용자 탈퇴 러시가 있다던지 하는 것은 아닙니다. Twitter는 유명세 덕분에 서비스가 불안정함에도 불구하고 계속해서 사용자들이 유입되고 있고요. 특히 일본에서도 인기가 높습니다.

다만 소셜 네트웍 사이트들 끼리 경쟁이 극도로 심해 지고 있다는 것은 사실입니다. 얼마전 Facebook이 새로 화면 개편을 했는데 거의 FriendFeed와 비슷한 모양으로 바뀌었습니다. 오늘 나온 FriendFeed의 새 베타 버전을 보니 거의 Facebook과 닮아 있네요. 이제 서로 서로를 베끼면서 소셜 메시징 혹은 소셜 신디케이션의 UI 표준이 거의 자리 잡혔다고 봐야겠습니다.

결국 뛰어난 기술 기반이 중요할 듯
솔직히 말해 웹 2.0의 개방이니 참여니 하는 것은 다 개뿔같은 소리이고, 현재 미국의 소셜 네트웍 비지니스 이 동네는 서로 죽이지 못해 안달나있는 심한 경쟁 체제에 접어 들어 있습니다. 웹 2.0의 성공을 거울 삼은 이들 업체들이 ‘개방 플랫폼화’라는 기술적 성공 요소는 잘 접목을 시켜 왔지만 심한 경쟁 때문에 빛을 바래고 있다고 봐야 합니다.

오픈 ID니 DataPortablity니 하는 것도 성공한 젊은 창업자들에게는 별로 안중에 없습니다. 결국 성공한 서비스가 자기네들 역사를 새로 쓰게 되겠지요.

누구도 흉내 못낼 검색 기술을 웹을 성공적으로 플랫폼화 시킨 구글의 입장에서도 자기네들 안으로 팔을 굽는 (폐쇄된) 서비스들이 못마땅해 보일 겁니다. 게다가 폭발적인 성장을 이루고 있는 소셜 네트웍 시장에 어떻게든 한자리 차지해야 한다는 강박관념도 있어 만들어낸 오픈 소셜 같은 훌륭한 철학이 젊은 애들의 철없는 장난 같은 서비스 때문에 매장되는 것 처럼 보이죠. 어른말 안듣는 애들 마냥 답답해 보이기 까지 합니다.

반짝이는 아이디어와 문화적 코드가 만나 스파크를 일으키려는 이런 도찐개찐 같은 서비스 경쟁은 ‘기획’의 세계에서는 일상 다반사로 일어납니다. 하지만, Twitter와 FriendFeed 사이의 문제에서 불거진 “전 세계 사람들이 동시에 메시지를 주고 받을려면 어떻게 해야할까?”라는 문제에 해답은 기획에 있지 않습니다. 바로 기술에 있죠.

MS가 전 세계 사람들이 함께 쓰는 운영체제를 만들고, 구글이 전 세계 정보를 끌어 모으고, 아마존이 전 세계에 컴퓨터를 빌려주겠다고 나서는 기반에는 자기 기술에 대한 경쟁력이 있기 때문입니다. 단기적 서비스 확장 뿐 아니라 장기적 인터넷 기업의 생존을 위해서도 기술 확보 능력이 정말 중요하다는 것을 반증하는 것이죠.

Twitter의 예에서 보듯이 아이디어가 구현되어 글로벌 서비스로 나오는 단계에서 기술력이 얼마나 중요한가 다시 한번 깨닫을 수 있습니다. 근본적 물음에 대한 해결 없이 수틀리면 그냥 돈(장비)으로 쳐바르는 우리네 기술력도 한번 돌이켜 봐야겠지요. 그리고 주위에 개발자들이 있으면 격려의 한마디 건네 주시는 것도 좋겠습니다.

이어지는 글… Twitter와 FriendFeed 두번째 이야기 – 스코블은 블로그로 돌아올 것인가?

새벽에 쓰는 글이라 좀 횡설수설합니다. 너무 심각하게 받아들이지 마시고 그냥 세상 밖 돌아가는 이야기라고 생각하시고 읽어 주세요.

http://channy.creation.net/blog/550

Posted by 솔라리스™
:

어도비시스템즈가 풍부한 이용자 경험(UX)을 제공하는 멀티미디어 콘텐츠를 다양한 운영체제와 기기에서 자유롭게 배포할 수 있는 ‘N스크린’ 전략을 본격화한다.

한국어도비시스템즈는 3월7일 서울 코엑스에서 ‘어도비 리프레시’ 세미나를 열고 이와 관련한 개발자와 디자이너, 콘텐츠 제작사 지원 전략을 소개했다.

‘어도비 리프레시’는 지난해 10월 미국 LA에서 연 글로벌 컨퍼런스 ‘어도비 맥스’ 주요 발표 내용을 나라별로 요약해 소개하고자 마련된 행사다. 이번 행사에는 어도비 플래시 기술 전문가들이 방한해 최신 동향과 어도비 관련 기술 및 제품 소개를 진행했다.

‘리치 앱’ 한 번 개발해 다양한 기기로 배포

이번 발표에서 가장 눈에 띄는 건 ‘모바일 플랫폼’ 확장 기술이다. 요컨대 지금껏 웹이나 인쇄매체에 주력하던 디자인·멀티미디어 콘텐츠를 다양한 모바일 기기로 손쉽게 확장할 수 있는 도구를 제공하겠다는 뜻이다.

어도비는 ‘크리에이티브 스위트’(CS) 제품들을 통해 종이 잡지와 웹용 콘텐츠를 손쉽게 변환·배포할 수 있는 도구를 제공해 왔다. 여기에 스마트폰과 태블릿 보급 추세에 맞춰, 앞으로는 한 번 제작한 멀티미디어 콘텐츠를 모바일 기기용 응용프로그램(앱)으로 손쉽게 변환하고 배포할 수 있는 기능을 제공하게 된다.

지난해 선보인 ‘어도비 플래시 CS5′는 최근 기능을 판올림하면서 ‘아이폰 OS’ 변환 기능을 새롭게 선보였다. 플래시 개발자가 어도비 플래시 CS5로 멀티미디어 콘텐츠를 제작한 뒤, 이를 클릭 몇 번으로 간단히 아이폰·아이패드용 네이티브 앱으로 변환할 수 있는 기능이다. 현재 애플 앱스토어에는 플래시 CS5로 제작해 변환된 앱이 150개 넘게 등록돼 있다.

이렇게 iOS용으로 만든 앱은 안드로이드용 앱으로도 손쉽게 변환할 수 있다. 한 번 만든 콘텐츠를 아이폰과 아이패드는 물론, 크기와 해상도가 제각각인 다양한 안드로이드용 스마트폰과 태블릿용 앱으로 손쉽게 확장·배포할 수 있게 된 셈이다. 윈도우나 맥OS, 리눅스 등 다양한 데스크톱 운영체제(OS)에서 동시에 쓸 수 있는 건 기본이다.

TV 화면으로도 확장 가능하다. 독립 실행 프로그램 형태로 제공되는 ‘어도비 에어(AIR)’를 TV에서도 쓸 수 있게 해주는 ‘에어 포 TV’를 활용하면 된다. 이 역시 어도비 플래시 CS5에서 추가 작업을 하지 않아도 손쉽게 TV용 앱으로 변환할 수 있다. 어도비는 이를 위해 2008년부터 다양한 기기에서 어도비 에어 기반으로 플래시 애플리케이션을 구동할 수 있는 ‘오픈스크린’ 프로젝트를 여러 하드웨어 제조사와 협력해 진행해오고 있다.

웹표준 기반 기기별 맞춤 웹사이트 제작 지원

개발자가 힘들이지 않고 차세대 웹표준 기술 기반 웹사이트와 휴대기기용 콘텐츠를 제작할 수 있는 기능도 제공한다. ‘어도비 드림위버 CS5′가 최근 적용한 새로운 기능을 이용하면 된다.

‘드림위버’는 원래 웹사이트 저작도구였지만, 이제 스마트폰과 태블릿용 모바일 화면을 손쉽게 구현할 수 있는 도구로 확장하는 모양새다. 최근 판올림한 드림위버 CS5는 HTML5와 CSS3 태그를 모두 지원하며, 코드를 자동 검색하고 자동완성 기능을 제공해 개발자나 디자이너가 전문 지식 없이도 최신 웹표준 기반 웹사이트를 제작할 수 있게 했다. 최신 웹킷 브라우저를 탑재해 ‘라이브뷰’ 기능으로 HTML5와 CSS3 기반 웹사이트를 실시간 확인하며 제작할 수 있으며, 한 번 제작한 웹사이트를 ‘멀티스크린’ 기능을 이용해 PC용 웹, 스마트폰용 모바일웹, 태블릿용 웹사이트로 자동 변환할 수 있게 했다.

폴 버넷 어도비 플래시·오픈웹 전도사는 “지금까지는 개발자나 디자이너가 각 기기별로 언어를 알아야 하고 앱을 새로 만들어야 하는 어려움이 있었지만, 어도비 CS5와 에어, 플래시를 이용하면 서로 다른 이용자 경험을 제공하는 다양한 기기별 맞춤 콘텐츠를 손쉽게 제작·변환할 수 있다”라며 “더욱 흡입력 있고 인터랙티브한 콘텐츠를 모바일로 손쉽게 확장할 수 있게 됐다”고 어도비 기술의 장점을 소개했다.

어도비는 스마트폰과 태블릿 영역에서 플래시 기술 확장을 위해 ‘블랙베리’ 제작사인 캐나다 리치인모션(RIM)과도 긴밀히 협력하고 있다. ‘프로페셔널용 태블릿’을 내세운 RIM의 새 태블릿 ‘플레이북’은 제작 단계부터 OS 자체에서 플래시를 손쉽게 구동할 수 있게 했다. 플레이북에 기본 탑재된 앱은 모두 어도비 에어 기반으로 제작됐다.

리차드 갤반 어도비 플래시 프로페셔널 제품 매니저는 이날 기조연설에서 “어도비는 한 번 만든 콘텐츠를 다양한 기기에서 손쉽게 구현하는 멀티스크린 기능을 지원하고, 코드를 잘 모르는 디자이너도 손쉽게 인터랙티브 콘텐츠를 제작할 수 있는 디자이너 접근성을 개선하는 데 주력하고 있다”라며 “이용자가 어도비 주요 도구를 활용해 보다 쉽고 빠르게 풍성한 콘텐츠를 제공하도록 장기 계획을 갖고 지원을 다하겠다”고 밝혔다.

출처 : http://www.bloter.net/archives/52507

Posted by 솔라리스™
:

지난 2월 21일 SK커뮤니케이션즈 본사 5층 대강당에서 진행된

<2011년 네이트 앱스토어 비즈니스 파트너 간담회>의 발표자료를 공유합니다.

 

많은 개발사 여러분들께서 참석해주셔서 더욱 빛났던 행사였습니다.

참석해주신 모든 개발사 여러분들께 감사드립니다.


네이트 앱스토어 현황 | SK컴즈 오픈소셜사업팀 김영을 부장
http://slidesha.re/gqLHnG

 

* * *

네이트 앱스토어 모바일 사업 전략 | SK컴즈 오픈소셜사업팀 박지연 차장
http://slidesha.re/hvlgh8

 

* * *

T Store의 현재와 SNG | SKT 컨텐츠마켓사업팀 김지은 매니져
http://slidesha.re/i7H9Nn

 

* * *

2011 개발사 지원 프로그램 | SK컴즈 오픈소셜사업팀 최윤난 차장
http://slidesha.re/g3lpJb

 

* * *

2010년 PF 성공사례발표 - 에브리타운 | 피버스튜디오 김대진 대표
http://slidesha.re/exsWAs

 

* * *

Social Games on Azure | 한국마이크로소프트 오성미 차장
http://slidesha.re/hgNnfT

Posted by 솔라리스™
:

Scalable Social Games, Robert Zubek of Zynga (liveblog)

Social games interesting from an engineering point of view sinc ethey live at the intersection of games and web. We spend time thinking about making games fun, making players want to come back. We know those engineering challenges, but the web introduces its own set, especially around users arriving somewhat unpredictably, effects where huge populations come in suddenly. SNSes are a great example of this, with spreading network effects and unpredictable traffi fluctuations.

At Zynga we have 65m daily players, 225m monthly. And usage can vary drastically — Roller Coaster Kingdom gained 1m DAUs in one weekend going from 700k to 1.7m. Another example, Fishville grow from 0 to 6m DAUs in one week. Huge scalability challenges. And finally, Farmville grew 25m DAUs in five months. The cliff is not as steep but the order of magnitude difference adds its own challenge.

Talk outline: Introducing game developers to best practices for web development. Maybe you come from consoles or mobile or whatever, the web world introduces its own set of challenges and also a whole set of solutions that are already developed that we steal, or, uh, learn from. :) If you are alreayd an experiened web developer, you may know this stuff already.

2 server approaches and two client approaches. So you get three major types.

1. Web server stack + HTML, Mafia Wars, Vampires, et
2. Web server stack + Flash, Farmville, Fishwville, Cafe world
3. Web + MMO stack + Flash, yoVille, Zynga Poker, Roller Coaster Kingdom

Web stack based on LAMP, logic in PHP, HTTP Comms. Very well understood protocol, limitations well known.

Mixed stack has game logic in MMO server such as Java, web stack for everything else. When web stack limitations are preventing the game development. Use web for the SNS pieces.

Fishville:

DB servers
–> web stack
Cache and queue servers

yoVille:

DB    >    MMO
Cache    >    Web & CDN

Why web stack? HTTP is very scalable, very short lived requests, scales very well, and easy to load balance. Each request is atomic. Stateless, easy to add more servers. But limitations esp for games: server-initiated actions (NPCs running around, if you come to lose, the monster reacts…) are hard to do over HTTP, since it is request/response. There are some tricks, like the long poll, but fundamentally this makes it harder to scale. Load balancers will get unhappy with you, you can saturate a connection.

The other thing is storing state between requests. This is more particular to game dev than web. Say you areplaying Farmville and collecting apples. You do a bunch of actions which result in many different requests to the servers, but we want to make sure that only the first click gives you an apple, so you cannot click a dozen times on one tree. Whih means stored state and validation. If you had clients talking to many web servers, you cannot use the DB< the poor thing will fall over. If we can guaratee that the client only talks to one web server, you can store it there, and save to db later. But this is tricky to do. Ensuring that people are no allowed to break session affinity even in the presence of maliious clients and browsers… hard.

So instead, you can wrap th DB servers in a caching layer that is faster that does not hit the DB all the time, such as Network Attached Caching. This works much better.

MMO servers… minimally MMO. Persistent socket connection per lient, live game support such as chat and server side push. Keeps game state in memory. We know when a player logs in and load from DB then… session affinity by default. Very different from web! We can’t do the easy load balancing like on web.

Why do them? They are harder to scale out because of load balaning. But you get stuff like the server side push, live events, lots of game state.

Diagram:

DB servers, maybe less caching wrapping it — talks to both web server and MMO server, then those both talk to client.

On the client side, things are simpler.

Flash allows high prodution quality games, game logic on client, can keep open socket. You can talk any protocol you want.

HTML+AJAX  the game is “just” a web page, minimal system reqs, and limited graphics.

SNS integration. “Easy” but not relate dto scaling. Call the host network to get friends, etc. You do run into lateny and scaling problems, as you grow larger you need to build your infrastructure so it can support gradeful performane degradation in the face of network issues. Networks provide REST APIs and sometimes client libraries.

Architectures:

Data is shared across all three of these: database, cache, etc.

Part II: Scaling solutions

aka not blowing up as you grow.

Two approaches: scaling up or scaling out.

up means that as you hit processor or IO limits, you get a better box.
out means that you add more boxes

The difference is largely architectural. When scaling up, you do not need to change code or design. But to scale out you need an architecture that works that way. Zynga chooses scaling out, huge win for us. At some point you cannot get a box big enough fast enough. Must easier to add small boxes, but you need the app to have architectural support for it.

Rollercoaster Kingdom gained a lot of players quickly. We started with one database, 500k DAUs in a week. Bottlenecked. Short term scaled up but switched to scaling out next.

Databases, very exciting. The first to fall over. Several ways to scale them. Terms unique to mySQL here but concepts the same for other systems:

Everyone starts out with one database, which is great. But you need to keep track of two things -  the limit on queries per second, do benchmarking using standard tools like SuperSmack. You want to know your q/s ceiling, and beyon that how will it perform. There are optimizations you can use to move it. And two, you need to know the player’s query profile in terms of inserts, selets, updates, and average profile per second. It might trail your DAU number, which is nice because then you can project q/s and know when you will reach capacity.

If your app grows then you will need to scale out.

Approach one, replicating data to read only slaves. Works well for blogs and web properties but hnot games, because games have a higher modification profile so your master is still a bottleneck. But useful for redundancy.

Approach two, multiple master. Better because of split writes, but now you have consistency resolution problems, which can be dealt with but increases CPU load.

Approach three and best and push the logic for resolution up to the app layer, a standard sharding approach. The app knows which data goes to which DB.

Partition data two ways”:

vertical by table, whih is easy but does not scale with DAUs. MOve players to a different box from items.

horizontal by row. Harder to do but gives best results. Different rows on different DBs, need good mapping from row to DB. Stripe rows across different boxes. Primary key modulo # of DBs. Do it on an immutable property of the row.  A logical RAID 0. Nice side eeffect to increase capacity… to sale out a shard, you add read only slaves, sync them, then shut down, cut replication, and hook it back up. Instant double capacity.

More clever schemes exist. Interaction layers which check where to go… but the nice thing about this is how straightforward it is.  No automatic replications, no magic, robust and easy to maintain.

YoVille: partiioning both ways, lots of joins had to be broken. Data patterns had to be redesigned, with sharding you need the shard id per query. Data replication had trouble catching up with high violume usage. In sharded world  cannot do joins across shards easily, there are solutions but they are expensive. Instead, do multiple selects or denormalize your data. Say a catalog of items and inventory, and you watch to match them. If catalog is small enough, just keep it in memory.

Skip transactions and foreign key constraints. Easier to push this to the app layer. The more you keep in RAM the less you will need to do this.

Caching.

If we don’t have to talk to the DB, let’s skip it. Spend memory to buy speed. Most popular right now is memache, network attached ram cache. Not just for caching queries but storing shared game state as well, such as the apple picking example. Stores simple key value pairs. Put structured game data there, and mutexes for actions across servers. Caveat: it is an LRU (least recently used) cache, not a DB. There is no persistence! If you put too much data in it, it will start dropping old values, so you need to make sure you have written the data to DB>

bc it is so foundational, you can shard it just like the DB. Different keys on different servers, or shard it veritcally or horizontally.

Game servers.

Web server part is very well known. Load-balance. Preferred approach is to load balance with a proxy first. This is nice from a security standpoint… but it i a single point of failure, capacity limits since the proxy will have a max # of connections.

If you hit those limits you load balance the load balancers… and using DNS load balancing in front of it. It doesn’t matter if dns propagation takes a while.

The other thing that is useful is redirecting media traffic away from media servers… swfs are big, audio is big, do not serve from the same place as game comms. You will spend all yor capaity on media files. Push it through a CDN, and if you are on the cloud already you can store them there instead. CDN makes it fast, sine the assets are close to the users. Another possibility is to use lightweight web servers that only server media files. But essentially, you want big server bank to only talk game data, not serve files. Seevral orders of magnitude performance by doing this.

MMO servers, the unusual part of the setup! Scaling is easiest when servers do not need to talk to each other. DBs can shard, memcache an shard, web can load balance farms, and MMOs? well, our approach is to shard like other servers.

Remove ny knowledge they have about each other and push complexity up or down. Moving it up means load balancing it somehow. Minimize interserver comms, all participants in a live event should be on the same server. Scaling out means no direct sharing — sharing thru third parties is OK, a separate service for that event traffic.

Do not let players choose their connections. Poor man’s load balancing, is a server gets hot remove it from the LB pool, if enough servers get hot, add more instances and send new connections there. Not quite true load balancing which limits scalability.

In deployment, downtime = lost revenues. In web just copy over PHP files. Socket servers are harder. How to deploy with zero downtime? Ideally you set up shadow new servers and slowly transition players over. This can be difficult — versioning issues.

For this reason, this is all harder than web servers.

Capacity planning.

We believe in scaling out, but demand can change fast.how to provision enough servers?  Different logistics. Do you provision physical servers or go to the cloud? If you have your own machines, you have more choice and controll and higher fixed costs. With cloud lower costs, faster provisioning, canot control CPU, virtualized IO, etc. On cloud easier to scale out than up.

For a legion of servers you need a custom dashboard for health, Munin for server monitoring graphs, and Nagios for alerts. First level for drilldown is graphs for every server family separately so you can isolate it to a given layer in the system. Once you know memache usage spiked, then you can drill down to particular machines…

Nagios… SMS alerts for server load, CPU load exeeds 4, test account fails to connect after 3 retries.

Put alerts on business stats too! DAUs dropping belo daily average for example. Sometimes they react faster than server stats.

If you are deployed in cloud, network problems are more common. Dropping off net or restarting is common. Be defensive, Reduce single points of failure, program defensively. This includes on the game side.

Q&A:

q: why mySQL? Other DBs are better for scaling.
a: there are other DBs that have been around longer, have greater community, but we don’t use the features those large DBs do. Looking back at the sharding slides — we don’t do a lot of even things like transactions. Easier to move that complexity to the app layer. Once you are on that path, it is a good solution.

q: did you benchmark, that sort of thing, for the different DBs?
a: yes, of course.

q: and for data integrity, if you threw foreign key constraints, that sounds scary! Is it kind of a nightmare?
a: No, not too bad at all, actually. Esp if you do not hit the DB all the time, you ind you don’t get into those dangerous situations as often.

q: is the task when you add more tables… is it as complex?
a: not too bad, has worke well.

q: assuming browsers pick it up, are you guys looking into webGL?
a: many technologies interesting, 3d in browser, silverlight. I would be interested in using them personally… once they achieve high market penetration.

q: why flash?
a: everyone has it. Very pragmatic approach.

q: Do you back up dbs?
a: of course

q: and how?
a: onc eyou go with cloud and amazon, you have to use that approach…  we have a number of redundant backups solutions.

q: I guess many joins are across friends… they have to tlak to multiple shards. Do you try to put friends on same shard?
a: no, everyone has different friends.

q: on SNS integration, did you run into issues with PHO not supporting asynh, with delays from answers from the SNS, running out of threads?
a: you will encounter delay with SNS comms, just part of the overall insfrastruture, could be anytihng, not just PHP. You have to program around it, have to find good solutions for dealing with it when it happens bc it will.

q: So you don’t switch from PHP, delay the process?
a: we did encounter a number of places where we had to dig deep into PHP in order to make it work well on that scale.

q: did you patch PHP?
a: we, uh… yes.

q: what are you feeling on tools like the no SQL sort of thing
a: we look into those atively, one the tech matures, it will be a very good candidate fot this sort of thing. But not currently implemented.

q: on sharding, you said use modulo to distrbute load. Once you have found a bottleneck, howdo you prepare the data to be moved from one shard to another.
a: You don’t move people between shards. You just copy a shard to two machine, and both have redundant, and then remove the redundant data.

q: on partitioning, partitioning to two tables. Say item trading that goes across two DBs, transactions may break? Changing ownership on two different dbs?
a: you need to do a guarantee across multiple DBs, putting the data in a memcache layer, locking it, then doing the write, or putting it in the app layre, implementing”transactions lite”

q: being on the cloud did you have to not use a service approach and have each PHP layer write direct to the DB instead of use a service layer? Say an MMO, achievments or presence services. Do you keep the servie layer as a web servie, or write direct to the DB? Your service call time can add time… even on the cloud.
a: Yes, you want this to be nicely modular… we end up not putting it on different machines. Same box as the game logic so there is no network traffic, so there is no separate layer between. So modular, but not in terms of network topology.

Posted by 솔라리스™
: