-
-
Notifications
You must be signed in to change notification settings - Fork 569
New admin method for fetching group offsets for multiple topics #992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
a72d09f
a2216eb
e940948
231ebee
5f42dbe
7a575d0
cef4830
fa0ea86
d6b4cf3
8943aa5
b7b06e0
5f5ac39
bb9567b
23e17e2
ca704d1
5786968
2f190a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -371,60 +371,88 @@ module.exports = ({ | |
| /** | ||
| * @param {string} groupId | ||
| * @param {string} topic | ||
| * @param {string[]} topics | ||
|
||
| * @param {boolean} [resolveOffsets=false] | ||
| * @return {Promise} | ||
| */ | ||
| const fetchOffsets = async ({ groupId, topic, resolveOffsets = false }) => { | ||
| const fetchOffsets = async ({ groupId, topic, topics, resolveOffsets = false }) => { | ||
| if (!groupId) { | ||
| throw new KafkaJSNonRetriableError(`Invalid groupId ${groupId}`) | ||
| } | ||
|
|
||
| if (!topic) { | ||
| throw new KafkaJSNonRetriableError(`Invalid topic ${topic}`) | ||
| if (!topic && !topics) { | ||
| topics = [] | ||
| } | ||
|
|
||
| const partitions = await findTopicPartitions(cluster, topic) | ||
| const coordinator = await cluster.findGroupCoordinator({ groupId }) | ||
| const partitionsToFetch = partitions.map(partition => ({ partition })) | ||
| if (!topic && !Array.isArray(topics)) { | ||
| throw new KafkaJSNonRetriableError(`Expected topic or topics array to be set`) | ||
| } | ||
|
|
||
| if (topic && topics) { | ||
| throw new KafkaJSNonRetriableError(`Either topic or topics must be set, not both`) | ||
| } | ||
|
|
||
| if (topic) { | ||
| topics = [topic] | ||
| } | ||
|
|
||
| const coordinator = await cluster.findGroupCoordinator({ groupId }) | ||
| const topicsToFetch = await Promise.all( | ||
| topics.map(async topic => { | ||
| const partitions = await findTopicPartitions(cluster, topic) | ||
| const partitionsToFetch = partitions.map(partition => ({ partition })) | ||
| return { topic, partitions: partitionsToFetch } | ||
| }) | ||
| ) | ||
| let { responses: consumerOffsets } = await coordinator.offsetFetch({ | ||
| groupId, | ||
| topics: [{ topic, partitions: partitionsToFetch }], | ||
| topics: topicsToFetch, | ||
| }) | ||
|
|
||
| if (resolveOffsets) { | ||
| const indexedOffsets = indexByPartition(await fetchTopicOffsets(topic)) | ||
| consumerOffsets = consumerOffsets.map(({ topic, partitions }) => ({ | ||
| topic, | ||
| partitions: partitions.map(({ offset, partition, ...props }) => { | ||
| let resolvedOffset = offset | ||
| if (Number(offset) === EARLIEST_OFFSET) { | ||
| resolvedOffset = indexedOffsets[partition].low | ||
| } | ||
| if (Number(offset) === LATEST_OFFSET) { | ||
| resolvedOffset = indexedOffsets[partition].high | ||
| } | ||
| consumerOffsets = await Promise.all( | ||
| consumerOffsets.map(async ({ topic, partitions }) => { | ||
| const indexedOffsets = indexByPartition(await fetchTopicOffsets(topic)) | ||
| const recalculatedPartitions = partitions.map(({ offset, partition, ...props }) => { | ||
| let resolvedOffset = offset | ||
| if (Number(offset) === EARLIEST_OFFSET) { | ||
| resolvedOffset = indexedOffsets[partition].low | ||
| } | ||
| if (Number(offset) === LATEST_OFFSET) { | ||
| resolvedOffset = indexedOffsets[partition].high | ||
| } | ||
| return { | ||
| partition, | ||
| offset: resolvedOffset, | ||
| ...props, | ||
| } | ||
| }) | ||
|
|
||
| await setOffsets({ groupId, topic, partitions: recalculatedPartitions }) | ||
|
|
||
| return { | ||
| partition, | ||
| offset: resolvedOffset, | ||
| ...props, | ||
| topic, | ||
| partitions: recalculatedPartitions, | ||
| } | ||
| }), | ||
| })) | ||
| const [{ partitions }] = consumerOffsets | ||
| await setOffsets({ groupId, topic, partitions }) | ||
| }) | ||
| ) | ||
| } | ||
|
|
||
| return consumerOffsets | ||
| .filter(response => response.topic === topic) | ||
| .map(({ partitions }) => | ||
| partitions.map(({ partition, offset, metadata }) => ({ | ||
| partition, | ||
| offset, | ||
| metadata: metadata || null, | ||
| })) | ||
| ) | ||
| .pop() | ||
| const result = consumerOffsets.map(({ topic, partitions }) => { | ||
| const completePartitions = partitions.map(({ partition, offset, metadata }) => ({ | ||
| partition, | ||
| offset, | ||
| metadata: metadata || null, | ||
Nevon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| })) | ||
|
|
||
| return { topic, partitions: completePartitions } | ||
| }) | ||
|
|
||
| if (topic) { | ||
| return result.pop().partitions | ||
| } else { | ||
| return result | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -434,7 +434,8 @@ export type Admin = { | |
| fetchTopicMetadata(options?: { topics: string[] }): Promise<{ topics: Array<ITopicMetadata> }> | ||
| fetchOffsets(options: { | ||
| groupId: string | ||
| topic: string | ||
| topic?: string | ||
|
||
| topics?: string[] | ||
| resolveOffsets?: boolean | ||
| }): Promise<Array<SeekEntry & { metadata: string | null }>> | ||
| fetchTopicOffsets(topic: string): Promise<Array<SeekEntry & { high: string; low: string }>> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.