/// <summary>
 /// Initializes a new instance of <see cref="SpannerCommand"/>
 /// </summary>
 /// <param name="commandText">If this command is a SQL Query, then commandText is
 /// the SQL statement. If its an update, insert or delete command, then this text
 /// is "[operation] [table]" such as "UPDATE MYTABLE"</param>. Must not be null.
 /// <param name="connection">The <see cref="SpannerConnection"/> that is
 /// associated with this <see cref="SpannerCommand"/>. Must not be null.</param>
 /// <param name="transaction">An optional <see cref="SpannerTransaction"/>
 /// created through <see>
 /// <cref>SpannerConnection.BeginTransactionAsync</cref></see>. May be null.</param>
 /// <param name="parameters">An optional collection of <see cref="SpannerParameter"/>
 /// that is used in the command. May be null.</param>
 public SpannerCommand(
     string commandText,
     SpannerConnection connection,
     SpannerTransaction transaction        = null,
     SpannerParameterCollection parameters = null)
     : this(SpannerCommandTextBuilder.FromCommandText(commandText), connection, transaction, parameters)
 {
 }
Пример #2
0
 private SpannerCommand(
     SpannerConnection connection,
     SpannerTransaction transaction,
     SpannerParameterCollection parameters) : this()
 {
     SpannerConnection = connection;
     _transaction      = transaction;
     Parameters        = parameters;
 }
Пример #3
0
 /// <summary>
 /// Initializes a new instance of <see cref="SpannerCommand"/>.
 /// </summary>
 /// <param name="connection">The <see cref="SpannerConnection"/> that is
 /// associated with this <see cref="SpannerCommand"/>. Must not be null.</param>
 /// <param name="transaction">The <see cref="SpannerTransaction"/>
 /// used when creating the <see cref="CommandPartition"/>.
 /// </param>
 /// <param name="commandPartition">
 /// The partition which this command is restricted to.
 /// See <see cref="SpannerConnection.BeginReadOnlyTransaction(TransactionId)"/>
 /// </param>
 public SpannerCommand(
     SpannerConnection connection,
     SpannerTransaction transaction,
     CommandPartition commandPartition)
     : this(connection, transaction, null, commandPartition)
 {
     GaxPreconditions.CheckNotNull(connection, nameof(connection));
     GaxPreconditions.CheckNotNull(transaction, nameof(transaction));
     GaxPreconditions.CheckNotNull(commandPartition, nameof(commandPartition));
     SpannerCommandTextBuilder = SpannerCommandTextBuilder.FromCommandText(commandPartition.ExecuteSqlRequest.Sql);
 }
        /// <summary>
        /// Initializes a new instance of <see cref="SpannerCommand"/>.
        /// </summary>
        /// <param name="commandTextBuilder">The <see cref="SpannerCommandTextBuilder"/>
        /// that configures the text of this command. Must not be null.</param>
        /// <param name="connection">The <see cref="SpannerConnection"/> that is
        /// associated with this <see cref="SpannerCommand"/>. Must not be null.</param>
        /// <param name="transaction">An optional <see cref="SpannerTransaction"/>
        /// created through <see>
        /// <cref>SpannerConnection.BeginTransactionAsync</cref>
        /// </see>
        /// </param>. May be null.
        /// <param name="parameters">An optional collection of <see cref="SpannerParameter"/>
        /// that is used in the command. May be null.</param>
        public SpannerCommand(
            SpannerCommandTextBuilder commandTextBuilder,
            SpannerConnection connection,
            SpannerTransaction transaction        = null,
            SpannerParameterCollection parameters = null)
            : this(connection, transaction, parameters)
        {
            GaxPreconditions.CheckNotNull(commandTextBuilder, nameof(commandTextBuilder));
            GaxPreconditions.CheckNotNull(connection, nameof(connection));

            SpannerCommandTextBuilder = commandTextBuilder;
        }
Пример #5
0
 /// <summary>
 /// Initializes a new instance of <see cref="SpannerCommand"/> for a read operation.
 /// </summary>
 /// <remarks>
 /// The initial command timeout is taken from the options associated with <paramref name="connection"/>.
 /// </remarks>
 /// <param name="commandTextBuilder">The <see cref="SpannerCommandTextBuilder"/>
 /// that configures the text of this command. Must be a read command and not be null.</param>
 /// <param name="connection">The <see cref="SpannerConnection"/> that is
 /// associated with this <see cref="SpannerCommand"/>. Must not be null.</param>
 /// <param name="keySet">The <see cref="KeySet"/> that is used to select rows. Must not be null.</param>
 /// <param name="transaction">An optional <see cref="SpannerTransaction"/>
 /// created through <see cref="SpannerConnection.BeginTransactionAsync" />. May be null.</param>
 internal SpannerCommand(
     SpannerCommandTextBuilder commandTextBuilder,
     SpannerConnection connection,
     KeySet keySet,
     SpannerTransaction transaction = null)
     : this(connection, transaction, null, null)
 {
     GaxPreconditions.CheckArgument(commandTextBuilder.SpannerCommandType == SpannerCommandType.Read,
                                    nameof(commandTextBuilder.SpannerCommandType), "KeySet is only allowed for Read commands");
     SpannerCommandTextBuilder = GaxPreconditions.CheckNotNull(commandTextBuilder, nameof(commandTextBuilder));
     KeySet = GaxPreconditions.CheckNotNull(keySet, nameof(keySet));
 }
 private async Task <SpannerTransaction> GetTransactionAsync(CancellationToken cancellationToken)
 {
     //note that we delay transaction creation (and thereby session allocation)
     if (_timestampBound != null)
     {
         return(_transaction ?? (_transaction =
                                     await _spannerConnection.BeginReadOnlyTransactionAsync(_timestampBound, cancellationToken)
                                     .ConfigureAwait(false)));
     }
     return(_transaction ?? (_transaction = await _spannerConnection.BeginTransactionAsync(cancellationToken)
                                            .ConfigureAwait(false)));
 }
Пример #7
0
 public void Dispose()
 {
     try
     {
         _transaction?.Dispose();
         // Protect against multiple disposals
         _transaction = null;
     }
     catch (Exception e)
     {
         Logger.Error("Error disposing", e);
     }
 }
Пример #8
0
 private SpannerCommand(
     SpannerConnection connection,
     SpannerTransaction transaction,
     SpannerParameterCollection parameters,
     CommandPartition commandPartition) : this(connection)
 {
     _transaction = transaction;
     Partition    = commandPartition;
     if (parameters != null)
     {
         Parameters = parameters;
     }
 }
 public void Dispose()
 {
     try
     {
         _transaction?.DisposeWithClient(_spannerClient);
         _spannerClientDisposal?.Invoke();
         // Protect against multiple disposals
         _transaction           = null;
         _spannerClientDisposal = null;
     }
     catch (Exception e)
     {
         Logger.DefaultLogger.Error(() => "Error disposing", e);
     }
 }
Пример #10
0
 /// <summary>
 /// Initializes a new instance of <see cref="SpannerCommand"/>.
 /// </summary>
 /// <param name="connection">The <see cref="SpannerConnection"/> that is
 /// associated with this <see cref="SpannerCommand"/>. Must not be null.</param>
 /// <param name="transaction">The <see cref="SpannerTransaction"/>
 /// used when creating the <see cref="CommandPartition"/>.
 /// </param>
 /// <param name="commandPartition">
 /// The partition which this command is restricted to.
 /// See <see cref="SpannerConnection.BeginReadOnlyTransaction(TransactionId)"/>
 /// </param>
 public SpannerCommand(
     SpannerConnection connection,
     SpannerTransaction transaction,
     CommandPartition commandPartition)
     : this(connection, transaction, null, commandPartition)
 {
     GaxPreconditions.CheckNotNull(connection, nameof(connection));
     GaxPreconditions.CheckNotNull(transaction, nameof(transaction));
     GaxPreconditions.CheckNotNull(commandPartition, nameof(commandPartition));
     SpannerCommandTextBuilder = commandPartition.Request.IsQuery
         ? SpannerCommandTextBuilder.FromCommandText(commandPartition.Request.ExecuteSqlRequest.Sql)
         : SpannerCommandTextBuilder.CreateReadTextBuilder(
         commandPartition.Request.ReadRequest.Table,
         ReadOptions.FromColumns(commandPartition.Request.ReadRequest.Columns));
 }
Пример #11
0
        private async Task <SpannerTransaction> GetTransactionAsync(CancellationToken cancellationToken, int timeoutSeconds)
        {
            // Note that we delay transaction creation (and thereby session allocation) until the first operation
            // (ExecuteMutations, ExecuteQuery, ExecuteDml)
            if (_transaction == null)
            {
                _transaction =
                    _timestampBound != null ? await _spannerConnection.BeginReadOnlyTransactionAsync(_timestampBound, cancellationToken).ConfigureAwait(false)
                    : _transactionId != null?_spannerConnection.BeginReadOnlyTransaction(_transactionId)
                        : await _spannerConnection.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);

                // The commit timeout is irrelevant for read-only transactions, but it's easier to set it unconditionally.
                _transaction.CommitTimeout = timeoutSeconds;
            }
            return(_transaction);
        }
        private async Task <SpannerTransaction> GetTransactionAsync(CancellationToken cancellationToken, int timeoutSeconds)
        {
            //note that we delay transaction creation (and thereby session allocation)
            if (_timestampBound != null)
            {
                return(_transaction ?? (_transaction =
                                            await _spannerConnection.BeginReadOnlyTransactionAsync(_timestampBound, cancellationToken)
                                            .ConfigureAwait(false)));
            }
            else if (_transactionId != null)
            {
                return(SpannerTransaction.FromTransactionId(_spannerConnection, _transactionId));
            }
            var result = _transaction ?? (_transaction = await _spannerConnection.BeginTransactionAsync(cancellationToken)
                                                         .ConfigureAwait(false));

            result.CommitTimeout = timeoutSeconds;
            return(result);
        }
        internal async Task <TResult> RunAsync <TResult>(Func <SpannerTransaction, Task <TResult> > asyncWork, CancellationToken cancellationToken = default)
        {
            GaxPreconditions.CheckNotNull(asyncWork, nameof(asyncWork));

            // Session will be initialized and subsequently modified by CommitAttempt.
            PooledSession session = null;

            try
            {
                return(await ExecuteWithRetryAsync(CommitAttempt, cancellationToken).ConfigureAwait(false));
            }
            finally
            {
                session?.Dispose();
            }

            async Task <TResult> CommitAttempt()
            {
                return(await ExecuteHelper.WithErrorTranslationAndProfiling(
                           async() =>
                {
                    SpannerTransaction transaction = null;

                    try
                    {
                        session = await(session?.WithFreshTransactionOrNewAsync(SpannerConnection.s_readWriteTransactionOptions, cancellationToken) ?? _connection.AcquireReadWriteSessionAsync(cancellationToken)).ConfigureAwait(false);
                        transaction = new SpannerTransaction(_connection, TransactionMode.ReadWrite, session, null);

                        TResult result = await asyncWork(transaction).ConfigureAwait(false);
                        await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);

                        return result;
                    }
                    // We only catch to attempt a rollback, and that is possible if we have a transaction already.
                    // If the exception was thrown when refreshing the session the we don't have a transaction yet.
                    catch when(transaction != null)
                    {
                        try
                        {
                            await transaction.RollbackAsync(cancellationToken).ConfigureAwait(false);
                        }
                        catch (Exception e)
                        {
                            // If the commit failed, calling Rollback will fail as well.
                            // We don't want that or any other rollback exception to propagate,
                            // it will not trigger the retry
                            _connection.Logger.Warn("A rollback attempt failed on RetriableTransaction.RunAsync.CommitAttempt", e);
                        }

                        // Throw, the retry helper will know when to retry.
                        throw;
                    }
                    finally
                    {
                        if (transaction != null)
                        {
                            // Let's make sure that the associated session is not released to the pool
                            // because we'll be attempting to get a fresh transaction for this same session first.
                            // If that fails will attempt a new session acquisition.
                            // This session will be disposed of by the pool if it can't be refreshed or by the RunAsync method
                            // if we are not retrying anymore.
                            transaction.DisposeBehavior = DisposeBehavior.Detach;
                            transaction.Dispose();
                        }
                    }
                }, "RetriableTransaction.RunAsync.CommitAttempt", _connection.Logger).ConfigureAwait(false));
            }
        }
 internal SpannerBatchCommand(SpannerTransaction transaction)
 {
     Transaction = GaxPreconditions.CheckNotNull(transaction, nameof(transaction));
     Connection  = transaction.SpannerConnection; // Never null
 }