backend: use redis to mark crawls as canceled immediately, avoid dupes in crawl list (even if paging is added for db results)

This commit is contained in:
Ilya Kreymer 2022-02-01 15:58:56 -08:00
parent 4b7522920a
commit 8acb43b171
3 changed files with 26 additions and 13 deletions

View File

@ -330,9 +330,7 @@ class CrawlOps:
crawl = CrawlOut.from_dict(res)
if not crawl:
raise HTTPException(
status_code=404, detail=f"Crawl not found: {crawlid}"
)
raise HTTPException(status_code=404, detail=f"Crawl not found: {crawlid}")
return await self._resolve_crawl_refs(crawl, archive)

View File

@ -258,9 +258,10 @@ class DockerManager:
if not graceful:
await container.kill(signal="SIGABRT")
result = self._make_crawl_for_container(container, "canceled", True)
await self._mark_is_stopping(crawl_id, "canceled")
else:
result = True
await self._mark_is_stopping(crawl_id)
await self._mark_is_stopping(crawl_id, "stopping")
await container.kill(signal="SIGTERM")
except aiodocker.exceptions.DockerError as exc:
@ -365,10 +366,12 @@ class DockerManager:
if aid and container["Config"]["Labels"]["btrix.archive"] != aid:
return None
stopping = await self._get_is_stopping(crawl_id)
stop_type = await self._get_is_stopping(crawl_id)
if stop_type == "canceled":
return None
return self._make_crawl_for_container(
container, "stopping" if stopping else "running", False, CrawlOut
container, "stopping" if stop_type else "running", False, CrawlOut
)
# pylint: disable=broad-except
except Exception as exc:
@ -528,9 +531,9 @@ class DockerManager:
)
return results
async def _mark_is_stopping(self, crawl_id):
async def _mark_is_stopping(self, crawl_id, stop_type):
""" mark crawl as stopping in redis """
await self.redis.setex(f"{crawl_id}:stop", 600, 1)
await self.redis.setex(f"{crawl_id}:stop", 600, stop_type)
async def _get_is_stopping(self, crawl_id):
""" check redis if crawl is marked for stopping """

View File

@ -5,6 +5,7 @@ import datetime
import json
import asyncio
import base64
import aioredis
from kubernetes_asyncio import client, config, watch
from kubernetes_asyncio.stream import WsApiClient
@ -45,8 +46,17 @@ class K8SManager:
self.no_delete_jobs = os.environ.get("NO_DELETE_JOBS", "0") != "0"
self.redis_url = os.environ["REDIS_URL"]
self.loop = asyncio.get_running_loop()
self.loop.create_task(self.run_event_loop())
self.loop.create_task(self.init_redis(self.redis_url))
async def init_redis(self, redis_url):
""" init redis async """
self.redis = await aioredis.from_url(
redis_url, encoding="utf-8", decode_responses=True
)
def set_crawl_ops(self, ops):
""" Set crawl ops handler """
@ -276,7 +286,7 @@ class K8SManager:
crawls = []
for job in jobs.items:
status = self._get_crawl_state(job)
status = await self._get_crawl_state(job)
if not status:
continue
@ -392,7 +402,7 @@ class K8SManager:
if not job or job.metadata.labels["btrix.archive"] != aid:
return None
status = self._get_crawl_state(job)
status = await self._get_crawl_state(job)
if not status:
return None
@ -429,6 +439,7 @@ class K8SManager:
await self._send_sig_to_pods(pods.items, aid)
result = self._make_crawl_for_job(job, "canceled", True)
await self.redis.setex(f"{job_name}:stop", 300, "canceled")
else:
result = True
@ -492,7 +503,7 @@ class K8SManager:
# ========================================================================
# Internal Methods
def _get_crawl_state(self, job):
async def _get_crawl_state(self, job):
if job.status.active:
return "running"
@ -500,12 +511,13 @@ class K8SManager:
finished = (job.status.succeeded or 0) + (job.status.failed or 0)
total = job.spec.parallelism or 1
if finished != total:
return "stopping"
# don't return anything if marked as cancel
if await self.redis.get(f"{job.metadata.name}:stop") != "canceled":
return "stopping"
# job fully done, do not treat as running or stopping
return None
# pylint: disable=no-self-use
def _make_crawl_for_job(self, job, state, finish_now=False, crawl_cls=Crawl):
""" Make a crawl object from a job"""