Skip to content

cache facebook

gaiaops edited this page Oct 24, 2011 · 7 revisions

See: https://github.com/gaiaops/gaia_core_php/blob/master/tests/facebook/apicache.t

Why cache api calls?

No matter how well you optimize your application, there are still some things outside of your control. If your application makes 3rd-party api calls, there is nothing you can do to make the 3rd-party website return data faster. API calls over the WAN are problematic and slow in general.

Facebook API calls are no different. Periodically requests come back blank, hang, or timeout. Programming defensively against these problems, we can retry http requests and cache previous values of that api call to use in subsequent requests.

Simply put, the best api call is the one you never have to make.

Caching Wrapper

Our api is a simple decorator for the original facebook object. Instantiate your facebook object as you would normally, but then wrap it with the caching layer:

$facebook = new Gaia\Facebook\APICache( new Facebook( $config ), $memcache );

This decorator object passes all calls to the object through to the core facebook object. But it does something special with the api method. The decorator intercepts this call and checks the cache to see if a recent value exists for this call. If so, it returns the value instantly. This can change an api call from several seconds down to milliseconds.

Refreshing the cache

Periodically we want to refresh the cache. But we want to be able to fall back on the stale cached version if we can't get anything back from facebook. So we place the value in the cache for a very long time but with an extra variable in the data payload indicating when the cache should be refreshed. When we hit this timeout, we attempt to grab the information from facebook, but set a very short curl timeout and an even shorter connect timeout. If the call times out, we can just return some stale information. We wait a minute or two and refresh the data when facebook is being more responsive. This is much better than getting stuck in the scenario where an api call hangs for 30 seconds or more. Our users never even know what is going on.

Retries

Sometimes an api call will hang or return a bad response on first try but subsequent requests might go through right away. We check for errors and try the call again if we don't have the data in the cache. We only retry if we don't have anything in the cache. If a stale version of the data exists, we just try once, then return the stale data instead.

User authentication, and caching

The facebook php sdk assumes all requests will be coming from a particular user. Each request is signed with an oauth token or some other form of signed request. If no token exists, it simply passes the application token in hopes that it will be enough to authorize the request. Many of the api calls you make to facebook are user specific. When we cache the api call results we embed the user token in the cache key so that users don't accidentally gain access to a cached version of the data that doesn't belong to them.

But not all facebook api calls need user authentication. Some are completely generic. Take this example:

curl http://graph.facebook.com/cocacola
{"id":"40796308305","name":"Coca-Cola","picture":"http:\/\/profile.ak.fbcdn.net\/hprofile-ak-snc4\/276879_40796308305_1578420141_s.jpg","link":"http:\/\/www.facebook.com\/coca-cola","likes":34145829,"category":"Food\/beverages","website":"http:\/\/www.coca-cola.com","username":"coca-cola","founded":"1886","products":"Coca-Cola is the most popular and biggest-selling soft drink in history, as well as the best-known product in the world.\n\nCreated in Atlanta, Georgia, by Dr. John S. Pemberton, Coca-Cola was first offered as a fountain beverage by mixing Coca-Cola syrup with carbonated water. Coca-Cola was introduced in 1886, patented in 1887, registered as a trademark in 1893 and by 1895 it was being sold in every state and territory in the United States. In 1899, The Coca-Cola Company began franchised bottling operations in the United States.\n\nCoca-Cola might owe its origins to the United States, but its popularity has made it truly universal. Today, you can find Coca-Cola in virtually every part of the world.","parking":{"street":0,"lot":0,"valet":0},"hours":{"mon_1_open":0,"mon_1_close":0,"tue_1_open":0,"tue_1_close":0,"wed_1_open":0,"wed_1_close":0,"thu_1_open":0,"thu_1_close":0,"fri_1_open":0,"fri_1_close":0,"sat_1_open":0,"sat_1_close":0,"sun_1_open":0,"sun_1_close":0,"mon_2_open":0,"mon_2_close":0,"tue_2_open":0,"tue_2_close":0,"wed_2_open":0,"wed_2_close":0,"thu_2_open":0,"thu_2_close":0,"fri_2_open":0,"fri_2_close":0,"sat_2_open":0,"sat_2_close":0,"sun_2_open":0,"sun_2_close":0},"payment_options":{"cash_only":0,"visa":0,"amex":0,"mastercard":0,"discover":0},"restaurant_services":{"reserve":0,"walkins":0,"groups":0,"kids":0,"takeout":0,"delivery":0,"catering":0,"waiter":0,"outdoor":0},"restaurant_specialties":{"breakfast":0,"lunch":0,"dinner":0,"coffee":0,"drinks":0}}

This api call and many others can be made without any authentication at all.

curl https://graph.facebook.com/19292868552
{"id":"19292868552","name":"Facebook Platform","picture":"http:\/\/profile.ak.fbcdn.net\/hprofile-ak-ash2\/211033_19292868552_7506301_s.jpg","link":"http:\/\/www.facebook.com\/platform","likes":2796587,"category":"Product\/service","website":"http:\/\/developers.facebook.com","username":"platform","founded":"2007","company_overview":"Facebook Platform enables anyone to build social apps on Facebook and the web.","mission":"To make the web more open and social.","parking":{"street":0,"lot":0,"valet":0},"hours":{"mon_1_open":0,"mon_1_close":0,"tue_1_open":0,"tue_1_close":0,"wed_1_open":0,"wed_1_close":0,"thu_1_open":0,"thu_1_close":0,"fri_1_open":0,"fri_1_close":0,"sat_1_open":0,"sat_1_close":0,"sun_1_open":0,"sun_1_close":0,"mon_2_open":0,"mon_2_close":0,"tue_2_open":0,"tue_2_close":0,"wed_2_open":0,"wed_2_close":0,"thu_2_open":0,"thu_2_close":0,"fri_2_open":0,"fri_2_close":0,"sat_2_open":0,"sat_2_close":0,"sun_2_open":0,"sun_2_close":0},"payment_options":{"cash_only":0,"visa":0,"amex":0,"mastercard":0,"discover":0},"restaurant_services":{"reserve":0,"walkins":0,"groups":0,"kids":0,"takeout":0,"delivery":0,"catering":0,"waiter":0,"outdoor":0},"restaurant_specialties":{"breakfast":0,"lunch":0,"dinner":0,"coffee":0,"drinks":0}}

If you want to cache these calls in a way that will share the cached information for any user that accesses it, you need a modified core facebook api object to skip signing the request or performing any authorization. My test example demonstrates this approach.

What if I don't want to use the facebook php-sdk

The facebook php-sdk is not exactly lightweight. In most cases you just need to make a simple curl call to access the graph api. Why go to all the trouble of using their wrapper? If you don't need signed requests or user access tokens, you may be right (though it is doubtful you'd be doing much more than shaving a few milliseconds off of the request). However, you can write your own curl caching api using logic similar to what is embedded into our wrapper for the sdk.

The methodology is straight-forward. First, check to see if you have a cached version of the api call. If not, go ahead and run the api call as normal. Stick the curl response into the cache, along with a timestamp of when the cache should be refreshed. The next time you get the cached data, If you have passed that timeout value, go ahead and run the curl call, but lower curl timeout values if you are refreshing the data, vs fetching it for the first time. Return stale data if the curl call times out.

Facebook-SDK with alternative persistence

TODO: The code is written, just need to document it with some examples.