2021-04-05 18:05:37 +00:00
|
|
|
""" access the activity stores stored in redis """
|
|
|
|
from abc import ABC, abstractmethod
|
|
|
|
import redis
|
|
|
|
|
|
|
|
from bookwyrm import settings
|
|
|
|
|
|
|
|
r = redis.Redis(
|
|
|
|
host=settings.REDIS_ACTIVITY_HOST, port=settings.REDIS_ACTIVITY_PORT, db=0
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class RedisStore(ABC):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""sets of ranked, related objects, like statuses for a user's feed"""
|
2021-04-05 18:10:26 +00:00
|
|
|
|
2021-04-05 18:05:37 +00:00
|
|
|
max_length = settings.MAX_STREAM_LENGTH
|
|
|
|
|
|
|
|
def get_value(self, obj):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""the object and rank"""
|
2021-04-05 18:05:37 +00:00
|
|
|
return {obj.id: self.get_rank(obj)}
|
|
|
|
|
|
|
|
def add_object_to_related_stores(self, obj, execute=True):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""add an object to all suitable stores"""
|
2021-04-05 18:05:37 +00:00
|
|
|
value = self.get_value(obj)
|
|
|
|
# we want to do this as a bulk operation, hence "pipeline"
|
|
|
|
pipeline = r.pipeline()
|
|
|
|
for store in self.get_stores_for_object(obj):
|
|
|
|
# add the status to the feed
|
|
|
|
pipeline.zadd(store, value)
|
|
|
|
# trim the store
|
2021-04-05 18:10:26 +00:00
|
|
|
pipeline.zremrangebyrank(store, 0, -1 * self.max_length)
|
2021-04-05 18:05:37 +00:00
|
|
|
if not execute:
|
|
|
|
return pipeline
|
|
|
|
# and go!
|
|
|
|
return pipeline.execute()
|
|
|
|
|
|
|
|
def remove_object_from_related_stores(self, obj):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""remove an object from all stores"""
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline = r.pipeline()
|
|
|
|
for store in self.get_stores_for_object(obj):
|
|
|
|
pipeline.zrem(store, -1, obj.id)
|
|
|
|
pipeline.execute()
|
|
|
|
|
|
|
|
def bulk_add_objects_to_store(self, objs, store):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""add a list of objects to a given store"""
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline = r.pipeline()
|
2021-04-05 18:10:26 +00:00
|
|
|
for obj in objs[: self.max_length]:
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline.zadd(store, self.get_value(obj))
|
|
|
|
if objs:
|
2021-04-05 18:10:26 +00:00
|
|
|
pipeline.zremrangebyrank(store, 0, -1 * self.max_length)
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline.execute()
|
|
|
|
|
|
|
|
def bulk_remove_objects_from_store(self, objs, store):
|
2021-05-22 22:53:07 +00:00
|
|
|
"""remove a list of objects from a given store"""
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline = r.pipeline()
|
2021-04-05 18:10:26 +00:00
|
|
|
for obj in objs[: self.max_length]:
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline.zrem(store, -1, obj.id)
|
|
|
|
pipeline.execute()
|
|
|
|
|
2021-04-06 15:31:18 +00:00
|
|
|
def get_store(self, store, **kwargs): # pylint: disable=no-self-use
|
2021-04-26 16:15:42 +00:00
|
|
|
"""load the values in a store"""
|
2021-04-06 15:31:18 +00:00
|
|
|
return r.zrevrange(store, 0, -1, **kwargs)
|
2021-04-05 18:05:37 +00:00
|
|
|
|
|
|
|
def populate_store(self, store):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""go from zero to a store"""
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline = r.pipeline()
|
|
|
|
queryset = self.get_objects_for_store(store)
|
|
|
|
|
2021-04-05 18:10:26 +00:00
|
|
|
for obj in queryset[: self.max_length]:
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline.zadd(store, self.get_value(obj))
|
|
|
|
|
|
|
|
# only trim the store if objects were added
|
|
|
|
if queryset.exists():
|
2021-04-05 18:10:26 +00:00
|
|
|
pipeline.zremrangebyrank(store, 0, -1 * self.max_length)
|
2021-04-05 18:05:37 +00:00
|
|
|
pipeline.execute()
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_objects_for_store(self, store):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""a queryset of what should go in a store, used for populating it"""
|
2021-04-05 18:05:37 +00:00
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_stores_for_object(self, obj):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""the stores that an object belongs in"""
|
2021-04-05 18:05:37 +00:00
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_rank(self, obj):
|
2021-04-26 16:15:42 +00:00
|
|
|
"""how to rank an object"""
|