Skip to content

Commit

Permalink
feat!: Commit/Rollback releases sessions to the pool
Browse files Browse the repository at this point in the history
If a commit or rollback are succesful, the SpannerTransaction is disposed so that resources are released.

BREAKING CHANGE: After a successfull commit or rollback, the transaction is diposed and may not be used again. See the Google.Cloud.Spanner.Data.SpannerTransaction.DisposeBehavior documentation for more information.

Closes googleapis#10673
  • Loading branch information
amanda-tarafa committed May 31, 2024
1 parent cfa961e commit 86fa6a6
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,50 @@ private async Task IncrementByOneAsync(SpannerConnection connection, bool orphan
}
}

[Fact]
public async Task Commit_ReturnsToPool()
{
using var connection = new SpannerConnection(_fixture.ConnectionString);
await connection.OpenAsync();

using var transaction = await connection.BeginTransactionAsync();
using var command = connection.CreateSelectCommand($"SELECT Int64Value FROM {_fixture.TableName} WHERE K=@k");
command.Parameters.Add("k", SpannerDbType.String, _key);
command.Transaction = transaction;

var value = await command.ExecuteScalarAsync();

transaction.Commit();

var poolStatistics = connection.GetSessionPoolSegmentStatistics();

// Because the session is eagerly returned to the pool after a commit, there shouldn't
// be any active sessions even before we dispose of the transaction explicitly.
Assert.Equal(0, poolStatistics.ActiveSessionCount);
}

[Fact]
public async Task Rollback_ReturnsToPool()
{
using var connection = new SpannerConnection(_fixture.ConnectionString);
await connection.OpenAsync();

using var transaction = await connection.BeginTransactionAsync();
using var command = connection.CreateSelectCommand($"SELECT Int64Value FROM {_fixture.TableName} WHERE K=@k");
command.Parameters.Add("k", SpannerDbType.String, _key);
command.Transaction = transaction;

var value = await command.ExecuteScalarAsync();

transaction.Rollback();

var poolStatistics = connection.GetSessionPoolSegmentStatistics();

// Because the session is eagerly returned to the pool after a rollback, there shouldn't
// be any active sessions even before we dispose of the transaction explicitly.
Assert.Equal(0, poolStatistics.ActiveSessionCount);
}

[Fact]
public async Task DetachOnDisposeTransactionIsDetached()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ Task<IEnumerable<long>> ISpannerTransaction.ExecuteBatchDmlAsync(ExecuteBatchDml
{
var callSettings = SpannerConnection.CreateCallSettings(settings => settings.CommitSettings, CommitTimeout, cancellationToken);
var response = await _session.CommitAsync(request, callSettings).ConfigureAwait(false);
// We dispose of the SpannerTransaction to inmediately release the session to the pool when possible.
Dispose();
if (response.CommitTimestamp == null)
{
throw new SpannerException(ErrorCode.Internal, "Commit succeeded, but returned a response with no commit timestamp");
Expand All @@ -464,17 +466,18 @@ private RequestOptions BuildCommitRequestOptions() =>
/// </summary>
/// <param name="cancellationToken">A cancellation token used for this task.</param>
#if NET462
public Task RollbackAsync(CancellationToken cancellationToken = default)
public async Task RollbackAsync(CancellationToken cancellationToken = default)
#else
public override Task RollbackAsync(CancellationToken cancellationToken = default)
public override async Task RollbackAsync(CancellationToken cancellationToken = default)
#endif
{
CheckNotDisposed();
GaxPreconditions.CheckState(Mode != TransactionMode.ReadOnly, "You cannot roll back a readonly transaction.");
var callSettings = SpannerConnection.CreateCallSettings(settings => settings.RollbackSettings, CommitTimeout, cancellationToken);
return ExecuteHelper.WithErrorTranslationAndProfiling(
await ExecuteHelper.WithErrorTranslationAndProfiling(
() => _session.RollbackAsync(new RollbackRequest(), callSettings),
"SpannerTransaction.Rollback", SpannerConnection.Logger);
"SpannerTransaction.Rollback", SpannerConnection.Logger).ConfigureAwait(false);
Dispose();
}

/// <summary>
Expand Down

0 comments on commit 86fa6a6

Please sign in to comment.