Modern enterprise applications are rarely built using a single programming language. A cloud-native application might use Python for AI and analytics, Node.js for REST APIs and C for latency-sensitive services. Despite the diversity of languages, the database-access goals are familiar: efficient connection pooling, intelligent routing, high availability, and predictable scalability.
Oracle Globally Distributed Database (GDD) enables applications to scale horizontally by distributing data across shards while presenting a single logical database to the application. To make these capabilities available consistently across languages, Oracle provides OCI-based (Oracle Call Interface) drivers that expose the same distributed database features through language-native APIs.
In this article, we’ll explore how Python, Node.js and C applications use the same OCI foundation to build scalable, shard-aware applications.
Key Takeaways
- Python and Node.js in Thick mode and C through OCI(Oracle Call Interface) give developers a consistent core workflow for Oracle Globally Distributed Database: create or reuse sessions, supply shard keys when needed, and release connections predictably. The APIs and feature coverage remain language-specific.
- Shard-aware connection routing is consistent across languages. Applications acquire connections using sharding keys (and optionally super sharding keys), allowing Oracle Globally Distributed Database to automatically route requests to the appropriate shard.
- Thoughtful pool sizing and high-availability configuration are essential in multi-shard systems: they support session reuse, help avoid uneven connection distribution, and can improve recovery behavior when the required services and client configuration are in place.

A Common Foundation
One of the strengths of Oracle’s language drivers is that they share a common implementation stack. Although the programming APIs differ, they ultimately leverage the same Oracle Call Interface (OCI) capabilities for connection management, sharding, and high availability. In Thick mode, the Python and Node.js drivers use Oracle Client libraries and Oracle Call Interface (OCI). C applications call OCI directly.
This means that developers can choose the programming language best suited to their workload while relying on consistent database behavior.
Why Thick Mode?
Thick mode is the Python and Node.js deployment mode that uses Oracle Client libraries and the Oracle Call Interface (OCI). It gives those applications access to the OCI-based connection behavior that C applications use directly.
- High-performance session pooling enables session reuse, reducing connection-creation overhead under load and helping applications manage demand across shards.
- Sharding and super sharding keys let the client request a connection for the data-owning shard, without embedding physical shard locations in application logic.
- Oracle Net and the native-client or provider capabilities supply the connection behavior needed for enterprise deployments, including the routing information used to establish the right connection.
- High availability is configured separately from driver selection. FAN, Application Continuity or Transparent Application Continuity (TAC), and Transparent Application Failover (TAF) can improve failure response and continuity during planned or unplanned events, but their availability and defaults depend on the driver or provider type, client version, database service, and pool or connection configuration.
Initializing Oracle Client enables Thick mode for Python and Node.js only. It does not, by itself, enable high-availability behavior. The same principle applies across C/OCI: configure and test the relevant services, client settings, and pool behavior for the driver in use.
Creating a Scalable Session Pool
Connection pooling is the first step toward scalable access. Instead of creating a new database connection for every request, applications reuse existing sessions, lowering latency and improving throughput. In a multi-shard system, pool configuration also matters because demand may not be evenly distributed across shards. Monitor pool utilization and shard-level workload patterns, then tune capacity for the traffic you actually observe.
The pool sizes below are illustrative only, not recommended defaults. Size pools using expected concurrency, request duration, database capacity, shard distribution, and service-level objectives.
Python
import oracledb
oracledb.init_oracle_client()
pool = oracledb.create_pool(
user=user,
password=password,
dsn=connect_string,
min=5,
max=50,
increment=5,
getmode=oracledb.POOL_GETMODE_WAIT
)
Node.js
const oracledb = require('oracledb');
oracledb.initOracleClient();
const pool = await oracledb.createPool({
user,
password,
connectString,
poolMin: 5,
poolMax: 50,
poolIncrement: 5
});
C
OCIEnvCreate(...);
OCIHandleAlloc(...);
OCISessionPoolCreate(
envhp,
errhp,
poolhp,
&poolName,
&poolNameLen,
connectString,
strlen(connectString),
minSessions,
maxSessions,
increment,
username,
strlen(username),
password,
strlen(password),
OCI_SPC_HOMOGENEOUS
);
Acquiring Shard-Aware Connections
The key benefit of shard-aware access is not that the application learns where shards live. It is that the client can direct work to the shard that owns the requested data. Supplying the sharding key and, where the data model uses one, the super sharding key helps avoid unnecessary data movement and keeps physical topology out of business logic. The following snippets are abbreviated examples. They omit credentials, error handling, variable declarations, pool shutdown, and full OCI environment and handle management. Production code should add those concerns and should release every acquired connection
Python
# The context manager releases the connection to the pool.
with pool.acquire(
supershardingkey=[region],
shardingkey=[customer_id]
) as connection:
with connection.cursor() as cursor:
cursor.execute(sql)
Node.js
let connection;
try {
connection = await pool.getConnection({
superShardingKey: [region],
shardingKey: [customerId]
});
await connection.execute(sql);
} finally {
if (connection) await connection.close(); // Return it to the pool
}
C
/* Create an authInfo handle, allocate key descriptors, add values,
attach both keys, then request the pooled session.
Error checks are omitted here. */
OCIAuthInfo *authInfo = NULL;
OCIShardingKey *shardKey = NULL, *superShardKey = NULL;
OCIHandleAlloc(envhp, (dvoid **)&authInfo,
OCI_HTYPE_AUTHINFO, 0, NULL);
OCIDescriptorAlloc(envhp, (dvoid **)&shardKey,
OCI_DTYPE_SHARDING_KEY, 0, NULL);
OCIDescriptorAlloc(envhp, (dvoid **)&superShardKey,
OCI_DTYPE_SHARDING_KEY, 0, NULL);
OCIShardingKeyColumnAdd(shardKey, errhp, &customerId,
sizeof(customerId), SQLT_INT, OCI_DEFAULT);
OCIShardingKeyColumnAdd(superShardKey, errhp, region,
strlen((char *)region), SQLT_CHR, OCI_DEFAULT);
OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO, shardKey, sizeof(shardKey),
OCI_ATTR_SHARDING_KEY, errhp);
OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO, superShardKey, sizeof(superShardKey),
OCI_ATTR_SUPER_SHARDING_KEY, errhp);
OCISessionGet(envhp, errhp, &svcHandle, authInfo,
poolName, poolNameLen, NULL, 0, NULL, NULL, NULL,
OCI_SESSGET_SPOOL);
/* Execute work on the routed shard, then release the session. */
OCISessionRelease(svcHandle, errhp, NULL, 0, OCI_DEFAULT);
OCIDescriptorFree(shardKey, OCI_DTYPE_SHARDING_KEY);
OCIDescriptorFree(superShardKey, OCI_DTYPE_SHARDING_KEY);
OCIHandleFree(authInfo, OCI_HTYPE_AUTHINFO);
Although the APIs differ, each performs the same sequence:
- Construct the sharding key.
- Request a pooled connection.
- Allow OCI to route the request to the correct shard.
Comparing the Drivers
The table helps an architect choose the API that matches the application language while preserving the same operating model.
| Capability | Python (Thick) | Node.js (Thick) | C |
| Client / driver | Oracle Client | Oracle Client | Direct OCI calls |
| Session pool | create_pool() | createPool() | OCISessionPoolCreate() |
| Shard-aware checkout | pool.acquire() | pool.getConnection() | OCISessionGet() |
| Sharding / super sharding keys | Language-native parameters | Language-native parameters | OCIShardingKey + OCIAttrSet() |
| Connection release | Context manager or close() | connection.close() | OCISessionRelease() |
The APIs are intentionally language-specific, while the core pooling and shard-aware routing workflow is consistent. Individual feature availability varies by driver mode, client version, database service, and configuration.
Conclusion
Oracle’s OCI-based drivers provide a unified foundation for building scalable applications across multiple programming languages. Whether applications are written in Python, Node.js or C, developers have access to the same core capabilities—session pooling, shard-aware routing, high availability, and efficient native connectivity—through familiar language APIs.
The result is a more consistent deployment and operating model across the application stack, with work directed to the data-owning shard and with less duplicated connection-management knowledge between language teams.
Frequently Asked Questions
Why should I use Thick mode instead of Thin mode?
Thin mode is suitable for many Oracle Database applications, but Thick mode uses Oracle Client libraries and the Oracle Call Interface (OCI). For Oracle Globally Distributed Database, it gives Python and Node.js applications access to OCI-based shard-aware connections and advanced connection management. Features such as Fast Application Notification (FAN), Application Continuity, Transparent Application Continuity (TAC), and Transparent Application Failover (TAF) require support from the relevant driver mode and version, database services, and client or pool configuration. Initializing Oracle Client enables Thick mode for Python and Node.js; it does not by itself enable high-availability behavior.
Why do I need sharding keys?
Sharding keys allow Oracle Globally Distributed Database to determine which shard contains the requested data. By supplying sharding keys when acquiring connections, applications are automatically routed to the appropriate shard, minimizing cross-shard communication and improving performance.
Do I need to know which shard my data resides on?
No. Applications simply provide the appropriate sharding key. Oracle Globally Distributed Database handles the routing transparently, allowing developers to focus on application logic rather than shard location.
Do all drivers have the same programming model and capabilities?
They share a consistent core workflow: configure a client and pool, request a connection with routing information when needed, perform work, and release the connection. The APIs are intentionally language-native, and availability of individual features can vary by driver mode, client version, database service, and configuration.
