ステートメント タイムアウトを構成する

このページでは、Spanner クライアント ライブラリを使用して 1 つのステートメントの実行にタイムアウトを設定する方法について説明します。これは、クライアント ライブラリのデフォルトのタイムアウト構成をオーバーライドするために使用できます。ステートメントが指定されたタイムアウト値内に完了できない場合、ステートメントは DEADLINE_EXCEEDED エラーで失敗します。

以下のサンプルは、Cloud Spanner クライアント ライブラリで 1 つのステートメント実行にタイムアウトを設定する方法を示しています。

Go


import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/spanner"
	"google.golang.org/grpc/codes"
)

func setStatementTimeout(w io.Writer, db string) error {
	client, err := spanner.NewClient(context.Background(), db)
	if err != nil {
		return err
	}
	defer client.Close()

	_, err = client.ReadWriteTransaction(context.Background(),
		func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
			// Create a context with a 60-second timeout and apply this timeout to the insert statement.
			ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 60*time.Second)
			defer cancel()
			stmt := spanner.Statement{
				SQL: `INSERT Singers (SingerId, FirstName, LastName)
					VALUES (39, 'George', 'Washington')`,
			}
			rowCount, err := txn.Update(ctxWithTimeout, stmt)
			// Get the error code from the error. This function returns codes.OK if err == nil.
			code := spanner.ErrCode(err)
			if code == codes.DeadlineExceeded {
				fmt.Fprintf(w, "Insert statement timed out.\n")
			} else if code == codes.OK {
				fmt.Fprintf(w, "%d record(s) inserted.\n", rowCount)
			} else {
				fmt.Fprintf(w, "Insert statement failed with error %v\n", err)
			}
			return err
		})
	if err != nil {
		return err
	}
	return nil
}

Java


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

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

static void executeSqlWithTimeout(DatabaseClient client) {
  CallContextConfigurator configurator = new CallContextConfigurator() {
    public <ReqT, RespT> ApiCallContext configure(ApiCallContext context, ReqT request,
        MethodDescriptor<ReqT, RespT> method) {
      // DML uses the ExecuteSql RPC.
      if (method == SpannerGrpc.getExecuteSqlMethod()) {
        // NOTE: You can use a GrpcCallContext to set a custom timeout for a single RPC
        // invocation. This timeout can however ONLY BE SHORTER than the default timeout
        // for the RPC. If you set a timeout that is longer than the default timeout, then
        // the default timeout will be used.
        return GrpcCallContext.createDefault()
            .withCallOptions(CallOptions.DEFAULT.withDeadlineAfter(60L, TimeUnit.SECONDS));
      }
      // Return null to indicate that the default should be used for other methods.
      return null;
    }
  };
  // Create a context that uses the custom call configuration.
  Context context =
      Context.current().withValue(SpannerOptions.CALL_CONTEXT_CONFIGURATOR_KEY, configurator);
  // Run the transaction in the custom context.
  context.run(() ->
      client.readWriteTransaction().<long[]>run(transaction -> {
        String sql = "INSERT INTO Singers (SingerId, FirstName, LastName)\n"
            + "VALUES (20, 'George', 'Washington')";
        long rowCount = transaction.executeUpdate(Statement.of(sql));
        System.out.printf("%d record inserted.%n", rowCount);
        return null;
      })
  );
}