@sap/cds 9.8.2 → 9.8.3
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 +6 -0
- package/lib/srv/protocols/hcql.js +1 -1
- package/libx/odata/middleware/batch.js +21 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Version 9.8.3 - 2026-03-12
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- OData batch parallel processing: Drain remaining queue when aborting
|
|
12
|
+
|
|
7
13
|
## Version 9.8.2 - 2026-03-10
|
|
8
14
|
|
|
9
15
|
### Fixed
|
|
@@ -47,7 +47,7 @@ class HCQLAdapter extends require('./http') {
|
|
|
47
47
|
|
|
48
48
|
// Error formatting
|
|
49
49
|
router.use ((err, req, res, next) => {
|
|
50
|
-
err.$response = e => ({ errors: [ { ...e, message: e.message } ] })
|
|
50
|
+
if (typeof err === 'object') err.$response = e => ({ errors: [ { ...e, message: e.message } ] })
|
|
51
51
|
next(err)
|
|
52
52
|
})
|
|
53
53
|
|
|
@@ -487,42 +487,43 @@ const _processBatch = async (srv, router, req, res, next, body, ct, boundary) =>
|
|
|
487
487
|
|
|
488
488
|
// ensure all subrequests run in this tx
|
|
489
489
|
// (if first subrequest fails without really opening the tx, the rest are executed in a "dangling tx")
|
|
490
|
-
return Promise.allSettled(subrequests)
|
|
491
|
-
.
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// throw first error and call srv.on('error') for the others
|
|
501
|
-
const first = failed.shift()
|
|
502
|
-
if (srv.handlers._error?.length)
|
|
503
|
-
for (const other of failed)
|
|
504
|
-
for (const each of srv.handlers._error) each.handler.call(srv, other.reason, cds.context)
|
|
505
|
-
throw first.reason
|
|
506
|
-
})
|
|
490
|
+
return Promise.allSettled(subrequests).then(ress => {
|
|
491
|
+
const failed = ress.filter(({ status }) => status === 'rejected')
|
|
492
|
+
if (!failed.length) return
|
|
493
|
+
// throw first error and call srv.on('error') for the others
|
|
494
|
+
const first = failed.shift()
|
|
495
|
+
if (srv.handlers._error?.length)
|
|
496
|
+
for (const other of failed)
|
|
497
|
+
for (const each of srv.handlers._error) each.handler.call(srv, other.reason, cds.context)
|
|
498
|
+
throw first.reason
|
|
499
|
+
})
|
|
507
500
|
})
|
|
508
501
|
.catch(err => {
|
|
509
502
|
responses._has_failure = true
|
|
510
503
|
|
|
511
504
|
// abort batch on first failure with odata.continue-on-error: false
|
|
512
|
-
if (!continue_on_error)
|
|
505
|
+
if (!continue_on_error) {
|
|
506
|
+
_continue = false
|
|
507
|
+
while (queue.length) queue.shift()()
|
|
508
|
+
}
|
|
513
509
|
|
|
514
510
|
if (!responses.some(r => r.status === 'fail')) {
|
|
515
511
|
// here, the commit was rejected even though all requests were successful (e.g., by custom handler or db consistency check)
|
|
516
512
|
_replaceResponsesWithCommitErrors(err, responses, ids)
|
|
517
513
|
}
|
|
518
514
|
})
|
|
519
|
-
.finally(() => {
|
|
515
|
+
.finally(async () => {
|
|
520
516
|
// trigger next in queue
|
|
521
517
|
if (queue.length) queue.shift()()
|
|
522
518
|
|
|
523
519
|
// late error serialization
|
|
524
520
|
if (responses._has_failure) _serializeErrors(responses)
|
|
525
521
|
|
|
522
|
+
// wait for all previous atomicity groups (ignoring errors via allSettled) for odata v2
|
|
523
|
+
const prevs = []
|
|
524
|
+
for (let i = 0; i < agIndex; i++) prevs.push(promises[i])
|
|
525
|
+
await Promise.allSettled(prevs)
|
|
526
|
+
|
|
526
527
|
if (isJson) _writeResponseJson(responses, res)
|
|
527
528
|
else _writeResponseMultipart(responses, res, boundary)
|
|
528
529
|
})
|
|
@@ -540,7 +541,7 @@ const _processBatch = async (srv, router, req, res, next, body, ct, boundary) =>
|
|
|
540
541
|
// trigger first max_parallel in queue
|
|
541
542
|
for (let i = 0; i < max_parallel; i++) if (queue.length) queue.shift()()
|
|
542
543
|
|
|
543
|
-
await Promise.
|
|
544
|
+
await Promise.allSettled(promises)
|
|
544
545
|
|
|
545
546
|
res.write(isJson ? ']}' : `--${boundary}--${CRLF}`)
|
|
546
547
|
res.end()
|