Directed reads Stay organized with collections Save and categorize content based on your preferences.
The page describes Spanner directed reads and how to use it.
Directed reads in Spanner provide the flexibility to routeread-only transactionsandsingle reads to a specificreplica type or region within a dual-region or multi-region instanceconfiguration or a custom regional configuration with optional read-onlyregion(s).
Benefits
Directed reads offer the following benefits:
- Provide more control over load-balancing workloads across multiple regions toachieve more uniform CPU utilization and avoid over-provisioning ofSpanner instances.
- Enable workload isolation. You can direct your analytics workloads andchange streams reads to specificSpanner replicas to minimize impact to transactional workloadsrunning on the same Spanner database.
Supported query operations
| Query operations | Are directed reads supported? |
|---|---|
| Stale read | Yes |
| Strong read | Yes |
| Read-write transaction | No |
Directed reads are not supported forread-write transactionsandpartitioned DML types of bulk updates. Thisis because read-write transactions must be processed in the leader region. Ifdirected reads are used in a read-write transaction, the transaction fails withaBAD_REQUEST error.
Limitations
Spanner directed reads have the following limitations:
- You can only use directed reads in a Spanner instance that isin adual-region instance configurationormulti-region instance configurationor acustom regional configuration with optional read-only region(s).
- You can't use directed reads with read-write requests because writerequests are always served by the leader region.
- You can't use directed reads in the Google Cloud console or Google Cloud CLI. It isavailable usingREST andRPC APIs and the Spannerclient libraries.
- You can specify a maximum of 10 replicas in a single directed read.
Before you begin
Consider the following before you use directed reads:
- The application might incur additional latency if you are routing reads to areplica or region other than the one closest to the application.
- You can route traffic based on:
- Region name (For example:
us-central1). - Replica type (Possible values:
READ_ONLYandREAD_WRITE).
- Region name (For example:
- The auto failover option in directed reads is enabled by default. When autofailover option is enabled and all of the specified replicas are unavailableor unhealthy, Spanner routes requests to a replica outside the
includeReplicaslist. If you disable the auto failover option and all ofthe specified replicas are unavailable or unhealthy, the directed readsrequest fails.
Directed reads parameters
If you're using the REST or RPC API to perform directed reads, you must definethese fields in thedirectedReadOptions parameter. You can only include one ofincludeReplicas orexcludeReplicas, not both.
includeReplicas: Contains a repeated set ofreplicaSelections. This listindicates the order in which directed reads to specific regions or replicatypes should be considered. You can specify a maximum of 10includeReplicas.replicaSelections: Consists of thelocationor replicatypeservingthe directed reads request. If you useincludeReplicas, you must provide at least one of the following fields:location: The location serving the directed reads request. Thelocation must be one of the regions within the dual-region ormulti-region configuration of your database. If the location is not oneof the regions within the dual-region or multi-region configuration ofyour database, requests won't be routed as expected. Instead, they areserved by the nearest region. For example, you can direct reads to thelocationus-central1on a database in the multi-region instanceconfigurationnam6.You can also specify the
locationparameter with aleaderornon-leaderstring literal. If you input theleadervalue,Spanner directs your requests to the database'sleader replica. Conversely,if you input thenon-leadervalue, Spanner fulfillsthe request in the nearest non-leader replica.type: The replica type serving the directed reads request. Possibletypes includeREAD_WRITEandREAD_ONLY.
autoFailoverDisabled: By default, this is set toFalse, which means autofailover is enabled. When auto failover option is enabled, and all of thespecified replicas are unavailable or unhealthy, Spannerroutes requests to a replica outside theincludeReplicaslist. If youdisable the auto failover option and all of the specified replicas areunavailable or unhealthy, the directed reads request fails. Possible valuesincludeTRUEfor disabled andFALSEfor enabled.
excludeReplicas: Contains a repeated set ofreplicaSelectionsthatis excluded from serving requests. Spanner doesn't routerequests to replicas in this list.replicaSelections: The location or replica type that is excludedfrom serving the directed reads request. If you useexcludeReplicas,you must provide at least one of the following fields:location: The location that is excluded from serving thedirected reads request.type: The replica type that is excluded from serving the directedreads request. Possible types includeREAD_WRITEandREAD_ONLY.
To see an example of what a REST request body looks like, click theREST tab in theUse directed reads section.
Use directed reads
You can use the Spanner client libraries and REST and RPC APIs toperform directed reads.
Client libraries
C++
voidDirectedRead(std::stringconst&project_id,std::stringconst&instance_id,std::stringconst&database_id){namespacespanner=::google::cloud::spanner;// Create a client with a DirectedReadOption.autoclient=spanner::Client(spanner::MakeConnection(spanner::Database(project_id,instance_id,database_id)),google::cloud::Options{}.set<spanner::DirectedReadOption>(spanner::ExcludeReplicas({spanner::ReplicaSelection("us-east4")})));spanner::SqlStatementselect("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");usingRowType=std::tuple<std::int64_t,std::int64_t,std::string>;// A DirectedReadOption on the operation will override the option set// at the client level.autorows=client.ExecuteQuery(std::move(select),google::cloud::Options{}.set<spanner::DirectedReadOption>(spanner::IncludeReplicas({spanner::ReplicaSelection(spanner::ReplicaType::kReadWrite)},/*auto_failover_disabled=*/true)));for(auto&row:spanner::StreamOf<RowType>(rows)){if(!row)throwstd::move(row).status();std::cout <<"SingerId: " <<std::get<0>(*row) <<" AlbumId: " <<std::get<1>(*row) <<" AlbumTitle: " <<std::get<2>(*row) <<"\n";}std::cout <<"Read completed for [spanner_directed_read]\n";}C#
usingGoogle.Cloud.Spanner.Data;usingGoogle.Cloud.Spanner.V1;usingSystem.Collections.Generic;usingSystem.Threading.Tasks;publicclassDirectedReadsAsyncSample{publicclassAlbum{publicintSingerId{get;set;}publicintAlbumId{get;set;}publicstringAlbumTitle{get;set;}}publicasyncTask<List<Album>>DirectedReadsAsync(stringprojectId,stringinstanceId,stringdatabaseId){stringconnectionString=$"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";usingvarconnection=newSpannerConnection(connectionString);usingvarcmd=connection.CreateSelectCommand("SELECT SingerId, AlbumId, AlbumTitle FROM Albums");// Set directed read options on a query or read command.cmd.DirectedReadOptions=newDirectedReadOptions{IncludeReplicas=newDirectedReadOptions.Types.IncludeReplicas{AutoFailoverDisabled=true,ReplicaSelections={newDirectedReadOptions.Types.ReplicaSelection{Location="us-central1",Type=DirectedReadOptions.Types.ReplicaSelection.Types.Type.ReadOnly}}}};varalbums=newList<Album>();usingvarreader=awaitcmd.ExecuteReaderAsync();while(awaitreader.ReadAsync()){albums.Add(newAlbum{AlbumId=reader.GetFieldValue<int>("AlbumId"),SingerId=reader.GetFieldValue<int>("SingerId"),AlbumTitle=reader.GetFieldValue<string>("AlbumTitle")});}returnalbums;}}Go
import("context""fmt""io""cloud.google.com/go/spanner"sppb"cloud.google.com/go/spanner/apiv1/spannerpb""google.golang.org/api/iterator")//Shows how to run a query with directed read options.//Only one of ExcludeReplicas or IncludeReplicas can be set//Each accepts a list of ReplicaSelections which contains Location and Type//* `location` - The location must be one of the regions within the//multi-region configuration of your database.//* `type` - The type of the replica//Some examples of using replica_selectors are://* `location:us-east1` --> The "us-east1" replica(s) of any available type//will be used to process the request.//* `type:READ_ONLY` --> The "READ_ONLY" type replica(s) in nearest//available location will be used to process the//request.//* `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)//in location "us-east1" will be used to process the request.//IncludeReplicas also contains an option for AutoFailoverDisabled which when set//Spanner will not route requests to a replica outside the//IncludeReplicas list when all the specified replicas are unavailable//or unhealthy. The default value is `false`funcdirectedReadOptions(wio.Writer,dbstring)error{// db = `projects/<project>/instances/<instance-id>/database/<database-id>`ctx:=context.Background()directedReadOptionsForClient:=&sppb.DirectedReadOptions{Replicas:&sppb.DirectedReadOptions_ExcludeReplicas_{ExcludeReplicas:&sppb.DirectedReadOptions_ExcludeReplicas{ReplicaSelections:[]*sppb.DirectedReadOptions_ReplicaSelection{{Location:"us-east4",},},},},}// DirectedReadOptions can be set at client level and will be used in all read-only transaction requestsclient,err:=spanner.NewClientWithConfig(ctx,db,spanner.ClientConfig{DirectedReadOptions:directedReadOptionsForClient})iferr!=nil{returnerr}deferclient.Close()// DirectedReadOptions set at Request level will override the options set at Client level.directedReadOptionsForRequest:=&sppb.DirectedReadOptions{Replicas:&sppb.DirectedReadOptions_IncludeReplicas_{IncludeReplicas:&sppb.DirectedReadOptions_IncludeReplicas{ReplicaSelections:[]*sppb.DirectedReadOptions_ReplicaSelection{{Type:sppb.DirectedReadOptions_ReplicaSelection_READ_ONLY,},},AutoFailoverDisabled:true,},},}statement:=spanner.Statement{SQL:`SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}// // Read rows while passing directedReadOptions directly to the query.iter:=client.Single().QueryWithOptions(ctx,statement,spanner.QueryOptions{DirectedReadOptions:directedReadOptionsForRequest})deferiter.Stop()for{row,err:=iter.Next()iferr==iterator.Done{returnnil}iferr!=nil{returnerr}varsingerID,albumIDint64varalbumTitlestringiferr:=row.Columns(&singerID,&albumID,&albumTitle);err!=nil{returnerr}fmt.Fprintf(w,"%d %d %s\n",singerID,albumID,albumTitle)}}Java
importcom.google.cloud.spanner.DatabaseClient;importcom.google.cloud.spanner.DatabaseId;importcom.google.cloud.spanner.Options;importcom.google.cloud.spanner.ResultSet;importcom.google.cloud.spanner.Spanner;importcom.google.cloud.spanner.SpannerOptions;importcom.google.cloud.spanner.Statement;importcom.google.spanner.v1.DirectedReadOptions;importcom.google.spanner.v1.DirectedReadOptions.ExcludeReplicas;importcom.google.spanner.v1.DirectedReadOptions.IncludeReplicas;importcom.google.spanner.v1.DirectedReadOptions.ReplicaSelection;publicclassDirectedReadSample{staticvoiddirectedRead(){// TODO(developer): Replace these variables before running the sample.finalStringprojectId="my-project";finalStringinstanceId="my-instance";finalStringdatabaseId="my-database";directedRead(projectId,instanceId,databaseId);}staticvoiddirectedRead(StringprojectId,StringinstanceId,StringdatabaseId){// Only one of excludeReplicas or includeReplicas can be set// Each accepts a list of replicaSelections which contains location and type// * `location` - The location must be one of the regions within the// multi-region configuration of your database.// * `type` - The type of the replica// Some examples of using replicaSelectors are:// * `location:us-east1` --> The "us-east1" replica(s) of any available type// will be used to process the request.// * `type:READ_ONLY` --> The "READ_ONLY" type replica(s) in nearest// . available location will be used to process the// request.// * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)// in location "us-east1" will be used to process// the request.// includeReplicas also contains an option called autoFailoverDisabled, which when set to true// will instruct Spanner to not route requests to a replica outside the// includeReplicas list when all the specified replicas are unavailable// or unhealthy. Default value is `false`.finalDirectedReadOptionsdirectedReadOptionsForClient=DirectedReadOptions.newBuilder().setExcludeReplicas(ExcludeReplicas.newBuilder().addReplicaSelections(ReplicaSelection.newBuilder().setLocation("us-east4").build()).build()).build();// You can set default `DirectedReadOptions` for a Spanner client. These options will be applied// to all read-only transactions that are executed by this client, unless specific// DirectedReadOptions are set for a query.// Directed read can only be used for read-only transactions. The default options will be// ignored for any read/write transaction that the client executes.try(Spannerspanner=SpannerOptions.newBuilder().setProjectId(projectId).setDirectedReadOptions(directedReadOptionsForClient).build().getService()){finalDatabaseClientdbClient=spanner.getDatabaseClient(DatabaseId.of(projectId,instanceId,databaseId));// DirectedReadOptions at request level will override the options set at// client level (through SpannerOptions).finalDirectedReadOptionsdirectedReadOptionsForRequest=DirectedReadOptions.newBuilder().setIncludeReplicas(IncludeReplicas.newBuilder().addReplicaSelections(ReplicaSelection.newBuilder().setType(ReplicaSelection.Type.READ_WRITE).build()).setAutoFailoverDisabled(true).build()).build();// Read rows while passing DirectedReadOptions directly to the query.try(ResultSetrs=dbClient.singleUse().executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"),Options.directedRead(directedReadOptionsForRequest))){while(rs.next()){System.out.printf("SingerId: %d, AlbumId: %d, AlbumTitle: %s\n",rs.getLong(0),rs.getLong(1),rs.getString(2));}System.out.println("Successfully executed read-only transaction with directedReadOptions");}}}}Node.js
// Imports the Google Cloud Spanner client libraryconst{Spanner,protos}=require('@google-cloud/spanner');// Only one of excludeReplicas or includeReplicas can be set// Each accepts a list of replicaSelections which contains location and type// * `location` - The location must be one of the regions within the// multi-region configuration of your database.// * `type` - The type of the replica// Some examples of using replicaSelectors are:// * `location:us-east1` --> The "us-east1" replica(s) of any available type// will be used to process the request.// * `type:READ_ONLY` --> The "READ_ONLY" type replica(s) in nearest//. available location will be used to process the// request.// * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)// in location "us-east1" will be used to process// the request.// includeReplicas also contains an option for autoFailover which when set// Spanner will not route requests to a replica outside the// includeReplicas list when all the specified replicas are unavailable// or unhealthy. The default value is `false`constdirectedReadOptionsForClient={excludeReplicas:{replicaSelections:[{location:'us-east4',},],},};// Instantiates a client with directedReadOptionsconstspanner=newSpanner({projectId:projectId,directedReadOptions:directedReadOptionsForClient,});asyncfunctionspannerDirectedReads(){// Gets a reference to a Cloud Spanner instance and backupconstinstance=spanner.instance(instanceId);constdatabase=instance.database(databaseId);constdirectedReadOptionsForRequest={includeReplicas:{replicaSelections:[{type:protos.google.spanner.v1.DirectedReadOptions.ReplicaSelection.Type.READ_ONLY,},],autoFailoverDisabled:true,},};awaitdatabase.getSnapshot(async(err,transaction)=>{if(err){console.error(err);return;}try{// Read rows while passing directedReadOptions directly to the query.// These will override the options passed at Client level.const[rows]=awaittransaction.run({sql:'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',directedReadOptions:directedReadOptionsForRequest,});rows.forEach(row=>{constjson=row.toJSON();console.log(`SingerId:${json.SingerId}, AlbumId:${json.AlbumId}, AlbumTitle:${json.AlbumTitle}`,);});console.log('Successfully executed read-only transaction with directedReadOptions',);}catch(err){console.error('ERROR:',err);}finally{transaction.end();// Close the database when finished.awaitdatabase.close();}});}spannerDirectedReads();PHP
use Google\Cloud\Spanner\SpannerClient;use Google\Cloud\Spanner\V1\DirectedReadOptions\ReplicaSelection\Type as ReplicaType;/** * Queries sample data from the database with directed read options. * Example: * ``` * directed_read($instanceId, $databaseId); * ``` * * @param string $instanceId The Spanner instance ID. * @param string $databaseId The Spanner database ID. */function directed_read(string $instanceId, string $databaseId): void{ $directedReadOptionsForClient = [ 'directedReadOptions' => [ 'excludeReplicas' => [ 'replicaSelections' => [ [ 'location' => 'us-east4' ] ] ] ] ]; $directedReadOptionsForRequest = [ 'directedReadOptions' => [ 'includeReplicas' => [ 'replicaSelections' => [ [ 'type' => ReplicaType::READ_WRITE ] ], 'autoFailoverDisabled' => true ] ] ]; $spanner = new SpannerClient($directedReadOptionsForClient); $instance = $spanner->instance($instanceId); $database = $instance->database($databaseId); $snapshot = $database->snapshot(); // directedReadOptions at Request level will override the options set at // Client level $results = $snapshot->execute( 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums', $directedReadOptionsForRequest ); foreach ($results as $row) { printf('SingerId: %s, AlbumId: %s, AlbumTitle: %s' . PHP_EOL, $row['SingerId'], $row['AlbumId'], $row['AlbumTitle']); }}Python
# instance_id = "your-spanner-instance"# database_id = "your-spanner-db-id"directed_read_options_for_client={"exclude_replicas":{"replica_selections":[{"location":"us-east4",},],},}# directed_read_options can be set at client level and will be used in all# read-only transaction requestsspanner_client=spanner.Client(directed_read_options=directed_read_options_for_client)instance=spanner_client.instance(instance_id)database=instance.database(database_id)directed_read_options_for_request={"include_replicas":{"replica_selections":[{"type_":DirectedReadOptions.ReplicaSelection.Type.READ_ONLY,},],"auto_failover_disabled":True,},}withdatabase.snapshot()assnapshot:# Read rows while passing directed_read_options directly to the query.# These will override the options passed at Client level.results=snapshot.execute_sql("SELECT SingerId, AlbumId, AlbumTitle FROM Albums",directed_read_options=directed_read_options_for_request,)forrowinresults:print("SingerId:{}, AlbumId:{}, AlbumTitle:{}".format(*row))Ruby
require"google/cloud/spanner"### This is a snippet for showcasing how to pass in directed read options.## @param project_id [String] The ID of the Google Cloud project.# @param instance_id [String] The ID of the spanner instance.# @param database_id [String] The ID of the database.#defspanner_directed_readproject_id:,instance_id:,database_id:# Only one of exclude_replicas or include_replicas can be set.# Each accepts a list of replica_selections which contains location and type# * `location` - The location must be one of the regions within the# multi-region configuration of your database.# * `type` - The type of the replica# Some examples of using replicaSelectors are:# * `location:us-east1` --> The "us-east1" replica(s) of any available type# will be used to process the request.# * `type:READ_ONLY` --> The "READ_ONLY" type replica(s) in the nearest# . available location will be used to process the# request.# * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)# in location "us-east1" will be used to process# the request.# include_replicas also contains an option for auto_failover_disabled. If set# Spanner will not route requests to a replica outside the# include_replicas list even if all the specified replicas are# unavailable or unhealthy. The default value is `false`.directed_read_options_for_client={include_replicas:{replica_selections:[{location:"us-east4"}]}}# Instantiates a client with directedReadOptionsspanner=Google::Cloud::Spanner.newproject:project_idclient=spanner.clientinstance_id,database_id,directed_read_options:directed_read_options_for_clientdirected_read_options={include_replicas:{replica_selections:[{type:"READ_WRITE"}],auto_failover_disabled:true}}result=client.execute_sql"SELECT SingerId, AlbumId, AlbumTitle FROM Albums",directed_read_options:directed_read_optionsresult.rows.eachdo|row|puts"SingerId:#{row[:SingerId]}"puts"AlbumId:#{row[:AlbumId]}"puts"AlbumTitle:#{row[:AlbumTitle]}"endputs"Successfully executed read-only transaction with directed_read_options"endREST
You can use the following REST APIs to perform directed reads:
For example, to perform directed reads inus-central1 usingexecuteSQL:
Forsession, enter:
projects/<VAR>PROJECT-ID</VAR>/instances/<VAR>INSTANCE-ID</VAR>/databases/<VAR>DATABASE-ID</VAR>/sessions/<VAR>SESSION-ID</VAR>Replace the following:
- PROJECT-ID: the project ID.
- INSTANCE-ID: the instance ID.
- DATABASE-ID: the database ID.
- SESSION-ID: the session ID.You receive the
SESSION-IDvalue when youcreate a session.
ForRequest body, use the following:
{ "directedReadOptions": { "includeReplicas": { "replicaSelections": [ { "location": "us-central1", } ] } }, "sql": "SELECT SingerId, AlbumId, AlbumTitle FROM Albums"}ClickExecute. The response shows the query results.
RPC
You can use the following RPC APIs to perform directed reads:
Monitoring
Spanner provides a latency metric to help you monitor directedreads activities in your instances. The metric is available inCloud Monitoring.
spanner.googleapis.com/api/read_request_latencies_by_serving_location
You can filter this metric using the/serving_location or/is_directed_read fields. The/serving location field indicates the locationof the Spanner server where the request is served from. The/is_directed_read field indicates whether the directed reads option isenabled.
For a full list of available metrics, seemetrics list for Spanner.
What's next
- Learn how to performReads outside of transactions.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-19 UTC.