This page describes how to set a timeout for a single statement execution using
the Spanner client libraries. This can be used to override the
default timeout configuration of the client library. The statement fails with a DEADLINE_EXCEEDED
error if the statement cannot finish within the given
timeout value.
These samples show how to set a timeout for a single statement execution in the Cloud Spanner client library.
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
)