4. Connecting to Oracle Database
Connections between node-oracledb and Oracle Database are used for executing SQL, PL/SQL, and for SODA.
By default, node-oracledb runs in a ‘Thin’ mode which connects directly to Oracle Database. This mode does not need Oracle Client libraries. However, when node-oracledb uses these libraries, then the driver is said to be in ‘Thick’ mode and has additional functionality. See Enabling node-oracledb Thick Mode.
Connections can be either:
Standalone: These connections are useful when the application needs a single connection to a database. Connections are created by calling
oracledb.getConnection().Pooled: These connections are important for performance when applications frequently connect and disconnect from the database. Oracle high availability features in the pool implementation mean that small pools can also be useful for applications that want a few connections available for infrequent use. Pools are created with
oracledb.createPool()at application initialization time, and thenpool.getConnection()can be called to obtain a connection from a pool.
Many connection behaviors can be controlled by node-oracledb options. Other settings can be configured in Oracle Net files or in connection strings. These include limiting the amount of time that opening a connection can take, or enabling network encryption.
4.1. Standalone Connections
Standalone connections are database connections that do not use a node-oracledb
connection pool. They are useful for applications that use a single connection
to a database. You can create connections by calling
oracledb.getConnection() and passing:
A database username
The database password for that user
Node-oracledb also supports external authentication and token-based authentication so passwords do not need to be in the application. For information on other authentication options supported, see Authentication Options.
An example passing credentials is:
const oracledb = require('oracledb');
async function run() {
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // contains the hr schema password
connectString : "localhost/FREEPDB1"
});
const result = await connection.execute(`SELECT city FROM locations`);
console.log("Result is:", result.rows);
await connection.close(); // Always close connections
}
run();
Connections must be released with connection.close() when they are no
longer needed. Make sure to release connections in all code paths including in
error handlers.
Note
If you do not explicitly close a connection, you may experience a short delay when the application terminates. This is due to the timing behavior of Node.js garbage collection which needs to free the connection reference.
4.2. Oracle Net Services Connection Strings
The connectString property of oracledb.getConnection() and
oracledb.createPool() is the Oracle Database Oracle Net Services
Connection String (commonly abbreviated as “connection string”) that
identifies which database service to connect to. The connectString value
can be one of Oracle Database’s naming methods:
An Oracle Easy Connect string
A Connect Descriptor string
A TNS Alias from a local tnsnames.ora file or external naming service
The SID of a local Oracle Database instance
If a connect string is not specified, the empty string “” is used which indicates to connect to the local, default database.
The connectionString property is an alias for connectString. Use
only one of the properties.
Note
Creating a connection in node-oracledb Thin mode always requires a
connection string, or the database host name and service name, to be
specified. Bequeath connections cannot be made. The Thin mode does not
reference Oracle environment variables such as ORACLE_SID,
TWO_TASK, or LOCAL.
4.2.1. Easy Connect Syntax for Connection Strings
An Easy Connect string is often the simplest
connection string to use when creating connections and pools. For example, to
connect to the Oracle Database service orclpdb1 that is running on the
host mydbmachine.example.com with the default Oracle Database port 1521,
use:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "mydbmachine.example.com/orclpdb1"
});
If the database is using a non-default port, for example 1984, the port must be given:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "mydbmachine.example.com:1984/orclpdb1"
});
The Easy Connect syntax has been extended in recent versions of Oracle Database client since its introduction in Oracle 10g. Check the Easy Connect Naming method in Oracle Net Service Administrator’s Guide for the syntax in your version of the Oracle Client libraries. The Easy Connect syntax supports Oracle Database service names. It cannot be used with the older System Identifiers (SID).
In node-oracledb Thin mode, any unknown Easy Connect options are ignored and are not passed to the database. See Connection String Differences for more information.
If you are using node-oracledb Thick mode with Oracle Client 19c (or later), the latest Easy Connect syntax allows the use of multiple hosts or ports, along with optional entries for the wallet location, the distinguished name of the database server, and even lets some network configuration options be set. Oracle’s Technical Paper on Easy Connect Plus Syntax discusses the syntax. The Easy Connect syntax means that tnsnames.ora or sqlnet.ora files are not needed for some further common connection scenarios.
For example, if a firewall terminates idle connections every five minutes, you
may decide it is more efficient to keep connections alive instead of having the
overhead of recreation. Your connection string could be
"mydbmachine.example.com/orclpdb1?expire_time=2" to send packets every two
minutes with the EXPIRE_TIME feature. The general
recommendation for EXPIRE_TIME is to use a value that is slightly less
than half of the termination period.
Another common use case for Easy Connect is to limit the amount of time
required to open a connection. For example, to return an error after 15 seconds
if a connection cannot be established to the database, use
"mydbmachine.example.com/orclpdb1?connect_timeout=15".
4.2.2. Connect Descriptors
Connect Descriptors can be embedded directly in node-oracledb applications. For example:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=mymachine.example.com)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))"
});
4.2.3. TNS Aliases for Connection Strings
Connect Descriptors are commonly stored in optional
tnsnames.ora configuration files and associated with
a TNS Alias. This alias can be used directly in the connectString
parameter of oracledb.getConnection() and
oracledb.createPool(). For example, given a file
/opt/oracle/config/tnsnames.ora with the following content:
sales =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.example.com)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)
You can connect in node-oracledb Thin mode by passing the TNS Alias “sales”
(case insensitive) as the connectString value using the following code:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "sales"
configDir : "/opt/oracle/config"
});
See Optional Oracle Net Configuration for more options on how
node-oracledb locates the tnsnames.ora files. Note that in node-oracledb
Thick mode, the configuration file must be in a default location or be set
during initialization, not at connection time.
TNS Aliases may also be defined in a directory server.
For general information on tnsnames.ora files, see the Oracle Net
documentation on tnsnames.ora.
Note
When using node-oracledb in Thin mode, the tnsnames.ora file will not
be automatically located. The file’s directory must be explicitly
specified when connecting.
You can retrieve the TNS Aliases that are defined in the
tnsnames.ora file using
oracledb.getNetworkServiceNames(). The directory that contains the
tnsnames.ora file can be specified in the configDir property of
getNetworkServiceNames(). For example, if the
tnsnames.ora file is stored in the /opt/oracle/config directory and
contains the following TNS Aliases:
sales =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.example.com)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ORCL)
)
)
finance =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mydbmachine.example.com)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = ORCLPDB1)
)
)
To retrieve the TNS Aliases from the above tnsnames.ora file, you can use:
const serviceNames = await oracledb.getNetworkServiceNames("/opt/oracle/config");
console.log(serviceNames);
This prints ['sales', 'finance'] as the output.
4.2.4. Centralized Configuration Provider URL Connection Strings
A Centralized Configuration Provider URL connection string allows node-oracledb configuration information to be stored centrally in OCI Object Storage, OCI Vault, local file, Azure App Configuration, or in a Azure Key Vault. Using a provider URL, node-oracledb will access the information stored in the configuration provider and use it to connect to Oracle Database.
The database connect descriptor and any database credentials stored in a configuration provider will be used by any language driver that accesses the configuration. Other driver-specific sections can exist. Node-oracledb will use the settings that are in a section with the prefix “njs”, and will ignore other sections.
The Centralized Configuration Provider URL must begin with “config-<configuration-provider>://” where the configuration-provider value can be set to ociobject, ocivault, file, azure, or azurevault depending on the location of your configuration information.
For example, consider the following connection configuration stored in OCI Object Storage:
{
"connect_descriptor": "localhost/orclpdb",
"user": "scott",
"njs": {
"poolMin": 2,
"poolMax": 10,
"prefetchRows": 2
"stmtCacheSize": 30
}
}
You could use this to create a connection pool by specifying the
connectString connection string parameter as shown below:
const pool = await oracledb.createPool({
connectString : "config-ociobject://abc.oraclecloud.com/n/abcnamespace/b/abcbucket/o/abcobject?oci_tenancy=abc123&oci_user=ociuser1&oci_fingerprint=ab:14:ba:13&oci_key_file=ociabc/ocikeyabc.pem"
});
const connection = await pool.getConnection();
The pool will be created using the pool properties defined in the configuration provider.
See Centralized Configuration Providers for more information.
4.2.5. JDBC and Oracle SQL Developer Connection Strings
The node-oracledb connection string syntax is different from Java JDBC and the common Oracle SQL Developer syntax. If these JDBC connection strings reference a service name like:
jdbc:oracle:thin:@hostname:port/service_name
for example:
jdbc:oracle:thin:@mydbmachine.example.com:1521/orclpdb1
then use Oracle’s Easy Connect syntax in node-oracledb:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "mydbmachine.example.com:1521/orclpdb1"
});
You may need to remove JDBC-specific parameters from the connection string and use node-oracledb alternatives.
Alternatively, if a JDBC connection string uses an old-style Oracle system identifier SID, and there is no service name available:
jdbc:oracle:thin:@hostname:port:sid
for example:
jdbc:oracle:thin:@mydbmachine.example.com:1521:orcl
then either embed the Connect Descriptor:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=mymachine.example.com)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SID=ORCL)))"
});
or create a Net Service Name:
# tnsnames.ora
finance =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mydbmachine.example.com)(PORT = 1521))
(CONNECT_DATA =
(SID = ORCL)
)
)
This can be referenced in node-oracledb:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "finance"
});
4.3. Authenticating to Oracle Database
When connecting to Oracle Database, authentication plays a key role in establishing an authorized connection. Node-oracledb supports various Oracle Database authentication methods which are listed below:
The Oracle Client libraries used by node-oracledb Thick mode may support additional authentication methods that are configured independently of the driver.
4.4. Centralized Configuration Providers
Centralized Configuration Providers allow the
storage and management of database connection credentials and application
configuration information in a central location. These providers allow you to
separately store configuration information from the code of your application.
The information that can be stored includes the connect descriptor, database
credentials, wallet, and node-oracledb specific properties such as connection
pool settings. Node-oracledb can use the centrally stored information to
connect to Oracle Database with oracledb.getConnection() and
oracledb.createPool().
The following configuration providers are supported by node-oracledb:
Configuration Information Stored in Centralized Configuration Providers
The configuration information can be stored in the above-mentioned configuration providers by using specific keys which are listed in the table below.
Key |
Description |
Required or Optional |
|---|---|---|
|
The database user name. |
Optional |
|
The password of the database user. For OCI Object Storage, OCI Vault, Azure Key Vault, and File configuration providers, the value is an object which contains the following parameters:
For Azure App Configuration, the password is the reference to the Azure Key Vault and Secret. Warning Storing passwords of type base64 or text in the JSON file for File, OCI Object Storage, and Azure App Configuration configuration providers should only ever be used in development or test environments. It can be used with Azure Vault and OCI Vault configuration providers. |
Optional |
|
The database connect descriptor. |
Required |
|
The reference to the wallet. For OCI Object Storage, OCI Vault, Azure Key Vault, and File configuration providers, the value is an object itself and contains the same parameters that are listed in the password parameter. This can only be used in node-oracledb Thin mode. For Azure App Configuration, this parameter is the reference to the Azure Key Vault and Secret that contains the wallet as the value. |
Optional |
|
The number of seconds that node-oracledb should keep the configuration information cached. See Caching Configuration Information. Added in version 6.10. |
Optional |
|
The node-oracledb specific properties. The properties that can be stored in OCI Object Storage include |
Optional |
Precedence of Properties
Defining attributes in multiple places is not recommended. However, if you
have defined the values of user and password in both the
application and the configuration provider, then the values defined in the
application will have the higher precedence.
If you are using Thin mode and have defined the node-oracledb specific
properties in both the application and the configuration provider, then the
values defined in the configuration provider will have the higher precedence.
If you have defined the walletContent property in the application and the
wallet_location key in the configuration provider, then the value defined
in the configuration provider will have the higher precedence.
If you are using Thick mode and have defined the node-oracledb properties in
an oraaccess.xml file (see Optional Oracle Client Configuration
File), the configuration provider, and the application, then
the values defined in the oraaccess.xml file will have the highest
precedence followed by the configuration provider and then the application.
4.4.1. Using a File Centralized Configuration Provider
The File Centralized Configuration Provider enables the storage and management of Oracle Database connection information using local files. This configuration provider support is available from node-oracledb 6.9 onwards.
To use a File Centralized Configuration Provider, you must:
Store the connection information in a JSON file on your local file system. See Connection Information for File Configuration Provider.
Use a File configuration provider connection string URL in the
connectStringproperty of connection and pool creation methods.
Note that node-oracledb caches configurations by default, see Caching Configuration Information.
Connection Information for File Configuration Provider
The connection information stored in a JSON file must contain at least a
connect_descriptor key to specify the database connection string.
Optionally, you can store the database user name, password, wallet location,
and node-oracledb properties. For details on the information that can be
stored in this configuration provider, see Configuration Information Stored in Configuration Providers.
An example of a JSON file that can be used with File Centralized Configuration Provider is:
{
"connect_descriptor": "(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)
(host=adb.region.oraclecloud.com))(connect_data=(service_name=dbsvcname))
(security=(ssl_server_dn_match=yes)))",
"user": "scott",
"password": {
"type": "base64",
"value": "dGlnZXI="
}
"njs": {
"stmtCacheSize": 30,
"prefetchRows": 2,
"poolMin": 2,
"poolMax": 10
}
}
This encodes the password as base64. See Using an OCI Object Storage Centralized Configuration Provider for other password examples.
File Centralized Configuration Provider connectString Syntax
The connectString parameter for oracledb.getConnection() and
oracledb.createPool() calls should use a connection string URL in the
format:
config-file://<filePath>?[alias=]
For example, if you have the above JSON file stored in
/opt/oracle/my-config1.json, you can connect to Oracle Database using:
const connection = await oracledb.getConnection({
connectString : "config-file:///opt/oracle/my-config1.json"
});
The parameters of the connection string URL format are detailed in the table below.
Parameter |
Description |
Required or Optional |
|---|---|---|
config-file |
Indicates that the centralized configuration provider is a file in your local system. |
Required |
<filePath> |
The file path and name of the JSON file that contains the configuration information. |
Required |
alias |
The connection alias name used to identify a specific configuration. |
Optional |
Multiple alias names can be defined in a JSON file as shown below:
{
"production": {
"connect_descriptor": "(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)
(host=adb.region.oraclecloud.com))(connect_data=(service_name=dbsvcname))
(security=(ssl_server_dn_match=yes)))"
},
"testing": {
"connect_descriptor": "(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)
(host=adb.region.oraclecloud.com))(connect_data=(service_name=dbsvcname))
(security=(ssl_server_dn_match=yes)))",
"user": "scott",
"password": {
"type": "base64",
"value": "dGlnZXI="
}
}
}
If you have this configuration stored in a JSON file in
/opt/oracle/my-config2.json, you can connect to Oracle Database using:
const connection = await oracledb.getConnection({
connectString : "config-file:///opt/oracle/my-config2.json?alias=production"
});
4.4.2. Using an OCI Object Storage Centralized Configuration Provider
The Oracle Cloud Infrastructure (OCI) Object Storage configuration provider enables the storage and management of Oracle Database connection information as JSON in OCI Object Storage. This configuration provider support was introduced in node-oracledb 6.6.
To use an OCI Object Storage Centralized Configuration Provider, you must:
Upload a JSON file that contains the connection information into an OCI Object Storage Bucket. See Connection Information for OCI Object Storage Centralized Configuration Provider.
Also, see Uploading an Object Storage Object to a Bucket and the Oracle Database Net Services Administrator’s Guide for the steps.
Install the required OCI modules. See Install Modules for OCI Object Storage Configuration Provider.
Load the ociobject plugin in your application using
require('oracledb/plugins/configProviders/ociobject').Use an OCI Object Storage connection string URL in the
connectStringproperty of connection and pool creation methods.
Note that node-oracledb caches configurations by default, see Caching Configuration Information.
Connection Information for OCI Object Storage Configuration Provider
The connection information stored in a JSON file must contain a
connect_descriptor key. Optionally, you can specify the database user
name, password, wallet location, and node-oracledb properties. The database
password can also be stored securely using OCI Vault. For details
on the information that can be stored in this configuration provider, see
Configuration Information Stored in Configuration Providers.
An example of a JSON file that can be used with OCI Object Centralized Storage Configuration Provider is:
{
"connect_descriptor": "(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)
(host=adb.region.oraclecloud.com))(connect_data=(service_name=dbsvcname))
(security=(ssl_server_dn_match=yes)))",
"user": "scott",
"password": {
"type": "ocivault",
"value": "ocid1.vaultsecret.my-secret-id"
},
"wallet_location": {
"type": "ocivault",
"value": "ocid1.vaultwallet.my-wallet-id"
},
"njs": {
"stmtCacheSize": 30,
"prefetchRows": 2,
"poolMin": 2,
"poolMax": 10
}
}
OCI Object Storage Centralized Configuration Provider connectString Syntax
The connectString parameter for oracledb.getConnection() and
oracledb.createPool() calls should use a connection string URL in the
format:
config-ociobject://<objectstorage-name>/n/<namespaceName>/b/<bucketName>/o/
<objectName>[?key=<networkServiceName>&<option1>=<value1>&<option2>=<value2>...]
For example, a connection string to access OCI Object Storage and connect to Oracle Database is:
const connection = await oracledb.getConnection({
connectString : "config-ociobject://abc.oraclecloud.com/n/abcnamespace/b/abcbucket/o/abcobject?oci_tenancy=abc123&oci_user=ociuser1&oci_fingerprint=ab:14:ba:13&oci_key_file=ociabc/ocikeyabc.pem"
});
The parameters of the connection string URL format are detailed in the table below.
Parameter |
Description |
Required or Optional |
|---|---|---|
config-ociobject |
Indicates that the configuration provider is OCI Object Storage. |
Required |
<objectstorage-name> |
The URL of OCI Object Storage endpoint. |
Required |
<namespaceName> |
The OCI Object Storage namespace where the JSON file is stored. |
Required |
<bucketName> |
The OCI Object Storage bucket name where the JSON file is stored. |
Required |
<objectName> |
The JSON file name. |
Required |
key=<networkServiceName> |
The network service name or alias if the JSON file contains one or more network service names. |
Optional |
<options> and <values> |
The authentication method and its corresponding parameters to access the OCI Object Storage configuration provider. You can specify one of the following authentication methods: API Key-based authentication, Instance Principal Authentication, and Resource Principal Authentication. See OCI Object Storage and OCI Vault Configuration Provider Authentication Methods for more information. |
Optional |
4.4.3. Using an OCI Vault Centralized Configuration Provider
Oracle Cloud Infrastructure (OCI) Vault Centralized Configuration Provider enables the storage and management of Oracle Database connection information as JSON objects. This Centralized Configuration Provider support is available from node-oracledb 6.9 onwards.
To use an OCI Vault Centralized Configuration Provider, you must:
Enter and save the connection information as a secret in OCI Vault by using the Manual secret generation method. The connection information must be entered as a JSON object. See Connection Information for OCI Vault Centralized Configuration Provider.
Also, see Creating a Secret in OCI Vault for the steps.
Install the required OCI modules. See Install Modules for OCI Vault Provider.
Load the ocivault plugin in your application using
require('oracledb/plugins/configProviders/ocivault').Use an OCI Vault connection string URL in the
connectStringproperty of connection and pool creation methods.
Note that node-oracledb caches configurations by default. See Caching Configuration Information.
Connection Information for OCI Vault Configuration Provider
The JSON object must contain a connect_descriptor key. Optionally, you can
specify the database user name, password, wallet location, and node-oracledb
properties. For details on the information that can be stored in this
Centralized Configuration Provider, see Configuration Information Stored in Configuration Providers.
The JSON object syntax for OCI Vault Centralized Configuration Provider is the same as the syntax for OCI Object Storage. See OCI Object Storage example.
OCI Vault Centralized Configuration Provider connectString Syntax
The connectString parameter for oracledb.getConnection() and
oracledb.createPool() calls should use a connection string URL in the
format:
config-ocivault://<ocidvault>?[<option1>=<value1>&<option2>=<value2>...]
For example, a connection string to access OCI Vault and connect to Oracle Database is:
const connection = await oracledb.getConnection({
connectString : "config-ocivault://ocid1.vaultsecret.oc1?oci_tenancy=abc123&oci_user=ociuser1&oci_fingerprint=ab:14:ba:13&oci_key_file=ociabc/ocikeyabc.pem"
});
The parameters of the connection string URL format are detailed in the table below.
Parameter |
Description |
Required or Optional |
|---|---|---|
config-ocivault |
Indicates that the configuration provider is OCI Vault. |
Required |
<ocidvault> |
The OCI vault identifier. |
Required |
<options> and <values> |
The authentication method and corresponding authentication parameters to access the OCI Vault configuration provider. You can specify one of the following authentication methods: API Key-based authentication, Instance Principal Authentication, and Resource Principal Authentication. See OCI Object Storage and OCI Vault Configuration Provider Authentication Methods for more information. |
Optional |
4.4.4. Using an Azure App Centralized Configuration Provider
Azure App Configuration is a cloud-based service provided by Microsoft Azure. It can be used for storage and management of Oracle Database connection information as key-value pairs. This configuration provider support was introduced in node-oracledb 6.6.
To use node-oracledb with Azure App Configuration, you must:
Enter and save your configuration information in your Azure App Configuration Provider as key-value pairs. See Connection Information for Azure App Centralized Configuration Provider.
Install the required Azure Application modules. See Install Modules for Azure App Configuration Provider.
Load the azure plugin in your application using
require('oracledb/plugins/configProviders/azure').Use an Azure App Configuration connection string URL in the
connectStringparameter of connection and pool creation methods.
Note that node-oracledb caches configurations by default, see Caching Configuration Information.
Connection Information for Azure App Configuration Provider
Key-value pairs for stored connection information can be added using the Configuration explorer page of your Azure App Configuration. See Create a key-value in Azure App Configuration for more information. Also, see the Oracle Net Service Administrator’s Guide.
You can organize the key-value pairs under a prefix based on your application’s needs. For example, if you have two applications, Sales and Human Resources, then you can store the relevant configuration information under the prefix sales and the prefix hr.
The key-value pairs must contain the key connect_descriptor which
specifies the database connect descriptor. This can be set using a prefix as
“<prefix>/connect_descriptor”, for example, sales/connect_descriptor.
You can additionally store the database user name using a key such as “<prefix>/user”, and store the password using “<prefix>/password”. For example, sales/user and sales/password. The database password can also be stored securely using Azure Key Vault.
Optional node-oracledb properties can be set using a key such as “<prefix>/njs/<key name>”, for example sales/node-oracledb/poolMin. This is similar to how Oracle Call Interface settings use keys like “<prefix>/oci/<key name>” as shown in Oracle Database Net Services Administrator’s Guide.
For details on the information that can be stored in this configuration provider, see Configuration Information Stored in Configuration Providers.
The following table shows sample configuration information defined using the
Configuration explorer page of your Azure App Configuration provider. The
example uses the prefix test/.
Sample Azure App Configuration Key |
Sample Value |
|---|---|
test/connect_descriptor |
(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)(host=adb.region.oraclecloud.com))(connect_data=(service_name=orcldb_svc1))) |
test/user |
scott |
test/password |
{“uri”:”https://mykeyvault.vault.azure.net/secrets/passwordsalescrm”} |
test/wallet_location |
{“uri”:”https://mykeyvault.vault.azure.net/secrets/walletsalescrm”} |
test/njs |
{“stmtCacheSize”:30, “prefetchRows”:2, “poolMin”:2, “poolMax”:10} |
Azure App Centralized Configuration Provider connectString Syntax
You must define a connection string URL in a specific format in the
connectString property of oracledb.getConnection() or
oracledb.createPool() to access the information stored in Azure App
Configuration. The syntax is:
config-azure://<appconfigname>[?key=<prefix>&label=<value>&<option1>=<value1>&<option2>=<value2>…]
For example, a connection string to access the Azure App Configuration provider and connect to Oracle Database is:
const connection = await oracledb.getConnection({
connectString : "config-azure://aznetnamingappconfig.azconfig.io/?key=test/&azure_client_id=123-456&azure_client_secret=MYSECRET&azure_tenant_id=789-123"
});
The parameters of the connection string URL format are detailed in the table below.
Parameter |
Description |
Required or Optional |
|---|---|---|
config-azure |
Indicates that the configuration provider is Azure App Configuration. |
Required |
<appconfigname> |
The URL of the App configuration endpoint. |
Required |
key=<prefix> |
A key prefix to identify the connection. You can organize configuration information under a prefix as per application requirements. |
Optional |
label=<value> |
The Azure App Configuration label name. |
Optional |
<options>=<values> |
The authentication method and its corresponding parameters to access the Azure App Configuration provider. You can specify one of the following authentication methods: Default Azure Credential, Service Principal with Client Secret, Service Principal with Client Certificate, and Managed Identity. See Azure App Configuration Provider Authentication Methods for more information on these authentication methods and their corresponding parameters. |
Optional |
4.4.5. Using an Azure Key Vault Centralized Configuration Provider
Azure Key Vault is a cloud-based service provided by Microsoft Azure. It can be used for storage and management of Oracle Database connection information as a JSON object. This configuration provider support is available from node-oracledb 6.9 onwards.
To use node-oracledb with Azure Key Vault, you must:
Enter and save the connection information as a secret in your Azure Key Vault. The connection information must be stored as a JSON object. See Connection Information for Azure Key Vault Centralized Configuration Provider.
For information on creating a secret, see Create a secret in Azure Key Vault.
Install the required Azure Application modules. See Install Modules for Azure Vault Configuration Provider.
Load the azurevault plugin in your application using
require('oracledb/plugins/configProviders/azurevault').Use an Azure Key Vault connection string URL in the
connectStringparameter of connection and pool creation methods.
Note that node-oracledb caches configurations by default, see Caching Configuration Information.
Connection Information for Azure Key Vault Configuration Provider
The JSON object must contain a connect_descriptor key. Optionally, you can
specify the database user name, password, wallet location, and node-oracledb
properties. For details on the information that can be stored in this
configuration provider, see Configuration Information Stored in Configuration Providers.
The JSON object syntax for Azure Key Vault configuration provider is same as the syntax for OCI Object Storage, see the OCI Object Storage example.
Azure Key Vault Configuration Provider connectString Syntax
You must define a connection string URL in a specific format in the
connectString property of oracledb.getConnection() or
oracledb.createPool() to access the information stored in Azure Key
Vault. The syntax is:
config-azurevault://<azure key vault url>?[<option1>=<value1>&<option2>=<value2>...]
For example, a connection string to access Azure Key Vault and connect to Oracle Database is:
const connection = await oracledb.getConnection({
connectString : "config-azurevault://https://abc.vault.azure.net/secrets/azurevaultjson?azure_client_id=123-456&azure_client_secret=MYSECRET&azure_tenant_id=789-123"
});
The parameters of the connection string URL format are detailed in the table below.
Parameter |
Description |
Required or Optional |
|---|---|---|
config-azurevault |
Indicates that the configuration provider is Azure Key Vault. |
Required |
<azure secret url> |
The unique identifier of a specific secret stored in Azure Key Vault. |
Required |
<options>=<values> |
The authentication method and its corresponding parameters to access the Azure Key Vault Configuration provider. You can specify one of the following authentication methods: Default Azure Credential, Service Principal with Client Secret, Service Principal with Client Certificate, and Managed Identity. See Azure App Configuration Provider Authentication Methods for more information on these authentication methods and their corresponding parameters. |
Optional |
4.4.6. Caching Configuration Information
Node-oracledb caches configurations obtained from Centralized Configuration Providers. This allows you to reuse the cached configuration information which significantly reduces the number of round-trips to the configuration provider.
You can define the number of seconds that node-oracledb should keep the
information cached by using the config_time_to_live key in a
Centralized Configuration Provider. After the
config_time_to_live duration expires, the cached configuration information
becomes invalid. So, node-oracledb refreshes the cache the next time the
configuration information from the configuration provider is required.
An example of setting the cache time to 12 hours for File or OCI Object Storage Centralized Configuration Providers is:
{
"connect_descriptor": "(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)
(host=adb.region.oraclecloud.com))(connect_data=(service_name=dbsvcname))
(security=(ssl_server_dn_match=yes)))",
"user": "scott",
"password": {
"type": "ocivault",
"value": "ocid1.vaultsecret.my-secret-id"
},
"config_time_to_live": 43200,
"njs": {
"stmtCacheSize": 30,
"prefetchRows": 2,
"poolMin": 2,
"poolMax": 10
}
}
Also, you can use the oracledb.configProviderCacheTimeout property to
specify the number of seconds that node-oracledb should keep the information
cached. The default time is 86,400 seconds (24 hours).
If the config_time_to_live key is defined in a Centralized Configuration
Provider, then this value overrides the value specified in the
oracledb.configProviderCacheTimeout property. If this key is not
defined in a configuration provider, then the value of
oracledb.configProviderCacheTimeout is considered to be
the duration that node-oracledb keeps the information cached.
4.5. Connections, Threads, and Parallelism
To scale and optimize your applications, it is useful to understand how connections interact with Node.js.
4.5.1. Connections and Worker Threads
Node.js has four background worker threads by default (not to be confused with
the newer user space worker_threads module). If you are using node-oracledb Thick mode and open more than
four standalone connections or pooled connections,
such as by increasing pool.poolMax, then you must increase the number
of worker threads available to node-oracledb.
Note
This section on Worker thread pool sizing applies only to node-oracledb
Thick mode. Changing UV_THREADPOOL_SIZE is not needed for node-oracledb
when using Thin mode.
A worker thread pool that is too small can cause a decrease in application performance, deadlocks, or failure in connection requests with the error NJS-040: connection request timeout or NJS-076: connection request rejected.
A Node.js worker thread is used by each node-oracledb Thick mode connection to execute a database statement. Each thread will wait until all round-trips between node-oracledb and the database for the statement are complete. When an application handles a sustained number of user requests, and database operations take some time to execute or the network is slow, then all available threads may be held in use. This prevents other connections from beginning work and stops Node.js from handling more user load.
The thread pool size should be equal to, or greater than, the maximum number of connections. If the application does database and non-database work concurrently, then additional threads could also be required for optimal throughput.
Increase the thread pool size by setting the environment variable
UV_THREADPOOL_SIZE
before starting Node.js. For example, on Linux your package.json may
have a script like:
"scripts": {
"start": "export UV_THREADPOOL_SIZE=10 && node index.js"
},
. . .
Or, on Windows:
"scripts": {
"start": "SET UV_THREADPOOL_SIZE=10 && node index.js"
},
. . .
With these, you can start your application with npm start. This will
allow up to 10 connections to be actively excuting SQL statements in
parallel.
On non-Windows platforms, the value can also be set inside the application. It must be set prior to any asynchronous Node.js call that uses the thread pool:
// !! First file executed. Non-Windows only !!
process.env.UV_THREADPOOL_SIZE = 10
// ... rest of code
If you set UV_THREADPOOL_SIZE too late in the application, or try to
set it this way on Windows, then the setting will be ignored and the
default thread pool size of 4 will still be used. Note that
pool.getStatistics() and pool.logStatistics() can only give
the value of the variable, not the actual size of the thread pool created.
On Linux you can use pstack to see how many threads are actually
running. Node.js will create a small number of threads in addition to
the expected number of worker threads.
The libuv library used by Node.js
12.5 and earlier limits the number of threads to 128. In Node.js 12.6
onward the limit is 1024. You should restrict the maximum number of
connections opened in an application,
that is, poolMax, to a value lower
than UV_THREADPOOL_SIZE. If you have multiple pools, make sure the
sum of all poolMax values is no larger than UV_THREADPOOL_SIZE.
4.5.2. Parallelism on Each Connection
Oracle Database can only execute operations one at a time on each
connection. Examples of operations include connection.execute(),
connection.executeMany(), connection.queryStream(),
connection.getDbObjectClass(), connection.commit(),
connection.close(), SODA calls, and streaming
from Lobs. Multiple connections may be in concurrent
use, but each connection can only do one thing at a time. Code will not
run faster when parallel database operations are attempted using a
single connection.
From node-oracledb 5.2, node-oracledb function calls that use a single connection for concurrent database access will be queued in the JavaScript layer of node-oracledb. In earlier node-oracledb versions, locking occurred in the Oracle Client libraries, which meant many threads could be blocked.
It is recommended to structure your code to avoid parallel operations on
a single connection. For example, avoid using Promise.all() on a
single connection. Similarly, instead of using async.parallel() or
async.each() which call each of their items in parallel, use
async.series() or async.eachSeries(). If you want to repeat a
number of INSERT or UPDATE statements, then use
connection.executeMany().
To rewrite code that uses Promise.all() you could, for example, use
a basic for loop with async/await to iterate through each
action:
async function myfunc() {
const stmts = [
`INSERT INTO ADRESSES (ADDRESS_ID, CITY) VALUES (94065, 'Redwood Shores')`,
`INSERT INTO EMPLOYEES (ADDRESS_ID, EMPLOYEE_NAME) VALUES (94065, 'Jones')`
];
for (const s of stmts) {
await connection.execute(s);
}
}
If you use ESlint for code validation, and it warns about await in
loops for code that
is using a single connection, then disable the no-await-in-loop rule
for these cases.
Another alternative rewrite for Promise.all() is to wrap the SQL
statements in a single PL/SQL block.
Note that using functions like Promise.all() to fetch rows from
nested cursor result sets can result in
inconsistent data.
During development, you can set oracledb.errorOnConcurrentExecute
to true to help identify application code that executes concurrent
database operations on a single connection. Such uses may be logic
errors such as missing await keywords that could lead to unexpected
results. When errorOnConcurrentExecute is set to true, an error
will be thrown so you can identify offending code. Setting
errorOnConcurrentExecute is not recommended for production use in
case it generates errors during normal operation. For example
third-party code such as a framework may naturally use Promise.all()
in its generic code. Or your application may be coded under the
assumption that node-oracledb will do any necessary serialization. Note
the use of errorOnConcurrentExecute will not affect parallel use of
multiple connections, which may all be in use concurrently, and each of
which can be doing a single operation.
4.6. Application Contexts
An application context stores user-specific information on the database that can enable or prevent a user from accessing data. See About Application Contexts in the Oracle Database documentation. Node-oracledb supports application contexts from version 6.9 onwards.
An application context has a namespace and a key-value pair. The namespace
CLIENTCONTEXT is reserved for use with client session-based application
contexts. Single or multiple application context values can be set when
connecting to the database by using the
appContext property in
oracledb.getConnection() in both node-oracledb Thin and Thick modes.
You must use an array of arrays to set this property value where each array
element contains string values for the application context namespace,
name, and value. For example:
const APP_CTX_NAMESPACE = 'CLIENTCONTEXT';
const APP_CTX_ENTRIES = [
[ APP_CTX_NAMESPACE, 'ATTR1', 'VALUE1' ],
[ APP_CTX_NAMESPACE, 'ATTR2', 'VALUE2' ],
[ APP_CTX_NAMESPACE, 'ATTR3', 'VALUE3' ],
];
const connection = await oracledb.getConnection({
user: "hr",
password: mypw, // contains the hr schema password
connectString: "mydbmachine.example.com/orclpdb1",
appContext: APP_CTX_ENTRIES
});
Application context values set during connection creation can be directly queried in your applications.
for (const entry of APP_CTX_ENTRIES) {
const result = await connection.execute(
`SELECT sys_context(:1, :2) FROM dual`,
[entry[0], entry[1]] // The bind value with namespace and name
);
console.log('The appcontext value is:', result.rows[0][0]);
}
This query prints:
The appcontext value is: VALUE1
The appcontext value is: VALUE2
The appcontext value is: VALUE3
See appcontext.js for a runnable example.
In Thin mode, you can set the application context for pooled connections by
using the appContext property in
oracledb.createPool().
You can use application contexts to set up restrictive policies that are automatically applied to any query executed. See Oracle Virtual Private Database (VPD) in the Oracle Database documentation.
Note that Database Resident Connection Pooling (DRCP) does not support setting or working with application contexts. You also cannot set application contexts with application-side connection pools in Thick mode. The application context setting is ignored in these cases.
4.7. Connection Pooling
Connection pooling significantly improves application performance and scalability, allows resource sharing, and lets applications use advanced Oracle High Availability features.
Various Oracle Database authentication methods are supported in node-oracledb, see Authentication Options.
The pooling solutions available to node-oracledb applications are:
Driver Connection Pools: These pools are managed by the driver layer. They provide readily available database connections that can be shared by multiple users and are quick for applications to obtain. They help make applications scalable and highly available. They are created with
oracledb.createPool().The main use case is for applications that hold connections for relatively short durations while doing database work, and that acquire and release connections back to the pool as needed to do those database operations. Using a driver pool is recommended for applications that need to support multiple users. High availability benefits also make driver pools useful for single-user applications that do infrequent database operations.
Database Resident Connection Pooling (DRCP): The pooling of server processes on the database host so they can be shared between application connections. This reduces the number of server processes that the database host needs to manage.
DRCP is useful if there are large number of application connections, typically from having multiple application processes, and those applications do frequent connection acquire and release calls as needed to do database operations. It is recommended to use DRCP in conjunction with a driver connection pool, since this reduces the number of re-authentications and session memory re-allocations.
Proxy Resident Connection Pooling (PRCP): Connection pooling is handled by a dedicated mid-tier connection proxy, CMAN-TDM.
This is useful for applications taking advantage of CMAN-TDM.
Implicit Connection Pooling: This can add pooling benefits to applications that connect when they start, and only close the connection when the application terminates — but relatively infrequently do database work. It makes use of DRCP or PRCP, but instead of relying on the application to explicitly acquire and release connections, Implicit Connection Pooling automatically detects when applications are not performing database work. It then allows the associated database server process to be used by another connection that needs to do a database operation. Implicit Connection Pooling is available from Oracle Database version 23 onwards.
Implicit Connection Pooling is useful for legacy applications or third-party code that cannot be updated to use a driver connection pool.
Node-oracledb driver connection pools are the first choice for performance, scalability, and high availability. If your database is under memory pressure from having too many applications opening too many connections, then consider either DRCP or Implicit Connection Pooling, depending on your application’s connection life-cycle. If you are utilizing CMAN-TDM, then using PRCP can be considered.
4.7.1. Driver Connection Pooling
Node-oracledb’s driver connection pooling lets applications create and maintain a pool of open connections to the database. Connection pooling is available in both Thin and Thick modes. Connection pooling is important for performance and scalability when applications need to handle a large number of users who do database work for short periods of time but have relatively long periods when the connections are not needed. The high availability features of pools also make small pools useful for applications that want a few connections available for infrequent use and requires them to be immediately usable when acquired. Applications that would benefit from connection pooling but are too difficult to modify from the use of standalone connections can take advantage of Implicit Connection Pooling.
Each node-oracledb process can use one or more connection pools. Each pool can contain zero or more connections. In addition to providing an immediately available set of connections, pools provide dead connection detection and transparently handle Oracle Database High Availability events. This helps shield applications during planned maintenance and from unplanned failures.
In node-oracledb Thick mode, the pool implementation uses Oracle’s session pool technology which supports additional Oracle Database features for example some advanced high availability features.
4.7.1.1. Creating a Connection Pool
A driver connection pool is created by calling oracledb.createPool().
Generally, applications will create a pool once as part of initialization.
Various pool settings can be specified as described in
createPool().
For example, to create a pool that initially contains one connection but can grow up to five connections:
const pool = await oracledb.createPool({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/FREEPDB1",
poolIncrement: 1,
poolMin: 1,
poolMax: 5
});
Note that in node-oracledb Thick mode, the number of worker threads should be sized correctly before creating a pool.
The default value of poolMin is 0, meaning no connections
are created when oracledb.createPool() is called. This means the
credentials and connection string are not validated when the pool is created,
and so problems such as invalid passwords will not return an error.
Credentials will be validated when a connection is later created, for example
with pool.getConnection(). Validation will occur when
createPool() is called if poolMin is
greater or equal to 1, since this creates one or more connections when the
pool is started.
During runtime, some pool properties can be changed with
pool.reconfigure().
A connection pool should be started during application initialization, for example before a web server is started:
const oracledb = require('oracledb');
const mypw = ... // set mypw to the hr schema password
// Start a connection pool (which becomes the default pool) and start the webserver
async function init() {
try {
await oracledb.createPool({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/FREEPDB1",
poolIncrement : 0,
poolMax : 4,
poolMin : 4
});
const server = http.createServer();
server.on('error', (err) => {
console.log('HTTP server problem: ' + err);
});
server.on('request', (request, response) => {
handleRequest(request, response);
});
await server.listen(3000);
console.log("Server is running");
} catch (err) {
console.error("init() error: " + err.message);
}
}
Each web request will invoke handleRequest(). In it, a connection
can be obtained from the pool and used:
async function handleRequest(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write("<!DOCTYPE html><html><head><title>My App</title></head><body>");
let connection;
try {
connection = await oracledb.getConnection(); // get a connection from the default pool
const result = await connection.execute(`SELECT * FROM locations`);
displayResults(response, result); // do something with the results
} catch (err) {
response.write("<p>Error: " + text + "</p>");
} finally {
if (connection) {
try {
await connection.close(); // always release the connection back to the pool
} catch (err) {
console.error(err);
}
}
}
response.write("</body></html>");
response.end();
}
See webapp.js for a runnable example.
4.7.1.2. Getting Connections From a Pool
After a pool has been created, your application can get a connection from it
by calling pool.getConnection():
const connection = await pool.getConnection();
If all connections in a pool are being used, then
subsequent getConnection() calls will be put in
a queue until a connection is available.
Each connection in a pool should be used for a given unit of work, such as a transaction or a set of sequentially executed statements. Statements should be executed sequentially, not in parallel on each connection. For example:
const oracledb = require('oracledb');
const mypw = ... // set mypw to the hr schema password
async function run() {
try {
await oracledb.createPool({
user : "hr",
password : mypw // mypw contains the hr schema password
connectString : "localhost/FREEPDB1"
});
let connection;
try {
// get connection from the pool and use it
connection = await oracledb.getConnection();
result = await connection.execute(`SELECT last_name FROM employees`);
console.log("Result is:", result);
} catch (err) {
throw (err);
} finally {
if (connection) {
try {
await connection.close(); // Put the connection back in the pool
} catch (err) {
throw (err);
}
}
}
} catch (err) {
console.error(err.message);
} finally {
await oracledb.getPool().close(0);
}
}
run();
4.7.1.3. Closing a Connection Pool
After an application finishes using a connection pool, it should release all
connections and terminate the connection pool by calling the
pool.close() method:
await pool.close();
Ensure that all the connections in all code paths including error handlers are released back to the pool.
When a connection is released back to its pool, any ongoing transaction will be rolled back however it will retain session state, such as NLS settings from ALTER SESSION statements. See Connection Tagging and Session State for more information.
Connections can also be dropped completely from the pool.
4.7.2. Connection Pool Sizing
The main characteristics of a connection pool are determined by its
attributes poolMin, poolMax,
poolIncrement, and poolTimeout.
Note
Note in node-oracledb Thick mode, the number of worker threads should be sized correctly before creating a pool. See Connections and Worker Threads
Setting poolMin causes the specified number of connections to be
established to the database during pool creation. This allows subsequent
pool.getConnection() calls to return quickly for an initial set of
users. An appropriate poolMax value avoids overloading the database
by limiting the maximum number of connections ever opened.
Pool expansion happens when pool.getConnection()
is called and both the following are true:
all the currently established connections in the pool are “checked out” of the pool by previous
pool.getConnection()callsthe number of those currently established connections is less than the pool’s
poolMaxsetting
Pool shrinkage happens when the application returns connections to the pool,
and they are then unused for more than poolTimeout
seconds. Any excess connections above poolMin will be closed. When
node-oracledb Thick mode is using using Oracle Client 19 or earlier, this pool
shrinkage is only initiated when the pool is accessed, so a pool in a
completely idle application will not shrink.
For pools created with External Authentication, with
homogeneous set to false, or
when using Database Resident Connection Pooling (DRCP), then
the number of connections initially created is zero even if a larger
value is specified for poolMin. Also in these cases the pool
increment is always 1, regardless of the value of
poolIncrement. Once the
number of open connections exceeds poolMin then the number of open
connections does not fall below poolMin.
The Oracle Real-World Performance Group’s recommendation is to use fixed
size connection pools. The values of poolMin and poolMax should
be the same. This avoids connection storms which can decrease
throughput. See Guideline for Preventing Connection Storms: Use Static
Pools, which contains more details about sizing of
pools. Having a fixed size will guarantee that the database can handle the
upper pool size. For example, if a pool needs to grow but the database
resources are limited, then pool.getConnection() may return errors such
as ORA-28547. With a
fixed pool size, this class of error will occur when the pool is created,
allowing you to change the size before users access the application. With a
dynamically growing pool, the error may occur much later after the pool has
been in use for some time.
The Real-World Performance Group also recommends keeping pool sizes
small, as this may perform better than larger pools. Use
pool.getStatistics() or pool.logStatistics() to monitor pool
usage. The pool attributes should be adjusted to handle the desired workload
within the bounds of resources available to Node.js and the database.
When the values of poolMin and poolMax are the same, poolIncrement
can be set greater than zero. (In Thick mode this needs Oracle Client 18c or
later). This value changes how a homogeneous pool grows when the number of connections
established has become lower than poolMin, for
example if network issues have caused connections to become unusable and they
have been dropped from the pool. Setting poolIncrement greater than 1 in
this scenario means the next pool.getConnection() call that needs to grow
the pool will initiate the creation of multiple connections. That
pool.getConnection() call will not return until the extra connections have
been created, so there is an initial time cost. However it can allow subsequent
connection requests to be immediately satisfied. In this growth scenario, a
poolIncrement of 0 is treated as 1.
The optional pool creation property
maxLifetimeSession also allows
pools to shrink. This property bounds the total length of time that a
connection can exist in a pool after first being created. It is mostly used
for defensive programming to mitigate against unforeseeable problems that may
occur with connections. If a connection was created maxLifetimeSession or
longer seconds ago, then it will be a candidate for being closed.
In node-oracledb Thick mode, Oracle Client libraries 12.1, or later, are needed to use maxLifetimeSession. Note that when using node-oracledb in Thick mode with Oracle Client libraries prior to 21c, pool shrinkage is only initiated when the pool is accessed. So, pools in fully dormant applications will not shrink until the application is next used.
If both poolTimeout and
maxLifetimeSession properties
are set on a pooled connection, the connection will be terminated if either
the idle timeout happens or the maxLifeTimeSession setting is exceeded.
In this case, if connections are idle for more than
poolTimeout, they are dropped only
when doing so will ensure that pool.connectionsOpen is more than or
equal to the poolMin setting. If the connection lifetime
exceeds maxLifetimeSession
seconds, it is dropped and a new connection is created in the pool.
Connection pool health can be impacted by firewalls, resource manager, or user profile IDLE_TIME values. For best efficiency, ensure these do not expire idle connections since this will require connections to be recreated which will impact performance and scalability. See Preventing Premature Connection Closing.
4.7.3. Connection Pool Closing and Draining
Closing a connection pool allows database resources to be freed. If
Node.js is killed without pool.close() being called
successfully, then some time may pass before the unused database-side of
connections are automatically cleaned up in the database.
When pool.close() is called with no parameter, the pool will be
closed only if all connections have been released to the pool with
connection.close(). Otherwise an error is returned and the pool will
not be closed.
An optional drainTime parameter can be used to force the pool closed
even if connections are in use. This lets the pool be ‘drained’ of
connections. The drainTime indicates how many seconds the pool is
allowed to remain active before it and its connections are terminated.
For example, to give active connections 10 seconds to complete their
work before being terminated:
await pool.close(10);
When a pool has been closed with a specified drainTime, then any new
pool.getConnection() calls will fail. If connections are currently
in use by the application, they can continue to be used for the
specified number of seconds, after which the pool and all open
connections are forcibly closed. Prior to this time limit, if there are
no connections currently “checked out” from the pool with
getConnection(), then the pool and its connections are immediately
closed.
In network configurations that drop (or in-line) out-of-band breaks, forced pool termination may hang unless you have DISABLE_OOB=ON in a sqlnet.ora file, see Optional Oracle Net Configuration.
Non-zero drainTime values are recommended so that applications have the
opportunity to gracefully finish database operations. However, pools can
be forcibly closed by specifying a zero drain time:
await pool.close(0);
Closing the pool would commonly be one of the last stages of a Node.js application. A typical closing routine look likes:
// Close the default connection pool with 10 seconds draining, and exit
async function closePoolAndExit() {
console.log("\nTerminating");
try {
await oracledb.getPool().close(10);
process.exit(0);
} catch(err) {
console.error(err.message);
process.exit(1);
}
}
It is helpful to invoke closePoolAndExit() if Node.js is sent a
signal or interrupted:
// Close the pool cleanly if Node.js is interrupted
process
.once('SIGTERM', closePoolAndExit)
.once('SIGINT', closePoolAndExit);
If pool.close() is called while a pool.reconfigure() is taking
place, then an error will be thrown.
4.7.4. Connection Pool Caching
When pools are created, they can be given a named alias. The alias can later be used to retrieve the related pool object for use. This facilitates sharing pools across modules and simplifies getting connections.
Pools are added to the cache by using a poolAlias property in the poolAttrs object:
async function init() {
try {
await oracledb.createPool({ // no need to store the returned pool
user: 'hr',
password: mypw, // mypw contains the hr schema password
connectString: 'localhost/FREEPDB1',
poolAlias: 'hrpool'
});
// do stuff
. . .
// get the pool from the cache and use it
const pool = oracledb.getPool('hrpool');
. . .
}
There can be multiple pools in the cache if each pool is created with a unique alias.
If a pool is created without providing a pool alias:
If no other pool in the cache already has the alias of ‘default’, then the new pool will be cached using the
pool.poolAlias‘default’.This pool is used by default in methods that utilize the connection pool cache.
If an existing pool in the cache already has the alias ‘default’, then
pool.poolAliasof the new pool will be undefined and the pool will be not stored in the pool cache. The application must retain a variable for subsequent pool use:const pool = await oracledb.createPool({ . . . }).
Methods that can affect or use the connection pool cache include:
oracledb.createPool(): Can add a pool to the cache.oracledb.getPool(): Retrieves a pool from the cache.oracledb.getConnection(): Can use a pool in the cache to retrieve connections.pool.close(): Automatically removes a pool from the cache.
4.7.4.1. Using the Default Pool
Assuming the connection pool cache is empty, the following will create a new pool and cache it using the pool alias ‘default’:
async function init() {
try {
await oracledb.createPool({
user: 'hr',
password: mypw, // mypw contains the hr schema password
connectString: 'localhost/FREEPDB1'
});
. . .
}
If you are using callbacks, note that createPool() is not
synchronous.
Connections can be returned by using the shortcut to
oracledb.getConnection() that returns a
connection from a pool:
const connection = await oracledb.getConnection();
. . . // Use connection from the previously created 'default' pool
await connection.close(); // always release the connection back to the pool
The default pool can also be retrieved using oracledb.getPool()
without passing the poolAlias parameter:
const pool = oracledb.getPool();
console.log(pool.poolAlias); // 'default'
const connection = await pool.getConnection();
. . . // Use connection
await connection.close();
4.7.4.2. Using Multiple Pools
If the application needs to use more than one pool at a time, unique pool aliases can be used when creating the pools:
await oracledb.createPool({
user: 'hr',
password: myhrpw, // myhrpw contains the hr schema password
connectString: 'localhost/FREEPDB1',
poolAlias: 'hrpool'
});
await oracledb.createPool({
user: 'sh',
password: myshpw, // myshpw contains the sh schema password
connectString: 'localhost/FREEPDB1',
poolAlias: 'shpool'
});
. . .
To get a connection from a pool, pass the pool alias:
const connection = await oracledb.getConnection('hrpool');
. . . // Use connection from the pool
await connection.close(); // always release the connection back to the pool
From node-oracledb 3.1.0 you can alternatively pass the alias as an attribute of the options:
const connection = await oracledb.getConnection({ poolAlias: 'hrpool' });
. . . // Use connection from the pool
await connection.close(); // always release the connection back to the pool
The presence of the poolAlias attribute indicates the previously
created connection pool should be used instead of creating a standalone
connection. This syntax is useful when you want to pass other attributes
to a pooled getConnection() call, such as for proxy
connections or with connection
tagging:
const connection = await oracledb.getConnection({ poolAlias: 'hrpool', tag: 'loc=cn;p=1' });
. . . // Use connection from the pool
await connection.close(); // always release the connection back to the pool
To use the default pool in this way you must explicitly pass the alias
default:
const connection = await oracledb.getConnection({ poolAlias: 'default', tag: 'loc=cn;p=1' });
. . . // Use connection from the pool
await connection.close(); // always release the connection back to the pool
A specific pool can be retrieved from the cache by passing its pool
alias to oracledb.getPool():
const pool = oracledb.getPool('hrpool');
const connection = await pool.getConnection();
. . . // Use connection from the pool
await connection.close();
4.7.5. Connection Pool Queue
The number of users that can concurrently do database operations is
limited by the number of connections in the pool. The maximum number of
connections is poolMax. Node-oracledb queues
any additional pool.getConnection() requests to prevent users from
immediately getting an error that the database is not available. The
connection pool queue allows applications to gracefully handle more
users than there are connections in the pool, and to handle connection
load spikes without having to set poolMax too large for general
operation.
If the application has called pool.getConnection() (or
oracledb.getConnection() calls that use a pool) enough times so that
all connections in the pool are in use, and further getConnection() calls
are made, then each of those new getConnection() requests will be queued
and will not return until an in-use connection is released back to the pool
with connection.close(). If, instead, poolMax has not been reached,
then the additional connection requests can be immediately satisfied and are
not queued.
The amount of time that a queued request will wait for a free connection
can be configured with queueTimeout. When
connections are timed out of the queue, the pool.getConnection()
call returns the error NJS-040: connection request timeout to the
application.
If more than oracledb.queueMax pending
connection requests are in the queue, then pool.getConnection()
calls will immediately return an error NJS-076: connection request
rejected. Pool queue length queueMax reached and will not be queued.
Use this to protect against connection request storms. The setting helps
applications return errors early when many connections are requested
concurrently. This avoids connection requests blocking (for up to
queueTimeout seconds) while waiting an
available pooled connection. It lets you see when the pool is too small.
You may also experience NJS-040 or NJS-076 errors if your application is not correctly closing connections, or if are using node-oracledb Thick mode and UV_THREADPOOL_SIZE is too small.
4.7.6. Connection Pool Monitoring
Connection pool usage should be monitored to choose the appropriate
settings for your workload. If the current settings are non optimal,
then pool.reconfigure() can be called to alter
the configuration.
Pool attributes connectionsInUse and
connectionsOpen always provide basic
information about an active pool:
const pool = await oracledb.createPool(...);
. . .
console.log(pool.connectionsOpen); // how big the pool actually is
console.log(pool.connectionsInUse); // how many of those connections are held by the application
Statistics are calculated from the time the pool was created, or since
pool.reconfigure() was used to reset the statistics.
The recording of pool queue statistics, pool
settings, and related environment variables can be enabled by setting
enableStatistics to true when using oracledb.createPool() or
pool.reconfigure().
To enable recording of statistics when creating the pool:
const pool = await oracledb.createPool({
enableStatistics : true, // default is false
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/FREEPDB1"
});
. . .
Statistics can alternatively be enabled on a running pool with:
await pool.reconfigure({ enableStatistics: true });
Applications can then get the current statistics by calling
pool.getStatistics() which returns a
PoolStatistics Class object. Attributes of
the object can be accessed individually for your tracing requirements.
The complete statistics can be printed by calling
poolstatistics.logStatistics().
const poolstatistics = pool.getStatistics();
console.log(poolstatistics.currentQueueLength); // print one attribute
poolstatistics.logStatistics(); // print all statistics to the console
Alternatively the statistics can be printed directly by calling
pool.logStatistics().
pool.logStatistics(); // print all statistics to the console
The output of poolstatistics.logStatistics() and
pool.logStatistics() is identical.
For efficiency, the minimum, maximum, average, and sum of times in the pool queue are calculated when requests are removed from the queue. They include times for connection requests that were dequeued when a pool connection became available, and also for connection requests that timed out. They do not include times for connection requests still waiting in the queue.
The sum of ‘requests failed’, ‘requests exceeding queueMax’, and
‘requests exceeding queueTimeout’ is the number of
pool.getConnection() calls that failed.
The PoolStatistics object and logStatistics()
function record the following:
Pool Statistics Class Attribute |
|
Description |
|---|---|---|
|
thin mode |
Indicates whether the driver is in Thin or Thick mode. |
|
gathered at |
The time the statistics were taken. |
|
up time (milliseconds) |
The number of milliseconds since this pool was created. |
|
up time from last reset (milliseconds) |
The number of milliseconds since the statistics were initialized or reset. |
|
connection requests |
The number of |
|
requests enqueued |
The number of |
|
requests dequeued |
The number of |
|
requests failed |
The number of getConnection() requests that failed due to an Oracle Database error. Does not include |
|
requests exceeding queueMax |
The number of getConnection() requests rejected because the number of connections in the pool queue exceeded the |
|
requests exceeding queueTimeout |
The number of queued getConnection() requests that were timed out from the pool queue because they exceeded the |
|
current queue length |
The current number of |
|
maximum queue length |
The maximum number of |
|
sum of time in queue (milliseconds) |
The sum of the time (milliseconds) that dequeued requests spent in the pool queue. |
|
minimum time in queue (milliseconds) |
The minimum time (milliseconds) that any dequeued request spent in the pool queue. |
|
maximum time in queue (milliseconds) |
The maximum time (milliseconds) that any dequeued request spent in the pool queue. |
|
average time in queue (milliseconds) |
The average time (milliseconds) that dequeued requests spent in the pool queue. |
|
The number of connections from this pool that |
|
|
The number of idle or in-use connections to the database that the pool is currently managing. |
|
|
The connection string that is used to connect to the Oracle Database instance. |
|
|
The edition name used. |
|
|
Denotes whether the Oracle Client events mode is enabled or not. |
|
|
Denotes whether connections are established using external authentication or not. |
|
|
Identifies whether the connections in the pool all have the same credentials (a ‘homogenous’ pool), or whether different credentials can be used (a ‘heterogeneous’ pool). |
|
|
The alias of this pool in the connection pool cache. |
|
|
The number of connections that are opened whenever a connection request exceeds the number of currently open connections. |
|
|
The maximum number of connections that can be open in the connection pool. |
|
|
The maximum number of connections in the pool that can be used for any given shard in a sharded database. |
|
|
The minimum number of connections a connection pool maintains, even when there is no activity to the target database. |
|
|
The maximum number of seconds that a connection can remain idle in a connection pool before node-oracledb pings the database prior to returning that connection to the application. |
|
|
The number of milliseconds that a connection should wait for a response from |
|
|
The time (in seconds) after which the pool terminates idle connections (unused in the pool). |
|
|
The time (in seconds) that a pooled connection can exist in a pool after first being created. |
|
|
The maximum number of pending |
|
|
The time (in milliseconds) that a connection request should wait in the queue before the request is terminated. |
|
|
The Node.js or PL/SQL function that is invoked by |
|
|
Determines whether the pool has a metadata cache enabled for SODA collection access. |
|
|
The number of statements to be cached in the statement cache of each connection. |
|
|
The database username for connections in the pool. |
|
|
UV_THREADPOOL_SIZE |
The value of process.env.UV_THREADPOOL_SIZE which is the number of worker threads for this process. Note this shows the value of the variable, however if this variable was set after the thread pool started, the thread pool will still be the default size of 4. This attribute only affects node-oracledb Thick mode. |
4.7.7. Connection Pool Pinging
When a connection is aquired from a pool with getConnection(),
node-oracledb does some internal checks to validate if the about-to-be-returned
connection is usable. If it is not usable, node-oracledb can replace it with a
different connection before returning this to the application.
Connections may become unusable for various reasons including network dropouts,
database instance failures, session termination from the database resource
manager or user resource profile IDLE_TIME, or from a DBA issuing an ALTER
SYSTEM KILL SESSION command.
By default, idle connections in the pool are unaware of these events. So, a
getConnection() call could return an unusable connection to the application
and errors would only occur when it is later used. The internal pool
validation checks help provide tolerance against this situation so that
statement execution using a connection is more likely to succeed.
Each time getConnection() is called, a lightweight connection validity
check occurs. (In node-oracledb Thick mode, this requires Oracle Client library
version 12.2 or later). The lightweight check allows node-oracledb to detect
and replace connections that have become unusable due to some network errors.
An additional internal check performed by getConnection() can be configured
during pool creation. This extra check helps detect errors such as the
connection having exceeded the user profile resource limits, or from an
explicit session closure from a DBA. This extra check performs a
round-trip ping to the database which impacts performance.
So, it is not done for each getConnection() call by default.
The frequency of pinging can be controlled with the
oracledb.poolPingInterval property or during pool creation to meet your quality of service
requirements.
The default poolPingInterval value is 60 seconds, which is
suitable for most active applications. Possible values are:
|
Behavior of a Pool |
|---|---|
|
Never checks for connection validity |
|
Always checks for connection validity |
|
Checks validity if the connection has been idle in the pool (not “checked out” to the application by |
When getConnection() is called to return a pooled connection, and
the connection has been idle in the pool (not “checked out” to the
application by getConnection()) for the specified
poolPingInterval time, then an internal “ping” will be performed
first. If the ping detects the connection is invalid then node-oracledb
internally drops the unusable connection and obtains another from the
pool. This second connection may also need a ping. This ping-and-release
process may be repeated until:
an existing connection that does not qualify for pinging is obtained. The
getConnection()call returns this to the application. Note that since a ping may not have been performed, the connection is not guaranteed to be usable.a new, usable connection is opened. This is returned to the application.
a number of unsuccessful attempts to find a valid connection have been made, after which an error is returned to the application.
Pools in active use may never have connections idle longer than
poolPingInterval, so pinging often only occurs for infrequently
accessed connection pools.
Because a ping may not occur every time a connection is returned from
pool.getConnection(), and also it is possible for network outages
to occur after getConnection() is called, applications should continue
to use appropriate statement execution error checking.
For ultimate scalability, disable explicit pool pinging by setting
poolPingInterval to a negative value, and make sure the firewall, database
resource manager, or user profile are not expiring idle connections. See
Preventing Premature Connection Closing. When
using node-oracledb Thick mode, use use Oracle client 12.2 (or later)
libraries.
In all cases, when a bad connection is released back to the pool with
connection.close(), the connection is automatically destroyed.
This allows a valid connection to the database to be opened by some
subsequent getConnection() call.
Explicit pings can be performed at any time with connection.ping().
The time to wait for a response from connection.ping() can be
controlled with the oracledb.poolPingTimeout property or with the
poolPingTimeout property during
pool creation.
The default poolPingTimeout value is 5000 milliseconds.
The behavior of a pool getConnection() call differs based on the value
specified in the poolPingTimeout property as detailed below.
|
Behavior of a Pool |
|---|---|
|
Returns the error Returns the error |
|
Waits until |
|
Waits for If |
4.7.8. Connection Tagging and Session State
Applications can set “session” state in each connection. For all
practical purposes, connections are synonymous with sessions. Examples
of session state are NLS settings from ALTER SESSION
statements. Pooled connections will retain their session state after
they have been released back to the pool with connection.close().
However, because pools can grow, or connections in the pool can be
recreated, there is no guarantee a subsequent pool.getConnection()
call will return a database connection that has any particular state.
The oracledb.createPool() option attribute
sessionCallback can be
used to set session state efficiently so that connections have a known
session state. The sessionCallback can be a Node.js function that
will be called whenever pool.getConnection() will return a newly
created database connection that has not been used before. It is also
called when connection tagging is being used and the requested tag is
not identical to the tag in the connection returned by the pool. It is
called before pool.getConnection() returns in these two cases. It
will not be called in other cases. Using a callback saves the cost of
setting session state if a previous user of a connection has already set
it. The caller of pool.getConnection() can always assume the correct
state is set. The sessionCallback can also be a PL/SQL procedure.
Connection tagging and sessionCallback were introduced in
node-oracledb 3.1.
There are three common scenarios for sessionCallback:
When all connections in the pool should have the same state use a simple Node.js Session Callback without tagging.
When connections in the pool require different state for different users use a Node.js Session Tagging Callback.
With DRCP, use a PL/SQL Session Tagging Callback.
4.7.8.1. Connection Tagging
Connection tagging is used when connections in a pool should have differing
session states. In order to retrieve a connection with a desired state, the
tag attribute in getConnection() needs to be set.
Note
In this release, connection tagging is only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
Pooled connections can be tagged to record their session state by
setting the property connection.tag to a user
chosen string that represents the state you have set in the connection.
A pool.getConnection({tag: 'mytag'}) call can request a connection
that has the specified tag. If no available connections with that tag
exist in the pool, an untagged connection or a newly created connection
will be returned. If the optional getConnection() attribute
matchAnyTag is true, then a connection that has a different tag
may be returned.
The sessionCallback
function is invoked before pool.getConnection() returns if the
requested tag is not identical to the actual tag of the pooled
connection. The callback can compare the requested tag with the current
actual tag in connection.tag. Any desired state change can be made
to the connection and connection.tag can be updated to record the
change. The best practice recommendation is to set the tag in the
callback function but, if required, a tag can be set anytime prior to
closing the connection. To clear a connection’s tag set
connection.tag to an empty string.
You would use tagging where you want pool.getConnection() to return
a connection which has one of several different states. If all
connections should have the same state then you can simply set
sessionCallback and not use tagging. Also, it may not be worthwhile
using a large number of different tags, or using tagging where
connections are being dropped and recreated
frequently since the chance of pool.getConnection() returning an
already initialized connection with the requested tag could be low, so
most pool.getConnection() calls would return a connection needing
its session reset, and tag management will just add overhead.
When node-oracledb is using Oracle Client libraries 12.2 or later, then
node-oracledb uses ‘multi-property tags’ and the tag string must be of
the form of one or more “name=value” pairs separated by a semi-colon,
for example "loc=uk;lang=cy". The Oracle session
pool used by node-oracledb has various heuristics
to determine which connection is returned to the application. Refer to the
multi-property tags documentation.
The callback function can parse the requested multi-property tag and
compare it with the connection’s actual properties in
connection.tag to determine what exact state to
set and what value to update connection.tag to.
4.7.8.2. Node.js Session Callback
When all connections in the pool should have the same state, a simple callback can be used.
This example sets two NLS settings in each pooled connection. They are
only set the very first time connections are established to the
database. The requestedTag parameter is ignored because it is only
valid when tagging is being used:
function initSession(connection, requestedTag, callbackFn) {
connection.execute(
`alter session set nls_date_format = 'YYYY-MM-DD' nls_language = AMERICAN`,
callbackFn);
}
try {
const pool = await oracledb.createPool({
user: 'hr',
password: mypw, // mypw contains the hr schema password
connectString: 'localhost/FREEPDB1',
sessionCallback: initSession
});
. . .
}
Note that a single ALTER SESSION statement is used to set multiple
properties, avoiding round-trips of repeated
execute() calls. If you need to execute multiple SQL statements,
then use an anonymous PL/SQL block for the same reason:
function initSession(connection, requestedTag, callbackFn) {
connection.clientId = "Chris";
connection.execute(
`begin
execute immediate 'alter session set nls_date_format = ''YYYY-MM-DD'' nls_language = AMERICAN';
insert into user_log (id, ts) values (sys_context('userenv', 'client_identifier'), systimestamp);
commit;
end;`,
callbackFn);
}
See sessionfixup.js for a runnable example.
4.7.8.3. Node.js Session Tagging Callback
When connections in the pool require different state for different users and you are not using DRCP, then use a JavaScript callback with tagging.
This example Node.js callback function ensures the connection contains valid settings for an application-specific “USER_TZ=X” property where X is a valid Oracle timezone:
function initSession(connection, requestedTag, callbackFn) {
const tagParts = requestedTag.split('=');
if (tagParts[0] != 'USER_TZ') {
callbackFn(new Error('Error: Only property USER_TZ is supported'));
return;
}
connection.execute(
`ALTER SESSION SET TIME_ZONE = '${tagParts[1]}'`,
(err) => {
// Record the connection's new state and return
connection.tag = requestedTag;
callbackFn(err);
}
);
}
try {
await oracledb.createPool({
user: 'hr',
password: mypw, // mypw contains the hr schema password
connectString: 'localhost/FREEPDB1',
sessionCallback: initSession
});
// Get a connection with a given tag (and corresponding session state) from the pool
const connection = await oracledb.getConnection({poolAlias: 'default', tag: "USER_TZ=UTC" });
. . . // Use the connection
// The connection will be returned to the pool with the tag value of connection.tag
await connection.close(); // always release the connection back to the pool
. . .
The initSession() session callback function is only invoked by
getConnection() if the node-oracledb connection pool cannot find a
connection with the requested tag. The session callback function adjusts
the connection state and records the matching tag.
Other parts of the application may request connections with different
tags. Eventually the pool would contain connections with various
different states (and equivalent tags). Each getConnection() call
will attempt to return a connection which already has the requested tag.
If a matching free connection cannot be found, the pool may grow or the
session state from another connection is cleared. Then initSession()
is called so that the desired connection state can be set.
For runnable examples, see sessiontagging1.js and sessiontagging2.js.
4.7.8.4. PL/SQL Session Tagging Callback
Note
In this release, PL/SQL callbacks are only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
When using DRCP, tagging is most efficient when using a PL/SQL callback.
When node-oracledb is using Oracle Client libraries 12.2 or later,
sessionCallback can be a string containing the name of a PL/SQL
procedure that is called when the requested tag does not match the
actual tag in the connection. When the application uses DRCP
connections, a PL/SQL callback can avoid the
round-trip calls that a Node.js function would require
to set session state. For non-DRCP connections, the PL/SQL callback will
require a round-trip from the application.
After a PL/SQL callback completes and pool.getConnection() returns,
connection.tag will have the same property values
as the requested tag. The property order may be different. For example
you may request “USER_TZ=UTC;LANGUAGE=FRENCH” but connection.tag may
be “LANGUAGE=FRENCH;USER_TZ=UTC”. When matchAnyTag is true, then
various heuristics are used to determine which connection in the pool to
use. See the multi-property tags documentation.
Additional properties may be present in connection.tag.
There is no direct way for Node.js to know if the PL/SQL procedure was
called or what session state it changed. After pool.getConnection()
returns, care must be taken to set connection.tag to an appropriate
value.
A sample PL/SQL callback procedure looks like:
CREATE OR REPLACE PACKAGE myPackage AS
TYPE property_t IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(64);
PROCEDURE buildTab(
tag IN VARCHAR2,
propertyTab OUT property_t
);
PROCEDURE myPlsqlCallback (
requestedTag IN VARCHAR2,
actualTag IN VARCHAR2
);
END;
/
CREATE OR REPLACE PACKAGE BODY myPackage AS
-- Parse the "property=value" pairs in the tag
PROCEDURE buildTab(tag IN VARCHAR2, propertyTab OUT property_t) IS
property VARCHAR2(64);
propertyName VARCHAR2(64);
propertyValue VARCHAR2(64);
propertyEndPos NUMBER := 1;
propertyStartPos NUMBER := 1;
propertyNameEndPos NUMBER := 1;
begin
WHILE (LENGTH(tag) > propertyEndPos)
LOOP
propertyEndPos := INSTR(tag, ';', propertyStartPos);
IF (propertyEndPos = 0) THEN
propertyEndPos := LENGTH(tag) + 1;
END IF;
propertyNameEndPos := INSTR(tag, '=', propertyStartPos);
propertyName := SUBSTR(tag, propertyStartPos,
propertyNameEndPos - propertyStartPos);
propertyValue := SUBSTR(tag, propertyNameEndPos + 1,
propertyEndPos - propertyNameEndPos - 1);
propertyTab(propertyName) := propertyValue;
propertyStartPos := propertyEndPos + 1;
END LOOP;
END;
PROCEDURE myPlsqlCallback (
requestedTag IN VARCHAR2,
actualTag IN VARCHAR2
) IS
reqPropTab property_t;
actPropTab property_t;
propertyName VARCHAR2(64);
BEGIN
buildTab(requestedTag, reqPropTab);
buildTab(actualTag, actPropTab);
-- Iterate over requested properties to set state when it's not
-- currently set, or not set to the desired value
propertyName := reqPropTab.FIRST;
WHILE (propertyName IS NOT NULL)
LOOP
IF ((NOT actPropTab.exists(propertyName)) OR
(actPropTab(propertyName) != reqPropTab(propertyName))) THEN
IF (propertyName = 'SDTZ') THEN
EXECUTE IMMEDIATE
'ALTER SESSION SET TIME_ZONE=''' || reqPropTab(propertyName) || '''';
ELSE
RAISE_APPLICATION_ERROR(-20001,'Unexpected session setting requested');
END IF;
END IF;
propertyName := reqPropTab.NEXT(propertyName);
END LOOP;
-- Could iterate over other actual properties to set any to a default state
END;
END myPackage;
/
This could be used in your application like:
const sessionTag = "SDTZ=UTC";
try {
const pool = await oracledb.createPool({
user: 'hr',
password: mypw, // mypw contains the hr schema password
connectString: 'localhost/FREEPDB1',
sessionCallback: "myPackage.myPlsqlCallback"
});
. . .
const connection = await pool.getConnection({tag: sessionTag});
. . . // The value of connection.tag will be sessionTag
// Use connection.
await connection.close();
}
4.7.9. Heterogeneous and Homogeneous Connection Pools
By default, connection pools are ‘homogeneous’ meaning that all connections use the same database credentials. Both node-oracledb Thin and Thick modes support homogeneous pools.
4.7.9.1. Creating Heterogeneous Pools
Node-oracledb Thick mode additionally supports heterogeneous pools, allowing
different user names and passwords to be passed each time a connection is
acquired from the pool with pool.getConnection().
To create a heterogeneous pool, set the createPool()
parameter, homogeneous, to false.
When a heterogeneous pool is created by setting
homogeneous to false and no
credentials supplied during pool creation, then a user name and password
may be passed to pool.getConnection():
const pool = await oracledb.createPool({
connectString : "localhost/FREEPDB1", // no user name or password
homogeneous : false,
. . . // other pool options such as poolMax
});
const connection = await pool.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
});
. . . // use connection
await connection.close();
The connectString is required during pool creation since the pool is
created for one database instance.
Different user names may be used each time pool.getConnection() is
called.
When applications want to use connection pools but are not able to use
connection.clientId to distinguish application users from
database schema owners then a ‘heterogeneous’ connection pool might be an
option.
To use heterogeneous pools with the connection pool cache, the alias should be explicitly stated, even if it is the default pool:
const connection = await oracledb.getConnection({
poolAlias: "default",
user : "hr",
password : mypw, // mypw contains the hr schema password
});
For heterogeneous pools, the number of connections initially created is
zero even if a larger value is specified for poolMin.
The pool increment is always 1, regardless of the value of
poolIncrement. Once the
number of open connections exceeds poolMin and connections are idle
for more than the poolTimeout seconds, then
the number of open connections does not fall below poolMin.
4.8. Connection Hook Functions
Node-oracledb supports centralized configuration provider and process configuration hook functions that can be used to customize connection logic.
4.8.1. Using Centralized Configuration Provider Hook Functions
The oracledb.registerConfigurationProviderHook() method registers the
centralized configuration provider extension
modules that were loaded in your code. When this method is called, it
registers a user hook function that will be called internally by node-oracledb
prior to connection or pool creation. The hook function will be invoked when
oracledb.getConnection() or oracledb.createPool() are called
with the connectString property prefixed with a specified configuration
provider. Your hook function can retrieve the connection information, which
node-oracledb can subsequently use to complete the connection or pool
creation.
Pre-supplied node-oracledb plugins such as OCI Object Storage
(ociobject), OCI Vault (ocivault),
Azure App Configuration (azure), and
Azure Key Vault (azurevault) make use of
oracledb.registerConfigurationProviderHook(). These plugins use the
information found in a connection method’s connectString property which
allows node-oracledb to access the required information from the configuration
provider, and connect to Oracle Database with this information. For the
complete plugin implementation, see the respective folders of the
configuration providers in the plugins/configProviders directory of the
node-oracledb package. Also, you can define your own plugins for centralized
configuration providers and register it using
oracledb.registerConfigurationProviderHook(), similar to how the
pre-supplied node-oracledb plugins are registered.
Once you define the plugin and register it using
oracledb.registerConfigurationProviderHook(), you can connect to Oracle
Database using the information stored in the configuration provider, for
example, with OCI Vault:
// Load the ocivault plugin
require('oracledb/plugins/configProviders/ocivault');
// Use an OCI vault connection string
const connection = await oracledb.getConnection({
connectString : "config-ocivault://ocid1.vaultsecret.oc1?oci_tenancy=abc123&oci_user=ociuser1&oci_fingerprint=ab:14:ba:13&oci_key_file=ociabc/ocikeyabc.pem"
});
For more information on the centralized configuration providers supported by node-oracledb, see Centralized Configuration Providers.
4.8.2. Using Process Configuration Hook Functions
The oracledb.registerProcessConfigurationHook() method registers
extension modules that were added to your code. When this method is called, it
registers a user hook function that will be called internally by node-oracledb
prior to connection or pool creation. The hook function will be called when
oracledb.getConnection() or oracledb.createPool() are called.
Your hook function accepts a copy of the parameters that will be used to create
the standalone or pool connections. The function can access and modify them in
any way necessary to allow node-oracledb to subsequently complete the
connection or pool creation request.
Pre-supplied node-oracledb plugins such as extensionOci and extensionAzure make
use of oracledb.registerProcessConfigurationHook(). The
extensionOci uses the information found in a
connection method’s tokenAuthConfigOci property and modifies the
accessToken property with a function that will acquire the authentication
token needed to complete a connection. The key code section showing
registering of a hook function is:
function hookFn(options) {
if (options.tokenAuthConfigOci) {
options.accessToken = async function callbackFn(refresh, config) {
return await getToken(config);
};
options.accessTokenConfig = options.tokenAuthConfigOci;
}
}
oracledb.registerProcessConfigurationHook(hookFn);
Your code can then try to connection, for example:
await oracledb.getConnection({
tokenAuthConfigOci: {
authType: ..., // OCI-specific parameters to be set when
profile: ..., // using extensionOci plugin with authType
configFileLocation: ... // configFileBasedAuthentication
}
externalAuth: true, // must specify external authentication
connectString: ... // Oracle Autonomous Database connection string
});
4.9. Database Resident Connection Pooling (DRCP)
Database Resident Connection Pooling (DRCP) enables database resource sharing for applications which use a large number of connections that run in multiple client processes or run on multiple middle-tier application servers. DRCP reduces the overall number of connections that a database must handle. DRCP support is available in both Thin and Thick modes.
DRCP is generally used only when the database host does not have enough
memory to keep all connections open concurrently. For example, if your
application runs as 10 Node.js processes each with a connection pool
having poolMax of 50, then the database host must be able to have 10
* 50 = 500 database server processes open at the same time. If the
database host does not have enough memory for these 500 server
processes, then DRCP may be a solution because a smaller pool of server
processes will be shared between all the Node.js connections.
DRCP is useful for applications which share the same database credentials, have similar session settings (for example date format settings or PL/SQL package state), and where the application gets a database connection, works on it for a relatively short duration, and then releases it.
For efficiency, it is recommended that DRCP connections should be used with node-oracledb’s local connection pool. Using DRCP with standalone connections is not as efficient but does allow the database to reuse database server processes which can provide a performance benefit for applications that cannot use a local connection pool. In this scenario, make sure to configure enough DRCP authentication servers to handle the connection load.
Although applications can choose whether or not to use DRCP pooled connections at runtime, care must be taken to configure the database appropriately for the number of expected connections, and also to stop inadvertent use of non-DRCP connections leading to a database server resource shortage. Conversely, avoid using DRCP connections for long-running operations.
For more information about DRCP, see the technical brief Extreme Oracle Database Connection Scalability with Database Resident Connection Pooling (DRCP), the user documentation Oracle Database Concepts Guide and Oracle Database Development Guide. For DRCP Configuration, see Oracle Database Administrator’s Guide.
To use DRCP with node-oracledb, perform the following steps:
4.9.1. Enabling DRCP in Oracle Database
Oracle Database versions prior to 21c have a single DRCP connection pool. From Oracle Database 21c, each pluggable database can optionally have its own pool, or can use the container level pool. From Oracle Database 23.4, you can create multiple pools at the pluggable, or container, database level. This multi-pool feature is useful where different applications connect to the same database, but there is a concern that one application’s use of the pool may impact other applications. If this is not the case, a single pool may allow best resource sharing on the database host.
Note that DRCP is already enabled in Oracle Autonomous Database and pool management is different to the steps below.
DRCP pools can be configured and administered by a DBA using the
DBMS_CONNECTION_POOL package:
EXECUTE DBMS_CONNECTION_POOL.CONFIGURE_POOL(
pool_name => 'SYS_DEFAULT_CONNECTION_POOL',
minsize => 4,
maxsize => 40,
incrsize => 2,
session_cached_cursors => 20,
inactivity_timeout => 300,
max_think_time => 600,
max_use_session => 500000,
max_lifetime_session => 86400)
Alternatively, the method DBMS_CONNECTION_POOL.ALTER_PARAM() can
set a single parameter:
EXECUTE DBMS_CONNECTION_POOL.ALTER_PARAM(
pool_name => 'SYS_DEFAULT_CONNECTION_POOL',
param_name => 'MAX_THINK_TIME',
param_value => '1200')
The inactivity_timeout parameter terminates idle pooled servers, helping
optimize database resources. To avoid pooled servers permanently being held
onto by a Node.js script, the max_think_time parameter can be set. The
parameters num_cbrok and maxconn_cbrok can be used to distribute the
persistent connections from the clients across multiple brokers. This may be
needed in cases where the operating system per-process descriptor limit is
small. Some customers have found that having several connection brokers
improves performance. The max_use_session and max_lifetime_session
parameters help protect against any unforeseen problems affecting server
processes. The default values will be suitable for most users. See the
Oracle DRCP documentation for details on these
parameters.
In general, if pool parameters are changed, then the pool should be restarted. Otherwise, server processes will continue to use old settings.
You can use a DBMS_CONNECTION_POOL.RESTORE_DEFAULTS() procedure to reset
all of the values.
When DRCP is used with Oracle RAC, each database instance has its own connection
broker and pool of servers. Each pool has the identical configuration. For
example, all pools start with minsize server processes. A single
DBMS_CONNECTION_POOL command will alter the pool of each instance at the same
time. The pool needs to be started before connection requests begin. The
command below does this by bringing up the broker, which registers itself with
the database listener:
EXECUTE DBMS_CONNECTION_POOL.START_POOL()
Once enabled this way, the pool automatically restarts when the database
instance restarts, unless explicitly stopped with the
DBMS_CONNECTION_POOL.STOP_POOL() command:
EXECUTE DBMS_CONNECTION_POOL.STOP_POOL()
Oracle Database version 23 allows a DRAINTIME argument to be passed to
STOP_POOL(), indicating that the pool will only be closed after the
specified time. This allows in-progress application work to continue. A
draintime value of 0 can be used to immediately close the pool. See the
database documentation on DBMS_CONNECTION_POOL.STOP_POOL().
In older Oracle Database versions, the pool cannot be stopped while connections are open.
4.9.2. Configuring Application to Use DRCP
Application connections using DRCP should specify a user-chosen connection class name when requesting a DRCP pooled server. A ‘purity’ of the connection session state can optionally be specified. See the Oracle Database documentation on benefiting from scalability for more information on purity and connection classes.
The best practice is to use DRCP in conjunction with a local driver
connection pool created with
oracledb.createPool(). The node-oracledb connection pool size does not
need to match the DRCP pool size. The limit on overall execution parallelism
is determined by the DRCP pool size. Note that when using DRCP with a
node-oracledb local connection pool in Thick mode, the local connection pool
min value is ignored and the pool will be created with zero connections.
Use a Pooled Server for a Connection
To enable connections to use a pooled server, you can:
Specify to use a pooled server in the
connectStringproperty (or its aliasconnectionString) oforacledb.createPool()ororacledb.getConnection(). For example with the Easy Connect syntax:const pool = await oracledb.createPool({ user : "hr", password : mypw, // mypw contains the hr schema password connectString : "mydbmachine.example.com/orclpdb1:pooled" });
Alternatively, add
(SERVER=POOLED)to the Connect Descriptor such as used in an Oracle Network configuration file tnsnames.ora:customerpool = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=dbhost.example.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=CUSTOMER) (SERVER=POOLED)))
Setting DRCP Connection Classes
The best practice is to specify a name for a connection by using the
oracledb.connectionClass property. If it is set, then the connection
class specified in this property is used in both standalone and pooled
connections. This user-chosen name provides some partitioning of DRCP session
memory so reuse is limited to similar applications. It provides maximum pool
sharing if multiple application processes are started and use the same class
name. A class name also allows better DRCP usage tracking in the database. In
the database monitoring views, the class name shown will be the value
specified in the application prefixed with the user name.
To enable a connection to use a pooled server and to specify a class name, you can use:
oracledb.connectionClass = 'HRPOOL';
const pool = await oracledb.createPool({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "mydbmachine.example.com/orclpdb1:pooled"
});
You can also specify the connection class in a connection string by setting
the POOL_CONNECTION_CLASS parameter. If this parameter is set, then this
connection class is used in both standalone and pooled connections. See
Setting the Connection Class and Purity in the Connection String.
If both the oracledb.connectionClass property and the
POOL_CONNECTION_CLASS connection string parameter are set, then the
POOL_CONNECTION_CLASS parameter has the highest priority and overrides the
default or application specified values.
If oracledb.connectionClass and POOL_CONNECTION_CLASS connection
string parameter are not set, then:
For standalone connections, the session request is sent to the shared connection class in DRCP.
For pooled connections, the pool generates a unique connection class if a previously generated connection class does not exist. This connection class is used when acquiring connections from the pool. Node-oracledb Thin mode generates a connection class with the prefix “NJS” while Thick mode generates a connection class with the prefix “OCI”.
If the connection class is not set, the pooled server session memory will not
be reused optimally, and the statistic views will record large values for
NUM_MISSES.
Acquiring a DRCP Connection
Once DRCP has been enabled and the driver connection pool has been created with the appropriate connection string, then your application can get a connection from a pool that uses DRCP by calling:
const connection = pool.getConnection();
Closing Connections when using DRCP
Similar to using a node-oracledb connection pool, Node.js scripts where
node-oracledb connections do not go out of scope quickly (which releases
them), or do not currently use connection.close() should be examined
to see if the connections can be closed earlier. This allows maximum reuse of
DRCP pooled servers by other users:
const pool = await oracledb.createPool({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb:pooled?pool_connection_class=MYAPP&pool_purity=self"
});
// Do some database operations
const connection = pool.getConnection();
...
connection.close();
// Do lots of non-database work
. . .
// Do some more database operations
const connection = pool.getConnection(); // Get a new pooled server only when needed
. . .
connection.close();
4.9.3. Setting DRCP Parameters in Connection Strings
You can specify the connection class and pool purity in connection strings when using node-oracledb Thin mode with Oracle Database 21c (or later). For node-oracledb Thick mode, you require Oracle Database 21c (or later) and Oracle Client 19c (or later).
DRCP allows the connection session memory to be reused or cleaned each time a
connection is acquired from the pool by specifying the pool purity. You can
specify the pool purity in node-oracledb by setting the POOL_PURITY
parameter in a connection string. The valid values for POOL_PURITY are
SELF and NEW. These values are not case-sensitive. The value NEW
indicates that the application must use a new session. The value SELF allows
the application to reuse both the pooled server process and session memory,
giving maximum benefit from DRCP. By default, node-oracledb pooled connections
use SELF and standalone connections use NEW.
The connection class can be specified in a connection string by setting the
POOL_CONNECTION_CLASS parameter. The value for POOL_CONNECTION_CLASS
can be any string conforming to connection class semantics and is
case-sensitive.
An example of setting the connection class and pool purity in an Easy Connect syntax is shown below:
const pool = await oracledb.createPool({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb:pooled?pool_connection_class=MYAPP&pool_purity=self"
});
An example of setting the connection class and pool purity in a Connect Descriptor is shown below:
db_alias =
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(PORT=1522)(HOST=abc.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=cdb1_pdb1.regress.rdbms.dev.us.oracle.com)(SERVER=POOLED)
(POOL_CONNECTION_CLASS=cclassname)(POOL_PURITY=SELF)))
4.9.4. Monitoring DRCP
Data dictionary views are available to monitor the performance of DRCP. Database administrators can check statistics such as the number of busy and free servers, and the number of hits and misses in the pool against the total number of requests from clients. The views include:
DBA_CPOOL_INFO
V$PROCESS
V$SESSION
V$CPOOL_STATS
V$CPOOL_CC_STATS
V$CPOOL_CONN_INFO
DBA_CPOOL_INFO View
DBA_CPOOL_INFO displays configuration information about the DRCP pool. The
columns are equivalent to the dbms_connection_pool.configure_pool()
settings described in the table of DRCP configuration options, with the
addition of a STATUS column. The status is ACTIVE if the pool has been
started and INACTIVE otherwise. Note that the pool name column is called
CONNECTION_POOL. This example checks whether the pool has been started and
finds the maximum number of pooled servers:
SQL> SELECT connection_pool, status, maxsize FROM dba_cpool_info;
CONNECTION_POOL STATUS MAXSIZE
---------------------------- ---------- ----------
SYS_DEFAULT_CONNECTION_POOL ACTIVE 40
V$PROCESS and V$SESSION Views
The V$SESSION view shows information about the currently active DRCP
sessions. It can also be joined with V$PROCESS through
V$SESSION.PADDR = V$PROCESS.ADDR to correlate the views.
V$CPOOL_STATS View
The V$CPOOL_STATS view displays information about the DRCP statistics for an instance. The V$CPOOL_STATS view can be used to assess the efficiency of the pool settings. This example query shows an application using the pool effectively. The low number of misses indicates that servers and sessions were reused. The wait count shows just over 1% of requests had to wait for a pooled server to become available:
NUM_REQUESTS NUM_HITS NUM_MISSES NUM_WAITS
------------ ---------- ---------- ----------
10031 99990 40 1055
If connectionClass was set (allowing pooled servers and sessions to be
reused), then NUM_MISSES will be low. If the pool maxsize is too small for
the connection load, then NUM_WAITS will be high.
V$CPOOL_CC_STATS View
The view V$CPOOL_CC_STATS displays information about the connection class level statistics for the pool per instance:
SQL> select cclass_name, num_requests, num_hits, num_misses
from v$cpool_cc_stats;
CCLASS_NAME NUM_REQUESTS NUM_HITS NUM_MISSES
-------------------------------- ------------ ---------- ----------
HR.MYCLASS 100031 99993 38
The class name columns shows the database user name appended with the connection class name.
V$CPOOL_CONN_INFO View
The V$POOL_CONN_INFO view gives insight into client processes that are connected to the connection broker, making it easier to monitor and trace applications that are currently using pooled servers or are idle. This view was introduced in Oracle 11gR2.
You can monitor the view V$CPOOL_CONN_INFO to, for example, identify misconfigured machines that do not have the connection class set correctly. This view maps the machine name to the class name. In node-oracledb Thick mode, the class name will default to one as shown below:
SQL> select cclass_name, machine from v$cpool_conn_info;
CCLASS_NAME MACHINE
--------------------------------------- ------------
GK.OCI:SP:wshbIFDtb7rgQwMyuYvodA gklinux
In this example, you would examine applications on gklinux and make them
set connectionClass.
When connecting to Oracle Autonomous Database on Shared Infrastructure (ADB-S), the V$CPOOL_CONN_INFO view can be used to track the number of connection hits and misses to show the pool efficiency.
4.10. Implicit Connection Pooling
Implicit connection pooling is useful for applications that cause excess database server load due to the number of standalone connections opened. When these applications cannot be rewritten to use node-oracledb connection pooling, then implicit connection pooling may be an option to reduce the load on the database system.
Implicit connection pooling allows application connections to share pooled
servers in DRCP or Oracle Connection Manager in Traffic Director
Mode’s (CMAN-TDM) Proxy Resident Connection Pooling (PRCP). Applications do not need to be modified. The
feature is enabled by adding a POOL_BOUNDARY parameter to the
application’s connection string. Applications do
not need to explicitly acquire, or release, connections to be able use a DRCP
or PRCP pool.
Implicit connection pooling is available in node-oracledb Thin and Thick modes. It requires Oracle Database version 23 or later. Node-oracledb Thick mode additionally requires Oracle Client version 23 or later libraries. The Thin mode works with implicit connection pooling from node-oracledb 6.4 onwards.
With implicit connection pooling, connections are internally acquired from the
DRCP or PRCP pool when they are actually used by the application to do database
work. They are internally released back to pool when not in use. This may
occur between the application’s explicit oracledb.getConnection() call
and connection.close() (or the application’s equivalent connection
release at end-of-scope). The internal connection release can be controlled by
the value of the POOL_BOUNDARY connection string parameter, which can be
either:
STATEMENT: If this boundary value is specified, then the connection is released back to the DRCP or PRCP pool when the connection is implicitly stateless. A connection is implicitly stateless when all open cursors in a session have been fetched through to completion, and there are no active transactions, no temporary tables, and no temporary LOBs.
TRANSACTION: If this boundary value is specified, then the connection is released back to the DRCP or PRCP pool when either one of the methods
connection.commit()orconnection.rollback()are called. It is recommended to not set the autoCommit property to true when using implicit connection pooling. If you do set this attribute to true, then you will be unable to:Fetch any data that requires multiple round-trips to the database
Inline with DRCP and PRCP best practices regarding session sharing across
differing applications, you should add a connection string
POOL_CONNECTION_CLASS parameter, using the same value for all applications
that are alike.
The DRCP and PRCP “purity” used by Implicit Connection Pooling defaults to
SELF, which allows reuse of the server process session memory for best
performance. Adding the connection string parameter POOL_PURITY=NEW will
change this and cause each use of a connection to recreate the session memory.
Configuring Implicit Connection Pooling
To use implicit connection pooling in node-oracledb with DRCP:
Enable DRCP in the database. For example in SQL*Plus:
SQL> EXECUTE DBMS_CONNECTION_POOL.START_POOL();
Specify the server type as pooled and also set the
POOL_BOUNDARYattribute to either STATEMENT or TRANSACTION in:The
connectStringproperty oforacledb.getConnection()ororacledb.createPool()in the Easy Connect string. For example:const connection = await oracledb.getConnection({ user : "hr", password : mypw, // mypw contains the hr schema password connectString : "mydbmachine.example.com:1521/orclpdb1:pooled?pool_boundary=statement" });
In the above example, implicit connection pooling is set to use statement boundary to release the connections back to the DRCP or PRCP pool.
Or the
CONNECT_DATAsection of the Connect Descriptor used in an Oracle Network configuration file such as tnsnames.ora. For example, to use implicit connection pooling with the transaction boundary:tnsalias = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=mymachine.example.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl) (SERVER=POOLED)(POOL_BOUNDARY=TRANSACTION)))
In the above example, implicit connection pooling is set to use transaction boundary to release the connections back to the DRCP or PRCP pool.
Note
Implicit connection pooling is not enabled if the application sets the
POOL_BOUNDARYattribute to TRANSACTION or STATEMENT but does not set theSERVER=POOLEDattribute in the connection string.
If you specify an invalid
POOL_BOUNDARYin the Easy Connect string or the Connect Descriptor, then the following error is returned:ORA-24545: invalid value of POOL_BOUNDARY specified in connect string
Set the connection class in:
The
connectStringproperty oforacledb.getConnection()ororacledb.createPool()in the Easy Connect string. For example, to use a class name ‘myapp’:const connection = await oracledb.getConnection({ user : "hr", password : mypw, // mypw contains the hr schema password connectString : "mydbmachine.example.com:1521/orclpdb1:pooled?pool_boundary=statement&pool_connection_class=myapp" });
Or the
CONNECT_DATAsection of the Connect Descriptor. For example, to use a class name ‘myapp’:tnsalias = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=mymachine.example.com) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orcl) (SERVER=POOLED)(POOL_BOUNDARY=TRANSACTION) (POOL_CONNECTION_CLASS=myapp)))
Use the same connection class name for application processes of the same type where you want session memory to be reused for connections.
The pool purity can also optionally be changed by adding
POOL_PURITY=NEWto the Easy Connect string or Connect Descriptor.
Similar steps can be used with PRCP. For general information on PRCP, see the technical brief CMAN-TDM — An Oracle Database connection proxy for scalable and highly available applications.
Implicit Pooling Notes
You should thoroughly test your application when using implicit connection pooling to ensure that the internal reuse of database servers does not cause any problems. For example, any session state such as the connection session id and serial number will vary throughout the lifetime of the application connection because different servers may be used at different times. Another example is when using a statement boundary of transaction. In this scenario, any commit can invalidate open cursors.
It is recommended to use node-oracledb’s local Connection Pooling where possible instead of implicit connection pooling. This gives multi-user applications more control over pooled server reuse.
4.11. Privileged Connections
Database privileges such as SYSDBA or SYSOPER can be associated with
the user when creating standalone and pooled connections. You can use one of
the Privileged Connection Constants as the
database privilege for the user.
For standalone connections, you must set the
privilege property in
oracledb.getConnection() as shown in the example below:
const connection = await oracledb.getConnection({
user : "sys",
password : "secret",
connectString : "localhost/orclpdb1",
privilege : oracledb.SYSDBA
});
console.log("I have power");
For pooled connections with node-oracledb Thin mode, you
must set the privilege,
user, and
password properties in
oracledb.createPool(). For example:
const pool = await oracledb.createPool({
user : "sys",
password : "secret",
connectString : "localhost/orclpdb1",
privilege : oracledb.SYSDBA
poolMin : 2,
poolMax : 10
});
const connection = await pool.getConnection();
The ability to specify database privileges with pooled connections in Thin mode was introduced in node-oracledb 6.5.1.
For node-oracledb Thick mode, privileged connections can only be created with
a heterogeneous pool. You must set the
homogeneous property to false in
oracledb.createPool() to use a heterogeneous pool. You can then
specify the privilege,
user, and
password properties in
pool.getConnection(). For example:
const pool = await oracledb.createPool({
connectString : "localhost/orclpdb1",
homogeneous : false,
poolMax : 10
});
const connection = await pool.getConnection({
user : "sys",
password : "secret",
privilege : oracledb.SYSDBA
})
If you create a homogeneous pool with an invalid value specified in the
privilege property of
oracledb.createPool() in both node-oracledb Thin and Thick modes, then
the following error is raised:
NJS-007: invalid value for "privilege" in parameter 1
However, any valid privilege property value is ignored in node-oracledb
Thick mode during homogeneous pool creation.
Note that if node-oracledb Thick mode is using the Oracle Client libraries
located in the Oracle Database installation, that is on the same machine as
the database and is not using Oracle Instant Client, then operating system
privileges may be used for authentication. In this case the password
value is ignored. For example on Linux, membership of the operating
system dba group allows SYSDBA
connections.
Administrative privileges can allow access to a database instance even when the database is not open. Control of these privileges is totally outside of the database itself. Care must be taken with authentication to ensure security. See the Database Administrator’s Guide for information.
4.12. Securely Encrypting Network Traffic to Oracle Database
You can encrypt the data transferred between Oracle Database and node-oracledb so that unauthorized parties are not able to view plain text values as the data passes over the network.
Both node-oracledb Thin and Thick modes support TLS. Refer to the Oracle Database Security Guide for more configuration information.
4.12.1. Native Network Encryption
With Oracle Database’s native network encryption, the client and database server negotiate a key using Diffie-Hellman key exchange. There is protection against man-in-the-middle attacks.
Note
Oracle native network encryption is only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
Native network encryption can be configured by editing Oracle Net’s optional sqlnet.ora configuration file. The file on either the database server and/or on each node-oracledb ‘client’ machine can be configured. Parameters control whether data integrity checking and encryption is required or just allowed, and which algorithms the client and server should consider for use.
As an example, to ensure all connections to the database are checked for
integrity and are also encrypted, create or edit the Oracle Database
$ORACLE_HOME/network/admin/sqlnet.ora file. Set the checksum
negotiation to always validate a checksum and set the checksum type to
your desired value. The network encryption settings can similarly be
set. For example, to use the SHA512 checksum and AES256 encryption use:
SQLNET.CRYPTO_CHECKSUM_SERVER = required
SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA512)
SQLNET.ENCRYPTION_SERVER = required
SQLNET.ENCRYPTION_TYPES_SERVER = (AES256)
If you definitely know that the database server enforces integrity and
encryption, then you do not need to configure node-oracledb separately.
However you can also, or alternatively, do so depending on your business
needs. Create a file sqlnet.ora on your client machine and locate it with
other Optional Oracle Net Configuration:
SQLNET.CRYPTO_CHECKSUM_CLIENT = required
SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT = (SHA512)
SQLNET.ENCRYPTION_CLIENT = required
SQLNET.ENCRYPTION_TYPES_CLIENT = (AES256)
The client and server sides can negotiate the protocols used if the settings indicate more than one value is accepted.
Note these are example settings only. You must review your security requirements and read the documentation for your Oracle version. In particular, review the available algorithms for security and performance.
The NETWORK_SERVICE_BANNER column of the database view
V$SESSION_CONNECT_INFO
can be used to verify the encryption status of a connection. For example with
SQL*Plus:
SQL> SELECT network_service_banner FROM v$session_connect_info;
If the connection is encrypted, then this query prints an output that includes the available encryption service, the crypto-checksumming service, and the algorithms in use, such as:
NETWORK_SERVICE_BANNER
-------------------------------------------------------------------------------------
TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
Encryption service for Linux: Version 19.0.1.0.0 - Production
AES256 Encryption service adapter for Linux: Version 19.0.1.0.0 - Production
Crypto-checksumming service for Linux: Version 19.0.1.0.0 - Production
SHA256 Crypto-checksumming service adapter for Linux: Version 19.0.1.0.0 - Production
If the connection is unencrypted, then the query will only print the available encryption and crypto-checksumming service messages. For example:
NETWORK_SERVICE_BANNER
-------------------------------------------------------------------------------------
TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
Encryption service for Linux: Version 19.0.1.0.0 - Production
Crypto-checksumming service for Linux: Version 19.0.1.0.0 - Production
For more information about Oracle Data Network Encryption and Integrity, and for information about configuring TLS network encryption, refer to the Oracle Database Security Guide. This manual also contains information about other important security features that Oracle Database provides, such Transparent Data Encryption of data-at-rest in the database.
4.13. Changing Passwords and Connecting with an Expired Password
4.13.1. Changing Passwords
Database passwords can be changed with connection.changePassword().
For example:
const currentpw = ... // the current password for the hr schema
const newpw = ... // the new hr schema password
const connection = await oracledb.getConnection({
user : "hr",
password : currentpw,
connectString : "localhost/orclpdb1"
});
await connection.changePassword("hr", currentpw, newpw);
Only DBAs or users with the ALTER USER privilege can change the password of another user. In this case, the old password value is ignored and can be an empty string:
const newpw = ... // the new password
const connection = await oracledb.getConnection({
user : "system", // a privileged user
password : mypw, // mypw contains the system schema password
connectString : "localhost/orclpdb1"
});
await connection.changePassword('hr', '', newpw);
4.13.2. Connecting with an Expired Password
When creating a standalone (non-pooled) connection, the user’s password can be changed at the time of connection. This is most useful when the user’s password has expired, because it allows a user to connect without requiring a DBA to reset their password.
Both the current and new passwords must be given when connecting. For example:
const oldpw = ... // the hr schema's old password
const newpw = ... // the new password
const connection = await oracledb.getConnection({
user : "hr",
password : oldpw,
newPassword : newpw,
connectString : "localhost/orclpdb1"
});
4.14. Connections and High Availability
To make highly available applications, use the latest versions of Oracle node-oracledb and Oracle Database. If you are using node-oracledb Thick mode, then also use the latest Oracle Client libraries which have improved implementations to make connections efficient and available. In addition, features like Connection Pool Pinging, Fast Application Notification (FAN), Application Continuity, and Oracle Net Services settings can all help high availability, often without the application being aware of any issue. Some of these features are only supported in node-oracledb Thick mode.
For application high availability, use a connection pool. Pools provide immediately available connections. Also the internal pool implementation supports a number of Oracle Database high availability features for planned and unplanned database instance downtime. Use a fixed size pool to avoid connection storms.
Configuring TCP timeouts can help avoid application hangs if there is a network failure. FAN is also useful.
Oracle Net options may be useful for high availability and performance
tuning. Connection establishment timeouts can be
set. The database’s listener.ora file can have
RATE_LIMIT and
QUEUESIZE parameters that can help
handle connection storms. In the bigger picture, Oracle Net can be used to
configure database service settings, such as for failover using
Oracle RAC or a standby database.
Database Resident Connection Pooling (DRCP) may be useful to reduce load on a database host. It can also help reduce connection time when a number of Node.js processes are used to scale up an application.
Finally, applications should always check for execution errors, and perform appropriate application-specific recovery.
4.14.1. Preventing Premature Connection Closing
When connections are idle, external events may disconnect them from the database. Unnecessarily having to re-establish connections can impact scalability, cause connection storms, or lead to application errors when invalid connections are attempted to be used.
There are three components to a node-oracledb connection:
The memory structure in node-oracledb that is returned by a
getConnection()call. It may be a standalone connection or stored in a connection pool.The underlying network connection between the database and node-oracledb Thin mode network handling code or Oracle Client libraries.
A server process, or thread, on the database host to handle database processing.
Node-oracledb connections may become unusable due to network dropouts,
database instance failures, exceeding user profile resource limits, or
by explicit session closure of the server process from a DBA. By
default, idle connections (the memory structures) in connection pools
are unaware of these events. A subsequent pool.getConnection() call
could successfully return a “connection” to the application that will
not be usable. An error would only occur when later calling functions
like connection.execute(). Similarly, using a standalone connection
where the network has dropped out, or the database instance is
unavailable, will return an error.
To avoid the overhead of connection re-creation, disable any firewall that is killing idle connections. Also disable the database resource manager and any user resource profile IDLE_TIME setting so they do not terminate sessions. These issues can be hidden by node-oracledb’s automatic connection re-establishment features so it is recommended to use AWR to check the connection rate, and then fix underlying causes.
You can use an ‘expire time’ setting to prevent firewalls from terminating idle connections and to adjust keepalive timeouts. The general recommendation is to use a value that is slightly less than half of the termination period. In node-oracledb Thin mode you can set the value in the connection string or with expireTime when connecting. This setting can also aid detection of a terminated remote database server.
With node-oracledb Thick mode, when using Oracle Client 19c, EXPIRE_TIME can be
used in tnsnames.ora connect descriptors or in Easy
Connect strings. With Oracle Client 21c the setting can
alternatively be in the application’s sqlnet.ora file. In
older versions of Oracle Client, a tnsnames.ora connect descriptor option
ENABLE=BROKEN can be used instead of EXPIRE_TIME.
If the network or the database server processes used by node-oracledb connections cannot be prevented from becoming unusable, tune Connection Pool Pinging. Another case where this internal pinging is helpful is during development, where a laptop may go offline for an extended time.
4.14.2. Fast Application Notification (FAN)
Users of Oracle Database FAN
must connect to a FAN-enabled database service. The application should
have oracledb.events is set to true. This value can also be
changed via Oracle Client Configuration.
Note
In this release, FAN is only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
FAN support is useful for planned and unplanned outages. It provides immediate notification to node-oracledb following outages related to the database, computers, and networks. Without FAN, node-oracledb can hang until a TCP timeout occurs and a network error is returned, which might be several minutes.
FAN allows node-oracledb to provide high availability features without
the application being aware of an outage. Unused, idle connections in a
connection pool will be automatically cleaned up. A future
pool.getConnection() call will establish a fresh connection to a
surviving database instance without the application being aware of any
service disruption.
To handle errors that affect active connections, you can add application logic to re-connect (this will connect to a surviving database instance) and replay application logic without having to return an error to the application user. Alternatively, use Application Continuity.
FAN benefits users of Oracle Database’s clustering technology (Oracle RAC) because connections to surviving database instances can be immediately made. Users of Oracle’s Data Guard with a broker will get FAN events generated when the standby database goes online. Standalone databases will send FAN events when the database restarts.
For a more information on FAN see the technical paper on Fast Application Notification.
4.14.3. Runtime Load Balancing (RLB)
Oracle Database RAC users with Oracle Database (RLB) advisory events configured should use
node-oracledb Connection Pooling and make sure
oracledb.events is true. The events mode can also be
changed via Oracle Client Configuration.
Note
In this release, RLB is only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
RLB allows optimal use of database resources by balancing database requests across RAC instances.
For a more information on RLB, see the technical paper on Fast Application Notification.
4.14.4. Application Continuity
Node-oracledb OLTP applications can take advantage of continuous availability with the Oracle Database features Application Continuity (AC) and Transparent Application Continuity (TAC). These help make unplanned database service downtime transparent to applications.
Note
In this release, Oracle AC and TAC functionalities are only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
See the technical papers Application Checklist for Continuous Service for MAA Solutions, Continuous Availability Application Continuity for the Oracle Database, and Continuous Availability Best Practices for Applications Using Autonomous Database - Dedicated.
When AC or TAC are configured on the database service, they are transparently available to node-oracledb applications.
4.14.5. Transaction Guard
From version 6.9 onwards, node-oracledb supports Transaction Guard which enables Node.js applications to verify the success or failure of the last transaction in the event of an unplanned outage. This feature requires Oracle Database 12.1 or later. For node-oracledb Thick mode, Oracle Client 12.1 or later is additionally required.
Using Transaction Guard helps to
Preserve the commit outcome
Ensure a known outcome for every transaction
See Oracle Database Development Guide for more information about using Transaction Guard.
When an error occurs during a commit, the Node.js application can acquire the
logical transaction ID (ltxid) from the connection and then call a
procedure to determine the outcome of the commit for this logical transaction
ID.
To use Transaction Guard in node-oracledb in a single-instance database, perform the following steps:
Grant execute privileges to the database users who will be checking the outcome of the commit. Log in as SYSDBA and run the following command:
GRANT EXECUTE ON DBMS_APP_CONT TO <username>;
Create a new service by calling DBMS_SERVICE.CREATE_SERVICE as SYSDBA. Replace the
<service-name>,<network-name>and<retention-value>values with suitable values. Note that theCOMMIT_OUTCOMEparameter must be set to true for Transaction Guard to function properly.DECLARE t_Params dbms_service.svc_parameter_array; BEGIN t_Params('COMMIT_OUTCOME') := 'true'; t_Params('RETENTION_TIMEOUT') := <retention-value>; DBMS_SERVICE.CREATE_SERVICE('<service-name>', '<network-name>', t_Params); END; /
Start the service by calling DBMS_SERVICE.START_SERVICE as SYSDBA:
BEGIN DBMS_SERVICE.START_SERVICE('<service-name>'); END; /
Ensure that the service is running by examining the output of the following query:
SELECT name, network_name FROM V$ACTIVE_SERVICES ORDER BY 1;
Refer to Oracle documentation if you are using RAC or standby databases.
Node.js Application code requirements to use Transaction Guard
In the Node.js application code:
Connect to the appropriately enabled database service. If the connection is TAF, AC, or TAC enabled, then do not proceed with Transaction Guard.
Check
error.isRecoverableto confirm the error is recoverable. If not, do not proceed with Transaction Guard.Use the connection attribute
connection.ltxidto find the logical transaction ID.Call the DBMS_APP_CONT.GET_LTXID_OUTCOME PL/SQL procedure with the logical transaction ID. This returns a boolean value indicating if the last transaction was committed and whether the last call was completed successfully or not.
Take any necessary action to re-do uncommitted work.
See transactionguard.js for an example of using Transaction Guard.
4.14.6. Database Call Timeouts
4.14.6.1. Limiting the time to open new connections
To limit the amount of time taken to establish new connections to Oracle Database:
In node-oracledb Thin mode: You can use the connection attributes connectTimeout or transportConnectTimeout, or use the CONNECT_TIMEOUT timeout parameter in a connection string.
In node-oracledb Thick mode: You can use SQLNET.OUTBOUND_CONNECT_TIMEOUT in a sqlnet.ora file or CONNECT_TIMEOUT in a connection string. When node-oracledb Thick mode uses Oracle Client libraries 19c or later, timeouts can be passed in Easy Connect strings, for example to timeout after 15 seconds:
"mydbmachine.example.com/orclpdb1?connect_timeout=15".
When using a connection pool, these values affect the time taken to establish
each connection stored in the pool. The queueTimeout and
queueMax settings control higher-level pool behavior.
4.14.6.2. Limiting the time taken to execute statements
To limit the amount of time taken to execute statements on connections:
In node-oracledb Thin mode: You can use
connection.callTimeoutwhich is described below.In node-oracledb Thick mode: You can use Oracle Net settings like SQLNET.RECV_TIMEOUT and SQLNET.SEND_TIMEOUT in a
sqlnet.orafile. Or you can use theconnection.callTimeoutattribute which is available when node-oracledb uses Oracle Client libraries version 18, or later. The necessary out-of-band break setting is automatically configured when using Oracle Client 19 and Oracle Database 19, or later. With older Oracle versions on systems that drop (or in-line) out-of-band breaks, you may need to add DISABLE_OOB=ON to asqlnet.orafile.
The connection.callTimeout attribute is a millisecond timeout for
executing database calls on a connection. The connection.callTimeout
period is on each individual round-trip between
node-oracledb and Oracle Database. Each node-oracledb method or operation
may require zero or more round-trips to Oracle Database. The callTimeout
value applies to each round-trip individually, not to the sum of all
round-trips. Time spent processing in node-oracledb before or after the
completion of each round-trip is not counted.
If the time from the start of any one round-trip to the completion of that same round-trip exceeds
callTimeoutmilliseconds, then the operation is halted and an error is returned.In the case where a node-oracledb operation requires more than one round-trip and each round-trip takes less than
callTimeoutmilliseconds, then no timeout will occur, even if the sum of all round-trip calls exceedscallTimeout.If no round-trip is required, the operation will never be interrupted.
After a timeout occurs, node-oracledb attempts to clean up the internal
connection state. The cleanup is allowed to take another callTimeout
milliseconds.
If the cleanup was successful, a DPI-1067 error will be returned and the application can continue to use the connection.
For small values of callTimeout, the connection cleanup may not
complete successfully within the additional callTimeout period. In
this case a DPI-1080 error is returned and the connection will no
longer be usable. The application should then close the connection.
4.15. Connecting to Oracle Real Application Clusters (RAC)
Oracle Real Application Clusters (RAC) allow a single Oracle Database to be run across multiple servers. This maximizes availability and enables horizontal scalability.
The Thin and Thick modes of node-oracledb can connect to Oracle RAC by using a standard RAC connection string. Best practice is to use a Connection Pool with events enabled. See the section Connections and High Availability.
Also see the technical papers Application Checklist for Continuous Service for MAA Solutions and Continuous Availability Application Continuity for the Oracle Database.
4.16. Connecting to Oracle Cloud Autonomous Databases
Node.js applications can connect to Oracle Autonomous Database (ADB) in Oracle Cloud using one-way TLS (Transport Layer Security) or mutual TLS (mTLS), depending on how the database instance is configured. One-way TLS and mTLS provide enhanced security for authentication and encryption.
A database username and password are still required for your application connections. Refer to the relevant Oracle Cloud documentation, for example, see Create Database Users in the Oracle Autonomous Database manual.
When using node-oracledb Thin mode, Node.js flags can be used to set the
minimum TLS version used to connect to Oracle Database. For example, node
--tls-min-v1.3 examples/select1.js.
4.16.1. One-way TLS Connection to Oracle Autonomous Database
With one-way TLS, the node-oracledb host machine must be in the Access Control List (ACL) of the ADB instance. Applications then connect to Oracle ADB by passing the database username, password, and appropriate connection string. A wallet is not used.
Both node-oracledb Thin and Thick modes support one-way TLS.
4.16.1.1. Allowing One-way TLS Access to Oracle Autonomous Database
To create an ADB instance that allows one-way TLS, choose the access setting Secure access from allowed IPs and VCNs only in the Oracle Cloud console during instance creation. Then specify the IP addresses, hostnames, CIDR blocks, Virtual Cloud networks (VCN), or Virtual Cloud network OCIDs where Node.js will be running. The ACL limits access to only the resources that have been defined and blocks all other incoming traffic.
Alternatively, to enable one-way TLS on an existing database, complete the following steps in the Oracle Cloud console in the Autonomous Database Information section of the ADB instance:
Click the Edit link next to Access Control List to update the Access Control List (ACL).
In the Edit Access Control List dialog box that is displayed, select the type of address list entries and the corresponding values. You can include the IP addresses, hostnames, CIDR block, Virtual Cloud Network (VCN), or Virtual Cloud network OCID where Node.js will be running.
Navigate back to the ADB instance details page and click the Edit link next to Mutual TLS (mTLS) Authentication.
In the Edit Mutual TLS Authentication dialog box that is displayed, deselect the Require mutual TLS (mTLS) authentication check box to disable the mTLS requirement on Oracle ADB and click Save Changes.
4.16.1.2. Connecting with node-oracledb Thin or Thick modes using One-way TLS
When your database has been enabled to allow one-way TLS, you can connect with node-oracledb by following these steps:
Navigate to the ADB instance details page on the Cloud console and click Database Connection at the top of the page.
In the Database Connection dialog box that is displayed, select TLS from the Connection Strings drop-down list.
Copy the appropriate Connection String for the connection service level you want.
Applications can connect using the database credentials and the copied connection string. Do not pass wallet parameters. For example, to connect to the Oracle ADB instance:
const cs = `(description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)
(host=abc.oraclecloud.com))(connect_data=(service_name=xyz.adb.oraclecloud.com))
(security=(ssl_server_dn_match=yes)))`;
connection = await oracledb.getConnection({
user: "scott",
password: mypw, // mypw contains the scott schema password
connectString: cs
});
If you prefer to keep connection descriptors out of application code, you can
add the descriptor with a TNS Alias to a tnsnames.ora file, and use the TNS alias as the dsn value.
A common cause of connection errors is not having the ACL correctly
configured. To aid troubleshooting, remove (retry_count=20)(retry_delay=3)
from the connect descriptor so that errors are returned faster. If network
configuration issues are suspected then, for initial troubleshooting with a
temporary database for testing, you can update the ACL to contain a CIDR block
of 0.0.0.0/0. However, this means that anybody can attempt to connect to
your database. So, you should recreate the database immediately after
identifying a working, more restrictive ACL.
To connect with node-oracledb Thick mode requires Oracle Client library
versions 19.14 (or later), or 21.5 (or later), or 23.3 (or later). If you have
also been experimenting with mTLS and your environment has sqlnet.ora and
tnsnames.ora files set up, then remove these before using node-oracledb
Thick mode with one-way TLS to avoid configuration clashes.
4.16.2. Mutual TLS connections to Oracle Cloud Autonomous Database
To enable connections from node-oracledb to Oracle Autonomous Database in Oracle Cloud using mutual TLS (mTLS), a wallet needs to be downloaded from the cloud console. mTLS is sometimes called Two-way TLS.
4.16.2.1. Allowing mTLS Access to Oracle Autonomous Database
When creating an ADB instance in the Oracle Cloud console, choose the access setting Secure access from everywhere.
4.16.2.2. Downloading the Database Wallet
After your Autonomous Database has been enabled to allow mTLS, download its
wallet.zip file which contains the certificate and network configuration
files:
Navigate to the ADB instance details page on the Oracle Cloud console and click Database connection at the top of the page.
In the Database Connection dialog box that is displayed, select the “Download Wallet” button in the Download client credentials (Wallet) section. The cloud console will ask you to create a wallet password. This password is required by node-oracledb in Thin mode, but not used in Thick mode.
Note: Keep wallet files in a secure location and only share them and the password with authorized users.
4.16.2.3. Connecting with node-oracledb Thin mode using mTLS
For node-oracledb Thin mode, unzip the wallet.zip file. Only two files from the zip are needed:
tnsnames.ora- Maps net service names used for application connection strings to your database services.ewallet.pem- Enables SSL/TLS connections in Thin mode. Keep this file secure.
If you do not have a PEM file, see Creating a PEM File for node-oracledb Thin Mode.
Move the two files to a directory that is accessible by your application. In
this example, the files are located in the same directory,
/opt/OracleCloud/MYDB.
A connection can be made by using your database credentials and setting the
connectString parameter to the desired TNS Alias from
the tnsnames.ora file. The configDir parameter indicates
the directory containing tnsnames.ora. The
walletLocation parameter is the directory containing the PEM file. The
walletPassword parameter should be set to the password created in the
cloud console when downloading the wallet. It is not the database user or
ADMIN password.For example, to connect as the ADMIN user using the
cjdb1_high TNS Alias:
connection = await oracledb.getConnection({
user: "admin",
password: mypw, // database password for ADMIN
connectString: "cjdb1_high", // TNS Alias from tnsnames.ora
configDir: "/opt/OracleCloud/MYDB", // directory with tnsnames.ora
walletLocation: "/opt/OracleCloud/MYDB", // directory with ewallet.pem
walletPassword: wp // not a database user password
});
Instead of storing and reading the content from the ewallet.pem file which
is specified in the walletLocation property, you can use the
walletContent property to directly
specify the security credentials required to establish a mutual TLS connection
to Oracle Database. This property was introduced in node-oracledb 6.6 and can
be used with the oracledb.getConnection() and
oracledb.createPool() methods. The value of this property takes
precedence and overrides the walletLocation property value of
oracledb.getConnection(), or the WALLET_LOCATION parameter
in the connection string.
4.16.2.4. Connecting with node-oracledb Thick mode using mTLS
For node-oracledb Thick mode, unzip the wallet.zip file. Only three files from it are needed:
tnsnames.ora- Maps TNS Aliases used for application connection strings to your database services.sqlnet.ora- Configures Oracle Network settings.cwallet.sso- Enables SSL/TLS connections in node-oracledb Thick mode. Keep this file secure.
There are two options for placing the required files:
Move the three files to the
network/admindirectory of the client libraries used by your application. For example, if you are using Instant Client version 23 and it is in$HOME/instantclient_23_9, then you would put the wallet files in$HOME/instantclient_23_9/network/admin/.A connection can be made using your database credentials and setting the
connectStringparameter to the desired TNS Alias from the tnsnames.ora file. For example, to connect as the ADMIN user using thecjdb1_highnetwork service name:connection = await oracledb.getConnection({ user: "admin", password: mypw, // mypw contains the admin schema password connectString: "cjdb1_high" });
Alternatively, move the three files to any accessible directory, for example
/opt/OracleCloud/MYDB.Then edit
sqlnet.oraand change the wallet location directory to the directory containing thecwallet.ssofile. For example:WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/opt/OracleCloud/MYDB"))) SSL_SERVER_DN_MATCH=yes
Since the
tnsnames.oraandsqlnet.orafiles are not in the default location, your application needs to indicate where they are, either with the configDir parameter toinitOracleClient(), or by using theTNS_ADMINenvironment variable. See Optional Oracle Net Configuration. (Neither of these settings are needed, and you do not need to editsqlnet.ora, if you have put all the files in thenetwork/admindirectory.)For example, to connect as the ADMIN user using the
cjdb1_highTNS alias:const oracledb = require('oracledb'); oracledb.initOracleClient({configDir: '/opt/OracleCloud/MYDB'}); connection = await oracledb.getConnection({ user: "admin", password: mpw, connectString: "cjdb1_high" });
In node-oracle Thick mode, to create mTLS connections in one Node.js process
to two or more Oracle Autonomous Databases, move each cwallet.sso file to
its own directory. For each connection use different connection string
WALLET_LOCATION parameters to specify the directory of each cwallet.sso
file. It is recommended to use Oracle Client libraries 19.17 (or later) when
using multiple wallets.
4.16.2.5. Using the Easy Connect Syntax with Autonomous Database
You can optionally use the Easy Connect syntax to connect to Oracle Autonomous Database. When using node-oracledb Thick mode this requires using Oracle Client libraries 19c or later.
This section details the parameters for mTLS connection.
The mapping from a cloud tnsnames.ora entries to an Easy Connect string is:
protocol://host:port/service_name?wallet_location=/my/dir&retry_count=N&retry_delay=N
For example, if your tnsnames.ora file had an entry:
cjjson_high = (description=(retry_count=20)(retry_delay=3)
(address=(protocol=tcps)(port=1522)
(host=efg.oraclecloud.com))
(connect_data=(service_name=abc_cjjson_high.adb.oraclecloud.com))
(security=(ssl_server_cert_dn="CN=efg.oraclecloud.com, O=Oracle Corporation, L=Redwood City, ST=California, C=US")))
Then your applications can connect using the connection string:
cs = "tcps://efg.oraclecloud.com:1522/abc_cjjson_high.adb.oraclecloud.com?wallet_location=/Users/cjones/Cloud/CJJSON&retry_count=20&retry_delay=3"
connection = await oracledb.getConnection({
user : "hr",
password : mypw,
connectString : cs
});
The walletLocation parameter needs to be set to the directory
containing the cwallet.sso or ewallet.pem file from the
wallet.zip file. The other files, including
tnsnames.ora, are not needed when you use the Easy Connect syntax.
You can add other Easy Connect parameters to the connection string, for example:
cs = cs + "&https_proxy=myproxy.example.com&https_proxy_port=80"
With node-oracledb Thin mode, the wallet password needs to be passed as a connection parameter.
4.16.2.6. Creating a PEM File for node-oracledb Thin Mode
For mutual TLS in node-oracledb Thin mode, the certificate must be Privacy
Enhanced Mail (PEM) format. If you are using Oracle Autonomous Database and
your wallet zip file does not already include a PEM file, then you can convert
the PKCS12 ewallet.p12 file to PEM format using third party tools. For
example, using OpenSSL:
openssl pkcs12 -in ewallet.p12 -out wallet.pem
Once the PEM file has been created, you can use it by passing its directory
location as the walletLocation property to
oracledb.getconnection() or oracledb.createPool(). Instead of
storing and reading the content from the ewallet.pem file which is
specified in the walletLocation property, you can use the
walletContent property to directly specify the security credentials
required to establish a mutual TLS connection to Oracle Database. The
walletContent property was introduced in node-oracledb 6.6 and can be used
with getConnection() and createPool(). The
value of this property takes precedence and overrides the walletLocation
value set in getConnection() or
createPool(), or the WALLET_LOCATION parameter in the
connection string. The getConnection() and
createPool() methods also accept a walletPassword
property, which can be the passphrase that was specified when the above
openSSL command was run. See Mutual TLS connections to Oracle Cloud Autonomous Database.
4.16.2.7. Connecting Through a Firewall via a Proxy
If you are behind a firewall, you can tunnel TLS/SSL connections via a proxy by setting connection attributes, or by using HTTPS_PROXY and HTTPS_PROXY_PORT port parameters available in your connection string.
Note
Oracle does not recommend connecting through a firewall via a proxy when performance is critical.
In node-oracledb Thin Mode
Proxy settings
httpsProxyandhttpsProxyPortcan be passed during connection or pool creation:connection = await oracledb.getConnection({ user: "admin", password: mypw, connectString: "cjdb1_high", configDir: "/opt/OracleCloud/MYDB", walletLocation: "/opt/OracleCloud/MYDB", walletPassword: wp, httpsProxy: 'myproxy.example.com', httpsProxyPort: 80 });
Alternatively, add the parameters to your Easy Connect string:
localhost/orclpdb&https_proxy=myproxy.example.com&https_proxy_port=80
Alternatively, update the Connect Descriptor (either being passed directly during connection or contained in your tnsnames.ora file). If you are using a tnsnames.ora file, a modified entry might look like:
cjdb1_high = (description= (address= (https_proxy=myproxy.example.com)(https_proxy_port=80) (protocol=tcps)(port=1522)(host= . . . )))
You can pass this connect descriptor during connection creation:
connection = await oracledb.getConnection({ user: "admin", password: mypw, connectString: "cjdb1_high", configDir: "/opt/OracleCloud/MYDB", walletLocation: "/opt/OracleCloud/MYDB", walletPassword: wp, });
In node-oracledb Thick Mode
If you are using an Easy Connect string, add
HTTPS_PROXYandHTTPS_PROXY_PORTparameters with appropriate values for your proxy. For example, you might pass parameters like:localhost/orclpdb&https_proxy=myproxy.example.com&https_proxy_port=80
Alternatively, update the Connect Descriptor (either being passed directly during connection or contained in your tnsnames.ora file). If you are using a tnsnames.ora file, a modified entry might look like:
cjdb1_high = (description= (address=(https_proxy=myproxy.example.com)(https_proxy_port=80) (protocol=tcps)(port=1522)(host= . . .
Additionally, create or edit a sqlnet.ora file and add a line:
SQLNET.USE_HTTPS_PROXY=on
4.17. Connecting using Multiple Wallets
You can make multiple connections with different wallets in one Node.js process.
In node-oracledb Thin mode
To use multiple wallets in node-oracledb Thin mode, pass the different
connection strings, wallet locations, and wallet password (if required) in each
oracledb.getConnection() call or when creating a connection pool:
connection = await oracledb.getConnection({
user: "user_name",
password: userpw,
connectString: "cjdb1_high",
configDir: "/opt/OracleCloud/MYDB",
walletLocation: "/opt/OracleCloud/MYDB",
walletPassword: walletpw
});
The configDir parameter is the directory containing the tnsnames.ora file. The walletLocation parameter is the directory
containing the ewallet.pem file. If you are using Oracle Autonomous
Database, both of these paths are typically the same directory where the
wallet.zip file was extracted.
In node-oracledb Thick mode
To use multiple wallets in node-oracledb Thick mode, a TCPS connection string
containing the MY_WALLET_DIRECTORY option needs to be created:
ocidbdemo_high = (description=(retry_count=1)(retry_delay=3)
(address=(protocol=tcps)(port=1522)(host=efg.oraclecloud.com))
(connect_data=(service_name=abc_ocidbdemo_high.adb.oraclecloud.com))
(security=(ssl_server_cert_dn="CN=ijk.oraclecloud.com, O=Oracle Corporation, L=Redwood City, ST=California, C=US")
(my_wallet_directory="/home/user1/Wallet_OCIDBDemo")))
Note
Use Oracle Client libraries 19.17, or later, or use Oracle Client version 21 or 23. These contain important bug fixes for using multiple wallets in the one process.
4.18. Connecting to Oracle Globally Distributed Database
Oracle Globally Distributed Database is a feature of Oracle Database that lets you automatically distribute and replicate data across a pool of Oracle databases that share no hardware or software. It was previously known as Oracle Sharding. It allows a database table to be split so that each database contains a table with the same columns but a different subset of rows. These tables are known as sharded tables. From the perspective of an application, a sharded table in Oracle Globally Distributed Database looks like a single table: the distribution of data across those shards is completely transparent to the application.
Sharding is configured in Oracle Database, see the Oracle Globally Distributed Database manual. It requires Oracle Database and Oracle Client libraries 12.2, or later.
Note
In this release, Oracle Globally Distributed Database is only supported in node-oracledb Thick mode. See Enabling node-oracledb Thick Mode.
When a connection is opened in node-oracledb using
oracledb.getConnection(), the
shardingKey and
superShardingKey
properties can be used to route the connection directly to a given
shard. A sharding key is always required. A super sharding key is
additionally required when using composite sharding, which is when data
has been partitioned by a list or range (the super sharding key), and
then further partitioned by a sharding key.
When creating a connection pool, the property
poolMaxPerShard can be set. This is used to balance
connections in the pool equally across shards. It requires Oracle Client
libraries 18.3 or later.
When connected to a shard, queries only returns data from that shard. For queries that need to access data from multiple shards, connections can be established to the coordinator shard catalog database. In this case, no shard key or super shard key is used.
The sharding and super sharding key properties are arrays of values, that is multiple values can be used. Array key values may be of type String (mapping to VARCHAR2 sharding keys), Number (NUMBER), Date (DATE), or Buffer (RAW). Multiple types may be used in each array. Sharding keys of TIMESTAMP type are not supported by node-oracledb.
4.18.1. Examples to Connect to a Globally Distributed Database Based on the Sharding Key Type
The examples listed in this section show how to establish connections to an Oracle Globally Distributed Database based on the sharding key type.
VARCHAR2
If sharding has been configured on a single VARCHAR2 column:
CREATE SHARDED TABLE customers (
cust_id NUMBER,
cust_name VARCHAR2(30),
class VARCHAR2(10) NOT NULL,
signup_date DATE,
cust_code RAW(20),
CONSTRAINT cust_name_pk PRIMARY KEY(cust_name))
PARTITION BY CONSISTENT HASH (cust_name)
PARTITIONS AUTO TABLESPACE SET ts1;
then a direct connection to a shard can be made by passing a single sharding key:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb1",
shardingKey : ["SCOTT"]
});
NUMBER
If sharding has been configured on a single NUMBER column:
CREATE SHARDED TABLE customers (
cust_id NUMBER,
cust_name VARCHAR2(30),
class VARCHAR2(10) NOT NULL,
signup_date DATE,
cust_code RAW(20),
CONSTRAINT cust_id_pk PRIMARY KEY(cust_id))
PARTITION BY CONSISTENT HASH (cust_id)
PARTITIONS AUTO TABLESPACE SET ts1;
then a direct connection to a shard can be made by passing a single sharding key:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb1",
shardingKey : [110]
});
Multiple Keys
If database shards have been partitioned with multiple keys such as:
CREATE SHARDED TABLE customers (
cust_id NUMBER NOT NULL,
cust_name VARCHAR2(30) NOT NULL,
class VARCHAR2(10) NOT NULL,
signup_date DATE,
cust_code RAW(20),
CONSTRAINT cust_pk PRIMARY KEY(cust_id, cust_name));
PARTITION BY CONSISTENT HASH (cust_id, cust_name)
PARTITIONS AUTO TABLESPACE SET ts1;
then direct connection to a shard can be established by specifying multiple keys, for example:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb1",
shardingKey : [70, "SCOTT"]
});
DATE
If the sharding key is a DATE column:
CREATE SHARDED TABLE customers (
cust_id NUMBER,
cust_name VARCHAR2(30),
class VARCHAR2(10) NOT NULL,
signup_date DATE,
cust_code RAW(20),
CONSTRAINT signup_date_pk PRIMARY KEY(signup_date))
PARTITION BY CONSISTENT HASH (signup_date)
PARTITIONS AUTO TABLESPACE SET ts1;
then direct connection to a shard needs a Date key that is in the session time zone. For example if the session time zone is set to UTC (see Fetching Dates and Timestamps) then Dates must also be in UTC:
key = new Date ("2019-11-30Z"); // when session time zone is UTC
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb1",
shardingKey : [key]
});
RAW
If the sharding key is a RAW column:
CREATE SHARDED TABLE customers (
cust_id NUMBER,
cust_name VARCHAR2(30),
class VARCHAR2(10) NOT NULL,
signup_date DATE,
cust_code RAW(20),
CONSTRAINT cust_code_pk PRIMARY KEY(cust_code))
PARTITION BY CONSISTENT HASH (cust_code)
PARTITIONS AUTO TABLESPACE SET ts1;
then direct connection to a shard could be established by:
const data = [0x00, 0x01, 0x02];
const key = Buffer.from(data);
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb1",
shardingKey : [key]
});
Composite Sharding
If composite sharding (requires both sharding key and super sharding key) was in use, for example:
CREATE SHARDED TABLE customers (
cust_id NUMBER NOT NULL,
cust_name VARCHAR2(30) NOT NULL,
class VARCHAR2(10) NOT NULL,
signup_date DATE,
cust_code RAW(20),
PARTITIONSET BY LIST (class)
PARTITION BY CONSISTENT HASH (cust_name)
PARTITIONS AUTO (PARTITIONSET gold VALUES ('gold') TABLESPACE SET ts1,
PARTITIONSET silver VALUES ('silver') TABLESPACE SET ts2);
)
then direct connection to a shard can be established by specifying a super sharding key and sharding key, for example:
const connection = await oracledb.getConnection({
user : "hr",
password : mypw, // mypw contains the hr schema password
connectString : "localhost/orclpdb1",
superShardingKey: ["gold"]
shardingKey : ["SCOTT"],
});