kafka-ts 1.1.8 → 1.2.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.
Files changed (77) hide show
  1. package/README.md +1 -1
  2. package/dist/consumer/consumer-group.js +84 -99
  3. package/dist/consumer/consumer.js +23 -32
  4. package/dist/consumer/offset-manager.js +6 -5
  5. package/dist/distributors/group-by-leader-id.d.ts +10 -0
  6. package/dist/distributors/group-by-leader-id.js +13 -0
  7. package/dist/distributors/group-partitions-by-topic.d.ts +6 -0
  8. package/dist/distributors/group-partitions-by-topic.js +12 -0
  9. package/dist/metadata.d.ts +1 -1
  10. package/dist/metadata.js +1 -1
  11. package/dist/producer/producer-buffer.d.ts +20 -0
  12. package/dist/producer/producer-buffer.js +118 -0
  13. package/dist/producer/producer-state.d.ts +15 -0
  14. package/dist/producer/producer-state.js +33 -0
  15. package/dist/producer/producer.d.ts +6 -11
  16. package/dist/producer/producer.js +32 -108
  17. package/dist/utils/decoder.js +13 -8
  18. package/dist/utils/encoder.d.ts +10 -8
  19. package/dist/utils/encoder.js +95 -58
  20. package/dist/utils/promise-chain.d.ts +5 -0
  21. package/dist/utils/promise-chain.js +39 -0
  22. package/dist/utils/retry.d.ts +1 -0
  23. package/dist/utils/retry.js +19 -0
  24. package/dist/utils/shared.d.ts +1 -1
  25. package/dist/utils/shared.js +8 -7
  26. package/package.json +1 -1
  27. package/dist/consumer/metadata.d.ts +0 -24
  28. package/dist/consumer/metadata.js +0 -64
  29. package/dist/distributors/messages-to-topic-partition-leaders.d.ts +0 -17
  30. package/dist/distributors/messages-to-topic-partition-leaders.js +0 -15
  31. package/dist/distributors/messages-to-topic-partition-leaders.test.d.ts +0 -1
  32. package/dist/distributors/messages-to-topic-partition-leaders.test.js +0 -30
  33. package/dist/examples/src/replicator.js +0 -34
  34. package/dist/examples/src/utils/json.js +0 -5
  35. package/dist/request-handler.d.ts +0 -16
  36. package/dist/request-handler.js +0 -67
  37. package/dist/request-handler.test.d.ts +0 -1
  38. package/dist/request-handler.test.js +0 -340
  39. package/dist/src/api/api-versions.js +0 -18
  40. package/dist/src/api/create-topics.js +0 -46
  41. package/dist/src/api/delete-topics.js +0 -26
  42. package/dist/src/api/fetch.js +0 -95
  43. package/dist/src/api/find-coordinator.js +0 -34
  44. package/dist/src/api/heartbeat.js +0 -22
  45. package/dist/src/api/index.js +0 -38
  46. package/dist/src/api/init-producer-id.js +0 -24
  47. package/dist/src/api/join-group.js +0 -48
  48. package/dist/src/api/leave-group.js +0 -30
  49. package/dist/src/api/list-offsets.js +0 -39
  50. package/dist/src/api/metadata.js +0 -47
  51. package/dist/src/api/offset-commit.js +0 -39
  52. package/dist/src/api/offset-fetch.js +0 -44
  53. package/dist/src/api/produce.js +0 -119
  54. package/dist/src/api/sync-group.js +0 -31
  55. package/dist/src/broker.js +0 -35
  56. package/dist/src/connection.js +0 -21
  57. package/dist/src/consumer/consumer-group.js +0 -131
  58. package/dist/src/consumer/consumer.js +0 -103
  59. package/dist/src/consumer/metadata.js +0 -52
  60. package/dist/src/consumer/offset-manager.js +0 -23
  61. package/dist/src/index.js +0 -19
  62. package/dist/src/producer/producer.js +0 -84
  63. package/dist/src/request-handler.js +0 -57
  64. package/dist/src/request-handler.test.js +0 -321
  65. package/dist/src/types.js +0 -2
  66. package/dist/src/utils/api.js +0 -5
  67. package/dist/src/utils/decoder.js +0 -161
  68. package/dist/src/utils/encoder.js +0 -137
  69. package/dist/src/utils/error.js +0 -10
  70. package/dist/utils/debug.d.ts +0 -2
  71. package/dist/utils/debug.js +0 -11
  72. package/dist/utils/lock.d.ts +0 -8
  73. package/dist/utils/lock.js +0 -44
  74. package/dist/utils/memo.d.ts +0 -1
  75. package/dist/utils/memo.js +0 -16
  76. package/dist/utils/mutex.d.ts +0 -3
  77. package/dist/utils/mutex.js +0 -32
@@ -4,25 +4,20 @@ import { Message } from '../types';
4
4
  export type ProducerOptions = {
5
5
  allowTopicAutoCreation?: boolean;
6
6
  partitioner?: Partitioner;
7
+ maxBatchSize?: number;
7
8
  };
8
9
  export declare class Producer {
9
10
  private cluster;
10
11
  private options;
11
12
  private metadata;
12
- private producerId;
13
- private producerEpoch;
14
- private sequences;
13
+ private state;
15
14
  private partition;
16
- private lock;
15
+ private chain;
16
+ private bufferByNodeId;
17
17
  constructor(cluster: Cluster, options: ProducerOptions);
18
- send(messages: Message[], { acks }?: {
19
- acks?: -1 | 1;
20
- }): Promise<void>;
18
+ send(messages: Message[]): Promise<void>;
21
19
  close(): Promise<void>;
22
20
  private ensureProducerInitialized;
23
- private initProducerId;
24
- private getSequence;
25
- private updateSequence;
26
- private fetchMetadata;
21
+ private fetchMetadataForTopics;
27
22
  private handleError;
28
23
  }
@@ -11,106 +11,60 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Producer = void 0;
13
13
  const api_1 = require("../api");
14
- const messages_to_topic_partition_leaders_1 = require("../distributors/messages-to-topic-partition-leaders");
14
+ const group_by_leader_id_1 = require("../distributors/group-by-leader-id");
15
15
  const partitioner_1 = require("../distributors/partitioner");
16
16
  const metadata_1 = require("../metadata");
17
17
  const error_1 = require("../utils/error");
18
- const lock_1 = require("../utils/lock");
19
18
  const logger_1 = require("../utils/logger");
19
+ const promise_chain_1 = require("../utils/promise-chain");
20
20
  const shared_1 = require("../utils/shared");
21
21
  const tracer_1 = require("../utils/tracer");
22
+ const producer_buffer_1 = require("./producer-buffer");
23
+ const producer_state_1 = require("./producer-state");
22
24
  const trace = (0, tracer_1.createTracer)('Producer');
23
25
  class Producer {
24
26
  cluster;
25
27
  options;
26
28
  metadata;
27
- producerId = 0n;
28
- producerEpoch = 0;
29
- sequences = {};
29
+ state;
30
30
  partition;
31
- lock = new lock_1.Lock();
31
+ chain = new promise_chain_1.PromiseChain();
32
+ bufferByNodeId = {};
32
33
  constructor(cluster, options) {
33
34
  this.cluster = cluster;
34
35
  this.options = {
35
36
  ...options,
36
37
  allowTopicAutoCreation: options.allowTopicAutoCreation ?? false,
37
38
  partitioner: options.partitioner ?? partitioner_1.defaultPartitioner,
39
+ maxBatchSize: options.maxBatchSize ?? 500,
38
40
  };
39
41
  this.metadata = new metadata_1.Metadata({ cluster });
42
+ this.state = new producer_state_1.ProducerState({ cluster });
40
43
  this.partition = this.options.partitioner({ metadata: this.metadata });
41
44
  }
42
- async send(messages, { acks = -1 } = {}) {
45
+ async send(messages) {
43
46
  await this.ensureProducerInitialized();
44
- const { allowTopicAutoCreation } = this.options;
45
- const defaultTimestamp = BigInt(Date.now());
46
- const topics = new Set(messages.map((message) => message.topic));
47
- await this.lock.acquire([...topics].map((topic) => `metadata:${topic}`), () => this.metadata.fetchMetadataIfNecessary({ topics, allowTopicAutoCreation }));
47
+ const topics = [...new Set(messages.map((message) => message.topic))];
48
+ await this.fetchMetadataForTopics(topics);
48
49
  const partitionedMessages = messages.map((message) => {
49
50
  message.partition = this.partition(message);
50
51
  return message;
51
52
  });
52
- const nodeTopicPartitionMessages = (0, messages_to_topic_partition_leaders_1.distributeMessagesToTopicPartitionLeaders)(partitionedMessages, this.metadata.getTopicPartitionLeaderIds());
53
- await Promise.all(Object.entries(nodeTopicPartitionMessages).map(async ([nodeId, topicPartitionMessages]) => {
53
+ const messagesByLeaderId = (0, group_by_leader_id_1.groupByLeaderId)(partitionedMessages, this.metadata.getTopicPartitionLeaderIds());
54
+ await Promise.all(Object.entries(messagesByLeaderId).map(async ([leaderId, messages]) => {
55
+ const nodeId = parseInt(leaderId);
56
+ const buffer = (this.bufferByNodeId[nodeId] ??= new producer_buffer_1.ProducerBuffer({
57
+ nodeId,
58
+ maxBatchSize: this.options.maxBatchSize,
59
+ cluster: this.cluster,
60
+ state: this.state,
61
+ }));
54
62
  try {
55
- await this.lock.acquire([`node:${nodeId}`], async () => {
56
- const topicData = Object.entries(topicPartitionMessages).map(([topic, partitionMessages]) => ({
57
- name: topic,
58
- partitionData: Object.entries(partitionMessages).map(([partition, messages]) => {
59
- const partitionIndex = parseInt(partition);
60
- let baseTimestamp;
61
- let maxTimestamp;
62
- messages.forEach(({ timestamp = defaultTimestamp }) => {
63
- if (!baseTimestamp || timestamp < baseTimestamp) {
64
- baseTimestamp = timestamp;
65
- }
66
- if (!maxTimestamp || timestamp > maxTimestamp) {
67
- maxTimestamp = timestamp;
68
- }
69
- });
70
- return {
71
- index: partitionIndex,
72
- baseOffset: 0n,
73
- partitionLeaderEpoch: -1,
74
- attributes: 0,
75
- lastOffsetDelta: messages.length - 1,
76
- baseTimestamp: baseTimestamp ?? 0n,
77
- maxTimestamp: maxTimestamp ?? 0n,
78
- producerId: this.producerId,
79
- producerEpoch: 0,
80
- baseSequence: this.getSequence(topic, partitionIndex),
81
- records: messages.map((message, index) => ({
82
- attributes: 0,
83
- timestampDelta: (message.timestamp ?? defaultTimestamp) - (baseTimestamp ?? 0n),
84
- offsetDelta: index,
85
- key: message.key ?? null,
86
- value: message.value,
87
- headers: Object.entries(message.headers ?? {}).map(([key, value]) => ({
88
- key,
89
- value,
90
- })),
91
- })),
92
- };
93
- }),
94
- }));
95
- await this.cluster.sendRequestToNode(parseInt(nodeId))(api_1.API.PRODUCE, {
96
- transactionalId: null,
97
- acks,
98
- timeoutMs: 30000,
99
- topicData,
100
- });
101
- topicData.forEach(({ name, partitionData }) => {
102
- partitionData.forEach(({ index, records }) => {
103
- this.updateSequence(name, index, records.length);
104
- });
105
- });
106
- });
63
+ await buffer.enqueue(messages);
107
64
  }
108
65
  catch (error) {
109
66
  await this.handleError(error);
110
- const messages = Object.values(topicPartitionMessages)
111
- .flatMap((partitionMessages) => Object.values(partitionMessages).flat())
112
- .map(({ partition, ...message }) => message);
113
- return this.send(messages, { acks });
67
+ return this.send(messages);
114
68
  }
115
69
  }));
116
70
  }
@@ -119,55 +73,25 @@ class Producer {
119
73
  }
120
74
  ensureProducerInitialized = (0, shared_1.shared)(async () => {
121
75
  await this.cluster.ensureConnected();
122
- if (!this.producerId) {
123
- await this.initProducerId();
76
+ if (!this.state.producerId) {
77
+ await this.state.initProducerId();
124
78
  }
125
79
  });
126
- async initProducerId() {
127
- try {
128
- const result = await this.cluster.sendRequest(api_1.API.INIT_PRODUCER_ID, {
129
- transactionalId: null,
130
- transactionTimeoutMs: 0,
131
- producerId: this.producerId,
132
- producerEpoch: this.producerEpoch,
133
- });
134
- this.producerId = result.producerId;
135
- this.producerEpoch = result.producerEpoch;
136
- this.sequences = {};
137
- }
138
- catch (error) {
139
- await this.handleError(error);
140
- return this.initProducerId();
141
- }
142
- }
143
- getSequence(topic, partition) {
144
- return this.sequences[topic]?.[partition] ?? 0;
145
- }
146
- updateSequence(topic, partition, messagesCount) {
147
- this.sequences[topic] ??= {};
148
- this.sequences[topic][partition] ??= 0;
149
- this.sequences[topic][partition] += messagesCount;
150
- }
151
- async fetchMetadata(topics, allowTopicAutoCreation) {
152
- try {
153
- await this.metadata.fetchMetadata({ topics, allowTopicAutoCreation });
154
- }
155
- catch (error) {
156
- await this.handleError(error);
157
- return this.fetchMetadata(topics, allowTopicAutoCreation);
158
- }
159
- }
80
+ fetchMetadataForTopics = (0, shared_1.shared)(async (topics) => {
81
+ const { allowTopicAutoCreation } = this.options;
82
+ await this.chain.run(topics.map((topic) => `metadata:${topic}`), () => this.metadata.fetchMetadataIfNecessary({ topics, allowTopicAutoCreation }));
83
+ });
160
84
  async handleError(error) {
161
85
  await (0, api_1.handleApiError)(error).catch(async (error) => {
162
86
  if (error instanceof error_1.KafkaTSApiError && error.errorCode === api_1.API_ERROR.NOT_LEADER_OR_FOLLOWER) {
163
87
  logger_1.log.debug('Refreshing metadata', { reason: error.message });
164
88
  const topics = Object.keys(this.metadata.getTopicPartitions());
165
- await this.fetchMetadata(topics, false);
89
+ await this.metadata.fetchMetadata({ topics, allowTopicAutoCreation: false });
166
90
  return;
167
91
  }
168
92
  if (error instanceof error_1.KafkaTSApiError && error.errorCode === api_1.API_ERROR.OUT_OF_ORDER_SEQUENCE_NUMBER) {
169
93
  logger_1.log.debug('Out of order sequence number. Reinitializing producer ID');
170
- await this.initProducerId();
94
+ await this.state.initProducerId();
171
95
  return;
172
96
  }
173
97
  throw error;
@@ -178,6 +102,6 @@ exports.Producer = Producer;
178
102
  __decorate([
179
103
  trace(() => ({ root: true })),
180
104
  __metadata("design:type", Function),
181
- __metadata("design:paramtypes", [Array, Object]),
105
+ __metadata("design:paramtypes", [Array]),
182
106
  __metadata("design:returntype", Promise)
183
107
  ], Producer.prototype, "send", null);
@@ -110,12 +110,16 @@ class Decoder {
110
110
  }
111
111
  readArray(callback) {
112
112
  const length = this.readInt32();
113
- const results = Array.from({ length }).map(() => callback(this));
113
+ const results = new Array(length);
114
+ for (let i = 0; i < length; i++)
115
+ results[i] = callback(this);
114
116
  return results;
115
117
  }
116
118
  readCompactArray(callback) {
117
119
  const length = this.readUVarInt() - 1;
118
- const results = Array.from({ length }).map(() => callback(this));
120
+ const results = new Array(length);
121
+ for (let i = 0; i < length; i++)
122
+ results[i] = callback(this);
119
123
  return results;
120
124
  }
121
125
  readVarIntArray(callback) {
@@ -125,15 +129,16 @@ class Decoder {
125
129
  }
126
130
  readRecords(callback) {
127
131
  const length = this.readInt32();
128
- return Array.from({ length }).map(() => {
132
+ const results = [];
133
+ for (let i = 0; i < length; i++) {
129
134
  const size = this.readVarInt();
130
- if (!size) {
131
- return null;
132
- }
135
+ if (!size)
136
+ continue;
133
137
  const child = new Decoder(this.buffer.subarray(this.offset, this.offset + size));
134
138
  this.offset += size;
135
- return callback(child);
136
- }).filter(x => x !== null);
139
+ results.push(callback(child));
140
+ }
141
+ return results;
137
142
  }
138
143
  read(length) {
139
144
  const value = this.buffer.subarray(this.offset, length !== undefined ? this.offset + length : undefined);
@@ -1,9 +1,11 @@
1
1
  export declare class Encoder {
2
- private chunks;
3
- getChunks(): Buffer<ArrayBufferLike>[];
2
+ private buffer;
3
+ private offset;
4
+ constructor(initialCapacity?: number);
5
+ private ensure;
4
6
  getBufferLength(): number;
5
- write(...buffers: Buffer[]): this;
6
- writeEncoder(encoder: Encoder): this;
7
+ write(src: Buffer): this;
8
+ writeEncoder(other: Encoder): this;
7
9
  writeInt8(value: number): this;
8
10
  writeInt16(value: number): this;
9
11
  writeInt32(value: number): this;
@@ -18,10 +20,10 @@ export declare class Encoder {
18
20
  writeVarIntString(value: string | null): this;
19
21
  writeUUID(value: string | null): this;
20
22
  writeBoolean(value: boolean): this;
21
- writeArray<T>(arr: T[], callback: (encoder: Encoder, item: T) => Encoder): this;
22
- writeCompactArray<T>(arr: T[] | null, callback: (encoder: Encoder, item: T) => Encoder): this;
23
- writeVarIntArray<T>(arr: T[], callback: (encoder: Encoder, item: T) => Encoder): this;
23
+ writeArray<T>(arr: T[], callback: (encoder: Encoder, item: T) => void): this;
24
+ writeCompactArray<T>(arr: T[] | null, callback: (encoder: Encoder, item: T) => void): this;
25
+ writeVarIntArray<T>(arr: T[], callback: (encoder: Encoder, item: T) => void): this;
24
26
  writeBytes(value: Buffer): this;
25
27
  writeCompactBytes(value: Buffer): this;
26
- value(): Buffer<ArrayBuffer>;
28
+ value(): Buffer<ArrayBufferLike>;
27
29
  }
@@ -2,121 +2,158 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Encoder = void 0;
4
4
  class Encoder {
5
- chunks = [];
6
- getChunks() {
7
- return this.chunks;
5
+ buffer;
6
+ offset = 0;
7
+ constructor(initialCapacity = 512) {
8
+ this.buffer = Buffer.allocUnsafe(initialCapacity);
9
+ }
10
+ ensure(extra) {
11
+ const need = this.offset + extra;
12
+ if (need <= this.buffer.length)
13
+ return;
14
+ let cap = this.buffer.length;
15
+ while (cap < need)
16
+ cap <<= 1;
17
+ const n = Buffer.allocUnsafe(cap);
18
+ this.buffer.copy(n, 0, 0, this.offset);
19
+ this.buffer = n;
8
20
  }
9
21
  getBufferLength() {
10
- return this.chunks.reduce((acc, chunk) => acc + chunk.length, 0);
22
+ return this.offset;
11
23
  }
12
- write(...buffers) {
13
- this.chunks.push(...buffers);
24
+ write(src) {
25
+ this.ensure(src.length);
26
+ src.copy(this.buffer, this.offset);
27
+ this.offset += src.length;
14
28
  return this;
15
29
  }
16
- writeEncoder(encoder) {
17
- return this.write(...encoder.getChunks());
30
+ writeEncoder(other) {
31
+ this.write(other.buffer.subarray(0, other.offset));
32
+ return this;
18
33
  }
19
34
  writeInt8(value) {
20
- const buffer = Buffer.allocUnsafe(1);
21
- buffer.writeInt8(value);
22
- return this.write(buffer);
35
+ this.ensure(1);
36
+ this.buffer.writeInt8(value, this.offset);
37
+ this.offset += 1;
38
+ return this;
23
39
  }
24
40
  writeInt16(value) {
25
- const buffer = Buffer.allocUnsafe(2);
26
- buffer.writeInt16BE(value);
27
- return this.write(buffer);
41
+ this.ensure(2);
42
+ this.buffer.writeInt16BE(value, this.offset);
43
+ this.offset += 2;
44
+ return this;
28
45
  }
29
46
  writeInt32(value) {
30
- const buffer = Buffer.allocUnsafe(4);
31
- buffer.writeInt32BE(value);
32
- return this.write(buffer);
47
+ this.ensure(4);
48
+ this.buffer.writeInt32BE(value, this.offset);
49
+ this.offset += 4;
50
+ return this;
33
51
  }
34
52
  writeUInt32(value) {
35
- const buffer = Buffer.allocUnsafe(4);
36
- buffer.writeUInt32BE(value);
37
- return this.write(buffer);
53
+ this.ensure(4);
54
+ this.buffer.writeUInt32BE(value, this.offset);
55
+ this.offset += 4;
56
+ return this;
38
57
  }
39
58
  writeInt64(value) {
40
- const buffer = Buffer.allocUnsafe(8);
41
- buffer.writeBigInt64BE(value);
42
- return this.write(buffer);
59
+ this.ensure(8);
60
+ this.buffer.writeBigInt64BE(value, this.offset);
61
+ this.offset += 8;
62
+ return this;
43
63
  }
44
64
  writeUVarInt(value) {
45
- const byteArray = [];
46
- while ((value & 0xffffff80) !== 0) {
47
- byteArray.push((value & 0x7f) | 0x80);
65
+ this.ensure(5);
66
+ while (value & 0xffffff80) {
67
+ this.buffer[this.offset++] = (value & 0x7f) | 0x80;
48
68
  value >>>= 7;
49
69
  }
50
- byteArray.push(value & 0x7f);
51
- return this.write(Buffer.from(byteArray));
70
+ this.buffer[this.offset++] = value & 0x7f;
71
+ return this;
52
72
  }
53
73
  writeVarInt(value) {
54
- const encodedValue = (value << 1) ^ (value >> 31);
55
- return this.writeUVarInt(encodedValue);
74
+ return this.writeUVarInt((value << 1) ^ (value >> 31));
56
75
  }
57
76
  writeUVarLong(value) {
58
- const byteArray = [];
59
- while ((value & 0xffffffffffffff80n) !== 0n) {
60
- byteArray.push(Number((value & BigInt(0x7f)) | BigInt(0x80)));
77
+ this.ensure(10);
78
+ while (value >= 0x80n) {
79
+ this.buffer[this.offset++] = Number((value & 0x7fn) | 0x80n);
61
80
  value >>= 7n;
62
81
  }
63
- byteArray.push(Number(value & BigInt(0x7f)));
64
- return this.write(Buffer.from(byteArray));
82
+ this.buffer[this.offset++] = Number(value);
83
+ return this;
65
84
  }
66
85
  writeVarLong(value) {
67
- const encodedValue = (value << BigInt(1)) ^ (value >> BigInt(63));
68
- return this.writeUVarLong(encodedValue);
86
+ return this.writeUVarLong((value << 1n) ^ (value >> 63n));
69
87
  }
70
88
  writeString(value) {
71
- if (value === null) {
89
+ if (value === null)
72
90
  return this.writeInt16(-1);
73
- }
74
91
  const buffer = Buffer.from(value, 'utf-8');
75
- return this.writeInt16(buffer.length).write(buffer);
92
+ this.writeInt16(buffer.length);
93
+ this.write(buffer);
94
+ return this;
76
95
  }
77
96
  writeCompactString(value) {
78
- if (value === null) {
97
+ if (value === null)
79
98
  return this.writeUVarInt(0);
80
- }
81
- const buffer = Buffer.from(value, 'utf-8');
82
- return this.writeUVarInt(buffer.length + 1).write(buffer);
99
+ const b = Buffer.from(value, 'utf-8');
100
+ this.writeUVarInt(b.length + 1);
101
+ this.write(b);
102
+ return this;
83
103
  }
84
104
  writeVarIntString(value) {
85
- if (value === null) {
105
+ if (value === null)
86
106
  return this.writeVarInt(-1);
87
- }
88
- const buffer = Buffer.from(value, 'utf-8');
89
- return this.writeVarInt(buffer.length).write(buffer);
107
+ const b = Buffer.from(value, 'utf-8');
108
+ this.writeVarInt(b.length);
109
+ this.write(b);
110
+ return this;
90
111
  }
91
112
  writeUUID(value) {
92
113
  if (value === null) {
93
- return this.write(Buffer.alloc(16));
114
+ this.ensure(16);
115
+ this.buffer.fill(0, this.offset, this.offset + 16);
116
+ this.offset += 16;
117
+ return this;
94
118
  }
95
- return this.write(Buffer.from(value, 'hex'));
119
+ this.write(Buffer.from(value, 'hex'));
120
+ return this;
96
121
  }
97
122
  writeBoolean(value) {
98
123
  return this.writeInt8(value ? 1 : 0);
99
124
  }
100
125
  writeArray(arr, callback) {
101
- return this.writeInt32(arr.length).write(...arr.flatMap((item) => callback(new Encoder(), item).getChunks()));
126
+ this.writeInt32(arr.length);
127
+ for (const it of arr)
128
+ callback(this, it);
129
+ return this;
102
130
  }
103
131
  writeCompactArray(arr, callback) {
104
- if (arr === null) {
132
+ if (arr === null)
105
133
  return this.writeUVarInt(0);
106
- }
107
- return this.writeUVarInt(arr.length + 1).write(...arr.flatMap((item) => callback(new Encoder(), item).getChunks()));
134
+ this.writeUVarInt(arr.length + 1);
135
+ for (const it of arr)
136
+ callback(this, it);
137
+ return this;
108
138
  }
109
139
  writeVarIntArray(arr, callback) {
110
- return this.writeVarInt(arr.length).write(...arr.flatMap((item) => callback(new Encoder(), item).getChunks()));
140
+ this.writeVarInt(arr.length);
141
+ for (const it of arr)
142
+ callback(this, it);
143
+ return this;
111
144
  }
112
145
  writeBytes(value) {
113
- return this.writeInt32(value.length).write(value);
146
+ this.writeInt32(value.length);
147
+ this.write(value);
148
+ return this;
114
149
  }
115
150
  writeCompactBytes(value) {
116
- return this.writeUVarInt(value.length + 1).write(value);
151
+ this.writeUVarInt(value.length + 1);
152
+ this.write(value);
153
+ return this;
117
154
  }
118
155
  value() {
119
- return Buffer.concat(this.chunks);
156
+ return this.buffer.subarray(0, this.offset);
120
157
  }
121
158
  }
122
159
  exports.Encoder = Encoder;
@@ -0,0 +1,5 @@
1
+ export declare class PromiseChain {
2
+ private locks;
3
+ run(keys: string[], callback: () => Promise<void>): Promise<void>;
4
+ private acquire;
5
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PromiseChain = void 0;
4
+ class PromiseChain {
5
+ locks = new Map();
6
+ async run(keys, callback) {
7
+ const orderedKeys = [...new Set(keys)].sort();
8
+ const releases = [];
9
+ for (const key of orderedKeys) {
10
+ const release = await this.acquire(key);
11
+ releases.push(release);
12
+ }
13
+ try {
14
+ await callback();
15
+ }
16
+ finally {
17
+ releases.reverse().forEach((release) => release());
18
+ }
19
+ }
20
+ async acquire(key) {
21
+ const previousTail = this.locks.get(key);
22
+ let release;
23
+ const currentTail = new Promise((resolve) => (release = resolve));
24
+ if (previousTail) {
25
+ this.locks.set(key, previousTail.then(() => currentTail));
26
+ await previousTail;
27
+ }
28
+ else {
29
+ this.locks.set(key, currentTail);
30
+ }
31
+ return () => {
32
+ release();
33
+ if (this.locks.get(key) === currentTail) {
34
+ this.locks.delete(key);
35
+ }
36
+ };
37
+ }
38
+ }
39
+ exports.PromiseChain = PromiseChain;
@@ -0,0 +1 @@
1
+ export declare const withRetry: (handleError: (error: unknown) => Promise<void>) => <T>(func: () => Promise<T>) => Promise<T>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withRetry = void 0;
4
+ const logger_1 = require("./logger");
5
+ const withRetry = (handleError) => async (func) => {
6
+ let lastError;
7
+ for (let i = 0; i < 15; i++) {
8
+ try {
9
+ return await func();
10
+ }
11
+ catch (error) {
12
+ await handleError(error);
13
+ lastError = error;
14
+ }
15
+ }
16
+ logger_1.log.warn('Retries exhausted', { lastError });
17
+ throw lastError;
18
+ };
19
+ exports.withRetry = withRetry;
@@ -1 +1 @@
1
- export declare const shared: <F extends () => Promise<any>>(func: F) => () => ReturnType<F>;
1
+ export declare const shared: <F extends (...args: any[]) => Promise<any>>(func: F) => (...args: Parameters<F>) => ReturnType<F>;
@@ -2,15 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.shared = void 0;
4
4
  const shared = (func) => {
5
- let promise;
6
- return () => {
7
- if (!promise) {
8
- promise = func();
9
- promise.finally(() => {
10
- promise = undefined;
5
+ let promises = {};
6
+ return (...args) => {
7
+ const key = JSON.stringify(args);
8
+ if (!promises[key]) {
9
+ promises[key] = func(...args);
10
+ promises[key].finally(() => {
11
+ delete promises[key];
11
12
  });
12
13
  }
13
- return promise;
14
+ return promises[key];
14
15
  };
15
16
  };
16
17
  exports.shared = shared;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kafka-ts",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
4
4
  "main": "dist/index.js",
5
5
  "author": "Priit Käärd",
6
6
  "license": "MIT",
@@ -1,24 +0,0 @@
1
- import { IsolationLevel } from "../api/fetch";
2
- import { Assignment } from "../api/sync-group";
3
- import { Cluster } from "../cluster";
4
- import { OffsetManager } from "./offset-manager";
5
- export type Metadata = ReturnType<typeof createMetadata>;
6
- type MetadataOptions = {
7
- cluster: Cluster;
8
- topics?: string[];
9
- isolationLevel?: IsolationLevel;
10
- allowTopicAutoCreation?: boolean;
11
- fromBeginning?: boolean;
12
- offsetManager?: OffsetManager;
13
- };
14
- export declare const createMetadata: ({ cluster, topics, isolationLevel, allowTopicAutoCreation, fromBeginning, offsetManager, }: MetadataOptions) => {
15
- init: () => Promise<void>;
16
- getTopicPartitions: () => Record<string, number[]>;
17
- getTopicIdByName: (name: string) => string;
18
- getTopicNameById: (id: string) => string;
19
- getAssignment: () => Assignment;
20
- setAssignment: (newAssignment: Assignment) => void;
21
- getLeaderIdByTopicPartition: (topic: string, partition: number) => number;
22
- getIsrNodeIdsByTopicPartition: (topic: string, partition: number) => number[];
23
- };
24
- export {};