The glorious return of the in-view runner

This commit is contained in:
Andrew Godwin 2022-11-26 11:54:14 -07:00
parent f20296bc1b
commit db88c733b4
3 changed files with 43 additions and 9 deletions

View file

@ -26,6 +26,7 @@ class StatorRunner:
liveness_file: Optional[str] = None, liveness_file: Optional[str] = None,
schedule_interval: int = 30, schedule_interval: int = 30,
lock_expiry: int = 300, lock_expiry: int = 300,
run_for: int = 0,
): ):
self.models = models self.models = models
self.runner_id = uuid.uuid4().hex self.runner_id = uuid.uuid4().hex
@ -34,9 +35,11 @@ class StatorRunner:
self.liveness_file = liveness_file self.liveness_file = liveness_file
self.schedule_interval = schedule_interval self.schedule_interval = schedule_interval
self.lock_expiry = lock_expiry self.lock_expiry = lock_expiry
self.run_for = run_for
async def run(self): async def run(self):
self.handled = 0 self.handled = 0
self.started = time.monotonic()
self.last_clean = time.monotonic() - self.schedule_interval self.last_clean = time.monotonic() - self.schedule_interval
self.tasks = [] self.tasks = []
# For the first time period, launch tasks # For the first time period, launch tasks
@ -71,17 +74,21 @@ class StatorRunner:
) )
self.handled += 1 self.handled += 1
space_remaining -= 1 space_remaining -= 1
# Prevent busylooping # Are we in limited run mode?
await asyncio.sleep(0.1) if self.run_for and (time.monotonic() - self.started) > self.run_for:
except KeyboardInterrupt:
# Wait for tasks to finish
print("Waiting for tasks to complete")
while True:
self.remove_completed_tasks()
if not self.tasks:
break break
# Prevent busylooping # Prevent busylooping
await asyncio.sleep(1) await asyncio.sleep(0.5)
except KeyboardInterrupt:
pass
# Wait for tasks to finish
print("Waiting for tasks to complete")
while True:
self.remove_completed_tasks()
if not self.tasks:
break
# Prevent busylooping
await asyncio.sleep(0.1)
print("Complete") print("Complete")
return self.handled return self.handled

24
stator/views.py Normal file
View file

@ -0,0 +1,24 @@
from django.conf import settings
from django.http import HttpResponse, HttpResponseForbidden
from django.views import View
from stator.models import StatorModel
from stator.runner import StatorRunner
class RequestRunner(View):
"""
Runs a Stator runner within a HTTP request.
For when you're on something serverless.
"""
async def get(self, request):
# Check the token, if supplied
if not settings.STATOR_TOKEN:
return HttpResponseForbidden("No token set")
if request.GET.get("token") != settings.STATOR_TOKEN:
return HttpResponseForbidden("Invalid token")
# Run on all models
runner = StatorRunner(StatorModel.subclasses, run_for=2)
handled = await runner.run()
return HttpResponse(f"Handled {handled}")

View file

@ -5,6 +5,7 @@ from django.views.static import serve
from activities.views import posts, search, timelines from activities.views import posts, search, timelines
from core import views as core from core import views as core
from stator import views as stator
from users.views import activitypub, admin, auth, follows, identity, settings from users.views import activitypub, admin, auth, follows, identity, settings
urlpatterns = [ urlpatterns = [
@ -121,6 +122,8 @@ urlpatterns = [
path(".well-known/nodeinfo", activitypub.NodeInfo.as_view()), path(".well-known/nodeinfo", activitypub.NodeInfo.as_view()),
path("nodeinfo/2.0/", activitypub.NodeInfo2.as_view()), path("nodeinfo/2.0/", activitypub.NodeInfo2.as_view()),
path("actor/", activitypub.SystemActorView.as_view()), path("actor/", activitypub.SystemActorView.as_view()),
# Stator
path(".stator/", stator.RequestRunner.as_view()),
# Django admin # Django admin
path("djadmin/", djadmin.site.urls), path("djadmin/", djadmin.site.urls),
# Media files # Media files