Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Job Cancelation #998

Merged
merged 16 commits into from
Aug 15, 2024
Merged

Job Cancelation #998

merged 16 commits into from
Aug 15, 2024

Conversation

natibek
Copy link
Contributor

@natibek natibek commented Jul 23, 2024

Addressed #822.

@natibek natibek requested a review from richrines1 July 26, 2024 21:31
Args:
job_ids: The UUID of the job (returned when the job was created).
kwargs: Extra options needed to fetch jobs.
- cq_token: CQ Cloud credentials.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't necessary (we recommend passing credentials when instantiating the client instead of for each call)

Suggested change
- cq_token: CQ Cloud credentials.

"""Cancel jobs associated with given job ids.

Args:
job_ids: The UUID of the job (returned when the job was created).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
job_ids: The UUID of the job (returned when the job was created).
job_ids: The UUIDs of the jobs (returned when the jobs were created).

@@ -192,6 +192,34 @@ def get_job(self, job_id: str) -> dict[str, str]:
"""
return self.fetch_jobs([job_id])[job_id]

def cancel_jobs(
self,
job_ids: list[str],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
job_ids: list[str],
job_ids: Sequence[str],

vendor throws error when trying to cancel.
"""
job_ids = self._job_id.split(",")
self._backend._provider._client.cancel_jobs(job_ids, kwargs=kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._backend._provider._client.cancel_jobs(job_ids, kwargs=kwargs)
self._backend._provider._client.cancel_jobs(job_ids, **kwargs)

SuperstaqServerException: If unable to get the status of the job from the API.
"""
job_ids = self._job_id.split(",")
self._client.cancel_jobs(job_ids, kwargs=kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._client.cancel_jobs(job_ids, kwargs=kwargs)
self._client.cancel_jobs(job_ids, **kwargs)

def cancel_jobs(
self,
job_ids: list[str],
**kwargs: Any,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this pass?

Suggested change
**kwargs: Any,
**kwargs: object,

(and ditto anywhere else we're adding Any)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.

Comment on lines 835 to 837
response_content = self._handle_response(response)
if response.ok:
return response
return response_content
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for noticing/fixing this! one thought: we probably only want to print warnings in the event of non-retriable status codes. what if we just replaced these lines in _handle_status_codes:

try:
json_content = response.json()
except requests.JSONDecodeError:
json_content = None

to

        try:
            json_content = self._handle_response(response)
        except requests.JSONDecodeError:
            json_content = None

(and then reverted the change to _handle_response, which shouldn't be necessary anymore)

- cq_token: CQ Cloud credentials.

Returns:
A dictionary mapping the job id to the cancellation attempt result or an error message.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might need an update: by the time we get here i think all that's left in the dict is {"succeeded": [<ids>]}

(also might be reasonable to just return the list of ids, instead of the full dictionary)

@@ -797,13 +821,14 @@ def _make_request(self, request: Callable[[], requests.Response]) -> requests.Re
TimeoutError: If the requests retried for more than `max_retry_seconds`.

Returns:
The `request.Response` from the final successful request call.
The 'requests.Response' from the final successful request call.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The 'requests.Response' from the final successful request call.
The `requests.Response` from the final successful request call.

(backticks indicate inline code)

Comment on lines 641 to 646
response1 = mock.MagicMock()
response2 = mock.MagicMock()
mock_post.side_effect = [response1, response2]
response1.ok = False
response1.status_code = requests.codes.service_unavailable
response2.ok = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this can be streamlined a bit:

Suggested change
response1 = mock.MagicMock()
response2 = mock.MagicMock()
mock_post.side_effect = [response1, response2]
response1.ok = False
response1.status_code = requests.codes.service_unavailable
response2.ok = True
response1 = mock.MagicMock(ok=False, status_code=requests.codes.service_unavailable)
response2 = mock.MagicMock(ok=True)
mock_post.side_effect = [response1, response2]

Comment on lines 57 to 59
with mock.patch(
"general_superstaq.superstaq_client._SuperstaqClient.cancel_jobs", return_value=None
) as mock_cancel:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this work?

Suggested change
with mock.patch(
"general_superstaq.superstaq_client._SuperstaqClient.cancel_jobs", return_value=None
) as mock_cancel:
with mock.patch("requests.post", return_value=requests.MagicMock(ok=True)) as mock_post:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original test passes so the mock seems to be called twice. The suggestion passes too with a minor fix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, can we make the same change in qss then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain why the new mock is better? Is the previous one stopping the program calls too early by returning None at _client.cancel_jobs()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the previous one stopping the program calls too early by returning None at _client.cancel_jobs()?

exactly - in general we want to be mocking as little as possible. basically all job.cancel() does is call client.cancel_jobs - if we mock that all we're really testing is the mock itself, not the actual interface between the two methods

in this case the logic is simple enough that it probably doesn't matter. But say we updated _client.cancel_jobs to take a different kind of input, but forgot to update job.cancel() - the previous test wouldn't catch this because we were replacing its call to _client.cancel_jobs() with a mocked function that accepts any input (note in this example the error would hopefully be caught by mypy, but that's not always the case)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thank you!

@@ -151,6 +151,16 @@ def status(self) -> str:
self._update_status_queue_info()
return self._overall_status

def cancel(self, **kwargs: Any) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def cancel(self, **kwargs: Any) -> None:
def cancel(self, **kwargs: object) -> None:

@@ -177,6 +177,16 @@ def _check_if_stopped(self) -> None:
self._job_id, self._overall_status
)

def cancel(self, **kwargs: Any) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def cancel(self, **kwargs: Any) -> None:
def cancel(self, **kwargs: object) -> None:

Copy link
Contributor

@richrines1 richrines1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lg % a few nits

"""Cancel the current job if it is not in a terminal state.

Args:
kwargs: Extra options needed to fetch jobs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
kwargs: Extra options needed to fetch jobs.
kwargs: Extra options needed to fetch jobs.


Args:
job_ids: The UUIDs of the jobs (returned when the jobs were created).
kwargs: Extra options needed to fetch jobs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
kwargs: Extra options needed to fetch jobs.
kwargs: Extra options needed to fetch jobs.

Raises:
SuperstaqServerException: For other API call failures.
"""
json_dict: dict[str, Any] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
json_dict: dict[str, Any] = {
json_dict: dict[str, list[str]] = {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks a few things. Since job_ids is a Sequence[str] type, we have to cast it to a list. Also, json.dumps returns a string so json_dict["options"] will have type str and not list[str]. I don't think server is designed to handle json_dict["options"] being a list type either. Should I keep it at Any or should I do some type casting to fix these issues?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah my bad. what about this?

Suggested change
json_dict: dict[str, Any] = {
json_dict: dict[str, str | Sequence[str]] = {

"""Cancel the current job if it is not in a terminal state.

Args:
kwargs: Extra options needed to fetch jobs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
kwargs: Extra options needed to fetch jobs.
kwargs: Extra options needed to fetch jobs.

Copy link
Contributor

@richrines1 richrines1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@natibek natibek merged commit d217a2a into main Aug 15, 2024
19 checks passed
@natibek natibek deleted the cancel-jobs branch August 15, 2024 20:54
bharat-thotakura added a commit that referenced this pull request Sep 11, 2024
Updates docs on some functionality info on features added by
#998,
#1034, and
#1048.

Also does a re-run refresh of the notebooks so that the output cells
reflect the latest changes (e.g., new targets/backends,
#1036, etc). Adds
support for Colab and Binder notebook badges as well
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants