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 then pool.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

  • A connect string

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:

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.

Table 4.1 Configuration Information Stored in Configuration Providers

Key

Description

Required or Optional

user

The database user name.

Optional

password

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:

  • type: The possible values of this required parameter are ocivault, azurevault, base64, and text.

  • value: The values of this required parameter dependent on the type parameter. The possible values are OCID of the secret when type is ocivault, Azure Key Vault URI when type is azurevault, and Base64 Encoded password when type is base64.

  • authentication: The possible values of this optional parameter are dependent on the configuration provider and include the authentication method and optional authentication 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

connect_descriptor

The database connect descriptor.

Required

wallet_location

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

config_time_to_live

The number of seconds that node-oracledb should keep the configuration information cached. See Caching Configuration Information.

Added in version 6.10.

Optional

njs

The node-oracledb specific properties. The properties that can be stored in OCI Object Storage include poolMin, poolMax, poolIncrement, poolTimeout, poolPingInterval, poolPingTimeout, stmtCacheSize, prefetchRows, and lobPrefetch.

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:

  1. Store the connection information in a JSON file on your local file system. See Connection Information for File Configuration Provider.

  2. Use a File configuration provider connection string URL in the connectString property 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.

Table 4.2 Connection String Parameters for File Configuration Provider

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:

  1. 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.

  2. Install the required OCI modules. See Install Modules for OCI Object Storage Configuration Provider.

  3. Load the ociobject plugin in your application using require('oracledb/plugins/configProviders/ociobject').

  4. Use an OCI Object Storage connection string URL in the connectString property 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.

Table 4.3 Connection String Parameters for OCI Object Storage

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:

  1. 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.

  2. Install the required OCI modules. See Install Modules for OCI Vault Provider.

  3. Load the ocivault plugin in your application using require('oracledb/plugins/configProviders/ocivault').

  4. Use an OCI Vault connection string URL in the connectString property 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.

Table 4.4 Connection String Parameters for OCI Vault

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:

  1. 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.

  2. Install the required Azure Application modules. See Install Modules for Azure App Configuration Provider.

  3. Load the azure plugin in your application using require('oracledb/plugins/configProviders/azure').

  4. Use an Azure App Configuration connection string URL in the connectString parameter 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.

Table 4.5 Connection String Parameters for Azure App Centralized Configuration

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:

  1. 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.

  2. Install the required Azure Application modules. See Install Modules for Azure Vault Configuration Provider.

  3. Load the azurevault plugin in your application using require('oracledb/plugins/configProviders/azurevault').

  4. Use an Azure Key Vault connection string URL in the connectString parameter 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.

Table 4.6 Connection String Parameters for Azure Key Vault

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() calls

  • the number of those currently established connections is less than the pool’s poolMax setting

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.poolAlias of 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:

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:

Table 4.7 PoolStatistics Class Attribute and Equivalent logStatistics() Label

Pool Statistics Class Attribute

logStatistics() Label

Description

thin

thin mode

Indicates whether the driver is in Thin or Thick mode.

gatheredDate

gathered at

The time the statistics were taken.

upTime

up time (milliseconds)

The number of milliseconds since this pool was created.

upTimeSinceReset

up time from last reset (milliseconds)

The number of milliseconds since the statistics were initialized or reset.

connectionRequests

connection requests

The number of getConnection() requests made to this pool.

requestsEnqueued

requests enqueued

The number of getConnection() requests that were added to this pool’s queue (waiting for the application to return an in-use connection to the pool) because every connection in this pool was already being used.

requestsDequeued

requests dequeued

The number of getConnection() requests that were dequeued when a connection in this pool became available for use.

failedRequests

requests failed

The number of getConnection() requests that failed due to an Oracle Database error. Does not include queueMax or queueTimeout errors.

rejectedRequests

requests exceeding queueMax

The number of getConnection() requests rejected because the number of connections in the pool queue exceeded the queueMax limit.

requestTimeouts

requests exceeding queueTimeout

The number of queued getConnection() requests that were timed out from the pool queue because they exceeded the queueTimeout time.

currentQueueLength

current queue length

The current number of getConnection() requests that are waiting in the pool queue.

maximumQueueLength

maximum queue length

The maximum number of getConnection() requests that were ever waiting in the pool queue at one time.

timeInQueue

sum of time in queue (milliseconds)

The sum of the time (milliseconds) that dequeued requests spent in the pool queue.

minimumTimeInQueue

minimum time in queue (milliseconds)

The minimum time (milliseconds) that any dequeued request spent in the pool queue.

maximumTimeInQueue

maximum time in queue (milliseconds)

The maximum time (milliseconds) that any dequeued request spent in the pool queue.

averageTimeInQueue

average time in queue (milliseconds)

The average time (milliseconds) that dequeued requests spent in the pool queue.

connectionsInUse

pool connections in use

The number of connections from this pool that getConnection() returned successfully to the application and have not yet been released back to the pool.

connectionsOpen

pool connections open

The number of idle or in-use connections to the database that the pool is currently managing.

connectString

connectString

The connection string that is used to connect to the Oracle Database instance.

edition

edition

The edition name used.

events

events

Denotes whether the Oracle Client events mode is enabled or not.

externalAuth

externalAuth

Denotes whether connections are established using external authentication or not.

homogeneous

homogeneous

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).

poolAlias

poolAlias

The alias of this pool in the connection pool cache.

poolIncrement

poolIncrement

The number of connections that are opened whenever a connection request exceeds the number of currently open connections.

poolMax

poolMax

The maximum number of connections that can be open in the connection pool.

poolMaxPerShard

poolMaxPerShard

The maximum number of connections in the pool that can be used for any given shard in a sharded database.

poolMin

poolMin

The minimum number of connections a connection pool maintains, even when there is no activity to the target database.

poolPingInterval

poolPingInterval (seconds)

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.

poolPingTimeout

poolPingTimeout (milliseconds)

The number of milliseconds that a connection should wait for a response from connection.ping().

poolTimeout

poolTimeout (seconds)

The time (in seconds) after which the pool terminates idle connections (unused in the pool).

maxLifetimeSession

maxLifetimeSession (seconds)

The time (in seconds) that a pooled connection can exist in a pool after first being created.

queueMax

queueMax

The maximum number of pending pool.getConnection() calls that can be queued.

queueTimeout

queueTimeout (milliseconds)

The time (in milliseconds) that a connection request should wait in the queue before the request is terminated.

sessionCallback

sessionCallback

The Node.js or PL/SQL function that is invoked by pool.getConnection() when the connection is brand new.

sodaMetaDataCache

sodaMetaDataCache

Determines whether the pool has a metadata cache enabled for SODA collection access.

stmtCacheSize

stmtCacheSize

The number of statements to be cached in the statement cache of each connection.

user

user

The database username for connections in the pool.

threadPoolSize

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:

Table 4.8 poolPingInterval Value

poolPingInterval Value

Behavior of a Pool getConnection() Call

n < 0

Never checks for connection validity

n = 0

Always checks for connection validity

n > 0

Checks validity if the connection has been idle in the pool (not “checked out” to the application by getConnection()) for at least n seconds

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.

Table 4.9 poolPingTimeout Value

poolPingTimeout Value

Behavior of a Pool getConnection() Call

n < 0

Returns the error NJS-007: invalid value for "poolPingTimeout" in parameter 1 if the poolPingTimeout property in oracledb.createPool() is set to a negative value.

Returns the error NJS-004: invalid value for property "poolPingTimeout" if oracledb.poolPingTimeout is set to a negative value.

n = 0

Waits until connection.ping() succeeds with a response or fails with an error.

n > 0

Waits for connection.ping() to respond by n milliseconds.

If ping() does not respond by n milliseconds, then the connection is forcefully closed.

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:

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:

  1. Enable DRCP in the database.

  2. Configure the application to use DRCP pooled servers.

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 connectString property (or its alias connectionString) of oracledb.createPool() or oracledb.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() or connection.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

    • Run queries that fetch LOB and JSON data

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:

  1. Enable DRCP in the database. For example in SQL*Plus:

    SQL> EXECUTE DBMS_CONNECTION_POOL.START_POOL();
    
  2. Specify the server type as pooled and also set the POOL_BOUNDARY attribute to either STATEMENT or TRANSACTION in:

    • The connectString property of oracledb.getConnection() or oracledb.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_DATA section 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_BOUNDARY attribute to TRANSACTION or STATEMENT but does not set the SERVER=POOLED attribute in the connection string.

    If you specify an invalid POOL_BOUNDARY in 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
    
  3. Set the connection class in:

    • The connectString property of oracledb.getConnection() or oracledb.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_DATA section 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=NEW to 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:

  1. 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.

  2. The underlying network connection between the database and node-oracledb Thin mode network handling code or Oracle Client libraries.

  3. 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:

  1. 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>;
    
  2. 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 the COMMIT_OUTCOME parameter 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;
    /
    
  3. 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.isRecoverable to confirm the error is recoverable. If not, do not proceed with Transaction Guard.

  • Use the connection attribute connection.ltxid to 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:

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.callTimeout which 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.ora file. Or you can use the connection.callTimeout attribute 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 a sqlnet.ora file.

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 callTimeout milliseconds, 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 callTimeout milliseconds, then no timeout will occur, even if the sum of all round-trip calls exceeds callTimeout.

  • 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:

  1. Click the Edit link next to Access Control List to update the Access Control List (ACL).

  2. 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.

  3. Navigate back to the ADB instance details page and click the Edit link next to Mutual TLS (mTLS) Authentication.

  4. 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:

  1. Navigate to the ADB instance details page on the Cloud console and click Database Connection at the top of the page.

  2. In the Database Connection dialog box that is displayed, select TLS from the Connection Strings drop-down list.

  3. 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:

  1. Navigate to the ADB instance details page on the Oracle Cloud console and click Database connection at the top of the page.

  2. 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:

  1. Move the three files to the network/admin directory 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 connectString parameter to the desired TNS Alias from the tnsnames.ora file. For example, to connect as the ADMIN user using the cjdb1_high network service name:

    connection = await oracledb.getConnection({
        user: "admin",
        password: mypw, // mypw contains the admin schema password
        connectString: "cjdb1_high"
    });
    
  2. Alternatively, move the three files to any accessible directory, for example /opt/OracleCloud/MYDB.

    Then edit sqlnet.ora and change the wallet location directory to the directory containing the cwallet.sso file. For example:

    WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/opt/OracleCloud/MYDB")))
    SSL_SERVER_DN_MATCH=yes
    

    Since the tnsnames.ora and sqlnet.ora files are not in the default location, your application needs to indicate where they are, either with the configDir parameter to initOracleClient(), or by using the TNS_ADMIN environment variable. See Optional Oracle Net Configuration. (Neither of these settings are needed, and you do not need to edit sqlnet.ora, if you have put all the files in the network/admin directory.)

    For example, to connect as the ADMIN user using the cjdb1_high TNS 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 httpsProxy and httpsProxyPort can 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_PROXY and HTTPS_PROXY_PORT parameters 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"],
});