@palmetto/pubsub 1.0.0
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/README.md +154 -0
- package/dist/bullmq/config.d.ts +21 -0
- package/dist/bullmq/config.js +2 -0
- package/dist/bullmq/connection.d.ts +2 -0
- package/dist/bullmq/connection.js +5 -0
- package/dist/bullmq/main.d.ts +6 -0
- package/dist/bullmq/main.js +21 -0
- package/dist/bullmq/publisher.d.ts +13 -0
- package/dist/bullmq/publisher.js +62 -0
- package/dist/bullmq/pubsub.d.ts +12 -0
- package/dist/bullmq/pubsub.js +35 -0
- package/dist/bullmq/subscriber.d.ts +27 -0
- package/dist/bullmq/subscriber.js +118 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +37 -0
- package/dist/interfaces.d.ts +120 -0
- package/dist/interfaces.js +28 -0
- package/dist/lazy-load.d.ts +11 -0
- package/dist/lazy-load.js +79 -0
- package/dist/main.d.ts +6 -0
- package/dist/main.js +22 -0
- package/dist/publisher.d.ts +11 -0
- package/dist/publisher.js +87 -0
- package/dist/rabbitmq/config.d.ts +84 -0
- package/dist/rabbitmq/config.js +82 -0
- package/dist/rabbitmq/connection.d.ts +41 -0
- package/dist/rabbitmq/connection.js +140 -0
- package/dist/rabbitmq/main.d.ts +5 -0
- package/dist/rabbitmq/main.js +21 -0
- package/dist/rabbitmq/publisher.d.ts +29 -0
- package/dist/rabbitmq/publisher.js +98 -0
- package/dist/rabbitmq/pubsub.d.ts +15 -0
- package/dist/rabbitmq/pubsub.js +37 -0
- package/dist/rabbitmq/subscriber.d.ts +33 -0
- package/dist/rabbitmq/subscriber.js +197 -0
- package/dist/rabbitmq/utility.d.ts +6 -0
- package/dist/rabbitmq/utility.js +11 -0
- package/dist/subscriber.d.ts +28 -0
- package/dist/subscriber.js +140 -0
- package/package.json +54 -0
- package/src/bullmq/README.md +63 -0
- package/src/rabbitmq/README.md +184 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @palmetto/pubsub
|
|
2
|
+
|
|
3
|
+
The BullMq transport provider for @palmetto/pubsub
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add @palmetto/pubsub bullmq zod
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Getting started
|
|
12
|
+
|
|
13
|
+
1. Define your connection string
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const config: ConnectionOptions = {
|
|
17
|
+
host: "localhost",
|
|
18
|
+
port: 6379,
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. Create the publisher
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const publisher = new Publisher(console, [BullMqPublisher(config, console)]);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
3. Create the subscriber
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const subscriber = new Subscriber(console, [
|
|
32
|
+
new BullMqSubscriber(config, console),
|
|
33
|
+
]);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
4. Create the queue configuration
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
const queue: BullMqQueueConfiguration = {
|
|
40
|
+
name: "my-queue-name",
|
|
41
|
+
schema: MyModelSchema,
|
|
42
|
+
transport: BULLMQ_TRANSPORT,
|
|
43
|
+
job: "my-optional-job-name",
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
5. Publish a message
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const message: MyModel = {... };
|
|
51
|
+
|
|
52
|
+
await publisher.publish(config, message);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
6. Subscribe to a queue
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
|
|
59
|
+
subscriber.addSubscriber(queue, (message: MyModel, context: BullMqMessageContext) => {
|
|
60
|
+
...
|
|
61
|
+
return MessageResult.Ok;
|
|
62
|
+
});
|
|
63
|
+
```
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# @palmetto/pubsub
|
|
2
|
+
|
|
3
|
+
The RabbitMq (AMQP) transport provider for @palmetto/pubsub
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add @palmetto/pubsub amqp-connection-manager amqplib zod
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
1. Define your connection string
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const config: RabbitMqConnectionConfig = {
|
|
17
|
+
host: "amqp://guest:guest@localhost:5672/",
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. Create the connection
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const connection = new RabbitMqConnection(config, console);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
3. Create the publisher
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const publisher = new Publisher(console, [
|
|
31
|
+
RabbitMqPublisher(connection, console),
|
|
32
|
+
]);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
4. Create the subscriber
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const subscriber = new Subscriber(console, [
|
|
39
|
+
new RabbitMqSubscriber(connection, console),
|
|
40
|
+
]);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
5. Create the queue configuration
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const queue: RabbitQueueExchangeConfiguration = {
|
|
47
|
+
name: "my-queue-name",
|
|
48
|
+
schema: MyModelSchema,
|
|
49
|
+
transport: RABBITMQ_TRANSPORT,
|
|
50
|
+
retries: 360,
|
|
51
|
+
retryDelay: 1_000,
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
6. Publish a message
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const message: MyModel = {... };
|
|
59
|
+
|
|
60
|
+
await publisher.publish(config, message);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
7. Subscribe to a queue
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
subscriber.addSubscriber(queue, (message: MyModel, context: RabbitMqMessageContext) => {
|
|
67
|
+
...
|
|
68
|
+
return MessageResult.Ok;
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
8. Subscribe to the dead-letter queue if you want to re-process completely failed messages. Set the `queueType` to `"dead-letter"`
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const dlQueue = {
|
|
76
|
+
...queue,
|
|
77
|
+
queueType: "dead-letter",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
subscriber.addSubscriber(dlQueue, (message: MyModel, context: RabbitMqMessageContext) => {
|
|
81
|
+
...
|
|
82
|
+
return MessageResult.Ok; // returning Ok will remove the message from the dlq
|
|
83
|
+
return MessageResult.Fail; // returning Fail will remove the message from the dlq
|
|
84
|
+
return MessageResult.Retry; // returning Retry will move the message to the retry queue and back into the normal queue processing
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Message failures
|
|
89
|
+
|
|
90
|
+
Message failures in RabbitMq subscribers are handled using special queues and exchanges. A retry queue is used to enable retry delays. A dead-letter queue is used to store messages that fail and run out of retries.
|
|
91
|
+
|
|
92
|
+
### Retrying messages
|
|
93
|
+
|
|
94
|
+
These properties configure message retries:
|
|
95
|
+
|
|
96
|
+
- `retries` : specifies the number of retries after which the message is discarded. When this value is undefined, the message will retry forever.
|
|
97
|
+
- `retryDelay`: specifies the number of milliseconds between retries.
|
|
98
|
+
|
|
99
|
+
#### The retry queue
|
|
100
|
+
|
|
101
|
+
The retry queue is created using the queue name and ends with `.rlq`. The queue is configured with a dead-letter exchange that sends messages back to the original queue. A retry exchange with a default name of the queue ending with `.rlx` is bound to the retry queue. When a `retryDelay` is defined, the original message is re-published into the retry exchange with an `expiration` of the `retryDelay`. When the message is at the head of the retry queue and the expiration has elapsed, then RabbitMq removes the message from the queue and publishes it back to the original queue.
|
|
102
|
+
|
|
103
|
+
It's important to set a `retryDelay` that works for all messages of the queue. The `expiration` cannot be changed after starting. Every message must have the same expiration in order for retries to work correctly. Only the message at the head of the queue is removed at the expiration time. If there are more messages in the queue with earlier expirations, they will not be removed (re-tried) until they are at the head of the queue.
|
|
104
|
+
|
|
105
|
+
### Failing messages
|
|
106
|
+
|
|
107
|
+
A dead-letter queue is created using the queue name and ends with `.dlq`. A dead-letter exchange is created using the queue name and ends with `.dlx`. Messages that fail are nack'ed to rabbit and are published to the main queue's dead-letter exchange.
|
|
108
|
+
|
|
109
|
+
Messages are sent to the dead-letter exchange when the handler returns `MessageResult.Fail` or when the message exceeds the number of `retries` configured.
|
|
110
|
+
|
|
111
|
+
## Queue and Exchange Configuration
|
|
112
|
+
|
|
113
|
+
The simplest way to configure PubSub RabbitMq is to pass a single name for the queue and let the framework decide the rest.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const config: RabbitQueueExchangeConfiguration = {
|
|
117
|
+
name: "my.queue",
|
|
118
|
+
transport: RABBITMQ_TRANSPORT,
|
|
119
|
+
schema: MyModelSchema,
|
|
120
|
+
};
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This configuration creates the following queues:
|
|
124
|
+
|
|
125
|
+
- `my.queue` the main queue which receives all messages published using the config
|
|
126
|
+
- dead-letter-exchange is set to `my.queue.dlx`
|
|
127
|
+
- `my.queue.dlq` the main queue's dead-letter queue where failed messages end up
|
|
128
|
+
- `my.queue.rtq` the main queue's retry queue where messages to be retried end up
|
|
129
|
+
- dead-letter-exchange is set to the main queue's exchange so that messages are retried
|
|
130
|
+
|
|
131
|
+
And these direct exchanges:
|
|
132
|
+
|
|
133
|
+
- `my.queue` the main exchange that is bound to the main queue
|
|
134
|
+
- `my.queue.dlx` the dead-letter exchange that receives failed messages from the main queue
|
|
135
|
+
- `my.queue.rtx` the retry exchange that receives messages to retry. it is bound to the retry queue
|
|
136
|
+
|
|
137
|
+
### Topic exchanges
|
|
138
|
+
|
|
139
|
+
Topic exchanges provide the ability to use a single exchange to copy messages to multiple queues simultaneously. When subscribing to a topic exchange, you must also provide a subscriber name to include in the queue name. This ensures that there is a unique queue for each subscriber of the exchange.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const config: RabbitQueueExchangeConfiguration = {
|
|
143
|
+
name: "my.queue",
|
|
144
|
+
transport: RABBITMQ_TRANSPORT,
|
|
145
|
+
schema: MyModelSchema,
|
|
146
|
+
exchangeType: "topic",
|
|
147
|
+
topicSubscriberName: "my-api-name",
|
|
148
|
+
retries: 100,
|
|
149
|
+
retryDelay: 10_000,
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This configuration would create a main queue called `my.queue.my-api-name`
|
|
154
|
+
|
|
155
|
+
### Custom names
|
|
156
|
+
|
|
157
|
+
You can override all the queue, exchange names and routing keys for each `queueType`
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const config: RabbitQueueExchangeConfiguration = {
|
|
161
|
+
name: "my.queue",
|
|
162
|
+
transport: RABBITMQ_TRANSPORT,
|
|
163
|
+
schema: MyModelSchema,
|
|
164
|
+
overrides: {
|
|
165
|
+
names: {
|
|
166
|
+
default: {
|
|
167
|
+
queueName: "my.queue.name",
|
|
168
|
+
exchangeName: "some.exchange",
|
|
169
|
+
routingKey: "some.routing.key",
|
|
170
|
+
},
|
|
171
|
+
"dead-letter": {
|
|
172
|
+
queueName: "my.dead.letter.queue",
|
|
173
|
+
exchangeName: "my.dead.letter.exchange",
|
|
174
|
+
routingKey: "some.routing.key",
|
|
175
|
+
},
|
|
176
|
+
retry: {
|
|
177
|
+
queueName: "my.retry.queue",
|
|
178
|
+
exchangeName: "my.retry.exchange",
|
|
179
|
+
routingKey: "some.routing.key",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
```
|