Skip to content

Conversation

@olavloite
Copy link
Contributor

Adds a method to execute DDL on Spanner without having the client awaiting the long-running operation to finish. This makes it possible to for example start the creation of a secondary index using the client library, without having the library waiting for that operation to finish. Creating a secondary index on a table with a large amount of data can take a very long time (several days).

Adds a method to execute DDL on Spanner without having the client awaiting the
long-running operation to finish. This makes it possible to for example start
the creation of a secondary index using the client library, without having the
library waiting for that operation to finish. Creating a secondary index on a
table with a large amount of data can take a very long time (several days).
@olavloite olavloite requested a review from a team as a code owner November 28, 2025 16:22
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @olavloite, 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 a significant enhancement to the Spanner client library by providing the ability to execute Data Definition Language (DDL) statements without blocking the client until the operation completes. This is particularly useful for long-running DDL tasks, such as creating secondary indexes on large tables, which can take a considerable amount of time. Users can now initiate these operations and receive a Google.LongRunning.Operation object to track their progress independently, offering greater flexibility and responsiveness in applications.

Highlights

  • Non-awaiting DDL Execution: New methods ExecuteDdl and ExecuteDdlAsync have been added to SpannerCommand, allowing DDL operations to be initiated without the client waiting for their completion. This is particularly beneficial for long-running DDL tasks.
  • Long-Running Operation (LRO) Tracking: The new DDL execution methods return a Google.LongRunning.Operation object. This enables users to asynchronously monitor the progress and status of DDL operations, such as creating large secondary indexes, which can take extended periods.
  • Backward Compatibility: The existing ExecuteNonQueryAsync method for DDL commands has been updated internally to utilize the new non-awaiting mechanism but still waits for completion by default. This ensures that existing codebases remain functional without requiring immediate changes.
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 a valuable feature for executing Spanner DDL statements without awaiting the completion of the long-running operation. The implementation correctly adds new ExecuteDdl and ExecuteDdlAsync methods and refactors the internal DDL execution logic to support this. My review identifies one issue where the return value of ExecuteNonQueryAsync for DDL statements was changed to an incorrect value; I've provided a suggestion to align it with the base DbCommand contract.

@olavloite olavloite force-pushed the spanner-non-blocking-ddl branch from 1425944 to c299d51 Compare November 28, 2025 16:25
@github-actions
Copy link

Pull request diff results
Finding changes in Google.Cloud.Spanner.Data...
Comparing old and new versions (by source)
Minor changes:
Class 'SpannerCommand'; method 'Operation ExecuteDdl(Boolean pollUntilCompleted = False)' added.
Class 'SpannerCommand'; method 'Task ExecuteDdlAsync(Boolean pollUntilCompleted = False, CancellationToken cancellationToken = null)' added.

Diff level: Minor

Comparing with previous NuGet package
Checking compatibility for Google.Cloud.Spanner.Data version 5.7.0
Differences from 5.0.0
Minor changes:
Enum 'LockHint' added.
Enum 'OrderBy' added.
Enum 'ReadLockMode' added.
Class 'ReadOptions'; method 'ReadOptions WithLockHint(Nullable lockHint)' added.
Class 'ReadOptions'; method 'ReadOptions WithOrderBy(Nullable orderBy)' added.
Class 'ReadOptions'; property 'Nullable LockHint { get; }' added.
Class 'ReadOptions'; property 'Nullable OrderBy { get; }' added.
Class 'SpannerCommand'; method 'Operation ExecuteDdl(Boolean pollUntilCompleted = False)' added.
Class 'SpannerCommand'; method 'Task ExecuteDdlAsync(Boolean pollUntilCompleted = False, CancellationToken cancellationToken = null)' added.
Class 'SpannerConnection'; method 'SpannerCommand CreateDeleteCommandForKeySet(String databaseTable, KeySet keySet)' added.
Class 'SpannerConnectionStringBuilder'; property 'IsolationLevel IsolationLevel { get; set; }' added.
Class 'SpannerConnectionStringBuilder'; property 'String UniverseDomain { get; set; }' added.
Class 'SpannerDataReader'; method 'Interval GetInterval(Int32 i)' added.
Class 'SpannerDbType'; property 'SpannerDbType Interval { get; }' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithIsolationLevel(IsolationLevel isolationLevel)' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithReadLockMode(ReadLockMode readLockMode)' added.
Class 'SpannerTransactionCreationOptions'; property 'ReadLockMode ReadLockMode { get; }' added.
Class 'SpannerTransactionCreationOptions'; property 'IsolationLevel IsolationLevel { get; }' added.
Dependency Grpc.Auth v2.0.0.0 removed
Dependency Google.Api.CommonProtos changed from v2.16.0.0 to v2.17.0.0
Dependency Google.Api.Gax changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Api.Gax.Grpc changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Apis.Auth changed from v1.68.0.0 to v1.72.0.0
Dependency Google.Cloud.Spanner.Admin.Database.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.Cloud.Spanner.Common.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.Cloud.Spanner.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.LongRunning changed from v3.3.0.0 to v3.5.0.0
Dependency Google.Protobuf changed from v3.28.2.0 to v3.31.1.0
Dependency Microsoft.Extensions.Configuration.Abstractions changed from v6.0.0.0 to v8.0.0.0

Diff level: Minor

Finished comparisons for Google.Cloud.Spanner.Data

1 similar comment
@github-actions
Copy link

Pull request diff results
Finding changes in Google.Cloud.Spanner.Data...
Comparing old and new versions (by source)
Minor changes:
Class 'SpannerCommand'; method 'Operation ExecuteDdl(Boolean pollUntilCompleted = False)' added.
Class 'SpannerCommand'; method 'Task ExecuteDdlAsync(Boolean pollUntilCompleted = False, CancellationToken cancellationToken = null)' added.

Diff level: Minor

Comparing with previous NuGet package
Checking compatibility for Google.Cloud.Spanner.Data version 5.7.0
Differences from 5.0.0
Minor changes:
Enum 'LockHint' added.
Enum 'OrderBy' added.
Enum 'ReadLockMode' added.
Class 'ReadOptions'; method 'ReadOptions WithLockHint(Nullable lockHint)' added.
Class 'ReadOptions'; method 'ReadOptions WithOrderBy(Nullable orderBy)' added.
Class 'ReadOptions'; property 'Nullable LockHint { get; }' added.
Class 'ReadOptions'; property 'Nullable OrderBy { get; }' added.
Class 'SpannerCommand'; method 'Operation ExecuteDdl(Boolean pollUntilCompleted = False)' added.
Class 'SpannerCommand'; method 'Task ExecuteDdlAsync(Boolean pollUntilCompleted = False, CancellationToken cancellationToken = null)' added.
Class 'SpannerConnection'; method 'SpannerCommand CreateDeleteCommandForKeySet(String databaseTable, KeySet keySet)' added.
Class 'SpannerConnectionStringBuilder'; property 'IsolationLevel IsolationLevel { get; set; }' added.
Class 'SpannerConnectionStringBuilder'; property 'String UniverseDomain { get; set; }' added.
Class 'SpannerDataReader'; method 'Interval GetInterval(Int32 i)' added.
Class 'SpannerDbType'; property 'SpannerDbType Interval { get; }' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithIsolationLevel(IsolationLevel isolationLevel)' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithReadLockMode(ReadLockMode readLockMode)' added.
Class 'SpannerTransactionCreationOptions'; property 'ReadLockMode ReadLockMode { get; }' added.
Class 'SpannerTransactionCreationOptions'; property 'IsolationLevel IsolationLevel { get; }' added.
Dependency Grpc.Auth v2.0.0.0 removed
Dependency Google.Api.CommonProtos changed from v2.16.0.0 to v2.17.0.0
Dependency Google.Api.Gax changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Api.Gax.Grpc changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Apis.Auth changed from v1.68.0.0 to v1.72.0.0
Dependency Google.Cloud.Spanner.Admin.Database.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.Cloud.Spanner.Common.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.Cloud.Spanner.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.LongRunning changed from v3.3.0.0 to v3.5.0.0
Dependency Google.Protobuf changed from v3.28.2.0 to v3.31.1.0
Dependency Microsoft.Extensions.Configuration.Abstractions changed from v6.0.0.0 to v8.0.0.0

Diff level: Minor

Finished comparisons for Google.Cloud.Spanner.Data

@olavloite
Copy link
Contributor Author

/gemini review

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 a valuable feature for executing Spanner DDL statements without awaiting the completion of the long-running operation. The implementation is clean, follows existing patterns in the codebase, and is accompanied by corresponding integration tests. My review includes a couple of suggestions to enhance the clarity of the documentation for the newly introduced methods.

@github-actions
Copy link

Pull request diff results
Finding changes in Google.Cloud.Spanner.Data...
Comparing old and new versions (by source)
Minor changes:
Class 'SpannerCommand'; method 'Operation ExecuteDdl(Boolean pollUntilCompleted = False)' added.
Class 'SpannerCommand'; method 'Task ExecuteDdlAsync(Boolean pollUntilCompleted = False, CancellationToken cancellationToken = null)' added.

Diff level: Minor

Comparing with previous NuGet package
Checking compatibility for Google.Cloud.Spanner.Data version 5.7.0
Differences from 5.0.0
Minor changes:
Enum 'LockHint' added.
Enum 'OrderBy' added.
Enum 'ReadLockMode' added.
Class 'ReadOptions'; method 'ReadOptions WithLockHint(Nullable lockHint)' added.
Class 'ReadOptions'; method 'ReadOptions WithOrderBy(Nullable orderBy)' added.
Class 'ReadOptions'; property 'Nullable LockHint { get; }' added.
Class 'ReadOptions'; property 'Nullable OrderBy { get; }' added.
Class 'SpannerCommand'; method 'Operation ExecuteDdl(Boolean pollUntilCompleted = False)' added.
Class 'SpannerCommand'; method 'Task ExecuteDdlAsync(Boolean pollUntilCompleted = False, CancellationToken cancellationToken = null)' added.
Class 'SpannerConnection'; method 'SpannerCommand CreateDeleteCommandForKeySet(String databaseTable, KeySet keySet)' added.
Class 'SpannerConnectionStringBuilder'; property 'IsolationLevel IsolationLevel { get; set; }' added.
Class 'SpannerConnectionStringBuilder'; property 'String UniverseDomain { get; set; }' added.
Class 'SpannerDataReader'; method 'Interval GetInterval(Int32 i)' added.
Class 'SpannerDbType'; property 'SpannerDbType Interval { get; }' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithIsolationLevel(IsolationLevel isolationLevel)' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithReadLockMode(ReadLockMode readLockMode)' added.
Class 'SpannerTransactionCreationOptions'; property 'ReadLockMode ReadLockMode { get; }' added.
Class 'SpannerTransactionCreationOptions'; property 'IsolationLevel IsolationLevel { get; }' added.
Dependency Grpc.Auth v2.0.0.0 removed
Dependency Google.Api.CommonProtos changed from v2.16.0.0 to v2.17.0.0
Dependency Google.Api.Gax changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Api.Gax.Grpc changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Apis.Auth changed from v1.68.0.0 to v1.72.0.0
Dependency Google.Cloud.Spanner.Admin.Database.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.Cloud.Spanner.Common.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.Cloud.Spanner.V1 changed from v5.0.0.0 to v5.7.0.0
Dependency Google.LongRunning changed from v3.3.0.0 to v3.5.0.0
Dependency Google.Protobuf changed from v3.28.2.0 to v3.31.1.0
Dependency Microsoft.Extensions.Configuration.Abstractions changed from v6.0.0.0 to v8.0.0.0

Diff level: Minor

Finished comparisons for Google.Cloud.Spanner.Data

Copy link
Contributor

@amanda-tarafa amanda-tarafa left a comment

Choose a reason for hiding this comment

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

I've left some specific comments, and there are a lot of files here that seem to come from you dev environment that need to be reverted.

But let's chat more internally.

var response = await databaseAdminClient.CreateDatabaseAsync(request).ConfigureAwait(false);
response = await response.PollUntilCompletedAsync().ConfigureAwait(false);
operation = response.RpcMessage;
if (pollUntilCompleted)
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of adding this if in two places, let's have this method return the un-polled operation and the wrapping ones poll until completion if they need to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is that really possible in this case, considering the fact that the two methods return two different operation types:

  1. Operation<Database, CreateDatabaseMetadata>
  2. Operation<Empty, UpdateDatabaseDdlMetadata>

(Type non-generic Operation class cannot be used for polling)

Copy link
Contributor

Choose a reason for hiding this comment

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

If the goal is to remove duplication might I instead suggest a a generic local function to centralize the polling and error-handling logic (at the bottom of this method):

async Task<Operation> HandleLro<TResp, TMeta>(Operation<TResp, TMeta> operation)
{
    if (pollUntilCompleted)
    {
        operation = await operation.PollUntilCompletedAsync().ConfigureAwait(false);
    }
    if (operation.IsFaulted)
    {
        throw SpannerException.FromOperationFailedException(operation.Exception);
    }
    return operation;
}

If we go the other way and use the returned Operation to poll later, the calling method will add extra overhead because it needs to establish a new channel since this method shuts down the one it creates in the finally block.

/// A reference to the long-running operation that was started for the DDL statement(s).
/// Note: The operation is null for DropDatabase commands.
/// </returns>
public Task<Operation> ExecuteDdlAsync(bool pollUntilCompleted = false, CancellationToken cancellationToken = default) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

We usually don't expose the underlying types in the ADO.NET layer. Is it really needed to return an operation? And in that case what part of the Operation is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced with Task<string> StartDdlAsync

@github-actions
Copy link

Pull request diff results
Finding changes in Google.Cloud.Spanner.Data...
Comparing old and new versions (by source)
Minor changes:
Class 'SpannerCommand'; method 'Task StartDdlAsync(CancellationToken cancellationToken = null)' added.

Diff level: Minor

Comparing with previous NuGet package
Checking compatibility for Google.Cloud.Spanner.Data version 5.8.0
Differences from 5.0.0
Minor changes:
Enum 'LockHint' added.
Enum 'OrderBy' added.
Enum 'ReadLockMode' added.
Class 'ReadOptions'; method 'ReadOptions WithLockHint(Nullable lockHint)' added.
Class 'ReadOptions'; method 'ReadOptions WithOrderBy(Nullable orderBy)' added.
Class 'ReadOptions'; property 'Nullable LockHint { get; }' added.
Class 'ReadOptions'; property 'Nullable OrderBy { get; }' added.
Class 'SpannerCommand'; method 'Task StartDdlAsync(CancellationToken cancellationToken = null)' added.
Class 'SpannerConnection'; method 'SpannerCommand CreateDeleteCommandForKeySet(String databaseTable, KeySet keySet)' added.
Class 'SpannerConnectionStringBuilder'; property 'IsolationLevel IsolationLevel { get; set; }' added.
Class 'SpannerConnectionStringBuilder'; property 'String UniverseDomain { get; set; }' added.
Class 'SpannerDataReader'; method 'Interval GetInterval(Int32 i)' added.
Class 'SpannerDbType'; property 'SpannerDbType Interval { get; }' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithIsolationLevel(IsolationLevel isolationLevel)' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithReadLockMode(ReadLockMode readLockMode)' added.
Class 'SpannerTransactionCreationOptions'; property 'ReadLockMode ReadLockMode { get; }' added.
Class 'SpannerTransactionCreationOptions'; property 'IsolationLevel IsolationLevel { get; }' added.
Dependency Grpc.Auth v2.0.0.0 removed
Dependency Google.Api.CommonProtos changed from v2.16.0.0 to v2.17.0.0
Dependency Google.Api.Gax changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Api.Gax.Grpc changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Apis.Auth changed from v1.68.0.0 to v1.72.0.0
Dependency Google.Cloud.Spanner.Admin.Database.V1 changed from v5.0.0.0 to v5.8.0.0
Dependency Google.Cloud.Spanner.Common.V1 changed from v5.0.0.0 to v5.8.0.0
Dependency Google.Cloud.Spanner.V1 changed from v5.0.0.0 to v5.8.0.0
Dependency Google.LongRunning changed from v3.3.0.0 to v3.5.0.0
Dependency Google.Protobuf changed from v3.28.2.0 to v3.31.1.0
Dependency Microsoft.Extensions.Configuration.Abstractions changed from v6.0.0.0 to v8.0.0.0

Diff level: Minor

Finished comparisons for Google.Cloud.Spanner.Data

@olavloite olavloite force-pushed the spanner-non-blocking-ddl branch from e2e1643 to 3c984c9 Compare December 15, 2025 08:24
@github-actions
Copy link

Pull request diff results
Finding changes in Google.Cloud.Spanner.Data...
Comparing old and new versions (by source)
Minor changes:
Class 'SpannerCommand'; method 'Task StartDdlAsync(CancellationToken cancellationToken = null)' added.

Diff level: Minor

Comparing with previous NuGet package
Checking compatibility for Google.Cloud.Spanner.Data version 5.8.0
Differences from 5.0.0
Minor changes:
Enum 'LockHint' added.
Enum 'OrderBy' added.
Enum 'ReadLockMode' added.
Class 'ReadOptions'; method 'ReadOptions WithLockHint(Nullable lockHint)' added.
Class 'ReadOptions'; method 'ReadOptions WithOrderBy(Nullable orderBy)' added.
Class 'ReadOptions'; property 'Nullable LockHint { get; }' added.
Class 'ReadOptions'; property 'Nullable OrderBy { get; }' added.
Class 'SpannerCommand'; method 'Task StartDdlAsync(CancellationToken cancellationToken = null)' added.
Class 'SpannerConnection'; method 'SpannerCommand CreateDeleteCommandForKeySet(String databaseTable, KeySet keySet)' added.
Class 'SpannerConnectionStringBuilder'; property 'IsolationLevel IsolationLevel { get; set; }' added.
Class 'SpannerConnectionStringBuilder'; property 'String UniverseDomain { get; set; }' added.
Class 'SpannerDataReader'; method 'Interval GetInterval(Int32 i)' added.
Class 'SpannerDbType'; property 'SpannerDbType Interval { get; }' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithIsolationLevel(IsolationLevel isolationLevel)' added.
Class 'SpannerTransactionCreationOptions'; method 'SpannerTransactionCreationOptions WithReadLockMode(ReadLockMode readLockMode)' added.
Class 'SpannerTransactionCreationOptions'; property 'ReadLockMode ReadLockMode { get; }' added.
Class 'SpannerTransactionCreationOptions'; property 'IsolationLevel IsolationLevel { get; }' added.
Dependency Grpc.Auth v2.0.0.0 removed
Dependency Google.Api.CommonProtos changed from v2.16.0.0 to v2.17.0.0
Dependency Google.Api.Gax changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Api.Gax.Grpc changed from v4.9.0.0 to v4.12.1.0
Dependency Google.Apis.Auth changed from v1.68.0.0 to v1.72.0.0
Dependency Google.Cloud.Spanner.Admin.Database.V1 changed from v5.0.0.0 to v5.8.0.0
Dependency Google.Cloud.Spanner.Common.V1 changed from v5.0.0.0 to v5.8.0.0
Dependency Google.Cloud.Spanner.V1 changed from v5.0.0.0 to v5.8.0.0
Dependency Google.LongRunning changed from v3.3.0.0 to v3.5.0.0
Dependency Google.Protobuf changed from v3.28.2.0 to v3.31.1.0
Dependency Microsoft.Extensions.Configuration.Abstractions changed from v6.0.0.0 to v8.0.0.0

Diff level: Minor

Finished comparisons for Google.Cloud.Spanner.Data

var response = await databaseAdminClient.CreateDatabaseAsync(request).ConfigureAwait(false);
response = await response.PollUntilCompletedAsync().ConfigureAwait(false);
operation = response.RpcMessage;
if (pollUntilCompleted)
Copy link
Contributor

Choose a reason for hiding this comment

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

If the goal is to remove duplication might I instead suggest a a generic local function to centralize the polling and error-handling logic (at the bottom of this method):

async Task<Operation> HandleLro<TResp, TMeta>(Operation<TResp, TMeta> operation)
{
    if (pollUntilCompleted)
    {
        operation = await operation.PollUntilCompletedAsync().ConfigureAwait(false);
    }
    if (operation.IsFaulted)
    {
        throw SpannerException.FromOperationFailedException(operation.Exception);
    }
    return operation;
}

If we go the other way and use the returned Operation to poll later, the calling method will add extra overhead because it needs to establish a new channel since this method shuts down the one it creates in the finally block.

/// The name of the long-running operation that was started for the DDL statement(s).
/// Note: The ID is empty for DropDatabase commands.
/// </returns>
public Task<string> StartDdlAsync(CancellationToken cancellationToken = default) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Can we add a param xml element, something like.

/// <param name="cancellationToken">An optional token for canceling the call.</param>

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a test to ensure an operation name is returned also, maybe something that extends this like so:

[Fact]
public async Task StartDdlAsync_ReturnsOperationAndCompletes()
{
    string dbName = GenerateDatabaseName();
    var operationsClient = DatabaseAdminClient.Create().CreateDatabaseOperationsClient;
    using var connection = new SpannerConnection(_fixture.Database.NoDbConnectionString);

    try
    {
        var createCmd = connection.CreateDdlCommand($"CREATE DATABASE {dbName}");
        var operationName = await createCmd.StartDdlAsync();

        Assert.False(string.IsNullOrEmpty(operationName));

        // Wait for the database creation LRO to finish
        var rawOperation = await operationsClient.GetOperationAsync(operationName);
        var operation = new Operation<Database, CreateDatabaseMetadata>(rawOperation, operationsClient);
        var completedOperation = await operation.PollUntilCompletedAsync();

        Assert.True(completedOperation.IsCompleted);
        Assert.Null(completedOperation.Exception);
    }
    finally
    {
        // Always cleanup the created database
        var dropCommand = connection.CreateDdlCommand($"DROP DATABASE {dbName}");
        await dropCommand.ExecuteNonQueryAsync();
    }

    // No sessions created, so no session pool.
}

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants