From 80539224898e9683c37cf188b709b5b98efffbc7 Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Tue, 24 Jan 2023 18:34:33 +0530 Subject: [PATCH 01/10] feat: leader aware routing --- src/batch-transaction.ts | 13 ++++++++++++- src/common.ts | 5 +++++ src/database.ts | 14 ++++++++++++-- src/index.ts | 14 ++++++++++++++ src/session.ts | 10 ++++++++-- src/transaction.ts | 39 ++++++++++++++++++++++++++++++++------- 6 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/batch-transaction.ts b/src/batch-transaction.ts index c6c7b6702..ec0fdefa2 100644 --- a/src/batch-transaction.ts +++ b/src/batch-transaction.ts @@ -21,7 +21,8 @@ import * as is from 'is'; import {Snapshot} from './transaction'; import {google} from '../protos/protos'; import {Session, Database} from '.'; -import {CLOUD_RESOURCE_HEADER} from '../src/common'; +import {CLOUD_RESOURCE_HEADER, LEADER_AWARE_ROUTING_HEADER} from '../src/common'; +import {Spanner} from '.'; export interface TransactionIdentifier { session: string | Session; @@ -132,6 +133,10 @@ class BatchTransaction extends Snapshot { delete reqOpts.gaxOptions; delete reqOpts.types; + + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader + }; this.createPartitions_( { @@ -139,6 +144,7 @@ class BatchTransaction extends Snapshot { method: 'partitionQuery', reqOpts, gaxOpts: query.gaxOptions, + headers: leaderAwareRoutingHeader, }, callback ); @@ -225,12 +231,17 @@ class BatchTransaction extends Snapshot { delete reqOpts.keys; delete reqOpts.ranges; + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader + }; + this.createPartitions_( { client: 'SpannerClient', method: 'partitionRead', reqOpts, gaxOpts: options.gaxOptions, + headers: leaderAwareRoutingHeader }, callback ); diff --git a/src/common.ts b/src/common.ts index ddc79d4fd..f8e91f9ab 100644 --- a/src/common.ts +++ b/src/common.ts @@ -75,3 +75,8 @@ export interface PagedOptionsWithFilter extends PagedOptions { * by the backend. */ export const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix'; + +/*! + * HTTP header to route the requests at Leader + */ +export const LEADER_AWARE_ROUTING_HEADER = 'x-goog-spanner-route-to-leader'; \ No newline at end of file diff --git a/src/database.ts b/src/database.ts index 97a3e79dc..b92382cea 100644 --- a/src/database.ts +++ b/src/database.ts @@ -85,7 +85,9 @@ import { RequestCallback, ResourceCallback, Schema, + LEADER_AWARE_ROUTING_HEADER } from './common'; +import {Spanner} from '.'; import {Duplex, Readable, Transform} from 'stream'; import {PreciseDate} from '@google-cloud/precise-date'; import {EnumKey, RequestConfig, TranslateEnumKeys} from '.'; @@ -523,13 +525,17 @@ class Database extends common.GrpcServiceObject { sessionCount: count, }; + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.instance.parent as Spanner).routeToLeader, + }; + this.request( { client: 'SpannerClient', method: 'batchCreateSessions', reqOpts, gaxOpts: options.gaxOptions, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, (err, resp) => { if (err) { @@ -791,13 +797,17 @@ class Database extends common.GrpcServiceObject { reqOpts.session.creatorRole = options.databaseRole || this.databaseRole || null; + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.instance.parent as Spanner).routeToLeader, + }; + this.request( { client: 'SpannerClient', method: 'createSession', reqOpts, gaxOpts: options.gaxOptions, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, (err, resp) => { if (err) { diff --git a/src/index.ts b/src/index.ts index e6501d933..784325643 100644 --- a/src/index.ts +++ b/src/index.ts @@ -111,6 +111,7 @@ export interface SpannerOptions extends GrpcClientOptions { servicePath?: string; port?: number; sslCreds?: grpc.ChannelCredentials; + routeToLeaderEnabled?: boolean; } export interface RequestConfig { client: string; @@ -210,6 +211,7 @@ class Spanner extends GrpcService { projectIdReplaced_: boolean; projectFormattedName_: string; resourceHeader_: {[k: string]: string}; + private routeToLeaderEnabled: boolean = true; /** * Placeholder used to auto populate a column with the commit timestamp. @@ -257,6 +259,13 @@ class Spanner extends GrpcService { return undefined; } + /** + * Gets the Route to leader flag. + */ + get routeToLeader(): boolean { + return this.routeToLeaderEnabled; + } + constructor(options?: SpannerOptions) { const scopes: Array<{}> = []; const clientClasses = [ @@ -310,6 +319,11 @@ class Spanner extends GrpcService { packageJson: require('../../package.json'), } as {} as GrpcServiceConfig; super(config, options); + + if(options.routeToLeaderEnabled != undefined && !options.routeToLeaderEnabled) { + this.routeToLeaderEnabled = false; + } + this.options = options; this.auth = new GoogleAuth(this.options); this.clients_ = new Map(); diff --git a/src/session.ts b/src/session.ts index 849e93914..d077b5dbb 100644 --- a/src/session.ts +++ b/src/session.ts @@ -36,9 +36,10 @@ import { CreateSessionOptions, } from './database'; import {ServiceObjectConfig} from '@google-cloud/common'; -import {NormalCallback, CLOUD_RESOURCE_HEADER} from './common'; +import {NormalCallback, CLOUD_RESOURCE_HEADER, LEADER_AWARE_ROUTING_HEADER} from './common'; import {grpc, CallOptions} from 'google-gax'; import IRequestOptions = google.spanner.v1.IRequestOptions; +import {Spanner} from '.'; export type GetSessionResponse = [Session, r.Response]; @@ -378,13 +379,18 @@ export class Session extends common.GrpcServiceObject { const reqOpts = { name: this.formattedName_, }; + + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.parent.parent.parent as Spanner).routeToLeader + }; + return this.request( { client: 'SpannerClient', method: 'getSession', reqOpts, gaxOpts, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, (err, resp) => { if (resp) { diff --git a/src/transaction.ts b/src/transaction.ts index 853bfb77f..b99822dfa 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -33,13 +33,14 @@ import { import {Session} from './session'; import {Key} from './table'; import {google as spannerClient} from '../protos/protos'; -import {NormalCallback, CLOUD_RESOURCE_HEADER} from './common'; +import {NormalCallback, CLOUD_RESOURCE_HEADER, LEADER_AWARE_ROUTING_HEADER} from './common'; import {google} from '../protos/protos'; import IAny = google.protobuf.IAny; import IQueryOptions = google.spanner.v1.ExecuteSqlRequest.IQueryOptions; import IRequestOptions = google.spanner.v1.IRequestOptions; import {Database} from '.'; import ReadLockMode = google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode; +import {Spanner} from '.'; export type Rows = Array; const RETRY_INFO_TYPE = 'type.googleapis.com/google.rpc.retryinfo'; @@ -370,13 +371,17 @@ export class Snapshot extends EventEmitter { reqOpts.requestOptions = this.requestOptions; } + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader && (this._options.readWrite != undefined || this._options.partitionedDml != undefined) + }; + this.request( { client: 'SpannerClient', method: 'beginTransaction', reqOpts, gaxOpts, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, ( err: null | grpc.ServiceError, @@ -602,6 +607,10 @@ export class Snapshot extends EventEmitter { } ); + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader && (this._options.readWrite != undefined || this._options.partitionedDml != undefined) + }; + const makeRequest = (resumeToken?: ResumeToken): Readable => { if (this.id && transaction.begin) { delete transaction.begin; @@ -612,7 +621,7 @@ export class Snapshot extends EventEmitter { method: 'streamingRead', reqOpts: Object.assign({}, reqOpts, {resumeToken}), gaxOpts: gaxOptions, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }); }; @@ -1077,6 +1086,10 @@ export class Snapshot extends EventEmitter { }); }; + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader && (this._options.readWrite != undefined || this._options.partitionedDml != undefined) + }; + const makeRequest = (resumeToken?: ResumeToken): Readable => { if (!reqOpts || (this.id && !reqOpts.transaction.id)) { try { @@ -1093,7 +1106,7 @@ export class Snapshot extends EventEmitter { method: 'executeStreamingSql', reqOpts: Object.assign({}, reqOpts, {resumeToken}), gaxOpts: gaxOptions, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }); }; @@ -1620,13 +1633,17 @@ export class Transaction extends Dml { statements, } as spannerClient.spanner.v1.ExecuteBatchDmlRequest; + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader + }; + this.request( { client: 'SpannerClient', method: 'executeBatchDml', reqOpts, gaxOpts, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, ( err: null | grpc.ServiceError, @@ -1789,13 +1806,17 @@ export class Transaction extends Dml { this.requestOptions ); + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader + }; + this.request( { client: 'SpannerClient', method: 'commit', reqOpts, gaxOpts: gaxOpts, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, (err: null | Error, resp: spannerClient.spanner.v1.ICommitResponse) => { this.end(); @@ -2124,13 +2145,17 @@ export class Transaction extends Dml { transactionId, }; + const leaderAwareRoutingHeader = { + [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader + }; + this.request( { client: 'SpannerClient', method: 'rollback', reqOpts, gaxOpts, - headers: this.resourceHeader_, + headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), }, (err: null | ServiceError) => { this.end(); From 6283de73e35844db70b578e401cdbd2aafaa1a3e Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Wed, 1 Feb 2023 11:47:41 +0530 Subject: [PATCH 02/10] routetoleaderenabled property and test cases --- src/batch-transaction.ts | 27 ++++++---- src/common.ts | 2 +- src/database.ts | 32 +++++++---- src/index.ts | 16 +++--- src/session.ts | 21 ++++++-- src/transaction.ts | 108 +++++++++++++++++++++++++++++--------- test/batch-transaction.ts | 9 ++++ test/database.ts | 28 ++++++++-- test/session.ts | 24 +++++++-- test/transaction.ts | 61 ++++++++++++++++++--- 10 files changed, 253 insertions(+), 75 deletions(-) diff --git a/src/batch-transaction.ts b/src/batch-transaction.ts index ec0fdefa2..9092c9cb9 100644 --- a/src/batch-transaction.ts +++ b/src/batch-transaction.ts @@ -21,7 +21,10 @@ import * as is from 'is'; import {Snapshot} from './transaction'; import {google} from '../protos/protos'; import {Session, Database} from '.'; -import {CLOUD_RESOURCE_HEADER, LEADER_AWARE_ROUTING_HEADER} from '../src/common'; +import { + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from '../src/common'; import {Spanner} from '.'; export interface TransactionIdentifier { @@ -133,10 +136,12 @@ class BatchTransaction extends Snapshot { delete reqOpts.gaxOptions; delete reqOpts.types; - - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader - }; + + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + headers = {[LEADER_AWARE_ROUTING_HEADER]: true}; + } this.createPartitions_( { @@ -144,7 +149,7 @@ class BatchTransaction extends Snapshot { method: 'partitionQuery', reqOpts, gaxOpts: query.gaxOptions, - headers: leaderAwareRoutingHeader, + headers: headers, }, callback ); @@ -231,9 +236,11 @@ class BatchTransaction extends Snapshot { delete reqOpts.keys; delete reqOpts.ranges; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + headers = {[LEADER_AWARE_ROUTING_HEADER]: true}; + } this.createPartitions_( { @@ -241,7 +248,7 @@ class BatchTransaction extends Snapshot { method: 'partitionRead', reqOpts, gaxOpts: options.gaxOptions, - headers: leaderAwareRoutingHeader + headers: headers, }, callback ); diff --git a/src/common.ts b/src/common.ts index f8e91f9ab..9b102f7b7 100644 --- a/src/common.ts +++ b/src/common.ts @@ -79,4 +79,4 @@ export const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix'; /*! * HTTP header to route the requests at Leader */ -export const LEADER_AWARE_ROUTING_HEADER = 'x-goog-spanner-route-to-leader'; \ No newline at end of file +export const LEADER_AWARE_ROUTING_HEADER = 'x-goog-spanner-route-to-leader'; diff --git a/src/database.ts b/src/database.ts index b92382cea..ef3a85954 100644 --- a/src/database.ts +++ b/src/database.ts @@ -85,7 +85,7 @@ import { RequestCallback, ResourceCallback, Schema, - LEADER_AWARE_ROUTING_HEADER + LEADER_AWARE_ROUTING_HEADER, } from './common'; import {Spanner} from '.'; import {Duplex, Readable, Transform} from 'stream'; @@ -525,9 +525,16 @@ class Database extends common.GrpcServiceObject { sessionCount: count, }; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.instance.parent as Spanner).routeToLeader, - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.instance.parent as Spanner).routeToLeaderEnabled) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } this.request( { @@ -535,7 +542,7 @@ class Database extends common.GrpcServiceObject { method: 'batchCreateSessions', reqOpts, gaxOpts: options.gaxOptions, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, (err, resp) => { if (err) { @@ -797,9 +804,16 @@ class Database extends common.GrpcServiceObject { reqOpts.session.creatorRole = options.databaseRole || this.databaseRole || null; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.instance.parent as Spanner).routeToLeader, - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.instance.parent as Spanner).routeToLeaderEnabled) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } this.request( { @@ -807,7 +821,7 @@ class Database extends common.GrpcServiceObject { method: 'createSession', reqOpts, gaxOpts: options.gaxOptions, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, (err, resp) => { if (err) { diff --git a/src/index.ts b/src/index.ts index 784325643..bb7b887bc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -211,7 +211,7 @@ class Spanner extends GrpcService { projectIdReplaced_: boolean; projectFormattedName_: string; resourceHeader_: {[k: string]: string}; - private routeToLeaderEnabled: boolean = true; + routeToLeaderEnabled = true; /** * Placeholder used to auto populate a column with the commit timestamp. @@ -259,13 +259,6 @@ class Spanner extends GrpcService { return undefined; } - /** - * Gets the Route to leader flag. - */ - get routeToLeader(): boolean { - return this.routeToLeaderEnabled; - } - constructor(options?: SpannerOptions) { const scopes: Array<{}> = []; const clientClasses = [ @@ -320,10 +313,13 @@ class Spanner extends GrpcService { } as {} as GrpcServiceConfig; super(config, options); - if(options.routeToLeaderEnabled != undefined && !options.routeToLeaderEnabled) { + if ( + options.routeToLeaderEnabled !== undefined && + !options.routeToLeaderEnabled + ) { this.routeToLeaderEnabled = false; } - + this.options = options; this.auth = new GoogleAuth(this.options); this.clients_ = new Map(); diff --git a/src/session.ts b/src/session.ts index d077b5dbb..1142692b1 100644 --- a/src/session.ts +++ b/src/session.ts @@ -36,7 +36,11 @@ import { CreateSessionOptions, } from './database'; import {ServiceObjectConfig} from '@google-cloud/common'; -import {NormalCallback, CLOUD_RESOURCE_HEADER, LEADER_AWARE_ROUTING_HEADER} from './common'; +import { + NormalCallback, + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from './common'; import {grpc, CallOptions} from 'google-gax'; import IRequestOptions = google.spanner.v1.IRequestOptions; import {Spanner} from '.'; @@ -380,9 +384,16 @@ export class Session extends common.GrpcServiceObject { name: this.formattedName_, }; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.parent.parent.parent as Spanner).routeToLeader - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.parent.parent.parent as Spanner).routeToLeaderEnabled) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } return this.request( { @@ -390,7 +401,7 @@ export class Session extends common.GrpcServiceObject { method: 'getSession', reqOpts, gaxOpts, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, (err, resp) => { if (resp) { diff --git a/src/transaction.ts b/src/transaction.ts index b99822dfa..5686c3c40 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -33,7 +33,11 @@ import { import {Session} from './session'; import {Key} from './table'; import {google as spannerClient} from '../protos/protos'; -import {NormalCallback, CLOUD_RESOURCE_HEADER, LEADER_AWARE_ROUTING_HEADER} from './common'; +import { + NormalCallback, + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from './common'; import {google} from '../protos/protos'; import IAny = google.protobuf.IAny; import IQueryOptions = google.spanner.v1.ExecuteSqlRequest.IQueryOptions; @@ -371,9 +375,20 @@ export class Snapshot extends EventEmitter { reqOpts.requestOptions = this.requestOptions; } - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader && (this._options.readWrite != undefined || this._options.partitionedDml != undefined) - }; + // Adding Leader aware routing header if route to leader is enabled and Transaction is ReadWrite or Partitioned DML + let headers; + if ( + (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && + (this._options.readWrite !== undefined || + this._options.partitionedDml !== undefined) + ) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } this.request( { @@ -381,7 +396,7 @@ export class Snapshot extends EventEmitter { method: 'beginTransaction', reqOpts, gaxOpts, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, ( err: null | grpc.ServiceError, @@ -607,9 +622,20 @@ export class Snapshot extends EventEmitter { } ); - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader && (this._options.readWrite != undefined || this._options.partitionedDml != undefined) - }; + // Adding Leader aware routing header if route to leader is enabled and Transaction is ReadWrite or Partitioned DML + let headers; + if ( + (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && + (this._options.readWrite !== undefined || + this._options.partitionedDml !== undefined) + ) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } const makeRequest = (resumeToken?: ResumeToken): Readable => { if (this.id && transaction.begin) { @@ -621,7 +647,7 @@ export class Snapshot extends EventEmitter { method: 'streamingRead', reqOpts: Object.assign({}, reqOpts, {resumeToken}), gaxOpts: gaxOptions, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }); }; @@ -1086,9 +1112,20 @@ export class Snapshot extends EventEmitter { }); }; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader && (this._options.readWrite != undefined || this._options.partitionedDml != undefined) - }; + // Adding Leader aware routing header if route to leader is enabled and Transaction is ReadWrite or Partitioned DML + let headers; + if ( + (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && + (this._options.readWrite !== undefined || + this._options.partitionedDml !== undefined) + ) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } const makeRequest = (resumeToken?: ResumeToken): Readable => { if (!reqOpts || (this.id && !reqOpts.transaction.id)) { @@ -1106,7 +1143,7 @@ export class Snapshot extends EventEmitter { method: 'executeStreamingSql', reqOpts: Object.assign({}, reqOpts, {resumeToken}), gaxOpts: gaxOptions, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }); }; @@ -1633,9 +1670,16 @@ export class Transaction extends Dml { statements, } as spannerClient.spanner.v1.ExecuteBatchDmlRequest; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } this.request( { @@ -1643,7 +1687,7 @@ export class Transaction extends Dml { method: 'executeBatchDml', reqOpts, gaxOpts, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, ( err: null | grpc.ServiceError, @@ -1806,9 +1850,16 @@ export class Transaction extends Dml { this.requestOptions ); - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } this.request( { @@ -1816,7 +1867,7 @@ export class Transaction extends Dml { method: 'commit', reqOpts, gaxOpts: gaxOpts, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, (err: null | Error, resp: spannerClient.spanner.v1.ICommitResponse) => { this.end(); @@ -2145,9 +2196,16 @@ export class Transaction extends Dml { transactionId, }; - const leaderAwareRoutingHeader = { - [LEADER_AWARE_ROUTING_HEADER]: (this.session.parent.parent.parent as Spanner).routeToLeader - }; + // Adding Leader aware routing header if route to leader is enabled + let headers; + if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + headers = Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + this.resourceHeader_ + ); + } else { + headers = this.resourceHeader_; + } this.request( { @@ -2155,7 +2213,7 @@ export class Transaction extends Dml { method: 'rollback', reqOpts, gaxOpts, - headers: Object.assign(leaderAwareRoutingHeader, this.resourceHeader_), + headers: headers, }, (err: null | ServiceError) => { this.end(); diff --git a/test/batch-transaction.ts b/test/batch-transaction.ts index f9d02272d..e3a6bca2c 100644 --- a/test/batch-transaction.ts +++ b/test/batch-transaction.ts @@ -57,8 +57,17 @@ const fakeCodec: any = { convertProtoTimestampToDate() {}, }; +const SPANNER = { + routeToLeaderEnabled: true, +}; + +const INSTANCE = { + parent: SPANNER, +}; + const DATABASE = { formattedName_: 'database', + parent: INSTANCE, }; class FakeTransaction { diff --git a/test/database.ts b/test/database.ts index 41ed7e12c..2065e2f68 100644 --- a/test/database.ts +++ b/test/database.ts @@ -28,10 +28,13 @@ import * as through from 'through2'; import * as pfy from '@google-cloud/promisify'; import {grpc} from 'google-gax'; import * as db from '../src/database'; -import {Instance} from '../src'; +import {Spanner, Instance} from '../src'; import {MockError} from './mockserver/mockspanner'; import {IOperation} from '../src/instance'; -import {CLOUD_RESOURCE_HEADER} from '../src/common'; +import { + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from '../src/common'; import {google} from '../protos/protos'; import RequestOptions = google.spanner.v1.RequestOptions; import EncryptionType = google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType; @@ -176,11 +179,16 @@ describe('Database', () => { // tslint:disable-next-line variable-name let DatabaseCached: typeof db.Database; + const SPANNER = { + routeToLeaderEnabled: true, + } as {} as Spanner; + const INSTANCE = { request: util.noop, requestStream: util.noop, formattedName_: 'instance-name', databases_: new Map(), + parent: SPANNER, } as {} as Instance; const NAME = 'table-name'; @@ -355,7 +363,13 @@ describe('Database', () => { assert.strictEqual(reqOpts.database, DATABASE_FORMATTED_NAME); assert.strictEqual(reqOpts.sessionCount, count); assert.strictEqual(gaxOpts, undefined); - assert.deepStrictEqual(headers, database.resourceHeader_); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + database.resourceHeader_ + ) + ); }); it('should accept just a count number', () => { @@ -1721,7 +1735,13 @@ describe('Database', () => { }, }); assert.strictEqual(config.gaxOpts, gaxOptions); - assert.deepStrictEqual(config.headers, database.resourceHeader_); + assert.deepStrictEqual( + config.headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + database.resourceHeader_ + ) + ); done(); }; diff --git a/test/session.ts b/test/session.ts index 03c9e45eb..cee22b8d8 100644 --- a/test/session.ts +++ b/test/session.ts @@ -21,8 +21,11 @@ import * as assert from 'assert'; import {before, beforeEach, describe, it} from 'mocha'; import * as extend from 'extend'; import * as proxyquire from 'proxyquire'; -import {CLOUD_RESOURCE_HEADER} from '../src/common'; -import {Database} from '../src'; +import { + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from '../src/common'; +import {Database, Instance, Spanner} from '../src'; let promisified = false; const fakePfy = extend({}, pfy, { @@ -67,10 +70,19 @@ describe('Session', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let session: any; + const SPANNER = { + routeToLeaderEnabled: true, + } as {} as Spanner; + + const INSTANCE = { + parent: SPANNER, + } as {} as Instance; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const DATABASE: any = { request: () => {}, formattedName_: 'formatted-database-name', + parent: INSTANCE, }; const NAME = 'session-name'; @@ -285,7 +297,13 @@ describe('Session', () => { name: session.formattedName_, }); assert.deepStrictEqual(config.gaxOpts, {}); - assert.deepStrictEqual(config.headers, session.resourceHeader_); + assert.deepStrictEqual( + config.headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + session.resourceHeader_ + ) + ); return requestReturnValue; }; diff --git a/test/transaction.ts b/test/transaction.ts index 0dbefaf46..a45157c43 100644 --- a/test/transaction.ts +++ b/test/transaction.ts @@ -24,7 +24,10 @@ import * as sinon from 'sinon'; import {codec} from '../src/codec'; import {google} from '../protos/protos'; -import {CLOUD_RESOURCE_HEADER} from '../src/common'; +import { + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from '../src/common'; import RequestOptions = google.spanner.v1.RequestOptions; import { BatchUpdateOptions, @@ -36,13 +39,25 @@ import {grpc} from 'google-gax'; describe('Transaction', () => { const sandbox = sinon.createSandbox(); - const PARENT = {formattedName_: 'formatted-database-name'}; const REQUEST = sandbox.stub(); const REQUEST_STREAM = sandbox.stub(); const SESSION_NAME = 'session-123'; + const SPANNER = { + routeToLeaderEnabled: true, + }; + + const INSTANCE = { + parent: SPANNER, + }; + + const DATABASE = { + formattedName_: 'formatted-database-name', + parent: INSTANCE, + }; + const SESSION = { - parent: PARENT, + parent: DATABASE, formattedName_: SESSION_NAME, request: REQUEST, requestStream: REQUEST_STREAM, @@ -1209,7 +1224,13 @@ describe('Transaction', () => { assert.strictEqual(reqOpts.session, SESSION_NAME); assert.deepStrictEqual(reqOpts.transaction, {id: fakeId}); assert.strictEqual(reqOpts.seqno, 1); - assert.deepStrictEqual(headers, transaction.resourceHeader_); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); }); it('should encode sql string statements', () => { @@ -1339,7 +1360,13 @@ describe('Transaction', () => { assert.strictEqual(client, 'SpannerClient'); assert.strictEqual(method, 'beginTransaction'); assert.deepStrictEqual(reqOpts.options, expectedOptions); - assert.deepStrictEqual(headers, transaction.resourceHeader_); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); }); it('should accept gaxOptions', done => { @@ -1380,7 +1407,13 @@ describe('Transaction', () => { assert.strictEqual(client, 'SpannerClient'); assert.strictEqual(method, 'beginTransaction'); assert.deepStrictEqual(reqOpts.options, expectedOptions); - assert.deepStrictEqual(headers, transaction.resourceHeader_); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); }); }); @@ -1398,7 +1431,13 @@ describe('Transaction', () => { assert.strictEqual(method, 'commit'); assert.strictEqual(reqOpts.session, SESSION_NAME); assert.deepStrictEqual(reqOpts.mutations, []); - assert.deepStrictEqual(headers, transaction.resourceHeader_); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); }); it('should accept gaxOptions as CallOptions', done => { @@ -1767,7 +1806,13 @@ describe('Transaction', () => { assert.strictEqual(client, 'SpannerClient'); assert.strictEqual(method, 'rollback'); assert.deepStrictEqual(reqOpts, expectedReqOpts); - assert.deepStrictEqual(headers, transaction.resourceHeader_); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); }); it('should accept gaxOptions', done => { From c1e83df62aee52a4a1d828f200e3c2f37946d9b2 Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Tue, 21 Feb 2023 16:07:35 +0530 Subject: [PATCH 03/10] feat: Added unit test cases --- src/batch-transaction.ts | 2 -- src/database.ts | 2 -- src/index.ts | 5 +++++ src/session.ts | 1 - src/transaction.ts | 6 ----- test/batch-transaction.ts | 17 +++++++++++--- test/index.ts | 6 +++++ test/session.ts | 21 +++++++++++++++++ test/transaction.ts | 47 ++++++++++++++++++++++++++++++++++++++- 9 files changed, 92 insertions(+), 15 deletions(-) diff --git a/src/batch-transaction.ts b/src/batch-transaction.ts index 9092c9cb9..84679faf7 100644 --- a/src/batch-transaction.ts +++ b/src/batch-transaction.ts @@ -137,7 +137,6 @@ class BatchTransaction extends Snapshot { delete reqOpts.gaxOptions; delete reqOpts.types; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { headers = {[LEADER_AWARE_ROUTING_HEADER]: true}; @@ -236,7 +235,6 @@ class BatchTransaction extends Snapshot { delete reqOpts.keys; delete reqOpts.ranges; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { headers = {[LEADER_AWARE_ROUTING_HEADER]: true}; diff --git a/src/database.ts b/src/database.ts index ef3a85954..2f3310aa8 100644 --- a/src/database.ts +++ b/src/database.ts @@ -525,7 +525,6 @@ class Database extends common.GrpcServiceObject { sessionCount: count, }; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.instance.parent as Spanner).routeToLeaderEnabled) { headers = Object.assign( @@ -804,7 +803,6 @@ class Database extends common.GrpcServiceObject { reqOpts.session.creatorRole = options.databaseRole || this.databaseRole || null; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.instance.parent as Spanner).routeToLeaderEnabled) { headers = Object.assign( diff --git a/src/index.ts b/src/index.ts index bb7b887bc..a7aa4f61c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -106,6 +106,11 @@ export type GetInstanceConfigOperationsCallback = PagedCallback< instanceAdmin.spanner.admin.instance.v1.IListInstanceConfigOperationsResponse >; +/** + * Session pool configuration options. + * @property {boolean} [routeToLeaderEnabled=True] If set to false leader aware routing will be disabled. + * Disabling leader aware routing would route all requests in RW/PDML transactions to any region. + */ export interface SpannerOptions extends GrpcClientOptions { apiEndpoint?: string; servicePath?: string; diff --git a/src/session.ts b/src/session.ts index 1142692b1..f3d9dae8d 100644 --- a/src/session.ts +++ b/src/session.ts @@ -384,7 +384,6 @@ export class Session extends common.GrpcServiceObject { name: this.formattedName_, }; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.parent.parent.parent as Spanner).routeToLeaderEnabled) { headers = Object.assign( diff --git a/src/transaction.ts b/src/transaction.ts index 5686c3c40..3f6c856aa 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -375,7 +375,6 @@ export class Snapshot extends EventEmitter { reqOpts.requestOptions = this.requestOptions; } - // Adding Leader aware routing header if route to leader is enabled and Transaction is ReadWrite or Partitioned DML let headers; if ( (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && @@ -622,7 +621,6 @@ export class Snapshot extends EventEmitter { } ); - // Adding Leader aware routing header if route to leader is enabled and Transaction is ReadWrite or Partitioned DML let headers; if ( (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && @@ -1112,7 +1110,6 @@ export class Snapshot extends EventEmitter { }); }; - // Adding Leader aware routing header if route to leader is enabled and Transaction is ReadWrite or Partitioned DML let headers; if ( (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && @@ -1670,7 +1667,6 @@ export class Transaction extends Dml { statements, } as spannerClient.spanner.v1.ExecuteBatchDmlRequest; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { headers = Object.assign( @@ -1850,7 +1846,6 @@ export class Transaction extends Dml { this.requestOptions ); - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { headers = Object.assign( @@ -2196,7 +2191,6 @@ export class Transaction extends Dml { transactionId, }; - // Adding Leader aware routing header if route to leader is enabled let headers; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { headers = Object.assign( diff --git a/test/batch-transaction.ts b/test/batch-transaction.ts index e3a6bca2c..0886706c1 100644 --- a/test/batch-transaction.ts +++ b/test/batch-transaction.ts @@ -27,7 +27,10 @@ import * as sinon from 'sinon'; import {Session, Database} from '../src'; import * as bt from '../src/batch-transaction'; import {PartialResultStream} from '../src/partial-result-stream'; -import {CLOUD_RESOURCE_HEADER} from '../src/common'; +import { + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from '../src/common'; let promisified = false; const fakePfy = extend({}, pfy, { @@ -158,11 +161,15 @@ describe('BatchTransaction', () => { batchTransaction.createQueryPartitions(QUERY, assert.ifError); - const {client, method, reqOpts, gaxOpts} = stub.lastCall.args[0]; + const {client, method, reqOpts, gaxOpts, headers} = stub.lastCall.args[0]; assert.strictEqual(client, 'SpannerClient'); assert.strictEqual(method, 'partitionQuery'); assert.deepStrictEqual(reqOpts, expectedQuery); assert.strictEqual(gaxOpts, GAX_OPTS); + assert.deepStrictEqual( + headers, + Object.assign({[LEADER_AWARE_ROUTING_HEADER]: true}) + ); }); it('should accept query as string', () => { @@ -309,11 +316,15 @@ describe('BatchTransaction', () => { batchTransaction.createReadPartitions(QUERY, assert.ifError); - const {client, method, reqOpts, gaxOpts} = stub.lastCall.args[0]; + const {client, method, reqOpts, gaxOpts, headers} = stub.lastCall.args[0]; assert.strictEqual(client, 'SpannerClient'); assert.strictEqual(method, 'partitionRead'); assert.deepStrictEqual(reqOpts, expectedQuery); assert.strictEqual(gaxOpts, GAX_OPTS); + assert.deepStrictEqual( + headers, + Object.assign({[LEADER_AWARE_ROUTING_HEADER]: true}) + ); }); }); diff --git a/test/index.ts b/test/index.ts index 6910454ad..b0997fd90 100644 --- a/test/index.ts +++ b/test/index.ts @@ -276,6 +276,12 @@ describe('Spanner', () => { assert.strictEqual(config.baseUrl, SERVICE_PATH); }); + it('should optionally accept routeToLeaderEnabled', () => { + const SERVICE_PATH = 'abc.def.ghi'; + const spanner = new Spanner({routeToLeaderEnabled: false}); + assert.strictEqual(spanner.routeToLeaderEnabled, false); + }); + it('should set projectFormattedName_', () => { assert.strictEqual( spanner.projectFormattedName_, diff --git a/test/session.ts b/test/session.ts index cee22b8d8..9570bcc27 100644 --- a/test/session.ts +++ b/test/session.ts @@ -311,6 +311,27 @@ describe('Session', () => { assert.strictEqual(returnValue, requestReturnValue); }); + it('should correctly call and return the request with Leader Aware Routing disabled.', () => { + const requestReturnValue = {}; + + function callback() {} + + session.parent.parent.parent.routeToLeaderEnabled = false; + session.request = config => { + assert.strictEqual(config.client, 'SpannerClient'); + assert.strictEqual(config.method, 'getSession'); + assert.deepStrictEqual(config.reqOpts, { + name: session.formattedName_, + }); + assert.deepStrictEqual(config.gaxOpts, {}); + assert.deepStrictEqual(config.headers, session.resourceHeader_); + return requestReturnValue; + }; + + const returnValue = session.getMetadata(callback); + assert.strictEqual(returnValue, requestReturnValue); + }); + it('should accept and pass gaxOptions to request', done => { const gaxOptions = {}; session.request = config => { diff --git a/test/transaction.ts b/test/transaction.ts index a45157c43..45cd9dbf6 100644 --- a/test/transaction.ts +++ b/test/transaction.ts @@ -1972,6 +1972,27 @@ describe('Transaction', () => { PARTIAL_RESULT_STREAM.callsFake(makeRequest => makeRequest()); }); + it('should send the correct options', done => { + const QUERY: ExecuteSqlRequest = { + sql: 'SELET * FROM `MyTable`', + }; + + transaction.requestStream = config => { + assert.strictEqual(config.client, 'SpannerClient'); + assert.strictEqual(config.method, 'executeStreamingSql'); + assert.deepStrictEqual( + config.headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); + done(); + }; + + transaction.runStream(QUERY); + }); + it('should set transaction tag when not `singleUse`', done => { const QUERY: ExecuteSqlRequest = { sql: 'SELET * FROM `MyTable`', @@ -1999,6 +2020,23 @@ describe('Transaction', () => { PARTIAL_RESULT_STREAM.callsFake(makeRequest => makeRequest()); }); + it('should send the correct options', () => { + const TABLE = 'my-table-123'; + transaction.createReadStream(TABLE); + + const {client, method, headers} = REQUEST_STREAM.lastCall.args[0]; + + assert.strictEqual(client, 'SpannerClient'); + assert.strictEqual(method, 'streamingRead'); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + transaction.resourceHeader_ + ) + ); + }); + it('should set transaction tag if not `singleUse`', () => { const TABLE = 'my-table-123'; const transactionTag = 'bar'; @@ -2056,9 +2094,16 @@ describe('Transaction', () => { pdml.begin(); const expectedOptions = {partitionedDml: {}}; - const {reqOpts} = stub.lastCall.args[0]; + const {reqOpts, headers} = stub.lastCall.args[0]; assert.deepStrictEqual(reqOpts.options, expectedOptions); + assert.deepStrictEqual( + headers, + Object.assign( + {[LEADER_AWARE_ROUTING_HEADER]: true}, + pdml.resourceHeader_ + ) + ); }); }); From d91602a1125a4d30450540883486f769e0a12e6a Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Tue, 21 Feb 2023 17:32:45 +0530 Subject: [PATCH 04/10] test: for LAR disabled --- test/index.ts | 1 - test/spanner.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/test/index.ts b/test/index.ts index b0997fd90..a37b92404 100644 --- a/test/index.ts +++ b/test/index.ts @@ -277,7 +277,6 @@ describe('Spanner', () => { }); it('should optionally accept routeToLeaderEnabled', () => { - const SERVICE_PATH = 'abc.def.ghi'; const spanner = new Spanner({routeToLeaderEnabled: false}); assert.strictEqual(spanner.routeToLeaderEnabled, false); }); diff --git a/test/spanner.ts b/test/spanner.ts index daf560999..60167b0ed 100644 --- a/test/spanner.ts +++ b/test/spanner.ts @@ -50,7 +50,10 @@ import {Float, Int, Json, Numeric, SpannerDate} from '../src/codec'; import * as stream from 'stream'; import * as util from 'util'; import {PreciseDate} from '@google-cloud/precise-date'; -import {CLOUD_RESOURCE_HEADER} from '../src/common'; +import { + CLOUD_RESOURCE_HEADER, + LEADER_AWARE_ROUTING_HEADER, +} from '../src/common'; import CreateInstanceMetadata = google.spanner.admin.instance.v1.CreateInstanceMetadata; import QueryOptions = google.spanner.v1.ExecuteSqlRequest.QueryOptions; import v1 = google.spanner.v1; @@ -1486,6 +1489,69 @@ describe('Spanner with mock server', () => { }); }); }); + + describe('LeaderAwareRouting', () => { + let spannerWithLARDisabled: Spanner; + let instanceWithEnvVar: Instance; + + function newTestDatabaseWithLARDisabled( + options?: SessionPoolOptions, + queryOptions?: IQueryOptions + ): Database { + return instanceWithEnvVar.database( + `database-${dbCounter++}`, + options, + queryOptions + ); + } + + before(() => { + spannerWithLARDisabled = new Spanner({ + servicePath: 'localhost', + port, + sslCreds: grpc.credentials.createInsecure(), + routeToLeaderEnabled: false, + }); + // Gets a reference to a Cloud Spanner instance and database + instanceWithEnvVar = spannerWithLARDisabled.instance('instance'); + }); + + it('should execute with leader aware routing enabled in a read/write transaction', async () => { + const database = newTestDatabase(); + await database.runTransactionAsync(async tx => { + await tx!.runUpdate({ + sql: insertSql, + }); + return await tx.commit(); + }); + await database.close(); + spannerMock.getMetadata().forEach(metadata => { + if (metadata.get(LEADER_AWARE_ROUTING_HEADER)[0] !== undefined) { + assert.strictEqual( + metadata.get(LEADER_AWARE_ROUTING_HEADER)[0], + 'true' + ); + } + }); + }); + + it('should execute with leader aware routing disabled in a read/write transaction', async () => { + const database = newTestDatabaseWithLARDisabled(); + await database.runTransactionAsync(async tx => { + await tx!.runUpdate({ + sql: insertSql, + }); + return await tx.commit(); + }); + await database.close(); + spannerMock.getMetadata().forEach(metadata => { + assert.strictEqual( + metadata.get(LEADER_AWARE_ROUTING_HEADER)[0], + undefined + ); + }); + }); + }); }); describe('queryOptions', () => { From c47edce79b8ac76bee7ad6b6d880304616b6397c Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Mon, 27 Feb 2023 16:36:40 +0530 Subject: [PATCH 05/10] fix: review comments --- src/batch-transaction.ts | 10 +++---- src/common.ts | 8 ++++++ src/database.ts | 20 ++++---------- src/session.ts | 12 +++------ src/transaction.ts | 56 +++++++++------------------------------ test/batch-transaction.ts | 4 +-- 6 files changed, 36 insertions(+), 74 deletions(-) diff --git a/src/batch-transaction.ts b/src/batch-transaction.ts index 84679faf7..f8937eda5 100644 --- a/src/batch-transaction.ts +++ b/src/batch-transaction.ts @@ -23,7 +23,7 @@ import {google} from '../protos/protos'; import {Session, Database} from '.'; import { CLOUD_RESOURCE_HEADER, - LEADER_AWARE_ROUTING_HEADER, + addLeaderAwareRoutingHeader, } from '../src/common'; import {Spanner} from '.'; @@ -137,9 +137,9 @@ class BatchTransaction extends Snapshot { delete reqOpts.gaxOptions; delete reqOpts.types; - let headers; + const headers: {[k: string]: string} = {}; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { - headers = {[LEADER_AWARE_ROUTING_HEADER]: true}; + addLeaderAwareRoutingHeader(headers); } this.createPartitions_( @@ -235,9 +235,9 @@ class BatchTransaction extends Snapshot { delete reqOpts.keys; delete reqOpts.ranges; - let headers; + const headers: {[k: string]: string} = {}; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { - headers = {[LEADER_AWARE_ROUTING_HEADER]: true}; + addLeaderAwareRoutingHeader(headers); } this.createPartitions_( diff --git a/src/common.ts b/src/common.ts index 9b102f7b7..634df1210 100644 --- a/src/common.ts +++ b/src/common.ts @@ -80,3 +80,11 @@ export const CLOUD_RESOURCE_HEADER = 'google-cloud-resource-prefix'; * HTTP header to route the requests at Leader */ export const LEADER_AWARE_ROUTING_HEADER = 'x-goog-spanner-route-to-leader'; + +/** + * Add Leader aware routing header to existing header list. + * @param headers Existing header list. + */ +export function addLeaderAwareRoutingHeader(headers: {[k: string]: string}) { + headers[LEADER_AWARE_ROUTING_HEADER] = 'true'; +} diff --git a/src/database.ts b/src/database.ts index 2f3310aa8..2e1d443e2 100644 --- a/src/database.ts +++ b/src/database.ts @@ -85,7 +85,7 @@ import { RequestCallback, ResourceCallback, Schema, - LEADER_AWARE_ROUTING_HEADER, + addLeaderAwareRoutingHeader, } from './common'; import {Spanner} from '.'; import {Duplex, Readable, Transform} from 'stream'; @@ -525,14 +525,9 @@ class Database extends common.GrpcServiceObject { sessionCount: count, }; - let headers; + const headers = this.resourceHeader_; if ((this.instance.parent as Spanner).routeToLeaderEnabled) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } this.request( @@ -803,14 +798,9 @@ class Database extends common.GrpcServiceObject { reqOpts.session.creatorRole = options.databaseRole || this.databaseRole || null; - let headers; + const headers = this.resourceHeader_; if ((this.instance.parent as Spanner).routeToLeaderEnabled) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } this.request( diff --git a/src/session.ts b/src/session.ts index f3d9dae8d..79801a133 100644 --- a/src/session.ts +++ b/src/session.ts @@ -39,7 +39,7 @@ import {ServiceObjectConfig} from '@google-cloud/common'; import { NormalCallback, CLOUD_RESOURCE_HEADER, - LEADER_AWARE_ROUTING_HEADER, + addLeaderAwareRoutingHeader, } from './common'; import {grpc, CallOptions} from 'google-gax'; import IRequestOptions = google.spanner.v1.IRequestOptions; @@ -384,16 +384,10 @@ export class Session extends common.GrpcServiceObject { name: this.formattedName_, }; - let headers; + const headers = this.resourceHeader_; if ((this.parent.parent.parent as Spanner).routeToLeaderEnabled) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } - return this.request( { client: 'SpannerClient', diff --git a/src/transaction.ts b/src/transaction.ts index 3f6c856aa..1165e6b8e 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -36,7 +36,7 @@ import {google as spannerClient} from '../protos/protos'; import { NormalCallback, CLOUD_RESOURCE_HEADER, - LEADER_AWARE_ROUTING_HEADER, + addLeaderAwareRoutingHeader, } from './common'; import {google} from '../protos/protos'; import IAny = google.protobuf.IAny; @@ -375,18 +375,13 @@ export class Snapshot extends EventEmitter { reqOpts.requestOptions = this.requestOptions; } - let headers; + const headers = this.resourceHeader_; if ( (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && (this._options.readWrite !== undefined || this._options.partitionedDml !== undefined) ) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } this.request( @@ -621,18 +616,13 @@ export class Snapshot extends EventEmitter { } ); - let headers; + const headers = this.resourceHeader_; if ( (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && (this._options.readWrite !== undefined || this._options.partitionedDml !== undefined) ) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } const makeRequest = (resumeToken?: ResumeToken): Readable => { @@ -1110,18 +1100,13 @@ export class Snapshot extends EventEmitter { }); }; - let headers; + const headers = this.resourceHeader_; if ( (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && (this._options.readWrite !== undefined || this._options.partitionedDml !== undefined) ) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } const makeRequest = (resumeToken?: ResumeToken): Readable => { @@ -1667,14 +1652,9 @@ export class Transaction extends Dml { statements, } as spannerClient.spanner.v1.ExecuteBatchDmlRequest; - let headers; + const headers = this.resourceHeader_; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } this.request( @@ -1846,14 +1826,9 @@ export class Transaction extends Dml { this.requestOptions ); - let headers; + const headers = this.resourceHeader_; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } this.request( @@ -2191,14 +2166,9 @@ export class Transaction extends Dml { transactionId, }; - let headers; + const headers = this.resourceHeader_; if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { - headers = Object.assign( - {[LEADER_AWARE_ROUTING_HEADER]: true}, - this.resourceHeader_ - ); - } else { - headers = this.resourceHeader_; + addLeaderAwareRoutingHeader(headers); } this.request( diff --git a/test/batch-transaction.ts b/test/batch-transaction.ts index 0886706c1..6ab0ab299 100644 --- a/test/batch-transaction.ts +++ b/test/batch-transaction.ts @@ -168,7 +168,7 @@ describe('BatchTransaction', () => { assert.strictEqual(gaxOpts, GAX_OPTS); assert.deepStrictEqual( headers, - Object.assign({[LEADER_AWARE_ROUTING_HEADER]: true}) + Object.assign({[LEADER_AWARE_ROUTING_HEADER]: 'true'}) ); }); @@ -323,7 +323,7 @@ describe('BatchTransaction', () => { assert.strictEqual(gaxOpts, GAX_OPTS); assert.deepStrictEqual( headers, - Object.assign({[LEADER_AWARE_ROUTING_HEADER]: true}) + Object.assign({[LEADER_AWARE_ROUTING_HEADER]: 'true'}) ); }); }); From 04d285a5c8f4b4583db39d4cf1f143f4fee77dd5 Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Mon, 27 Feb 2023 17:11:02 +0530 Subject: [PATCH 06/10] fix: review comments --- src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index a7aa4f61c..76eccc6b3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -318,10 +318,7 @@ class Spanner extends GrpcService { } as {} as GrpcServiceConfig; super(config, options); - if ( - options.routeToLeaderEnabled !== undefined && - !options.routeToLeaderEnabled - ) { + if (options.routeToLeaderEnabled === false) { this.routeToLeaderEnabled = false; } From a4a5b50e97b8820483b0e5233bfec43bf508a181 Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Wed, 8 Mar 2023 12:56:51 +0530 Subject: [PATCH 07/10] fix: review comments --- src/batch-transaction.ts | 7 +++---- src/database.ts | 18 ++++++++++++++---- src/session.ts | 13 ++++++++++++- src/transaction.ts | 26 ++++++++++++++++++-------- test/spanner.ts | 9 ++++++--- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/batch-transaction.ts b/src/batch-transaction.ts index f8937eda5..c74432088 100644 --- a/src/batch-transaction.ts +++ b/src/batch-transaction.ts @@ -20,12 +20,11 @@ import * as extend from 'extend'; import * as is from 'is'; import {Snapshot} from './transaction'; import {google} from '../protos/protos'; -import {Session, Database} from '.'; +import {Session, Database, Spanner} from '.'; import { CLOUD_RESOURCE_HEADER, addLeaderAwareRoutingHeader, } from '../src/common'; -import {Spanner} from '.'; export interface TransactionIdentifier { session: string | Session; @@ -138,7 +137,7 @@ class BatchTransaction extends Snapshot { delete reqOpts.types; const headers: {[k: string]: string} = {}; - if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } @@ -236,7 +235,7 @@ class BatchTransaction extends Snapshot { delete reqOpts.ranges; const headers: {[k: string]: string} = {}; - if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } diff --git a/src/database.ts b/src/database.ts index 2e1d443e2..4beba3792 100644 --- a/src/database.ts +++ b/src/database.ts @@ -87,10 +87,9 @@ import { Schema, addLeaderAwareRoutingHeader, } from './common'; -import {Spanner} from '.'; import {Duplex, Readable, Transform} from 'stream'; import {PreciseDate} from '@google-cloud/precise-date'; -import {EnumKey, RequestConfig, TranslateEnumKeys} from '.'; +import {EnumKey, RequestConfig, TranslateEnumKeys, Spanner} from '.'; import arrify = require('arrify'); import {ServiceError} from 'google-gax'; import IPolicy = google.iam.v1.IPolicy; @@ -526,7 +525,7 @@ class Database extends common.GrpcServiceObject { }; const headers = this.resourceHeader_; - if ((this.instance.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } @@ -799,7 +798,7 @@ class Database extends common.GrpcServiceObject { options.databaseRole || this.databaseRole || null; const headers = this.resourceHeader_; - if ((this.instance.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } @@ -3273,6 +3272,17 @@ class Database extends common.GrpcServiceObject { const databaseName = name.split('/').pop(); return instanceName + '/databases/' + databaseName; } + + /** + * Gets the Spanner object + * + * @private + * + * @returns {Spanner} + */ + private _getSpanner(): Spanner { + return this.instance.parent as Spanner; + } } /*! Developer Documentation diff --git a/src/session.ts b/src/session.ts index 79801a133..256d8af14 100644 --- a/src/session.ts +++ b/src/session.ts @@ -385,7 +385,7 @@ export class Session extends common.GrpcServiceObject { }; const headers = this.resourceHeader_; - if ((this.parent.parent.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } return this.request( @@ -522,6 +522,17 @@ export class Session extends common.GrpcServiceObject { const sessionName = name.split('/').pop(); return databaseName + '/sessions/' + sessionName; } + + /** + * Gets the Spanner object + * + * @private + * + * @returns {Spanner} + */ + private _getSpanner(): Spanner { + return this.parent.parent.parent as Spanner; + } } /*! Developer Documentation diff --git a/src/transaction.ts b/src/transaction.ts index 1165e6b8e..7bd194574 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -42,9 +42,8 @@ import {google} from '../protos/protos'; import IAny = google.protobuf.IAny; import IQueryOptions = google.spanner.v1.ExecuteSqlRequest.IQueryOptions; import IRequestOptions = google.spanner.v1.IRequestOptions; -import {Database} from '.'; +import {Database, Spanner} from '.'; import ReadLockMode = google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode; -import {Spanner} from '.'; export type Rows = Array; const RETRY_INFO_TYPE = 'type.googleapis.com/google.rpc.retryinfo'; @@ -377,7 +376,7 @@ export class Snapshot extends EventEmitter { const headers = this.resourceHeader_; if ( - (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && + this._getSpanner().routeToLeaderEnabled && (this._options.readWrite !== undefined || this._options.partitionedDml !== undefined) ) { @@ -618,7 +617,7 @@ export class Snapshot extends EventEmitter { const headers = this.resourceHeader_; if ( - (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && + this._getSpanner().routeToLeaderEnabled && (this._options.readWrite !== undefined || this._options.partitionedDml !== undefined) ) { @@ -1102,7 +1101,7 @@ export class Snapshot extends EventEmitter { const headers = this.resourceHeader_; if ( - (this.session.parent.parent.parent as Spanner).routeToLeaderEnabled && + this._getSpanner().routeToLeaderEnabled && (this._options.readWrite !== undefined || this._options.partitionedDml !== undefined) ) { @@ -1332,6 +1331,17 @@ export class Snapshot extends EventEmitter { .once('end', () => this._idWaiter.emit('end')) ); } + + /** + * Gets the Spanner object + * + * @private + * + * @returns {Spanner} + */ + protected _getSpanner(): Spanner { + return this.session.parent.parent.parent as Spanner; + } } /*! Developer Documentation @@ -1653,7 +1663,7 @@ export class Transaction extends Dml { } as spannerClient.spanner.v1.ExecuteBatchDmlRequest; const headers = this.resourceHeader_; - if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } @@ -1827,7 +1837,7 @@ export class Transaction extends Dml { ); const headers = this.resourceHeader_; - if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } @@ -2167,7 +2177,7 @@ export class Transaction extends Dml { }; const headers = this.resourceHeader_; - if ((this.session.parent.parent.parent as Spanner).routeToLeaderEnabled) { + if (this._getSpanner().routeToLeaderEnabled) { addLeaderAwareRoutingHeader(headers); } diff --git a/test/spanner.ts b/test/spanner.ts index 60167b0ed..eeca9579d 100644 --- a/test/spanner.ts +++ b/test/spanner.ts @@ -1492,13 +1492,13 @@ describe('Spanner with mock server', () => { describe('LeaderAwareRouting', () => { let spannerWithLARDisabled: Spanner; - let instanceWithEnvVar: Instance; + let instanceWithLARDisabled: Instance; function newTestDatabaseWithLARDisabled( options?: SessionPoolOptions, queryOptions?: IQueryOptions ): Database { - return instanceWithEnvVar.database( + return instanceWithLARDisabled.database( `database-${dbCounter++}`, options, queryOptions @@ -1513,7 +1513,7 @@ describe('Spanner with mock server', () => { routeToLeaderEnabled: false, }); // Gets a reference to a Cloud Spanner instance and database - instanceWithEnvVar = spannerWithLARDisabled.instance('instance'); + instanceWithLARDisabled = spannerWithLARDisabled.instance('instance'); }); it('should execute with leader aware routing enabled in a read/write transaction', async () => { @@ -1525,14 +1525,17 @@ describe('Spanner with mock server', () => { return await tx.commit(); }); await database.close(); + let metadataCountWithLAREnabled = 0; spannerMock.getMetadata().forEach(metadata => { if (metadata.get(LEADER_AWARE_ROUTING_HEADER)[0] !== undefined) { + metadataCountWithLAREnabled++; assert.strictEqual( metadata.get(LEADER_AWARE_ROUTING_HEADER)[0], 'true' ); } }); + assert.notStrictEqual(metadataCountWithLAREnabled, 0); }); it('should execute with leader aware routing disabled in a read/write transaction', async () => { From 5609929804cc7a36885307d3ccd197b9aca841cf Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Wed, 8 Mar 2023 14:02:13 +0530 Subject: [PATCH 08/10] fix : test cases --- test/batch-transaction.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/batch-transaction.ts b/test/batch-transaction.ts index 6ab0ab299..340e153d0 100644 --- a/test/batch-transaction.ts +++ b/test/batch-transaction.ts @@ -24,7 +24,7 @@ import * as extend from 'extend'; import * as proxyquire from 'proxyquire'; import * as sinon from 'sinon'; -import {Session, Database} from '../src'; +import {Session, Database, Spanner} from '../src'; import * as bt from '../src/batch-transaction'; import {PartialResultStream} from '../src/partial-result-stream'; import { @@ -86,6 +86,11 @@ class FakeTransaction { static encodeParams(): object { return {}; } + + _getSpanner(): Spanner { + return SPANNER as Spanner; + } + run() {} read() {} } From ee2b6385d53009997f1ac22741c85102c813787c Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Wed, 8 Mar 2023 15:19:13 +0530 Subject: [PATCH 09/10] fix: lint --- test/batch-transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/batch-transaction.ts b/test/batch-transaction.ts index 340e153d0..33e3dd403 100644 --- a/test/batch-transaction.ts +++ b/test/batch-transaction.ts @@ -90,7 +90,7 @@ class FakeTransaction { _getSpanner(): Spanner { return SPANNER as Spanner; } - + run() {} read() {} } From c150674620c1d2767b7f61f6d2f6778bf314be4d Mon Sep 17 00:00:00 2001 From: surbhigarg92 Date: Wed, 26 Apr 2023 11:43:04 +0530 Subject: [PATCH 10/10] feat: Default disable LAR feature --- src/index.ts | 6 +++--- test/spanner.ts | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index 76eccc6b3..fef4c7642 100644 --- a/src/index.ts +++ b/src/index.ts @@ -216,7 +216,7 @@ class Spanner extends GrpcService { projectIdReplaced_: boolean; projectFormattedName_: string; resourceHeader_: {[k: string]: string}; - routeToLeaderEnabled = true; + routeToLeaderEnabled = false; /** * Placeholder used to auto populate a column with the commit timestamp. @@ -318,8 +318,8 @@ class Spanner extends GrpcService { } as {} as GrpcServiceConfig; super(config, options); - if (options.routeToLeaderEnabled === false) { - this.routeToLeaderEnabled = false; + if (options.routeToLeaderEnabled === true) { + this.routeToLeaderEnabled = true; } this.options = options; diff --git a/test/spanner.ts b/test/spanner.ts index eeca9579d..0fb559b87 100644 --- a/test/spanner.ts +++ b/test/spanner.ts @@ -1491,14 +1491,14 @@ describe('Spanner with mock server', () => { }); describe('LeaderAwareRouting', () => { - let spannerWithLARDisabled: Spanner; - let instanceWithLARDisabled: Instance; + let spannerWithLAREnabled: Spanner; + let instanceWithLAREnabled: Instance; - function newTestDatabaseWithLARDisabled( + function newTestDatabaseWithLAREnabled( options?: SessionPoolOptions, queryOptions?: IQueryOptions ): Database { - return instanceWithLARDisabled.database( + return instanceWithLAREnabled.database( `database-${dbCounter++}`, options, queryOptions @@ -1506,18 +1506,18 @@ describe('Spanner with mock server', () => { } before(() => { - spannerWithLARDisabled = new Spanner({ + spannerWithLAREnabled = new Spanner({ servicePath: 'localhost', port, sslCreds: grpc.credentials.createInsecure(), - routeToLeaderEnabled: false, + routeToLeaderEnabled: true, }); // Gets a reference to a Cloud Spanner instance and database - instanceWithLARDisabled = spannerWithLARDisabled.instance('instance'); + instanceWithLAREnabled = spannerWithLAREnabled.instance('instance'); }); it('should execute with leader aware routing enabled in a read/write transaction', async () => { - const database = newTestDatabase(); + const database = newTestDatabaseWithLAREnabled(); await database.runTransactionAsync(async tx => { await tx!.runUpdate({ sql: insertSql, @@ -1539,7 +1539,7 @@ describe('Spanner with mock server', () => { }); it('should execute with leader aware routing disabled in a read/write transaction', async () => { - const database = newTestDatabaseWithLARDisabled(); + const database = newTestDatabase(); await database.runTransactionAsync(async tx => { await tx!.runUpdate({ sql: insertSql,