Skip to content

Comments

fix(jdbc): various perf improvements#4114

Open
logachev wants to merge 1 commit intomainfrom
kirl/minor_perf_improvements
Open

fix(jdbc): various perf improvements#4114
logachev wants to merge 1 commit intomainfrom
kirl/minor_perf_improvements

Conversation

@logachev
Copy link
Contributor

Various small-ish changes that affect startup or query performance.

  1. (Major) Fixed Stateless Queries. Our code was using deprecated field setQueryPreviewEnabled instead of setDefaultJobCreationMode, so stateless queries were not used.
  2. (Minor) Surprisingly BigQueryOptions.getDefaultProjectId() is visible in JFR profiling because it calls metadata server if env are not present. I think it is responsible for ~0.2s on startup even when projectId is available, so added lazy defaults for that property.
  3. (Minor) p12 key is unlikely to be used a lot, but our code during init always tried it first; Moved it to be last option.

@logachev logachev requested review from a team as code owners February 24, 2026 04:26
@product-auto-label product-auto-label bot added size: m Pull request size is medium. api: bigquery Issues related to the googleapis/java-bigquery API. labels Feb 24, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @logachev, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces several performance improvements to the BigQuery JDBC driver. The primary focus is on optimizing startup and query execution by fixing an issue with stateless query configuration, deferring the loading of the default project ID, and re-prioritizing private key parsing to reduce overhead for less common key types. These changes aim to enhance the overall efficiency and responsiveness of the driver.

Highlights

  • Stateless Queries Fix: Corrected the configuration for stateless queries by switching from the deprecated setQueryPreviewEnabled to setDefaultJobCreationMode, ensuring stateless queries are properly utilized.
  • Lazy Project ID Loading: Implemented lazy loading for BigQueryOptions.getDefaultProjectId() to prevent unnecessary calls to the metadata server during startup, which was identified as a performance bottleneck.
  • P12 Key Priority Adjustment: Reordered the private key loading mechanism to prioritize JSON and PKCS8 formats over P12 keys, moving P12 to the last option as it is less frequently used.
Changelog
  • google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java
    • Fixed stateless query mode by using setDefaultJobCreationMode instead of setQueryPreviewEnabled.
    • Implemented lazy loading for BigQueryOptions.getDefaultProjectId() to improve startup performance.
  • google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionProperty.java
    • Added support for lazy default values using Supplier<String>.
  • google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtility.java
    • Changed isJson method to accept byte[] for more flexible input.
    • Reordered private key loading preference to check JSON and PKCS8 formats before P12.
    • Updated P12 private key loading to work with byte arrays directly.
  • google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java
    • Introduced parseStringPropertyLazyDefault for properties with lazily evaluated defaults.
    • Applied lazy default evaluation to the PROJECT_ID_PROPERTY_NAME property.
  • google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java
    • Added new tests for JobCreationMode to ensure correct configuration.
  • google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtilityTest.java
    • Updated existing P12 private key tests to align with the new privateKeyFromP12Bytes method.
Activity
  • No specific activity (comments, reviews, progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several performance improvements, including a fix for stateless queries, lazy loading for the default project ID to improve startup time, and reordering of private key parsing logic. However, a critical security vulnerability was identified in the new key parsing logic. The switch from using an input stream to Files.readAllBytes() for reading key files specified in the connection string introduces a risk of Denial of Service (DoS) through memory exhaustion if a large file is specified, and potentially exposes the system to NetNTLM hash leakage on Windows via UNC paths. Furthermore, this new key parsing logic could also affect authentication using file-based PKCS8 keys.

Comment on lines +375 to 381
if (isJson(keyBytes)) {
stream = new ByteArrayInputStream(keyBytes);
} else if (pvtKey != null) {
key = privateKeyFromPkcs8(pvtKey);
} else {
key = privateKeyFromP12Bytes(keyBytes, p12Password);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The updated logic for parsing private keys has a flaw. It doesn't correctly handle PKCS8 keys provided via a file path (OAuthPvtKeyPath or OAuthPvtKey as a path).

  1. If OAuthPvtKeyPath is used for a PKCS8 key file, pvtKey will be null, causing the else if (pvtKey != null) branch to be skipped and the code will incorrectly try to parse the key as P12 format.
  2. If OAuthPvtKey contains a path to a PKCS8 key file, privateKeyFromPkcs8(pvtKey) will be called with the file path string instead of its content, which will fail.

This breaks authentication for these scenarios. The logic should attempt to parse the loaded keyBytes in the desired order (JSON -> PKCS8 -> P12), regardless of whether the key came from a string or a file.

Here is a suggested fix that correctly sequences the parsing attempts on the keyBytes.

      if (isJson(keyBytes)) {
        stream = new ByteArrayInputStream(keyBytes);
      } else if (keyBytes != null) {
        // Try to parse as PKCS8 private key
        key = privateKeyFromPkcs8(new String(keyBytes, java.nio.charset.StandardCharsets.UTF_8));
        if (key == null) {
          // If not PKCS8, try to parse as P12
          key = privateKeyFromP12Bytes(keyBytes, p12Password);
        }
      }

}
} else if (isJson(pvtKey)) {
stream = new ByteArrayInputStream(pvtKey.getBytes());
keyBytes = Files.readAllBytes(Paths.get(keyPath));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The use of Files.readAllBytes() to read a key file specified in the connection string can lead to a Denial of Service (DoS) via an OutOfMemoryError. An attacker who can control the connection string (e.g., in applications that allow users to provide their own JDBC URL) can point the OAuthPvtKeyPath or OAuthPvtKey property to a very large file (such as /dev/zero or a large log file) on the server. This will cause the application to attempt to read the entire file into memory, potentially crashing the JVM. Additionally, on Windows systems, providing a UNC path (e.g., \\attacker-ip\share\file) could lead to an information disclosure by leaking the Windows user's NetNTLM hash when the driver attempts to access the remote share.

      if (isFileExists(keyPath)) {
        try (InputStream is = Files.newInputStream(Paths.get(keyPath))) {
          // Limit the read size to a reasonable maximum for a key file (e.g., 1MB)
          byte[] buffer = new byte[1024 * 1024];
          int bytesRead = is.read(buffer);
          if (bytesRead != -1) {
            keyBytes = java.util.Arrays.copyOf(buffer, bytesRead);
          }
        }
      }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: bigquery Issues related to the googleapis/java-bigquery API. size: m Pull request size is medium.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant