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

Provide support for large job arguments #1501

Merged
merged 22 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/qiskit_serverless/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class BaseClient(JsonSerializable):
>>> )
"""

def __init__(
def __init__( # pylint: disable=too-many-positional-arguments
self,
name: str,
host: Optional[str] = None,
Expand Down Expand Up @@ -386,7 +386,7 @@ class ServerlessClient(BaseClient):
>>> )
"""

def __init__(
def __init__( # pylint: disable=too-many-positional-arguments
self,
name: Optional[str] = None,
host: Optional[str] = None,
Expand Down
8 changes: 5 additions & 3 deletions client/qiskit_serverless/serializers/program_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.utils.json import RuntimeDecoder, RuntimeEncoder

from qiskit_serverless.core.constants import ENV_JOB_ARGUMENTS


class QiskitObjectsEncoder(RuntimeEncoder):
"""Json encoder for Qiskit objects."""
Expand Down Expand Up @@ -81,4 +79,8 @@ def get_arguments() -> Dict[str, Any]:
Returns:
Dictionary of arguments.
"""
return json.loads(os.environ.get(ENV_JOB_ARGUMENTS, "{}"), cls=QiskitObjectsDecoder)
arguments = "{}"
if os.path.isfile("arguments.serverless"):
with open("arguments.serverless", "r", encoding="utf-8") as f:
arguments = f.read()
return json.loads(arguments, cls=QiskitObjectsDecoder)
5 changes: 3 additions & 2 deletions client/qiskit_serverless/utils/runtime_service_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class ServerlessRuntimeService(QiskitRuntimeService):
QiskitRuntimeService (QiskitRuntimeService): Qiskit runtime service object.
"""

def run(
def run( # pylint: disable=too-many-positional-arguments
self,
program_id: str,
inputs: Dict,
Expand All @@ -105,7 +105,8 @@ def run(
session_id: Optional[str] = None,
start_session: Optional[bool] = False,
) -> Union[RuntimeJob, RuntimeJobV2]:
runtime_job = super().run(
"""Run a serverless Runtime service job."""
runtime_job = super()._run(
program_id,
inputs,
options,
Expand Down
8 changes: 3 additions & 5 deletions client/tests/serializers/test_program_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@

"""QiskitPattern serializers tests."""
import json
import os
from unittest import TestCase, skip

import numpy as np
from qiskit.circuit.random import random_circuit
from qiskit_ibm_runtime import QiskitRuntimeService

from qiskit_serverless.core.constants import ENV_JOB_ARGUMENTS
from qiskit_serverless.serializers.program_serializers import (
QiskitObjectsDecoder,
QiskitObjectsEncoder,
Expand Down Expand Up @@ -61,8 +59,8 @@ def test_argument_parsing(self):
circuit = random_circuit(4, 2)
array = np.array([[42.0], [0.0]])

os.environ[ENV_JOB_ARGUMENTS] = json.dumps(
{"circuit": circuit, "array": array}, cls=QiskitObjectsEncoder
)
with open("arguments.serverless", "w", encoding="utf-8") as f:
json.dump({"circuit": circuit, "array": array}, f, cls=QiskitObjectsEncoder)

parsed_arguments = get_arguments()
self.assertEqual(list(parsed_arguments.keys()), ["circuit", "array"])
13 changes: 13 additions & 0 deletions gateway/api/ray.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ def submit(self, job: Job) -> Optional[str]:
# get entrypoint
entrypoint = f"python {program.entrypoint}"

# upload arguments to working directory
# if no arguments, write an empty dict to the arguments file
with open(
working_directory_for_upload + "/arguments.serverless",
"w",
encoding="utf-8",
) as f:
if job.arguments:
logger.debug("uploading arguments for job %s", job.id)
f.write(job.arguments)
else:
f.write({})

# set tracing
carrier = {}
TraceContextTextMapPropagator().inject(carrier)
Expand Down
17 changes: 15 additions & 2 deletions gateway/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from ray.dashboard.modules.job.common import JobStatus
from django.conf import settings
from parsley import makeGrammar
import objsize

from .models import Job

Expand Down Expand Up @@ -112,18 +113,30 @@ def decrypt_string(string: str) -> str:
return fernet.decrypt(string.encode("utf-8")).decode("utf-8")


def build_env_variables(token, job: Job, arguments: str) -> Dict[str, str]:
def build_env_variables(token, job: Job, args: str = None) -> Dict[str, str]:
"""Builds env variables for job.

Args:
token: django request token decoded
job: job
arguments: program arguments

Returns:
env variables dict
"""
extra = {}
# only set arguments envvar if not too big
# remove this after sufficient time for users to upgrade client
arguments = "{}"
if args:
if objsize.get_deep_size(args) < 100000:
logger.debug("passing arguments as envvar for job %s", job.id)
arguments = args
else:
logger.warning(
"arguments for job %s are too large and will not be written to env var",
job.id,
)

if settings.SETTINGS_AUTH_MECHANISM != "default":
extra = {
"QISKIT_IBM_TOKEN": str(token),
Expand Down
3 changes: 2 additions & 1 deletion gateway/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ qiskit-ibm-runtime>=0.29.0
tzdata>=2024.1
django-cors-headers>=4.4.0, <5
parsley>=1.3, <2
whitenoise>=6.7.0, <7
whitenoise>=6.7.0, <7
objsize>=0.7.0
10 changes: 4 additions & 6 deletions gateway/tests/api/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,26 @@ def test_build_env_for_job(self):
token = "42"
job = MagicMock()
job.id = "42"
env_vars = build_env_variables(token=token, job=job, arguments={"answer": 42})
env_vars = build_env_variables(token=token, job=job)
self.assertEqual(
env_vars,
{
"ENV_JOB_GATEWAY_TOKEN": "42",
"ENV_JOB_GATEWAY_HOST": "http://localhost:8000",
"ENV_JOB_ID_GATEWAY": "42",
"ENV_JOB_ARGUMENTS": {"answer": 42},
"ENV_JOB_ARGUMENTS": "{}",
},
)

with self.settings(
SETTINGS_AUTH_MECHANISM="custom_token", SECRET_KEY="super-secret"
):
env_vars_with_qiskit_runtime = build_env_variables(
token=token, job=job, arguments={"answer": 42}
)
env_vars_with_qiskit_runtime = build_env_variables(token=token, job=job)
expecting = {
"ENV_JOB_GATEWAY_TOKEN": "42",
"ENV_JOB_GATEWAY_HOST": "http://localhost:8000",
"ENV_JOB_ID_GATEWAY": "42",
"ENV_JOB_ARGUMENTS": {"answer": 42},
"ENV_JOB_ARGUMENTS": "{}",
"QISKIT_IBM_TOKEN": "42",
"QISKIT_IBM_CHANNEL": "ibm_quantum",
"QISKIT_IBM_URL": "https://auth.quantum-computing.ibm.com/api",
Expand Down