-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
sessiontagging1.js
193 lines (177 loc) · 7.31 KB
/
sessiontagging1.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* Copyright (c) 2019, 2023, Oracle and/or its affiliates. */
/******************************************************************************
*
* This software is dual-licensed to you under the Universal Permissive License
* (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
* 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
* either license.
*
* If you elect to accept the software under the Apache License, Version 2.0,
* the following applies:
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* NAME
* sessiontagging1.js
*
* DESCRIPTION
* Shows a simple connection callback function to set the "session
* state" of pooled connections when the requested connection tag
* does not match the connection's current tag.
*
* Each connection in a connection pool can retain state (such as
* ALTER SESSION values) from when the connection was previously
* used. Connection tagging allows the connection state to be
* recorded and later checked. This removes the overhead of
* unnecessarily re-executing ALTER SESSION commands on re-used
* connections.
*
* When using Oracle Client libraries 12.2 or later, then
* sessionCallback can alternatively be a string containing the name
* of a PL/SQL procedure - see documentation.
*
* Run this script and experiment sending web requests. For example
* send 20 requests with a concurrency of 4:
* ab -n 20 -c 4 http://127.0.0.1:7000/
*
*****************************************************************************/
'use strict';
Error.stackTraceLimit = 50;
const http = require('http');
const oracledb = require('oracledb');
const dbConfig = require('./dbconfig.js');
const httpPort = 7000;
// This example requires node-oracledb Thick mode.
//
// Thick mode requires Oracle Client or Oracle Instant Client libraries. On
// Windows and macOS Intel you can specify the directory containing the
// libraries at runtime or before Node.js starts. On other platforms (where
// Oracle libraries are available) the system library search path must always
// include the Oracle library path before Node.js starts. If the search path
// is not correct, you will get a DPI-1047 error. See the node-oracledb
// installation documentation.
let clientOpts = {};
// On Windows and macOS Intel platforms, set the environment
// variable NODE_ORACLEDB_CLIENT_LIB_DIR to the Oracle Client library path
if (process.platform === 'win32' || (process.platform === 'darwin' && process.arch === 'x64')) {
clientOpts = { libDir: process.env.NODE_ORACLEDB_CLIENT_LIB_DIR };
}
oracledb.initOracleClient(clientOpts); // enable node-oracledb Thick mode
// initSession() will be invoked internally when each brand new pooled
// connection is first used, or when a getConnection() call requests a
// connection tag and a connection without an identical tag is
// returned. Its callback function 'callbackFn' should be invoked only
// when all desired session state has been set.
// This implementation assumes that every pool.getConnection() will
// request a tag having the format USER_TZ=X, where X is a valid
// Oracle timezone. See sessiontagging2.js for a more complete
// implementation.
function initSession(connection, requestedTag, callbackFn) {
console.log(`In initSession. requested tag: ${requestedTag}, actual tag: ${connection.tag}`);
const tagParts = requestedTag.split('=');
if (tagParts[0] != 'USER_TZ') {
callbackFn(new Error('Error: Only property USER_TZ is supported'));
return;
}
// Execute the session state change. Note: if you have multiple SQL
// statements to execute, put them in a single anonymous PL/SQL
// block for efficiency.
connection.execute(
`ALTER SESSION SET TIME_ZONE = '${tagParts[1]}'`,
(err) => {
connection.tag = requestedTag; // Record the connection's new state
callbackFn(err);
}
);
}
async function init() {
try {
await oracledb.createPool({
user: dbConfig.user,
password: dbConfig.password,
connectString: dbConfig.connectString,
sessionCallback: initSession,
poolMin: 1,
poolMax: 4,
poolIncrement: 1
});
// Create HTTP server and listen on port httpPort
const server = http.createServer();
server.listen(httpPort)
.on('request', handleRequest)
.on('error', (err) => {
console.error('HTTP server problem: ' + err);
})
.on('listening', () => {
console.log('Server running at http://localhost:' + httpPort);
});
} catch (err) {
console.error('init() error: ' + err.message);
}
}
async function handleRequest(request, response) {
let connection;
// This would normally be determined by some other means, such as user
// preference, geo location, etc.
const sessionTagNeeded = Math.random() > 0.5 ?
"USER_TZ=UTC" : "USER_TZ=Australia/Melbourne";
try {
// Get a connection from the default connection pool, requesting
// one with a given tag.
// Depending on the parallelism that the app is invoked with (and
// the random setting of sessionTag), getConnection() will either:
// (i) find a connection with the requested tag already in the
// connection pool. initSession() will not be invoked.
// (ii) If a connection with the requested tag is not available
// in the pool, then a connection with a new session (i.e. no
// tag) is used. Alternatively a connection with a different
// tag is used if matchAnyTag is true. In both these cases the
// requested tag and actual tag do not match, so initSession()
// will be invoked before getConnection() returns. This lets
// the desired session state be set.
connection = await oracledb.getConnection({poolAlias: 'default', tag: sessionTagNeeded /*, matchAnyTag: true */});
const result = await connection.execute(`SELECT TO_CHAR(CURRENT_DATE, 'DD-Mon-YYYY HH24:MI') FROM DUAL`);
console.log(`getConnection() tag needed was ${sessionTagNeeded}\n ${result.rows[0][0]}`);
} catch (err) {
console.error(err.message);
} finally {
if (connection) {
try {
console.log(` Closing connection tag is ${connection.tag}`);
await connection.close();
} catch (err) {
console.error(err.message);
}
}
response.end();
}
}
async function closePoolAndExit() {
console.log("\nTerminating");
try {
// Get the 'default' pool from the pool cache and close it (force
// closed after 3 seconds).
// If this hangs, you may need DISABLE_OOB=ON in a sqlnet.ora file.
// This setting should not be needed if both Oracle Client and Oracle
// Database are 19c (or later).
await oracledb.getPool().close(3);
process.exit(0);
} catch (err) {
console.error(err.message);
process.exit(1);
}
}
process
.once('SIGTERM', closePoolAndExit)
.once('SIGINT', closePoolAndExit);
init();