@cap-js/db-service 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/lib/common/generic-pool.js +35 -23
- package/lib/cqn2sql.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [2.1.2](https://github.com/cap-js/cds-dbs/compare/db-service-v2.1.1...db-service-v2.1.2) (2025-06-12)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
* Hierarchy View support for ancestors/descendants requests ([#1241](https://github.com/cap-js/cds-dbs/issues/1241)) ([1ccb8b7](https://github.com/cap-js/cds-dbs/commit/1ccb8b7ded50f77de1d71d79b0c4b2040ee6a4f1))
|
|
13
|
+
|
|
14
|
+
## [2.1.1](https://github.com/cap-js/cds-dbs/compare/db-service-v2.1.0...db-service-v2.1.1) (2025-06-06)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
* Better opt in flag for builtin generic pool ([#1234](https://github.com/cap-js/cds-dbs/issues/1234)) ([98282ff](https://github.com/cap-js/cds-dbs/commit/98282ffd86cb143ebe9000f75b4a51156f6c3539))
|
|
20
|
+
* miscellaneous fixes for builtin pool ([#1235](https://github.com/cap-js/cds-dbs/issues/1235)) ([c657a0b](https://github.com/cap-js/cds-dbs/commit/c657a0ba2292fd429adb1b69aa9ead3eec9d5be0))
|
|
21
|
+
|
|
7
22
|
## [2.1.0](https://github.com/cap-js/cds-dbs/compare/db-service-v2.0.1...db-service-v2.1.0) (2025-06-04)
|
|
8
23
|
|
|
9
24
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
const cds = require('@sap/cds')
|
|
2
2
|
const LOG = cds.log('db')
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
return require('generic-pool').createPool(factory, config)
|
|
7
|
-
}
|
|
4
|
+
const use_new_pool = cds.requires.db?.pool?.builtin || cds.env.features.pool === 'builtin'
|
|
5
|
+
const createPool = use_new_pool ? (...args) => new Pool(...args) : require('generic-pool').createPool
|
|
8
6
|
|
|
9
7
|
function ConnectionPool (factory, tenant) {
|
|
10
8
|
let bound_factory = { __proto__: factory, create: factory.create.bind(null, tenant) }
|
|
@@ -35,7 +33,7 @@ function TrackedConnectionPool (factory, tenant) {
|
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
const DEBUG = /\bpool\b/.test(process.env.DEBUG)
|
|
38
|
-
module.exports = DEBUG && !
|
|
36
|
+
module.exports = DEBUG && !use_new_pool ? TrackedConnectionPool : ConnectionPool
|
|
39
37
|
|
|
40
38
|
// Drop-in replacement for https://github.com/coopernurse/node-pool
|
|
41
39
|
// TODO: fifo: true? relevant for our use case?
|
|
@@ -57,7 +55,7 @@ const RequestState = Object.freeze({
|
|
|
57
55
|
|
|
58
56
|
class Request {
|
|
59
57
|
constructor (ttl) {
|
|
60
|
-
this.state =
|
|
58
|
+
this.state = RequestState.PENDING
|
|
61
59
|
this.promise = new Promise((resolve, reject) => {
|
|
62
60
|
this._resolve = value => {
|
|
63
61
|
clearTimeout(this._timeout)
|
|
@@ -71,12 +69,12 @@ class Request {
|
|
|
71
69
|
}
|
|
72
70
|
if (typeof ttl === 'number' && ttl >= 0) {
|
|
73
71
|
const err = new Error(`Pool resource could not be acquired within ${ttl / 1000}s`)
|
|
74
|
-
this._timeout = setTimeout(() => this._reject(err), ttl)
|
|
72
|
+
this._timeout = setTimeout(() => this._reject(err), ttl).unref()
|
|
75
73
|
}
|
|
76
74
|
})
|
|
77
75
|
}
|
|
78
|
-
resolve (v) { if (this.state ===
|
|
79
|
-
reject (e) { if (this.state ===
|
|
76
|
+
resolve (v) { if (this.state === RequestState.PENDING) this._resolve(v) }
|
|
77
|
+
reject (e) { if (this.state === RequestState.PENDING) this._reject(e) }
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
class PooledResource {
|
|
@@ -133,7 +131,9 @@ constructor (factory, options = {}) {
|
|
|
133
131
|
this._queue = []
|
|
134
132
|
|
|
135
133
|
this.#scheduleEviction()
|
|
136
|
-
|
|
134
|
+
|
|
135
|
+
const initial = this.options.min - this.size
|
|
136
|
+
for (let i = 0; i < initial; i++) this.#createResource()
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
async acquire() {
|
|
@@ -173,7 +173,7 @@ constructor (factory, options = {}) {
|
|
|
173
173
|
|
|
174
174
|
async clear() {
|
|
175
175
|
await Promise.allSettled(Array.from(this._creates))
|
|
176
|
-
await Promise.allSettled(Array.from(this.
|
|
176
|
+
await Promise.allSettled(Array.from(this._all).map(resource => this.#destroy(resource)))
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
async #createResource() {
|
|
@@ -250,16 +250,23 @@ constructor (factory, options = {}) {
|
|
|
250
250
|
this._available.delete(resource)
|
|
251
251
|
this._loans.delete(resource.obj)
|
|
252
252
|
try {
|
|
253
|
-
|
|
253
|
+
const destroyPromise = Promise.resolve(this.factory.destroy(resource.obj))
|
|
254
|
+
const { destroyTimeoutMillis } = this.options
|
|
255
|
+
if (destroyTimeoutMillis && destroyTimeoutMillis > 0) {
|
|
256
|
+
const timeout = new Promise((_, reject) =>
|
|
257
|
+
setTimeout(() => reject(new Error(`Resource destruction timed out after ${destroyTimeoutMillis}ms`)), destroyTimeoutMillis).unref()
|
|
258
|
+
)
|
|
259
|
+
await Promise.race([destroyPromise, timeout])
|
|
260
|
+
} else {
|
|
261
|
+
await destroyPromise
|
|
262
|
+
}
|
|
254
263
|
} catch (e) {
|
|
255
264
|
LOG.error(e)
|
|
256
265
|
/* FIXME: We have to ignore errors here due to a TypeError in hdb */
|
|
257
266
|
/* This was also a problem with the old (generic-pool) implementation */
|
|
258
267
|
/* Root cause in hdb needs to be fixed */
|
|
259
268
|
} finally {
|
|
260
|
-
if (!this._draining && this.size < this.options.min)
|
|
261
|
-
await this.#createResource()
|
|
262
|
-
}
|
|
269
|
+
if (!this._draining && this.size < this.options.min) this.#createResource()
|
|
263
270
|
}
|
|
264
271
|
}
|
|
265
272
|
|
|
@@ -268,14 +275,19 @@ constructor (factory, options = {}) {
|
|
|
268
275
|
if (evictionRunIntervalMillis <= 0) return
|
|
269
276
|
this._scheduledEviction = setTimeout(async () => {
|
|
270
277
|
try {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
const evictionCandidates = Array.from(this._available).slice(0, numTestsPerEvictionRun)
|
|
279
|
+
const destructionPromises = []
|
|
280
|
+
for (const resource of evictionCandidates) {
|
|
281
|
+
const idleTime = Date.now() - resource.lastIdleTime
|
|
282
|
+
const softEvict = softIdleTimeoutMillis > 0 && softIdleTimeoutMillis < idleTime && this._all.size > min
|
|
283
|
+
const hardEvict = idleTimeoutMillis < idleTime
|
|
284
|
+
if (softEvict || hardEvict) {
|
|
285
|
+
if (this._available.delete(resource)) {
|
|
286
|
+
destructionPromises.push(this.#destroy(resource))
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
await Promise.all(destructionPromises)
|
|
279
291
|
} finally {
|
|
280
292
|
this.#scheduleEviction()
|
|
281
293
|
}
|
package/lib/cqn2sql.js
CHANGED
|
@@ -376,11 +376,12 @@ class CQN2SQLRenderer {
|
|
|
376
376
|
const expandedByOne = { list: [] } // DistanceTo(...,1)
|
|
377
377
|
const expandedByZero = { list: [] } // not DistanceTo(...,null)
|
|
378
378
|
let expandedFilter = []
|
|
379
|
+
// If a root where exists it should always be DistanceFromRoot otherwise when a recurse.where exists with only DistanceTo() calls
|
|
379
380
|
let distanceType = 'DistanceFromRoot'
|
|
380
381
|
let distanceVal
|
|
381
382
|
|
|
382
383
|
if (recurse.where) {
|
|
383
|
-
distanceType = 'Distance'
|
|
384
|
+
distanceType = where?.length ? 'DistanceFromRoot' : 'Distance'
|
|
384
385
|
if (recurse.where[0] === 'and') recurse.where = recurse.where.slice(1)
|
|
385
386
|
expandedFilter = [...recurse.where]
|
|
386
387
|
collectDistanceTo(expandedFilter)
|
|
@@ -463,7 +464,7 @@ class CQN2SQLRenderer {
|
|
|
463
464
|
},
|
|
464
465
|
where: expandedFilter.length ? expandedFilter : undefined,
|
|
465
466
|
orderBy: [{ ref: ['HIERARCHY_RANK'], sort: 'asc' }],
|
|
466
|
-
groupBy: [{ ref: ['NODE_ID'] },{ ref: ['PARENT_ID'] }, { ref: ['HIERARCHY_RANK'] }, { ref: ['HIERARCHY_LEVEL'] }, { ref: ['HIERARCHY_TREE_SIZE'] }, ...columnsOut.filter(c => c.ref)],
|
|
467
|
+
groupBy: [{ ref: ['NODE_ID'] }, { ref: ['PARENT_ID'] }, { ref: ['HIERARCHY_RANK'] }, { ref: ['HIERARCHY_LEVEL'] }, { ref: ['HIERARCHY_TREE_SIZE'] }, ...columnsOut.filter(c => c.ref)],
|
|
467
468
|
}
|
|
468
469
|
}
|
|
469
470
|
|
package/package.json
CHANGED