Skip to content

Commit 58e1799

Browse files
committed
Added call timeouts when using Oracle client libraries 18.1 and higher
1 parent c10f891 commit 58e1799

File tree

4 files changed

+195
-12
lines changed

4 files changed

+195
-12
lines changed

doc/api.md

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,12 @@ limitations under the License.
111111
4. [Connection Class](#connectionclass)
112112
- 4.1 [Connection Properties](#connectionproperties)
113113
- 4.1.1 [`action`](#propconnaction)
114-
- 4.1.2 [`clientId`](#propconnclientid)
115-
- 4.1.3 [`module`](#propconnmodule)
116-
- 4.1.4 [`oracleServerVersion`](#propconnoracleserverversion)
117-
- 4.1.5 [`oracleServerVersionString`](#propconnoracleserverversionstring)
118-
- 4.1.6 [`stmtCacheSize`](#propconnstmtcachesize)
114+
- 4.1.2 [`callTimeout`](#propconncalltimeout)
115+
- 4.1.3 [`clientId`](#propconnclientid)
116+
- 4.1.4 [`module`](#propconnmodule)
117+
- 4.1.5 [`oracleServerVersion`](#propconnoracleserverversion)
118+
- 4.1.6 [`oracleServerVersionString`](#propconnoracleserverversionstring)
119+
- 4.1.7 [`stmtCacheSize`](#propconnstmtcachesize)
119120
- 4.2 [Connection Methods](#connectionmethods)
120121
- 4.2.1 [`break()`](#break)
121122
- 4.2.2 [`changePassword()`](#changepassword)
@@ -232,6 +233,7 @@ limitations under the License.
232233
- 8.9 [Connections and High Availability](#connectionha)
233234
- 8.9.1 [Fast Application Notification (FAN)](#connectionfan)
234235
- 8.9.2 [Runtime Load Balancing (RLB)](#connectionrlb)
236+
- 8.9.3 [Database Call Timeouts](#dbcalltimeouts)
235237
- 8.10 [Optional Client Configuration Files](#tnsadmin)
236238
9. [SQL Execution](#sqlexecution)
237239
- 9.1 [SELECT Statements](#select)
@@ -1187,7 +1189,7 @@ This property may be overridden when [creating a connection pool](#createpool).
11871189
See [Connection Pool Pinging](#connpoolpinging) for more discussion.
11881190

11891191
This property was added in node-oracledb 1.12. It was disabled when
1190-
using Oracle Client 12.2+ until node-oracledb 3.0.
1192+
using Oracle Client 12.2 (and later) until node-oracledb 3.0.
11911193

11921194
##### Example
11931195

@@ -1946,7 +1948,26 @@ This is a write-only property. Displaying a Connection object will
19461948
show a value of `null` for this attribute. See
19471949
[End-to-end Tracing, Mid-tier Authentication, and Auditing](#endtoend).
19481950

1949-
#### <a name="propconnclientid"></a> 4.1.2 `connection.clientId`
1951+
#### <a name="propconncalltimeout"></a> 4.1.2 `connection.callTimeout`
1952+
1953+
```
1954+
Number callTimeout
1955+
```
1956+
1957+
Sets the maximum number of milliseconds that each underlying
1958+
round-trip between node-oracledb and Oracle Database may take. Each
1959+
node-oracledb method or operation may make zero or more round-trips.
1960+
The `callTimeout` value applies to each round-trip individually, not
1961+
to the sum of all round-trips. Time spent processing in node-oracledb
1962+
before or after the completion of each round-trip is not counted.
1963+
1964+
See [Database Call Timeouts](#dbcalltimeouts) for more information.
1965+
1966+
This property was added in node-oracledb 3.0. An exception will occur
1967+
if node-oracledb is not using Oracle client library version 18.1 or
1968+
later.
1969+
1970+
#### <a name="propconnclientid"></a> 4.1.3 `connection.clientId`
19501971

19511972
```
19521973
writeonly String clientId
@@ -1960,7 +1981,7 @@ This is a write-only property. Displaying a Connection object will
19601981
show a value of `null` for this attribute. See
19611982
[End-to-end Tracing, Mid-tier Authentication, and Auditing](#endtoend).
19621983

1963-
#### <a name="propconnmodule"></a> 4.1.3 `connection.module`
1984+
#### <a name="propconnmodule"></a> 4.1.4 `connection.module`
19641985

19651986
```
19661987
writeonly String module
@@ -1972,7 +1993,7 @@ This is a write-only property. Displaying a Connection object will
19721993
show a value of `null` for this attribute. See
19731994
[End-to-end Tracing, Mid-tier Authentication, and Auditing](#endtoend).
19741995

1975-
#### <a name="propconnoracleserverversion"></a> 4.1.4 `connection.oracleServerVersion`
1996+
#### <a name="propconnoracleserverversion"></a> 4.1.5 `connection.oracleServerVersion`
19761997

19771998
```
19781999
readonly Number oracleServerVersion
@@ -1988,7 +2009,7 @@ instead of 1803000000.
19882009

19892010
This property was added in node-oracledb 1.3.
19902011

1991-
#### <a name="propconnoracleserverversionstring"></a> 4.1.5 `connection.oracleServerVersionString`
2012+
#### <a name="propconnoracleserverversionstring"></a> 4.1.6 `connection.oracleServerVersionString`
19922013

19932014
```
19942015
readonly String oracleServerVersionString
@@ -2003,7 +2024,7 @@ libraries. Otherwise it will show the base release such as
20032024

20042025
This property was added in node-oracledb 2.2.
20052026

2006-
#### <a name="propconnstmtcachesize"></a> 4.1.6 `connection.stmtCacheSize`
2027+
#### <a name="propconnstmtcachesize"></a> 4.1.7 `connection.stmtCacheSize`
20072028

20082029
```
20092030
readonly Number stmtCacheSize
@@ -4879,7 +4900,6 @@ application being aware of any service disruption.
48794900
For a more information on FAN see the [whitepaper on Fast Application
48804901
Notification][97].
48814902
4882-
48834903
#### <a name="connectionrlb"></a> 8.9.2 Runtime Load Balancing (RLB)
48844904
48854905
[Oracle Database RAC][93] users with [Oracle Database (RLB)][65]
@@ -4894,6 +4914,44 @@ requests across RAC instances.
48944914
For a more information on RLB, see the [whitepaper on Fast Application
48954915
Notification][97].
48964916
4917+
#### <a name="dbcalltimeouts"></a> 8.9.3 Database Call Timeouts
4918+
4919+
When node-oracledb is using Oracle client 18 libraries, a millisecond
4920+
timeout for database calls can be set with
4921+
[`connection.callTimeout`](#propconncalltimeout).
4922+
4923+
The call timeout is on each individual round-trip between
4924+
node-oracledb and Oracle Database. Each node-oracledb method or
4925+
operation may require zero or more round-trips to Oracle Database.
4926+
The `callTimeout` value applies to each round-trip individually, not
4927+
to the sum of all round-trips. Time spent processing in node-oracledb
4928+
before or after the completion of each round-trip is not counted.
4929+
4930+
- If the time from the start of any one round-trip to the completion
4931+
of that same round-trip exceeds `callTimeout` milliseconds, then the
4932+
operation is halted and error "DPI-1067: call timeout of N ms exceeded
4933+
with ORA-XXX" is returned.
4934+
4935+
- In the case where a node-oracledb operation requires more than one
4936+
round-trip and each round-trip takes less than `callTimeout`
4937+
milliseconds, then no timeout will occur, even if the sum of all
4938+
round-trip calls exceeds `callTimeout`.
4939+
4940+
- If no round-trip is required, the operation will never be
4941+
interrupted.
4942+
4943+
After a timeout occurs, node-oracledb attempts to clean up the
4944+
internal connection state. The cleanup is allowed to take another
4945+
`callTimeout` milliseconds.
4946+
4947+
If the cleanup was successful, an *DPI-1067* error will be returned and the
4948+
application can continue to use the connection.
4949+
4950+
For small values of `callTimeout`, the connection cleanup may not
4951+
complete successfully within the additional `callTimeout` period. In
4952+
this case an *ORA-3114* is returned and the connection will no longer
4953+
be usable. It should be closed.
4954+
48974955
### <a name="tnsadmin"></a> 8.10 Optional Client Configuration Files
48984956
48994957
Optional Oracle Client configuration files are read when node-oracledb

examples/calltimeout.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. */
2+
3+
/******************************************************************************
4+
*
5+
* You may not use the identified files except in compliance with the Apache
6+
* License, Version 2.0 (the "License.")
7+
*
8+
* You may obtain a copy of the License at
9+
* http://www.apache.org/licenses/LICENSE-2.0.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
*
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* NAME
19+
* calltimeout.js
20+
*
21+
* DESCRIPTION
22+
* Shows how to time out long running database calls.
23+
* See https://oracle.github.io/node-oracledb/doc/api.html#dbcalltimeouts
24+
* Node-oracledb must be using Oracle Client 18c libraries, or greater.
25+
*
26+
* This example uses Async/Await of Node 8.
27+
*
28+
*****************************************************************************/
29+
30+
let oracledb = require("oracledb");
31+
let dbConfig = require('./dbconfig.js');
32+
33+
// "Sleep" in the database for a number of seconds.
34+
// This uses an inefficent sleep implementation instead of
35+
// dbms_lock.sleep() which not all users can use.
36+
const sql = `
37+
DECLARE
38+
t DATE := SYSDATE + (:sleepsec * (1/86400));
39+
BEGIN
40+
LOOP
41+
EXIT WHEN t <= SYSDATE;
42+
END LOOP;
43+
END;`;
44+
45+
let dboptime = 4; // seconds the DB operation will take
46+
let timeout = 2; // seconds the application will wait for the DB operation
47+
48+
async function runTest() {
49+
let connection;
50+
51+
try {
52+
connection = await oracledb.getConnection(dbConfig);
53+
connection.callTimeout = timeout * 1000; // milliseconds
54+
console.log("Database call timeout set to " + connection.callTimeout / 1000 + " seconds");
55+
console.log("Executing a " + dboptime + " second DB operation");
56+
await connection.execute(sql, [dboptime]);
57+
console.log("DB operation successfully completed");
58+
} catch (err) {
59+
if (err.message.startsWith('DPI-1067:') || err.errorNum === 3114)
60+
console.log('DB operation was stopped after exceeding the call timeout');
61+
else
62+
console.error(err);
63+
} finally {
64+
if (connection) {
65+
try {
66+
await connection.close();
67+
} catch (err) {
68+
console.error(err);
69+
}
70+
}
71+
}
72+
}
73+
74+
runTest();

src/njsConnection.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ void njsConnection::Init(Local<Object> target)
113113
Nan::New<v8::String>("oracleServerVersionString").ToLocalChecked(),
114114
njsConnection::GetOracleServerVersionString,
115115
njsConnection::SetOracleServerVersionString);
116+
Nan::SetAccessor(tpl->InstanceTemplate(),
117+
Nan::New<v8::String>("callTimeout").ToLocalChecked(),
118+
njsConnection::GetCallTimeout, njsConnection::SetCallTimeout);
116119

117120
connectionTemplate_s.Reset(tpl);
118121
Nan::Set(target, Nan::New<v8::String>("Connection").ToLocalChecked(),
@@ -2803,3 +2806,49 @@ NAN_SETTER(njsConnection::SetOracleServerVersionString)
28032806
PropertyIsReadOnly("oracleServerVersionString");
28042807
}
28052808

2809+
2810+
//-----------------------------------------------------------------------------
2811+
// njsConnection::GetCallTimeout()
2812+
// Get accessor of "callTimeout" property.
2813+
//-----------------------------------------------------------------------------
2814+
NAN_GETTER(njsConnection::GetCallTimeout)
2815+
{
2816+
uint32_t callTimeout;
2817+
2818+
njsConnection *connection = (njsConnection*) ValidateGetter(info);
2819+
if (!connection)
2820+
return;
2821+
if (!connection->IsValid()) {
2822+
info.GetReturnValue().Set(Nan::Undefined());
2823+
return;
2824+
}
2825+
if (dpiConn_getCallTimeout(connection->dpiConnHandle, &callTimeout) < 0) {
2826+
njsOracledb::ThrowDPIError();
2827+
return;
2828+
}
2829+
info.GetReturnValue().Set(callTimeout);
2830+
}
2831+
2832+
2833+
//-----------------------------------------------------------------------------
2834+
// njsConnection::SetCallTimeout()
2835+
// Set accessor of "callTimeout" property.
2836+
//-----------------------------------------------------------------------------
2837+
NAN_SETTER(njsConnection::SetCallTimeout)
2838+
{
2839+
uint32_t callTimeout;
2840+
2841+
njsConnection *connection = (njsConnection*) ValidateSetter(info);
2842+
if (!connection)
2843+
return;
2844+
if (!value->IsUint32()) {
2845+
string errMsg = njsMessages::Get(errInvalidPropertyValue,
2846+
"callTimeout");
2847+
Nan::ThrowError(errMsg.c_str());
2848+
return;
2849+
}
2850+
callTimeout = Nan::To<uint32_t>(value).FromJust();
2851+
if (dpiConn_setCallTimeout(connection->dpiConnHandle, callTimeout) < 0)
2852+
njsOracledb::ThrowDPIError();
2853+
}
2854+

src/njsConnection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ class njsConnection: public njsCommon {
156156
static NAN_GETTER(GetAction);
157157
static NAN_GETTER(GetOracleServerVersion);
158158
static NAN_GETTER(GetOracleServerVersionString);
159+
static NAN_GETTER(GetCallTimeout);
159160

160161
// Define Setter Accessors to properties
161162
static NAN_SETTER(SetStmtCacheSize);
@@ -164,6 +165,7 @@ class njsConnection: public njsCommon {
164165
static NAN_SETTER(SetAction);
165166
static NAN_SETTER(SetOracleServerVersion);
166167
static NAN_SETTER(SetOracleServerVersionString);
168+
static NAN_SETTER(SetCallTimeout);
167169

168170
// internal methods
169171
static bool CreateVarBuffer(njsVariable *var, njsBaton *baton);

0 commit comments

Comments
 (0)