Fleets results: return presigned URL redirect instead of buffering content#2203
Fleets results: return presigned URL redirect instead of buffering content#2203avilches wants to merge 36 commits into
Conversation
|
The PR shares code from #2186, so it needs to be merged after it (and probably solve conflicts) |
0961a34 to
928da2c
Compare
| request=lambda: requests.get( | ||
| url, | ||
| headers=get_headers(token=self.token, instance=self.instance, channel=self.channel), | ||
| params={"with_result": "false"}, |
There was a problem hiding this comment.
Default for with_result is true, so now has been changed explicitly to false, so the Gateway skips the COS read and returns result: null. This does not break anything because job() don't use the result.
| gateway_url = f"{self.host}/api/{self.version}/jobs/{job_id}/result/" | ||
| response = requests.get( | ||
| gateway_url, | ||
| headers=get_headers(token=self.token, instance=self.instance, channel=self.channel), | ||
| timeout=REQUESTS_TIMEOUT, | ||
| ) | ||
| return json.loads(response_data.get("result", "{}") or "{}", cls=QiskitObjectsDecoder) | ||
| if response.status_code == 204: | ||
| return None | ||
| # Not all redirects go to COS — HTTP→HTTPS redirects stay on the same host. | ||
| # Checking the hostname detects only redirects to an external host (COS/MinIO). | ||
| redirected_to_cos = urlparse(response.url).hostname != urlparse(gateway_url).hostname | ||
| if redirected_to_cos: | ||
| return json.loads(response.text, cls=QiskitObjectsDecoder) if response.ok else None | ||
| return json.loads(response.json().get("result", "{}") or "{}", cls=QiskitObjectsDecoder) |
There was a problem hiding this comment.
Use the new /result endpoint following the same approach than /logs and /provider-logs
|
|
||
| @dataclass | ||
| class LogsResult: | ||
| class GetLogsResponse: |
There was a problem hiding this comment.
LogsResult was a terrible name. Now, GetJobLogsUseCase -> returns GetJobLogsResponse and GetResultUseCase -> returns GetResultResponse
… to check raw_result is None
…-url' into worktree-fleet-results-presigned-url
|
|
||
|
|
||
| @swagger_auto_schema( | ||
| method="post", |
There was a problem hiding this comment.
add a swagger auto schema for GET
| if result is not None: | ||
| job.result = result | ||
| # with_result is legacy, new clients will use the new GET /:id/results endpoints | ||
| if with_result: |
There was a problem hiding this comment.
We need to ensure then that this with_result is false. I think we need to change it in the serializer if I remember correctly.
There was a problem hiding this comment.
Confirmed: the default value was true. I changed to false in this commit
Summary
Continuing the approach from #2186 with the Fleet logs. This PR adds
GET /jobs/{id}/result/as a new endpoint for downloiad job results and updates the Python client to use it.New endpoint:
GET /jobs/{id}/result/Serves both Ray and Fleet jobs, but behaves differently by runner:
results.jsonexists in COS and returns 302 redirect to a presigned URL (result ready) or 204 No Content (not ready yet). The gateway never download the COS content.The existing
GET /jobs/{id}/endpoint is unchanged and still supports?with_result=truefor backward compatibility with older clients.Client:
result()GET /jobs/{id}/?with_result=true.GET /jobs/{id}/result/and handles 302 (follows redirect to COS, decodes JSON from the COS response), 204 (returnsNone), and 200 (decodesresponse["result"]as before for Ray jobs).Client:
job()GET /jobs/{id}/without an explicitwith_resultparameter. The server defaulted towith_result=true, so it read the result from COS or the local filesystem on every call.with_result=false. The server skips the storage read entirely and returnsresult: null. This does not break anything:job()only usesidandcompute_profilefrom the response and never exposed the result field to the caller. The result has always been fetched separately viaresult().