Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions node.js/messaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ module.exports = async srv => {
In _srv/own.js_ (CAP Messaging Services):
```js
module.exports = async srv => {
const externalService = await cds.connect.to('messaging')
// Connect directly to the messaging service; use fully qualified event names
const messaging = await cds.connect.to('messaging')
messaging.on('ExternalService.ExternalEvent', async msg => {
await srv.emit('OwnService.OwnEvent', msg.data)
})
Expand Down Expand Up @@ -243,10 +244,11 @@ const messaging = await cds.connect.to('messaging')

this.after(['CREATE', 'UPDATE', 'DELETE'], 'Reviews', async (_, req) => {
const { ID } = req.data
// average rating across all reviews for this book
const { rating } = await cds.run(
SELECT.one(['round(avg(rating),2) as rating'])
.from(Reviews)
.where({ ID }))
.where({ book_ID: ID }))

// send to a topic
await messaging.emit('my/custom/topic',
Expand Down Expand Up @@ -345,7 +347,7 @@ If you register at least one handler, a queue will automatically be created if n
You have the following configuration options:

- `queue`: An object containing the `name` property as the name of your queue, additional properties are described [in the SAP Business Accelerator Hub](https://hub.sap.com/api/SAPEventMeshDefaultManagementAPIs/path/putQueue).
- `amqp`: AQMP client options as described in the [`@sap/xb-msg-amqp-v100` documentation](https://www.npmjs.com/package/@sap/xb-msg-amqp-v100?activeTab=readme)
- `amqp`: AMQP client options as described in the [`@sap/xb-msg-amqp-v100` documentation](https://www.npmjs.com/package/@sap/xb-msg-amqp-v100?activeTab=readme)

If the queue name isn't specified, it's derived from `application_name` and the first four characters of `application_id` of your `VCAP_APPLICATION` environmental variable, as well as the `namespace` property of your SAP Event Mesh binding in `VCAP_SERVICES`: `{namespace}/{application_name}/{truncated_application_id}`.
This makes sure that every application has its own queue.
Expand Down Expand Up @@ -555,7 +557,13 @@ You can use local messaging to communicate inside one Node.js process. It's espe

`kind`: `composite-messaging`

If you have several messaging services and don't want to mention them explicitly in your code, you can create a `composite-messaging` service where you can define routes for incoming and outgoing messages. In those routes, you can use glob patterns to match topics (`**` for any number of any character, `*` for any number of any character except `/` and `.`, `?` for a single character).
If you have several messaging services and don't want to mention them explicitly in your code, you can create a `composite-messaging` service where you can define routes for incoming and outgoing messages. In those routes, you can use glob patterns to match topics:

| Pattern | Matches |
|---------|---------|
| `?` | Any single character |
| `*` | Any sequence of characters, but does **not** cross `/` or `.` separators |
| `**` | Any sequence of characters, including `/` and `.` separators |

Example:

Expand Down
10 changes: 7 additions & 3 deletions node.js/queue.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ The optional parameters are:
- `maxAttempts` (default `20`): The number of unsuccessful emits until the message is considered unprocessable. The message will remain in the database table!
- `storeLastError` (default `true`): Specifies whether error information of the last failed emit is stored in the tasks table.
- `legacyLocking` (default `true`): If set to `false`, database locks are only used to set the status of the message to `processing` to prevent long-kept database locks. Although this is the recommended approach, it is incompatible with task runners still on `@sap/cds^8`.
- `timeout` (default `"1h"`): The time after which a message with `status === "processing"` is considered to be abandoned and eligable to be processed again. Only for `legacyLocking === false`.
- `timeout` (default `"1h"`): The time after which a message with `status === "processing"` is considered to be abandoned and eligible to be processed again. Only for `legacyLocking === false`.

:::

Expand All @@ -126,7 +126,8 @@ therefore also acts as a dead letter queue.
See [Managing the Dead Letter Queue](#managing-the-dead-letter-queue), to learn about how to handle such messages.

There is only one active message processor per service, tenant, app instance, and message.
This ensures that no duplicate emits happen, except in the highly unlikely case of an app crash right after successful processing but before the message could be deleted.
In a single-instance deployment this ensures that no duplicate emits happen, except in the highly unlikely case of an app crash right after successful processing but before the message could be deleted.
In multi-instance deployments, database-level locking (enabled via `legacyLocking: false`) is recommended to prevent two instances from picking up the same message concurrently.

::: tip Unrecoverable errors
Some errors during the emit are identified as unrecoverable, for example in [SAP Event Mesh](../guides/events/event-mesh) if the used topic is forbidden.
Expand Down Expand Up @@ -226,7 +227,7 @@ To manually trigger the message processing, for example if your server is restar

```js
const srv = await cds.connect.to('yourService')
cds.queued(srv).flush()
await cds.queued(srv).flush()
```

#### Task Callbacks
Expand Down Expand Up @@ -310,6 +311,9 @@ entity:

```js
const db = await cds.connect.to('db')
// Delete only entries that have exceeded max attempts (dead letters)
await DELETE.from('cds.outbox.Messages').where({ status: 'failed' })
// Or delete everything (use with caution in a running system)
await DELETE.from('cds.outbox.Messages')
```

Expand Down
Loading