24
24
from cirq_google .engine .asyncio_executor import AsyncioExecutor
25
25
from cirq_google .engine .stream_manager import (
26
26
_get_retry_request_or_raise ,
27
- ProgramAlreadyExistsError ,
28
27
ResponseDemux ,
29
28
StreamError ,
30
29
StreamManager ,
@@ -524,9 +523,40 @@ async def test():
524
523
duet .run (test )
525
524
526
525
@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 (
528
557
self , client_constructor
529
558
):
559
+ expected_result = quantum .QuantumResult (parent = 'projects/proj/programs/prog/jobs/job0' )
530
560
fake_client , manager = setup (client_constructor )
531
561
532
562
async def test ():
@@ -542,10 +572,75 @@ async def test():
542
572
)
543
573
)
544
574
)
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
547
584
manager .stop ()
548
585
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
+
549
644
duet .run (test )
550
645
551
646
@mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
@@ -690,6 +785,7 @@ async def test():
690
785
(Code .PROGRAM_ALREADY_EXISTS , 'get_quantum_result' ),
691
786
(Code .JOB_DOES_NOT_EXIST , 'create_quantum_program_and_job' ),
692
787
(Code .JOB_DOES_NOT_EXIST , 'create_quantum_job' ),
788
+ (Code .JOB_ALREADY_EXISTS , 'get_quantum_result' ),
693
789
],
694
790
)
695
791
def test_get_retry_request_or_raise_expects_stream_error (
@@ -720,6 +816,7 @@ def test_get_retry_request_or_raise_expects_stream_error(
720
816
current_request ,
721
817
create_quantum_program_and_job_request ,
722
818
create_quantum_job_request ,
819
+ get_quantum_result_request ,
723
820
)
724
821
725
822
@mock .patch .object (quantum , 'QuantumEngineServiceAsyncClient' , autospec = True )
0 commit comments