Autumn 2015
Caches are hard to get right
@post('/add')
def add():
... get name and number from form
... add to database
@get('search/<name>')
def search(name):
number = mcClient.get(name)
if (number is None):
number = riakBucket.get(name)
mcClient.set(name, number)
return "Some html with the number"
Caches are hard to get right - let’s try to solve this - attempt 1
@post('/add')
def add():
... get name and number from form
... remove from memcache
... add to database
@get('search/<name>')
def search(name):
number = mcClient.get(name)
if (number is None):
number = riakBucket.get(name)
mcClient.set(name, number)
return "Some html with the number"
/add?name=A&number=0
Add and search simultaneously
/add?name=A&number=1 | /search?name=A | DB | MC |
---|---|---|---|
Get data from form | (A,0) | (A,0) | |
remove from memcache | (A,0) | (-,-) | |
number = mcClient.get(name) | (A,0) | (-,-) | |
if (number is None): | (A,0) | (-,-) | |
number = riakBucket.get(name) | (A,0) | (-,-) | |
mcClient.set(name, number) | (A,0) | (A,0) | |
add to database | (A,1) | (A,0) |
/search?name=A //returns 0
Caches are hard to get right - let’s try to solve this - attempt 2
@post('/add')
def add():
... get name and number from form
... add to database
... remove from memcache
@get('search/<name>')
def search(name):
number = mcClient.get(name)
if (number is None):
number = riakBucket.get(name)
mcClient.set(name, number)
return "Some html with the number"
/add?name=A&number=0
Add and search simultaneously
/add?name=A&number=1 | /search?name=A | DB | MC |
---|---|---|---|
number = mcClient.get(name) | (A,0) | (A,0) | |
if (number is None): | (A,0) | (-,-) | |
number = riakBucket.get(name) | (A,0) | (-,-) | |
#number == 0 | (A,0) | (-,-) | |
Get data from form | (A,0) | (-,-) | |
remove from memcache | (A,0) | (-,-) | |
add to database | (A,1) | (-,-) | |
mcClient.set(name, number) | (A,1) | (A,0) |
/search?name=A //returns 0
You could also do
@post('/add')
def add():
... get name and number from form
... add to database
... set on memcache = overwrite
/add?name=A&number=0
Add and search simultaneously
/add?name=A&number=0 | /search?name=A | DB | MC |
---|---|---|---|
number = mcClient.get(name) | (A,0) | (A,0) | |
if (number is None): | (A,0) | (-,-) | |
number = riakBucket.get(name) | (A,0) | (-,-) | |
#number == 0 | (A,0) | (-,-) | |
Get data from form | (A,0) | (-,-) | |
set on memcache | (A,0) | (A,1) | |
add to database | (A,1) | (A,1) | |
mcClient.set(name, number) | (A,1) | (A,0) |
/search?name=A //returns 0
Caches are hard to get right - let’s try to solve this
@post('/add')
def add():
... get name and number from form
... add to database
... remove from memcache
@get('search/<name>')
def search(name):
number = mcClient.get(name)
if (number is None):
number = riakBucket.get(name)
mcClient.cas(name, number) #only do a set if not updated
return "Some html with the number"
Problem : The teacher cannot find any guarantees on whether the CAS counter is updated on delete. If this is not the case, the following could happen: stale data in cache in the following (multi-threaded) case:
/add?name=A&number=1
Add and search simultaneously
/add?name=A&number=2 | /search?name=A | DB | MC |
---|---|---|---|
number = mcClient.get(name) | (A,1) | (-,-) | |
if (number is None): | (A,1) | (-,-) | |
number = riakBucket.get(name) | (A,1) | (-,-) | |
Get data from form | (A,1) | (-,-) | |
add to database | (A,2) | (-,-) | |
remove from memcache | (A,2) | (-,-) | |
mcClient.cas(name, number) #case1 | (A,2) | (A,1) | |
mcClient.cas(name, number) #case2 | (A,2) | (-,-) |
In search
if mc.get(name):
return mc[name]
Or
mc[name] = fetched.encoded_data
return mc[name]
It might be that the data in memcache is removed in between the two calls.