1use crate::database_client::DatabaseClient;
16use crate::error::internal_error;
17use crate::model::TransactionOptions;
18use crate::model::TransactionSelector;
19use crate::model::transaction_options::{Mode, ReadOnly};
20use crate::precommit::PrecommitTokenTracker;
21use crate::result_set::{ResultSet, ResultSetParams, StreamOperation};
22use crate::statement::Statement;
23use crate::timestamp_bound::TimestampBound;
24use crate::transaction_retry_policy::is_aborted;
25use google_cloud_gax::backoff_policy::BackoffPolicyArg;
26use google_cloud_gax::options::internal::RequestOptionsExt as _;
27use google_cloud_gax::retry_policy::RetryPolicyArg;
28use std::mem::replace;
29use std::sync::{Arc, Mutex};
30use std::time::Duration;
31use tokio::sync::Notify;
32
33pub struct SingleUseReadOnlyTransactionBuilder {
48 client: DatabaseClient,
49 timestamp_bound: Option<TimestampBound>,
50}
51
52impl SingleUseReadOnlyTransactionBuilder {
53 pub(crate) fn new(client: DatabaseClient) -> Self {
54 Self {
55 client,
56 timestamp_bound: None,
57 }
58 }
59
60 pub fn set_timestamp_bound(mut self, bound: TimestampBound) -> Self {
77 self.timestamp_bound = Some(bound);
78 self
79 }
80
81 pub fn build(self) -> SingleUseReadOnlyTransaction {
94 let read_only = match self.timestamp_bound {
95 Some(b) => ReadOnly::default().set_timestamp_bound(b.0),
96 None => ReadOnly::default().set_strong(true),
97 };
98 let transaction_selector = crate::model::TransactionSelector::default()
99 .set_single_use(TransactionOptions::default().set_read_only(read_only));
100
101 let session_name = self.client.session_name();
102 let channel_hint = self.client.spanner.next_channel_hint();
103 SingleUseReadOnlyTransaction {
104 context: ReadContext {
105 session_name,
106 client: self.client,
107 transaction_selector: ReadContextTransactionSelector::Fixed(
108 transaction_selector,
109 None,
110 ),
111 precommit_token_tracker: PrecommitTokenTracker::new_noop(),
112 transaction_tag: None,
113 channel_hint,
114 begin_transaction_request_options: None,
115 },
116 }
117 }
118}
119
120#[derive(Debug)]
138pub struct SingleUseReadOnlyTransaction {
139 context: ReadContext,
140}
141
142impl SingleUseReadOnlyTransaction {
143 pub async fn execute_query<T: Into<Statement>>(
164 &self,
165 statement: T,
166 ) -> crate::Result<ResultSet> {
167 self.context.execute_query(statement, None).await
168 }
169
170 pub async fn execute_read<T: Into<crate::read::ReadRequest>>(
202 &self,
203 read: T,
204 ) -> crate::Result<ResultSet> {
205 self.context.execute_read(read).await
206 }
207}
208
209#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
211#[non_exhaustive]
212pub enum BeginTransactionOption {
213 #[default]
216 InlineBegin,
217 ExplicitBegin,
219}
220
221pub struct MultiUseReadOnlyTransactionBuilder {
237 client: DatabaseClient,
238 timestamp_bound: Option<TimestampBound>,
239 begin_transaction_option: BeginTransactionOption,
240 begin_gax_options: Option<crate::RequestOptions>,
241}
242
243impl MultiUseReadOnlyTransactionBuilder {
244 pub(crate) fn new(client: DatabaseClient) -> Self {
245 Self {
246 client,
247 timestamp_bound: None,
248 begin_transaction_option: BeginTransactionOption::InlineBegin,
249 begin_gax_options: None,
250 }
251 }
252
253 pub fn with_begin_transaction_option(mut self, option: BeginTransactionOption) -> Self {
285 self.begin_transaction_option = option;
286 self
287 }
288
289 pub fn with_begin_attempt_timeout(mut self, timeout: Duration) -> Self {
307 self.begin_gax_options
308 .get_or_insert_with(crate::RequestOptions::default)
309 .set_attempt_timeout(timeout);
310 self
311 }
312
313 pub fn with_begin_retry_policy(mut self, policy: impl Into<RetryPolicyArg>) -> Self {
331 self.begin_gax_options
332 .get_or_insert_with(crate::RequestOptions::default)
333 .set_retry_policy(policy);
334 self
335 }
336
337 pub fn with_begin_backoff_policy(mut self, policy: impl Into<BackoffPolicyArg>) -> Self {
355 self.begin_gax_options
356 .get_or_insert_with(crate::RequestOptions::default)
357 .set_backoff_policy(policy);
358 self
359 }
360
361 pub fn set_timestamp_bound(mut self, bound: TimestampBound) -> Self {
374 self.timestamp_bound = Some(bound);
375 self
376 }
377
378 async fn begin(
379 &self,
380 session_name: String,
381 options: TransactionOptions,
382 channel_hint: usize,
383 request_options: crate::RequestOptions,
384 ) -> crate::Result<ReadContextTransactionSelector> {
385 let response = execute_begin_transaction(
386 &self.client,
387 session_name,
388 options,
389 None,
390 channel_hint,
391 request_options,
392 None,
393 )
394 .await?;
395
396 let transaction_selector = crate::model::TransactionSelector::default().set_id(response.id);
397
398 Ok(ReadContextTransactionSelector::Fixed(
399 transaction_selector,
400 response.read_timestamp,
401 ))
402 }
403
404 pub async fn build(self) -> crate::Result<MultiUseReadOnlyTransaction> {
417 let read_only = ReadOnly::default().set_return_read_timestamp(true);
418 let read_only = match self.timestamp_bound.as_ref() {
419 Some(b) => read_only.set_timestamp_bound(b.0.clone()),
420 None => read_only.set_strong(true),
421 };
422 let options = TransactionOptions::default().set_read_only(read_only);
423
424 let session_name = self.client.session_name();
425 let channel_hint = self.client.spanner.next_channel_hint();
426 let selector = match self.begin_transaction_option {
427 BeginTransactionOption::ExplicitBegin => {
428 self.begin(
429 session_name.clone(),
430 options,
431 channel_hint,
432 self.begin_gax_options.clone().unwrap_or_default(),
433 )
434 .await?
435 }
436 BeginTransactionOption::InlineBegin => ReadContextTransactionSelector::Lazy(Arc::new(
437 Mutex::new(TransactionState::NotStarted(options)),
438 )),
439 };
440
441 Ok(MultiUseReadOnlyTransaction {
442 context: ReadContext {
443 session_name,
444 client: self.client,
445 transaction_selector: selector,
446 precommit_token_tracker: PrecommitTokenTracker::new_noop(),
447 transaction_tag: None,
448 channel_hint,
449 begin_transaction_request_options: self.begin_gax_options.clone(),
450 },
451 })
452 }
453}
454
455#[derive(Debug)]
477pub struct MultiUseReadOnlyTransaction {
478 pub(crate) context: ReadContext,
479}
480
481impl MultiUseReadOnlyTransaction {
482 pub fn read_timestamp(&self) -> Option<wkt::Timestamp> {
484 self.context.transaction_selector.read_timestamp()
485 }
486
487 pub async fn execute_query<T: Into<Statement>>(
508 &self,
509 statement: T,
510 ) -> crate::Result<ResultSet> {
511 self.context.execute_query(statement, None).await
512 }
513
514 pub async fn execute_read<T: Into<crate::read::ReadRequest>>(
546 &self,
547 read: T,
548 ) -> crate::Result<ResultSet> {
549 self.context.execute_read(read).await
550 }
551}
552
553pub(crate) async fn execute_begin_transaction(
555 client: &crate::database_client::DatabaseClient,
556 session_name: String,
557 options: crate::model::TransactionOptions,
558 transaction_tag: Option<String>,
559 channel_hint: usize,
560 request_options: crate::RequestOptions,
561 mutation_key: Option<crate::model::Mutation>,
562) -> crate::Result<crate::model::Transaction> {
563 let mut request = crate::model::BeginTransactionRequest::default()
564 .set_session(session_name)
565 .set_options(options)
566 .set_or_clear_mutation_key(mutation_key);
567 if let Some(tag) = transaction_tag {
568 request = request
569 .set_request_options(crate::model::RequestOptions::default().set_transaction_tag(tag));
570 }
571
572 client
573 .spanner
574 .begin_transaction(request, request_options, channel_hint)
575 .await
576}
577
578#[derive(Clone, Debug)]
579pub(crate) enum ReadContextTransactionSelector {
580 Fixed(crate::model::TransactionSelector, Option<wkt::Timestamp>),
581 Lazy(Arc<Mutex<TransactionState>>),
582}
583
584#[derive(Clone, Debug)]
585pub(crate) enum TransactionState {
586 NotStarted(crate::model::TransactionOptions),
587 Starting(crate::model::TransactionOptions, Arc<Notify>),
588 Started(crate::model::TransactionSelector, Option<wkt::Timestamp>),
589 Failed(Arc<crate::Error>),
590 FirstStatementFailed,
591}
592
593enum SelectorStatus {
594 Ready(crate::model::TransactionSelector),
595 Wait(std::sync::Arc<tokio::sync::Notify>),
596}
597
598fn to_start_error(err: &crate::Error) -> crate::Error {
599 if let Some(status) = err.status() {
600 crate::Error::service(status.clone())
601 } else {
602 crate::error::internal_error(format!("Transaction failed to start: {}", err))
603 }
604}
605
606impl ReadContextTransactionSelector {
607 pub(crate) async fn selector(&self) -> crate::Result<crate::model::TransactionSelector> {
608 match self {
609 Self::Fixed(selector, _) => Ok(selector.clone()),
610 Self::Lazy(_) => loop {
611 match self.poll_selector_status()? {
612 SelectorStatus::Ready(selector) => return Ok(selector),
613 SelectorStatus::Wait(notify) => notify.notified().await,
614 }
615 },
616 }
617 }
618
619 fn poll_selector_status(&self) -> crate::Result<SelectorStatus> {
622 let Self::Lazy(lazy) = self else {
623 unreachable!("poll_selector_status called on non-Lazy selector");
624 };
625 let mut guard = lazy.lock().expect("transaction state mutex poisoned");
626
627 if let TransactionState::Started(selector, _) = &*guard {
629 return Ok(SelectorStatus::Ready(selector.clone()));
630 }
631
632 let pending_options = if let TransactionState::NotStarted(options) = &*guard {
634 Some(options.clone())
635 } else {
636 None
639 };
640 if let Some(options) = pending_options {
641 let notify = Arc::new(Notify::new());
643 *guard = TransactionState::Starting(options.clone(), Arc::clone(¬ify));
644 return Ok(SelectorStatus::Ready(
645 crate::model::TransactionSelector::default().set_begin(options),
646 ));
647 }
648
649 match &*guard {
651 TransactionState::Failed(err) => Err(to_start_error(err)),
655 TransactionState::FirstStatementFailed => {
660 let status = google_cloud_gax::error::rpc::Status::default()
661 .set_code(google_cloud_gax::error::rpc::Code::Aborted)
662 .set_message("Aborted due to failed initial statement");
663 Err(crate::Error::service(status))
664 }
665 TransactionState::Starting(_, notify) => Ok(SelectorStatus::Wait(Arc::clone(notify))),
667 TransactionState::Started(_, _) | TransactionState::NotStarted(_) => unreachable!(),
668 }
669 }
670}
671
672pub(crate) struct ExplicitBeginParams {
673 pub(crate) client: crate::database_client::DatabaseClient,
674 pub(crate) session_name: String,
675 pub(crate) transaction_tag: Option<String>,
676 pub(crate) channel_hint: usize,
677 pub(crate) request_options: crate::RequestOptions,
678 pub(crate) is_stream_fallback: bool,
679 pub(crate) precommit_token_tracker: crate::precommit::PrecommitTokenTracker,
680 pub(crate) mutation_key: Option<crate::model::Mutation>,
681}
682
683impl ReadContextTransactionSelector {
684 pub(crate) async fn begin_explicitly(&self, params: ExplicitBeginParams) -> crate::Result<()> {
689 let Self::Lazy(lazy) = self else {
690 return Ok(());
691 };
692
693 enum FallbackAction {
694 Begin(
695 crate::model::TransactionOptions,
696 Option<Arc<tokio::sync::Notify>>,
697 ),
698 Wait(Arc<tokio::sync::Notify>),
699 None,
700 }
701
702 let action = {
703 let mut guard = lazy
704 .lock()
705 .map_err(|_| internal_error("transaction state mutex poisoned"))?;
706 match &*guard {
707 TransactionState::NotStarted(options) => {
708 let options = options.clone();
711 let notify = Arc::new(tokio::sync::Notify::new());
712 *guard = TransactionState::Starting(options.clone(), Arc::clone(¬ify));
713 FallbackAction::Begin(options, Some(notify))
714 }
715 TransactionState::Starting(options, notify) => {
716 if !params.is_stream_fallback {
722 FallbackAction::Wait(Arc::clone(notify))
723 } else {
724 FallbackAction::Begin(options.clone(), Some(Arc::clone(notify)))
725 }
726 }
727 TransactionState::Started(_, _)
728 | TransactionState::Failed(_)
729 | TransactionState::FirstStatementFailed => {
730 FallbackAction::None
733 }
734 }
735 };
736
737 let (options, notify_opt) = match action {
738 FallbackAction::None => return Ok(()),
739 FallbackAction::Wait(notify) => {
740 notify.notified().await;
741 return Ok(());
742 }
743 FallbackAction::Begin(opts, notif) => (opts, notif),
744 };
745
746 let response = match execute_begin_transaction(
750 ¶ms.client,
751 params.session_name,
752 options,
753 params.transaction_tag,
754 params.channel_hint,
755 params.request_options,
756 params.mutation_key,
757 )
758 .await
759 {
760 Ok(r) => r,
761 Err(e) => {
762 let mut guard = lazy.lock().expect("transaction state mutex poisoned");
763 let error = Arc::new(e);
764 *guard = TransactionState::Failed(Arc::clone(&error));
765 drop(guard);
768 if let Some(notify) = notify_opt {
769 notify.notify_waiters();
770 }
771
772 return Err(to_start_error(&error));
773 }
774 };
775
776 self.update(response.id, response.read_timestamp)?;
777 params
778 .precommit_token_tracker
779 .update(response.precommit_token);
780
781 Ok(())
782 }
783
784 pub(crate) fn update(
793 &self,
794 id: bytes::Bytes,
795 timestamp: Option<wkt::Timestamp>,
796 ) -> crate::Result<()> {
797 let Self::Lazy(lazy) = self else {
798 return Ok(());
799 };
800 let mut guard = lazy.lock().expect("transaction state mutex poisoned");
801
802 if matches!(
803 &*guard,
804 TransactionState::NotStarted(_) | TransactionState::Starting(_, _)
805 ) {
806 let previous_state = replace(
810 &mut *guard,
811 TransactionState::Started(TransactionSelector::default().set_id(id), timestamp),
812 );
813 drop(guard);
814
815 if let TransactionState::Starting(_, notify) = previous_state {
817 notify.notify_waiters();
818 }
819 Ok(())
820 } else if let TransactionState::Started(existing_selector, _) = &*guard {
821 if existing_selector.id() == Some(&id) {
825 Ok(())
826 } else {
827 Err(crate::error::internal_error(
828 "got a transaction id for an already Started or Failed transaction",
829 ))
830 }
831 } else {
832 Err(crate::error::internal_error(
834 "got a transaction id for an already Started or Failed transaction",
835 ))
836 }
837 }
838
839 pub(crate) fn get_id_no_wait(&self) -> crate::Result<Option<bytes::Bytes>> {
845 use crate::model::transaction_selector::Selector;
846 match self {
847 Self::Fixed(selector, _) => {
848 if let Some(Selector::Id(id)) = &selector.selector {
849 return Ok(Some(id.clone()));
850 }
851 }
852 Self::Lazy(lazy) => {
853 let guard = lazy
854 .lock()
855 .map_err(|_| internal_error("transaction state mutex poisoned"))?;
856 if let TransactionState::Started(selector, _) = &*guard
857 && let Some(Selector::Id(id)) = &selector.selector
858 {
859 return Ok(Some(id.clone()));
860 }
861 }
862 }
863 Ok(None)
864 }
865
866 pub(crate) fn is_starting(&self) -> crate::Result<bool> {
868 match self {
869 Self::Lazy(lazy) => {
870 let guard = lazy
871 .lock()
872 .map_err(|_| internal_error("transaction state mutex poisoned"))?;
873 Ok(matches!(&*guard, TransactionState::Starting(_, _)))
874 }
875 _ => Ok(false),
876 }
877 }
878
879 pub(crate) fn maybe_reset_starting(&self) {
887 let Self::Lazy(lazy) = self else {
888 return;
889 };
890
891 let mut guard = lazy.lock().expect("transaction state mutex poisoned");
892 if let TransactionState::Starting(options, notify) = &*guard {
893 let options = options.clone();
894 let notify = Arc::clone(notify);
895 *guard = TransactionState::NotStarted(options);
896 drop(guard);
897 notify.notify_waiters();
898 }
899 }
900
901 pub(crate) fn is_first_statement_failed(&self) -> bool {
902 let Self::Lazy(lazy) = self else {
903 return false;
904 };
905 let guard = lazy.lock().expect("transaction state mutex poisoned");
906 matches!(&*guard, TransactionState::FirstStatementFailed)
907 }
908
909 pub(crate) fn is_read_write(&self) -> bool {
910 let Self::Lazy(lazy) = self else {
911 return false;
912 };
913 let guard = lazy.lock().expect("transaction state mutex poisoned");
914 match &*guard {
915 TransactionState::NotStarted(options) | TransactionState::Starting(options, _) => {
916 matches!(&options.mode, Some(Mode::ReadWrite(_)))
917 }
918 _ => false,
919 }
920 }
921
922 pub(crate) fn set_failed(&self, err: &crate::Error) {
923 let Self::Lazy(lazy) = self else {
924 return;
925 };
926 let mut guard = lazy.lock().expect("transaction state mutex poisoned");
927 if let TransactionState::Starting(options, notify) = &*guard {
928 let notify = Arc::clone(notify);
929 if is_aborted(err) {
930 *guard = TransactionState::NotStarted(options.clone());
931 } else {
932 *guard = TransactionState::FirstStatementFailed;
933 }
934 drop(guard);
935 notify.notify_waiters();
936 }
937 }
938
939 pub(crate) fn check_failed(&self) -> crate::Result<()> {
940 let Self::Lazy(lazy) = self else {
941 return Ok(());
942 };
943 let guard = lazy.lock().expect("transaction state mutex poisoned");
944 match &*guard {
945 TransactionState::Failed(err) => Err(to_start_error(err)),
946 TransactionState::FirstStatementFailed => {
947 Err(crate::error::aborted_due_to_failed_initial_statement())
948 }
949 _ => Ok(()),
950 }
951 }
952
953 pub(crate) fn read_timestamp(&self) -> Option<wkt::Timestamp> {
959 match self {
960 Self::Fixed(_, timestamp) => *timestamp,
961 Self::Lazy(lazy) => {
962 let guard = lazy.lock().expect("transaction state mutex poisoned");
963 if let TransactionState::Started(_, timestamp) = &*guard {
964 *timestamp
965 } else {
966 None
967 }
968 }
969 }
970 }
971}
972
973#[derive(Clone, Debug)]
974pub(crate) struct ReadContext {
975 pub(crate) session_name: String,
976 pub(crate) client: DatabaseClient,
977 pub(crate) transaction_selector: ReadContextTransactionSelector,
978 pub(crate) precommit_token_tracker: PrecommitTokenTracker,
979 pub(crate) transaction_tag: Option<String>,
980 pub(crate) channel_hint: usize,
981 pub(crate) begin_transaction_request_options: Option<crate::RequestOptions>,
982}
983
984impl ReadContext {
985 pub(crate) fn amend_request_options(
992 &self,
993 mut options: Option<crate::model::RequestOptions>,
994 ) -> Option<crate::model::RequestOptions> {
995 if let Some(tag) = &self.transaction_tag {
996 options
997 .get_or_insert_with(crate::model::RequestOptions::default)
998 .transaction_tag = tag.clone();
999 }
1000 options
1001 }
1002
1003 pub(crate) async fn begin_explicitly_if_not_started(
1007 &self,
1008 fallback_options: crate::RequestOptions,
1009 is_stream_fallback: bool,
1010 mutation_key: Option<crate::model::Mutation>,
1011 ) -> crate::Result<bool> {
1012 let ReadContextTransactionSelector::Lazy(lazy) = &self.transaction_selector else {
1013 return Ok(false);
1014 };
1015 let is_started = matches!(&*lazy.lock().unwrap(), TransactionState::Started(_, _));
1016 if is_started {
1017 return Ok(false);
1018 }
1019
1020 let options = merge_request_options(
1021 fallback_options,
1022 self.begin_transaction_request_options.as_ref(),
1023 );
1024
1025 self.transaction_selector
1026 .begin_explicitly(ExplicitBeginParams {
1027 client: self.client.clone(),
1028 session_name: self.session_name.clone(),
1029 transaction_tag: self.transaction_tag.clone(),
1030 channel_hint: self.channel_hint,
1031 request_options: options,
1032 is_stream_fallback,
1033 precommit_token_tracker: self.precommit_token_tracker.clone(),
1034 mutation_key,
1035 })
1036 .await?;
1037 Ok(true)
1038 }
1039}
1040
1041fn merge_request_options(
1044 mut destination: crate::RequestOptions,
1045 source: Option<&crate::RequestOptions>,
1046) -> crate::RequestOptions {
1047 let Some(source) = source else {
1048 return destination;
1049 };
1050
1051 if let Some(timeout) = source.attempt_timeout() {
1052 destination.set_attempt_timeout(*timeout);
1053 }
1054 if let Some(retry) = source.retry_policy() {
1055 destination.set_retry_policy(retry.clone());
1056 }
1057 if let Some(backoff) = source.backoff_policy() {
1058 destination.set_backoff_policy(backoff.clone());
1059 }
1060 if let Some(src_headers) = source.get_extension::<http::HeaderMap>() {
1061 let mut dest_headers = destination
1062 .get_extension::<http::HeaderMap>()
1063 .cloned()
1064 .unwrap_or_default();
1065 for (name, value) in src_headers.iter() {
1066 dest_headers.insert(name.clone(), value.clone());
1067 }
1068 destination = destination.insert_extension(dest_headers);
1069 }
1070 destination
1071}
1072
1073macro_rules! execute_stream_with_retry {
1075 ($self:expr, $request:ident, $gax_options:ident, $rpc_method:ident, $operation_variant:path) => {{
1076 let stream = match $self
1077 .client
1078 .spanner
1079 .$rpc_method($request.clone(), $gax_options.clone(), $self.channel_hint)
1080 .send()
1081 .await
1082 {
1083 Ok(s) => s,
1084 Err(e) => {
1085 let is_starting = matches!(
1086 $request
1087 .transaction
1088 .as_ref()
1089 .and_then(|t| t.selector.as_ref()),
1090 Some(crate::model::transaction_selector::Selector::Begin(_))
1091 );
1092 if is_starting {
1093 if $self.transaction_selector.is_read_write() {
1094 $self.transaction_selector.set_failed(&e);
1095 return Err(e);
1096 } else {
1097 if is_aborted(&e) {
1098 return Err(e);
1099 }
1100 if $self
1101 .begin_explicitly_if_not_started($gax_options.clone(), true, None)
1102 .await?
1103 {
1104 $request.transaction =
1105 Some($self.transaction_selector.selector().await?);
1106 $self
1107 .client
1108 .spanner
1109 .$rpc_method(
1110 $request.clone(),
1111 $gax_options.clone(),
1112 $self.channel_hint,
1113 )
1114 .send()
1115 .await?
1116 } else {
1117 return Err(e);
1118 }
1119 }
1120 } else {
1121 return Err(e);
1122 }
1123 }
1124 };
1125
1126 ResultSet::create(ResultSetParams {
1127 stream,
1128 transaction_selector: Some($self.transaction_selector.clone()),
1129 precommit_token_tracker: $self.precommit_token_tracker.clone(),
1130 client: $self.client.clone(),
1131 session_name: $self.session_name.clone(),
1132 transaction_tag: $self.transaction_tag.clone(),
1133 operation: $operation_variant($request),
1134 channel_hint: $self.channel_hint,
1135 gax_options: $gax_options,
1136 })
1137 .await
1138 }};
1139}
1140
1141impl ReadContext {
1142 pub(crate) async fn execute_query<T: Into<Statement>>(
1143 &self,
1144 statement: T,
1145 seqno: Option<i64>,
1146 ) -> crate::Result<ResultSet> {
1147 let statement = statement.into();
1148 let gax_options = statement.gax_options().clone();
1149 let mut request = statement
1150 .into_request()
1151 .set_session(self.session_name.clone())
1152 .set_transaction(self.transaction_selector.selector().await?)
1153 .set_seqno(seqno.unwrap_or(0));
1154 request.request_options = self.amend_request_options(request.request_options);
1155
1156 execute_stream_with_retry!(
1157 self,
1158 request,
1159 gax_options,
1160 execute_streaming_sql,
1161 StreamOperation::Query
1162 )
1163 }
1164
1165 pub(crate) async fn execute_read<T: Into<crate::read::ReadRequest>>(
1166 &self,
1167 read: T,
1168 ) -> crate::Result<ResultSet> {
1169 let read = read.into();
1170 let gax_options = read.gax_options.clone();
1171 let mut request = read
1172 .into_request()
1173 .set_session(self.session_name.clone())
1174 .set_transaction(self.transaction_selector.selector().await?);
1175 request.request_options = self.amend_request_options(request.request_options);
1176
1177 execute_stream_with_retry!(
1178 self,
1179 request,
1180 gax_options,
1181 streaming_read,
1182 StreamOperation::Read
1183 )
1184 }
1185}
1186
1187#[cfg(test)]
1188pub(crate) mod tests {
1189 use super::*;
1190 use crate::result_set::tests::adapt;
1191 use crate::result_set::tests::string_val;
1192 use crate::statement::Statement;
1193 use crate::value::Value;
1194 use gaxi::grpc::tonic::{self, Code, Response, Status};
1195 use google_cloud_gax::error::rpc::Code as GaxCode;
1196 use google_cloud_gax::exponential_backoff::ExponentialBackoff;
1197 use google_cloud_gax::retry_policy::NeverRetry;
1198 use google_cloud_test_macros::tokio_test_no_panics;
1199 use http::{HeaderMap, HeaderName, HeaderValue};
1200 use mock_v1::transaction_selector::Selector;
1201 use spanner_grpc_mock::MockSpanner;
1202 use spanner_grpc_mock::google::spanner::v1 as mock_v1;
1203 use std::sync::mpsc::channel as std_channel;
1204 use std::sync::{Arc, Mutex as StdMutex};
1205 use tokio::sync::oneshot::channel as oneshot_channel;
1206 use tokio::sync::{Barrier, Mutex, Notify, mpsc};
1207
1208 #[test]
1209 fn auto_traits() {
1210 static_assertions::assert_impl_all!(SingleUseReadOnlyTransactionBuilder: Send, Sync);
1211 static_assertions::assert_impl_all!(SingleUseReadOnlyTransaction: Send, Sync, std::fmt::Debug);
1212 static_assertions::assert_impl_all!(MultiUseReadOnlyTransactionBuilder: Send, Sync);
1213 static_assertions::assert_impl_all!(MultiUseReadOnlyTransaction: Send, Sync, std::fmt::Debug);
1214 static_assertions::assert_impl_all!(ReadContext: Send, Sync, std::fmt::Debug);
1215 }
1216
1217 pub(crate) fn create_session_mock() -> spanner_grpc_mock::MockSpanner {
1218 let mut mock = spanner_grpc_mock::MockSpanner::new();
1219 mock.expect_create_session().once().returning(|_| {
1220 Ok(Response::new(mock_v1::Session {
1221 name: "projects/p/instances/i/databases/d/sessions/123".to_string(),
1222 ..Default::default()
1223 }))
1224 });
1225 mock
1226 }
1227
1228 fn setup_select1() -> spanner_grpc_mock::google::spanner::v1::PartialResultSet {
1229 spanner_grpc_mock::google::spanner::v1::PartialResultSet {
1230 metadata: Some(spanner_grpc_mock::google::spanner::v1::ResultSetMetadata {
1231 row_type: Some(spanner_grpc_mock::google::spanner::v1::StructType {
1232 fields: vec![Default::default()],
1233 }),
1234 ..Default::default()
1235 }),
1236 values: vec![prost_types::Value {
1237 kind: Some(prost_types::value::Kind::StringValue("1".to_string())),
1238 }],
1239 last: true,
1240 ..Default::default()
1241 }
1242 }
1243
1244 pub(crate) async fn setup_db_client(
1245 mock: spanner_grpc_mock::MockSpanner,
1246 ) -> (DatabaseClient, tokio::task::JoinHandle<()>) {
1247 use crate::client::Spanner;
1248 use google_cloud_auth::credentials::anonymous::Builder as Anonymous;
1249 let (address, server) = spanner_grpc_mock::start("0.0.0.0:0", mock)
1250 .await
1251 .expect("Failed to start mock server");
1252
1253 let spanner = Spanner::builder()
1254 .with_endpoint(address)
1255 .with_credentials(Anonymous::new().build())
1256 .build()
1257 .await
1258 .expect("Failed to build client");
1259
1260 let db_client = spanner
1261 .database_client("projects/p/instances/i/databases/d")
1262 .build()
1263 .await
1264 .expect("Failed to create DatabaseClient");
1265
1266 (db_client, server)
1267 }
1268
1269 #[tokio_test_no_panics]
1270 async fn single_use_builder() {
1271 let mock = create_session_mock();
1272
1273 let (db_client, _server) = setup_db_client(mock).await;
1274
1275 let tx = db_client.single_use().build();
1276 let selector = tx
1277 .context
1278 .transaction_selector
1279 .selector()
1280 .await
1281 .expect("Failed to get selector");
1282 let ro = selector
1283 .single_use()
1284 .expect("Expected SingleUse selector")
1285 .read_only()
1286 .expect("Expected ReadOnly mode");
1287 assert_eq!(
1288 ro.timestamp_bound,
1289 Some(crate::model::transaction_options::read_only::TimestampBound::Strong(true))
1290 );
1291
1292 let tx2 = db_client
1293 .single_use()
1294 .set_timestamp_bound(crate::timestamp_bound::TimestampBound::max_staleness(
1295 std::time::Duration::from_secs(10),
1296 ))
1297 .build();
1298 let selector = tx2
1299 .context
1300 .transaction_selector
1301 .selector()
1302 .await
1303 .expect("Failed to get selector");
1304 let ro2 = selector
1305 .single_use()
1306 .expect("Expected SingleUse selector")
1307 .read_only()
1308 .expect("Expected ReadOnly mode");
1309 assert_eq!(
1310 ro2.timestamp_bound,
1311 Some(
1312 crate::model::transaction_options::read_only::TimestampBound::MaxStaleness(
1313 Box::new(wkt::Duration::new(10, 0).expect("failed to create Duration"))
1314 )
1315 )
1316 );
1317 }
1318
1319 #[tokio_test_no_panics]
1320 async fn execute_single_query() {
1321 use super::super::result_set::tests::string_val;
1322 use crate::statement::Statement;
1323 use crate::value::Value;
1324
1325 let mut mock = create_session_mock();
1326
1327 mock.expect_execute_streaming_sql().once().returning(|req| {
1328 let req = req.into_inner();
1329 assert_eq!(
1330 req.session,
1331 "projects/p/instances/i/databases/d/sessions/123"
1332 );
1333 assert_eq!(req.sql, "SELECT 1");
1334
1335 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1336 setup_select1(),
1337 )])))
1338 });
1339
1340 let (db_client, _server) = setup_db_client(mock).await;
1341
1342 let tx = db_client.single_use().build();
1343 let mut rs = tx
1344 .execute_query(Statement::builder("SELECT 1").build())
1345 .await
1346 .expect("Failed to execute query");
1347
1348 let row = rs.next().await.expect("has row").expect("has valid row");
1349 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
1350 let result = rs.next().await;
1351 assert!(result.is_none(), "expected None, got {result:?}");
1352 }
1353
1354 #[tokio_test_no_panics]
1355 async fn execute_multi_query() {
1356 use super::super::result_set::tests::string_val;
1357 use crate::statement::Statement;
1358 use crate::value::Value;
1359 use spanner_grpc_mock::google::spanner::v1 as mock_v1;
1360
1361 let mut mock = create_session_mock();
1362
1363 mock.expect_begin_transaction().once().returning(|req| {
1364 let req = req.into_inner();
1365 assert_eq!(
1366 req.session,
1367 "projects/p/instances/i/databases/d/sessions/123"
1368 );
1369 Ok(tonic::Response::new(mock_v1::Transaction {
1370 id: vec![1, 2, 3],
1371 read_timestamp: Some(prost_types::Timestamp {
1373 seconds: 123456789,
1374 nanos: 0,
1375 }),
1376 ..Default::default()
1377 }))
1378 });
1379
1380 mock.expect_execute_streaming_sql()
1381 .times(2)
1382 .returning(|req| {
1383 let req = req.into_inner();
1384 assert_eq!(
1385 req.session,
1386 "projects/p/instances/i/databases/d/sessions/123"
1387 );
1388 assert_eq!(
1389 req.transaction
1390 .expect("transaction should be present")
1391 .selector
1392 .expect("selector should be present"),
1393 mock_v1::transaction_selector::Selector::Id(vec![1, 2, 3])
1394 );
1395
1396 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1397 setup_select1(),
1398 )])))
1399 });
1400
1401 let (db_client, _server) = setup_db_client(mock).await;
1402
1403 let tx = db_client
1404 .read_only_transaction()
1405 .with_begin_transaction_option(BeginTransactionOption::ExplicitBegin)
1406 .build()
1407 .await
1408 .expect("Failed to start tx");
1409 assert_eq!(
1410 tx.read_timestamp()
1411 .expect("expected read timestamp")
1412 .seconds(),
1413 123456789
1414 );
1415
1416 for _ in 0..2 {
1417 let mut rs = tx
1418 .execute_query(Statement::builder("SELECT 1").build())
1419 .await
1420 .expect("Failed to execute query");
1421
1422 let row = rs.next().await.expect("has row").expect("has valid row");
1423 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
1424
1425 let result = rs.next().await;
1426 assert!(result.is_none(), "expected None, got {result:?}");
1427 }
1428 }
1429
1430 #[tokio_test_no_panics]
1431 async fn execute_multi_query_inline_begin() -> anyhow::Result<()> {
1432 use super::super::result_set::tests::string_val;
1433 use crate::statement::Statement;
1434 use crate::value::Value;
1435 use spanner_grpc_mock::google::spanner::v1 as mock_v1;
1436
1437 let mut mock = create_session_mock();
1438
1439 mock.expect_begin_transaction().never();
1441
1442 let mut seq = mockall::Sequence::new();
1443
1444 mock.expect_execute_streaming_sql()
1445 .times(1)
1446 .in_sequence(&mut seq)
1447 .returning(move |req| {
1448 let req = req.into_inner();
1449 assert_eq!(
1450 req.session,
1451 "projects/p/instances/i/databases/d/sessions/123"
1452 );
1453
1454 match req.transaction.unwrap().selector.unwrap() {
1456 mock_v1::transaction_selector::Selector::Begin(_) => {}
1457 _ => panic!("Expected Selector::Begin"),
1458 }
1459 let mut rs = setup_select1();
1460 rs.metadata.as_mut().unwrap().transaction = Some(mock_v1::Transaction {
1461 id: vec![4, 5, 6],
1462 read_timestamp: Some(prost_types::Timestamp {
1463 seconds: 987654321,
1464 nanos: 0,
1465 }),
1466 ..Default::default()
1467 });
1468 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(rs)])))
1469 });
1470
1471 mock.expect_execute_streaming_sql()
1472 .times(1)
1473 .in_sequence(&mut seq)
1474 .returning(move |req| {
1475 let req = req.into_inner();
1476 match req.transaction.unwrap().selector.unwrap() {
1478 mock_v1::transaction_selector::Selector::Id(id) => {
1479 assert_eq!(id, vec![4, 5, 6]);
1480 }
1481 _ => panic!("Expected Selector::Id"),
1482 }
1483 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1484 setup_select1(),
1485 )])))
1486 });
1487
1488 let (db_client, _server) = setup_db_client(mock).await;
1489
1490 let tx = db_client
1491 .read_only_transaction()
1492 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1493 .build()
1494 .await?;
1495
1496 assert!(tx.read_timestamp().is_none());
1498
1499 for i in 0..2 {
1500 let mut rs = tx
1501 .execute_query(Statement::builder("SELECT 1").build())
1502 .await?;
1503
1504 let row = rs.next().await.expect("Expected a row")?;
1505 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
1506
1507 let result = rs.next().await;
1508 assert!(result.is_none(), "Expected None, got {result:?}");
1509
1510 if i == 0 {
1511 assert_eq!(
1513 tx.read_timestamp()
1514 .expect("Expected read timestamp")
1515 .seconds(),
1516 987654321
1517 );
1518 }
1519 }
1520
1521 Ok(())
1522 }
1523
1524 #[tokio_test_no_panics]
1525 async fn execute_single_read() {
1526 use super::super::result_set::tests::string_val;
1527 use crate::key::KeySet;
1528 use crate::read::ReadRequest;
1529 use crate::value::Value;
1530
1531 let mut mock = create_session_mock();
1532
1533 mock.expect_streaming_read().once().returning(|req| {
1534 let req = req.into_inner();
1535 assert_eq!(
1536 req.session,
1537 "projects/p/instances/i/databases/d/sessions/123"
1538 );
1539 assert_eq!(req.table, "Users");
1540 assert_eq!(req.columns, vec!["Id".to_string(), "Name".to_string()]);
1541
1542 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1543 setup_select1(),
1544 )])))
1545 });
1546
1547 let (db_client, _server) = setup_db_client(mock).await;
1548
1549 let tx = db_client.single_use().build();
1550 let read = ReadRequest::builder("Users", vec!["Id", "Name"])
1551 .with_keys(KeySet::all())
1552 .build();
1553 let mut rs = tx.execute_read(read).await.expect("Failed to execute read");
1554
1555 let row = rs.next().await.expect("has row").expect("has valid row");
1556 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
1557 let result = rs.next().await;
1558 assert!(result.is_none(), "expected None, got {result:?}");
1559 }
1560
1561 #[tokio_test_no_panics]
1562 async fn execute_multi_read() -> anyhow::Result<()> {
1563 use super::super::result_set::tests::string_val;
1564 use crate::key::KeySet;
1565 use crate::read::ReadRequest;
1566 use crate::value::Value;
1567 use spanner_grpc_mock::google::spanner::v1 as mock_v1;
1568
1569 let mut mock = create_session_mock();
1570
1571 mock.expect_begin_transaction().never();
1573
1574 let mut seq = mockall::Sequence::new();
1575
1576 mock.expect_streaming_read()
1577 .times(1)
1578 .in_sequence(&mut seq)
1579 .returning(move |req| {
1580 let req = req.into_inner();
1581 assert_eq!(
1582 req.session,
1583 "projects/p/instances/i/databases/d/sessions/123"
1584 );
1585
1586 match req.transaction.unwrap().selector.unwrap() {
1588 mock_v1::transaction_selector::Selector::Begin(_) => {}
1589 _ => panic!("Expected Selector::Begin"),
1590 }
1591 let mut rs = setup_select1();
1592 rs.metadata.as_mut().unwrap().transaction = Some(mock_v1::Transaction {
1593 id: vec![4, 5, 6],
1594 read_timestamp: Some(prost_types::Timestamp {
1595 seconds: 987654321,
1596 nanos: 0,
1597 }),
1598 ..Default::default()
1599 });
1600 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(rs)])))
1601 });
1602
1603 mock.expect_streaming_read()
1604 .times(1)
1605 .in_sequence(&mut seq)
1606 .returning(move |req| {
1607 let req = req.into_inner();
1608 match req.transaction.unwrap().selector.unwrap() {
1610 mock_v1::transaction_selector::Selector::Id(id) => {
1611 assert_eq!(id, vec![4, 5, 6]);
1612 }
1613 _ => panic!("Expected Selector::Id"),
1614 }
1615 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1616 setup_select1(),
1617 )])))
1618 });
1619
1620 let (db_client, _server) = setup_db_client(mock).await;
1621
1622 let tx = db_client
1623 .read_only_transaction()
1624 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1625 .build()
1626 .await?;
1627
1628 assert!(tx.read_timestamp().is_none());
1630
1631 for i in 0..2 {
1632 let read = ReadRequest::builder("Users", vec!["Id", "Name"])
1633 .with_keys(KeySet::all())
1634 .build();
1635 let mut rs = tx.execute_read(read).await?;
1636
1637 let row = rs.next().await.expect("Expected a row")?;
1638 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
1639
1640 let result = rs.next().await;
1641 assert!(result.is_none(), "Expected None, got {result:?}");
1642
1643 if i == 0 {
1644 assert_eq!(
1646 tx.read_timestamp()
1647 .expect("Expected read timestamp")
1648 .seconds(),
1649 987654321
1650 );
1651 }
1652 }
1653
1654 Ok(())
1655 }
1656
1657 #[tokio_test_no_panics]
1658 async fn inline_begin_failure_retry_success() -> anyhow::Result<()> {
1659 use crate::value::Value;
1660 use gaxi::grpc::tonic::Status;
1661 use tonic::Response;
1662
1663 let mut mock = create_session_mock();
1664 let mut seq = mockall::Sequence::new();
1665
1666 mock.expect_execute_streaming_sql()
1668 .times(1)
1669 .in_sequence(&mut seq)
1670 .returning(|_| Err(Status::internal("Internal error")));
1671
1672 mock.expect_begin_transaction()
1674 .times(1)
1675 .in_sequence(&mut seq)
1676 .returning(|req| {
1677 let req = req.into_inner();
1678 assert_eq!(
1679 req.session,
1680 "projects/p/instances/i/databases/d/sessions/123"
1681 );
1682 Ok(Response::new(mock_v1::Transaction {
1684 id: vec![7, 8, 9],
1685 read_timestamp: Some(prost_types::Timestamp {
1686 seconds: 123456789,
1687 nanos: 0,
1688 }),
1689 ..Default::default()
1690 }))
1691 });
1692
1693 mock.expect_execute_streaming_sql()
1695 .times(1)
1696 .in_sequence(&mut seq)
1697 .returning(|req| {
1698 let req = req.into_inner();
1699 match req.transaction.unwrap().selector.unwrap() {
1701 mock_v1::transaction_selector::Selector::Id(id) => {
1702 assert_eq!(id, vec![7, 8, 9]);
1703 }
1704 _ => panic!("Expected Selector::Id"),
1705 }
1706 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1707 setup_select1(),
1708 )])))
1709 });
1710
1711 let (db_client, _server) = setup_db_client(mock).await;
1712 let tx = db_client
1713 .read_only_transaction()
1714 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1715 .build()
1716 .await?;
1717
1718 let mut rs = tx
1719 .execute_query(Statement::builder("SELECT 1").build())
1720 .await?;
1721
1722 let row = rs
1723 .next()
1724 .await
1725 .ok_or_else(|| anyhow::anyhow!("Expected a row but stream cleanly exhausted"))??;
1726 assert_eq!(
1727 row.raw_values(),
1728 [Value(string_val("1"))],
1729 "The parsed row value safely matched the underlying stream chunk"
1730 );
1731
1732 Ok(())
1733 }
1734
1735 #[tokio_test_no_panics]
1736 async fn inline_begin_failure_retry_failure() -> anyhow::Result<()> {
1737 use gaxi::grpc::tonic::Status;
1738 use tonic::Response;
1739
1740 let mut mock = create_session_mock();
1741 let mut seq = mockall::Sequence::new();
1742
1743 mock.expect_execute_streaming_sql()
1745 .times(1)
1746 .in_sequence(&mut seq)
1747 .returning(|_| Err(Status::internal("Internal error first")));
1748
1749 mock.expect_begin_transaction()
1751 .times(1)
1752 .in_sequence(&mut seq)
1753 .returning(|_| {
1754 Ok(Response::new(mock_v1::Transaction {
1755 id: vec![7, 8, 9],
1756 read_timestamp: Some(prost_types::Timestamp {
1757 seconds: 123456789,
1758 nanos: 0,
1759 }),
1760 ..Default::default()
1761 }))
1762 });
1763
1764 mock.expect_execute_streaming_sql()
1766 .times(1)
1767 .in_sequence(&mut seq)
1768 .returning(|_| Err(Status::internal("Internal error second")));
1769
1770 let (db_client, _server) = setup_db_client(mock).await;
1771 let tx = db_client
1772 .read_only_transaction()
1773 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1774 .build()
1775 .await?;
1776
1777 let rs_result = tx
1778 .execute_query(Statement::builder("SELECT 1").build())
1779 .await;
1780
1781 assert!(
1782 rs_result.is_err(),
1783 "The failed execution bubbled upwards securely"
1784 );
1785 let err_str = rs_result.unwrap_err().to_string();
1786 assert!(
1787 err_str.contains("Internal error second"),
1788 "Secondary error message accurately propagates: {}",
1789 err_str
1790 );
1791
1792 Ok(())
1793 }
1794
1795 #[tokio_test_no_panics]
1796 async fn inline_begin_failure_fallback_rpc_fails() -> anyhow::Result<()> {
1797 use gaxi::grpc::tonic::Status;
1798
1799 let mut mock = create_session_mock();
1800 let mut seq = mockall::Sequence::new();
1801
1802 mock.expect_execute_streaming_sql()
1804 .times(1)
1805 .in_sequence(&mut seq)
1806 .returning(|_| Err(Status::internal("Internal error query")));
1807
1808 mock.expect_begin_transaction()
1810 .times(1)
1811 .in_sequence(&mut seq)
1812 .returning(|_| Err(Status::internal("Internal error begin tx")));
1813
1814 let (db_client, _server) = setup_db_client(mock).await;
1815 let tx = db_client
1816 .read_only_transaction()
1817 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1818 .build()
1819 .await?;
1820
1821 let rs_result = tx
1822 .execute_query(Statement::builder("SELECT 1").build())
1823 .await;
1824
1825 assert!(
1826 rs_result.is_err(),
1827 "The explicitly errored fallback boot securely propagated outwards"
1828 );
1829 let err_str = rs_result.unwrap_err().to_string();
1830 assert!(
1831 err_str.contains("Internal error begin tx"),
1832 "Natively propagated specific BeginTx bounds: {}",
1833 err_str
1834 );
1835
1836 Ok(())
1837 }
1838
1839 #[tokio_test_no_panics]
1840 async fn inline_begin_read_failure_retry_success() -> anyhow::Result<()> {
1841 use crate::key::KeySet;
1842 use crate::read::ReadRequest;
1843 use crate::value::Value;
1844 use gaxi::grpc::tonic::Status;
1845 use tonic::Response;
1846
1847 let mut mock = create_session_mock();
1848 let mut seq = mockall::Sequence::new();
1849
1850 mock.expect_streaming_read()
1852 .times(1)
1853 .in_sequence(&mut seq)
1854 .returning(|_| Err(Status::internal("Internal error")));
1855
1856 mock.expect_begin_transaction()
1858 .times(1)
1859 .in_sequence(&mut seq)
1860 .returning(|_| {
1861 Ok(Response::new(mock_v1::Transaction {
1862 id: vec![7, 8, 9],
1863 read_timestamp: None,
1864 ..Default::default()
1865 }))
1866 });
1867
1868 mock.expect_streaming_read()
1870 .times(1)
1871 .in_sequence(&mut seq)
1872 .returning(|req| {
1873 let req = req.into_inner();
1874 match req.transaction.unwrap().selector.unwrap() {
1876 mock_v1::transaction_selector::Selector::Id(id) => {
1877 assert_eq!(id, vec![7, 8, 9]);
1878 }
1879 _ => panic!("Expected Selector::Id"),
1880 }
1881 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
1882 setup_select1(),
1883 )])))
1884 });
1885
1886 let (db_client, _server) = setup_db_client(mock).await;
1887 let tx = db_client
1888 .read_only_transaction()
1889 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1890 .build()
1891 .await?;
1892
1893 let read = ReadRequest::builder("Users", vec!["Id", "Name"])
1894 .with_keys(KeySet::all())
1895 .build();
1896 let mut rs = tx.execute_read(read).await?;
1897
1898 let row = rs
1899 .next()
1900 .await
1901 .ok_or_else(|| anyhow::anyhow!("Expected a row uniquely returned"))??;
1902 assert_eq!(
1903 row.raw_values(),
1904 [Value(string_val("1"))],
1905 "The macro correctly unpacked read arrays seamlessly"
1906 );
1907
1908 Ok(())
1909 }
1910
1911 #[tokio_test_no_panics]
1912 async fn single_use_query_send_error_returns_immediately() -> anyhow::Result<()> {
1913 use crate::statement::Statement;
1914 use gaxi::grpc::tonic::Status;
1915
1916 let mut mock = create_session_mock();
1917
1918 mock.expect_execute_streaming_sql()
1919 .times(1)
1920 .returning(|_| Err(Status::internal("Internal error single use query")));
1921
1922 mock.expect_begin_transaction().never();
1923
1924 let (db_client, _server) = setup_db_client(mock).await;
1925 let tx = db_client.single_use().build();
1927
1928 let rs_result = tx
1929 .execute_query(Statement::builder("SELECT 1").build())
1930 .await;
1931
1932 assert!(rs_result.is_err());
1933 let err_str = rs_result.unwrap_err().to_string();
1934 assert!(err_str.contains("Internal error single use query"));
1935
1936 Ok(())
1937 }
1938
1939 #[tokio_test_no_panics]
1940 async fn inline_begin_already_started_query_send_error_returns_immediately()
1941 -> anyhow::Result<()> {
1942 use crate::statement::Statement;
1943 use gaxi::grpc::tonic::Status;
1944 use spanner_grpc_mock::google::spanner::v1 as mock_v1;
1945
1946 let mut mock = create_session_mock();
1947 let mut seq = mockall::Sequence::new();
1948
1949 mock.expect_begin_transaction().never();
1950
1951 mock.expect_execute_streaming_sql()
1953 .times(1)
1954 .in_sequence(&mut seq)
1955 .returning(move |_req| {
1956 let mut rs = setup_select1();
1957 rs.metadata.as_mut().unwrap().transaction = Some(mock_v1::Transaction {
1958 id: vec![4, 5, 6],
1959 read_timestamp: None,
1960 ..Default::default()
1961 });
1962 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(rs)])))
1963 });
1964
1965 mock.expect_execute_streaming_sql()
1967 .times(1)
1968 .in_sequence(&mut seq)
1969 .returning(|_| Err(Status::internal("Internal error second query")));
1970
1971 let (db_client, _server) = setup_db_client(mock).await;
1972
1973 let tx = db_client
1974 .read_only_transaction()
1975 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
1976 .build()
1977 .await?;
1978
1979 let mut rs = tx
1981 .execute_query(Statement::builder("SELECT 1").build())
1982 .await?;
1983 let _ = rs.next().await.expect("has row")?;
1984
1985 let rs_result = tx
1987 .execute_query(Statement::builder("SELECT 2").build())
1988 .await;
1989
1990 assert!(rs_result.is_err());
1991 let err_str = rs_result.unwrap_err().to_string();
1992 assert!(err_str.contains("Internal error second query"));
1993
1994 Ok(())
1995 }
1996
1997 #[tokio_test_no_panics]
1998 async fn execute_concurrent_queries_inline_begin() -> anyhow::Result<()> {
1999 let mut mock = create_session_mock();
2000 mock.expect_begin_transaction().never();
2001
2002 let mut seq = mockall::Sequence::new();
2003 let (tx_sender, rx_receiver) = mpsc::channel(1);
2004 let rx_receiver = Arc::new(Mutex::new(Some(rx_receiver)));
2005
2006 let task1_ready = Arc::new(Notify::new());
2007 let task1_ready_clone = Arc::clone(&task1_ready);
2008 let tasks_started = Arc::new(Barrier::new(3));
2009
2010 mock.expect_execute_streaming_sql()
2012 .times(1)
2013 .in_sequence(&mut seq)
2014 .returning(move |req| {
2015 task1_ready_clone.notify_one();
2016 let req = req.into_inner();
2017 match req.transaction.unwrap().selector.unwrap() {
2018 Selector::Begin(_) => {}
2019 _ => panic!("Expected Selector::Begin for first query"),
2020 }
2021 let rx = rx_receiver
2022 .try_lock()
2023 .expect("mutex poisoned")
2024 .take()
2025 .unwrap();
2026 Ok(Response::from(rx))
2027 });
2028
2029 mock.expect_execute_streaming_sql()
2031 .times(2)
2032 .in_sequence(&mut seq)
2033 .returning(move |req| {
2034 let req = req.into_inner();
2035 match req.transaction.unwrap().selector.unwrap() {
2036 Selector::Id(id) => {
2037 assert_eq!(id, vec![4, 5, 6]);
2038 }
2039 _ => panic!("Expected Selector::Id for other queries"),
2040 }
2041
2042 let (tx, rx) = mpsc::channel(1);
2043 tx.try_send(Ok(setup_select1()))
2044 .expect("send should succeed");
2045 Ok(Response::from(rx))
2046 });
2047
2048 let (db_client, _server) = setup_db_client(mock).await;
2049 let tx = db_client
2050 .read_only_transaction()
2051 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2052 .build()
2053 .await?;
2054 let tx = Arc::new(tx);
2055
2056 let tx1 = Arc::clone(&tx);
2059 let handle1 = tokio::spawn(async move {
2060 let mut rs = tx1
2061 .execute_query(Statement::builder("SELECT 1").build())
2062 .await?;
2063 let _ = rs.next().await;
2065 Ok::<_, crate::Error>(rs)
2066 });
2067
2068 task1_ready.notified().await;
2070
2071 let tx2 = Arc::clone(&tx);
2072 let tasks_started2 = Arc::clone(&tasks_started);
2073 let handle2 = tokio::spawn(async move {
2074 tasks_started2.wait().await;
2075 tx2.execute_query(Statement::builder("SELECT 1").build())
2076 .await
2077 });
2078
2079 let tx3 = Arc::clone(&tx);
2080 let tasks_started3 = Arc::clone(&tasks_started);
2081 let handle3 = tokio::spawn(async move {
2082 tasks_started3.wait().await;
2083 tx3.execute_query(Statement::builder("SELECT 1").build())
2084 .await
2085 });
2086
2087 tasks_started.wait().await;
2089
2090 tokio::task::yield_now().await;
2094
2095 let mut rs = setup_select1();
2098 rs.metadata
2099 .as_mut()
2100 .expect("metadata should be present")
2101 .transaction = Some(mock_v1::Transaction {
2102 id: vec![4, 5, 6],
2103 read_timestamp: Some(prost_types::Timestamp {
2104 seconds: 987654321,
2105 nanos: 0,
2106 }),
2107 ..Default::default()
2108 });
2109 tx_sender.send(Ok(rs)).await.expect("channel broken");
2110 drop(tx_sender);
2111
2112 let mut rs1 = handle1.await??;
2114 let mut rs2 = handle2.await??;
2115 let mut rs3 = handle3.await??;
2116
2117 assert!(rs1.next().await.is_none());
2119
2120 let row2 = rs2.next().await.expect("Expected a row")?;
2121 assert_eq!(row2.raw_values(), [Value(string_val("1"))]);
2122 assert!(rs2.next().await.is_none());
2123
2124 let row3 = rs3.next().await.expect("Expected a row")?;
2125 assert_eq!(row3.raw_values(), [Value(string_val("1"))]);
2126 assert!(rs3.next().await.is_none());
2127
2128 assert_eq!(
2130 tx.read_timestamp()
2131 .expect("read timestamp should be populated")
2132 .seconds(),
2133 987654321
2134 );
2135
2136 Ok(())
2137 }
2138
2139 #[tokio_test_no_panics]
2140 async fn execute_concurrent_queries_inline_begin_failed_cascade() -> anyhow::Result<()> {
2141 let mut mock = create_session_mock();
2142 let mut seq = mockall::Sequence::new();
2143
2144 let (tx_sender, rx_receiver) = mpsc::channel(1);
2145 let rx_receiver = Arc::new(Mutex::new(Some(rx_receiver)));
2146
2147 let task1_ready = Arc::new(Notify::new());
2148 let task1_ready_clone = Arc::clone(&task1_ready);
2149 let tasks_started = Arc::new(Barrier::new(3));
2150
2151 mock.expect_execute_streaming_sql()
2154 .times(1)
2155 .in_sequence(&mut seq)
2156 .returning(move |_req| {
2157 task1_ready_clone.notify_one();
2158 let rx = rx_receiver
2159 .try_lock()
2160 .expect("mutex poisoned")
2161 .take()
2162 .expect("receiver should be present");
2163 Ok(tonic::Response::from(rx))
2164 });
2165
2166 mock.expect_begin_transaction()
2168 .times(1)
2169 .in_sequence(&mut seq)
2170 .returning(|_| {
2171 Err(gaxi::grpc::tonic::Status::internal(
2172 "Fallback BeginTransaction failed",
2173 ))
2174 });
2175
2176 mock.expect_execute_streaming_sql().times(0).returning(|_| {
2178 panic!("Other queries should not launch after failure to start the transaction")
2179 });
2180
2181 let (db_client, _server) = setup_db_client(mock).await;
2182 let tx = db_client
2183 .read_only_transaction()
2184 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2185 .build()
2186 .await?;
2187 let tx = Arc::new(tx);
2188
2189 let tx1 = Arc::clone(&tx);
2191 let handle1 = tokio::spawn(async move {
2192 let mut rs = tx1
2193 .execute_query(Statement::builder("SELECT 1").build())
2194 .await?;
2195 rs.next().await.ok_or_else(|| {
2196 crate::error::internal_error("stream exhausted (this should never happen)")
2197 })??;
2198 Ok::<_, crate::Error>(rs)
2199 });
2200
2201 task1_ready.notified().await;
2203
2204 let tx2 = Arc::clone(&tx);
2205 let tasks_started2 = Arc::clone(&tasks_started);
2206 let handle2 = tokio::spawn(async move {
2207 tasks_started2.wait().await;
2208 tx2.execute_query(Statement::builder("SELECT 1").build())
2209 .await
2210 });
2211
2212 let tx3 = Arc::clone(&tx);
2213 let tasks_started3 = Arc::clone(&tasks_started);
2214 let handle3 = tokio::spawn(async move {
2215 tasks_started3.wait().await;
2216 tx3.execute_query(Statement::builder("SELECT 1").build())
2217 .await
2218 });
2219
2220 tasks_started.wait().await;
2222
2223 tokio::task::yield_now().await;
2227
2228 tx_sender
2230 .send(Err(gaxi::grpc::tonic::Status::internal(
2231 "Mocked boot failed",
2232 )))
2233 .await
2234 .expect("channel broken");
2235 drop(tx_sender);
2236
2237 let err1 = handle1
2239 .await?
2240 .expect_err("task 1 should have failed")
2241 .to_string();
2242 let err2 = handle2
2243 .await?
2244 .expect_err("task 2 should have failed")
2245 .to_string();
2246 let err3 = handle3
2247 .await?
2248 .expect_err("task 3 should have failed")
2249 .to_string();
2250
2251 assert!(
2252 err1.contains("Fallback BeginTransaction failed"),
2253 "err1: {}",
2254 err1
2255 );
2256 assert!(
2257 err2.contains("Fallback BeginTransaction failed"),
2258 "err2: {}",
2259 err2
2260 );
2261 assert!(
2262 err3.contains("Fallback BeginTransaction failed"),
2263 "err3: {}",
2264 err3
2265 );
2266
2267 Ok(())
2268 }
2269
2270 #[tokio_test_no_panics]
2271 async fn execute_concurrent_queries_inline_begin_stream_restart_deadlock_prevention()
2272 -> crate::Result<()> {
2273 let mut mock = create_session_mock();
2274 mock.expect_begin_transaction().never();
2275
2276 let mut seq = mockall::Sequence::new();
2277
2278 let (tx_sender, rx_receiver) = mpsc::channel(1);
2279 let rx_receiver = Arc::new(Mutex::new(Some(rx_receiver)));
2280
2281 let task1_ready = Arc::new(Notify::new());
2282 let task1_ready_clone = Arc::clone(&task1_ready);
2283 let tasks_started = Arc::new(Barrier::new(3));
2284
2285 mock.expect_execute_streaming_sql()
2287 .times(1)
2288 .in_sequence(&mut seq)
2289 .returning(move |req| {
2290 let req = req.into_inner();
2291 task1_ready_clone.notify_one();
2294 match req
2295 .transaction
2296 .expect("transaction should be present")
2297 .selector
2298 .expect("selector should be present")
2299 {
2300 Selector::Begin(_) => {}
2301 _ => panic!("Expected Selector::Begin for first query"),
2302 }
2303 let rx = rx_receiver
2304 .try_lock()
2305 .expect("mutex poisoned")
2306 .take()
2307 .expect("receiver should be present");
2308 Ok(Response::from(rx))
2309 });
2310
2311 mock.expect_execute_streaming_sql()
2314 .times(1)
2315 .in_sequence(&mut seq)
2316 .returning(move |req| {
2317 let req = req.into_inner();
2318 match req
2319 .transaction
2320 .expect("transaction should be present")
2321 .selector
2322 .expect("selector should be present")
2323 {
2324 Selector::Begin(_) => {
2325 let mut rs = setup_select1();
2326 rs.metadata
2327 .as_mut()
2328 .expect("metadata should be present")
2329 .transaction = Some(mock_v1::Transaction {
2330 id: vec![4, 5, 6],
2331 ..Default::default()
2332 });
2333 let (tx, rx) = mpsc::channel(1);
2334 tx.try_send(Ok(rs)).expect("send should succeed");
2335 Ok(Response::from(rx))
2336 }
2337 _ => panic!("Expected Selector::Begin for stream restart query"),
2338 }
2339 });
2340
2341 mock.expect_execute_streaming_sql()
2343 .times(2)
2344 .in_sequence(&mut seq)
2345 .returning(move |req| {
2346 let req = req.into_inner();
2347 match req
2348 .transaction
2349 .expect("transaction should be present")
2350 .selector
2351 .expect("selector should be present")
2352 {
2353 Selector::Id(id) => {
2354 assert_eq!(id, vec![4, 5, 6]);
2355 let (tx, rx) = mpsc::channel(1);
2356 tx.try_send(Ok(setup_select1()))
2357 .expect("send should succeed");
2358 Ok(Response::from(rx))
2359 }
2360 _ => panic!("Expected Selector::Id for concurrent queries"),
2361 }
2362 });
2363
2364 let (db_client, _server) = setup_db_client(mock).await;
2365 let tx = db_client
2366 .read_only_transaction()
2367 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2368 .build()
2369 .await?;
2370 let tx = Arc::new(tx);
2371
2372 let handle1_tx = Arc::clone(&tx);
2373 let handle1 = tokio::spawn(async move {
2374 let mut rs = handle1_tx
2375 .execute_query(Statement::builder("SELECT 1").build())
2376 .await?;
2377 let _ = rs.next().await.ok_or_else(|| {
2378 crate::error::internal_error("stream exhausted (this should never happen)")
2379 })??;
2380 Ok::<_, crate::Error>(rs)
2381 });
2382
2383 task1_ready.notified().await;
2385
2386 let handle2_tx = Arc::clone(&tx);
2387 let tasks_started2 = Arc::clone(&tasks_started);
2388 let handle2 = tokio::spawn(async move {
2389 tasks_started2.wait().await;
2390 let mut rs = handle2_tx
2391 .execute_query(Statement::builder("SELECT 1").build())
2392 .await?;
2393 let _ = rs.next().await.ok_or_else(|| {
2394 crate::error::internal_error("stream exhausted (this should never happen)")
2395 })??;
2396 Ok::<_, crate::Error>(rs)
2397 });
2398
2399 let handle3_tx = Arc::clone(&tx);
2400 let tasks_started3 = Arc::clone(&tasks_started);
2401 let handle3 = tokio::spawn(async move {
2402 tasks_started3.wait().await;
2403 let mut rs = handle3_tx
2404 .execute_query(Statement::builder("SELECT 1").build())
2405 .await?;
2406 let _ = rs.next().await.ok_or_else(|| {
2407 crate::error::internal_error("stream exhausted (this should never happen)")
2408 })??;
2409 Ok::<_, crate::Error>(rs)
2410 });
2411
2412 tasks_started.wait().await;
2414
2415 tokio::task::yield_now().await;
2419
2420 let grpc_status = Status::new(gaxi::grpc::tonic::Code::Unavailable, "transient error");
2421 tx_sender.send(Err(grpc_status)).await.expect("send failed");
2422 drop(tx_sender);
2423
2424 let mut rs1 = handle1.await.expect("Task 1 panicked")?;
2429 let mut rs2 = handle2.await.expect("Task 2 panicked")?;
2430 let mut rs3 = handle3.await.expect("Task 3 panicked")?;
2431
2432 assert!(rs1.next().await.is_none(), "Stream 1 should be exhausted");
2435 assert!(rs2.next().await.is_none(), "Stream 2 should be exhausted");
2436 assert!(rs3.next().await.is_none(), "Stream 3 should be exhausted");
2437
2438 Ok(())
2439 }
2440
2441 #[tokio_test_no_panics]
2442 async fn execute_concurrent_queries_late_arrival_failure() -> anyhow::Result<()> {
2443 let mut mock = create_session_mock();
2444 let mut seq = mockall::Sequence::new();
2445
2446 mock.expect_execute_streaming_sql()
2448 .times(1)
2449 .in_sequence(&mut seq)
2450 .returning(|req| {
2451 let req = req.into_inner();
2452 match req
2453 .transaction
2454 .expect("transaction should be present")
2455 .selector
2456 .expect("selector should be present")
2457 {
2458 Selector::Begin(_) => {}
2459 _ => panic!("Expected Selector::Begin for first query"),
2460 }
2461 Err(Status::internal("Initial inline-begin failed"))
2462 });
2463
2464 mock.expect_begin_transaction()
2466 .times(1)
2467 .in_sequence(&mut seq)
2468 .returning(|_| Err(Status::internal("Fallback BeginTransaction failed")));
2469
2470 mock.expect_execute_streaming_sql().never();
2472
2473 let (db_client, _server) = setup_db_client(mock).await;
2474 let tx = db_client
2475 .read_only_transaction()
2476 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2477 .build()
2478 .await?;
2479
2480 let err1 = tx
2482 .execute_query(Statement::builder("SELECT 1").build())
2483 .await
2484 .expect_err("First query should fail");
2485 assert!(
2486 err1.to_string()
2487 .contains("Fallback BeginTransaction failed")
2488 );
2489
2490 let err2 = tx
2493 .execute_query(Statement::builder("SELECT 1").build())
2494 .await
2495 .expect_err("Late query should fail immediately");
2496 assert!(
2497 err2.to_string()
2498 .contains("Fallback BeginTransaction failed")
2499 );
2500
2501 Ok(())
2502 }
2503
2504 #[tokio_test_no_panics]
2505 async fn execute_concurrent_reads_inline_begin() -> anyhow::Result<()> {
2506 use crate::key::KeySet;
2507 use crate::read::ReadRequest;
2508 let mut mock = create_session_mock();
2509 mock.expect_begin_transaction().never();
2510
2511 let mut seq = mockall::Sequence::new();
2512 let (tx_sender, rx_receiver) = mpsc::channel(1);
2513 let rx_receiver = Arc::new(Mutex::new(Some(rx_receiver)));
2514
2515 let task1_ready = Arc::new(Notify::new());
2516 let task1_ready_clone = Arc::clone(&task1_ready);
2517 let tasks_started = Arc::new(Barrier::new(3));
2518
2519 mock.expect_streaming_read()
2521 .times(1)
2522 .in_sequence(&mut seq)
2523 .returning(move |req| {
2524 task1_ready_clone.notify_one();
2525 let req = req.into_inner();
2526 match req
2527 .transaction
2528 .expect("transaction should be present")
2529 .selector
2530 .expect("selector should be present")
2531 {
2532 mock_v1::transaction_selector::Selector::Begin(_) => {}
2533 _ => panic!("Expected Selector::Begin for first read"),
2534 }
2535
2536 let rx = rx_receiver
2537 .try_lock()
2538 .expect("mutex poisoned")
2539 .take()
2540 .expect("receiver should be present");
2541 Ok(Response::from(rx))
2542 });
2543
2544 mock.expect_streaming_read()
2546 .times(2)
2547 .in_sequence(&mut seq)
2548 .returning(move |req| {
2549 let req = req.into_inner();
2550 match req
2551 .transaction
2552 .expect("transaction should be present")
2553 .selector
2554 .expect("selector should be present")
2555 {
2556 mock_v1::transaction_selector::Selector::Id(id) => {
2557 assert_eq!(id, vec![4, 5, 6]);
2558 }
2559 _ => panic!("Expected Selector::Id for other reads"),
2560 }
2561
2562 let (tx, rx) = mpsc::channel(1);
2563 tx.try_send(Ok(setup_select1()))
2564 .expect("send should succeed");
2565 Ok(Response::from(rx))
2566 });
2567
2568 let (db_client, _server) = setup_db_client(mock).await;
2569 let tx = db_client
2570 .read_only_transaction()
2571 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2572 .build()
2573 .await?;
2574 let tx = Arc::new(tx);
2575
2576 let read_req = ReadRequest::builder("Table", vec!["Col"])
2577 .with_keys(KeySet::all())
2578 .build();
2579
2580 let tx1 = Arc::clone(&tx);
2582 let read1 = read_req.clone();
2583 let handle1 = tokio::spawn(async move {
2584 let mut rs = tx1.execute_read(read1).await?;
2585 let _ = rs.next().await;
2586 Ok::<_, crate::Error>(rs)
2587 });
2588
2589 task1_ready.notified().await;
2590
2591 let tx2 = Arc::clone(&tx);
2592 let read2 = read_req.clone();
2593 let tasks_started2 = Arc::clone(&tasks_started);
2594 let handle2 = tokio::spawn(async move {
2595 tasks_started2.wait().await;
2596 let mut rs = tx2.execute_read(read2).await?;
2597 let _ = rs.next().await;
2598 Ok::<_, crate::Error>(rs)
2599 });
2600
2601 let tx3 = Arc::clone(&tx);
2602 let read3 = read_req.clone();
2603 let tasks_started3 = Arc::clone(&tasks_started);
2604 let handle3 = tokio::spawn(async move {
2605 tasks_started3.wait().await;
2606 let mut rs = tx3.execute_read(read3).await?;
2607 let _ = rs.next().await;
2608 Ok::<_, crate::Error>(rs)
2609 });
2610
2611 tasks_started.wait().await;
2612 tokio::task::yield_now().await;
2613
2614 let mut rs = setup_select1();
2616 rs.metadata
2617 .as_mut()
2618 .expect("metadata should be present")
2619 .transaction = Some(mock_v1::Transaction {
2620 id: vec![4, 5, 6],
2621 ..Default::default()
2622 });
2623 tx_sender.send(Ok(rs)).await.expect("send failed");
2624 drop(tx_sender);
2625
2626 let mut rs1 = handle1.await.expect("Task 1 panicked")?;
2627 let mut rs2 = handle2.await.expect("Task 2 panicked")?;
2628 let mut rs3 = handle3.await.expect("Task 3 panicked")?;
2629
2630 assert!(rs1.next().await.is_none());
2631 assert!(rs2.next().await.is_none());
2632 assert!(rs3.next().await.is_none());
2633
2634 Ok(())
2635 }
2636
2637 #[tokio_test_no_panics]
2638 async fn execute_inline_begin_idempotent_update() -> anyhow::Result<()> {
2639 let (db_client, _server) = setup_db_client(create_session_mock()).await;
2640 let tx = db_client
2642 .read_only_transaction()
2643 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2644 .build()
2645 .await?;
2646
2647 let id1 = bytes::Bytes::from_static(b"tx1");
2648 let id2 = bytes::Bytes::from_static(b"tx2");
2649
2650 tx.context.transaction_selector.update(id1.clone(), None)?;
2652 assert_eq!(
2653 tx.context
2654 .transaction_selector
2655 .selector()
2656 .await?
2657 .id()
2658 .expect("ID should be present"),
2659 &id1
2660 );
2661
2662 tx.context.transaction_selector.update(id1.clone(), None)?;
2666
2667 let err2 = tx
2669 .context
2670 .transaction_selector
2671 .update(id2, None)
2672 .expect_err("Update after Started should fail");
2673 assert!(err2.to_string().contains("already Started or Failed"));
2674
2675 Ok(())
2676 }
2677
2678 #[tokio_test_no_panics]
2679 async fn execute_inline_begin_with_transient_failure() -> anyhow::Result<()> {
2680 let mut mock = create_session_mock();
2681 let mut seq = mockall::Sequence::new();
2682
2683 mock.expect_execute_streaming_sql()
2685 .times(1)
2686 .in_sequence(&mut seq)
2687 .returning(|_| Err(Status::new(Code::Unavailable, "Transient 1")));
2688
2689 mock.expect_begin_transaction()
2691 .times(1)
2692 .in_sequence(&mut seq)
2693 .returning(|_| {
2694 Ok(Response::new(mock_v1::Transaction {
2695 id: vec![7, 8, 9],
2696 ..Default::default()
2697 }))
2698 });
2699
2700 mock.expect_execute_streaming_sql()
2702 .times(1)
2703 .in_sequence(&mut seq)
2704 .returning(|_| {
2705 let (tx, rx) = mpsc::channel(1);
2706 tx.try_send(Ok(setup_select1()))
2707 .expect("send should succeed");
2708 Ok(Response::from(rx))
2709 });
2710
2711 let (db_client, _server) = setup_db_client(mock).await;
2712 let tx = db_client
2713 .read_only_transaction()
2714 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2715 .build()
2716 .await?;
2717
2718 let mut rs = tx
2719 .execute_query(Statement::builder("SELECT 1").build())
2720 .await?;
2721 assert!(rs.next().await.is_some());
2722 assert!(rs.next().await.is_none());
2723
2724 Ok(())
2725 }
2726
2727 #[tokio_test_no_panics]
2728 async fn leader_aware_routing_query_in_read_only() -> anyhow::Result<()> {
2729 let mut mock = create_session_mock();
2730 mock.expect_execute_streaming_sql().once().returning(|req| {
2731 assert!(
2732 req.metadata()
2733 .get("x-goog-spanner-route-to-leader")
2734 .is_none()
2735 );
2736 let stream = adapt([Ok(mock_v1::PartialResultSet {
2737 metadata: Some(mock_v1::ResultSetMetadata {
2738 row_type: Some(mock_v1::StructType { fields: vec![] }),
2739 ..Default::default()
2740 }),
2741 ..Default::default()
2742 })]);
2743 Ok(tonic::Response::from(stream))
2744 });
2745
2746 let (db_client, _server) = setup_db_client(mock).await;
2747 let tx = db_client.single_use().build();
2748 let _rs = tx
2749 .execute_query(Statement::builder("SELECT 1").build())
2750 .await?;
2751 Ok(())
2752 }
2753
2754 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
2755 async fn execute_concurrent_begin_explicitly_redundancy_prevention() -> anyhow::Result<()> {
2756 let (tx_rpc, rx_rpc) = std_channel();
2757 let (tx_started, rx_started) = oneshot_channel();
2758 let tx_started_mutex = StdMutex::new(Some(tx_started));
2759
2760 let mut mock = create_session_mock();
2761 let mut seq = mockall::Sequence::new();
2762
2763 mock.expect_execute_streaming_sql()
2765 .once()
2766 .in_sequence(&mut seq)
2767 .returning(move |_req| {
2768 if let Some(tx) = tx_started_mutex.lock().expect("mutex poisoned").take() {
2769 let _ = tx.send(());
2770 }
2771 rx_rpc.recv().expect("channel broken");
2772 let (tx, rx) = mpsc::channel(1);
2773 let metadata = mock_v1::ResultSetMetadata {
2774 transaction: Some(mock_v1::Transaction {
2775 id: vec![42],
2776 ..Default::default()
2777 }),
2778 ..Default::default()
2779 };
2780 let prs = mock_v1::PartialResultSet {
2781 metadata: Some(metadata),
2782 ..Default::default()
2783 };
2784 tx.try_send(Ok(prs)).expect("send should succeed");
2785 Ok(tonic::Response::new(rx))
2786 });
2787
2788 mock.expect_execute_streaming_sql()
2791 .once()
2792 .in_sequence(&mut seq)
2793 .returning(move |req| {
2794 let req = req.into_inner();
2795 assert_eq!(
2796 req.transaction,
2797 Some(mock_v1::TransactionSelector {
2798 selector: Some(mock_v1::transaction_selector::Selector::Id(vec![42])),
2799 })
2800 );
2801 let (tx, rx) = mpsc::channel(1);
2802 let metadata = mock_v1::ResultSetMetadata {
2803 row_type: Some(mock_v1::StructType { fields: vec![] }),
2804 ..Default::default()
2805 };
2806 let prs = mock_v1::PartialResultSet {
2807 metadata: Some(metadata),
2808 ..Default::default()
2809 };
2810 tx.try_send(Ok(prs)).expect("send should succeed");
2811 Ok(tonic::Response::new(rx))
2812 });
2813
2814 let (db_client, _server) = setup_db_client(mock).await;
2815
2816 let tx = Arc::new(
2817 db_client
2818 .read_only_transaction()
2819 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
2820 .build()
2821 .await?,
2822 );
2823
2824 let tx_leader = Arc::clone(&tx);
2825 let handle_leader = tokio::spawn(async move {
2826 let mut rs = tx_leader
2827 .execute_query(Statement::builder("SELECT 1").build())
2828 .await?;
2829 let _ = rs.next().await;
2830 Ok::<_, crate::Error>(())
2831 });
2832
2833 rx_started.await.expect("oneshot broken");
2834
2835 let tx_follower = Arc::clone(&tx);
2838 let handle_follower = tokio::spawn(async move {
2839 let mut rs = tx_follower
2840 .execute_query(Statement::builder("SELECT 2").build())
2841 .await?;
2842 let _ = rs.next().await;
2843 Ok::<_, crate::Error>(())
2844 });
2845
2846 tx_rpc.send(()).expect("send failed");
2848
2849 handle_leader.await.expect("Task 1 panicked")?;
2850 handle_follower.await.expect("Task 2 panicked")?;
2851
2852 Ok(())
2853 }
2854
2855 #[tokio_test_no_panics]
2856 async fn execute_multi_query_redundant_transaction_id_explicit() -> anyhow::Result<()> {
2857 run_execute_multi_query_redundant_transaction_id(BeginTransactionOption::ExplicitBegin)
2858 .await
2859 }
2860
2861 #[tokio_test_no_panics]
2862 async fn execute_multi_query_redundant_transaction_id_inline() -> anyhow::Result<()> {
2863 run_execute_multi_query_redundant_transaction_id(BeginTransactionOption::InlineBegin).await
2864 }
2865
2866 async fn run_execute_multi_query_redundant_transaction_id(
2867 option: BeginTransactionOption,
2868 ) -> anyhow::Result<()> {
2869 let mut mock = create_session_mock();
2870 let mut sequence = mockall::Sequence::new();
2871
2872 if option == BeginTransactionOption::ExplicitBegin {
2873 mock.expect_begin_transaction()
2874 .once()
2875 .in_sequence(&mut sequence)
2876 .returning(|req| {
2877 let req = req.into_inner();
2878 assert_eq!(
2879 req.session,
2880 "projects/p/instances/i/databases/d/sessions/123"
2881 );
2882 Ok(tonic::Response::new(mock_v1::Transaction {
2883 id: vec![4, 5, 6],
2884 read_timestamp: Some(prost_types::Timestamp {
2885 seconds: 123456789,
2886 nanos: 0,
2887 }),
2888 ..Default::default()
2889 }))
2890 });
2891
2892 mock.expect_execute_streaming_sql()
2893 .times(2)
2894 .returning(|req| {
2895 let req = req.into_inner();
2896 assert_eq!(
2897 req.transaction
2898 .expect("transaction should be present")
2899 .selector
2900 .expect("selector should be present"),
2901 mock_v1::transaction_selector::Selector::Id(vec![4, 5, 6])
2902 );
2903
2904 let mut result_set_partial = setup_select1();
2905 result_set_partial
2906 .metadata
2907 .as_mut()
2908 .expect("metadata should be present")
2909 .transaction = Some(mock_v1::Transaction {
2910 id: vec![4, 5, 6],
2911 read_timestamp: Some(prost_types::Timestamp {
2912 seconds: 123456789,
2913 nanos: 0,
2914 }),
2915 ..Default::default()
2916 });
2917 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
2918 result_set_partial,
2919 )])))
2920 });
2921 } else {
2922 mock.expect_begin_transaction().never();
2923
2924 mock.expect_execute_streaming_sql()
2925 .times(1)
2926 .in_sequence(&mut sequence)
2927 .returning(|req| {
2928 let req = req.into_inner();
2929 assert_eq!(
2930 req.session,
2931 "projects/p/instances/i/databases/d/sessions/123"
2932 );
2933
2934 match req
2935 .transaction
2936 .expect("transaction should be present")
2937 .selector
2938 .expect("selector should be present")
2939 {
2940 mock_v1::transaction_selector::Selector::Begin(_) => {}
2941 _ => panic!("Expected Selector::Begin"),
2942 }
2943 let mut result_set_partial = setup_select1();
2944 result_set_partial
2945 .metadata
2946 .as_mut()
2947 .expect("metadata should be present")
2948 .transaction = Some(mock_v1::Transaction {
2949 id: vec![4, 5, 6],
2950 read_timestamp: Some(prost_types::Timestamp {
2951 seconds: 987654321,
2952 nanos: 0,
2953 }),
2954 ..Default::default()
2955 });
2956 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
2957 result_set_partial,
2958 )])))
2959 });
2960
2961 mock.expect_execute_streaming_sql()
2962 .times(1)
2963 .in_sequence(&mut sequence)
2964 .returning(|req| {
2965 let req = req.into_inner();
2966 match req
2967 .transaction
2968 .expect("transaction should be present")
2969 .selector
2970 .expect("selector should be present")
2971 {
2972 mock_v1::transaction_selector::Selector::Id(id) => {
2973 assert_eq!(id, vec![4, 5, 6]);
2974 }
2975 _ => panic!("Expected Selector::Id"),
2976 }
2977 let mut result_set_partial = setup_select1();
2978 result_set_partial
2979 .metadata
2980 .as_mut()
2981 .expect("metadata should be present")
2982 .transaction = Some(mock_v1::Transaction {
2983 id: vec![4, 5, 6],
2984 read_timestamp: Some(prost_types::Timestamp {
2985 seconds: 987654321,
2986 nanos: 0,
2987 }),
2988 ..Default::default()
2989 });
2990 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
2991 result_set_partial,
2992 )])))
2993 });
2994 }
2995
2996 let (db_client, _server) = setup_db_client(mock).await;
2997
2998 let transaction = db_client
2999 .read_only_transaction()
3000 .with_begin_transaction_option(option)
3001 .build()
3002 .await
3003 .expect("Failed to start transaction");
3004
3005 for _ in 0..2 {
3006 let mut result_set = transaction
3007 .execute_query(Statement::builder("SELECT 1").build())
3008 .await
3009 .expect("Failed to execute query");
3010
3011 let row = result_set
3012 .next()
3013 .await
3014 .expect("has row")
3015 .expect("has valid row");
3016 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
3017
3018 let next_result = result_set.next().await;
3019 assert!(next_result.is_none(), "expected None, got {next_result:?}");
3020 }
3021
3022 Ok(())
3023 }
3024
3025 #[tokio_test_no_panics]
3026 async fn read_only_transaction_begin_with_never_retry() -> anyhow::Result<()> {
3027 let mut mock = MockSpanner::new();
3028 let mut sequence = mockall::Sequence::new();
3029
3030 mock.expect_begin_transaction()
3031 .once()
3032 .in_sequence(&mut sequence)
3033 .returning(|_| Err(tonic::Status::unavailable("transient error")));
3034
3035 mock.expect_create_session().returning(|_| {
3036 Ok(Response::new(mock_v1::Session {
3037 name: "session".to_string(),
3038 multiplexed: true,
3039 ..Default::default()
3040 }))
3041 });
3042
3043 let (db_client, _server) = setup_db_client(mock).await;
3044
3045 let res = db_client
3046 .read_only_transaction()
3047 .with_begin_transaction_option(BeginTransactionOption::ExplicitBegin)
3048 .with_begin_retry_policy(NeverRetry)
3049 .build()
3050 .await;
3051
3052 assert!(res.is_err(), "should fail immediately without retry");
3053 let err = res.unwrap_err();
3054 assert_eq!(err.status().expect("status").code, GaxCode::Unavailable);
3055
3056 Ok(())
3057 }
3058
3059 #[tokio_test_no_panics]
3060 async fn read_only_transaction_lazy_begin_fallback_never_retry() -> anyhow::Result<()> {
3061 let mut mock = MockSpanner::new();
3062 let mut sequence = mockall::Sequence::new();
3063
3064 mock.expect_execute_streaming_sql()
3066 .once()
3067 .in_sequence(&mut sequence)
3068 .returning(|_| Err(tonic::Status::unavailable("transient error")));
3069
3070 mock.expect_begin_transaction()
3072 .once()
3073 .in_sequence(&mut sequence)
3074 .returning(|_| Err(tonic::Status::unavailable("transient error")));
3075
3076 mock.expect_create_session().returning(|_| {
3077 Ok(Response::new(mock_v1::Session {
3078 name: "session".to_string(),
3079 multiplexed: true,
3080 ..Default::default()
3081 }))
3082 });
3083
3084 let (db_client, _server) = setup_db_client(mock).await;
3085
3086 let transaction = db_client
3087 .read_only_transaction()
3088 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
3089 .with_begin_retry_policy(NeverRetry)
3090 .build()
3091 .await?;
3092
3093 let stmt = Statement::builder("SELECT 1").build();
3094 let res = transaction.execute_query(stmt).await;
3095
3096 assert!(
3097 res.is_err(),
3098 "should fail immediately during fallback without retrying the fallback RPC"
3099 );
3100 let err = res.unwrap_err();
3101 assert_eq!(err.status().expect("status").code, GaxCode::Unavailable);
3102
3103 Ok(())
3104 }
3105
3106 #[tokio_test_no_panics]
3107 async fn read_only_transaction_begin_with_attempt_timeout() -> anyhow::Result<()> {
3108 let mut mock = MockSpanner::new();
3109 let mut sequence = mockall::Sequence::new();
3110
3111 mock.expect_begin_transaction()
3112 .once()
3113 .in_sequence(&mut sequence)
3114 .withf(|req| {
3115 let timeout_header = req.metadata().get("grpc-timeout");
3116 assert!(
3117 timeout_header.is_some(),
3118 "grpc-timeout header should be present"
3119 );
3120 let val = timeout_header.unwrap().to_str().unwrap();
3121 assert!(
3122 val.contains("5000") || val.contains("5"),
3123 "timeout header value '{}' should represent 5 seconds",
3124 val
3125 );
3126 true
3127 })
3128 .returning(|_| {
3129 Ok(Response::new(mock_v1::Transaction {
3130 id: vec![42],
3131 ..Default::default()
3132 }))
3133 });
3134
3135 mock.expect_create_session().returning(|_| {
3136 Ok(Response::new(mock_v1::Session {
3137 name: "session".to_string(),
3138 multiplexed: true,
3139 ..Default::default()
3140 }))
3141 });
3142
3143 let (db_client, _server) = setup_db_client(mock).await;
3144
3145 let _transaction = db_client
3146 .read_only_transaction()
3147 .with_begin_transaction_option(BeginTransactionOption::ExplicitBegin)
3148 .with_begin_attempt_timeout(std::time::Duration::from_secs(5))
3149 .build()
3150 .await?;
3151
3152 Ok(())
3153 }
3154
3155 #[tokio_test_no_panics]
3156 async fn read_only_transaction_builder_sets_gax_options() -> anyhow::Result<()> {
3157 let mut mock = MockSpanner::new();
3158 mock.expect_create_session().returning(|_| {
3159 Ok(Response::new(mock_v1::Session {
3160 name: "session".to_string(),
3161 multiplexed: true,
3162 ..Default::default()
3163 }))
3164 });
3165 let (db_client, _server) = setup_db_client(mock).await;
3166
3167 let builder = db_client
3168 .read_only_transaction()
3169 .with_begin_attempt_timeout(Duration::from_secs(5))
3170 .with_begin_retry_policy(NeverRetry)
3171 .with_begin_backoff_policy(ExponentialBackoff::default());
3172
3173 let gax = builder
3174 .begin_gax_options
3175 .as_ref()
3176 .expect("begin_gax_options missing");
3177 assert_eq!(*gax.attempt_timeout(), Some(Duration::from_secs(5)));
3178 assert!(gax.retry_policy().is_some());
3179 assert!(gax.backoff_policy().is_some());
3180
3181 Ok(())
3182 }
3183
3184 #[tokio_test_no_panics]
3185 async fn read_only_transaction_lazy_begin_fallback_uses_statement_options_when_unconfigured()
3186 -> anyhow::Result<()> {
3187 let mut mock = MockSpanner::new();
3188 let mut sequence = mockall::Sequence::new();
3189
3190 mock.expect_execute_streaming_sql()
3192 .once()
3193 .in_sequence(&mut sequence)
3194 .returning(|_| Err(tonic::Status::unavailable("transient error")));
3195
3196 mock.expect_begin_transaction()
3199 .once()
3200 .in_sequence(&mut sequence)
3201 .withf(|req| {
3202 let timeout_header = req.metadata().get("grpc-timeout");
3203 assert!(
3204 timeout_header.is_some(),
3205 "grpc-timeout header should be present"
3206 );
3207 let val = timeout_header.unwrap().to_str().unwrap();
3208 assert!(
3209 val.contains("5000") || val.contains("5"),
3210 "timeout header value '{}' should represent 5 seconds",
3211 val
3212 );
3213 true
3214 })
3215 .returning(|_| {
3216 Ok(Response::new(mock_v1::Transaction {
3217 id: vec![42],
3218 ..Default::default()
3219 }))
3220 });
3221
3222 mock.expect_execute_streaming_sql()
3224 .once()
3225 .in_sequence(&mut sequence)
3226 .withf(|req| {
3227 matches!(
3228 req.get_ref()
3229 .transaction
3230 .as_ref()
3231 .and_then(|t| t.selector.as_ref()),
3232 Some(mock_v1::transaction_selector::Selector::Id(id)) if id == &vec![42]
3233 )
3234 })
3235 .returning(|_| {
3236 let mut result_set_partial = setup_select1();
3237 result_set_partial
3238 .metadata
3239 .as_mut()
3240 .expect("metadata should be present")
3241 .transaction = Some(mock_v1::Transaction {
3242 id: vec![42],
3243 ..Default::default()
3244 });
3245 Ok(gaxi::grpc::tonic::Response::from(adapt([Ok(
3246 result_set_partial,
3247 )])))
3248 });
3249
3250 mock.expect_create_session().returning(|_| {
3251 Ok(Response::new(mock_v1::Session {
3252 name: "session".to_string(),
3253 multiplexed: true,
3254 ..Default::default()
3255 }))
3256 });
3257
3258 let (db_client, _server) = setup_db_client(mock).await;
3259
3260 let transaction = db_client
3261 .read_only_transaction()
3262 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
3263 .build()
3264 .await?;
3265
3266 let mut stmt_opts = crate::RequestOptions::default();
3267 stmt_opts.set_attempt_timeout(Duration::from_secs(5));
3268 let stmt = Statement::builder("SELECT 1")
3269 .build()
3270 .with_gax_options(stmt_opts);
3271
3272 let mut rs = transaction.execute_query(stmt).await?;
3273 let row = rs.next().await.expect("has row")?;
3274 assert_eq!(row.raw_values(), [Value(string_val("1"))]);
3275
3276 Ok(())
3277 }
3278
3279 #[tokio_test_no_panics]
3280 async fn read_only_transaction_lazy_begin_fallback_merges_custom_options() -> anyhow::Result<()>
3281 {
3282 let mut mock = MockSpanner::new();
3283 let mut sequence = mockall::Sequence::new();
3284
3285 mock.expect_execute_streaming_sql()
3287 .once()
3288 .in_sequence(&mut sequence)
3289 .returning(|_| Err(tonic::Status::unavailable("transient error")));
3290
3291 mock.expect_begin_transaction()
3296 .once()
3297 .in_sequence(&mut sequence)
3298 .withf(|req| {
3299 let timeout_header = req.metadata().get("grpc-timeout");
3300 assert!(
3301 timeout_header.is_some(),
3302 "grpc-timeout header should be present"
3303 );
3304 let val = timeout_header.unwrap().to_str().unwrap();
3305 assert!(
3306 val.contains("5000") || val.contains("5"),
3307 "timeout header value '{}' should represent 5 seconds",
3308 val
3309 );
3310 true
3311 })
3312 .returning(|_| Err(tonic::Status::unavailable("transient error")));
3313
3314 mock.expect_create_session().returning(|_| {
3315 Ok(Response::new(mock_v1::Session {
3316 name: "session".to_string(),
3317 multiplexed: true,
3318 ..Default::default()
3319 }))
3320 });
3321
3322 let (db_client, _server) = setup_db_client(mock).await;
3323
3324 let transaction = db_client
3325 .read_only_transaction()
3326 .with_begin_transaction_option(BeginTransactionOption::InlineBegin)
3327 .with_begin_retry_policy(NeverRetry)
3328 .build()
3329 .await?;
3330
3331 let mut stmt_opts = crate::RequestOptions::default();
3332 stmt_opts.set_attempt_timeout(Duration::from_secs(5));
3333 let stmt = Statement::builder("SELECT 1")
3334 .build()
3335 .with_gax_options(stmt_opts);
3336
3337 let res = transaction.execute_query(stmt).await;
3338
3339 assert!(
3340 res.is_err(),
3341 "should fail immediately because of NeverRetry"
3342 );
3343 let err = res.unwrap_err();
3344 assert_eq!(err.status().expect("status").code, GaxCode::Unavailable);
3345
3346 Ok(())
3347 }
3348
3349 #[test]
3350 fn test_merge_request_options() {
3351 let mut dest = crate::RequestOptions::default();
3353 dest.set_attempt_timeout(Duration::from_secs(2));
3354 dest.set_retry_policy(NeverRetry);
3355
3356 let merged = merge_request_options(dest, None);
3358
3359 assert_eq!(*merged.attempt_timeout(), Some(Duration::from_secs(2)));
3360 assert!(merged.retry_policy().is_some());
3361
3362 let dest = crate::RequestOptions::default();
3364
3365 let mut source = crate::RequestOptions::default();
3366 source.set_attempt_timeout(Duration::from_secs(5));
3367 source.set_retry_policy(NeverRetry);
3368
3369 let merged = merge_request_options(dest, Some(&source));
3370
3371 assert_eq!(*merged.attempt_timeout(), Some(Duration::from_secs(5)));
3372 assert!(merged.retry_policy().is_some());
3373
3374 let mut dest = crate::RequestOptions::default();
3376 let mut dest_headers = HeaderMap::new();
3377 dest_headers.insert(
3378 HeaderName::from_static("x-goog-spanner-route-to-leader"),
3379 HeaderValue::from_static("true"),
3380 );
3381 dest = dest.insert_extension(dest_headers);
3382
3383 let mut source = crate::RequestOptions::default();
3384 let mut src_headers = HeaderMap::new();
3385 src_headers.insert(
3386 HeaderName::from_static("x-custom-header"),
3387 HeaderValue::from_static("custom-value"),
3388 );
3389 source = source.insert_extension(src_headers);
3390
3391 let merged = merge_request_options(dest, Some(&source));
3392 let merged_headers = merged
3393 .get_extension::<HeaderMap>()
3394 .expect("HeaderMap missing");
3395
3396 assert_eq!(
3397 merged_headers
3398 .get("x-goog-spanner-route-to-leader")
3399 .unwrap()
3400 .to_str()
3401 .unwrap(),
3402 "true"
3403 );
3404 assert_eq!(
3405 merged_headers
3406 .get("x-custom-header")
3407 .unwrap()
3408 .to_str()
3409 .unwrap(),
3410 "custom-value"
3411 );
3412 }
3413
3414 #[test]
3415 fn test_transaction_selector_check_failed_propagates_original_error() {
3416 let options = TransactionOptions {
3417 mode: Some(Mode::ReadWrite(Default::default())),
3418 ..Default::default()
3419 };
3420 let selector = ReadContextTransactionSelector::Lazy(Arc::new(StdMutex::new(
3421 TransactionState::Starting(options, Arc::new(Notify::new())),
3422 )));
3423
3424 let initial_err = crate::error::internal_error("initial statement error");
3425 selector.set_failed(&initial_err);
3426
3427 assert!(selector.is_first_statement_failed());
3428
3429 let res = selector.check_failed();
3430 assert!(res.is_err());
3431 let err = res.unwrap_err();
3432 assert!(
3433 err.to_string()
3434 .contains("Aborted due to failed initial statement")
3435 );
3436 }
3437
3438 #[tokio_test_no_panics]
3439 async fn test_subsequent_query_failure_does_not_lock_mutex() -> anyhow::Result<()> {
3440 let mut mock = create_session_mock();
3441
3442 mock.expect_execute_streaming_sql()
3444 .once()
3445 .returning(|_| Err(tonic::Status::new(tonic::Code::Internal, "query failed")));
3446
3447 let (db_client, _server) = setup_db_client(mock).await;
3448
3449 let mut transaction_selector = crate::model::TransactionSelector::default();
3451 transaction_selector.selector = Some(crate::model::transaction_selector::Selector::Id(
3452 bytes::Bytes::copy_from_slice(&[1, 2, 3]),
3453 ));
3454
3455 let state = Arc::new(StdMutex::new(TransactionState::Started(
3456 transaction_selector.clone(),
3457 None,
3458 )));
3459 let selector = ReadContextTransactionSelector::Lazy(state.clone());
3460
3461 let context = ReadContext {
3462 session_name: "projects/p/instances/i/databases/d/sessions/123".to_string(),
3463 client: db_client,
3464 transaction_selector: selector,
3465 precommit_token_tracker: crate::read_only_transaction::PrecommitTokenTracker::new(),
3466 transaction_tag: None,
3467 channel_hint: 0,
3468 begin_transaction_request_options: None,
3469 };
3470
3471 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3473 let _guard = state.lock().unwrap();
3474 panic!("poisoning the mutex intentionally");
3475 }));
3476
3477 let statement = Statement::builder("SELECT 1").build();
3479 let request = statement
3480 .into_request()
3481 .set_session(context.session_name.clone())
3482 .set_transaction(transaction_selector);
3483
3484 let gax_options = google_cloud_gax::options::RequestOptions::default();
3485
3486 async fn run_macro(
3487 context: &ReadContext,
3488 mut request: crate::model::ExecuteSqlRequest,
3489 gax_options: google_cloud_gax::options::RequestOptions,
3490 ) -> crate::Result<ResultSet> {
3491 execute_stream_with_retry!(
3492 context,
3493 request,
3494 gax_options,
3495 execute_streaming_sql,
3496 StreamOperation::Query
3497 )
3498 }
3499
3500 let res = run_macro(&context, request, gax_options).await;
3501
3502 assert!(res.is_err());
3504 let err = res.unwrap_err();
3505 assert_eq!(err.status().map(|s| s.code), Some(GaxCode::Internal));
3506 assert_eq!(
3507 err.status().map(|s| s.message.as_str()),
3508 Some("query failed")
3509 );
3510
3511 Ok(())
3512 }
3513}