Memcache Examples

This page provides Python code examples for using memcache. Memcache is a high-performance, distributed memory object caching system that provides fast access to cached data. To learn more about memcache, read the Memcache Overview .

The memcache Pattern

Memcache is typically used with the following pattern:

  • The application receives a query from the user or the application.
  • The application checks whether the data needed to satisfy that query is in memcache.
    • If the data is in memcache, the application uses that data.
    • If the data is not in memcache, the application queries the Datastore and stores the results in memcache for future requests.

The pseudocode below represents a typical memcache request:

  def 
  
 get_data 
 (): 
 data 
 = 
 memcache 
 . 
 get 
 ( 
 'key' 
 ) 
 if 
 data 
 is 
 not 
 None 
 : 
 return 
 data 
 else 
 : 
 data 
 = 
 query_for_data 
 () 
 memcache 
 . 
 add 
 ( 
 'key' 
 , 
 data 
 , 
 60 
 ) 
 return 
 data 
 

ndb internally uses memcache to speed up queries. However, if you wish, you can also explicitly add memcache calls to gain more control about the speed-ups.

Caching data

The following example demonstrates several ways to set values in memcache using the Python API.

  # Add a value if it doesn't exist in the cache 
 # with a cache expiration of 1 hour. 
 memcache 
 . 
 add 
 ( 
 key 
 = 
 "weather_USA_98105" 
 , 
 value 
 = 
 "raining" 
 , 
 time 
 = 
 3600 
 ) 
 # Set several values, overwriting any existing values for these keys. 
 memcache 
 . 
 set_multi 
 ( 
 { 
 "USA_98115" 
 : 
 "cloudy" 
 , 
 "USA_94105" 
 : 
 "foggy" 
 , 
 "USA_94043" 
 : 
 "sunny" 
 }, 
 key_prefix 
 = 
 "weather_" 
 , 
 time 
 = 
 3600 
 ) 
 # Atomically increment an integer value. 
 memcache 
 . 
 set 
 ( 
 key 
 = 
 "counter" 
 , 
 value 
 = 
 0 
 ) 
 memcache 
 . 
 incr 
 ( 
 "counter" 
 ) 
 memcache 
 . 
 incr 
 ( 
 "counter" 
 ) 
 memcache 
 . 
 incr 
 ( 
 "counter" 
 ) 
 

To learn more about the add() , set_multi() , and set() methods, see the memcache Python API documentation .

Modifying guestbook.py to use memcache

The Guestbook application queries the Datastore on every request (via ndb, so it already gains some of the memcache speed-ups). You can modify the Guestbook application to use memcache explicitly before resorting to querying the Datastore.

First we'll import the memcache module and create the method that checks memcache before running a query.

  def 
  
 get_greetings 
 ( 
 self 
 , 
 guestbook_name 
 ): 
  
 """ 
 get_greetings() 
 Checks the cache to see if there are cached greetings. 
 If not, call render_greetings and set the cache 
 Args: 
 guestbook_name: Guestbook entity group key (string). 
 Returns: 
 A string of HTML containing greetings. 
 """ 
 greetings 
 = 
 memcache 
 . 
 get 
 ( 
 ' 
 {} 
 :greetings' 
 . 
 format 
 ( 
 guestbook_name 
 )) 
 if 
 greetings 
 is 
 None 
 : 
 greetings 
 = 
 self 
 . 
 render_greetings 
 ( 
 guestbook_name 
 ) 
 try 
 : 
 added 
 = 
 memcache 
 . 
 add 
 ( 
 ' 
 {} 
 :greetings' 
 . 
 format 
 ( 
 guestbook_name 
 ), 
 greetings 
 , 
 10 
 ) 
 if 
 not 
 added 
 : 
 logging 
 . 
 error 
 ( 
 'Memcache set failed.' 
 ) 
 except 
 ValueError 
 : 
 logging 
 . 
 error 
 ( 
 'Memcache set failed - data larger than 1MB' 
 ) 
 return 
 greetings 
 

Next we'll separate out the querying and creation of the HTML for the page. When we don't hit the cache, we'll call this method to query the Datastore and build the HTML string that we'll store in memcache.

  def 
  
 render_greetings 
 ( 
 self 
 , 
 guestbook_name 
 ): 
  
 """ 
 render_greetings() 
 Queries the database for greetings, iterate through the 
 results and create the HTML. 
 Args: 
 guestbook_name: Guestbook entity group key (string). 
 Returns: 
 A string of HTML containing greetings 
 """ 
 greetings 
 = 
 ndb 
 . 
 gql 
 ( 
 'SELECT * ' 
 'FROM Greeting ' 
 'WHERE ANCESTOR IS :1 ' 
 'ORDER BY date DESC LIMIT 10' 
 , 
 guestbook_key 
 ( 
 guestbook_name 
 )) 
 output 
 = 
 cStringIO 
 . 
 StringIO 
 () 
 for 
 greeting 
 in 
 greetings 
 : 
 if 
 greeting 
 . 
 author 
 : 
 output 
 . 
 write 
 ( 
 '<b> 
 {} 
< /b> wrote:' 
 . 
 format 
 ( 
 greeting 
 . 
 author 
 )) 
 else 
 : 
 output 
 . 
 write 
 ( 
 'An anonymous person wrote:' 
 ) 
 output 
 . 
 write 
 ( 
 '<blockquote> 
 {} 
< /blockquote>' 
 . 
 format 
 ( 
 cgi 
 . 
 escape 
 ( 
 greeting 
 . 
 content 
 ))) 
 return 
 output 
 . 
 getvalue 
 () 
 

Finally we will update the MainPage handler to call the get_greetings() method and display some stats about the number of times the cache was hit or missed.

  import 
  
 cgi 
 import 
  
 cStringIO 
 import 
  
 logging 
 import 
  
 urllib 
 from 
  
 google.appengine.api 
  
 import 
 memcache 
 from 
  
 google.appengine.api 
  
 import 
 users 
 from 
  
 google.appengine.ext 
  
 import 
 ndb 
 import 
  
 webapp2 
 class 
  
 Greeting 
 ( 
 ndb 
 . 
 Model 
 ): 
  
 """Models an individual Guestbook entry with author, content, and date.""" 
 author 
 = 
 ndb 
 . 
 StringProperty 
 () 
 content 
 = 
 ndb 
 . 
 StringProperty 
 () 
 date 
 = 
 ndb 
 . 
 DateTimeProperty 
 ( 
 auto_now_add 
 = 
 True 
 ) 
 def 
  
 guestbook_key 
 ( 
 guestbook_name 
 = 
 None 
 ): 
  
 """Constructs a Datastore key for a Guestbook entity with guestbook_name""" 
 return 
 ndb 
 . 
 Key 
 ( 
 'Guestbook' 
 , 
 guestbook_name 
 or 
 'default_guestbook' 
 ) 
 class 
  
 MainPage 
 ( 
 webapp2 
 . 
 RequestHandler 
 ): 
 def 
  
 get 
 ( 
 self 
 ): 
 self 
 . 
 response 
 . 
 out 
 . 
 write 
 ( 
 '<html><body>' 
 ) 
 guestbook_name 
 = 
 self 
 . 
 request 
 . 
 get 
 ( 
 'guestbook_name' 
 ) 
 greetings 
 = 
 self 
 . 
 get_greetings 
 ( 
 guestbook_name 
 ) 
 stats 
 = 
 memcache 
 . 
 get_stats 
 () 
 self 
 . 
 response 
 . 
 write 
 ( 
 '<b>Cache Hits: 
 {} 
< /b><br>' 
 . 
 format 
 ( 
 stats 
 [ 
 'hits' 
 ])) 
 self 
 . 
 response 
 . 
 write 
 ( 
 '<b>Cache Misses: 
 {} 
< /b><br><br>' 
 . 
 format 
 ( 
 stats 
 [ 
 'misses' 
 ])) 
 self 
 . 
 response 
 . 
 write 
 ( 
 greetings 
 ) 
 self 
 . 
 response 
 . 
 write 
 ( 
 """ 
 <form action="/sign? 
 {} 
 " method="post" 
> <div><textarea name="content" rows="3" cols="60"></textarea></div> 
 <div><input type="submit" value="Sign Guestbook"></div> 
 </form> 
 <hr> 
 <form>Guestbook name: <input value=" 
 {} 
 " name="guestbook_name" 
> <input type="submit" value="switch"></form> 
 </body> 
 </html>""" 
 . 
 format 
 ( 
 urllib 
 . 
 urlencode 
 ({ 
 'guestbook_name' 
 : 
 guestbook_name 
 }), 
 cgi 
 . 
 escape 
 ( 
 guestbook_name 
 ))) 
 def 
  
 get_greetings 
 ( 
 self 
 , 
 guestbook_name 
 ): 
  
 """ 
 get_greetings() 
 Checks the cache to see if there are cached greetings. 
 If not, call render_greetings and set the cache 
 Args: 
 guestbook_name: Guestbook entity group key (string). 
 Returns: 
 A string of HTML containing greetings. 
 """ 
 greetings 
 = 
 memcache 
 . 
 get 
 ( 
 ' 
 {} 
 :greetings' 
 . 
 format 
 ( 
 guestbook_name 
 )) 
 if 
 greetings 
 is 
 None 
 : 
 greetings 
 = 
 self 
 . 
 render_greetings 
 ( 
 guestbook_name 
 ) 
 try 
 : 
 added 
 = 
 memcache 
 . 
 add 
 ( 
 ' 
 {} 
 :greetings' 
 . 
 format 
 ( 
 guestbook_name 
 ), 
 greetings 
 , 
 10 
 ) 
 if 
 not 
 added 
 : 
 logging 
 . 
 error 
 ( 
 'Memcache set failed.' 
 ) 
 except 
 ValueError 
 : 
 logging 
 . 
 error 
 ( 
 'Memcache set failed - data larger than 1MB' 
 ) 
 return 
 greetings 
 def 
  
 render_greetings 
 ( 
 self 
 , 
 guestbook_name 
 ): 
  
 """ 
 render_greetings() 
 Queries the database for greetings, iterate through the 
 results and create the HTML. 
 Args: 
 guestbook_name: Guestbook entity group key (string). 
 Returns: 
 A string of HTML containing greetings 
 """ 
 greetings 
 = 
 ndb 
 . 
 gql 
 ( 
 'SELECT * ' 
 'FROM Greeting ' 
 'WHERE ANCESTOR IS :1 ' 
 'ORDER BY date DESC LIMIT 10' 
 , 
 guestbook_key 
 ( 
 guestbook_name 
 )) 
 output 
 = 
 cStringIO 
 . 
 StringIO 
 () 
 for 
 greeting 
 in 
 greetings 
 : 
 if 
 greeting 
 . 
 author 
 : 
 output 
 . 
 write 
 ( 
 '<b> 
 {} 
< /b> wrote:' 
 . 
 format 
 ( 
 greeting 
 . 
 author 
 )) 
 else 
 : 
 output 
 . 
 write 
 ( 
 'An anonymous person wrote:' 
 ) 
 output 
 . 
 write 
 ( 
 '<blockquote> 
 {} 
< /blockquote>' 
 . 
 format 
 ( 
 cgi 
 . 
 escape 
 ( 
 greeting 
 . 
 content 
 ))) 
 return 
 output 
 . 
 getvalue 
 () 
 class 
  
 Guestbook 
 ( 
 webapp2 
 . 
 RequestHandler 
 ): 
 def 
  
 post 
 ( 
 self 
 ): 
 # We set the same parent key on the 'Greeting' to ensure each greeting 
 # is in the same entity group. Queries across the single entity group 
 # are strongly consistent. However, the write rate to a single entity 
 # group is limited to ~1/second. 
 guestbook_name 
 = 
 self 
 . 
 request 
 . 
 get 
 ( 
 'guestbook_name' 
 ) 
 greeting 
 = 
 Greeting 
 ( 
 parent 
 = 
 guestbook_key 
 ( 
 guestbook_name 
 )) 
 if 
 users 
 . 
 get_current_user 
 (): 
 greeting 
 . 
 author 
 = 
 users 
 . 
 get_current_user 
 () 
 . 
 nickname 
 () 
 greeting 
 . 
 content 
 = 
 self 
 . 
 request 
 . 
 get 
 ( 
 'content' 
 ) 
 greeting 
 . 
 put 
 () 
 memcache 
 . 
 delete 
 ( 
 ' 
 {} 
 :greetings' 
 . 
 format 
 ( 
 guestbook_name 
 )) 
 self 
 . 
 redirect 
 ( 
 '/?' 
 + 
 urllib 
 . 
 urlencode 
 ({ 
 'guestbook_name' 
 : 
 guestbook_name 
 })) 
 app 
 = 
 webapp2 
 . 
 WSGIApplication 
 ([( 
 '/' 
 , 
 MainPage 
 ), 
 ( 
 '/sign' 
 , 
 Guestbook 
 )], 
 debug 
 = 
 True 
 ) 
 
Create a Mobile Website
View Site in Mobile | Classic
Share by: