mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-05-31 22:58:18 +00:00
Delete unused redis activity keys from UI, first pass
This commit is contained in:
parent
f608ab17bb
commit
cbe1014fb3
|
@ -85,6 +85,10 @@
|
||||||
{% url 'settings-celery' as url %}
|
{% url 'settings-celery' as url %}
|
||||||
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Celery status" %}</a>
|
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Celery status" %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
{% url 'settings-redis' as url %}
|
||||||
|
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Redis status" %}</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% url 'settings-schedules' as url %}
|
{% url 'settings-schedules' as url %}
|
||||||
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Scheduled tasks" %}</a>
|
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Scheduled tasks" %}</a>
|
||||||
|
|
68
bookwyrm/templates/settings/redis.html
Normal file
68
bookwyrm/templates/settings/redis.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{% extends 'settings/layout.html' %}
|
||||||
|
{% load humanize %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Redis Status" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}{% trans "Redis Status" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block panel %}
|
||||||
|
|
||||||
|
{% if info %}
|
||||||
|
<section class="block content">
|
||||||
|
<h2>{% trans "Info" %}</h2>
|
||||||
|
<div class="columns has-text-centered is-multiline">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="notification">
|
||||||
|
<p class="header">{% trans "Used memory" %}</p>
|
||||||
|
<p class="title is-5">{{ info.used_memory_human }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="notification">
|
||||||
|
<p class="header">{% trans "Total system memory" %}</p>
|
||||||
|
<p class="title is-5">{{ info.total_system_memory_human }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="notification">
|
||||||
|
<p class="header">{% trans "Keys" %}</p>
|
||||||
|
<p class="title is-5">{{ info.db0.keys | intcomma }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
{{ dead_key_count }}
|
||||||
|
<form name="erase-keys" method="POST" action="{% url 'settings-redis' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="button">go</button>
|
||||||
|
</form>
|
||||||
|
<form name="erase-keys" method="POST" action="{% url 'settings-redis' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="dry_run" value="True">
|
||||||
|
<button type="submit" class="button">dry run</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
{% else %}
|
||||||
|
<div class="notification is-danger is-flex is-align-items-start">
|
||||||
|
<span class="icon icon-warning is-size-4 pr-3" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "Could not connect to Redis Activity" %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if errors %}
|
||||||
|
<div class="block content">
|
||||||
|
<h2>{% trans "Errors" %}</h2>
|
||||||
|
{% for error in errors %}
|
||||||
|
<pre>{{ error }}</pre>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -369,6 +369,7 @@ urlpatterns = [
|
||||||
re_path(
|
re_path(
|
||||||
r"^settings/celery/ping/?$", views.celery_ping, name="settings-celery-ping"
|
r"^settings/celery/ping/?$", views.celery_ping, name="settings-celery-ping"
|
||||||
),
|
),
|
||||||
|
re_path(r"^settings/redis/?$", views.RedisStatus.as_view(), name="settings-redis"),
|
||||||
re_path(
|
re_path(
|
||||||
r"^settings/schedules/(?P<task_id>\d+)?$",
|
r"^settings/schedules/(?P<task_id>\d+)?$",
|
||||||
views.ScheduledTasks.as_view(),
|
views.ScheduledTasks.as_view(),
|
||||||
|
|
|
@ -5,6 +5,7 @@ from .admin.announcements import EditAnnouncement, delete_announcement
|
||||||
from .admin.automod import AutoMod, automod_delete, run_automod
|
from .admin.automod import AutoMod, automod_delete, run_automod
|
||||||
from .admin.automod import schedule_automod_task, unschedule_automod_task
|
from .admin.automod import schedule_automod_task, unschedule_automod_task
|
||||||
from .admin.celery_status import CeleryStatus, celery_ping
|
from .admin.celery_status import CeleryStatus, celery_ping
|
||||||
|
from .admin.redis import RedisStatus
|
||||||
from .admin.schedule import ScheduledTasks
|
from .admin.schedule import ScheduledTasks
|
||||||
from .admin.dashboard import Dashboard
|
from .admin.dashboard import Dashboard
|
||||||
from .admin.federation import Federation, FederatedServer
|
from .admin.federation import Federation, FederatedServer
|
||||||
|
|
63
bookwyrm/views/admin/redis.py
Normal file
63
bookwyrm/views/admin/redis.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
""" redis cache status """
|
||||||
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
import redis
|
||||||
|
|
||||||
|
from bookwyrm import models, settings
|
||||||
|
|
||||||
|
r = redis.from_url(settings.REDIS_ACTIVITY_URL)
|
||||||
|
|
||||||
|
# pylint: disable= no-self-use
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@method_decorator(
|
||||||
|
permission_required("bookwyrm.edit_instance_settings", raise_exception=True),
|
||||||
|
name="dispatch",
|
||||||
|
)
|
||||||
|
class RedisStatus(View):
|
||||||
|
"""Are your tasks running? Well you'd better go catch them"""
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
"""See workers and active tasks"""
|
||||||
|
data = {"errors": []}
|
||||||
|
try:
|
||||||
|
data["info"] = r.info
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception as err:
|
||||||
|
data["errors"].append(err)
|
||||||
|
|
||||||
|
return TemplateResponse(request, "settings/redis.html", data)
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def post(self, request):
|
||||||
|
"""Erase invalid keys"""
|
||||||
|
dry_run = request.POST.get("dry_run")
|
||||||
|
patterns = [":*:*"] # this pattern is a django cache with no prefix
|
||||||
|
for user_id in models.User.objects.filter(
|
||||||
|
is_deleted=True, local=True
|
||||||
|
).values_list("id", flat=True):
|
||||||
|
patterns.append(f"{user_id}-*")
|
||||||
|
|
||||||
|
deleted_count = 0
|
||||||
|
for pattern in patterns:
|
||||||
|
deleted_count += erase_keys(pattern, dry_run=dry_run)
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
return HttpResponse(f"{deleted_count} keys identified for deletion")
|
||||||
|
return HttpResponse(f"{deleted_count} keys deleted")
|
||||||
|
|
||||||
|
|
||||||
|
def erase_keys(pattern, count=1000, dry_run=False):
|
||||||
|
"""Delete all redis activity keys according to a provided regex pattern"""
|
||||||
|
pipeline = r.pipeline()
|
||||||
|
key_count = 0
|
||||||
|
for keys in r.scan_iter(match=pattern, count=count):
|
||||||
|
key_count += len(keys)
|
||||||
|
if not dry_run:
|
||||||
|
for key in keys:
|
||||||
|
pipeline.delete(key)
|
||||||
|
if not dry_run:
|
||||||
|
pipeline.execute()
|
||||||
|
return key_count
|
Loading…
Reference in a new issue