2424from cirq_google .engine .asyncio_executor import AsyncioExecutor
2525from cirq_google .engine .stream_manager import (
2626 _get_retry_request_or_raise ,
27- ProgramAlreadyExistsError ,
2827 ResponseDemux ,
2928 StreamError ,
3029 StreamManager ,
@@ -524,9 +523,40 @@ async def test():
524523 duet .run (test )
525524
526525 @mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
527- def test_submit_program_already_exists_expects_program_already_exists_error (
526+ def test_submit_program_already_exists_expects_get_result_request (self , client_constructor ):
527+ expected_result = quantum .QuantumResult (parent = 'projects/proj/programs/prog/jobs/job0' )
528+ fake_client , manager = setup (client_constructor )
529+
530+ async def test ():
531+ async with duet .timeout_scope (5 ):
532+ actual_result_future = manager .submit (
533+ REQUEST_PROJECT_NAME , REQUEST_PROGRAM , REQUEST_JOB0
534+ )
535+ await fake_client .wait_for_requests ()
536+ await fake_client .reply (
537+ quantum .QuantumRunStreamResponse (
538+ error = quantum .StreamError (
539+ code = quantum .StreamError .Code .PROGRAM_ALREADY_EXISTS
540+ )
541+ )
542+ )
543+ await fake_client .wait_for_requests ()
544+ await fake_client .reply (quantum .QuantumRunStreamResponse (result = expected_result ))
545+ actual_result = await actual_result_future
546+ manager .stop ()
547+
548+ assert actual_result == expected_result
549+ assert len (fake_client .all_stream_requests ) == 2
550+ assert 'create_quantum_program_and_job' in fake_client .all_stream_requests [0 ]
551+ assert 'get_quantum_result' in fake_client .all_stream_requests [1 ]
552+
553+ duet .run (test )
554+
555+ @mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
556+ def test_submit_program_already_exists_but_job_does_not_exist_expects_create_job_request (
528557 self , client_constructor
529558 ):
559+ expected_result = quantum .QuantumResult (parent = 'projects/proj/programs/prog/jobs/job0' )
530560 fake_client , manager = setup (client_constructor )
531561
532562 async def test ():
@@ -542,10 +572,75 @@ async def test():
542572 )
543573 )
544574 )
545- with pytest .raises (ProgramAlreadyExistsError ):
546- await actual_result_future
575+ await fake_client .wait_for_requests ()
576+ await fake_client .reply (
577+ quantum .QuantumRunStreamResponse (
578+ error = quantum .StreamError (code = quantum .StreamError .Code .JOB_DOES_NOT_EXIST )
579+ )
580+ )
581+ await fake_client .wait_for_requests ()
582+ await fake_client .reply (quantum .QuantumRunStreamResponse (result = expected_result ))
583+ actual_result = await actual_result_future
547584 manager .stop ()
548585
586+ assert actual_result == expected_result
587+ assert len (fake_client .all_stream_requests ) == 3
588+ assert 'create_quantum_program_and_job' in fake_client .all_stream_requests [0 ]
589+ assert 'get_quantum_result' in fake_client .all_stream_requests [1 ]
590+ assert 'create_quantum_job' in fake_client .all_stream_requests [2 ]
591+
592+ duet .run (test )
593+
594+ @mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
595+ def test_submit_job_already_exist_expects_get_result_request (self , client_constructor ):
596+ """Verifies the behavior when the client receives a JOB_ALREADY_EXISTS error.
597+
598+ This error is only expected to be triggered in the following race condition:
599+ 1. The client sends a CreateQuantumProgramAndJobRequest.
600+ 2. The client's stream disconnects.
601+ 3. The client retries with a new stream and a GetQuantumResultRequest.
602+ 4. The job doesn't exist yet, and the client receives a "job not found" error.
603+ 5. Scheduler creates the program and job.
604+ 6. The client retries with a CreateJobRequest and fails with a "job already exists" error.
605+
606+ The JOB_ALREADY_EXISTS error from `CreateQuantumJobRequest` is only possible if the job
607+ doesn't exist yet at the last `GetQuantumResultRequest`.
608+ """
609+ expected_result = quantum .QuantumResult (parent = 'projects/proj/programs/prog/jobs/job0' )
610+ fake_client , manager = setup (client_constructor )
611+
612+ async def test ():
613+ async with duet .timeout_scope (5 ):
614+ actual_result_future = manager .submit (
615+ REQUEST_PROJECT_NAME , REQUEST_PROGRAM , REQUEST_JOB0
616+ )
617+ await fake_client .wait_for_requests ()
618+ await fake_client .reply (google_exceptions .ServiceUnavailable ('unavailable' ))
619+ await fake_client .wait_for_requests ()
620+ # Trigger a retry with `CreateQuantumJobRequest`.
621+ await fake_client .reply (
622+ quantum .QuantumRunStreamResponse (
623+ error = quantum .StreamError (code = quantum .StreamError .Code .JOB_DOES_NOT_EXIST )
624+ )
625+ )
626+ await fake_client .wait_for_requests ()
627+ await fake_client .reply (
628+ quantum .QuantumRunStreamResponse (
629+ error = quantum .StreamError (code = quantum .StreamError .Code .JOB_ALREADY_EXISTS )
630+ )
631+ )
632+ await fake_client .wait_for_requests ()
633+ await fake_client .reply (quantum .QuantumRunStreamResponse (result = expected_result ))
634+ actual_result = await actual_result_future
635+ manager .stop ()
636+
637+ assert actual_result == expected_result
638+ assert len (fake_client .all_stream_requests ) == 4
639+ assert 'create_quantum_program_and_job' in fake_client .all_stream_requests [0 ]
640+ assert 'get_quantum_result' in fake_client .all_stream_requests [1 ]
641+ assert 'create_quantum_job' in fake_client .all_stream_requests [2 ]
642+ assert 'get_quantum_result' in fake_client .all_stream_requests [3 ]
643+
549644 duet .run (test )
550645
551646 @mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
@@ -690,6 +785,7 @@ async def test():
690785 (Code .PROGRAM_ALREADY_EXISTS , 'get_quantum_result' ),
691786 (Code .JOB_DOES_NOT_EXIST , 'create_quantum_program_and_job' ),
692787 (Code .JOB_DOES_NOT_EXIST , 'create_quantum_job' ),
788+ (Code .JOB_ALREADY_EXISTS , 'get_quantum_result' ),
693789 ],
694790 )
695791 def test_get_retry_request_or_raise_expects_stream_error (
@@ -720,6 +816,7 @@ def test_get_retry_request_or_raise_expects_stream_error(
720816 current_request ,
721817 create_quantum_program_and_job_request ,
722818 create_quantum_job_request ,
819+ get_quantum_result_request ,
723820 )
724821
725822 @mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
0 commit comments