Closed amitagarwal-dev closed 1 year ago
@cjbj
Thanks for reporting this. We are currently looking into it.
Have you tried using the latest node-oracledb version (6.0.3) with Thin mode #1587
Yes, i tried 6.0.3 also.
Same issue is there
@sharadraju issue 1 is happening only when i am trying to connect db via a vpn, if i connect directly to my office network via a lan then it is reconnecting when network comes online.
if vpn connection drops then it is not reconnecting even through i reconnected again.
Do we have any timeout for getting connection from the pool? It will be better if i get some timeout error while getting connection from the pool, so that admin can check the connectivity.
A network hang could be mitigated by https://node-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#limiting-the-time-to-open-new-connections
If the pool is already at max size, then queueTimeout
plays a role, see https://node-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#connection-pool-queue
You also have the connectTimeout
and transportConnectTimeout
parameters introduced from node-oracledb 6.0 onwards available with the Thin mode. Please see https://node-oracledb.readthedocs.io/en/latest/api_manual/oracledb.html#createpoolpoolattrspooltimeout
@cjbj @sharadraju thanks for the reply!
let me go through the links. Also i have an update for issue 2.
Actually Line 47 is preventing it from returning from the function where i am throwing the error.
Because connection is already (DPI-1080: connection was closed by ORA-3156)
closed, so my connection.close() is somewhere got stuck. let me see how to handle this.
Give me some time, after the implementation i will close the issue by myself.
connection.close() is getting stucked infinitely after (DPI-1080: connection was closed by ORA-3156)
.
Did you happen try (though it is unlikely to have changed), 6.0 in Thick or Thin mode?
connection.close() is getting stucked infinitely after
(DPI-1080: connection was closed by ORA-3156)
.
I tried this in 5.5.0
You also have the
connectTimeout
andtransportConnectTimeout
parameters introduced from node-oracledb 6.0 onwards available with the Thin mode. Please see https://node-oracledb.readthedocs.io/en/latest/api_manual/oracledb.html#createpoolpoolattrspooltimeout
@sharadraju i am changing connect string in 5.5.0, will it work? I added (CONNECT_TIMEOUT=40)
function connect(database) {
if (database.ISSID == 1) {
let connectString = `(DESCRIPTION=(CONNECT_TIMEOUT=40)(ADDRESS=(PROTOCOL=TCP)(HOST=${database.HOST})(PORT=${database.PORT}))(CONNECT_DATA=(SID=${database.DATABASE})))`;
return connectString;
} else {
let connectString = `(DESCRIPTION=(CONNECT_TIMEOUT=40)(ADDRESS=(PROTOCOL=TCP)(HOST=${database.HOST})(PORT=${database.PORT}))(CONNECT_DATA=(SERVICE_NAME=${database.DATABASE})))`;
return connectString;
}
}
Yes CONNECT_TIMEOUT in the connect string should work.
connection.close() is getting stucked infinitely after
(DPI-1080: connection was closed by ORA-3156)
.
async closePoolConnection() {
await this._pool.close();
}
Can you call method after connection close. Program will not terminate until pool is open.
@monita1208 i didn't get your point.
I am following the below flow in my application:
oracledb.createPool(option)
. pool.getConnection()
. connection.execute()
. connection.close()
. So if network drop is there after step 2 then step 4 is hanging.
@monita1208 i didn't get your point.
I am following the below flow in my application:
- create a pool by calling
oracledb.createPool(option)
.- get a connection from the pool by calling
pool.getConnection()
.- execute a query by calling
connection.execute()
.- close the connection by calling
connection.close()
.- close the pool once shutting down the application.
So if network drop is there after step 2 then step 4 is hanging.
@amitagarwal-dev , since the network drop is there in step 3, you might want to set connection.callTimeout()
to an appropriate value. See callTimeout
@sharadraju step 3 already thrown an error i.e (DPI-1080: connection was closed by ORA-3156)
.
I guess callTimeout will be helpful when i am working with a valid connection but here error itself is saying connection was closed by ORA-3156
.
Yes CONNECT_TIMEOUT in the connect string should work.
@sharadraju let me test
The reason to use callTimeout
would be to throw the Timeout error and return control to the application before the DPI-1080 error is thrown. i.e., before the connection becomes invalid
But, based on your test case, it seems you would want us to handle the DPI-1080 error and then let the application continue further.
We are trying to replicate your scenario internally, though. Please give us some time to get back on this.
CONNECT_TIMEOUT would only be applicable at the time of connection creation. Since the connection is already created in your case, this parameter may not be helpful.
wait this._pool.close();
You almost definitely should pass a draintime argument here otherwise the pool may not close if connections are still in use. Check the doc and examples.
Regarding the pool sizing, is your Node.js process really capable of handling 1000 connections efficiently (it might be, but is it something to check)? Should you use a fixed pool (see the pool sizing doc)?
poolMax: 1000, poolMin: 10,
The timeout looks low and could lead to more DB instability if connections have to be recreated frequently:
poolTimeout: 15,
@sharadraju if you see line 91, i already tried callTimeout
but no effect on the error and the issue.
wait this._pool.close();
You almost definitely should pass a draintime argument here otherwise the pool may not close if connections are still in use. Check the doc and examples.
Regarding the pool sizing, is your Node.js process really capable of handling 1000 connections efficiently (it might be, but is it something to check)? Should you use a fixed pool (see the pool sizing doc)?
poolMax: 1000, poolMin: 10,
The timeout looks low and could lead to more DB instability if connections have to be recreated frequently:
poolTimeout: 15,
@cjbj these are random values right now, my production configuration is different. I already did a load testing to tune these parameters. You helped me on this earlier (#1457 and #1465 ). Currently i am just using one connection.
We are trying to reproduce the issue . Can you set EXPIRE_TIME=1 and confirm the behaviour.
With 18c+ client libraries it can be added as (EXPIRE_TIME=n) to the DESCRIPTION section of a connect descriptor
With 19c+ client libraries it can be used via Easy Connect: host/service?expire_time=n.
With 21c client libraries it can be used in a client-side sqlnet.ora.
Please update init() of shared program to include pool close. Due to poolMin = 10 and 1 connection close with DPI error, still 9 connections left in pool. Pool close call will terminate open connections.
async function init(){
let option ={
user: 'user',
password: 'pwd' ,
connectString: 'CS',
poolMax: 1000, //The maximum number of connections to which a connection pool can grow.
poolMin: 10, //The minimum number of connections which will be open.
poolTimeout: 15, //The number of seconds after which idle connections (unused in the pool) are terminated.
queueTimeout: 3000, //Number of milliseconds after which connection requests waiting in the connection request queue are terminated.
enableStatistics: true,
queueMax: 1000,
poolAlias: "WORKFLOW",
poolPingInterval : 10
};
try {
await orarepo.getPool(option);
console.log("pool created");
await callSp();
} catch(error) {
console.log("init error", error);
} finally {
await orarepo.closePoolConnection();
}
}
Can you also try below sample program. You can try with node driver version 6.0.3 and 5.5 (thick mode) 1) Pool creation done 2) A connection got created 3) Sleep for 10 sec started 4) Disconnect from VPN 5) 10 sec sleep time over 6) Execute a query. This will throw DPI-1080 7) Close connection Program doesn't go on infinite wait.
const oracledb = require('oracledb');
oracledb.initOracleClient({ libDir: 'Oracle client library location' });
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function run() {
const config = {
user: 'user',
password: 'pwd',
poolMin: 0,
poolMax: 10,
poolTimeout: 120,
queueTimeout: 10000,
enableStatistics: true,
poolAlias: "10",
connectString : 'CS',
};
let pool = await oracledb.createPool(config);
const conn1 = await pool.getConnection();
conn1.callTimeout = 1;
console.log("sleep started, disconnect from VPN");
await sleep(10000);
console.log("n/w dropped and sleep over");
console.log('waiting....')
try {
const res = await conn1.execute(`select 1 from dual`);
console.log('res=' + JSON.stringify(res));
} catch (error) {
console.log(error);
}
await conn1.close();
await pool.close(0);
}
run();
@amitagarwal-dev , For the hang, we are able to simulate it locally. Can you set the following in client sqlnet.ora file , run the client application and see if it resolves the hang and terminates gracefully.
SQLNET.RECV_TIMEOUT=5
SQLNET.EXPIRE_TIME=1
@monita1208 i tried your second code snippet, it is working fine but when i give poolmin=10 then again await conn1.close();
is hanging.
Also conn1.callTimeout = 1;
is causing DPI-1080: connection was closed by ORA-3156
error in case of not stopping my vpn,
may be because 1ms is too less for a round trip timeout, if i am increasing it to 1000ms then it is working fine.
So i guess callTimeout is coming into the picture when we have a valid connection.
I tried with 5.5.0.
Do you have any idea for this:
when i give poolmin=10 then again `await conn1.close();` is hanging.
@sudarshan12s i am not able to find sqlnet.ora
file in my machine. Do you know the path? i tried with locate sqlnet.ora
command but no lead.
@amitagarwal-dev You can update callTimeout value as per your need. 1ms is low value and it is used here only for testing reported issue.
-- when i give poolmin=10 then again await conn1.close();
is hanging.
Program is going into irresponsive/hang state when we update sample program with poolMin 10. Solution is to update sqlnet.ora file with value : SQLNET.RECV_TIMEOUT=5
For more info on this parameter refer: https://docs.oracle.com/cd/E11882_01/network.112/e10835/sqlnet.htm#NETRF227
You can use echo $TNS_ADMIN
to get location of sqlnet.ora file.
"TNS_ADMIN is an environment variable that points to the directory where the SQL*Net configuration files (like sqlnet.ora and tnsnames.ora)".
@amitagarwal-dev the node-oracledb doc explains where you can find an existing, or create a new, sqlnet.ora file. See the section Using Optional Oracle Configuration Files.
@monita1208 let me try and confirm.
@monita1208 below code is working fine with SQLNET.RECV_TIMEOUT=10
, now await conn1.close()
is not hanging infinitely but it is taking a lot of time in order to resolve the await. i guess it depends on the poolmin value. Below is the code and the log.
const oracledb = require('oracledb');
//oracledb.initOracleClient({ libDir: 'Oracle client library location' });
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function connect(database) {
if (database.ISSID == 1) {
let connectString = `(DESCRIPTION=(CONNECT_TIMEOUT=40)(ADDRESS=(PROTOCOL=TCP)(HOST=${database.HOST})(PORT=${database.PORT}))(CONNECT_DATA=(SID=${database.DATABASE})))`;
return connectString;
} else {
let connectString = `(DESCRIPTION=(CONNECT_TIMEOUT=40)(ADDRESS=(PROTOCOL=TCP)(HOST=${database.HOST})(PORT=${database.PORT}))(CONNECT_DATA=(SERVICE_NAME=${database.DATABASE})))`;
return connectString;
}
}
async function run() {
let config ={
user: 'base_fi_6',
password: 'base_fi_6' ,
connectString: connect({HOST: '10.10.30.130', PORT: 1521, DATABASE: 'orcl' }),
poolMax: 100, //The maximum number of connections to which a connection pool can grow.
poolMin: 10, //The minimum number of connections which will be open.
poolTimeout: 15, //The number of seconds after which idle connections (unused in the pool) are terminated.
queueTimeout: 3000, //Number of milliseconds after which connection requests waiting in the connection request queue are terminated.
enableStatistics: true,
queueMax: 1000,
poolAlias: "WORKFLOW",
};
let pool = await oracledb.createPool(config);
console.log("sleep started, disconnect from VPN",new Date());
await sleep(20000);
console.log("n/w dropped and sleep over",new Date());
console.log('waiting for pool.getConnection() ....',new Date())
const conn1 = await pool.getConnection();
conn1.callTimeout = 1000;
console.log("sleep started, disconnect from VPN",new Date());
await sleep(20000);
console.log("n/w dropped and sleep over",new Date());
console.log('waiting....',new Date())
try {
const res = await conn1.execute(`select 1 from dual`);
console.log('res=' + JSON.stringify(res));
} catch (error) {
console.log(error,new Date());
}
console.log('doing conn1.close()',new Date());
await conn1.close();
console.log('doing pool.close(0)',new Date());
await pool.close(0);
}
run().catch(error => {
console.log("catch error: ",error);
});
* Logs with poolmin=2
`NOTE: I stopped my vpn just after line 1`.
### Points to check:
* See the time difference between line 12 and 13 in both the cases.
@amitagarwal-dev You can check if time taken for conn1 closure is consistent with multiple attempts, when poolMin is 10 and poolMin is 2.
When you are closing conn1 using await conn1.close();
, it is going to close single connection only. At time of pool close, remaining connections will be taken care.
At pool closure, if poolMin is 10 then it will take more time as compared to poolMin 2.
@monita1208 whatever logs I shared in which I am closing the current connection only, so it should take even time. When I will close pool then only poolmin should come into the picture.
@monita1208 can you simulate the same?
any update?
@amitagarwal-dev You can always check the open and in-use count of connections using the pool parameters
pool.connectionsOpen
and pool.connectionsInUse
.
Before closing the connection parameter results are-
pool.connectionsOpen = (poolMin value) 10
pool.connectionsInUse = 1
After closing the connection parameter results are-
pool.connectionsOpen = 0
pool.connectionsInUse = 0
Due to the network issue, the connection which we were trying to close is already closed resulting in a check and closure of the remaining connections in the pool. Thus, you were getting ~3mins time in closing a connection with poolMin as 10 and ~30sec time in closing a connection with poolMin as 2.
In case of no network issues and at the time of connection closure, only a single connection will be closed and the closure of the remaining connections happens at the time of pool close.
@monita1208 is this the expected behaviour or its a bug?
@amitagarwal-dev This is expected behaviour.
@monita1208 don't you think that conn1.close() should operate on that connection only? Instead of closing all the connection in the pool, I guess pool should close the other invalid connection if it is not taken.
In a production environment, open connection will be more. Awaiting for close() will take a lot of time.
@amitagarwal-dev This is pool feature of clearing bad connections from pool.
ok thanks for helping @monita1208 @sudarshan12s @cjbj
@amitagarwal-dev The pool check in the case of an invalid connection has been the behavior for all of our Oracle Database drivers now. We do not want a pool of invalid connections to remain open, once the network has gone bad. What you are requesting is a behavior change, across all the drivers.
We will however evaluate it internally and see if we can provide an option of closing a single bad connection from a pool (without the pool check), in case of a network outage in a future release.
@sharadraju I don't know whether its a problem or not but whoever is taking the connection from the pool is closing the connection using conn.close(), so close() scope should remain to that connection only. Why close() is interfering with the other connection?
Issue:
1)
pool.getConnection()
is hanging infinitely if network connection is dropped. I need to calloracledb.createPool(option);
in order to establish a pool connection again. There is no reconnect by default or an error from oracledb to handle this.2) While getting DPI1080 error from
connection.execute()
, i don't know why my promise is not returning. In case of other error same code is working fine.Steps i am following to recreate both the issues:
1) first issue:
pool.getConnection()
after 20 sec which never returns or throws an error.2) second issue:
connection.execute()
followed bypool.getConnection()
after 5 sec which throws an error (DPI-1080: connection was closed by ORA-3156
) but my async function never returns even though i am throwing that error.Code
Full code is here: https://github.com/amitagarwal-dev/oracledb-test.git