@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 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 createPool = (factory, config) => {
5
- if (cds.requires.db?.pool?.builtin) return new Pool(factory, config)
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 && !cds.requires.db?.pool?.builtin ? TrackedConnectionPool : ConnectionPool
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 = 'pending'
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 === 'pending') this._resolve(v) }
79
- reject (e) { if (this.state === 'pending') this._reject(e) }
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
- for (let i = 0; i < this.options.min - this.size; i++) this.#createResource()
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._available).map(resource => this.#destroy(resource)))
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
- await this.factory.destroy(resource.obj)
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 resourcesToEvict = Array.from(this._available)
272
- .slice(0, numTestsPerEvictionRun)
273
- .filter(resource => {
274
- const idleTime = Date.now() - resource.lastIdleTime
275
- const softEvict = softIdleTimeoutMillis > 0 && softIdleTimeoutMillis < idleTime && min < this._available.size
276
- return softEvict || idleTimeoutMillis < idleTime
277
- })
278
- await Promise.all(resourcesToEvict.map(resource => this.#destroy(resource)))
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/db-service",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "CDS base database service",
5
5
  "homepage": "https://github.com/cap-js/cds-dbs/tree/main/db-service#cds-base-database-service",
6
6
  "repository": {