Skip to content

Commit 15ad9d1

Browse files
authored
feat(csharp/src/Drivers/BigQuery): add additional billing and timeout properties and test settings (apache#2566)
- adds the `adbc.bigquery.client.timeout` value for the BigQueryClient - adds the `adbc.bigquery.billing_project_id` value for the billing queries against - adds support for referencing shared values in multi-environment test configurations - adds tests for the new settings --------- Co-authored-by: David Coe <>
1 parent 05985a8 commit 15ad9d1

File tree

11 files changed

+198
-45
lines changed

11 files changed

+198
-45
lines changed

csharp/src/Drivers/BigQuery/BigQueryConnection.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,11 @@ public BigQueryConnection(IReadOnlyDictionary<string, string> properties)
7373
internal BigQueryClient Open()
7474
{
7575
string? projectId = null;
76+
string? billingProjectId = null;
7677
string? clientId = null;
7778
string? clientSecret = null;
7879
string? refreshToken = null;
80+
TimeSpan? clientTimeout = null;
7981

8082
string tokenEndpoint = BigQueryConstants.TokenEndpoint;
8183

@@ -87,6 +89,9 @@ internal BigQueryClient Open()
8789
if (!this.properties.TryGetValue(BigQueryParameters.ProjectId, out projectId))
8890
projectId = BigQueryConstants.DetectProjectId;
8991

92+
// the billing project can be null if it's not specified
93+
this.properties.TryGetValue(BigQueryParameters.BillingProjectId, out billingProjectId);
94+
9095
if (this.properties.TryGetValue(BigQueryParameters.IncludePublicProjectId, out string? result))
9196
{
9297
if (!string.IsNullOrEmpty(result))
@@ -128,7 +133,26 @@ internal BigQueryClient Open()
128133
this.credential = ApplyScopes(GoogleCredential.FromJson(json));
129134
}
130135

131-
BigQueryClient client = BigQueryClient.Create(projectId, this.credential);
136+
if (this.properties.TryGetValue(BigQueryParameters.ClientTimeout, out string? timeoutSeconds) &&
137+
int.TryParse(timeoutSeconds, out int seconds))
138+
{
139+
clientTimeout = TimeSpan.FromSeconds(seconds);
140+
}
141+
142+
BigQueryClientBuilder bigQueryClientBuilder = new BigQueryClientBuilder()
143+
{
144+
ProjectId = projectId,
145+
QuotaProject = billingProjectId,
146+
GoogleCredential = this.credential
147+
};
148+
149+
BigQueryClient client = bigQueryClientBuilder.Build();
150+
151+
if (clientTimeout.HasValue)
152+
{
153+
client.Service.HttpClient.Timeout = clientTimeout.Value;
154+
}
155+
132156
this.client = client;
133157
return client;
134158
}
@@ -1033,7 +1057,7 @@ private IReadOnlyDictionary<string, string> ParseOptions()
10331057
BigQueryParameters.UseLegacySQL,
10341058
BigQueryParameters.LargeDecimalsAsString,
10351059
BigQueryParameters.LargeResultsDestinationTable,
1036-
BigQueryParameters.GetQueryResultsOptionsTimeoutMinutes,
1060+
BigQueryParameters.GetQueryResultsOptionsTimeout,
10371061
BigQueryParameters.MaxFetchConcurrency
10381062
};
10391063

csharp/src/Drivers/BigQuery/BigQueryParameters.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace Apache.Arrow.Adbc.Drivers.BigQuery
2323
public class BigQueryParameters
2424
{
2525
public const string ProjectId = "adbc.bigquery.project_id";
26+
public const string BillingProjectId = "adbc.bigquery.billing_project_id";
2627
public const string ClientId = "adbc.bigquery.client_id";
2728
public const string ClientSecret = "adbc.bigquery.client_secret";
2829
public const string RefreshToken = "adbc.bigquery.refresh_token";
@@ -34,7 +35,8 @@ public class BigQueryParameters
3435
public const string LargeDecimalsAsString = "adbc.bigquery.large_decimals_as_string";
3536
public const string Scopes = "adbc.bigquery.scopes";
3637
public const string IncludeConstraintsWithGetObjects = "adbc.bigquery.include_constraints_getobjects";
37-
public const string GetQueryResultsOptionsTimeoutMinutes = "adbc.bigquery.get_query_results_options.timeout";
38+
public const string ClientTimeout = "adbc.bigquery.client.timeout";
39+
public const string GetQueryResultsOptionsTimeout = "adbc.bigquery.get_query_results_options.timeout";
3840
public const string MaxFetchConcurrency = "adbc.bigquery.max_fetch_concurrency";
3941
public const string IncludePublicProjectId = "adbc.bigquery.include_public_project_id";
4042
}

csharp/src/Drivers/BigQuery/BigQueryStatement.cs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,11 @@ public override QueryResult ExecuteQuery()
5757

5858
GetQueryResultsOptions getQueryResultsOptions = new GetQueryResultsOptions();
5959

60-
if (this.Options?.TryGetValue(BigQueryParameters.GetQueryResultsOptionsTimeoutMinutes, out string? timeoutMinutes) == true)
60+
if (this.Options?.TryGetValue(BigQueryParameters.GetQueryResultsOptionsTimeout, out string? timeoutSeconds) == true &&
61+
int.TryParse(timeoutSeconds, out int seconds) &&
62+
seconds >= 0)
6163
{
62-
if (int.TryParse(timeoutMinutes, out int minutes))
63-
{
64-
if (minutes >= 0)
65-
{
66-
getQueryResultsOptions.Timeout = TimeSpan.FromMinutes(minutes);
67-
}
68-
}
64+
getQueryResultsOptions.Timeout = TimeSpan.FromSeconds(seconds);
6965
}
7066

7167
BigQueryResults results = job.GetQueryResults(getQueryResultsOptions);
@@ -126,15 +122,11 @@ public override UpdateResult ExecuteUpdate()
126122
QueryOptions options = ValidateOptions();
127123
GetQueryResultsOptions getQueryResultsOptions = new GetQueryResultsOptions();
128124

129-
if (this.Options?.TryGetValue(BigQueryParameters.GetQueryResultsOptionsTimeoutMinutes, out string? timeoutMinutes) == true)
125+
if (this.Options?.TryGetValue(BigQueryParameters.GetQueryResultsOptionsTimeout, out string? timeoutSeconds) == true &&
126+
int.TryParse(timeoutSeconds, out int seconds) &&
127+
seconds >= 0)
130128
{
131-
if (int.TryParse(timeoutMinutes, out int minutes))
132-
{
133-
if (minutes >= 0)
134-
{
135-
getQueryResultsOptions.Timeout = TimeSpan.FromMinutes(minutes);
136-
}
137-
}
129+
getQueryResultsOptions.Timeout = TimeSpan.FromSeconds(seconds);
138130
}
139131

140132
BigQueryResults result = this.client.ExecuteQuery(

csharp/src/Drivers/BigQuery/readme.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,23 @@ The following parameters can be used to configure the driver behavior. The param
4242

4343
https://cloud.google.com/dotnet/docs/reference/Google.Cloud.BigQuery.V2/latest/Google.Cloud.BigQuery.V2.QueryOptions#Google_Cloud_BigQuery_V2_QueryOptions_AllowLargeResults
4444

45+
**adbc.bigquery.billing_project_id**<br>
46+
&nbsp;&nbsp;&nbsp;&nbsp;The [Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects) used for accessing billing BigQuery. If not specified, will default to the detected project ID.
47+
4548
**adbc.bigquery.client_id**<br>
4649
&nbsp;&nbsp;&nbsp;&nbsp;The OAuth client ID. Required for `user` authentication.
4750

4851
**adbc.bigquery.client_secret**<br>
4952
&nbsp;&nbsp;&nbsp;&nbsp;The OAuth client secret. Required for `user` authentication.
5053

54+
**adbc.bigquery.client.timeout**<br>
55+
&nbsp;&nbsp;&nbsp;&nbsp;Optional. Sets the timeout (in seconds) for the BigQueryClient. Similar to a ConnectionTimeout.
56+
5157
**adbc.bigquery.auth_json_credential**<br>
5258
&nbsp;&nbsp;&nbsp;&nbsp;Required if using `service` authentication. This value is passed to the [GoogleCredential.FromJson](https://cloud.google.com/dotnet/docs/reference/Google.Apis/latest/Google.Apis.Auth.OAuth2.GoogleCredential#Google_Apis_Auth_OAuth2_GoogleCredential_FromJson_System_String) method.
5359

5460
**adbc.bigquery.get_query_results_options.timeout**<br>
55-
&nbsp;&nbsp;&nbsp;&nbsp;Optional. Sets the timeout (in minutes) for the GetQueryResultsOptions value. If not set, defaults to 5 minutes.
61+
&nbsp;&nbsp;&nbsp;&nbsp;Optional. Sets the timeout (in seconds) for the GetQueryResultsOptions value. If not set, defaults to 5 minutes. Similar to a CommandTimeout.
5662

5763
**adbc.bigquery.max_fetch_concurrency**<br>
5864
&nbsp;&nbsp;&nbsp;&nbsp;Optional. Sets the [maxStreamCount](https://cloud.google.com/dotnet/docs/reference/Google.Cloud.BigQuery.Storage.V1/latest/Google.Cloud.BigQuery.Storage.V1.BigQueryReadClient#Google_Cloud_BigQuery_Storage_V1_BigQueryReadClient_CreateReadSession_System_String_Google_Cloud_BigQuery_Storage_V1_ReadSession_System_Int32_Google_Api_Gax_Grpc_CallSettings_) for the CreateReadSession method. If not set, defaults to 1.

csharp/test/Apache.Arrow.Adbc.Tests/MultiEnvironmentTestConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18+
using System;
1819
using System.Collections.Generic;
20+
using System.Text.Json;
1921
using System.Text.Json.Serialization;
2022

2123
namespace Apache.Arrow.Adbc.Tests
@@ -42,5 +44,11 @@ public abstract class MultiEnvironmentTestConfiguration<T>
4244
/// </summary>
4345
[JsonPropertyName("environments")]
4446
public Dictionary<string, T> Environments { get; set; } = new Dictionary<string, T>();
47+
48+
/// <summary>
49+
/// Values that are shared across environments so they don't need to be repeated.
50+
/// </summary>
51+
[JsonPropertyName("shared")]
52+
public Dictionary<string, string> SharedKeyValuePairs { get; set; } = new Dictionary<string, string>();
4553
}
4654
}

csharp/test/Apache.Arrow.Adbc.Tests/MultiEnvironmentTestUtils.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System;
1919
using System.Collections.Generic;
2020
using System.IO;
21+
using System.Reflection;
2122
using System.Text.Json;
2223

2324
namespace Apache.Arrow.Adbc.Tests
@@ -38,7 +39,6 @@ public static T LoadMultiEnvironmentTestConfiguration<T>(string environmentVaria
3839
{
3940
// use a JSON file for the various settings
4041
string json = File.ReadAllText(environmentValue);
41-
4242
testConfiguration = JsonSerializer.Deserialize<T>(json)!;
4343
}
4444
}
@@ -60,17 +60,34 @@ public static List<TEnvironment> GetTestEnvironments<TEnvironment>(MultiEnvironm
6060
throw new InvalidOperationException("There are no environments configured");
6161

6262
List<TEnvironment> environments = new List<TEnvironment>();
63+
string term = "$ref:shared.";
6364

6465
foreach (string environmentName in GetEnvironmentNames(testConfiguration.TestEnvironmentNames))
6566
{
66-
if (testConfiguration.Environments.TryGetValue(environmentName, out TEnvironment? testEnvironment))
67+
if (!testConfiguration.Environments.TryGetValue(environmentName, out TEnvironment? testEnvironment) || testEnvironment is null)
68+
continue;
69+
70+
testEnvironment.Name = environmentName;
71+
72+
if (testConfiguration.SharedKeyValuePairs.Count > 0)
6773
{
68-
if (testEnvironment != null)
74+
foreach (PropertyInfo pi in testEnvironment.GetType().GetProperties())
6975
{
70-
testEnvironment.Name = environmentName;
71-
environments.Add(testEnvironment);
76+
if (pi.PropertyType == typeof(string) &&
77+
pi.GetValue(testEnvironment) is string propertyValue &&
78+
propertyValue.StartsWith(term, StringComparison.Ordinal))
79+
{
80+
string lookupKey = propertyValue.AsSpan(term.Length).ToString();
81+
82+
if (testConfiguration.SharedKeyValuePairs.TryGetValue(lookupKey, out string? sharedValue))
83+
{
84+
pi.SetValue(testEnvironment, sharedValue);
85+
}
86+
}
7287
}
7388
}
89+
90+
environments.Add(testEnvironment);
7491
}
7592

7693
if (environments.Count == 0)

csharp/test/Drivers/BigQuery/BigQueryTestConfiguration.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
using System.Collections.Generic;
1918
using System.Text.Json.Serialization;
2019

2120
namespace Apache.Arrow.Adbc.Tests.Drivers.BigQuery
@@ -41,6 +40,9 @@ public BigQueryTestEnvironment()
4140
[JsonPropertyName("projectId")]
4241
public string? ProjectId { get; set; }
4342

43+
[JsonPropertyName("billingProjectId")]
44+
public string? BillingProjectId { get; set; }
45+
4446
[JsonPropertyName("clientId")]
4547
public string ClientId { get; set; } = string.Empty;
4648

@@ -68,9 +70,30 @@ public BigQueryTestEnvironment()
6870
[JsonPropertyName("includePublicProjectId")]
6971
public bool IncludePublicProjectId { get; set; } = false;
7072

73+
/// <summary>
74+
/// Sets the query timeout (in minutes).
75+
/// </summary>
7176
[JsonPropertyName("timeoutMinutes")]
7277
public int? TimeoutMinutes { get; set; }
7378

79+
/// <summary>
80+
/// Sets the query timeout (in seconds).
81+
/// </summary>
82+
[JsonPropertyName("queryTimeout")]
83+
public int? QueryTimeout { get; set; }
84+
85+
/// <summary>
86+
/// The number of seconds to allow for the HttpClient timeout.
87+
/// </summary>
88+
[JsonPropertyName("clientTimeout")]
89+
public int? ClientTimeout { get; set; }
90+
91+
/// <summary>
92+
/// Indicates if timeout tests should run during this execution.
93+
/// </summary>
94+
[JsonPropertyName("runTimeoutTests")]
95+
public bool RunTimeoutTests { get; set; } = false;
96+
7497
[JsonPropertyName("maxStreamCount")]
7598
public int? MaxStreamCount { get; set; }
7699
}

csharp/test/Drivers/BigQuery/BigQueryTestingUtils.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ internal static Dictionary<string, string> GetBigQueryParameters(BigQueryTestEnv
5858
parameters.Add(BigQueryParameters.ProjectId, testEnvironment.ProjectId!);
5959
}
6060

61+
if (!string.IsNullOrEmpty(testEnvironment.BillingProjectId))
62+
{
63+
parameters.Add(BigQueryParameters.BillingProjectId, testEnvironment.BillingProjectId!);
64+
}
65+
6166
if (!string.IsNullOrEmpty(testEnvironment.JsonCredential))
6267
{
6368
parameters.Add(BigQueryParameters.AuthenticationType, BigQueryConstants.ServiceAccountAuthenticationType);
@@ -92,7 +97,17 @@ internal static Dictionary<string, string> GetBigQueryParameters(BigQueryTestEnv
9297

9398
if (testEnvironment.TimeoutMinutes.HasValue)
9499
{
95-
parameters.Add(BigQueryParameters.GetQueryResultsOptionsTimeoutMinutes, testEnvironment.TimeoutMinutes.Value.ToString());
100+
int seconds = testEnvironment.TimeoutMinutes.Value * 60;
101+
parameters.Add(BigQueryParameters.GetQueryResultsOptionsTimeout, seconds.ToString());
102+
}
103+
else if (testEnvironment.QueryTimeout.HasValue)
104+
{
105+
parameters.Add(BigQueryParameters.GetQueryResultsOptionsTimeout, testEnvironment.QueryTimeout.Value.ToString());
106+
}
107+
108+
if (testEnvironment.ClientTimeout.HasValue)
109+
{
110+
parameters.Add(BigQueryParameters.ClientTimeout, testEnvironment.ClientTimeout.Value.ToString());
96111
}
97112

98113
if (testEnvironment.MaxStreamCount.HasValue)

csharp/test/Drivers/BigQuery/DriverTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System;
1919
using System.Collections.Generic;
2020
using System.Linq;
21+
using System.Threading.Tasks;
2122
using Apache.Arrow.Adbc.Drivers.BigQuery;
2223
using Apache.Arrow.Adbc.Tests.Metadata;
2324
using Apache.Arrow.Adbc.Tests.Xunit;
@@ -307,5 +308,43 @@ private AdbcConnection GetAdbcConnection(string? environmentName)
307308

308309
return _configuredConnections[environmentName!];
309310
}
311+
312+
/// <summary>
313+
/// Validates the ClientTimeout parameter.
314+
/// </summary>
315+
[SkippableFact, Order(7)]
316+
public void ClientTimeoutTest()
317+
{
318+
foreach (BigQueryTestEnvironment environment in _environments)
319+
{
320+
if (environment.RunTimeoutTests && environment.ClientTimeout.HasValue)
321+
{
322+
AdbcConnection adbcConnection = BigQueryTestingUtils.GetBigQueryAdbcConnection(environment);
323+
324+
AdbcStatement statement = adbcConnection.CreateStatement();
325+
statement.SqlQuery = environment.Query;
326+
327+
Assert.Throws<TaskCanceledException>(() => { statement.ExecuteQuery(); });
328+
}
329+
}
330+
}
331+
332+
/// <summary>
333+
/// Validates the GetQueryResultsOptionsTimeoutMinutes parameter.
334+
/// </summary>
335+
[SkippableFact, Order(8)]
336+
public void QueryTimeoutTest()
337+
{
338+
foreach (BigQueryTestEnvironment environment in _environments)
339+
{
340+
if (environment.RunTimeoutTests && (environment.QueryTimeout.HasValue || environment.TimeoutMinutes.HasValue))
341+
{
342+
AdbcConnection adbcConnection = BigQueryTestingUtils.GetBigQueryAdbcConnection(environment);
343+
AdbcStatement statement = adbcConnection.CreateStatement();
344+
statement.SqlQuery = environment.Query;
345+
Assert.Throws<TimeoutException>(() => { statement.ExecuteQuery(); });
346+
}
347+
}
348+
}
310349
}
311350
}

csharp/test/Drivers/BigQuery/Resources/bigqueryconfig.json

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
{
22
"testEnvironments": [ "", "" ],
3+
"shared": {
4+
"clientId": "clientId_example",
5+
"clientSecret": "clientSecret_example"
6+
}
37
"environments": {
48
"<env1>": {
59
"projectId": "",
6-
"clientId": "",
7-
"clientSecret": "",
10+
"clientId": "$ref:shared.clientId",
11+
"clientSecret": "$ref:shared.clientSecret",
812
"refreshToken": "",
913
"maxStreamCount": 1,
1014
"metadata": {
@@ -17,6 +21,21 @@
1721
"expectedResults": 0
1822
},
1923
"<env2>": {
24+
"projectId": "",
25+
"clientId": "$ref:shared.clientId",
26+
"clientSecret": "$ref:shared.clientSecret",
27+
"refreshToken": "",
28+
"maxStreamCount": 1,
29+
"metadata": {
30+
"catalog": "",
31+
"schema": "",
32+
"table": "",
33+
"expectedColumnCount": 0
34+
},
35+
"query": "",
36+
"expectedResults": 0
37+
},
38+
"<env3>": {
2039
"projectId": "",
2140
"clientId": "",
2241
"clientSecret": "",

0 commit comments

Comments
 (0)