Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
ecommerce
Commits
b140c429
Commit
b140c429
authored
Apr 18, 2018
by
Christopher Lee
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Setup request cache
parent
51b122fd
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
168 additions
and
0 deletions
+168
-0
ecommerce/request_cache/__init__.py
+164
-0
ecommerce/settings/local.py
+4
-0
No files found.
ecommerce/request_cache/__init__.py
0 → 100644
View file @
b140c429
"""
A cache that is cleared after every request.
This module requires that :class:`request_cache.middleware.RequestCache`
is installed in order to clear the cache after each request.
"""
import
logging
from
django.core.cache
import
caches
from
django.core.cache.backends.base
import
BaseCache
log
=
logging
.
getLogger
(
__name__
)
class
RequestPlusRemoteCache
(
BaseCache
):
"""
This Django cache backend implements two layers of caching.
The first layer is a threadlocal dictionary that is tied to the life of a
given request. The second layer is another named Django cache -- e.g. the
"default" entry in settings.CACHES, typically backed by memcached.
Some baseline rules:
1. Treat it as a global namespace, like any other cache. The per-request
local cache is only going to live for the lifetime of one request, but
the backing cache is going to something like Memcached, where key
collision is possible.
2. Timeouts are ignored for the purposes of the in-memory request cache, but
do apply to the backing remote cache. One consequence of this is that
sending an explicit timeout of 0 in `set` or `add` will cause that item
to only be cached across the duration of the request and will not cause
a write to the remote cache.
3. If you're in a situation where key generation performance is actually a
concern (many thousands of lookups), then just use the request cache
directly instead of this hybrid.
"""
def
__init__
(
self
,
name
,
params
):
try
:
super
(
RequestPlusRemoteCache
,
self
)
.
__init__
(
params
)
self
.
_remote_cache
=
caches
[
params
[
'REMOTE_CACHE_NAME'
]]
except
Exception
:
log
.
exception
(
"DjangoRequestCache
%
s could not load backing remote cache."
,
name
)
raise
# This is a threadlocal that will get wiped out for each request.
self
.
_local_dict
=
get_cache
(
"DjangoRequestCache"
)
def
add
(
self
,
key
,
value
,
timeout
=
0
,
version
=
None
):
"""
Set a value in the cache if the key does not already exist. If
timeout is given, that timeout will be used for the key; otherwise
the timeout will default to 0, and the (key, value) will only be stored
in the local in-memory request cache, not the backing remote cache.
Returns True if the value was stored, False otherwise.
"""
local_key
=
self
.
make_key
(
key
,
version
)
if
local_key
in
self
.
_local_dict
:
return
False
self
.
_local_dict
[
local_key
]
=
value
if
timeout
!=
0
:
self
.
_remote_cache
.
add
(
key
,
value
,
timeout
=
timeout
,
version
=
version
)
return
True
def
get
(
self
,
key
,
default
=
None
,
version
=
None
):
"""
Fetch a given key from the cache. If the key does not exist, return
default, which itself defaults to None.
"""
# Simple case: It's already in our local memory...
local_key
=
self
.
make_key
(
key
,
version
)
if
local_key
in
self
.
_local_dict
:
return
self
.
_local_dict
[
local_key
]
# Now try looking it up in our backing cache...
external_value
=
self
.
_remote_cache
.
get
(
key
,
default
=
default
,
version
=
None
)
# This might be None, but we store it anyway to prevent repeated requests
# to the same non-existent key during the course of the request.
self
.
_local_dict
[
local_key
]
=
external_value
return
external_value
def
set
(
self
,
key
,
value
,
timeout
=
0
,
version
=
None
):
"""
Set a value in the cache. If timeout is given, that timeout will be used
for the key when storing in the remote cache; otherwise the timeout will
default to 0, and the (key, value) will only be stored in the local
in-memory request cache.
For example::
# This will only be stored in the local request cache, and should
# be used for items where there are potentially many, many keys.
dj_req_cache.set('has_access:user1243:block3048', True, 0)
# This value will be stored in both the local request cache and the
"""
local_key
=
self
.
make_key
(
key
,
version
)
self
.
_local_dict
[
local_key
]
=
value
if
timeout
!=
0
:
self
.
_remote_cache
.
set
(
key
,
value
,
timeout
=
timeout
,
version
=
version
)
def
delete
(
self
,
key
,
version
=
None
):
"""
Delete a key from the cache, failing silently.
Note that this *will* flow through to the backing remote cache.
"""
local_key
=
self
.
make_key
(
key
,
version
)
if
local_key
in
self
.
_local_
:
del
self
.
_local_dict
[
local_key
]
self
.
_remote_cache
.
delete
(
key
,
version
=
version
)
def
get_many
(
self
,
keys
,
version
=
None
):
mapping
=
{}
# First get all the keys that exist locally.
for
key
in
keys
:
local_key
=
self
.
make_key
(
key
)
if
local_key
in
self
.
_local_dict
:
mapping
[
key
]
=
self
.
_local_dict
[
local_key
]
# Now check the external cache for everything that we didn't find
remaining_keys
=
set
(
keys
)
-
set
(
mapping
)
external_mapping
=
self
.
_remote_cache
.
get_many
(
remaining_keys
,
version
=
version
)
# Update both the mapping that we're returning as well as our local cache
mapping
.
update
(
external_mapping
)
self
.
_local_dict
.
update
({
self
.
make_key
(
key
):
value
for
key
,
value
in
external_mapping
.
items
()
})
return
mapping
def
set_many
(
self
,
data
,
timeout
=
0
,
version
=
None
):
self
.
_local_dict
.
update
({
self
.
make_key
(
key
):
value
for
key
,
value
in
data
.
items
()
})
self
.
_remote_cache
.
set_many
(
data
,
timeout
=
timeout
,
version
=
version
)
def
delete_many
(
self
,
keys
,
version
=
None
):
for
key
in
keys
:
del
self
.
_local_dict
[
self
.
make_key
(
key
)]
self
.
_remote_cache
.
delete_many
(
keys
)
def
clear
(
self
):
self
.
_local_dict
.
clear
()
self
.
_remote_cache
.
clear
()
def
close
(
self
,
**
kwargs
):
self
.
_local_dict
.
clear
()
self
.
_remote_cache
.
close
()
\ No newline at end of file
ecommerce/settings/local.py
View file @
b140c429
...
@@ -40,6 +40,10 @@ DATABASES = {
...
@@ -40,6 +40,10 @@ DATABASES = {
CACHES
=
{
CACHES
=
{
'default'
:
{
'default'
:
{
'BACKEND'
:
'django.core.cache.backends.locmem.LocMemCache'
,
'BACKEND'
:
'django.core.cache.backends.locmem.LocMemCache'
,
},
'request'
:
{
'BACKEND'
:
'ecommerce.request_cache.RequestPlusRemoteCache'
,
'REMOTE_CACHE_NAME'
:
'default'
,
}
}
}
}
# END CACHE CONFIGURATION
# END CACHE CONFIGURATION
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment