Skip to content

Commit 0703f41

Browse files
feat: Leader aware routing (#1783)
* feat: leader aware routing * routetoleaderenabled property and test cases * feat: Added unit test cases * test: for LAR disabled * fix: review comments * fix: review comments * fix: review comments * fix : test cases * fix: lint * feat: Default disable LAR feature --------- Co-authored-by: Astha Mohta <35952883+asthamohta@users.noreply.github.com>
1 parent 23ec060 commit 0703f41

File tree

12 files changed

+423
-35
lines changed

12 files changed

+423
-35
lines changed

src/batch-transaction.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ import * as extend from 'extend';
2020
import * as is from 'is';
2121
import {Snapshot} from './transaction';
2222
import {google} from '../protos/protos';
23-
import {Session, Database} from '.';
24-
import {CLOUD_RESOURCE_HEADER} from '../src/common';
23+
import {Session, Database, Spanner} from '.';
24+
import {
25+
CLOUD_RESOURCE_HEADER,
26+
addLeaderAwareRoutingHeader,
27+
} from '../src/common';
2528

2629
export interface TransactionIdentifier {
2730
session: string | Session;
@@ -133,12 +136,18 @@ class BatchTransaction extends Snapshot {
133136
delete reqOpts.gaxOptions;
134137
delete reqOpts.types;
135138

139+
const headers: {[k: string]: string} = {};
140+
if (this._getSpanner().routeToLeaderEnabled) {
141+
addLeaderAwareRoutingHeader(headers);
142+
}
143+
136144
this.createPartitions_(
137145
{
138146
client: 'SpannerClient',
139147
method: 'partitionQuery',
140148
reqOpts,
141149
gaxOpts: query.gaxOptions,
150+
headers: headers,
142151
},
143152
callback
144153
);
@@ -225,12 +234,18 @@ class BatchTransaction extends Snapshot {
225234
delete reqOpts.keys;
226235
delete reqOpts.ranges;
227236

237+
const headers: {[k: string]: string} = {};
238+
if (this._getSpanner().routeToLeaderEnabled) {
239+
addLeaderAwareRoutingHeader(headers);
240+
}
241+
228242
this.createPartitions_(
229243
{
230244
client: 'SpannerClient',
231245
method: 'partitionRead',
232246
reqOpts,
233247
gaxOpts: options.gaxOptions,
248+
headers: headers,
234249
},
235250
callback
236251
);

src/common.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,16 @@ export interface PagedOptionsWithFilter extends PagedOptions {
7575
* by the backend.
7676
*/
7777
export const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix';
78+
79+
/*!
80+
* HTTP header to route the requests at Leader
81+
*/
82+
export const LEADER_AWARE_ROUTING_HEADER = 'x-goog-spanner-route-to-leader';
83+
84+
/**
85+
* Add Leader aware routing header to existing header list.
86+
* @param headers Existing header list.
87+
*/
88+
export function addLeaderAwareRoutingHeader(headers: {[k: string]: string}) {
89+
headers[LEADER_AWARE_ROUTING_HEADER] = 'true';
90+
}

src/database.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@ import {
8585
RequestCallback,
8686
ResourceCallback,
8787
Schema,
88+
addLeaderAwareRoutingHeader,
8889
} from './common';
8990
import {Duplex, Readable, Transform} from 'stream';
9091
import {PreciseDate} from '@google-cloud/precise-date';
91-
import {EnumKey, RequestConfig, TranslateEnumKeys} from '.';
92+
import {EnumKey, RequestConfig, TranslateEnumKeys, Spanner} from '.';
9293
import arrify = require('arrify');
9394
import {ServiceError} from 'google-gax';
9495
import IPolicy = google.iam.v1.IPolicy;
@@ -523,13 +524,18 @@ class Database extends common.GrpcServiceObject {
523524
sessionCount: count,
524525
};
525526

527+
const headers = this.resourceHeader_;
528+
if (this._getSpanner().routeToLeaderEnabled) {
529+
addLeaderAwareRoutingHeader(headers);
530+
}
531+
526532
this.request<google.spanner.v1.IBatchCreateSessionsResponse>(
527533
{
528534
client: 'SpannerClient',
529535
method: 'batchCreateSessions',
530536
reqOpts,
531537
gaxOpts: options.gaxOptions,
532-
headers: this.resourceHeader_,
538+
headers: headers,
533539
},
534540
(err, resp) => {
535541
if (err) {
@@ -791,13 +797,18 @@ class Database extends common.GrpcServiceObject {
791797
reqOpts.session.creatorRole =
792798
options.databaseRole || this.databaseRole || null;
793799

800+
const headers = this.resourceHeader_;
801+
if (this._getSpanner().routeToLeaderEnabled) {
802+
addLeaderAwareRoutingHeader(headers);
803+
}
804+
794805
this.request<google.spanner.v1.ISession>(
795806
{
796807
client: 'SpannerClient',
797808
method: 'createSession',
798809
reqOpts,
799810
gaxOpts: options.gaxOptions,
800-
headers: this.resourceHeader_,
811+
headers: headers,
801812
},
802813
(err, resp) => {
803814
if (err) {
@@ -3261,6 +3272,17 @@ class Database extends common.GrpcServiceObject {
32613272
const databaseName = name.split('/').pop();
32623273
return instanceName + '/databases/' + databaseName;
32633274
}
3275+
3276+
/**
3277+
* Gets the Spanner object
3278+
*
3279+
* @private
3280+
*
3281+
* @returns {Spanner}
3282+
*/
3283+
private _getSpanner(): Spanner {
3284+
return this.instance.parent as Spanner;
3285+
}
32643286
}
32653287

32663288
/*! Developer Documentation

src/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,17 @@ export type GetInstanceConfigOperationsCallback = PagedCallback<
106106
instanceAdmin.spanner.admin.instance.v1.IListInstanceConfigOperationsResponse
107107
>;
108108

109+
/**
110+
* Session pool configuration options.
111+
* @property {boolean} [routeToLeaderEnabled=True] If set to false leader aware routing will be disabled.
112+
* Disabling leader aware routing would route all requests in RW/PDML transactions to any region.
113+
*/
109114
export interface SpannerOptions extends GrpcClientOptions {
110115
apiEndpoint?: string;
111116
servicePath?: string;
112117
port?: number;
113118
sslCreds?: grpc.ChannelCredentials;
119+
routeToLeaderEnabled?: boolean;
114120
}
115121
export interface RequestConfig {
116122
client: string;
@@ -210,6 +216,7 @@ class Spanner extends GrpcService {
210216
projectIdReplaced_: boolean;
211217
projectFormattedName_: string;
212218
resourceHeader_: {[k: string]: string};
219+
routeToLeaderEnabled = false;
213220

214221
/**
215222
* Placeholder used to auto populate a column with the commit timestamp.
@@ -310,6 +317,11 @@ class Spanner extends GrpcService {
310317
packageJson: require('../../package.json'),
311318
} as {} as GrpcServiceConfig;
312319
super(config, options);
320+
321+
if (options.routeToLeaderEnabled === true) {
322+
this.routeToLeaderEnabled = true;
323+
}
324+
313325
this.options = options;
314326
this.auth = new GoogleAuth(this.options);
315327
this.clients_ = new Map();

src/session.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ import {
3636
CreateSessionOptions,
3737
} from './database';
3838
import {ServiceObjectConfig} from '@google-cloud/common';
39-
import {NormalCallback, CLOUD_RESOURCE_HEADER} from './common';
39+
import {
40+
NormalCallback,
41+
CLOUD_RESOURCE_HEADER,
42+
addLeaderAwareRoutingHeader,
43+
} from './common';
4044
import {grpc, CallOptions} from 'google-gax';
4145
import IRequestOptions = google.spanner.v1.IRequestOptions;
46+
import {Spanner} from '.';
4247

4348
export type GetSessionResponse = [Session, r.Response];
4449

@@ -378,13 +383,18 @@ export class Session extends common.GrpcServiceObject {
378383
const reqOpts = {
379384
name: this.formattedName_,
380385
};
386+
387+
const headers = this.resourceHeader_;
388+
if (this._getSpanner().routeToLeaderEnabled) {
389+
addLeaderAwareRoutingHeader(headers);
390+
}
381391
return this.request(
382392
{
383393
client: 'SpannerClient',
384394
method: 'getSession',
385395
reqOpts,
386396
gaxOpts,
387-
headers: this.resourceHeader_,
397+
headers: headers,
388398
},
389399
(err, resp) => {
390400
if (resp) {
@@ -512,6 +522,17 @@ export class Session extends common.GrpcServiceObject {
512522
const sessionName = name.split('/').pop();
513523
return databaseName + '/sessions/' + sessionName;
514524
}
525+
526+
/**
527+
* Gets the Spanner object
528+
*
529+
* @private
530+
*
531+
* @returns {Spanner}
532+
*/
533+
private _getSpanner(): Spanner {
534+
return this.parent.parent.parent as Spanner;
535+
}
515536
}
516537

517538
/*! Developer Documentation

0 commit comments

Comments
 (0)