Configure transaction timeout

This page describes how to set a timeout for transactions using the Spanner client libraries. The transaction fails with a DEADLINE_EXCEEDED error if the transaction can't finish within the given timeout value.

You can set timeout values for transactions and for RPC request statements . Setting a longer timeout value for the transaction than the timeout value for the statement that is executed in the transaction doesn't increase the timeout for the statement, which is constrained by its own timeout value.

Also, if the timeout error occurs during the execution of the Commit request, it's still possible that the transaction was committed.

You can set a transaction timeout using the Go, Java, Python, and Node.js client libraries.

Go

  import 
  
 ( 
  
 "context" 
  
 "errors" 
  
 "fmt" 
  
 "io" 
  
 "time" 
  
 "cloud.google.com/go/spanner" 
  
 "google.golang.org/api/iterator" 
  
 "google.golang.org/grpc/codes" 
 ) 
 func 
  
 transactionTimeout 
 ( 
 w 
  
 io 
 . 
 Writer 
 , 
  
 db 
  
 string 
 ) 
  
 error 
  
 { 
  
 ctx 
  
 := 
  
 context 
 . 
 Background 
 () 
  
 client 
 , 
  
 err 
  
 := 
  
 spanner 
 . 
 NewClient 
 ( 
 ctx 
 , 
  
 db 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 err 
  
 } 
  
 defer 
  
 client 
 . 
 Close 
 () 
  
 // Create a context with a 60-second timeout and use this context to run a read/write transaction. 
  
 // This context timeout will be applied to the entire transaction, and the transaction will fail 
  
 // if it cannot finish within the specified timeout value. The Spanner client library applies the 
  
 // (remainder of the) timeout to each statement that is executed in the transaction. 
  
 ctx 
 , 
  
 cancel 
  
 := 
  
 context 
 . 
 WithTimeout 
 ( 
 context 
 . 
 Background 
 (), 
  
 60 
 * 
 time 
 . 
 Second 
 ) 
  
 defer 
  
 cancel 
 () 
  
 _ 
 , 
  
 err 
  
 = 
  
 client 
 . 
 ReadWriteTransaction 
 ( 
 ctx 
 , 
  
 func 
 ( 
 ctx 
  
 context 
 . 
 Context 
 , 
  
 txn 
  
 * 
 spanner 
 . 
 ReadWriteTransaction 
 ) 
  
 error 
  
 { 
  
 selectStmt 
  
 := 
  
 spanner 
 . 
  Statement 
 
 { 
  
 SQL 
 : 
  
 `SELECT SingerId, FirstName, LastName FROM Singers ORDER BY LastName, FirstName` 
 , 
  
 } 
  
 // The context that is passed in to the transaction function should be used for each statement 
  
 // is executed. 
  
 iter 
  
 := 
  
 txn 
 . 
 Query 
 ( 
 ctx 
 , 
  
 selectStmt 
 ) 
  
 defer 
  
 iter 
 . 
 Stop 
 () 
  
 for 
  
 { 
  
 row 
 , 
  
 err 
  
 := 
  
 iter 
 . 
 Next 
 () 
  
 if 
  
 errors 
 . 
 Is 
 ( 
 err 
 , 
  
 iterator 
 . 
 Done 
 ) 
  
 { 
  
 break 
  
 } 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 err 
  
 } 
  
 var 
  
 singerID 
  
 int64 
  
 var 
  
 firstName 
 , 
  
 lastName 
  
 string 
  
 if 
  
 err 
  
 := 
  
 row 
 . 
  Columns 
 
 ( 
& singerID 
 , 
  
& firstName 
 , 
  
& lastName 
 ); 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 err 
  
 } 
  
 fmt 
 . 
 Fprintf 
 ( 
 w 
 , 
  
 "%d %s %s\n" 
 , 
  
 singerID 
 , 
  
 firstName 
 , 
  
 lastName 
 ) 
  
 } 
  
 stmt 
  
 := 
  
 spanner 
 . 
  Statement 
 
 { 
  
 SQL 
 : 
  
 `INSERT INTO Singers (SingerId, FirstName, LastName) 
 VALUES (38, 'George', 'Washington')` 
 , 
  
 } 
  
 rowCount 
 , 
  
 err 
  
 := 
  
 txn 
 . 
 Update 
 ( 
 ctx 
 , 
  
 stmt 
 ) 
  
 if 
  
 err 
  
 != 
  
 nil 
  
 { 
  
 return 
  
 err 
  
 } 
  
 fmt 
 . 
 Fprintf 
 ( 
 w 
 , 
  
 "%d record(s) inserted.\n" 
 , 
  
 rowCount 
 ) 
  
 return 
  
 nil 
  
 }) 
  
 // Check if an error was returned by the transaction. 
  
 // The spanner.ErrCode(err) function will return codes.OK if err == nil. 
  
 code 
  
 := 
  
 spanner 
 . 
  ErrCode 
 
 ( 
 err 
 ) 
  
 if 
  
 code 
  
 == 
  
 codes 
 . 
 OK 
  
 { 
  
 fmt 
 . 
 Fprintf 
 ( 
 w 
 , 
  
 "Transaction with timeout was executed successfully\n" 
 ) 
  
 } 
  
 else 
  
 if 
  
 code 
  
 == 
  
 codes 
 . 
 DeadlineExceeded 
  
 { 
  
 fmt 
 . 
 Fprintf 
 ( 
 w 
 , 
  
 "Transaction timed out\n" 
 ) 
  
 } 
  
 else 
  
 { 
  
 fmt 
 . 
 Fprintf 
 ( 
 w 
 , 
  
 "Transaction failed with error code %v\n" 
 , 
  
 code 
 ) 
  
 } 
  
 return 
  
 err 
 } 
 

Java

  import 
  
 com.google.cloud.spanner. DatabaseClient 
 
 ; 
 import 
  
 com.google.cloud.spanner. DatabaseId 
 
 ; 
 import 
  
 com.google.cloud.spanner. ResultSet 
 
 ; 
 import 
  
 com.google.cloud.spanner. Spanner 
 
 ; 
 import 
  
 com.google.cloud.spanner. SpannerOptions 
 
 ; 
 import 
  
 com.google.cloud.spanner. Statement 
 
 ; 
 import 
  
 io.grpc.Context 
 ; 
 import 
  
 io.grpc.Context.CancellableContext 
 ; 
 import 
  
 io.grpc.Deadline 
 ; 
 import 
  
 java.util.concurrent.Executors 
 ; 
 import 
  
 java.util.concurrent.ScheduledExecutorService 
 ; 
 import 
  
 java.util.concurrent.TimeUnit 
 ; 
 /** 
 * Sample showing how to set a timeout for an entire transaction for the Cloud Spanner Java client. 
 */ 
 class 
 TransactionTimeoutExample 
  
 { 
  
 static 
  
 void 
  
 executeTransactionWithTimeout 
 () 
  
 { 
  
 // TODO(developer): Replace these variables before running the sample. 
  
 String 
  
 projectId 
  
 = 
  
 "my-project" 
 ; 
  
 String 
  
 instanceId 
  
 = 
  
 "my-instance" 
 ; 
  
 String 
  
 databaseId 
  
 = 
  
 "my-database" 
 ; 
  
 executeTransactionWithTimeout 
 ( 
 projectId 
 , 
  
 instanceId 
 , 
  
 databaseId 
 , 
  
 60L 
 , 
  
 TimeUnit 
 . 
 SECONDS 
 ); 
  
 } 
  
 // Execute a read/write transaction with a timeout for the entire transaction. 
  
 static 
  
 void 
  
 executeTransactionWithTimeout 
 ( 
  
 String 
  
 projectId 
 , 
  
 String 
  
 instanceId 
 , 
  
 String 
  
 databaseId 
 , 
  
 long 
  
 timeoutValue 
 , 
  
 TimeUnit 
  
 timeoutUnit 
 ) 
  
 { 
  
 try 
  
 ( 
  Spanner 
 
  
 spanner 
  
 = 
  
  SpannerOptions 
 
 . 
 newBuilder 
 (). 
 setProjectId 
 ( 
 projectId 
 ). 
 build 
 () 
  
 . 
 getService 
 ()) 
  
 { 
  
  DatabaseClient 
 
  
 client 
  
 = 
  
 spanner 
 . 
 getDatabaseClient 
 ( 
  DatabaseId 
 
 . 
 of 
 ( 
 projectId 
 , 
  
 instanceId 
 , 
  
 databaseId 
 )); 
  
 // Create a gRPC context with a deadline and with cancellation. 
  
 // gRPC context deadlines require the use of a scheduled executor. 
  
 ScheduledExecutorService 
  
 executor 
  
 = 
  
 Executors 
 . 
 newSingleThreadScheduledExecutor 
 (); 
  
 try 
  
 ( 
 CancellableContext 
  
 context 
  
 = 
  
 Context 
 . 
 current 
 () 
  
 . 
 withDeadline 
 ( 
 Deadline 
 . 
 after 
 ( 
 timeoutValue 
 , 
  
 timeoutUnit 
 ), 
  
 executor 
 ) 
  
 . 
 withCancellation 
 ()) 
  
 { 
  
 context 
 . 
 run 
 ( 
  
 () 
  
 - 
>  
 { 
  
 client 
  
 . 
 readWriteTransaction 
 () 
  
 . 
 run 
 ( 
  
 transaction 
  
 - 
>  
 { 
  
 try 
  
 ( 
 ResultSet 
  
 resultSet 
  
 = 
  
 transaction 
 . 
 executeQuery 
 ( 
  
 Statement 
 . 
 of 
 ( 
  
 "SELECT SingerId, FirstName, LastName\n" 
  
 + 
  
 "FROM Singers\n" 
  
 + 
  
 "ORDER BY LastName, FirstName" 
 ))) 
  
 { 
  
 while 
  
 ( 
 resultSet 
 . 
 next 
 ()) 
  
 { 
  
 System 
 . 
 out 
 . 
 printf 
 ( 
  
 "%d %s %s\n" 
 , 
  
 resultSet 
 . 
 getLong 
 ( 
 "SingerId" 
 ), 
  
 resultSet 
 . 
 getString 
 ( 
 "FirstName" 
 ), 
  
 resultSet 
 . 
 getString 
 ( 
 "LastName" 
 )); 
  
 } 
  
 } 
  
 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 
  
 executeTransactionWithTimeout 
 () 
  
 { 
  
 // Gets a reference to a Cloud Spanner instance and database. 
  
 const 
  
 instance 
  
 = 
  
 spanner 
 . 
 instance 
 ( 
 instanceId 
 ); 
  
 const 
  
 database 
  
 = 
  
 instance 
 . 
 database 
 ( 
 databaseId 
 ); 
  
 const 
  
 options 
  
 = 
  
 { 
  
 timeout 
 : 
  
 60000 
 , 
  
 // 60 seconds timeout 
  
 }; 
  
 try 
  
 { 
  
 await 
  
 database 
 . 
  runTransactionAsync 
 
 ( 
 options 
 , 
  
 async 
  
 tx 
  
 = 
>  
 { 
  
 const 
  
 [ 
 results 
 ] 
  
 = 
  
 await 
  
 tx 
 . 
 run 
 ( 
  
 'SELECT SingerId, FirstName, LastName FROM Singers ORDER BY LastName, FirstName' 
 , 
  
 ); 
  
 results 
 . 
 forEach 
 ( 
 result 
  
 = 
>  
 { 
  
 console 
 . 
 log 
 ( 
  
 ` 
 ${ 
 result 
 [ 
 0 
 ]. 
 name 
 } 
 : 
 ${ 
 result 
 [ 
 0 
 ]. 
 value 
 . 
 value 
 } 
 , 
 ${ 
 result 
 [ 
 1 
 ]. 
 name 
 } 
 : 
 ${ 
 result 
 [ 
 1 
 ]. 
 value 
 } 
 , 
 ${ 
 result 
 [ 
 2 
 ]. 
 name 
 } 
 : 
 ${ 
 result 
 [ 
 2 
 ]. 
 value 
 } 
 ` 
 , 
  
 ); 
  
 }); 
  
 const 
  
 sql 
  
 = 
  
 "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (100, 'George', 'Washington')" 
 ; 
  
 const 
  
 [ 
 rowCount 
 ] 
  
 = 
  
 await 
  
 tx 
 . 
 runUpdate 
 ( 
 sql 
 ); 
  
 console 
 . 
 log 
 ( 
 ` 
 ${ 
 rowCount 
 } 
 record inserted.` 
 ); 
  
 await 
  
 tx 
 . 
 commit 
 (); 
  
 }); 
  
 } 
  
 catch 
  
 ( 
 err 
 ) 
  
 { 
  
 console 
 . 
 error 
 ( 
 'ERROR:' 
 , 
  
 err 
 ); 
  
 } 
  
 finally 
  
 { 
  
 await 
  
 database 
 . 
 close 
 (); 
  
 } 
 } 
 executeTransactionWithTimeout 
 (); 
 

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 
  
 read_then_write 
 ( 
 transaction 
 ): 
 # Read records. 
 results 
 = 
 transaction 
 . 
 execute_sql 
 ( 
 "SELECT SingerId, FirstName, LastName FROM Singers ORDER BY LastName, FirstName" 
 ) 
 for 
 result 
 in 
 results 
 : 
 print 
 ( 
 "SingerId: 
 {} 
 , FirstName: 
 {} 
 , LastName: 
 {} 
 " 
 . 
 format 
 ( 
 * 
 result 
 )) 
 # Insert a record. 
 row_ct 
 = 
 transaction 
 . 
 execute_update 
 ( 
 "INSERT INTO Singers (SingerId, FirstName, LastName) " 
 " VALUES (100, 'George', 'Washington')" 
 ) 
 print 
 ( 
 " 
 {} 
 record(s) inserted." 
 . 
 format 
 ( 
 row_ct 
 )) 
 # configure transaction timeout to 60 seconds 
 database 
 . 
 run_in_transaction 
 ( 
 read_then_write 
 , 
 timeout_secs 
 = 
 60 
 ) 
 
Create a Mobile Website
View Site in Mobile | Classic
Share by: