本页面介绍了如何使用 Spanner 客户端库为单个语句执行设置超时。此方法可用于替换客户端库的默认超时配置。如果语句无法在给定的超时值内完成,则会失败并显示 DEADLINE_EXCEEDED
错误。
这些示例展示了如何在 Cloud Spanner 客户端库中为单个语句执行设置超时。
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;
})
);
}
Node.js
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');
// Creates a client
const spanner = new Spanner({
projectId: projectId,
});
async function executeSqlWithTimeout() {
// Gets a reference to a Cloud Spanner instance and database.
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);
try {
await database.runTransactionAsync(async tx => {
// NOTE: You can use gaxOptions 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.
const query = {
sql: "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (110, 'George', 'Washington')",
gaxOptions: {
timeout: 60000, // 60 seconds timeout
},
};
const results = await tx.run(query);
console.log(`${results[1].rowCountExact} record inserted.`);
await tx.commit();
});
} catch (err) {
console.error('ERROR:', err);
} finally {
await database.close();
}
}
executeSqlWithTimeout();
Python
# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
def write(transaction):
# Insert a record and configure the statement timeout to 60 seconds
# 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.
row_ct = transaction.execute_update(
"INSERT INTO Singers (SingerId, FirstName, LastName) "
" VALUES (110, 'George', 'Washington')",
timeout=60,
)
print("{} record(s) inserted.".format(row_ct))
database.run_in_transaction(write)