pulsar-client 1.14.0 → 1.15.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/index.d.ts CHANGED
@@ -68,6 +68,8 @@ export interface ProducerConfig {
68
68
  schema?: SchemaInfo;
69
69
  accessMode?: ProducerAccessMode;
70
70
  batchingType?: ProducerBatchType;
71
+ messageRouter?: MessageRouter;
72
+ batchingMaxAllowedSizeInBytes?: number;
71
73
  }
72
74
 
73
75
  export class Producer {
@@ -176,6 +178,23 @@ export class MessageId {
176
178
  toString(): string;
177
179
  }
178
180
 
181
+ export interface TopicMetadata {
182
+ numPartitions: number;
183
+ }
184
+
185
+ /**
186
+ * @callback MessageRouter
187
+ * @description When producing messages to a partitioned topic, this router is used to select the
188
+ * target partition for each message. The router only works when the `messageRoutingMode` is set to
189
+ * `CustomPartition`. Please note that `getTopicName()` cannot be called on the `message`, otherwise
190
+ * the behavior will be undefined because the topic is unknown before sending the message.
191
+ * @param message The message to be routed.
192
+ * @param topicMetadata Metadata for the partitioned topic the message is being routed to.
193
+ * @returns {number} The index of the target partition (must be a number between 0 and
194
+ * topicMetadata.numPartitions - 1).
195
+ */
196
+ export type MessageRouter = (message: Message, topicMetadata: TopicMetadata) => number;
197
+
179
198
  export interface SchemaInfo {
180
199
  schemaType: SchemaType;
181
200
  name?: string;
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "pulsar-client",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Pulsar Node.js client",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
+ "resolutions": {
8
+ "minimatch": "^9.0.0"
9
+ },
7
10
  "directories": {
8
11
  "src": "src",
9
12
  "example": "examples"
package/src/Producer.cc CHANGED
@@ -73,6 +73,7 @@ Napi::Value Producer::NewInstance(const Napi::CallbackInfo &info, std::shared_pt
73
73
  auto instanceContext = static_cast<ProducerNewInstanceContext *>(ctx);
74
74
  auto deferred = instanceContext->deferred;
75
75
  auto cClient = instanceContext->cClient;
76
+ auto producerConfig = instanceContext->producerConfig;
76
77
  delete instanceContext;
77
78
 
78
79
  if (result != pulsar_result_Ok) {
@@ -81,10 +82,11 @@ Napi::Value Producer::NewInstance(const Napi::CallbackInfo &info, std::shared_pt
81
82
 
82
83
  std::shared_ptr<pulsar_producer_t> cProducer(rawProducer, pulsar_producer_free);
83
84
 
84
- deferred->Resolve([cProducer](const Napi::Env env) {
85
+ deferred->Resolve([cProducer, producerConfig](const Napi::Env env) {
85
86
  Napi::Object obj = Producer::constructor.New({});
86
87
  Producer *producer = Producer::Unwrap(obj);
87
88
  producer->SetCProducer(cProducer);
89
+ producer->producerConfig = producerConfig;
88
90
  return obj;
89
91
  });
90
92
  },
package/src/Producer.h CHANGED
@@ -23,6 +23,8 @@
23
23
  #include <napi.h>
24
24
  #include <pulsar/c/client.h>
25
25
  #include <pulsar/c/producer.h>
26
+ #include <memory>
27
+ #include "ProducerConfig.h"
26
28
 
27
29
  class Producer : public Napi::ObjectWrap<Producer> {
28
30
  public:
@@ -35,6 +37,9 @@ class Producer : public Napi::ObjectWrap<Producer> {
35
37
 
36
38
  private:
37
39
  std::shared_ptr<pulsar_producer_t> cProducer;
40
+ // Extend the lifetime of the producer config since it's env and router function could be used when sending
41
+ // messages
42
+ std::shared_ptr<ProducerConfig> producerConfig;
38
43
  Napi::Value Send(const Napi::CallbackInfo &info);
39
44
  Napi::Value Flush(const Napi::CallbackInfo &info);
40
45
  Napi::Value Close(const Napi::CallbackInfo &info);
@@ -18,8 +18,14 @@
18
18
  */
19
19
  #include "SchemaInfo.h"
20
20
  #include "ProducerConfig.h"
21
+ #include "Message.h"
22
+ #include <cstdio>
21
23
  #include <map>
24
+ #include "napi-inl.h"
25
+ #include "napi.h"
22
26
  #include "pulsar/ProducerConfiguration.h"
27
+ #include "pulsar/c/message.h"
28
+ #include "pulsar/c/message_router.h"
23
29
 
24
30
  static const std::string CFG_TOPIC = "topic";
25
31
  static const std::string CFG_PRODUCER_NAME = "producerName";
@@ -34,6 +40,7 @@ static const std::string CFG_COMPRESS_TYPE = "compressionType";
34
40
  static const std::string CFG_BATCH_ENABLED = "batchingEnabled";
35
41
  static const std::string CFG_BATCH_MAX_DELAY = "batchingMaxPublishDelayMs";
36
42
  static const std::string CFG_BATCH_MAX_MSG = "batchingMaxMessages";
43
+ static const std::string CFG_BATCH_MAX_ALLOWED_SIZE_IN_BYTES = "batchingMaxAllowedSizeInBytes";
37
44
  static const std::string CFG_SCHEMA = "schema";
38
45
  static const std::string CFG_PROPS = "properties";
39
46
  static const std::string CFG_PUBLIC_KEY_PATH = "publicKeyPath";
@@ -42,6 +49,7 @@ static const std::string CFG_CRYPTO_FAILURE_ACTION = "cryptoFailureAction";
42
49
  static const std::string CFG_CHUNK_ENABLED = "chunkingEnabled";
43
50
  static const std::string CFG_ACCESS_MODE = "accessMode";
44
51
  static const std::string CFG_BATCHING_TYPE = "batchingType";
52
+ static const std::string CFG_MESSAGE_ROUTER = "messageRouter";
45
53
 
46
54
  struct _pulsar_producer_configuration {
47
55
  pulsar::ProducerConfiguration conf;
@@ -82,6 +90,25 @@ static std::map<std::string, pulsar::ProducerConfiguration::BatchingType> PRODUC
82
90
  {"KeyBasedBatching", pulsar::ProducerConfiguration::KeyBasedBatching},
83
91
  };
84
92
 
93
+ static int choosePartition(pulsar_message_t* msg, pulsar_topic_metadata_t* metadata, void* ctx) {
94
+ auto router = static_cast<Napi::FunctionReference*>(ctx);
95
+ const auto& env = router->Env();
96
+ auto jsMessage = Message::NewInstance(Napi::Object::New(env),
97
+ std::shared_ptr<pulsar_message_t>(msg, [](pulsar_message_t*) {}));
98
+ int numPartitions = pulsar_topic_metadata_get_num_partitions(metadata);
99
+
100
+ Napi::Object jsTopicMetadata = Napi::Object::New(env);
101
+ jsTopicMetadata.Set("numPartitions", Napi::Number::New(env, numPartitions));
102
+
103
+ try {
104
+ return router->Call({jsMessage, jsTopicMetadata}).ToNumber().Int32Value();
105
+ } catch (const Napi::Error& e) {
106
+ // TODO: how to handle the error properly? For now, return an invalid partition to fail the send
107
+ fprintf(stderr, "Error when calling messageRouter: %s\n", e.what());
108
+ return numPartitions;
109
+ }
110
+ }
111
+
85
112
  ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
86
113
  this->cProducerConfig = std::shared_ptr<pulsar_producer_configuration_t>(
87
114
  pulsar_producer_configuration_create(), pulsar_producer_configuration_free);
@@ -131,8 +158,10 @@ ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
131
158
  pulsar_producer_configuration_set_block_if_queue_full(this->cProducerConfig.get(), blockIfQueueFull);
132
159
  }
133
160
 
161
+ bool useCustomPartition = false;
134
162
  if (producerConfig.Has(CFG_ROUTING_MODE) && producerConfig.Get(CFG_ROUTING_MODE).IsString()) {
135
163
  std::string messageRoutingMode = producerConfig.Get(CFG_ROUTING_MODE).ToString().Utf8Value();
164
+ useCustomPartition = (messageRoutingMode == "CustomPartition");
136
165
  if (MESSAGE_ROUTING_MODE.count(messageRoutingMode))
137
166
  pulsar_producer_configuration_set_partitions_routing_mode(this->cProducerConfig.get(),
138
167
  MESSAGE_ROUTING_MODE.at(messageRoutingMode));
@@ -173,6 +202,16 @@ ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
173
202
  }
174
203
  }
175
204
 
205
+ if (producerConfig.Has(CFG_BATCH_MAX_ALLOWED_SIZE_IN_BYTES) &&
206
+ producerConfig.Get(CFG_BATCH_MAX_ALLOWED_SIZE_IN_BYTES).IsNumber()) {
207
+ int64_t batchingMaxAllowedSizeInBytes =
208
+ producerConfig.Get(CFG_BATCH_MAX_ALLOWED_SIZE_IN_BYTES).ToNumber().Int64Value();
209
+ if (batchingMaxAllowedSizeInBytes > 0) {
210
+ pulsar_producer_configuration_set_batching_max_allowed_size_in_bytes(
211
+ this->cProducerConfig.get(), (unsigned long)batchingMaxAllowedSizeInBytes);
212
+ }
213
+ }
214
+
176
215
  if (producerConfig.Has(CFG_SCHEMA) && producerConfig.Get(CFG_SCHEMA).IsObject()) {
177
216
  SchemaInfo* schemaInfo = new SchemaInfo(producerConfig.Get(CFG_SCHEMA).ToObject());
178
217
  schemaInfo->SetProducerSchema(this->cProducerConfig);
@@ -224,6 +263,15 @@ ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
224
263
  if (PRODUCER_BATCHING_TYPE.count(batchingType)) {
225
264
  this->cProducerConfig.get()->conf.setBatchingType(PRODUCER_BATCHING_TYPE.at(batchingType));
226
265
  }
266
+
267
+ if (useCustomPartition && producerConfig.Has(CFG_MESSAGE_ROUTER)) {
268
+ auto value = producerConfig.Get(CFG_MESSAGE_ROUTER);
269
+ if (value.IsFunction()) {
270
+ messageRouter = Napi::Persistent(value.As<Napi::Function>());
271
+ pulsar_producer_configuration_set_message_router(this->cProducerConfig.get(), choosePartition,
272
+ &messageRouter);
273
+ }
274
+ }
227
275
  }
228
276
 
229
277
  ProducerConfig::~ProducerConfig() {}
@@ -22,6 +22,11 @@
22
22
 
23
23
  #include <napi.h>
24
24
  #include <pulsar/c/producer_configuration.h>
25
+ #include <memory>
26
+
27
+ struct MessageRouterContext {
28
+ Napi::FunctionReference messageRouter;
29
+ };
25
30
 
26
31
  class ProducerConfig {
27
32
  public:
@@ -33,6 +38,8 @@ class ProducerConfig {
33
38
  private:
34
39
  std::shared_ptr<pulsar_producer_configuration_t> cProducerConfig;
35
40
  std::string topic;
41
+ std::unique_ptr<MessageRouterContext> routerContext;
42
+ Napi::FunctionReference messageRouter;
36
43
  };
37
44
 
38
45
  #endif