Skip to content

Commit ba118e5

Browse files
committed
Allow SQL to be excluded from the statement cache
1 parent 84f0fdd commit ba118e5

File tree

9 files changed

+472
-65
lines changed

9 files changed

+472
-65
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
**This release is under development and information may be incomplete**
66

7+
- Added a
8+
[`keepInStmtCache`](https://oracle.github.io/node-oracledb/doc/api.html#propexeckeepinstmtcache)
9+
option to `execute()`, `executeMany()`, and `queryStream()` to control
10+
whether executed statements are retained in the Statement Cache.
11+
([Issue #182](https://github.com/oracle/node-oracledb/issues/182)).
12+
713
- Encapsulated the connection pool statistics in a [PoolStatistics
814
Class](https://oracle.github.io/node-oracledb/doc/api.html#poolstatisticsclass).
915
Added a

doc/api.md

Lines changed: 134 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,11 @@ For installation information, see the [Node-oracledb Installation Instructions][
194194
- 4.2.6.3.3 [`extendedMetaData`](#propexecextendedmetadata)
195195
- 4.2.6.3.4 [`fetchArraySize`](#propexecfetcharraysize)
196196
- 4.2.6.3.5 [`fetchInfo`](#propexecfetchinfo)
197-
- 4.2.6.3.6 [`maxRows`](#propexecmaxrows)
198-
- 4.2.6.3.7 [`outFormat`](#propexecoutformat)
199-
- 4.2.6.3.8 [`prefetchRows`](#propexecprefetchrows)
200-
- 4.2.6.3.9 [`resultSet`](#propexecresultset)
197+
- 4.2.6.3.6 [`keepInStmtCache`](#propexeckeepinstmtcache)
198+
- 4.2.6.3.7 [`maxRows`](#propexecmaxrows)
199+
- 4.2.6.3.8 [`outFormat`](#propexecoutformat)
200+
- 4.2.6.3.9 [`prefetchRows`](#propexecprefetchrows)
201+
- 4.2.6.3.10 [`resultSet`](#propexecresultset)
201202
- 4.2.6.4 [`execute()`: Callback Function](#executecallback)
202203
- 4.2.6.4.1 [`implicitResults`](#execimplicitresults)
203204
- 4.2.6.4.2 [`lastRowid`](#execlastrowid)
@@ -216,6 +217,7 @@ For installation information, see the [Node-oracledb Installation Instructions][
216217
- 4.2.7.3.3 [`bindDefs`](#executemanyoptbinddefs)
217218
- [`dir`](#executemanyoptbinddefs), [`maxSize`](#executemanyoptbinddefs), [`type`](#executemanyoptbinddefs)
218219
- 4.2.7.3.4 [`dmlRowCounts`](#executemanyoptdmlrowcounts)
220+
- 4.2.7.3.5 [`keepInStmtCache`](#executemanyoptkeepinstmtcache)
219221
- 4.2.7.4 [`executeMany()`: Callback Function](#executemanycallback)
220222
- 4.2.7.4.1 [`batchErrors`](#execmanybatcherrors)
221223
- 4.2.7.4.2 [`dmlRowCounts`](#execmanydmlrowscounts)
@@ -3815,23 +3817,39 @@ by Node.js and V8 memory restrictions.
38153817
See [Query Result Type Mapping](#typemap) for more information on query type
38163818
mapping.
38173819

3818-
###### <a name="propexecmaxrows"></a> 4.2.6.3.6 `maxRows`
3820+
###### <a name="propexeckeepinstmtcache"></a> 4.2.6.3.6 `keepInStmtCache`
3821+
3822+
```
3823+
Boolean keepInStmtCache
3824+
```
3825+
3826+
When `keepInStmtCache` is *true*, and statement caching is enabled, then the
3827+
statement will be added to the cache if it is not already present. This helps
3828+
the performance of re-executed statements. See [Statement
3829+
Caching](#stmtcache).
3830+
3831+
The default value is *true*.
3832+
3833+
This attribute was added in node-oracledb 5.3. In earlier versions, statements
3834+
were always added to the statement cache, if caching was enabled.
3835+
3836+
###### <a name="propexecmaxrows"></a> 4.2.6.3.7 `maxRows`
38193837

38203838
```
38213839
Number maxRows
38223840
```
38233841

38243842
Overrides [`oracledb.maxRows`](#propdbmaxrows).
38253843

3826-
###### <a name="propexecoutformat"></a> 4.2.6.3.7 `outFormat`
3844+
###### <a name="propexecoutformat"></a> 4.2.6.3.8 `outFormat`
38273845

38283846
```
38293847
Number outFormat
38303848
```
38313849

38323850
Overrides [`oracledb.outFormat`](#propdboutformat).
38333851

3834-
###### <a name="propexecprefetchrows"></a> 4.2.6.3.8 `prefetchRows`
3852+
###### <a name="propexecprefetchrows"></a> 4.2.6.3.9 `prefetchRows`
38353853

38363854
```
38373855
Number prefetchRows
@@ -3841,7 +3859,7 @@ Overrides [`oracledb.prefetchRows`](#propdbprefetchrows).
38413859

38423860
This attribute is not used in node-oracledb version 2, 3 or 4.
38433861

3844-
###### <a name="propexecresultset"></a> 4.2.6.3.9 `resultSet`
3862+
###### <a name="propexecresultset"></a> 4.2.6.3.10 `resultSet`
38453863

38463864
```
38473865
Boolean resultSet
@@ -4174,6 +4192,22 @@ The default value is *false*.
41744192
This feature works when node-oracledb is using version 12, or later, of the
41754193
Oracle client library, and using Oracle Database 12, or later.
41764194

4195+
###### <a name="executemanyoptkeepinstmtcache"></a> 4.2.7.3.5 `keepInStmtCache`
4196+
4197+
```
4198+
Boolean keepInStmtCache
4199+
```
4200+
4201+
When `keepInStmtCache` is *true*, and statement caching is enabled, then the
4202+
statement will be added to the cache if it is not already present. This helps
4203+
the performance of re-executed statements. See [Statement
4204+
Caching](#stmtcache).
4205+
4206+
The default value is *true*.
4207+
4208+
This attribute was added in node-oracledb 5.3. In earlier versions, statements
4209+
were always added to the statement cache, if caching was enabled.
4210+
41774211
##### <a name="executemanycallback"></a> 4.2.7.4 `executeMany()`: Callback Function
41784212

41794213
```
@@ -4406,6 +4440,11 @@ as `oracledb.STMT_TYPE_UNKNOWN`. DDL statements are not parsed, so
44064440
syntax errors in them will not be reported. The direction and types
44074441
of bind variables cannot be determined.
44084442

4443+
The statement is always added to the [statement cache](#stmtcache). This
4444+
improves performance if `getStatementInfo()` is repeatedly called with the same
4445+
statement, or if the statement is used in an [`execute()`](#execute) call or
4446+
similar.
4447+
44094448
This method was added in node-oracledb 2.2.
44104449

44114450
##### Parameters
@@ -13965,7 +14004,7 @@ column names. Make sure to use a Allow List of names to avoid SQL
1396514004
Injection security risks.
1396614005

1396714006
Each final SQL statement will obviously be distinct, and will use a
13968-
slot in the [statement cache](#stmtcache).
14007+
slot in the [statement cache](#stmtcache) by default.
1396914008

1397014009
It is possible to bind column names used in an ORDER BY:
1397114010

@@ -16992,63 +17031,44 @@ console.log('Round-trips required: ' + (after - before)); // 1 round-trip
1699217031

1699317032
### <a name="stmtcache"></a> 32.3. Statement Caching
1699417033

16995-
Node-oracledb's [`execute()`](#execute) and
16996-
[`queryStream()`](#querystream) methods use the [Oracle Call Interface
16997-
statement cache][61] to make re-execution of statements efficient.
16998-
This cache removes the need for the separate 'prepare' or 'parse'
16999-
methods which are sometimes seen in other Oracle APIs: there is no
17000-
separate method in node-oracledb.
17001-
17002-
Each non-pooled connection and each session in the connection pool has
17003-
its own cache of statements with a default size of 30. Statement
17004-
caching lets cursors be used without re-parsing the statement.
17005-
Statement caching also reduces meta data transfer costs between
17006-
node-oracledb and the database. Performance and scalability are
17007-
improved.
17034+
Node-oracledb's [`execute()`](#execute), [`executeMany()`](#executemany),
17035+
[`getStatementInfo()`](#getstmtinfo), and [`queryStream()`](#querystream)
17036+
methods use the [Oracle Call Interface statement cache][61] to make
17037+
re-execution of statements efficient. Statement caching lets cursors be used
17038+
without re-parsing the statement. Each cached statement will retain its
17039+
cursor. Statement caching also reduces meta data transfer costs between
17040+
node-oracledb and the database. Performance and scalability are improved.
1700817041

17009-
In general, set the statement cache to the size of the working set of
17010-
statements being executed by the application.
17011-
17012-
Statement caching can be disabled by setting the size to 0. Disabling
17013-
the cache may be beneficial when the quantity or order of statements
17014-
causes cache entries to be flushed before they get a chance to be
17015-
reused. For example if there are more distinct statements than cache
17016-
slots, and the order of statement execution causes older statements to
17017-
be flushed from the cache before the statements are re-executed.
17018-
17019-
Disabling the statement cache may also be useful in test and development
17020-
environments where database schema objects are being recreated, or where
17021-
identical query text is used with different `fetchAsString` or `fetchInfo` data
17022-
types. Doing so can lead to the statement cache becoming invalid, and the
17023-
application receiving various errors, for example *ORA-3106*. After a
17024-
statement execution error is returned to the application, node-oracledb drops
17025-
that statement from the cache. This allows subsequent re-executions of the
17026-
statement using that connection to succeed.
17027-
17028-
The statement cache size can be set globally with [stmtCacheSize](#propdbstmtcachesize):
17042+
Each non-pooled connection and each session in the connection pool has its own
17043+
cache of statements with a default size of 30. The cache key is the statement
17044+
string. This means a single cache entry can be reused when a statement is
17045+
re-executed with different bind variable values.
17046+
17047+
The statement cache removes the need for the separate 'prepare' or 'parse'
17048+
methods which are sometimes seen in other Oracle APIs: there is no separate
17049+
method in node-oracledb.
17050+
17051+
##### Setting the Statement Cache
17052+
17053+
The statement cache size can be set globally with
17054+
[oracledb.stmtCacheSize](#propdbstmtcachesize):
1702917055

1703017056
```javascript
1703117057
oracledb.stmtCacheSize = 40;
1703217058
```
1703317059

17034-
The value can be overridden in an `oracledb.getConnection()` call:
17060+
The value can be overridden in an `oracledb.getConnection()` call, or when
17061+
creating a pool with [`oracledb.createPool()`](#createpool). For example:
1703517062

1703617063
```javascript
17037-
const mypw = ... // the hr schema password
17038-
17039-
const connection = await oracledb.getConnection(
17040-
{
17041-
user : "hr",
17042-
password : mypw,
17043-
connectString : "localhost/XEPDB1",
17044-
stmtCacheSize : 40
17045-
}
17046-
);
17064+
await oracledb.createPool({
17065+
user : "hr",
17066+
password : mypw, // mypw contains the hr schema password
17067+
connectString : "localhost/XEPDB1",
17068+
stmtCacheSize : 50
17069+
});
1704717070
```
1704817071

17049-
The value can also be overridden in the `poolAttrs` parameter to
17050-
the [`createPool()`](#createpool) method.
17051-
1705217072
When using Oracle Client 21 (or later), changing the cache size with
1705317073
[`pool.reconfigure()`](#poolreconfigure) does not immediately affect
1705417074
connections previously acquired and currently in use. When those connections
@@ -17058,18 +17078,68 @@ statement cache size has no effect on connections that already exist in the
1705817078
pool but will affect new connections that are subsequently created, for example
1705917079
when the pool grows.
1706017080

17061-
With Oracle Database 12c, or later, the statement cache size can be
17081+
##### Tuning the Statement Cache
17082+
17083+
In general, set the statement cache to the size of the working set of
17084+
statements being executed by the application. [SODA](#sodaoverview) internally
17085+
makes SQL calls, so tuning the cache is also beneficial for SODA applications.
17086+
17087+
With Oracle Client Libraries 12c, or later, the statement cache size can be
1706217088
automatically tuned with the [Oracle Client Configuration](#oraaccess)
1706317089
`oraaccess.xml` file.
1706417090

17065-
To manually tune the statement cache size, monitor general application load and
17066-
the [Automatic Workload Repository][62] (AWR) "bytes sent via SQL*Net to
17067-
client" values. The latter statistic should benefit from not shipping
17068-
statement metadata to node-oracledb. Adjust the statement cache size to your
17069-
satisfaction.
17091+
For manual tuning use views like `V$SYSSTAT`:
17092+
17093+
```sql
17094+
SELECT value FROM V$SYSSTAT WHERE name = 'parse count (total)'
17095+
```
17096+
17097+
Find the value before and after running application load to give the
17098+
number of statement parses during the load test. Alter the statement cache
17099+
size and repeat the test until you find a minimal number of parses.
17100+
17101+
If you have [Automatic Workload Repository][62] (AWR) reports you can monitor
17102+
general application load and the "bytes sent via SQL*Net to client" values.
17103+
The latter statistic should benefit from not shipping statement metadata to
17104+
node-oracledb. Adjust the statement cache size and re-run the test to find the
17105+
best cache size.
17106+
17107+
##### Disabling the Statement Cache
1707017108

17071-
[SODA](#sodaoverview) internally makes SQL calls, so tuning the statement cache
17072-
is also beneficial for SODA applications.
17109+
Individual statements can be excluded from the statement cache by setting the
17110+
attribute [`keepInStmtCache`](#propexeckeepinstmtcache) to *false*. This will
17111+
prevent a rarely executed statement from flushing a potential more frequently
17112+
executed one from a full cache. For example, if a statement will only ever be
17113+
executed once:
17114+
17115+
```javascript
17116+
result = await connection.execute(
17117+
`SELECT v FROM t WHERE k = 123`,
17118+
[],
17119+
{ keepInStmtCache: false }
17120+
);
17121+
```
17122+
17123+
Statement caching can be disabled completely by setting the cache size to 0:
17124+
17125+
```javascript
17126+
oracledb.stmtCacheSize = 0;
17127+
```
17128+
17129+
Disabling the cache may be beneficial when the quantity or order of statements
17130+
causes cache entries to be flushed before they get a chance to be reused. For
17131+
example, if there are more distinct statements than cache slots and the order
17132+
of statement execution causes older statements to be flushed from the cache
17133+
before they are re-executed.
17134+
17135+
Disabling the statement cache may also be helpful in test and development
17136+
environments. The statement cache can become invalid if connections remain
17137+
open and database schema objects are recreated. This can also happen when a
17138+
connection uses identical query text with different `fetchAsString` or
17139+
`fetchInfo` data types. Applications can receive errors such as *ORA-3106*.
17140+
After a statement execution error is returned once to the application,
17141+
node-oracledb automatically drops that statement from the cache. This lets
17142+
subsequent re-executions of the statement on that connection to succeed.
1707317143

1707417144
### <a name="clientresultcache"></a> 32.4 Client Result Caching (CRC)
1707517145

src/njsConnection.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,13 @@ static bool njsConnection_executeAsync(njsBaton *baton)
560560
return njsBaton_setErrorDPI(baton);
561561
}
562562

563+
// mark statement for removal from the cache, if applicable
564+
if (!baton->keepInStmtCache) {
565+
if (dpiStmt_deleteFromCache(baton->dpiStmtHandle) < 0) {
566+
return njsBaton_setErrorDPI(baton);
567+
}
568+
}
569+
563570
// execute statement
564571
mode = (baton->autoCommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
565572
DPI_MODE_EXEC_DEFAULT;
@@ -712,6 +719,7 @@ static bool njsConnection_executeProcessArgs(njsBaton *baton,
712719
baton->outFormat = baton->oracleDb->outFormat;
713720
baton->extendedMetaData = baton->oracleDb->extendedMetaData;
714721
baton->prefetchRows = baton->oracleDb->prefetchRows;
722+
baton->keepInStmtCache = true;
715723

716724
if (!njsUtils_copyArray(env, baton->oracleDb->fetchAsBufferTypes,
717725
baton->oracleDb->numFetchAsBufferTypes, sizeof(uint32_t),
@@ -764,6 +772,9 @@ static bool njsConnection_executeProcessArgs(njsBaton *baton,
764772
if (!njsBaton_getFetchInfoFromArg(baton, env, args, 2, "fetchInfo",
765773
&baton->numFetchInfo, &baton->fetchInfo, NULL))
766774
return false;
775+
if (!njsBaton_getBoolFromArg(baton, env, args, 2, "keepInStmtCache",
776+
&baton->keepInStmtCache, NULL))
777+
return false;
767778

768779
// validate binds in second argument; these must be done after options are
769780
// processed as those options may influence how bind variables are created
@@ -815,6 +826,13 @@ static bool njsConnection_executeManyAsync(njsBaton *baton)
815826
if (!njsConnection_prepareAndBind(conn, baton))
816827
return false;
817828

829+
// mark statement for removal from the cache, if applicable
830+
if (!baton->keepInStmtCache) {
831+
if (dpiStmt_deleteFromCache(baton->dpiStmtHandle) < 0) {
832+
return njsBaton_setErrorDPI(baton);
833+
}
834+
}
835+
818836
// execute statement
819837
mode = (baton->autoCommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
820838
DPI_MODE_EXEC_DEFAULT;
@@ -926,6 +944,7 @@ static bool njsConnection_executeManyProcessArgs(njsBaton *baton,
926944
{
927945
// setup defaults and set JavaScript values to simplify creation of
928946
// returned objects
947+
baton->keepInStmtCache = true;
929948
baton->autoCommit = baton->oracleDb->autoCommit;
930949
if (!njsBaton_setJsValues(baton, env))
931950
return false;
@@ -948,6 +967,9 @@ static bool njsConnection_executeManyProcessArgs(njsBaton *baton,
948967
if (!njsBaton_getBoolFromArg(baton, env, args, 2, "dmlRowCounts",
949968
&baton->dmlRowCounts, NULL))
950969
return false;
970+
if (!njsBaton_getBoolFromArg(baton, env, args, 2, "keepInStmtCache",
971+
&baton->keepInStmtCache, NULL))
972+
return false;
951973

952974
return true;
953975
}

src/njsModule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ struct njsBaton {
497497
bool clientInitiated;
498498
bool dbObjectAsPojo;
499499
bool sodaMetadataCache;
500+
bool keepInStmtCache;
500501

501502
// LOB buffer (requires free only if string was used)
502503
uint64_t bufferSize;

0 commit comments

Comments
 (0)