Skip to content

Commit 727fa21

Browse files
committed
Connection and protocol optimizations
1 parent 51fb040 commit 727fa21

File tree

14 files changed

+260
-54
lines changed

14 files changed

+260
-54
lines changed

doc/src/release_notes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Common Changes
3131
Thin Mode Changes
3232
+++++++++++++++++
3333

34+
#) Add support to improve Connection Establishment Latency for DB
35+
versions 23.3 onwards.
36+
3437
#) Fixed bug in handling server to client piggybacks which are sometimes
3538
received during Authentcation phase.
3639
`Issue #1589 <https://github.com/oracle/node-oracledb/issues/1589>`__.

lib/thin/connection.js

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const ThinResultSetImpl = require('./resultSet.js');
3232
const ThinLobImpl = require("./lob.js");
3333
const Protocol = require("./protocol/protocol.js");
3434
const { BaseBuffer } = require('./protocol/buffer.js');
35-
const {NetworkSession:nsi, getConnectionInfo} = require("./sqlnet/networkSession.js");
35+
const {NetworkSession:nsi} = require("./sqlnet/networkSession.js");
3636
const { Statement } = require("./statement");
3737
const thinUtil = require('./util');
3838
const sqlNetConstants = require('./sqlnet/constants.js');
@@ -49,6 +49,8 @@ const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
4949
class TDSBuffer extends BaseBuffer {
5050
}
5151

52+
const connectionCookies = new Map();
53+
5254
class ThinConnectionImpl extends ConnectionImpl {
5355

5456
/**
@@ -457,6 +459,26 @@ class ThinConnectionImpl extends ConnectionImpl {
457459
return (this._pool) ? true : false;
458460
}
459461

462+
//---------------------------------------------------------------------------
463+
// getConnectionCookieByUUID()
464+
//
465+
// It fetches from map which keeps keyname as UUID+ServiceName.
466+
// UUID identifies the CDB instance and ServiceName identifies the
467+
// entity within a PDB which uniquely identifies a pdb instance.
468+
//---------------------------------------------------------------------------
469+
getConnectionCookieByUUID(uuid) {
470+
let cookie;
471+
if (uuid) {
472+
const key = uuid + ((this.serviceName) ? this.serviceName : this.sid);
473+
cookie = connectionCookies.get(key);
474+
if (!cookie) {
475+
cookie = {};
476+
connectionCookies.set(key, cookie);
477+
}
478+
}
479+
return cookie;
480+
}
481+
460482
/**
461483
*
462484
* @param {object} params Configuration of the connection
@@ -495,6 +517,7 @@ class ThinConnectionImpl extends ConnectionImpl {
495517
this._clientInfoModified = false;
496518
this._module = "";
497519
this._moduleModified = false;
520+
this._drcpEnabled = false;
498521
this.serviceName = '';
499522
this.remoteAddress = '';
500523
this.comboKey = null; // used in changePassword API
@@ -503,11 +526,21 @@ class ThinConnectionImpl extends ConnectionImpl {
503526
finalizationRegistry.register(this, this.nscon);
504527
await this.nscon.connect(params);
505528

506-
this._connInfo = (this.isPooled()) ? params._connInfo.map((x) => x) :
507-
await getConnectionInfo(params);
508-
this._drcpEnabled = String(this._connInfo[0]).toLowerCase() === 'pooled';
509-
this.serviceName = this._connInfo[2];
510-
this.purity = this._connInfo[3] | constants.PURITY_DEFAULT;
529+
let serverType;
530+
if (this.isPooled()) {
531+
serverType = params._connInfo[0];
532+
this.serviceName = params._connInfo[2];
533+
this.purity = params._connInfo[3] | constants.PURITY_DEFAULT;
534+
this.sid = params._connInfo[4];
535+
} else {
536+
serverType = this.nscon.getOption(sqlNetConstants.SERVERTYPE);
537+
this.serviceName = this.nscon.getOption(sqlNetConstants.SVCNAME);
538+
this.sid = this.nscon.getOption(sqlNetConstants.SID);
539+
this.purity = this.nscon.getOption(sqlNetConstants.PURITY) | constants.PURITY_DEFAULT;
540+
}
541+
if (serverType) {
542+
this._drcpEnabled = serverType.toLowerCase() === 'pooled';
543+
}
511544
this.remoteAddress = this.nscon.getOption(sqlNetConstants.REMOTEADDR);
512545
this.connectionClass = params.connectionClass;
513546

@@ -533,15 +566,40 @@ class ThinConnectionImpl extends ConnectionImpl {
533566
}
534567

535568
try {
536-
let message = new messages.ProtocolMessage(this);
537-
await this._protocol._processMessage(message);
538-
message = new messages.DataTypeMessage(this);
539-
await this._protocol._processMessage(message);
540-
message = new messages.AuthMessage(this, params);
541-
// Does OSESSKEY for non-token Authentication else OAUTH
542-
await this._protocol._processMessage(message);
569+
const cookie = this.getConnectionCookieByUUID(this.nscon.dbUUID);
570+
this.nscon.dbUUID = null;
571+
const protocolMessage = new messages.ProtocolMessage(this);
572+
const dataTypeMessage = new messages.DataTypeMessage(this);
573+
const authMessage = new messages.AuthMessage(this, params);
574+
let sentCookie = false;
575+
if (cookie && cookie.populated) {
576+
const cookieMessage = new messages.ConnectionCookieMessage(this);
577+
cookieMessage.cookie = cookie;
578+
cookieMessage.protocolMessage = protocolMessage;
579+
cookieMessage.dataTypeMessage = dataTypeMessage;
580+
cookieMessage.authMessage = authMessage;
581+
await this._protocol._processMessage(cookieMessage);
582+
sentCookie = true;
583+
} else {
584+
await this._protocol._processMessage(protocolMessage);
585+
}
586+
if (!sentCookie || !cookie.populated) {
587+
await this._protocol._processMessage(dataTypeMessage);
588+
// Does OSESSKEY for non-token Authentication else OAUTH
589+
await this._protocol._processMessage(authMessage);
590+
if (cookie && !cookie.populated) {
591+
cookie.protocolVersion = protocolMessage.serverVersion;
592+
cookie.serverBanner = protocolMessage.serverBanner;
593+
cookie.charsetID = this._protocol.caps.charSetID;
594+
cookie.ncharsetID = this._protocol.caps.nCharsetId;
595+
cookie.flags = protocolMessage.serverFlags;
596+
cookie.compileCaps = Buffer.from(protocolMessage.serverCompileCaps);
597+
cookie.runtimeCaps = Buffer.from(protocolMessage.serverRunTimeCaps);
598+
cookie.populated = true;
599+
}
600+
}
543601
if (!params.token) { // non-token Authentication
544-
await this._protocol._processMessage(message); // OAUTH
602+
await this._protocol._processMessage(authMessage); // OAUTH
545603
}
546604
} catch (err) {
547605
this.nscon.disconnect();

lib/thin/protocol/buffer.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,24 @@ class BaseBuffer {
923923
}
924924
}
925925

926+
//---------------------------------------------------------------------------
927+
// writeUB2()
928+
//
929+
// Writes an unsigned integer (up to 2 bytes in length) in variable length
930+
// format to the buffer.
931+
//---------------------------------------------------------------------------
932+
writeUB2(value) {
933+
if (value === 0) {
934+
this.writeUInt8(0);
935+
} else if (value <= 0xff) {
936+
this.writeUInt8(1);
937+
this.writeUInt8(value);
938+
} else {
939+
this.writeUInt8(2);
940+
this.writeUInt16BE(value);
941+
}
942+
}
943+
926944
//---------------------------------------------------------------------------
927945
// writeUB8()
928946
//

lib/thin/protocol/constants.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,10 @@ module.exports = {
425425
TNS_MSG_TYPE_FLUSH_OUT_BINDS: 19,
426426
TNS_MSG_TYPE_BIT_VECTOR: 21,
427427
TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK: 23,
428-
TNS_MSG_TYPE_IMPLICIT_RESULTSET: 27,
429428
TNS_MSG_TYPE_ONEWAY_FN: 26,
429+
TNS_MSG_TYPE_IMPLICIT_RESULTSET: 27,
430+
TNS_MSG_TYPE_RENEGOTIATE: 28,
431+
TNS_MSG_TYPE_COOKIE: 30,
430432

431433
// parameter keyword numbers,
432434
TNS_KEYWORD_NUM_CURRENT_SCHEMA: 168,

lib/thin/protocol/messages/base.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,19 @@ class Message {
200200
if (opcode === constants.TNS_SERVER_PIGGYBACK_LTXID) {
201201
const num_bytes = buf.readUB4();
202202
if (num_bytes > 0) {
203-
buf.skipBytes(num_bytes);
203+
buf.skipBytesChunked();
204204
}
205205
} else if ((opcode === constants.TNS_SERVER_PIGGYBACK_QUERY_CACHE_INVALIDATION)
206206
|| (opcode === constants.TNS_SERVER_PIGGYBACK_TRACE_EVENT)) {
207207
// pass
208208
} else if (opcode === constants.TNS_SERVER_PIGGYBACK_OS_PID_MTS) {
209-
buf.skipUB2();
210-
buf.skipBytesChunked();
209+
const numDtys = buf.readUB2();
210+
buf.skipUB1();
211+
buf.skipBytes(numDtys);
211212
} else if (opcode === constants.TNS_SERVER_PIGGYBACK_SYNC) {
212213
buf.skipUB2(); // skip number of DTYs
213214
buf.skipUB1(); // skip length of DTYs
214-
const num_elements = buf.readUB2();
215+
const num_elements = buf.readUB4();
215216
buf.skipBytes(1); // skip length
216217
for (let i = 0; i < num_elements; i++) {
217218
let temp16 = buf.readUB2();
@@ -223,14 +224,14 @@ class Message {
223224
buf.skipBytesChunked();
224225
}
225226
buf.skipUB2(); // skip flags
226-
buf.skipUB4(); // skip overall flags
227227
}
228+
buf.skipUB4(); // skip overall flags
228229
} else if (opcode === constants.TNS_SERVER_PIGGYBACK_EXT_SYNC) {
229230
buf.skipUB2();
230231
buf.skipUB1();
231232
} else if (opcode === constants.TNS_SERVER_PIGGYBACK_AC_REPLAY_CONTEXT) {
232233
buf.skipUB2(); // skip number of DTYs
233-
buf.skipUB1(1); // skip length of DTYs
234+
buf.skipUB1(); // skip length of DTYs
234235
buf.skipUB4(); // skip flags
235236
buf.skipUB4(); // skip error code
236237
buf.skipUB1(); // skip queue
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) 2023 Oracle and/or its affiliates.
2+
3+
//-----------------------------------------------------------------------------
4+
//
5+
// This software is dual-licensed to you under the Universal Permissive License
6+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
7+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
8+
// either license.
9+
//
10+
// If you elect to accept the software under the Apache License, Version 2.0,
11+
// the following applies:
12+
//
13+
// Licensed under the Apache License, Version 2.0 (the "License");
14+
// you may not use this file except in compliance with the License.
15+
// You may obtain a copy of the License at
16+
//
17+
// https://www.apache.org/licenses/LICENSE-2.0
18+
//
19+
// Unless required by applicable law or agreed to in writing, software
20+
// distributed under the License is distributed on an "AS IS" BASIS,
21+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22+
// See the License for the specific language governing permissions and
23+
// limitations under the License.
24+
//
25+
//-----------------------------------------------------------------------------
26+
27+
'use strict';
28+
29+
const constants = require("../constants.js");
30+
const Message = require("./base.js");
31+
32+
/**
33+
* Executes a Fast Negotiation RPC function.
34+
* It sends Protocol Negotiation, Cookie Message, Datatype Negotiation
35+
* and OSESS Key RPC in single round trip.
36+
*
37+
* @class ConnectionCookieMessage
38+
* @extends {Message}
39+
*/
40+
class ConnectionCookieMessage extends Message {
41+
42+
/**
43+
* Serializes the ConnectionCookieMessage function arguments
44+
*
45+
* @param {object} buf input arguments
46+
*/
47+
encode(buf) {
48+
this.protocolMessage.encode(buf);
49+
buf.writeUInt8(constants.TNS_MSG_TYPE_COOKIE);
50+
buf.writeUInt8(1); // cookie version
51+
buf.writeUInt8(this.cookie.protocolVersion);
52+
buf.writeUInt16LE(this.cookie.charsetID);
53+
buf.writeUInt8(this.cookie.flags);
54+
buf.writeUInt16LE(this.cookie.ncharsetID);
55+
buf.writeBytesWithLength(this.cookie.serverBanner);
56+
buf.writeBytesWithLength(this.cookie.compileCaps);
57+
buf.writeBytesWithLength(this.cookie.runtimeCaps);
58+
this.dataTypeMessage.encode(buf);
59+
this.authMessage.encode(buf);
60+
}
61+
62+
processMessage(buf, messageType) {
63+
if (messageType === constants.TNS_MSG_TYPE_RENEGOTIATE) {
64+
this.cookie.populated = false;
65+
} else if (messageType === constants.TNS_MSG_TYPE_PROTOCOL) {
66+
this.protocolMessage.processMessage(buf, messageType);
67+
} else if (messageType === constants.TNS_MSG_TYPE_DATA_TYPES) {
68+
this.dataTypeMessage.processMessage(buf, messageType);
69+
} else {
70+
this.authMessage.processMessage(buf, messageType);
71+
}
72+
}
73+
74+
}
75+
76+
module.exports = ConnectionCookieMessage;

lib/thin/protocol/messages/dataType.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,18 @@ const Message = require("./base.js");
3838
*/
3939
class DataTypeMessage extends Message {
4040

41-
processMessage(buf) {
42-
while (true) { // eslint-disable-line
43-
const dataType = buf.readUInt16BE();
44-
if (dataType === 0)
45-
break;
46-
const convDataType = buf.readUInt16BE();
47-
if (convDataType !== 0)
48-
buf.skipBytes(4);
41+
processMessage(buf, messageType) {
42+
if (messageType === constants.TNS_MSG_TYPE_DATA_TYPES) {
43+
while (true) { // eslint-disable-line
44+
const dataType = buf.readUInt16BE();
45+
if (dataType === 0)
46+
break;
47+
const convDataType = buf.readUInt16BE();
48+
if (convDataType !== 0)
49+
buf.skipBytes(4);
50+
}
51+
} else {
52+
super.processMessage(buf, messageType);
4953
}
5054
}
5155

lib/thin/protocol/messages/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ const PingMessage = require('./ping.js');
3737
const ProtocolMessage = require('./protocol.js');
3838
const RollbackMessage = require('./rollback.js');
3939
const SessionReleaseMessage = require('./sessionRelease.js');
40+
const ConnectionCookieMessage = require('./connectionCookie.js');
4041

4142
module.exports = {
4243
AuthMessage,
4344
CommitMessage,
45+
ConnectionCookieMessage,
4446
DataTypeMessage,
4547
ExecuteMessage,
4648
FetchMessage,

lib/thin/protocol/messages/protocol.js

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,35 @@ class ProtocolMessage extends Message {
5050
buf.writeUInt8(0);
5151
}
5252

53-
decode(buf) {
54-
const message_type = buf.readUInt8();
55-
if (message_type === constants.TNS_MSG_TYPE_PROTOCOL) {
56-
buf.skipBytes(2); // skip protocol array
57-
let x = buf.readUInt8();
58-
while (x !== 0) { // skip server banner
59-
x = buf.readUInt8();
60-
}
61-
buf.skipBytes(2); // character set ID
62-
buf.skipUB1(1); // skip server flags
63-
const num_elem = buf.readUInt16LE();
64-
if (num_elem > 0) { // skip elements
65-
buf.skipBytes(num_elem * 5);
66-
}
67-
const fdoLen = buf.readUInt16BE();
68-
const fdo = buf.readBytes(fdoLen);
69-
const ix = 6 + fdo[5] + fdo[6];
70-
buf.caps.nCharsetId = (fdo[ix + 3] << 8) + fdo[ix + 4];
71-
buf.caps.adjustForServerCompileCaps(buf.readBytesWithLength());
72-
buf.caps.adjustForServerRuntimeCaps(buf.readBytesWithLength());
53+
processMessage(buf, messageType) {
54+
if (messageType === constants.TNS_MSG_TYPE_PROTOCOL) {
55+
this.processProtocolInfo(buf);
56+
} else {
57+
super.processMessage(buf, messageType);
58+
}
59+
}
60+
61+
processProtocolInfo(buf) {
62+
this.serverVersion = buf.readUInt8();
63+
buf.skipUB1();
64+
this.serverBanner = buf.readNullTerminatedBytes(48);
65+
buf.caps.charSetID = buf.readUInt16LE();
66+
this.serverFlags = buf.readUInt8();
67+
const num_elem = buf.readUInt16LE();
68+
if (num_elem > 0) { // skip elements
69+
buf.skipBytes(num_elem * 5);
70+
}
71+
const fdoLen = buf.readUInt16BE();
72+
const fdo = buf.readBytes(fdoLen);
73+
const ix = 6 + fdo[5] + fdo[6];
74+
buf.caps.nCharsetId = (fdo[ix + 3] << 8) + fdo[ix + 4];
75+
this.serverCompileCaps = Buffer.from(buf.readBytesWithLength());
76+
if (this.serverCompileCaps) {
77+
buf.caps.adjustForServerCompileCaps(this.serverCompileCaps);
78+
}
79+
this.serverRunTimeCaps = Buffer.from(buf.readBytesWithLength());
80+
if (this.serverRunTimeCaps) {
81+
buf.caps.adjustForServerRuntimeCaps(this.serverRunTimeCaps);
7382
}
7483
}
7584

0 commit comments

Comments
 (0)