Using timeouts

This section describes available timeouts and provides examples of their usage in various programming languages.

Prerequisites for using timeouts

The timeout mechanism in YDB is designed to:

  • Make sure the query execution time doesn't exceed a certain interval after which its result is not interesting for further use.
  • Detect network connectivity issues.

Both of these use cases are important for ensuring the fault tolerance of the entire system. Let's take a closer look at timeouts.

Operation timeout

The operation_timeout value shows the time during which the query result is interesting to the user. If the operation fails during this time, the server returns an error with the Timeout code and tries to terminate the query, but its cancellation is not guaranteed. So the query that the user was returned the Timeout error for can be both successfully executed on the server and canceled.

Timeout for canceling an operation

The cancel_after value shows the time after which the server will start canceling the query, if it can be canceled. If canceled, the server returns the Cancelled error code.

Transport timeout

The client must set a transport timeout for each query. This value lets you determine the amount of time that the client is ready to wait for a response from the server. If the server doesn't respond during this time, the client will get a transport error with the DeadlineExceeded code. Be sure to set such a client timeout value that won't trigger transport timeouts under the normal operation of the application and network.

Using timeouts

We recommend that you always set an operation timeout and transport timeout. The value of the transport timeout should be 50-100 milliseconds more than that of the operation timeout, that way there is some time left for the client to get a server error with the Timeout code.

Timeout usage example:

import ydb

def execute_in_tx(session, query):
  settings = ydb.BaseRequestSettings()
  settings = settings.with_timeout(0.5)  # transport timeout
  settings = settings.with_operation_timeout(0.4)  # operation timeout
  settings = settings.with_cancel_after(0.4)  # cancel after timeout
  session.transaction().execute(
      query,
      commit_tx=True,
      settings=settings,
  )
#include <ydb/public/sdk/cpp/client/ydb.h>
#include <ydb/public/sdk/cpp/client/ydb_table.h>
#include <ydb/public/sdk/cpp/client/ydb_value.h>

using namespace NYdb;
using namespace NYdb::NTable;

TAsyncStatus ExecuteInTx(TSession& session, TString query, TParams params) {
  return session.ExecuteDataQuery(
      query
      , TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()
      , TExecDataQuerySettings()
      .OperationTimeout(TDuration::MilliSeconds(300))  // operation timeout
      .ClientTimeout(TDuration::MilliSeconds(400))   // transport timeout
      .CancelAfter(TDuration::MilliSeconds(300)));  // cancel after timeout
}
import (
  "context"

  ydb "github.com/ydb-platform/ydb-go-sdk/v3"
  "github.com/ydb-platform/ydb-go-sdk/v3/table"
)

func executeInTx(ctx context.Context, s table.Session, query string) {
  ctx, cancel := context.WithTimeout(ctx, time.Millisecond*300) // client and by default operation timeout
  defer cancel()
  ctx = ydb.WithOperationTimeout(ctx, time.Millisecond*400)     // operation timeout override
  ctx = ydb.WithOperationCancelAfter(ctx, time.Millisecond*300) // cancel after timeout
  tx := table.TxControl(table.BeginTx(table.WithSerializableReadWrite()), table.CommitTx())
  _, res, err := s.Execute(ctx, tx, query, table.NewQueryParameters())
}