mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
exp: implement internal queue.kill support(#7587)
fix: #7587 1. Add implement kill method for local queue class 2. Add a test to make sure the following job will be success after the original job was killed. 3. Some refactoring work on `exp remove`
This commit is contained in:
committed by
Peter Rowlands (변기호)
parent
22deca3a6a
commit
4df5a1321d
@@ -170,6 +170,14 @@ class BaseStashQueue(ABC):
|
|||||||
This method blocks until the specified item has been collected.
|
This method blocks until the specified item has been collected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def kill(self, revs: str) -> None:
|
||||||
|
"""Kill the specified running entries in the queue.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
revs: Stash revs or running exp name to be killed.
|
||||||
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def shutdown(self, kill: bool = False):
|
def shutdown(self, kill: bool = False):
|
||||||
"""Shutdown the queue worker.
|
"""Shutdown the queue worker.
|
||||||
@@ -495,3 +503,28 @@ class BaseStashQueue(ABC):
|
|||||||
executor.collect_cache(exp.repo, exec_result.ref_info)
|
executor.collect_cache(exp.repo, exec_result.ref_info)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
def get_queue_entry_by_names(
|
||||||
|
self,
|
||||||
|
exp_names: Collection[str],
|
||||||
|
) -> Dict[str, Optional[QueueEntry]]:
|
||||||
|
from scmrepo.exceptions import RevError as InternalRevError
|
||||||
|
|
||||||
|
exp_name_set = set(exp_names)
|
||||||
|
result: Dict[str, Optional[QueueEntry]] = {}
|
||||||
|
rev_entries = {}
|
||||||
|
|
||||||
|
for entry in self.iter_queued():
|
||||||
|
if entry.name in exp_name_set:
|
||||||
|
result[entry.name] = entry
|
||||||
|
else:
|
||||||
|
rev_entries[entry.stash_rev] = entry
|
||||||
|
|
||||||
|
for exp_name in exp_name_set.difference(result.keys()):
|
||||||
|
try:
|
||||||
|
rev = self.scm.resolve_rev(exp_name)
|
||||||
|
if rev in rev_entries:
|
||||||
|
result[exp_name] = rev_entries[rev]
|
||||||
|
except InternalRevError:
|
||||||
|
result[exp_name] = None
|
||||||
|
return result
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import time
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
|
Collection,
|
||||||
Dict,
|
Dict,
|
||||||
Generator,
|
Generator,
|
||||||
List,
|
List,
|
||||||
@@ -20,7 +21,7 @@ from kombu.message import Message
|
|||||||
from dvc.daemon import daemonize
|
from dvc.daemon import daemonize
|
||||||
from dvc.exceptions import DvcException
|
from dvc.exceptions import DvcException
|
||||||
|
|
||||||
from ..exceptions import ExpQueueEmptyError
|
from ..exceptions import ExpQueueEmptyError, UnresolvedExpNamesError
|
||||||
from ..executor.base import (
|
from ..executor.base import (
|
||||||
EXEC_PID_DIR,
|
EXEC_PID_DIR,
|
||||||
EXEC_TMP_DIR,
|
EXEC_TMP_DIR,
|
||||||
@@ -200,6 +201,29 @@ class LocalCeleryQueue(BaseStashQueue):
|
|||||||
pass
|
pass
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
def kill(self, revs: Collection[str]) -> None:
|
||||||
|
to_kill: Set[QueueEntry] = set()
|
||||||
|
not_active: List[str] = []
|
||||||
|
name_dict: Dict[
|
||||||
|
str, Optional[QueueEntry]
|
||||||
|
] = self.get_queue_entry_by_names(set(revs))
|
||||||
|
|
||||||
|
missing_rev: List[str] = []
|
||||||
|
active_queue_entry = set(self.iter_active())
|
||||||
|
for rev, queue_entry in name_dict.items():
|
||||||
|
if queue_entry is None:
|
||||||
|
missing_rev.append(rev)
|
||||||
|
elif queue_entry not in active_queue_entry:
|
||||||
|
not_active.append(rev)
|
||||||
|
else:
|
||||||
|
to_kill.add(queue_entry)
|
||||||
|
|
||||||
|
if missing_rev:
|
||||||
|
raise UnresolvedExpNamesError(missing_rev)
|
||||||
|
|
||||||
|
for queue_entry in to_kill:
|
||||||
|
self.proc.kill(queue_entry.name)
|
||||||
|
|
||||||
def _shutdown_handler(self, task_id: str = None, **kwargs):
|
def _shutdown_handler(self, task_id: str = None, **kwargs):
|
||||||
if task_id in self._shutdown_task_ids:
|
if task_id in self._shutdown_task_ids:
|
||||||
self._shutdown_task_ids.remove(task_id)
|
self._shutdown_task_ids.remove(task_id)
|
||||||
@@ -336,5 +360,8 @@ class WorkspaceQueue(BaseStashQueue):
|
|||||||
def get_result(self, entry: QueueEntry) -> Optional[ExecutorResult]:
|
def get_result(self, entry: QueueEntry) -> Optional[ExecutorResult]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def kill(self, revs: Collection[str]) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def shutdown(self, kill: bool = False):
|
def shutdown(self, kill: bool = False):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Dict, List, Mapping, Optional, Set, Union
|
from typing import TYPE_CHECKING, Dict, List, Mapping, Optional, Union
|
||||||
|
|
||||||
from dvc.repo import locked
|
from dvc.repo import locked
|
||||||
from dvc.repo.scm_context import scm_context
|
from dvc.repo.scm_context import scm_context
|
||||||
@@ -85,7 +85,9 @@ def _resolve_exp_by_name(
|
|||||||
commit_ref_dict[exp_ref] = exp_name
|
commit_ref_dict[exp_ref] = exp_name
|
||||||
|
|
||||||
if not git_remote:
|
if not git_remote:
|
||||||
_named_entries = _get_queue_entry_by_names(repo, remained)
|
_named_entries = (
|
||||||
|
repo.experiments.celery_queue.get_queue_entry_by_names(remained)
|
||||||
|
)
|
||||||
for exp_name, entry in _named_entries.items():
|
for exp_name, entry in _named_entries.items():
|
||||||
if entry is not None:
|
if entry is not None:
|
||||||
queue_entry_dict[exp_name] = entry
|
queue_entry_dict[exp_name] = entry
|
||||||
@@ -127,30 +129,6 @@ def _clear_all_commits(repo, git_remote) -> List:
|
|||||||
return _remove_commited_exps(repo.scm, ref_infos, git_remote)
|
return _remove_commited_exps(repo.scm, ref_infos, git_remote)
|
||||||
|
|
||||||
|
|
||||||
def _get_queue_entry_by_names(
|
|
||||||
repo: "Repo",
|
|
||||||
exp_name_set: Set[str],
|
|
||||||
) -> Dict[str, Optional[QueueEntry]]:
|
|
||||||
from scmrepo.exceptions import RevError as InternalRevError
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
rev_entries = {}
|
|
||||||
for entry in repo.experiments.celery_queue.iter_queued():
|
|
||||||
if entry.name in exp_name_set:
|
|
||||||
result[entry.name] = entry
|
|
||||||
else:
|
|
||||||
rev_entries[entry.stash_rev] = entry
|
|
||||||
|
|
||||||
for exp_name in exp_name_set.difference(result.keys()):
|
|
||||||
try:
|
|
||||||
rev = repo.scm.resolve_rev(exp_name)
|
|
||||||
if rev in rev_entries:
|
|
||||||
result[exp_name] = rev_entries[rev]
|
|
||||||
except InternalRevError:
|
|
||||||
result[exp_name] = None
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _remove_commited_exps(
|
def _remove_commited_exps(
|
||||||
scm: "Git", exp_ref_dict: Mapping[ExpRefInfo, str], remote: Optional[str]
|
scm: "Git", exp_ref_dict: Mapping[ExpRefInfo, str], remote: Optional[str]
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ def test_shutdown_no_tasks(test_queue, mocker):
|
|||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def _foo():
|
def _foo(arg="foo"):
|
||||||
return "foo"
|
return arg
|
||||||
|
|
||||||
|
|
||||||
def test_shutdown_active_tasks(test_queue, mocker):
|
def test_shutdown_active_tasks(test_queue, mocker):
|
||||||
@@ -35,3 +35,35 @@ def test_shutdown_active_tasks(test_queue, mocker):
|
|||||||
assert "foo" == result.get()
|
assert "foo" == result.get()
|
||||||
assert result.id not in test_queue._shutdown_task_ids
|
assert result.id not in test_queue._shutdown_task_ids
|
||||||
shutdown_spy.assert_called_once()
|
shutdown_spy.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_run_after_kill(test_queue, mocker):
|
||||||
|
import time
|
||||||
|
|
||||||
|
from celery import chain
|
||||||
|
|
||||||
|
sig_bar = test_queue.proc.run(
|
||||||
|
["python3", "-c", "import time; time.sleep(5)"], name="bar"
|
||||||
|
)
|
||||||
|
result_bar = sig_bar.freeze()
|
||||||
|
sig_foo = _foo.s()
|
||||||
|
result_foo = sig_foo.freeze()
|
||||||
|
run_chain = chain(sig_bar, sig_foo)
|
||||||
|
|
||||||
|
run_chain.delay()
|
||||||
|
timeout = time.time() + 10
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if result_bar.status == "RUNNING":
|
||||||
|
break
|
||||||
|
if time.time() > timeout:
|
||||||
|
raise AssertionError()
|
||||||
|
|
||||||
|
assert result_foo.status == "PENDING"
|
||||||
|
test_queue.proc.kill("bar")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if result_foo.status == "SUCCESS":
|
||||||
|
break
|
||||||
|
if time.time() > timeout:
|
||||||
|
raise AssertionError()
|
||||||
|
|||||||
Reference in New Issue
Block a user