SongPop Behind The Scene

Ada yang tahu SongPop?

Mestinya sih banyak yang pada tahu yah :)
Jadi Song Pop itu adalah game tebak lagu di Facebook buatan FreshPlanet. Selain di Facebook, SongPop juga bikin mobile app-nya untuk iOS dan Android.

songpop-3

Cara mainnya sih gampang. Pilih lawan kamu dari friendlist facebook, pilih genre musik, nanti akan ada 5 lagu yang harus kamu dengerin selama beberapa detik dan tebak judul lagu atau nama penyanyinya.

 

Sederhana kan?  Yoi, kesederhanaan ini yang justru membuat sukses game-game di jejaring sosial.

Nah, kali ini kita ga akan membahas gimana cara bermain SongPop yang baik dan benar sesuai dengan tangga lagu billboard. Kali ini mari kita ngomongin mengenai isi dapur FreshPlanet, bagaimana mereka running SongPop ini.

Menurut data dari AppData (eh berima :P) SongPop FacebookApp mempunyai lebih dari 11,3 juta monthly active users (MAU). Daily active users (DAU) sendiri dah di atas angka 2 juta, dan game ini baru diluncurkan sekitar Mei 2012 kemarin. Belum ada setahun dan belum termasuk yang dari iOS ataupun Android Client. Nah silakan berhitung deh.

Nah, gimana sih caranya si FreshPlanet ini handle trafik game yang sebesar itu?
Di Wikufest 2013 kemarin ada adik kelas yang bertanya ke saya gimana caranya bikin layanan sosial media yang proper. Tapi karena saya belum tahu konsepnya dan cuman dapat info kalau dia ingin doing everything all in, saya cuman mengajukan pertanyaan dasar terkait infrastruktur dulu. Dan sepertinya pertanyaan saya tadi cukup membuatnya berhitung dengan serius :) *baru soal foto doang sih, belum ke yang lain-lain :P*

google-app-engine-logo

Oke, kembali ke laptop.
FreshPlanet infrastrukturnya menggunakan Google App Engine (GAE) dan Google Cloud Storage. Di tahun 2013 ini, ga jaman harus bangun infrastruktur sendiri untuk semua hal. Buat yang ikutan kelas CloudComputingnya si Dondy harusnya dah dapat gambaran yah.

Nah GAE ini sudah bukan kategori IaaS tapi lebih ke arah PaaS. Dengan memanfaatkan GAE ini, FreshPlanet mampu membuat Song Pop untuk dapat autoscale (baca: auto) hingga 60 juta user, 1 juta DAU, 17TB/hari delivery content (tentu saja lagu dan gambar), 10,000+ query/detik. Song Pop sendiri dihandle oleh “cuman” 6 orang, dan cuman 1 saja yang kerja fulltime ngurusin backend-nya.

songpop_gae_gc

Mengutip wawancara Zafir Khan dari GoogleAppEngine dengan Olivier Michon dan Alexis Hanicotte dari FreshPlanet ada beberapa tips yang mereka terapkan untuk optimalisasi dan tentunya skalabilitas si Song Pop itu sendiri. Diantaranya:

IMG_7213Denormalisasi: data user Song Pop tersebar di bermacam model, tapi tetap koleksinya di pool di satu tempat untuk mengurangi read latency.
Caching: Masih nyambung soal denormalisasi, data lawan main kita akan di cache. Jadi sistem ga akan selalu melakukan “query” ke user data cuman buat tau lawan main kita itu sapa ajah. Analoginya, query sekali, cache, query lagi kalo ada trigger cachenya tadi sudah obsolete/expired. FreshPlanet menggunakan Memcache untuk 2 hal di atas. Memcache itu fitur di GAE kok, dan orang-orang FreshPlanet menggunakan Python API-nya GAE buat kebutuhan ini. Hayo hayo pada belajar Python sana. It’s a fun languange.
Strategi: Iyah, bikin aplikasi itu harus ada strateginya. Apalagi kalau aplikasinya tadi bukan sekedar one-time asal jadi doang. Memilih GAE, beli premier support dari Google untuk kebutuhan GAE-nya tadi adalah salah satu strategi FreshPlanet untuk SongPop. Ceritanya saat DAU Song Pop mencapai angka 1 juta, query Datastore (yang digunakan untuk mencari lawan main secara acak) mulai lemot dan banyak timeoutnya. Untuk beresin ini, FreshPlanet menentukan deadline dan aktivitas fallback sistemnya, kemudian dengan bantuan Premier support melakukan tracing dan identifikasi yang bikin lemot query Datastore tadi apa. Dan seperti disebutkan di wawancara tersebut, masalahnya ternyata karena Datastore bergantung ke berbagai macam properti. Walaupun sudah di-index, tetap saja jumlah kebanyakan. Solusi yang muncul akhirnya ada opsi mau nambah sebuah composite index yang berisi semua properti yang dibutuhkan atau menggabungkan properti-properti tadi jadi satu.
Content Delivery: masih hosting static content dan ‘dynamic content’ atau apps di satu server yang sama? Satu mountpoint, LUN, directory yang sama? Nah coba baca-baca soal CDN (Content Delivery Network) deh :) Song Pop menggunakan Google Cloud Storage untuk serving lagu dan gambar dengan lebih dimanapun si user tadi berada. Jadi kalau kita main di Indonesia, sample lagu-nya ga harus kita download langsung dari server di Amerika sana misalnya :)

Detilnya wawancaranya bisa dibaca sendiri di sini deh. Walau jika dibaca keseluruhan hampir ga ada detil teknis howto-nya, tapi konsep yang disinggung di sini seru banget.

So… GAE bisa dicoba-coba gratis lho :) SDK-nya juga bisa didownload n insall di komputer masing-masing.

Dan… sekarang saatnya mengganti huruf P di PHP itu dengan Python :)

Tambahan referensi:

How To Tweets Your Current iTunes Track

Well, it’s nothing. Last Saturday I’ve stuck in office, have nothing to do but arranging my iTunes collection. Been awake since Friday night, and it seems my sleep disorder haven’t been healed yet :( So while i’m twittering i want also to use my current track played in my iTunes to be posted in twitter.

I remember i’ve made a small python script to do this couple years ago but lost the source code *facepalm* Hmmm, I think i gave the codes to @cothat and @rara79. But it will be rude to wake up those guys just for asking source codes :P

I rewrite it all over again, just to get me bored and hopefully sleep after that :P

Anyway, these are the codes that make it happen :)

You can make it simple and more robust though. I don’t put any exception to check whether my iTunes is running or not (yet) to prevent flooding my timeline with probably blank message :P

My previous version will run a loop and sleep couple of seconds. And within that loop it will check is the iTunes playing same song as previous or not. It will post to twitter if new song is detected :) Useful if you like to skip song in the middle.

[cc lang=’python’ ]
“””
simple Python Script to get current track being played in iTunes
and tweet it (and also update your FB status)
dependencies:
– py-appscript, Control AppleScriptable applications from Python
– tweepy, Twitter API for Python

Author: Nuri Abidin
Date: 2012-02-04
“””
from appscript import *
from decimal import *
from ConfigParser import ConfigParser
from optparse import OptionParser
from sys import exit
from time import strftime, sleep, localtime
import string, os, re, sys, tweepy

class nuyTwiTunes:
def __init__(self, config_file=””):
config = self.getconf(config_file)
self.twitter_consumer_key = config[‘consumer_key’]
self.twitter_consumer_secret = config[‘consumer_secret’]
self.twitter_access_token = config[‘access_token’]
self.twitter_token_secret = config[‘token_secret’]
self.print_console(‘Initiating the application…’)

# just printing formatted text to console
def print_console(self, s):
t = strftime(‘%Y-%m-%d – %X’)
print “%s %s” % (t,s)

# read the config file
def getconf(self, config_file):
param = {}
c = os.path.expanduser( config_file )

if not os.path.exists(c):
self.print_console(‘ERROR: No configuration file: %s’ % c)
exit(1)

conf = ConfigParser()
conf.read(c)

#read config items to dictionary
for section in conf.sections():
for items in conf.items(section):
param[items[0]] = items[1]
return param

# ‘read’ the current track being played in iTunes
def get_itunes_track(self):
myApp = app(‘iTunes’)
mySong = (“%s – %s” % (myApp.current_track.artist(), myApp.current_track.name()) )
songTime = myApp.current_track.time();
timeChunks = string.split(songTime, “:”)
playingTime = (Decimal(timeChunks[0]) * 60) + Decimal(timeChunks[1])
return mySong, playingTime

# post to twitter
def post_tweet(self, the_tweet=”Hai ^_^”):
auth = tweepy.OAuthHandler(self.twitter_consumer_key, self.twitter_consumer_secret)
auth.set_access_token(self.twitter_access_token, self.twitter_token_secret)
api = tweepy.API(auth)
api.update_status(the_tweet)

# post current iTunes track to twitter
def tweet_the_tunes(self):
# play and sleep until track changes
while True:
(track_name, play_time) = self.get_itunes_track()
tweet = “iPlay: %s ^_^ #fb” % (track_name)
self.print_console(“%s –%s seconds–” % (tweet,play_time) )
self.post_tweet(tweet)
sleep(play_time)

def main():
usage = “usage: %prog [options]”
parser = OptionParser(usage=usage)
parser.add_option(‘-c’, ‘–config’,
dest=”config_file”,
default=”nuy_tunes.conf”,
)
(options, args) = parser.parse_args()

try:
if options.config_file:
config_file = options.config_file
else:
config_file = “./nuy_tunes.conf”

my_tunes = nuyTwiTunes( config_file )
my_tunes.tweet_the_tunes()
except:
raise
parser.print_help()
exit(1)

if __name__ == “__main__”:
main()
[/cc]

and the configuration file is simply like this

[cc lang=’python’ ]
[twitter]
consumer_key = your_twitter_consumer_key
consumer_secret = your_twitter_consumer_secret
access_token = your_twitter_access_token
token_secret = your_twitter_token_secret
[/cc]

Once more thing …
You need to use a Mac to do this :P
Having one is a good thing :P

*yawn*
and blogging this script make me feel sleepy already …