moviewyrm/bookwyrm/redis_store.py

97 lines
3.2 KiB
Python
Raw Normal View History

""" access the activity stores stored in redis """
from abc import ABC, abstractmethod
import redis
from bookwyrm import settings
r = redis.Redis(
2022-01-05 16:27:39 +00:00
host=settings.REDIS_ACTIVITY_HOST,
port=settings.REDIS_ACTIVITY_PORT,
password=settings.REDIS_ACTIVITY_PASSWORD,
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
max_length = settings.MAX_STREAM_LENGTH
def get_value(self, obj):
2021-04-26 16:15:42 +00:00
"""the object and rank"""
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"""
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
if self.max_length:
pipeline.zremrangebyrank(store, 0, -1 * self.max_length)
if not execute:
return pipeline
# and go!
return pipeline.execute()
def remove_object_from_related_stores(self, obj, stores=None):
2021-04-26 16:15:42 +00:00
"""remove an object from all stores"""
2022-01-07 21:30:11 +00:00
# if the stoers are provided, the object can just be an id
if stores and isinstance(obj, int):
obj_id = obj
else:
obj_id = obj.id
2021-10-04 16:47:33 +00:00
stores = self.get_stores_for_object(obj) if stores is None else stores
pipeline = r.pipeline()
2021-08-23 17:48:23 +00:00
for store in stores:
2022-01-07 21:30:11 +00:00
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"""
pipeline = r.pipeline()
2021-04-05 18:10:26 +00:00
for obj in objs[: self.max_length]:
pipeline.zadd(store, self.get_value(obj))
if objs and self.max_length:
2021-04-05 18:10:26 +00:00
pipeline.zremrangebyrank(store, 0, -1 * self.max_length)
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"""
pipeline = r.pipeline()
2021-04-05 18:10:26 +00:00
for obj in objs[: self.max_length]:
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)
def populate_store(self, store):
2021-04-26 16:15:42 +00:00
"""go from zero to a store"""
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]:
pipeline.zadd(store, self.get_value(obj))
# only trim the store if objects were added
if queryset.exists() and self.max_length:
2021-04-05 18:10:26 +00:00
pipeline.zremrangebyrank(store, 0, -1 * self.max_length)
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"""
@abstractmethod
def get_stores_for_object(self, obj):
2021-04-26 16:15:42 +00:00
"""the stores that an object belongs in"""
@abstractmethod
def get_rank(self, obj):
2021-04-26 16:15:42 +00:00
"""how to rank an object"""