Recuperar estadísticas de confirmación para una transacción

Para ayudarte a comprender, optimizar y diagnosticar mejor los problemas de transacciones, Cloud Spanner te brinda acceso a las estadísticas de confirmación de transacciones. Por el momento, puedes recuperar la cantidad total de mutaciones de una transacción.

Cuándo usar estadísticas de confirmación

Conocer el recuento de mutaciones de una transacción puede ser útil en las siguientes situaciones.

Optimización para los recorridos de ida y vuelta

Para ayudar a mejorar el rendimiento de la aplicación, puedes reducir la cantidad de recorridos de ida y vuelta a la base de datos haciendo todo el trabajo posible en cada transacción. En esta situación, deseas maximizar la cantidad de mutaciones por transacción y, al mismo tiempo, mantenerte dentro de los límites del sistema.

Para determinar cuántas filas puedes confirmar por transacción y, a la vez, mantenerte por debajo del límite, primero confirma una fila en una transacción. Esto te proporciona un modelo de referencia del recuento de mutaciones por fila. Luego, divide el límite del sistema por el modelo de referencia para obtener un número de filas por transacción. Para obtener más información sobre cómo se cuentan las mutaciones, consulta esta nota.

Ten en cuenta que optimizar la ida y vuelta no siempre es beneficioso, en especial si genera más contenciones de bloqueo. Puedes solucionar problemas de bloqueo en tu base de datos con las estadísticas de bloqueo.

Supervisa las transacciones para evitar alcanzar los límites del sistema

A medida que aumenta el uso de la aplicación, es posible que la cantidad de mutaciones de tu transacción también aumente. Para evitar alcanzar el límite del sistema y hacer que la transacción falle con el tiempo, puedes supervisar de forma proactiva la estadística de confirmación del recuento de mutaciones a lo largo del tiempo. Si observas que este valor aumenta para la misma transacción, podría ser hora de volver a optimizar tu transacción, como se describe en la sección anterior.

Cómo acceder a las estadísticas de confirmación

Las estadísticas de confirmación no se muestran de forma predeterminada. En su lugar, debes establecer la marca return_commit_stats en verdadera en cada CommitRequest. Si tu intento de confirmación supera la cantidad máxima permitida de mutaciones en una transacción, la confirmación fallará y se mostrará un error INVALID_dfpvideo.

El siguiente es un ejemplo de cómo mostrar estadísticas de confirmación mediante las bibliotecas cliente de Cloud Spanner.

Recupera estadísticas de confirmación

En el siguiente ejemplo, se muestra cómo obtener estadísticas de confirmación mediante las bibliotecas cliente de Cloud Spanner.

C++

El siguiente código llama a set_return_stats() en CommitOptions y muestra un recuento de mutaciones de 6, ya que estamos insertando o actualizando 2 filas y 3 columnas en cada fila.

void GetCommitStatistics(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  auto commit = client.Commit(
      spanner::Mutations{
          spanner::UpdateMutationBuilder(
              "Albums", {"SingerId", "AlbumId", "MarketingBudget"})
              .EmplaceRow(1, 1, 200000)
              .EmplaceRow(2, 2, 400000)
              .Build()},
      spanner::CommitOptions{}.set_return_stats(true));

  if (!commit) throw std::runtime_error(commit.status().message());
  if (commit->commit_stats) {
    std::cout << "Updated data with " << commit->commit_stats->mutation_count
              << " mutations.\n";
  }
  std::cout << "Update was successful [spanner_get_commit_stats]\n";
}

C#

En C#, las estadísticas de confirmación no se muestran directamente a través de la API. En su lugar, se registran en el nivel de registro Información mediante el registrador predeterminado.

El siguiente código habilita el registro de estadísticas de confirmación para todas las transacciones mediante la configuración de la propiedad LogCommitStats de SpannerConnectionStringBuilder como verdadera. El código también implementa un registrador de muestras que mantiene una referencia a la última respuesta de confirmación observada. A continuación, se recupera la MutationCount de esta respuesta y se muestra.


using Google.Cloud.Spanner.Data;
using Google.Cloud.Spanner.V1;
using Google.Cloud.Spanner.V1.Internal.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

public class LogCommitStatsAsyncSample
{
    public async Task<long> LogCommitStatsAsync(string projectId, string instanceId, string databaseId)
    {
        // Commit statistics are logged at level Info by the default logger.
        // This sample uses a custom logger to access the commit statistics.
        // See https://googleapis.github.io/google-cloud-dotnet/docs/Google.Cloud.Spanner.Data/logging.html
        // for more information on how to use loggers.
        var logger = new CommitStatsSampleLogger();
        var options = new SessionPoolOptions();
        var poolManager = SessionPoolManager.Create(options, logger);
        var connectionStringBuilder = new SpannerConnectionStringBuilder
        {
            ConnectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}",
            // Set LogCommitStats to true to enable logging commit statistics for all transactions on the connection.
            // LogCommitStats can also be enabled/disabled for individual Spanner transactions.
            LogCommitStats = true,
            SessionPoolManager = poolManager,
        };

        using var connection = new SpannerConnection(connectionStringBuilder);
        await connection.OpenAsync();

        using var cmd = connection.CreateDmlCommand("INSERT Singers (SingerId, FirstName, LastName) VALUES (110, 'Virginia', 'Watson')");
        var rowCount = await cmd.ExecuteNonQueryAsync();
        var mutationCount = logger._lastCommitResponse.CommitStats.MutationCount;

        Console.WriteLine($"{rowCount} row(s) inserted...");
        Console.WriteLine($"{mutationCount} mutation(s) in transaction...");

        return mutationCount;
    }

    /// <summary>
    /// Sample logger that keeps a reference to the last seen commit response.
    /// Use the default logger if you only want to log the commit stats.
    /// </summary>
    public class CommitStatsSampleLogger : Logger
    {
        internal CommitResponse _lastCommitResponse;

        /// <summary>
        /// This method is called when a transaction that requested commit stats is committed.
        /// </summary>
        public override void LogCommitStats(CommitRequest request, CommitResponse response)
        {
            _lastCommitResponse = response;
            base.LogCommitStats(request, response);
        }

        protected override void LogImpl(LogLevel level, string message, Exception exception) =>
            WriteLine(exception == null ? $"{level}: {message}" : $"{level}: {message}, Exception: {exception}");

        protected override void LogPerformanceEntries(IEnumerable<string> entries)
        {
            string separator = Environment.NewLine + "  ";
            WriteLine($"Performance:{separator}{string.Join(separator, entries)}");
        }

        private void WriteLine(string line) => Trace.TraceInformation(line);
    }
}

Comienza a usarlo

Con el siguiente código, se establece la marca ReturnCommitStats y se imprime el recuento de mutaciones cuando la transacción se confirma de forma correcta.


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
)

func commitStats(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return fmt.Errorf("commitStats.NewClient: %v", err)
	}
	defer client.Close()

	resp, err := client.ReadWriteTransactionWithOptions(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		stmt := spanner.Statement{
			SQL: `INSERT Singers (SingerId, FirstName, LastName)
					VALUES (110, 'Virginia', 'Watson')`,
		}
		rowCount, err := txn.Update(ctx, stmt)
		if err != nil {
			return err
		}
		fmt.Fprintf(w, "%d record(s) inserted.\n", rowCount)
		return nil
	}, spanner.TransactionOptions{CommitOptions: spanner.CommitOptions{ReturnCommitStats: true}})
	if err != nil {
		return fmt.Errorf("commitStats.ReadWriteTransactionWithOptions: %v", err)
	}
	fmt.Fprintf(w, "%d mutations in transaction\n", resp.CommitStats.MutationCount)
	return nil
}

Java


import com.google.cloud.spanner.CommitResponse;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.Options;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import java.util.Arrays;

public class GetCommitStatsSample {

  static void getCommitStats() {
    // TODO(developer): Replace these variables before running the sample.
    final String projectId = "my-project";
    final String instanceId = "my-instance";
    final String databaseId = "my-database";

    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
      final DatabaseClient databaseClient = spanner
          .getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId));
      getCommitStats(databaseClient);
    }
  }

  static void getCommitStats(DatabaseClient databaseClient) {
    final CommitResponse commitResponse = databaseClient.writeWithOptions(Arrays.asList(
        Mutation.newInsertOrUpdateBuilder("Albums")
            .set("SingerId")
            .to("1")
            .set("AlbumId")
            .to("1")
            .set("MarketingBudget")
            .to("200000")
            .build(),
        Mutation.newInsertOrUpdateBuilder("Albums")
            .set("SingerId")
            .to("2")
            .set("AlbumId")
            .to("2")
            .set("MarketingBudget")
            .to("400000")
            .build()
    ), Options.commitStats());

    System.out.println(
        "Updated data with " + commitResponse.getCommitStats().getMutationCount() + " mutations.");
  }
}

Node.js

En el siguiente código, se establece la marca returnCommitStats y se muestra un recuento de mutaciones de 6, ya que estamos insertando o actualizando 2 filas y 3 columnas en cada fila.

// Imports the Google Cloud client library.
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client.
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database.
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

// Instantiate Spanner table objects.
const albumsTable = database.table('Albums');

// Updates rows in the Venues table.
try {
  const [response] = await albumsTable.upsert(
    [
      {SingerId: '1', AlbumId: '1', MarketingBudget: '200000'},
      {SingerId: '2', AlbumId: '2', MarketingBudget: '400000'},
    ],
    {returnCommitStats: true}
  );
  console.log(
    `Updated data with ${response.commitStats.mutationCount} mutations.`
  );
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;

/**
 * Creates a database and tables for sample data.
 * Example:
 * ```
 * create_database($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function get_commit_stats($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $commitStats = $database->runTransaction(function (Transaction $t) use ($spanner) {
        $t->updateBatch('Albums', [
            [
                'SingerId' => 1,
                'AlbumId' => 1,
                'MarketingBudget' => 200000,
            ],
            [
                'SingerId' => 2,
                'AlbumId' => 2,
                'MarketingBudget' => 400000,
            ]
        ]);
        $t->commit(['returnCommitStats' => true]);
        return $t->getCommitStats();
    });

    print('Updated data with ' . $commitStats['mutationCount'] . ' mutations.' . PHP_EOL);
}

Python

En lugar de mostrar estadísticas de confirmación directamente a través de la API, la biblioteca cliente de Python las registra a través de stdout en el nivel Info.

El siguiente código habilita el registro de estadísticas de confirmación para todas las transacciones mediante la configuración de database.log_commit_stats = True. El código también implementa un registrador de muestras que mantiene una referencia a la última respuesta de confirmación observada. Luego, la mutation_count se recupera de esta respuesta y se muestra.

def log_commit_stats(instance_id, database_id):
    """Inserts sample data using DML and displays the commit statistics. """
    # By default, commit statistics are logged via stdout at level Info.
    # This sample uses a custom logger to access the commit statistics.
    class CommitStatsSampleLogger(logging.Logger):
        def __init__(self):
            self.last_commit_stats = None
            super().__init__("commit_stats_sample")

        def info(self, msg, *args, **kwargs):
            if kwargs["extra"] and "commit_stats" in kwargs["extra"]:
                self.last_commit_stats = kwargs["extra"]["commit_stats"]
            super().info(msg)

    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id, logger=CommitStatsSampleLogger())
    database.log_commit_stats = True

    def insert_singers(transaction):
        row_ct = transaction.execute_update(
            "INSERT Singers (SingerId, FirstName, LastName) "
            " VALUES (110, 'Virginia', 'Watson')"
        )

        print("{} record(s) inserted.".format(row_ct))

    database.run_in_transaction(insert_singers)
    commit_stats = database.logger.last_commit_stats
    print(
        "{} mutation(s) in transaction.".format(
            commit_stats.mutation_count
        )
    )

Ruby

En el siguiente código, se establece la marca return_commit_stats y se muestra un recuento de mutaciones de 6, ya que estamos insertando o actualizando 2 filas y 3 columnas en cada fila.

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

records = [
  { SingerId: 1, AlbumId: 1, MarketingBudget: 200_000 },
  { SingerId: 2, AlbumId: 2, MarketingBudget: 400_000 }
]
commit_options = { return_commit_stats: true }
resp = client.upsert "Albums", records, commit_options: commit_options
puts "Updated data with #{resp.stats.mutation_count} mutations."

¿Qué sigue?