-
Notifications
You must be signed in to change notification settings - Fork 130
feat(bigquery-v2): add lro polling support for bq jobs - WIP - prototype #5835
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
7e6d1e0
0d8dfa3
df73ab8
ef97166
9afc758
b9da40f
ebd966a
bda56d9
807fac4
c51d6ac
7ccc707
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| // Copyright 2025 Google LLC | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // https://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| use crate::builder::job_service::{GetQueryResults, InsertJob}; | ||
| use crate::client::JobService; | ||
| use crate::model::{GetQueryResultsResponse, Job}; | ||
| use google_cloud_lro::Poller; | ||
| use google_cloud_lro::internal::DiscoveryOperation; | ||
|
|
||
| impl DiscoveryOperation for Job { | ||
| fn done(&self) -> bool { | ||
| self.status | ||
| .as_ref() | ||
| .map(|s| s.state.as_str()) | ||
| .unwrap_or_default() | ||
| == "DONE" | ||
| } | ||
|
|
||
| fn name(&self) -> Option<&String> { | ||
| self.job_reference.as_ref().map(|r| &r.job_id) | ||
| } | ||
| } | ||
|
|
||
| impl DiscoveryOperation for GetQueryResultsResponse { | ||
| fn done(&self) -> bool { | ||
| self.job_complete == Some(true) | ||
| } | ||
|
|
||
| fn name(&self) -> Option<&String> { | ||
| self.job_reference.as_ref().map(|r| &r.job_id) | ||
| } | ||
| } | ||
|
|
||
| /// Extension trait for [`InsertJob`] to support Long-Running Operation (LRO) polling. | ||
| /// | ||
| /// # Example | ||
| /// ```no_run | ||
| /// use google_cloud_bigquery_v2::client::JobService; | ||
| /// use google_cloud_bigquery_v2::model::{Job, JobConfiguration, JobConfigurationQuery}; | ||
| /// use google_cloud_bigquery_v2::operation::InsertJobBuilderExt; | ||
| /// use google_cloud_lro::Poller; | ||
| /// | ||
| /// async fn example(client: JobService, project_id: &str) -> Result<(), google_cloud_gax::error::Error> { | ||
| /// let mut poller = client | ||
| /// .insert_job() | ||
| /// .set_project_id(project_id) | ||
| /// .set_job( | ||
| /// Job::new().set_configuration( | ||
| /// JobConfiguration::new().set_query( | ||
| /// JobConfigurationQuery::new().set_query("SELECT 1") | ||
| /// ) | ||
| /// ) | ||
| /// ) | ||
| /// .poller(&client, project_id, None); | ||
| /// | ||
| /// // Wait for the job to complete | ||
| /// let job = poller.until_done().await?; | ||
| /// | ||
| /// // Check for BigQuery specific errors inside the payload | ||
| /// if let Some(status) = job.status { | ||
| /// if let Some(err) = status.error_result { | ||
| /// println!("Job failed with: {}", err.message); | ||
| /// } | ||
| /// } | ||
| /// Ok(()) | ||
| /// } | ||
| /// ``` | ||
| pub trait InsertJobBuilderExt { | ||
| /// Returns a poller to monitor the status of the inserted job. | ||
| fn poller( | ||
| self, | ||
| client: &JobService, | ||
| project_id: impl Into<String>, | ||
| location: Option<String>, | ||
| ) -> impl Poller<Job, Job>; | ||
| } | ||
|
haphungw marked this conversation as resolved.
|
||
|
|
||
| impl InsertJobBuilderExt for InsertJob { | ||
| fn poller( | ||
| self, | ||
| client: &JobService, | ||
| project_id: impl Into<String>, | ||
| location: Option<String>, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as mentioned on another comments, I think accepting those parameters here again sounds like duplication, since the get_job() call accepts a |
||
| ) -> impl Poller<Job, Job> { | ||
| let client_clone = client.clone(); | ||
| let project_id = project_id.into(); | ||
|
|
||
| let start = move || { | ||
| let req = self; | ||
| async move { req.send().await } | ||
| }; | ||
|
|
||
| let query = move |name: String| { | ||
| let client = client_clone.clone(); | ||
| let project_id = project_id.clone(); | ||
| let location = location.clone(); | ||
| async move { | ||
| let mut b = client.get_job().set_project_id(project_id).set_job_id(name); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is using |
||
| if let Some(loc) = location { | ||
| b = b.set_location(loc); | ||
| } | ||
| let mut options = google_cloud_gax::options::RequestOptions::default(); | ||
| options.set_retry_policy(google_cloud_gax::retry_policy::NeverRetry); | ||
| b.with_options(options).send().await | ||
| } | ||
| }; | ||
|
|
||
| let polling_error_policy = | ||
| std::sync::Arc::new(google_cloud_gax::polling_error_policy::Aip194Strict); | ||
| let polling_backoff_policy = std::sync::Arc::new( | ||
| google_cloud_gax::exponential_backoff::ExponentialBackoff::default(), | ||
| ); | ||
|
|
||
| google_cloud_lro::internal::new_discovery_poller( | ||
| polling_error_policy, | ||
| polling_backoff_policy, | ||
| start, | ||
| query, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /// Extension trait for [`GetQueryResults`] to support Long-Running Operation (LRO) polling. | ||
| /// | ||
| /// # Example | ||
| /// ```no_run | ||
| /// use google_cloud_bigquery_v2::client::JobService; | ||
| /// use google_cloud_bigquery_v2::operation::GetQueryResultsBuilderExt; | ||
| /// use google_cloud_lro::Poller; | ||
| /// | ||
| /// async fn example(client: JobService, project_id: &str, job_id: &str) -> Result<(), google_cloud_gax::error::Error> { | ||
| /// let mut poller = client | ||
| /// .get_query_results() | ||
| /// .set_project_id(project_id) | ||
| /// .set_job_id(job_id) | ||
| /// .poller(&client, project_id, None); | ||
| /// | ||
| /// let response = poller.until_done().await?; | ||
| /// | ||
| /// if response.job_complete.unwrap_or(false) { | ||
| /// println!("Query finished! Received {} rows", response.total_rows.unwrap_or_default()); | ||
| /// } | ||
| /// Ok(()) | ||
| /// } | ||
| /// ``` | ||
| pub trait GetQueryResultsBuilderExt { | ||
| /// Returns a poller to monitor the status of the query results. | ||
| fn poller( | ||
| self, | ||
| client: &JobService, | ||
| project_id: impl Into<String>, | ||
| location: Option<String>, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as mentioned on another comments, I think accepting those parameters here again sounds like duplication, since the get_query_results() call accepts a |
||
| ) -> impl Poller<GetQueryResultsResponse, GetQueryResultsResponse>; | ||
| } | ||
|
|
||
| impl GetQueryResultsBuilderExt for GetQueryResults { | ||
| fn poller( | ||
| self, | ||
| client: &JobService, | ||
| project_id: impl Into<String>, | ||
| location: Option<String>, | ||
| ) -> impl Poller<GetQueryResultsResponse, GetQueryResultsResponse> { | ||
| let client_clone = client.clone(); | ||
| let project_id = project_id.into(); | ||
|
|
||
| let start = move || { | ||
| let req = self; | ||
| async move { req.send().await } | ||
| }; | ||
|
|
||
| let query = move |name: String| { | ||
| let client = client_clone.clone(); | ||
| let project_id = project_id.clone(); | ||
| let location = location.clone(); | ||
| async move { | ||
| let mut b = client | ||
| .get_query_results() | ||
| .set_project_id(project_id) | ||
| .set_job_id(name); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should pass maxResults=0 when using getQueryResults to wait for a query to finish. For large rows, it can add many seconds to the request time, even when no rows are actually returned. |
||
| if let Some(loc) = location { | ||
| b = b.set_location(loc); | ||
| } | ||
| let mut options = google_cloud_gax::options::RequestOptions::default(); | ||
| options.set_retry_policy(google_cloud_gax::retry_policy::NeverRetry); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to have a way to pass a retry policy, because we can actually need to retry in some scenarios (see https://docs.cloud.google.com/bigquery/docs/error-messages) and even crazier, in some cases we only detect that the job has to be retried by pooling it. In some cases we need to re send the query (call jobs.query or jobs.insert again) with a different ID (see jobBackendError at https://docs.cloud.google.com/bigquery/docs/error-messages). We don't need to solve this right away, just pointing out that we need to take into account that we do need to customize retry policies here. |
||
| b.with_options(options).send().await | ||
| } | ||
| }; | ||
|
|
||
| let polling_error_policy = | ||
| std::sync::Arc::new(google_cloud_gax::polling_error_policy::Aip194Strict); | ||
| let polling_backoff_policy = std::sync::Arc::new( | ||
| google_cloud_gax::exponential_backoff::ExponentialBackoff::default(), | ||
| ); | ||
|
|
||
| google_cloud_lro::internal::new_discovery_poller( | ||
| polling_error_policy, | ||
| polling_backoff_policy, | ||
| start, | ||
| query, | ||
| ) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.