Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cache search filter field values #3343

Merged
merged 6 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions contentcuration/contentcuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,8 +835,8 @@ def on_create(self):
node_id=self.id,
)

# if this change affects the public channel list, clear the channel cache
if self.public:
# if this change affects the published channel list, clear the channel cache
if self.public and (self.main_tree and self.main_tree.published):
vkWeb marked this conversation as resolved.
Show resolved Hide resolved
delete_public_channel_cache_keys()

def on_update(self):
Expand Down Expand Up @@ -876,8 +876,8 @@ def on_update(self):
if self.main_tree and self.main_tree._field_updates.changed():
self.main_tree.save()

# if this change affects the public channel list, clear the channel cache
if "public" in original_values:
# if this change affects the published channel list, clear the channel cache
if "public" in original_values and (self.main_tree and self.main_tree.published):
delete_public_channel_cache_keys()

def save(self, *args, **kwargs):
Expand Down Expand Up @@ -1658,9 +1658,13 @@ def recalculate_editors_storage(self):
def on_create(self):
self.changed = True
self.recalculate_editors_storage()
if self.published:
delete_public_channel_cache_keys()

def on_update(self):
self.changed = self.changed or self.has_changes()
if self.published:
delete_public_channel_cache_keys()
vkWeb marked this conversation as resolved.
Show resolved Hide resolved

def move_to(self, target, *args, **kwargs):
parent_was_trashtree = self.parent.channel_trash.exists()
Expand Down
4 changes: 4 additions & 0 deletions contentcuration/contentcuration/utils/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ def delete_public_channel_cache_keys():
"""
Delete all caches related to the public channel caching.
"""
from contentcuration.views.base import PUBLIC_CHANNELS_CACHE_KEYS

delete_cache_keys("*get_public_channel_list*")
delete_cache_keys("*get_user_public_channels*")
django_cache.delete_many(list(PUBLIC_CHANNELS_CACHE_KEYS.values()))


def redis_retry(func):
Expand Down Expand Up @@ -134,6 +137,7 @@ class ResourceSizeCache:
If the django_cache is Redis, then we use the lower level Redis client to use
its hash commands, HSET and HGET, to ensure we can store lots of data in performant way
"""

def __init__(self, node, cache=None):
self.node = node
self.cache = cache or django_cache
Expand Down
109 changes: 66 additions & 43 deletions contentcuration/contentcuration/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.core.exceptions import PermissionDenied
from django.db.models import Count
from django.db.models import IntegerField
Expand Down Expand Up @@ -49,6 +50,13 @@
from contentcuration.viewsets.channelset import PublicChannelSetSerializer

PUBLIC_CHANNELS_CACHE_DURATION = 30 # seconds
PUBLIC_CHANNELS_CACHE_KEYS = {
"list": "public_channel_list",
"languages": "public_channel_languages",
"licenses": "public_channel_licenses",
"kinds": "public_channel_kinds",
"collections": "public_channel_collections",
}

MESSAGES = "i18n_messages"
PREFERENCES = "user_preferences"
Expand Down Expand Up @@ -125,53 +133,70 @@ def channel_list(request):
current_user = current_user_for_context(None if anon else request.user)
preferences = DEFAULT_USER_PREFERENCES if anon else request.user.content_defaults

public_channel_list = Channel.objects.filter(
public=True, main_tree__published=True, deleted=False,
).values_list("main_tree__tree_id", flat=True)
public_channel_list = cache.get(PUBLIC_CHANNELS_CACHE_KEYS["list"])
if public_channel_list is None:
public_channel_list = Channel.objects.filter(
public=True, main_tree__published=True, deleted=False,
).values_list("main_tree__tree_id", flat=True)
cache.set(PUBLIC_CHANNELS_CACHE_KEYS["list"], public_channel_list, None)

# Get public channel languages
public_lang_query = (
Language.objects.filter(
channel_language__public=True,
channel_language__main_tree__published=True,
channel_language__deleted=False,
languages = cache.get(PUBLIC_CHANNELS_CACHE_KEYS["languages"])
if languages is None:
public_lang_query = (
Language.objects.filter(
channel_language__public=True,
channel_language__main_tree__published=True,
channel_language__deleted=False,
)
.values("lang_code")
.annotate(count=Count("lang_code"))
.order_by("lang_code")
)
.values("lang_code")
.annotate(count=Count("lang_code"))
.order_by("lang_code")
)
languages = {lang["lang_code"]: lang["count"] for lang in public_lang_query}
languages = {lang["lang_code"]: lang["count"] for lang in public_lang_query}
cache.set(PUBLIC_CHANNELS_CACHE_KEYS["languages"], json_for_parse_from_data(languages), None)

# Get public channel licenses
public_license_query = (
License.objects.filter(contentnode__tree_id__in=public_channel_list)
.values_list("id", flat=True)
.order_by("id")
.distinct()
)
licenses = list(public_license_query)
licenses = cache.get(PUBLIC_CHANNELS_CACHE_KEYS["licenses"])
if licenses is None:
public_license_query = (
License.objects.filter(contentnode__tree_id__in=public_channel_list)
.values_list("id", flat=True)
.order_by("id")
.distinct()
)
licenses = list(public_license_query)
cache.set(PUBLIC_CHANNELS_CACHE_KEYS["licenses"], json_for_parse_from_data(licenses), None)

# Get public channel kinds
public_kind_query = (
ContentKind.objects.filter(contentnodes__tree_id__in=public_channel_list)
.values_list("kind", flat=True)
.order_by("kind")
.distinct()
)
kinds = list(public_kind_query)
kinds = cache.get(PUBLIC_CHANNELS_CACHE_KEYS["kinds"])
if kinds is None:
public_kind_query = (
ContentKind.objects.filter(contentnodes__tree_id__in=public_channel_list)
.values_list("kind", flat=True)
.order_by("kind")
.distinct()
)
kinds = list(public_kind_query)
cache.set(PUBLIC_CHANNELS_CACHE_KEYS["kinds"], json_for_parse_from_data(kinds), None)

# Get public channel sets
public_channelset_query = ChannelSet.objects.filter(public=True).annotate(
count=SQCountDistinct(
Channel.objects.filter(
secret_tokens=OuterRef("secret_token"),
public=True,
main_tree__published=True,
deleted=False,
).values_list("id", flat=True),
field="id",
collections = cache.get(PUBLIC_CHANNELS_CACHE_KEYS["collections"])
if collections is None:
public_channelset_query = ChannelSet.objects.filter(public=True).annotate(
count=SQCountDistinct(
Channel.objects.filter(
secret_tokens=OuterRef("secret_token"),
public=True,
main_tree__published=True,
deleted=False,
).values_list("id", flat=True),
field="id",
)
)
)
cache.set(PUBLIC_CHANNELS_CACHE_KEYS["collections"], json_for_parse_from_serializer(
PublicChannelSetSerializer(public_channelset_query, many=True)), None)

return render(
request,
"channel_list.html",
Expand All @@ -180,12 +205,10 @@ def channel_list(request):
PREFERENCES: json_for_parse_from_data(preferences),
MESSAGES: json_for_parse_from_data(get_messages()),
"LIBRARY_MODE": settings.LIBRARY_MODE,
"public_languages": json_for_parse_from_data(languages),
"public_kinds": json_for_parse_from_data(kinds),
"public_licenses": json_for_parse_from_data(licenses),
"public_collections": json_for_parse_from_serializer(
PublicChannelSetSerializer(public_channelset_query, many=True)
),
"public_languages": languages,
"public_kinds": kinds,
"public_licenses": licenses,
"public_collections": collections,
},
)

Expand Down